diff options
Diffstat (limited to 'comm/mail/components/unifiedtoolbar/content/unified-toolbar-tab.mjs')
-rw-r--r-- | comm/mail/components/unifiedtoolbar/content/unified-toolbar-tab.mjs | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/comm/mail/components/unifiedtoolbar/content/unified-toolbar-tab.mjs b/comm/mail/components/unifiedtoolbar/content/unified-toolbar-tab.mjs new file mode 100644 index 0000000000..134aec6cf1 --- /dev/null +++ b/comm/mail/components/unifiedtoolbar/content/unified-toolbar-tab.mjs @@ -0,0 +1,119 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Template ID: unifiedToolbarTabTemplate + * Attributes: + * - selected: If the tab is active. + * - aria-controls: The ID of the tab pane this controls. + * Events: + * - tabswitch: When the active tab is changed. + */ +class UnifiedToolbarTab extends HTMLElement { + /** + * @type {?HTMLButtonElement} + */ + #tab = null; + + connectedCallback() { + if (this.shadowRoot) { + return; + } + this.setAttribute("role", "presentation"); + const shadowRoot = this.attachShadow({ mode: "open" }); + + const template = document + .getElementById("unifiedToolbarTabTemplate") + .content.cloneNode(true); + this.#tab = template.querySelector("button"); + this.#tab.tabIndex = this.hasAttribute("selected") ? 0 : -1; + if (this.hasAttribute("selected")) { + this.#tab.setAttribute("aria-selected", "true"); + } + this.#tab.setAttribute("aria-controls", this.getAttribute("aria-controls")); + this.removeAttribute("aria-controls"); + + const styles = document.createElement("link"); + styles.setAttribute("rel", "stylesheet"); + styles.setAttribute( + "href", + "chrome://messenger/skin/shared/unifiedToolbarTab.css" + ); + + shadowRoot.append(styles, template); + + this.#tab.addEventListener("click", () => { + this.select(); + }); + this.#tab.addEventListener("keydown", this.#handleKey); + } + + #handleKey = event => { + const rightIsForward = document.dir === "ltr"; + const rightSibling = + (rightIsForward ? "next" : "previous") + "ElementSibling"; + const leftSibling = + (rightIsForward ? "previous" : "next") + "ElementSibling"; + switch (event.key) { + case "ArrowLeft": + this[leftSibling]?.focus(); + break; + case "ArrowRight": + this[rightSibling]?.focus(); + break; + case "Home": + this.parentNode.firstElementChild?.focus(); + break; + case "End": + this.parentNode.lastElementChild?.focus(); + break; + default: + return; + } + + event.stopPropagation(); + event.preventDefault(); + }; + + #toggleTabPane(visible) { + this.pane.hidden = !visible; + } + + /** + * Select this tab. Deselects the previously selected tab and shows the tab + * pane for this tab. + */ + select() { + this.parentElement + .querySelector("unified-toolbar-tab[selected]") + ?.unselect(); + this.#tab.setAttribute("aria-selected", "true"); + this.#tab.tabIndex = 0; + this.setAttribute("selected", true); + this.#toggleTabPane(true); + const tabSwitchEvent = new Event("tabswitch", { + bubbles: true, + }); + this.dispatchEvent(tabSwitchEvent); + } + + /** + * Remove the selection for this tab and hide the associated tab pane. + */ + unselect() { + this.#tab.removeAttribute("aria-selected"); + this.#tab.tabIndex = -1; + this.removeAttribute("selected"); + this.#toggleTabPane(false); + } + + focus() { + this.#tab.focus(); + } + + get pane() { + return document.getElementById(this.#tab.getAttribute("aria-controls")); + } +} +customElements.define("unified-toolbar-tab", UnifiedToolbarTab); |