/* 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/. */ import { html } from "chrome://global/content/vendor/lit.all.mjs"; import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; /** * A grouping of navigation buttons that is displayed at the page level, * intended to change the selected view, provide a heading, and have links * to external resources. * * @tagname moz-page-nav * @property {string} currentView - The currently selected view. * @property {string} heading - A heading to be displayed at the top of the navigation. * @slot [default] - Used to append moz-page-nav-button elements to the navigation. */ export default class MozPageNav extends MozLitElement { static properties = { currentView: { type: String }, heading: { type: String }, }; static queries = { headingEl: "#page-nav-header", primaryNavGroupSlot: ".primary-nav-group slot", secondaryNavGroupSlot: "#secondary-nav-group slot", }; get pageNavButtons() { return this.primaryNavGroupSlot .assignedNodes() .filter( node => node?.localName === "moz-page-nav-button" && !node.hidden ); } onChangeView(e) { this.currentView = e.target.view; } handleFocus(e) { if (e.key == "ArrowDown" || e.key == "ArrowRight") { e.preventDefault(); this.focusNextView(); } else if (e.key == "ArrowUp" || e.key == "ArrowLeft") { e.preventDefault(); this.focusPreviousView(); } } focusPreviousView() { let pageNavButtons = this.pageNavButtons; let currentIndex = pageNavButtons.findIndex(b => b.selected); let prev = pageNavButtons[currentIndex - 1]; if (prev) { prev.activate(); prev.buttonEl.focus(); } } focusNextView() { let pageNavButtons = this.pageNavButtons; let currentIndex = pageNavButtons.findIndex(b => b.selected); let next = pageNavButtons[currentIndex + 1]; if (next) { next.activate(); next.buttonEl.focus(); } } render() { return html` `; } updated() { let isViewSelected = false; let assignedPageNavButtons = this.pageNavButtons; for (let button of assignedPageNavButtons) { button.selected = button.view == this.currentView; isViewSelected = isViewSelected || button.selected; } if (!isViewSelected && assignedPageNavButtons.length) { // Current page nav has no matching view, reset to the first view. assignedPageNavButtons[0].activate(); } } } customElements.define("moz-page-nav", MozPageNav); /** * A navigation button intended to change the selected view within a page. * * @tagname moz-page-nav-button * @property {string} iconSrc - The chrome:// url for the icon used for the button. * @property {string} l10nId - The fluent ID for the button's text * @property {boolean} selected - Whether or not the button is currently selected. * @slot [default] - Used to append the l10n string to the button. */ export class MozPageNavButton extends MozLitElement { static properties = { iconSrc: { type: String }, l10nId: { type: String }, selected: { type: Boolean }, }; connectedCallback() { super.connectedCallback(); this.setAttribute("role", "none"); } static queries = { buttonEl: "button", }; get view() { return this.getAttribute("view"); } activate() { this.dispatchEvent( new CustomEvent("change-view", { bubbles: true, composed: true, }) ); } render() { return html` `; } } customElements.define("moz-page-nav-button", MozPageNavButton);