sidebar-pins-promo.mjs (5470B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 import { 6 classMap, 7 html, 8 map, 9 nothing, 10 } from "chrome://global/content/vendor/lit.all.mjs"; 11 import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; 12 13 const { XPCOMUtils } = ChromeUtils.importESModule( 14 "resource://gre/modules/XPCOMUtils.sys.mjs" 15 ); 16 17 let lazy = {}; 18 ChromeUtils.defineESModuleGetters(lazy, { 19 SidebarManager: 20 "moz-src:///browser/components/sidebar/SidebarManager.sys.mjs", 21 }); 22 const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab"; 23 24 /** 25 * A promotional card for drag-to-pin tabs. 26 * 27 * It is only displayed in the expanded sidebar state to vertical tab users who 28 * do not have any pinned tabs. 29 * 30 * The card can be dismissed manually by clicking the X button. It is 31 * automatically dismissed by adding a pin via a context menu or by dragging 32 * the tab over the card and pinning. 33 */ 34 export default class SidebarPinsPromo extends MozLitElement { 35 static queries = { 36 card: ".promo-card", 37 closeButton: ".close-button", 38 }; 39 40 constructor() { 41 super(); 42 XPCOMUtils.defineLazyPreferenceGetter( 43 this, 44 "verticalTabsEnabled", 45 "sidebar.verticalTabs", 46 false, 47 () => this.requestUpdate() 48 ); 49 XPCOMUtils.defineLazyPreferenceGetter( 50 this, 51 "dragToPinPromoDismissed", 52 "sidebar.verticalTabs.dragToPinPromo.dismissed", 53 false, 54 () => this.requestUpdate() 55 ); 56 this.launcherObserver = new MutationObserver(() => this.requestUpdate()); 57 } 58 #icons = [ 59 { name: "firefox", src: "chrome://browser/skin/sidebar/firefox.svg" }, 60 { name: "slack", src: "chrome://browser/skin/sidebar/slack.svg" }, 61 { name: "foxy", src: "chrome://browser/skin/sidebar/foxy.svg" }, 62 { name: "gmail", src: "chrome://browser/skin/sidebar/gmail.svg" }, 63 ]; 64 65 connectedCallback() { 66 super.connectedCallback(); 67 window.addEventListener("TabPinned", this); 68 this.addEventListener("dragover", this); 69 this.addEventListener("drop", this); 70 this.addEventListener("dragleave", this); 71 lazy.SidebarManager.addEventListener("checkForPinnedTabsComplete", this); 72 lazy.SidebarManager.checkForPinnedTabs(); 73 this.launcherObserver.observe(window.SidebarController.sidebarMain, { 74 attributeFilter: ["expanded"], 75 }); 76 77 XPCOMUtils.defineLazyPreferenceGetter( 78 this, 79 "verticalTabsEnabled", 80 "sidebar.verticalTabs", 81 false, 82 () => this.requestUpdate() 83 ); 84 XPCOMUtils.defineLazyPreferenceGetter( 85 this, 86 "dragToPinPromoDismissed", 87 "sidebar.verticalTabs.dragToPinPromo.dismissed", 88 false, 89 () => this.requestUpdate() 90 ); 91 } 92 93 disconnectedCallback() { 94 super.disconnectedCallback(); 95 window.removeEventListener("TabPinned", this); 96 this.removeEventListener("dragover", this); 97 this.removeEventListener("drop", this); 98 this.removeEventListener("dragleave", this); 99 lazy.SidebarManager.removeEventListener("checkForPinnedTabsComplete", this); 100 this.launcherObserver.disconnect(); 101 } 102 103 /** 104 * Handle drag-and-drop events from MozTabbrowserTab elements. 105 * 106 * @param {DragEvent} event 107 */ 108 handleEvent(event) { 109 switch (event.type) { 110 case "dragover": 111 if (event.dataTransfer.types.includes(TAB_DROP_TYPE)) { 112 event.preventDefault(); 113 this.card.toggleAttribute("dragactive", true); 114 } 115 break; 116 case "drop": 117 case "dragleave": 118 this.card.toggleAttribute("dragactive", false); 119 break; 120 case "TabPinned": 121 this.dismissDragToPinPromo(); 122 break; 123 case "checkForPinnedTabsComplete": 124 this.requestUpdate(); 125 break; 126 } 127 } 128 129 dismissDragToPinPromo() { 130 Services.prefs.setBoolPref( 131 "sidebar.verticalTabs.dragToPinPromo.dismissed", 132 true 133 ); 134 } 135 136 /** 137 * Wrap one of the icons to be shown in a dotted border box. 138 * 139 * @param {object} icon 140 * @returns {HTMLDivElement} 141 */ 142 #iconCellTemplate(icon) { 143 return html`<div 144 class=${classMap({ 145 "icon-cell": true, 146 [`icon-${icon.name}`]: true, 147 })} 148 > 149 <img src=${icon.src} role="presentation" /> 150 </div>`; 151 } 152 153 get shouldRender() { 154 return ( 155 this.verticalTabsEnabled && 156 lazy.SidebarManager.checkForPinnedTabsComplete && 157 !this.dragToPinPromoDismissed && 158 window.SidebarController.sidebarMain.hasAttribute("expanded") 159 ); 160 } 161 162 willUpdate() { 163 this.toggleAttribute("hidden", !this.shouldRender); 164 } 165 166 render() { 167 if (!this.shouldRender) { 168 return nothing; 169 } 170 return html` <link 171 rel="stylesheet" 172 href="chrome://browser/content/sidebar/sidebar-pins-promo.css" 173 /> 174 <moz-card class="promo-card"> 175 <div class="promo-text" data-l10n-id="sidebar-pins-promo-text"></div> 176 <div class="icon-row"> 177 ${map(this.#icons, icon => this.#iconCellTemplate(icon))} 178 </div> 179 </moz-card> 180 <moz-button 181 class="close-button" 182 iconsrc="resource://content-accessible/close-12.svg" 183 data-l10n-id="sidebar-panel-header-close-button" 184 @click=${this.dismissDragToPinPromo} 185 size="small" 186 type="icon ghost" 187 > 188 </moz-button>`; 189 } 190 } 191 192 customElements.define("sidebar-pins-promo", SidebarPinsPromo);