/* 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 "./search-bar.mjs"; // eslint-disable-line import/no-unassigned-import import "./customization-palette.mjs"; // eslint-disable-line import/no-unassigned-import import "./customization-target.mjs"; // eslint-disable-line import/no-unassigned-import import { BUTTON_STYLE_MAP, BUTTON_STYLE_PREF, } from "resource:///modules/ButtonStyle.mjs"; const { getDefaultItemIdsForSpace } = ChromeUtils.importESModule( "resource:///modules/CustomizableItems.sys.mjs" ); /** * Template ID: unifiedToolbarCustomizationPaneTemplate * Attributes: * - space: Identifier of the space this pane is for. Changes are not observed. * - current-items: Currently used items in this space. * - builtin-space: Boolean indicating if the space is a built in space (true) or an * extension provided space (false). */ class UnifiedToolbarCustomizationPane extends HTMLElement { /** * Reference to the customization target for the main toolbar area. * * @type {CustomizationTarget?} */ #toolbarTarget = null; /** * Reference to the title of the space specific palette. * * @type {?HTMLHeadingElement} */ #spaceSpecificTitle = null; /** * Reference to the palette for items only available in the current space. * * @type {?CustomizationPalette} */ #spaceSpecificPalette = null; /** * Reference to the palette for items available in all spaces. * * @type {?CustomizationPalette} */ #genericPalette = null; /** * List of the item IDs that are in the toolbar by default in this area. * * @type {string[]} */ #defaultItemIds = []; /** * The search bar used to filter the items in the palettes. * * @type {?SearchBar} */ #searchBar = null; connectedCallback() { if (this.shadowRoot) { document.l10n.connectRoot(this.shadowRoot); return; } this.setAttribute("role", "tabpanel"); const shadowRoot = this.attachShadow({ mode: "open" }); document.l10n.connectRoot(shadowRoot); const space = this.getAttribute("space"); const template = document .getElementById("unifiedToolbarCustomizationPaneTemplate") .content.cloneNode(true); const styles = document.createElement("link"); styles.setAttribute("rel", "stylesheet"); styles.setAttribute( "href", "chrome://messenger/skin/shared/unifiedToolbarCustomizationPane.css" ); this.#toolbarTarget = template.querySelector(".toolbar-target"); this.#toolbarTarget.setAttribute("space", space); this.#spaceSpecificTitle = template.querySelector(".space-specific-title"); document.l10n.setAttributes( this.#spaceSpecificTitle, this.hasAttribute("builtin-space") ? `customize-palette-${space}-specific-title` : "customize-palette-extension-specific-title" ); this.#spaceSpecificTitle.id = `${space}PaletteTitle`; this.#spaceSpecificPalette = template.querySelector( ".space-specific-palette" ); this.#spaceSpecificPalette.id = `${space}Palette`; this.#spaceSpecificPalette.setAttribute( "aria-labelledby", this.#spaceSpecificTitle.id ); this.#spaceSpecificPalette.setAttribute("space", space); const genericTitle = template.querySelector(".generic-palette-title"); genericTitle.id = `${space}GenericPaletteTitle`; this.#genericPalette = template.querySelector(".generic-palette"); this.#genericPalette.id = `${space}GenericPalette`; this.#genericPalette.setAttribute("aria-labelledby", genericTitle.id); this.#searchBar = template.querySelector("search-bar"); this.#searchBar.addEventListener("search", this.#handleSearch); this.#searchBar.addEventListener("autocomplete", this.#handleFilter); this.initialize(); shadowRoot.append(styles, template); this.addEventListener("dragover", this.#handleDragover); } disconnectedCallback() { document.l10n.disconnectRoot(this.shadowRoot); } #handleFilter = event => { this.#spaceSpecificPalette.filterItems(event.detail); this.#genericPalette.filterItems(event.detail); }; #handleSearch = event => { // Don't clear the search bar. event.preventDefault(); }; /** * Default handler to indicate nothing can be dropped in the customization, * except for the dragging and dropping in the palettes and targets. * * @param {DragEvent} event - Drag over event. */ #handleDragover = event => { event.dataTransfer.dropEffect = "none"; event.preventDefault(); }; /** * Initialize the contents of this element from the state. The relevant state * for this element are the items currently in the toolbar for this space. * * @param {boolean} [deep = false] - If true calls initialize on all the * targets and palettes. */ initialize(deep = false) { const space = this.getAttribute("space"); this.#defaultItemIds = getDefaultItemIdsForSpace(space); const currentItems = this.hasAttribute("current-items") ? this.getAttribute("current-items") : this.#defaultItemIds.join(","); this.#toolbarTarget.setAttribute("current-items", currentItems); this.#spaceSpecificPalette.setAttribute("items-in-use", currentItems); this.#genericPalette.setAttribute("items-in-use", currentItems); if (deep) { this.#searchBar.reset(); this.#toolbarTarget.initialize(); this.#spaceSpecificPalette.initialize(); this.#genericPalette.initialize(); this.#spaceSpecificTitle.hidden = this.#spaceSpecificPalette.isEmpty; this.#spaceSpecificPalette.hidden = this.#spaceSpecificPalette.isEmpty; } this.updateButtonStyle( BUTTON_STYLE_MAP[Services.prefs.getIntPref(BUTTON_STYLE_PREF, 0)] ); } /** * Reset the items in the targets to the defaults. */ reset() { this.#toolbarTarget.setItems(this.#defaultItemIds); this.#spaceSpecificPalette.setItems(this.#defaultItemIds); this.#genericPalette.setItems(this.#defaultItemIds); } /** * Add an item to the default target in this space. Can only add items that * are available in all spaces. * * @param {string} itemId - Item ID of the item to add to the default target. */ addItem(itemId) { this.#genericPalette.addItemById(itemId); } /** * Remove an item from all targets in this space. * * @param {string} itemId - Item ID of the item to remove from this pane's * targets. */ removeItem(itemId) { this.#toolbarTarget.removeItemById(itemId); } /** * Check if an item is currently in a target in this pane. * * @param {string} itemId - Item ID of the item to check for. * @returns {boolean} If the item is currently used in this pane. */ hasItem(itemId) { return Boolean(this.#toolbarTarget.hasItem(itemId)); } /** * If the customization state of this space matches its default state. * * @type {boolean} */ get matchesDefaultState() { const itemsInToolbar = this.#toolbarTarget.itemIds; return itemsInToolbar.join(",") === this.#defaultItemIds.join(","); } /** * If the customization state of this space matches the currently saved * configuration. * * @type {boolean} */ get hasChanges() { return this.#toolbarTarget.hasChanges; } /** * Current customization state for this space. * * @type {string[]} */ get itemIds() { return this.#toolbarTarget.itemIds; } /** * Update the class of the toolbar preview to reflect the selected button * style. * * @param {string} value - The class to apply. */ updateButtonStyle(value) { this.#toolbarTarget.classList.remove(...BUTTON_STYLE_MAP); this.#toolbarTarget.classList.add(value); } } customElements.define( "unified-toolbar-customization-pane", UnifiedToolbarCustomizationPane );