/* 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, classMap } from "../vendor/lit.all.mjs"; import { MozBoxBase } from "../lit-utils.mjs"; import { GROUP_TYPES } from "chrome://global/content/elements/moz-box-group.mjs"; const DIRECTION_RIGHT = "Right"; const DIRECTION_LEFT = "Left"; const NAVIGATION_DIRECTIONS = { LTR: { FORWARD: DIRECTION_RIGHT, BACKWARD: DIRECTION_LEFT, }, RTL: { FORWARD: DIRECTION_LEFT, BACKWARD: DIRECTION_RIGHT, }, }; /** * A custom element used for highlighting important information and/or providing * context for specific settings. * * @tagname moz-box-item * @property {string} label - Label for the button. * @property {string} description - Descriptive text for the button. * @property {string} iconSrc - The src for an optional icon shown next to the label. * @property {string} layout - Layout style for the box content, either "default" or "large-icon". * @slot default - Slot for the box item's content, which overrides label and description. * @slot actions - Slot for the actions positioned at the end of the component container. * @slot actions-start - Slot for the actions positioned at the start of the component container. */ export default class MozBoxItem extends MozBoxBase { #actionEls = []; static properties = { layout: { type: String, reflect: true }, supportPage: { type: String, attribute: "support-page" }, }; static queries = { defaultSlotEl: "slot:not([name])", actionsStartSlotEl: "slot[name=actions-start]", actionsSlotEl: "slot[name=actions]", handleEl: ".handle", }; constructor() { super(); this.layout = "default"; this.addEventListener("keydown", e => this.handleKeydown(e)); } firstUpdated() { this.getActionEls(); } handleKeydown(event) { let isHandleEvent = event.originalTarget === this.handleEl; if ( !isHandleEvent && event.target?.slot !== "actions" && event.target?.slot !== "actions-start" ) { return; } let target = isHandleEvent ? event.originalTarget : event.target; let directions = this.getNavigationDirections(); switch (event.key) { case directions.FORWARD: case `Arrow${directions.FORWARD}`: { let nextIndex = this.#actionEls.indexOf(target) + 1; let nextEl = this.#actionEls[nextIndex]; nextEl?.focus(); break; } case directions.BACKWARD: case `Arrow${directions.BACKWARD}`: { let prevIndex = this.#actionEls.indexOf(target) - 1; let prevEl = this.#actionEls[prevIndex]; prevEl?.focus(); break; } } } getNavigationDirections() { if (this.isDocumentRTL) { return NAVIGATION_DIRECTIONS.RTL; } return NAVIGATION_DIRECTIONS.LTR; } get isDocumentRTL() { if (typeof Services !== "undefined") { return Services.locale.isAppLocaleRTL; } return document.dir === "rtl"; } get isDraggable() { return ( this.parentElement?.type == GROUP_TYPES.reorderable && this.slot != "header" && this.slot != "footer" ); } focus(event) { if (event?.key == "Up" || event?.key == "ArrowUp") { let actionEls = this.actionsSlotEl.assignedElements(); let lastActions = actionEls.length ? actionEls : this.actionsStartSlotEl?.assignedElements(); let lastAction = lastActions?.[lastActions.length - 1] ?? this.handleEl; lastAction?.focus(); } else { let firstAction = this.handleEl ?? this.actionsStartSlotEl?.assignedElements()?.[0] ?? this.actionsSlotEl.assignedElements()?.[0]; firstAction?.focus(); } } getActionEls() { let handleEl = this.handleEl ? [this.handleEl] : []; let startActions = this.actionsStartSlotEl?.assignedElements() ?? []; let endActions = this.actionsSlotEl.assignedElements(); this.#actionEls = [...handleEl, ...startActions, ...endActions]; } stylesTemplate() { return html`${super.stylesTemplate()} `; } slotTemplate(name) { return html` `; } textTemplate() { if (this.supportPage) { return this.supportTextTemplate(); } return super.textTemplate(); } supportTextTemplate() { return html`
${this.iconTemplate()} ${this.labelTemplate()}${!this.description ? this.supportPageTemplate() : ""} ${this.descriptionTemplate()}${this.description ? this.supportPageTemplate() : ""}
`; } supportPageTemplate() { if (this.supportPage) { return html``; } return ""; } render() { return html` ${this.stylesTemplate()}
${this.isDraggable ? html`` : ""} ${this.slotTemplate("actions-start")}
${this.label ? this.textTemplate() : html``}
${this.slotTemplate("actions")}
`; } } customElements.define("moz-box-item", MozBoxItem);