summaryrefslogtreecommitdiffstats
path: root/browser/components/firefoxview/fxview-tab-list.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/firefoxview/fxview-tab-list.mjs')
-rw-r--r--browser/components/firefoxview/fxview-tab-list.mjs496
1 files changed, 369 insertions, 127 deletions
diff --git a/browser/components/firefoxview/fxview-tab-list.mjs b/browser/components/firefoxview/fxview-tab-list.mjs
index 055540722a..978ab79724 100644
--- a/browser/components/firefoxview/fxview-tab-list.mjs
+++ b/browser/components/firefoxview/fxview-tab-list.mjs
@@ -45,8 +45,11 @@ 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} 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 {
constructor() {
@@ -59,6 +62,9 @@ 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.updatesPaused = true;
this.#register();
@@ -71,9 +77,12 @@ 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 },
+ secondaryActionClass: { type: String },
+ tertiaryActionClass: { type: String },
};
static queries = {
@@ -99,8 +108,20 @@ export default class FxviewTabList extends MozLitElement {
}
}
- if (this.maxTabsLength > 0) {
+ // 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) {
this.tabItems = this.tabItems.slice(0, this.maxTabsLength);
}
}
@@ -176,56 +197,93 @@ 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();
- this.focusPrevRow();
+ if (
+ (this.pinnedTabsGridView &&
+ this.activeIndex >= this.pinnedTabs.length) ||
+ !this.pinnedTabsGridView
+ ) {
+ this.focusPrevRow();
+ }
} else if (e.code == "ArrowDown") {
// Focus either the link or button of the next row based on this.currentActiveElementId
e.preventDefault();
- this.focusNextRow();
+ if (
+ this.pinnedTabsGridView &&
+ this.activeIndex < this.pinnedTabs.length
+ ) {
+ this.focusIndex(this.pinnedTabs.length);
+ } else {
+ 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") {
- if (
- (fxviewTabRow.soundPlaying || fxviewTabRow.muted) &&
- this.currentActiveElementId === "fxview-tab-row-secondary-button"
- ) {
- this.currentActiveElementId = fxviewTabRow.focusMediaButton();
- } else {
- this.currentActiveElementId = fxviewTabRow.focusLink();
- }
- } else if (
- (fxviewTabRow.soundPlaying || fxviewTabRow.muted) &&
- this.currentActiveElementId === "fxview-tab-row-main"
- ) {
- this.currentActiveElementId = fxviewTabRow.focusMediaButton();
+ this.moveFocusLeft(fxviewTabRow);
} else {
- this.currentActiveElementId = fxviewTabRow.focusButton();
+ this.moveFocusRight(fxviewTabRow);
}
} 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") {
- if (
- (fxviewTabRow.soundPlaying || fxviewTabRow.muted) &&
- this.currentActiveElementId === "fxview-tab-row-main"
- ) {
- this.currentActiveElementId = fxviewTabRow.focusMediaButton();
- } else {
- this.currentActiveElementId = fxviewTabRow.focusButton();
- }
- } else if (
- (fxviewTabRow.soundPlaying || fxviewTabRow.muted) &&
- this.currentActiveElementId === "fxview-tab-row-secondary-button"
- ) {
- this.currentActiveElementId = fxviewTabRow.focusMediaButton();
+ this.moveFocusRight(fxviewTabRow);
} else {
- this.currentActiveElementId = fxviewTabRow.focusLink();
+ this.moveFocusLeft(fxviewTabRow);
}
}
}
+ 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);
}
@@ -236,12 +294,18 @@ export default class FxviewTabList extends MozLitElement {
async focusIndex(index) {
// Focus link or button of item
- if (lazy.virtualListEnabledPref) {
- let row = this.rootVirtualListEl.getItem(index);
+ if (
+ ((this.pinnedTabsGridView && index > this.pinnedTabs.length) ||
+ !this.pinnedTabsGridView) &&
+ lazy.virtualListEnabledPref
+ ) {
+ let row = this.rootVirtualListEl.getItem(index - this.pinnedTabs.length);
if (!row) {
return;
}
- let subList = this.rootVirtualListEl.getSubListForItem(index);
+ let subList = this.rootVirtualListEl.getSubListForItem(
+ index - this.pinnedTabs.length
+ );
if (!subList) {
return;
}
@@ -286,23 +350,30 @@ export default class FxviewTabList extends MozLitElement {
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=${tabItem.containerObj}
+ .containerObj=${ifDefined(tabItem.containerObj)}
.currentActiveElementId=${this.currentActiveElementId}
.dateTimeFormat=${this.dateTimeFormat}
.favicon=${tabItem.icon}
- .isBookmark=${ifDefined(tabItem.isBookmark)}
- .muted=${ifDefined(tabItem.muted)}
- .pinned=${ifDefined(tabItem.pinned)}
+ .indicators=${ifDefined(tabItem.indicators)}
+ .pinnedTabsGridView=${ifDefined(this.pinnedTabsGridView)}
.primaryL10nId=${tabItem.primaryL10nId}
.primaryL10nArgs=${ifDefined(tabItem.primaryL10nArgs)}
- role="listitem"
+ role=${this.pinnedTabsGridView && tabItem.indicators?.includes("pinned")
+ ? "none"
+ : "listitem"}
.secondaryL10nId=${tabItem.secondaryL10nId}
.secondaryL10nArgs=${ifDefined(tabItem.secondaryL10nArgs)}
- .attention=${ifDefined(tabItem.attention)}
- .soundPlaying=${ifDefined(tabItem.soundPlaying)}
+ .tertiaryL10nId=${ifDefined(tabItem.tertiaryL10nId)}
+ .tertiaryL10nArgs=${ifDefined(tabItem.tertiaryL10nArgs)}
+ .secondaryActionClass=${this.secondaryActionClass}
+ .tertiaryActionClass=${ifDefined(this.tertiaryActionClass)}
.sourceClosedId=${ifDefined(tabItem.sourceClosedId)}
.sourceWindowId=${ifDefined(tabItem.sourceWindowId)}
.closedId=${ifDefined(tabItem.closedId || tabItem.closedId)}
@@ -311,7 +382,6 @@ export default class FxviewTabList extends MozLitElement {
.time=${ifDefined(time)}
.timeMsPref=${ifDefined(this.timeMsPref)}
.title=${tabItem.title}
- .titleChanged=${ifDefined(tabItem.titleChanged)}
.url=${tabItem.url}
></fxview-tab-row>
`;
@@ -326,9 +396,26 @@ export default class FxviewTabList extends MozLitElement {
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>
+ `
+ )}
<div
id="fxview-tab-list"
class="fxview-tab-list"
+ data-l10n-id="firefoxview-tabs"
role="list"
@keydown=${this.handleFocusElementInRow}
>
@@ -337,7 +424,12 @@ export default class FxviewTabList extends MozLitElement {
() => html`
<virtual-list
.activeIndex=${this.activeIndex}
- .items=${this.tabItems}
+ .pinnedTabsIndexOffset=${this.pinnedTabsGridView
+ ? this.pinnedTabs.length
+ : 0}
+ .items=${this.pinnedTabsGridView
+ ? this.unpinnedTabs
+ : this.tabItems}
.template=${this.itemTemplate}
></virtual-list>
`
@@ -374,23 +466,23 @@ customElements.define("fxview-tab-list", FxviewTabList);
* @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 {boolean} isBookmark - Whether an open tab is bookmarked
+ * @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} muted - Whether an open tab is muted
- * @property {boolean} pinned - Whether an open tab is pinned
+ * @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
* @property {string} secondaryL10nArgs - The l10n args used for the secondary action element
- * @property {boolean} attention - Whether to show a notification dot
- * @property {boolean} soundPlaying - Whether an open tab has soundPlaying
+ * @property {string} secondaryActionClass - The class used to style the secondary action element
+ * @property {string} tertiaryL10nId - The l10n id used for the tertiary action button
+ * @property {string} tertiaryL10nArgs - The l10n args used for the tertiary action element
+ * @property {string} tertiaryActionClass - The class used to style the tertiary action element
* @property {object} tabElement - The MozTabbrowserTab element for the tab item.
* @property {number} time - The timestamp for when the tab was last accessed.
* @property {string} title - The title for the tab item.
- * @property {boolean} titleChanged - Whether the title has changed for an open tab
* @property {string} url - The url for the tab item.
* @property {number} timeMsPref - The frequency in milliseconds of updates to relative time
* @property {string} searchQuery - The query string to highlight, if provided.
@@ -410,31 +502,33 @@ export class FxviewTabRow extends MozLitElement {
dateTimeFormat: { type: String },
favicon: { type: String },
hasPopup: { type: String },
- isBookmark: { type: Boolean },
- muted: { type: Boolean },
- pinned: { type: Boolean },
+ indicators: { type: Array },
+ pinnedTabsGridView: { type: Boolean },
primaryL10nId: { type: String },
primaryL10nArgs: { type: String },
secondaryL10nId: { type: String },
secondaryL10nArgs: { type: String },
- soundPlaying: { type: Boolean },
+ secondaryActionClass: { type: String },
+ tertiaryL10nId: { type: String },
+ tertiaryL10nArgs: { type: String },
+ tertiaryActionClass: { type: String },
closedId: { type: Number },
sourceClosedId: { type: Number },
sourceWindowId: { type: String },
tabElement: { type: Object },
time: { type: Number },
title: { type: String },
- titleChanged: { type: Boolean },
- attention: { type: Boolean },
timeMsPref: { type: Number },
url: { type: String },
searchQuery: { type: String },
};
static queries = {
- mainEl: ".fxview-tab-row-main",
- buttonEl: "#fxview-tab-row-secondary-button:not([hidden])",
+ 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() {
@@ -445,13 +539,40 @@ 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();
}
- focusButton() {
- this.buttonEl.focus();
- return this.buttonEl.id;
+ focusSecondaryButton() {
+ this.secondaryButtonEl.focus();
+ return this.secondaryButtonEl.id;
+ }
+
+ focusTertiaryButton() {
+ this.tertiaryButtonEl.focus();
+ return this.tertiaryButtonEl.id;
}
focusMediaButton() {
@@ -562,6 +683,9 @@ 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)
@@ -577,12 +701,108 @@ export class FxviewTabRow extends MozLitElement {
}
}
- muteOrUnmuteTab() {
+ tertiaryActionHandler(event) {
+ if (
+ (event.type == "click" && event.detail && !event.altKey) ||
+ // detail=0 is from keyboard
+ (event.type == "click" && !event.detail)
+ ) {
+ event.preventDefault();
+ this.dispatchEvent(
+ new CustomEvent("fxview-tab-list-tertiary-action", {
+ bubbles: true,
+ composed: true,
+ detail: { originalEvent: event, item: this },
+ })
+ );
+ }
+ }
+
+ 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);
+ }
+ }
this.tabElement.toggleMuteAudio();
- this.muted = !this.muted;
}
- render() {
+ #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"),
+ })}"
+ >
+ <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>
+ `
+ )}
+ </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}
+ >
+ ${this.#faviconTemplate()}
+ </button>`;
+ }
+
+ #unpinnedTabItemTemplate() {
const title = this.title;
const relativeString = this.relativeTime(
this.time,
@@ -598,25 +818,8 @@ export class FxviewTabRow extends MozLitElement {
const timeString = this.timeFluentId(this.dateTimeFormat);
const time = this.time;
const timeArgs = JSON.stringify({ time });
- 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"
- />
- <a
+
+ return html`<a
href=${ifDefined(this.url)}
class="fxview-tab-row-main"
id="fxview-tab-row-main"
@@ -628,29 +831,9 @@ export class FxviewTabRow extends MozLitElement {
data-l10n-args=${ifDefined(this.primaryL10nArgs)}
@click=${this.primaryActionHandler}
@keydown=${this.primaryActionHandler}
+ title=${!this.primaryL10nId ? this.url : null}
>
- <span
- class="${classMap({
- "fxview-tab-row-favicon-wrapper": true,
- bookmark: this.isBookmark && !this.attention,
- notification: this.pinned
- ? this.attention || this.titleChanged
- : this.attention,
- soundplaying: this.soundPlaying && !this.muted && this.pinned,
- muted: this.muted && this.pinned,
- })}"
- >
- <span
- class="fxview-tab-row-favicon icon"
- id="fxview-tab-row-favicon"
- style=${styleMap({
- backgroundImage: `url(${this.getImageUrl(
- this.favicon,
- this.url
- )})`,
- })}
- ></span>
- </span>
+ ${this.#faviconTemplate()}
<span
class="fxview-tab-row-title text-truncated-ellipsis"
id="fxview-tab-row-title"
@@ -701,34 +884,42 @@ export class FxviewTabRow extends MozLitElement {
</span>
</a>
${when(
- (this.soundPlaying || this.muted) && !this.pinned,
+ 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.muted
- ? "fxviewtabrow-unmute-tab-button"
- : "fxviewtabrow-mute-tab-button"
- }
- data-l10n-args=${JSON.stringify({ tabTitle: title })}
- muted=${ifDefined(this.muted)}
- soundplaying=${this.soundPlaying && !this.muted}
- @click=${this.muteOrUnmuteTab}
- tabindex="${
- this.active &&
- this.currentActiveElementId === "fxview-tab-row-media-button"
- ? "0"
- : "-1"
- }"
- ></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="fxview-tab-row-button ghost-button icon-button semi-transparent"
+ 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"
- part="secondary-button"
data-l10n-id=${this.secondaryL10nId}
data-l10n-args=${ifDefined(this.secondaryL10nArgs)}
aria-haspopup=${ifDefined(this.hasPopup)}
@@ -739,6 +930,53 @@ export class FxviewTabRow extends MozLitElement {
: "-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)
+ )}
`;
}
@@ -780,6 +1018,7 @@ export class VirtualList extends MozLitElement {
isAlwaysVisible: { type: Boolean },
isVisible: { type: Boolean, state: true },
isSubList: { type: Boolean },
+ pinnedTabsIndexOffset: { type: Number },
};
createRenderRoot() {
@@ -790,6 +1029,7 @@ export class VirtualList extends MozLitElement {
super();
this.activeIndex = 0;
this.itemOffset = 0;
+ this.pinnedTabsIndexOffset = 0;
this.items = [];
this.subListItems = [];
this.itemHeightEstimate = FXVIEW_ROW_HEIGHT_PX;
@@ -893,14 +1133,16 @@ export class VirtualList extends MozLitElement {
.template=${this.template}
.items=${data}
.itemHeightEstimate=${this.itemHeightEstimate}
- .itemOffset=${i * this.maxRenderCountEstimate}
+ .itemOffset=${i * this.maxRenderCountEstimate +
+ this.pinnedTabsIndexOffset}
.isAlwaysVisible=${i ==
parseInt(this.activeIndex / this.maxRenderCountEstimate, 10)}
isSubList
></virtual-list>`;
};
- itemTemplate = (data, i) => this.template(data, this.itemOffset + i);
+ itemTemplate = (data, i) =>
+ this.template(data, this.itemOffset + i + this.pinnedTabsIndexOffset);
render() {
if (this.isAlwaysVisible || this.isVisible) {