tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

setting-group.mjs (5012B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import { html } from "chrome://global/content/vendor/lit.all.mjs";
      6 import {
      7  SettingElement,
      8  spread,
      9 } from "chrome://browser/content/preferences/widgets/setting-element.mjs";
     10 
     11 /** @import { SettingElementConfig } from "chrome://browser/content/preferences/widgets/setting-element.mjs" */
     12 /** @import { SettingControlConfig, SettingControlEvent } from "../setting-control/setting-control.mjs" */
     13 /** @import { Preferences } from "chrome://global/content/preferences/Preferences.mjs" */
     14 
     15 /**
     16 * @typedef {object} SettingGroupConfigExtensions
     17 * @property {SettingControlConfig[]} items Array of SettingControlConfigs to render.
     18 * @property {number} [headingLevel] A heading level to create the legend as (1-6).
     19 * @property {boolean} [inProgress]
     20 * Hide this section unless the browser.settings-redesign.enabled or
     21 * browser.settings-redesign.<groupid>.enabled prefs are true.
     22 */
     23 /** @typedef {SettingElementConfig & SettingGroupConfigExtensions} SettingGroupConfig */
     24 
     25 const CLICK_HANDLERS = new Set([
     26  "dialog-button",
     27  "moz-box-button",
     28  "moz-box-item",
     29  "moz-box-link",
     30  "moz-button",
     31  "moz-box-group",
     32  "moz-message-bar",
     33 ]);
     34 
     35 /**
     36 * Enumish of attribute names used for changing setting-group and groupbox
     37 * visibilities based on the visibility of child setting-controls.
     38 */
     39 const HiddenAttr = Object.freeze({
     40  /** Attribute used to hide elements without using the hidden attribute. */
     41  Self: "data-hidden-by-setting-group",
     42  /** Attribute used to signal that this element should not be searchable. */
     43  Search: "data-hidden-from-search",
     44 });
     45 
     46 export class SettingGroup extends SettingElement {
     47  constructor() {
     48    super();
     49 
     50    /**
     51     * @type {Preferences['getSetting'] | undefined}
     52     */
     53    this.getSetting = undefined;
     54 
     55    /**
     56     * @type {SettingGroupConfig | undefined}
     57     */
     58    this.config = undefined;
     59  }
     60 
     61  static properties = {
     62    config: { type: Object },
     63    groupId: { type: String },
     64    getSetting: { type: Function },
     65  };
     66 
     67  static queries = {
     68    controlEls: { all: "setting-control" },
     69  };
     70 
     71  createRenderRoot() {
     72    return this;
     73  }
     74 
     75  async handleVisibilityChange() {
     76    await this.updateComplete;
     77    // @ts-expect-error bug 1997478
     78    let hasVisibleControls = [...this.controlEls].some(el => !el.hidden);
     79    let groupbox = /** @type {XULElement} */ (this.closest("groupbox"));
     80    if (hasVisibleControls) {
     81      if (this.hasAttribute(HiddenAttr.Self)) {
     82        this.removeAttribute(HiddenAttr.Self);
     83        this.removeAttribute(HiddenAttr.Search);
     84      }
     85      if (groupbox && groupbox.hasAttribute(HiddenAttr.Self)) {
     86        groupbox.removeAttribute(HiddenAttr.Search);
     87        groupbox.removeAttribute(HiddenAttr.Self);
     88      }
     89    } else {
     90      this.setAttribute(HiddenAttr.Self, "");
     91      this.setAttribute(HiddenAttr.Search, "true");
     92      if (groupbox && !groupbox.hasAttribute(HiddenAttr.Search)) {
     93        groupbox.setAttribute(HiddenAttr.Search, "true");
     94        groupbox.setAttribute(HiddenAttr.Self, "");
     95      }
     96    }
     97  }
     98 
     99  async getUpdateComplete() {
    100    let result = await super.getUpdateComplete();
    101    // @ts-expect-error bug 1997478
    102    await Promise.all([...this.controlEls].map(el => el.updateComplete));
    103    return result;
    104  }
    105 
    106  /**
    107   * Notify child controls when their input has fired an event. When controls
    108   * are nested the parent receives events for the nested controls, so this is
    109   * actually easier to manage here; it also registers fewer listeners.
    110   *
    111   * @param {SettingControlEvent<InputEvent>} e
    112   */
    113  onChange(e) {
    114    let inputEl = e.target;
    115    inputEl.control?.onChange(inputEl);
    116  }
    117 
    118  /**
    119   * Notify child controls when their input has been clicked. When controls
    120   * are nested the parent receives events for the nested controls, so this is
    121   * actually easier to manage here; it also registers fewer listeners.
    122   *
    123   * @param {SettingControlEvent<MouseEvent>} e
    124   */
    125  onClick(e) {
    126    let inputEl = e.target;
    127    if (!CLICK_HANDLERS.has(inputEl.localName)) {
    128      return;
    129    }
    130    inputEl.control?.onClick(e);
    131  }
    132 
    133  /**
    134   * @param {SettingControlConfig} item
    135   */
    136  itemTemplate(item) {
    137    let setting = this.getSetting(item.id);
    138    return html`<setting-control
    139      .setting=${setting}
    140      .config=${item}
    141      .getSetting=${this.getSetting}
    142    ></setting-control>`;
    143  }
    144 
    145  render() {
    146    if (!this.config) {
    147      return "";
    148    }
    149    return html`<moz-fieldset
    150      .headingLevel=${this.config.headingLevel}
    151      @change=${this.onChange}
    152      @toggle=${this.onChange}
    153      @click=${this.onClick}
    154      @visibility-change=${this.handleVisibilityChange}
    155      ${spread(this.getCommonPropertyMapping(this.config))}
    156      >${this.config.items.map(item => this.itemTemplate(item))}</moz-fieldset
    157    >`;
    158  }
    159 }
    160 customElements.define("setting-group", SettingGroup);