syncedtabs-tab-list.mjs (5940B)
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 http://mozilla.org/MPL/2.0/. */ 4 5 import { 6 html, 7 ifDefined, 8 when, 9 } from "chrome://global/content/vendor/lit.all.mjs"; 10 import { 11 FxviewTabListBase, 12 FxviewTabRowBase, 13 } from "chrome://browser/content/firefoxview/fxview-tab-list.mjs"; 14 // eslint-disable-next-line import/no-unassigned-import 15 import "chrome://global/content/elements/moz-button.mjs"; 16 17 const lazy = {}; 18 let XPCOMUtils; 19 20 XPCOMUtils = ChromeUtils.importESModule( 21 "resource://gre/modules/XPCOMUtils.sys.mjs" 22 ).XPCOMUtils; 23 XPCOMUtils.defineLazyPreferenceGetter( 24 lazy, 25 "virtualListEnabledPref", 26 "browser.firefox-view.virtual-list.enabled" 27 ); 28 29 /** 30 * A list of synced tabs that are clickable and able to be remotely closed 31 */ 32 33 export class SyncedTabsTabList extends FxviewTabListBase { 34 constructor() { 35 super(); 36 } 37 38 static queries = { 39 ...FxviewTabListBase.queries, 40 rowEls: { 41 all: "syncedtabs-tab-row", 42 }, 43 }; 44 45 itemTemplate = (tabItem, i) => { 46 return html` 47 <syncedtabs-tab-row 48 ?active=${i == this.activeIndex} 49 .canClose=${ifDefined(tabItem.canClose)} 50 .closeRequested=${ifDefined(tabItem.closeRequested)} 51 ?compact=${this.compactRows} 52 .currentActiveElementId=${this.currentActiveElementId} 53 .favicon=${tabItem.icon} 54 .fxaDeviceId=${tabItem.fxaDeviceId} 55 .primaryL10nId=${tabItem.primaryL10nId} 56 .primaryL10nArgs=${ifDefined(tabItem.primaryL10nArgs)} 57 .secondaryL10nId=${tabItem.secondaryL10nId} 58 .secondaryL10nArgs=${ifDefined(tabItem.secondaryL10nArgs)} 59 .tertiaryL10nId=${ifDefined(tabItem.tertiaryL10nId)} 60 .tertiaryL10nArgs=${ifDefined(tabItem.tertiaryL10nArgs)} 61 .secondaryActionClass=${this.secondaryActionClass} 62 .tertiaryActionClass=${ifDefined(tabItem.tertiaryActionClass)} 63 .sourceClosedId=${ifDefined(tabItem.sourceClosedId)} 64 .sourceWindowId=${ifDefined(tabItem.sourceWindowId)} 65 .closedId=${ifDefined(tabItem.closedId || tabItem.closedId)} 66 role="listitem" 67 .tabElement=${ifDefined(tabItem.tabElement)} 68 .title=${tabItem.title} 69 .url=${tabItem.url} 70 .searchQuery=${ifDefined(this.searchQuery)} 71 .hasPopup=${this.hasPopup} 72 ></fxview-tab-row> 73 `; 74 }; 75 76 stylesheets() { 77 return [ 78 super.stylesheets(), 79 html`<link 80 rel="stylesheet" 81 href="chrome://browser/content/firefoxview/syncedtabs-tab-list.css" 82 />`, 83 ]; 84 } 85 86 render() { 87 if (this.searchQuery && !this.tabItems.length) { 88 return this.emptySearchResultsTemplate(); 89 } 90 return html` 91 ${this.stylesheets()} 92 <div 93 id="fxview-tab-list" 94 class="fxview-tab-list" 95 data-l10n-id="firefoxview-tabs" 96 role="list" 97 @keydown=${this.handleFocusElementInRow} 98 > 99 ${when( 100 lazy.virtualListEnabledPref, 101 () => html` 102 <virtual-list 103 .activeIndex=${this.activeIndex} 104 .items=${this.tabItems} 105 .template=${this.itemTemplate} 106 ></virtual-list> 107 `, 108 () => 109 html`${this.tabItems.map((tabItem, i) => 110 this.itemTemplate(tabItem, i) 111 )}` 112 )} 113 </div> 114 <slot name="menu"></slot> 115 `; 116 } 117 } 118 119 customElements.define("syncedtabs-tab-list", SyncedTabsTabList); 120 121 /** 122 * A tab item that displays favicon, title, url, and time of last access 123 * 124 * @property {boolean} canClose - Whether the tab item has the ability to be closed remotely 125 * @property {boolean} closeRequested - Whether the tab has been requested closed but not removed from the list 126 * @property {string} fxaDeviceId - The device Id the tab item belongs to, for closing tabs remotely 127 */ 128 129 export class SyncedTabsTabRow extends FxviewTabRowBase { 130 constructor() { 131 super(); 132 } 133 134 static properties = { 135 ...FxviewTabRowBase.properties, 136 canClose: { type: Boolean }, 137 closeRequested: { type: Boolean }, 138 fxaDeviceId: { type: String }, 139 }; 140 141 secondaryButtonTemplate() { 142 return html`${when( 143 this.secondaryL10nId && this.secondaryActionHandler, 144 () => 145 html`<moz-button 146 type="icon ghost" 147 class="fxview-tab-row-button" 148 iconSrc=${this.getIconSrc(this.secondaryActionClass)} 149 ?disabled=${this.closeRequested} 150 id="fxview-tab-row-secondary-button" 151 data-l10n-id=${this.secondaryL10nId} 152 data-l10n-args=${ifDefined(this.secondaryL10nArgs)} 153 aria-haspopup=${ifDefined(this.hasPopup)} 154 @click=${this.secondaryActionHandler} 155 tabindex=${this.active && 156 this.currentActiveElementId === "fxview-tab-row-secondary-button" 157 ? "0" 158 : "-1"} 159 ></moz-button>` 160 )}`; 161 } 162 163 render() { 164 return html` 165 ${this.stylesheets()} 166 <a 167 href=${ifDefined(this.url)} 168 class="fxview-tab-row-main" 169 id="fxview-tab-row-main" 170 ?disabled=${this.closeRequested} 171 tabindex=${this.active && 172 this.currentActiveElementId === "fxview-tab-row-main" 173 ? "0" 174 : "-1"} 175 data-l10n-id=${ifDefined(this.primaryL10nId)} 176 data-l10n-args=${ifDefined(this.primaryL10nArgs)} 177 @click=${this.primaryActionHandler} 178 @keydown=${this.primaryActionHandler} 179 title=${!this.primaryL10nId ? this.url : null} 180 > 181 ${this.faviconTemplate()} ${this.titleTemplate()} 182 ${when( 183 !this.compact, 184 () => 185 html`${this.urlTemplate()} ${this.dateTemplate()} 186 ${this.timeTemplate()}` 187 )} 188 </a> 189 ${this.secondaryButtonTemplate()} ${this.tertiaryButtonTemplate()} 190 `; 191 } 192 } 193 194 customElements.define("syncedtabs-tab-row", SyncedTabsTabRow);