diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
commit | d8bbc7858622b6d9c278469aab701ca0b609cddf (patch) | |
tree | eff41dc61d9f714852212739e6b3738b82a2af87 /browser/components/firefoxview/fxview-tab-list.mjs | |
parent | Releasing progress-linux version 125.0.3-1~progress7.99u1. (diff) | |
download | firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/firefoxview/fxview-tab-list.mjs')
-rw-r--r-- | browser/components/firefoxview/fxview-tab-list.mjs | 689 |
1 files changed, 234 insertions, 455 deletions
diff --git a/browser/components/firefoxview/fxview-tab-list.mjs b/browser/components/firefoxview/fxview-tab-list.mjs index 978ab79724..57181e3bea 100644 --- a/browser/components/firefoxview/fxview-tab-list.mjs +++ b/browser/components/firefoxview/fxview-tab-list.mjs @@ -12,6 +12,8 @@ import { } from "chrome://global/content/vendor/lit.all.mjs"; import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; import { escapeRegExp } from "./helpers.mjs"; +// eslint-disable-next-line import/no-unassigned-import +import "chrome://global/content/elements/moz-button.mjs"; const NOW_THRESHOLD_MS = 91000; const FXVIEW_ROW_HEIGHT_PX = 32; @@ -45,13 +47,13 @@ if (!window.IS_STORYBOOK) { * @property {string} dateTimeFormat - Expected format for date and/or time * @property {string} hasPopup - The aria-haspopup attribute for the secondary action, if required * @property {number} maxTabsLength - The max number of tabs for the list - * @property {boolean} pinnedTabsGridView - Whether to show pinned tabs in a grid view * @property {Array} tabItems - Items to show in the tab list * @property {string} searchQuery - The query string to highlight, if provided. + * @property {string} searchInProgress - Whether a search has been initiated. * @property {string} secondaryActionClass - The class used to style the secondary action element * @property {string} tertiaryActionClass - The class used to style the tertiary action element */ -export default class FxviewTabList extends MozLitElement { +export class FxviewTabListBase extends MozLitElement { constructor() { super(); window.MozXULElement.insertFTLIfNeeded("toolkit/branding/brandings.ftl"); @@ -62,10 +64,8 @@ export default class FxviewTabList extends MozLitElement { this.dateTimeFormat = "relative"; this.maxTabsLength = 25; this.tabItems = []; - this.pinnedTabs = []; - this.pinnedTabsGridView = false; - this.unpinnedTabs = []; this.compactRows = false; + this.searchInProgress = false; this.updatesPaused = true; this.#register(); } @@ -77,16 +77,18 @@ export default class FxviewTabList extends MozLitElement { dateTimeFormat: { type: String }, hasPopup: { type: String }, maxTabsLength: { type: Number }, - pinnedTabsGridView: { type: Boolean }, tabItems: { type: Array }, updatesPaused: { type: Boolean }, searchQuery: { type: String }, + searchInProgress: { type: Boolean }, secondaryActionClass: { type: String }, tertiaryActionClass: { type: String }, }; static queries = { - rowEls: { all: "fxview-tab-row" }, + rowEls: { + all: "fxview-tab-row", + }, rootVirtualListEl: "virtual-list", }; @@ -108,20 +110,7 @@ export default class FxviewTabList extends MozLitElement { } } - // Move pinned tabs to the beginning of the list - if (this.pinnedTabsGridView) { - // Can set maxTabsLength to -1 to have no max - this.unpinnedTabs = this.tabItems.filter( - tab => !tab.indicators?.includes("pinned") - ); - this.pinnedTabs = this.tabItems.filter(tab => - tab.indicators?.includes("pinned") - ); - if (this.maxTabsLength > 0) { - this.unpinnedTabs = this.unpinnedTabs.slice(0, this.maxTabsLength); - } - this.tabItems = [...this.pinnedTabs, ...this.unpinnedTabs]; - } else if (this.maxTabsLength > 0) { + if (this.maxTabsLength > 0) { this.tabItems = this.tabItems.slice(0, this.maxTabsLength); } } @@ -148,7 +137,7 @@ export default class FxviewTabList extends MozLitElement { "timeMsPref", "browser.tabs.firefox-view.updateTimeMs", NOW_THRESHOLD_MS, - (prefName, oldVal, newVal) => { + () => { this.clearIntervalTimer(); if (!this.isConnected) { return; @@ -197,93 +186,32 @@ export default class FxviewTabList extends MozLitElement { if (e.code == "ArrowUp") { // Focus either the link or button of the previous row based on this.currentActiveElementId e.preventDefault(); - if ( - (this.pinnedTabsGridView && - this.activeIndex >= this.pinnedTabs.length) || - !this.pinnedTabsGridView - ) { - this.focusPrevRow(); - } + this.focusPrevRow(); } else if (e.code == "ArrowDown") { // Focus either the link or button of the next row based on this.currentActiveElementId e.preventDefault(); - if ( - this.pinnedTabsGridView && - this.activeIndex < this.pinnedTabs.length - ) { - this.focusIndex(this.pinnedTabs.length); - } else { - this.focusNextRow(); - } + this.focusNextRow(); } else if (e.code == "ArrowRight") { // Focus either the link or the button in the current row and // set this.currentActiveElementId to that element's ID e.preventDefault(); if (document.dir == "rtl") { - this.moveFocusLeft(fxviewTabRow); + fxviewTabRow.moveFocusLeft(); } else { - this.moveFocusRight(fxviewTabRow); + fxviewTabRow.moveFocusRight(); } } else if (e.code == "ArrowLeft") { // Focus either the link or the button in the current row and // set this.currentActiveElementId to that element's ID e.preventDefault(); if (document.dir == "rtl") { - this.moveFocusRight(fxviewTabRow); + fxviewTabRow.moveFocusRight(); } else { - this.moveFocusLeft(fxviewTabRow); + fxviewTabRow.moveFocusLeft(); } } } - moveFocusRight(fxviewTabRow) { - if ( - this.pinnedTabsGridView && - fxviewTabRow.indicators?.includes("pinned") - ) { - this.focusNextRow(); - } else if ( - (fxviewTabRow.indicators?.includes("soundplaying") || - fxviewTabRow.indicators?.includes("muted")) && - this.currentActiveElementId === "fxview-tab-row-main" - ) { - this.currentActiveElementId = fxviewTabRow.focusMediaButton(); - } else if ( - this.currentActiveElementId === "fxview-tab-row-media-button" || - this.currentActiveElementId === "fxview-tab-row-main" - ) { - this.currentActiveElementId = fxviewTabRow.focusSecondaryButton(); - } else if ( - fxviewTabRow.tertiaryButtonEl && - this.currentActiveElementId === "fxview-tab-row-secondary-button" - ) { - this.currentActiveElementId = fxviewTabRow.focusTertiaryButton(); - } - } - - moveFocusLeft(fxviewTabRow) { - if ( - this.pinnedTabsGridView && - (fxviewTabRow.indicators?.includes("pinned") || - (this.currentActiveElementId === "fxview-tab-row-main" && - this.activeIndex === this.pinnedTabs.length)) - ) { - this.focusPrevRow(); - } else if ( - this.currentActiveElementId === "fxview-tab-row-tertiary-button" - ) { - this.currentActiveElementId = fxviewTabRow.focusSecondaryButton(); - } else if ( - (fxviewTabRow.indicators?.includes("soundplaying") || - fxviewTabRow.indicators?.includes("muted")) && - this.currentActiveElementId === "fxview-tab-row-secondary-button" - ) { - this.currentActiveElementId = fxviewTabRow.focusMediaButton(); - } else { - this.currentActiveElementId = fxviewTabRow.focusLink(); - } - } - focusPrevRow() { this.focusIndex(this.activeIndex - 1); } @@ -294,18 +222,12 @@ export default class FxviewTabList extends MozLitElement { async focusIndex(index) { // Focus link or button of item - if ( - ((this.pinnedTabsGridView && index > this.pinnedTabs.length) || - !this.pinnedTabsGridView) && - lazy.virtualListEnabledPref - ) { - let row = this.rootVirtualListEl.getItem(index - this.pinnedTabs.length); + if (lazy.virtualListEnabledPref) { + let row = this.rootVirtualListEl.getItem(index); if (!row) { return; } - let subList = this.rootVirtualListEl.getSubListForItem( - index - this.pinnedTabs.length - ); + let subList = this.rootVirtualListEl.getSubListForItem(index); if (!subList) { return; } @@ -347,27 +269,15 @@ export default class FxviewTabList extends MozLitElement { time = tabItem.time || tabItem.closedAt; } } + return html` <fxview-tab-row - exportparts="secondary-button" - class=${classMap({ - pinned: - this.pinnedTabsGridView && tabItem.indicators?.includes("pinned"), - })} ?active=${i == this.activeIndex} ?compact=${this.compactRows} - .hasPopup=${this.hasPopup} - .containerObj=${ifDefined(tabItem.containerObj)} .currentActiveElementId=${this.currentActiveElementId} - .dateTimeFormat=${this.dateTimeFormat} .favicon=${tabItem.icon} - .indicators=${ifDefined(tabItem.indicators)} - .pinnedTabsGridView=${ifDefined(this.pinnedTabsGridView)} .primaryL10nId=${tabItem.primaryL10nId} .primaryL10nArgs=${ifDefined(tabItem.primaryL10nArgs)} - role=${this.pinnedTabsGridView && tabItem.indicators?.includes("pinned") - ? "none" - : "listitem"} .secondaryL10nId=${tabItem.secondaryL10nId} .secondaryL10nArgs=${ifDefined(tabItem.secondaryL10nArgs)} .tertiaryL10nId=${ifDefined(tabItem.tertiaryL10nId)} @@ -377,41 +287,36 @@ export default class FxviewTabList extends MozLitElement { .sourceClosedId=${ifDefined(tabItem.sourceClosedId)} .sourceWindowId=${ifDefined(tabItem.sourceWindowId)} .closedId=${ifDefined(tabItem.closedId || tabItem.closedId)} - .searchQuery=${ifDefined(this.searchQuery)} + role="listitem" .tabElement=${ifDefined(tabItem.tabElement)} .time=${ifDefined(time)} - .timeMsPref=${ifDefined(this.timeMsPref)} .title=${tabItem.title} .url=${tabItem.url} + .searchQuery=${ifDefined(this.searchQuery)} + .timeMsPref=${ifDefined(this.timeMsPref)} + .hasPopup=${this.hasPopup} + .dateTimeFormat=${this.dateTimeFormat} ></fxview-tab-row> `; }; + stylesheets() { + return html`<link + rel="stylesheet" + href="chrome://browser/content/firefoxview/fxview-tab-list.css" + />`; + } + render() { - if (this.searchQuery && this.tabItems.length === 0) { - return this.#emptySearchResultsTemplate(); + if ( + this.searchQuery && + this.tabItems.length === 0 && + !this.searchInProgress + ) { + return this.emptySearchResultsTemplate(); } return html` - <link - rel="stylesheet" - href="chrome://browser/content/firefoxview/fxview-tab-list.css" - /> - ${when( - this.pinnedTabsGridView && this.pinnedTabs.length, - () => html` - <div - id="fxview-tab-list" - class="fxview-tab-list pinned" - data-l10n-id="firefoxview-pinned-tabs" - role="tablist" - @keydown=${this.handleFocusElementInRow} - > - ${this.pinnedTabs.map((tabItem, i) => - this.itemTemplate(tabItem, i) - )} - </div> - ` - )} + ${this.stylesheets()} <div id="fxview-tab-list" class="fxview-tab-list" @@ -424,28 +329,21 @@ export default class FxviewTabList extends MozLitElement { () => html` <virtual-list .activeIndex=${this.activeIndex} - .pinnedTabsIndexOffset=${this.pinnedTabsGridView - ? this.pinnedTabs.length - : 0} - .items=${this.pinnedTabsGridView - ? this.unpinnedTabs - : this.tabItems} + .items=${this.tabItems} .template=${this.itemTemplate} ></virtual-list> - ` - )} - ${when( - !lazy.virtualListEnabledPref, - () => html` - ${this.tabItems.map((tabItem, i) => this.itemTemplate(tabItem, i))} - ` + `, + () => + html`${this.tabItems.map((tabItem, i) => + this.itemTemplate(tabItem, i) + )}` )} </div> <slot name="menu"></slot> `; } - #emptySearchResultsTemplate() { + emptySearchResultsTemplate() { return html` <fxview-empty-state class="search-results" headerLabel="firefoxview-search-results-empty" @@ -455,23 +353,20 @@ export default class FxviewTabList extends MozLitElement { </fxview-empty-state>`; } } -customElements.define("fxview-tab-list", FxviewTabList); +customElements.define("fxview-tab-list", FxviewTabListBase); /** * A tab item that displays favicon, title, url, and time of last access * * @property {boolean} active - Should current item have focus on keydown * @property {boolean} compact - Whether to hide the URL and date/time for this tab. - * @property {object} containerObj - Info about an open tab's container if within one * @property {string} currentActiveElementId - ID of currently focused element within each tab item * @property {string} dateTimeFormat - Expected format for date and/or time * @property {string} hasPopup - The aria-haspopup attribute for the secondary action, if required - * @property {string} indicators - An array of tab indicators if any are present * @property {number} closedId - The tab ID for when the tab item was closed. * @property {number} sourceClosedId - The closedId of the closed window its from if applicable * @property {number} sourceWindowId - The sessionstore id of the window its from if applicable * @property {string} favicon - The favicon for the tab item. - * @property {boolean} pinnedTabsGridView - Whether the show pinned tabs in a grid view * @property {string} primaryL10nId - The l10n id used for the primary action element * @property {string} primaryL10nArgs - The l10n args used for the primary action element * @property {string} secondaryL10nId - The l10n id used for the secondary action button @@ -487,23 +382,14 @@ customElements.define("fxview-tab-list", FxviewTabList); * @property {number} timeMsPref - The frequency in milliseconds of updates to relative time * @property {string} searchQuery - The query string to highlight, if provided. */ -export class FxviewTabRow extends MozLitElement { - constructor() { - super(); - this.active = false; - this.currentActiveElementId = "fxview-tab-row-main"; - } - +export class FxviewTabRowBase extends MozLitElement { static properties = { active: { type: Boolean }, compact: { type: Boolean }, - containerObj: { type: Object }, currentActiveElementId: { type: String }, dateTimeFormat: { type: String }, favicon: { type: String }, hasPopup: { type: String }, - indicators: { type: Array }, - pinnedTabsGridView: { type: Boolean }, primaryL10nId: { type: String }, primaryL10nArgs: { type: String }, secondaryL10nId: { type: String }, @@ -523,12 +409,16 @@ export class FxviewTabRow extends MozLitElement { searchQuery: { type: String }, }; + constructor() { + super(); + this.active = false; + this.currentActiveElementId = "fxview-tab-row-main"; + } + static queries = { mainEl: "#fxview-tab-row-main", secondaryButtonEl: "#fxview-tab-row-secondary-button:not([hidden])", tertiaryButtonEl: "#fxview-tab-row-tertiary-button", - mediaButtonEl: "#fxview-tab-row-media-button", - pinnedTabButtonEl: "button#fxview-tab-row-main", }; get currentFocusable() { @@ -539,50 +429,45 @@ export class FxviewTabRow extends MozLitElement { return focusItem; } - connectedCallback() { - super.connectedCallback(); - this.addEventListener("keydown", this.handleKeydown); - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.removeEventListener("keydown", this.handleKeydown); - } - - handleKeydown(e) { - if ( - this.active && - this.pinnedTabsGridView && - this.indicators?.includes("pinned") && - e.key === "m" && - e.ctrlKey - ) { - this.muteOrUnmuteTab(); - } - } - focus() { this.currentFocusable.focus(); } focusSecondaryButton() { + let tabList = this.getRootNode().host; this.secondaryButtonEl.focus(); - return this.secondaryButtonEl.id; + tabList.currentActiveElementId = this.secondaryButtonEl.id; } focusTertiaryButton() { + let tabList = this.getRootNode().host; this.tertiaryButtonEl.focus(); - return this.tertiaryButtonEl.id; - } - - focusMediaButton() { - this.mediaButtonEl.focus(); - return this.mediaButtonEl.id; + tabList.currentActiveElementId = this.tertiaryButtonEl.id; } focusLink() { + let tabList = this.getRootNode().host; this.mainEl.focus(); - return this.mainEl.id; + tabList.currentActiveElementId = this.mainEl.id; + } + + moveFocusRight() { + if (this.currentActiveElementId === "fxview-tab-row-main") { + this.focusSecondaryButton(); + } else if ( + this.tertiaryButtonEl && + this.currentActiveElementId === "fxview-tab-row-secondary-button" + ) { + this.focusTertiaryButton(); + } + } + + moveFocusLeft() { + if (this.currentActiveElementId === "fxview-tab-row-tertiary-button") { + this.focusSecondaryButton(); + } else { + this.focusLink(); + } } dateFluentArgs(timestamp, dateTimeFormat) { @@ -652,16 +537,6 @@ export class FxviewTabRow extends MozLitElement { return icon; } - getContainerClasses() { - let containerClasses = ["fxview-tab-row-container-indicator", "icon"]; - if (this.containerObj) { - let { icon, color } = this.containerObj; - containerClasses.push(`identity-icon-${icon}`); - containerClasses.push(`identity-color-${color}`); - } - return containerClasses; - } - primaryActionHandler(event) { if ( (event.type == "click" && !event.altKey) || @@ -683,9 +558,6 @@ export class FxviewTabRow extends MozLitElement { secondaryActionHandler(event) { if ( - (this.pinnedTabsGridView && - this.indicators?.includes("pinned") && - event.type == "contextmenu") || (event.type == "click" && event.detail && !event.altKey) || // detail=0 is from keyboard (event.type == "click" && !event.detail) @@ -718,92 +590,80 @@ export class FxviewTabRow extends MozLitElement { } } - muteOrUnmuteTab(e) { - e?.preventDefault(); - // If the tab has no sound playing, the mute/unmute button will be removed when toggled. - // We should move the focus to the right in that case. This does not apply to pinned tabs - // on the Open Tabs page. - let shouldMoveFocus = - (!this.pinnedTabsGridView || - (!this.indicators.includes("pinned") && this.pinnedTabsGridView)) && - this.mediaButtonEl && - !this.indicators.includes("soundplaying") && - this.currentActiveElementId === "fxview-tab-row-media-button"; - - // detail=0 is from keyboard - if (e?.type == "click" && !e?.detail && shouldMoveFocus) { - let tabList = this.getRootNode().host; - if (document.dir == "rtl") { - tabList.moveFocusLeft(this); - } else { - tabList.moveFocusRight(this); - } + /** + * Find all matches of query within the given string, and compute the result + * to be rendered. + * + * @param {string} query + * @param {string} string + */ + highlightSearchMatches(query, string) { + const fragments = []; + const regex = RegExp(escapeRegExp(query), "dgi"); + let prevIndexEnd = 0; + let result; + while ((result = regex.exec(string)) !== null) { + const [indexStart, indexEnd] = result.indices[0]; + fragments.push(string.substring(prevIndexEnd, indexStart)); + fragments.push( + html`<strong>${string.substring(indexStart, indexEnd)}</strong>` + ); + prevIndexEnd = regex.lastIndex; } - this.tabElement.toggleMuteAudio(); + fragments.push(string.substring(prevIndexEnd)); + return fragments; + } + + stylesheets() { + return html`<link + rel="stylesheet" + href="chrome://browser/content/firefoxview/fxview-tab-row.css" + />`; } - #faviconTemplate() { + faviconTemplate() { return html`<span - class="${classMap({ - "fxview-tab-row-favicon-wrapper": true, - pinned: this.indicators?.includes("pinned"), - pinnedOnNewTab: this.indicators?.includes("pinnedOnNewTab"), - attention: this.indicators?.includes("attention"), - bookmark: this.indicators?.includes("bookmark"), - })}" + class="fxview-tab-row-favicon icon" + id="fxview-tab-row-favicon" + style=${styleMap({ + backgroundImage: `url(${this.getImageUrl(this.favicon, this.url)})`, + })} + ></span>`; + } + + titleTemplate() { + const title = this.title; + return html`<span + class="fxview-tab-row-title text-truncated-ellipsis" + id="fxview-tab-row-title" + dir="auto" > - <span - class="fxview-tab-row-favicon icon" - id="fxview-tab-row-favicon" - style=${styleMap({ - backgroundImage: `url(${this.getImageUrl(this.favicon, this.url)})`, - })} - ></span> ${when( - this.pinnedTabsGridView && - this.indicators?.includes("pinned") && - (this.indicators?.includes("muted") || - this.indicators?.includes("soundplaying")), - () => html` - <button - class="fxview-tab-row-pinned-media-button ghost-button icon-button" - id="fxview-tab-row-media-button" - tabindex="-1" - data-l10n-id=${this.indicators?.includes("muted") - ? "fxviewtabrow-unmute-tab-button-no-context" - : "fxviewtabrow-mute-tab-button-no-context"} - muted=${this.indicators?.includes("muted")} - soundplaying=${this.indicators?.includes("soundplaying") && - !this.indicators?.includes("muted")} - @click=${this.muteOrUnmuteTab} - ></button> - ` + this.searchQuery, + () => this.highlightSearchMatches(this.searchQuery, title), + () => title )} </span>`; } - #pinnedTabItemTemplate() { - return html` <button - class="fxview-tab-row-main ghost-button semi-transparent" - id="fxview-tab-row-main" - aria-haspopup=${ifDefined(this.hasPopup)} - data-l10n-id=${ifDefined(this.primaryL10nId)} - data-l10n-args=${ifDefined(this.primaryL10nArgs)} - tabindex=${this.active && - this.currentActiveElementId === "fxview-tab-row-main" - ? "0" - : "-1"} - role="tab" - @click=${this.primaryActionHandler} - @keydown=${this.primaryActionHandler} - @contextmenu=${this.secondaryActionHandler} + urlTemplate() { + return html`<span + class="fxview-tab-row-url text-truncated-ellipsis" + id="fxview-tab-row-url" > - ${this.#faviconTemplate()} - </button>`; + ${when( + this.searchQuery, + () => + this.highlightSearchMatches( + this.searchQuery, + this.formatURIForDisplay(this.url) + ), + () => this.formatURIForDisplay(this.url) + )} + </span>`; } - #unpinnedTabItemTemplate() { - const title = this.title; + dateTemplate() { const relativeString = this.relativeTime( this.time, this.dateTimeFormat, @@ -815,11 +675,81 @@ export class FxviewTabRow extends MozLitElement { !window.IS_STORYBOOK ? this.timeMsPref : NOW_THRESHOLD_MS ); const dateArgs = this.dateFluentArgs(this.time, this.dateTimeFormat); + return html`<span class="fxview-tab-row-date" id="fxview-tab-row-date"> + <span + ?hidden=${relativeString || !dateString} + data-l10n-id=${ifDefined(dateString)} + data-l10n-args=${ifDefined(dateArgs)} + ></span> + <span ?hidden=${!relativeString}>${relativeString}</span> + </span>`; + } + + timeTemplate() { const timeString = this.timeFluentId(this.dateTimeFormat); const time = this.time; const timeArgs = JSON.stringify({ time }); + return html`<span + class="fxview-tab-row-time" + id="fxview-tab-row-time" + ?hidden=${!timeString} + data-timestamp=${ifDefined(this.time)} + data-l10n-id=${ifDefined(timeString)} + data-l10n-args=${ifDefined(timeArgs)} + > + </span>`; + } - return html`<a + secondaryButtonTemplate() { + return html`${when( + this.secondaryL10nId && this.secondaryActionHandler, + () => html`<moz-button + type="icon ghost" + class=${classMap({ + "fxview-tab-row-button": true, + [this.secondaryActionClass]: this.secondaryActionClass, + })} + id="fxview-tab-row-secondary-button" + data-l10n-id=${this.secondaryL10nId} + data-l10n-args=${ifDefined(this.secondaryL10nArgs)} + aria-haspopup=${ifDefined(this.hasPopup)} + @click=${this.secondaryActionHandler} + tabindex="${this.active && + this.currentActiveElementId === "fxview-tab-row-secondary-button" + ? "0" + : "-1"}" + ></moz-button>` + )}`; + } + + tertiaryButtonTemplate() { + return html`${when( + this.tertiaryL10nId && this.tertiaryActionHandler, + () => html`<moz-button + type="icon ghost" + class=${classMap({ + "fxview-tab-row-button": true, + [this.tertiaryActionClass]: this.tertiaryActionClass, + })} + id="fxview-tab-row-tertiary-button" + data-l10n-id=${this.tertiaryL10nId} + data-l10n-args=${ifDefined(this.tertiaryL10nArgs)} + aria-haspopup=${ifDefined(this.hasPopup)} + @click=${this.tertiaryActionHandler} + tabindex="${this.active && + this.currentActiveElementId === "fxview-tab-row-tertiary-button" + ? "0" + : "-1"}" + ></moz-button>` + )}`; + } +} + +export class FxviewTabRow extends FxviewTabRowBase { + render() { + return html` + ${this.stylesheets()} + <a href=${ifDefined(this.url)} class="fxview-tab-row-main" id="fxview-tab-row-main" @@ -833,176 +763,16 @@ export class FxviewTabRow extends MozLitElement { @keydown=${this.primaryActionHandler} title=${!this.primaryL10nId ? this.url : null} > - ${this.#faviconTemplate()} - <span - class="fxview-tab-row-title text-truncated-ellipsis" - id="fxview-tab-row-title" - dir="auto" - > - ${when( - this.searchQuery, - () => this.#highlightSearchMatches(this.searchQuery, title), - () => title - )} - </span> - <span class=${this.getContainerClasses().join(" ")}></span> - <span - class="fxview-tab-row-url text-truncated-ellipsis" - id="fxview-tab-row-url" - ?hidden=${this.compact} - > - ${when( - this.searchQuery, - () => - this.#highlightSearchMatches( - this.searchQuery, - this.formatURIForDisplay(this.url) - ), - () => this.formatURIForDisplay(this.url) - )} - </span> - <span - class="fxview-tab-row-date" - id="fxview-tab-row-date" - ?hidden=${this.compact} - > - <span - ?hidden=${relativeString || !dateString} - data-l10n-id=${ifDefined(dateString)} - data-l10n-args=${ifDefined(dateArgs)} - ></span> - <span ?hidden=${!relativeString}>${relativeString}</span> - </span> - <span - class="fxview-tab-row-time" - id="fxview-tab-row-time" - ?hidden=${this.compact || !timeString} - data-timestamp=${ifDefined(this.time)} - data-l10n-id=${ifDefined(timeString)} - data-l10n-args=${ifDefined(timeArgs)} - > - </span> + ${this.faviconTemplate()} ${this.titleTemplate()} + ${when( + !this.compact, + () => html`${this.urlTemplate()} ${this.dateTemplate()} + ${this.timeTemplate()}` + )} </a> - ${when( - this.indicators?.includes("soundplaying") || - this.indicators?.includes("muted"), - () => html`<button - class=fxview-tab-row-button ghost-button icon-button semi-transparent" - id="fxview-tab-row-media-button" - data-l10n-id=${ - this.indicators?.includes("muted") - ? "fxviewtabrow-unmute-tab-button-no-context" - : "fxviewtabrow-mute-tab-button-no-context" - } - muted=${this.indicators?.includes("muted")} - soundplaying=${ - this.indicators?.includes("soundplaying") && - !this.indicators?.includes("muted") - } - @click=${this.muteOrUnmuteTab} - tabindex="${ - this.active && - this.currentActiveElementId === "fxview-tab-row-media-button" - ? "0" - : "-1" - }" - ></button>`, - () => html`<span></span>` - )} - ${when( - this.secondaryL10nId && this.secondaryActionHandler, - () => html`<button - class=${classMap({ - "fxview-tab-row-button": true, - "ghost-button": true, - "icon-button": true, - "semi-transparent": true, - [this.secondaryActionClass]: this.secondaryActionClass, - })} - id="fxview-tab-row-secondary-button" - data-l10n-id=${this.secondaryL10nId} - data-l10n-args=${ifDefined(this.secondaryL10nArgs)} - aria-haspopup=${ifDefined(this.hasPopup)} - @click=${this.secondaryActionHandler} - tabindex="${this.active && - this.currentActiveElementId === "fxview-tab-row-secondary-button" - ? "0" - : "-1"}" - ></button>` - )} - ${when( - this.tertiaryL10nId && this.tertiaryActionHandler, - () => html`<button - class=${classMap({ - "fxview-tab-row-button": true, - "ghost-button": true, - "icon-button": true, - "semi-transparent": true, - [this.tertiaryActionClass]: this.tertiaryActionClass, - })} - id="fxview-tab-row-tertiary-button" - data-l10n-id=${this.tertiaryL10nId} - data-l10n-args=${ifDefined(this.tertiaryL10nArgs)} - aria-haspopup=${ifDefined(this.hasPopup)} - @click=${this.tertiaryActionHandler} - tabindex="${this.active && - this.currentActiveElementId === "fxview-tab-row-tertiary-button" - ? "0" - : "-1"}" - ></button>` - )}`; - } - - render() { - return html` - ${when( - this.containerObj, - () => html` - <link - rel="stylesheet" - href="chrome://browser/content/usercontext/usercontext.css" - /> - ` - )} - <link - rel="stylesheet" - href="chrome://global/skin/in-content/common.css" - /> - <link - rel="stylesheet" - href="chrome://browser/content/firefoxview/fxview-tab-row.css" - /> - ${when( - this.pinnedTabsGridView && this.indicators?.includes("pinned"), - this.#pinnedTabItemTemplate.bind(this), - this.#unpinnedTabItemTemplate.bind(this) - )} + ${this.secondaryButtonTemplate()} ${this.tertiaryButtonTemplate()} `; } - - /** - * Find all matches of query within the given string, and compute the result - * to be rendered. - * - * @param {string} query - * @param {string} string - */ - #highlightSearchMatches(query, string) { - const fragments = []; - const regex = RegExp(escapeRegExp(query), "dgi"); - let prevIndexEnd = 0; - let result; - while ((result = regex.exec(string)) !== null) { - const [indexStart, indexEnd] = result.indices[0]; - fragments.push(string.substring(prevIndexEnd, indexStart)); - fragments.push( - html`<strong>${string.substring(indexStart, indexEnd)}</strong>` - ); - prevIndexEnd = regex.lastIndex; - } - fragments.push(string.substring(prevIndexEnd)); - return fragments; - } } customElements.define("fxview-tab-row", FxviewTabRow); @@ -1040,10 +810,16 @@ export class VirtualList extends MozLitElement { this.isSubList = false; this.isVisible = false; this.intersectionObserver = new IntersectionObserver( - ([entry]) => (this.isVisible = entry.isIntersecting), + ([entry]) => { + this.isVisible = entry.isIntersecting; + }, { root: this.ownerDocument } ); - this.resizeObserver = new ResizeObserver(([entry]) => { + this.selfResizeObserver = new ResizeObserver(() => { + // Trigger the intersection observer once the tab rows have rendered + this.triggerIntersectionObserver(); + }); + this.childResizeObserver = new ResizeObserver(([entry]) => { if (entry.contentRect?.height > 0) { // Update properties on top-level virtual-list this.parentElement.itemHeightEstimate = entry.contentRect.height; @@ -1058,7 +834,8 @@ export class VirtualList extends MozLitElement { disconnectedCallback() { super.disconnectedCallback(); this.intersectionObserver.disconnect(); - this.resizeObserver.disconnect(); + this.childResizeObserver.disconnect(); + this.selfResizeObserver.disconnect(); } triggerIntersectionObserver() { @@ -1090,7 +867,6 @@ export class VirtualList extends MozLitElement { this.items.slice(i, i + this.maxRenderCountEstimate) ); } - this.triggerIntersectionObserver(); } } @@ -1103,13 +879,17 @@ export class VirtualList extends MozLitElement { firstUpdated() { this.intersectionObserver.observe(this); + this.selfResizeObserver.observe(this); if (this.isSubList && this.children[0]) { - this.resizeObserver.observe(this.children[0]); + this.childResizeObserver.observe(this.children[0]); } } updated(changedProperties) { this.updateListHeight(changedProperties); + if (changedProperties.has("items") && !this.isSubList) { + this.triggerIntersectionObserver(); + } } updateListHeight(changedProperties) { @@ -1157,5 +937,4 @@ export class VirtualList extends MozLitElement { return ""; } } - customElements.define("virtual-list", VirtualList); |