tor-browser

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

setting-pane.mjs (4189B)


      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 { MozLitElement } from "chrome://global/content/lit-utils.mjs";
      7 
      8 /**
      9 * @typedef {object} SettingPaneConfig
     10 * @property {string} [parent] The pane that links to this one.
     11 * @property {string} l10nId Fluent id for the heading/description.
     12 * @property {string[]} groupIds What setting groups should be rendered.
     13 * @property {string} [iconSrc] Optional icon shown in the page header.
     14 * @property {string} [module] Import path for module housing the config.
     15 * @property {() => boolean} [visible] If this pane is visible.
     16 */
     17 
     18 export class SettingPane extends MozLitElement {
     19  static properties = {
     20    name: { type: String },
     21    isSubPane: { type: Boolean },
     22    config: { type: Object },
     23  };
     24 
     25  static queries = {
     26    pageHeaderEl: "moz-page-header",
     27  };
     28 
     29  constructor() {
     30    super();
     31    /** @type {string} */
     32    this.name = undefined;
     33    /** @type {boolean} */
     34    this.isSubPane = false;
     35    /** @type {SettingPaneConfig} */
     36    this.config = undefined;
     37  }
     38 
     39  createRenderRoot() {
     40    return this;
     41  }
     42 
     43  async getUpdateComplete() {
     44    let result = await super.getUpdateComplete();
     45    // @ts-ignore bug 1997478
     46    await this.pageHeaderEl.updateComplete;
     47    return result;
     48  }
     49 
     50  goBack() {
     51    window.gotoPref(this.config.parent);
     52  }
     53 
     54  handleVisibility() {
     55    if (this.config.visible) {
     56      let visible = this.config.visible();
     57      if (!visible && !this.isSubPane) {
     58        let categoryButton = /** @type {XULElement} */ (
     59          document.querySelector(`#categories [value="${this.name}"]`)
     60        );
     61        if (categoryButton) {
     62          categoryButton.remove();
     63        }
     64        this.remove();
     65      }
     66    }
     67  }
     68 
     69  connectedCallback() {
     70    super.connectedCallback();
     71 
     72    this.handleVisibility();
     73 
     74    document.addEventListener(
     75      "paneshown",
     76      /**
     77       * @param {CustomEvent} e
     78       */
     79      e => {
     80        if (this.isSubPane && e.detail.category === this.name) {
     81          /**
     82           * Automatically focus to the first focusable element in the
     83           * sub page when it's accessed, which is always the Back Button.
     84           */
     85          this.pageHeaderEl.backButtonEl.focus();
     86          /**
     87           * ...but execute moveFocus here to move to the very
     88           * next focusable element after the Back button (if there is one).
     89           */
     90          Services.focus.moveFocus(
     91            window,
     92            null,
     93            Services.focus.MOVEFOCUS_FORWARD,
     94            Services.focus.FLAG_BYJS
     95          );
     96        }
     97      }
     98    );
     99    this.setAttribute("data-category", this.name);
    100    this.hidden = true;
    101    if (this.isSubPane) {
    102      this.setAttribute("data-hidden-from-search", "true");
    103      this.setAttribute("data-subpanel", "true");
    104      this._createCategoryButton();
    105    }
    106  }
    107 
    108  init() {
    109    if (!this.hasUpdated) {
    110      this.performUpdate();
    111    }
    112    if (this.config.module) {
    113      ChromeUtils.importESModule(this.config.module, { global: "current" });
    114    }
    115    for (let groupId of this.config.groupIds) {
    116      window.initSettingGroup(groupId);
    117    }
    118  }
    119 
    120  _createCategoryButton() {
    121    let categoryButton = document.createXULElement("richlistitem");
    122    categoryButton.classList.add("category");
    123    if (this.isSubPane) {
    124      categoryButton.classList.add("hidden-category");
    125    }
    126    categoryButton.setAttribute("value", this.name);
    127    document.getElementById("categories").append(categoryButton);
    128  }
    129 
    130  /** @param {string} groupId */
    131  groupTemplate(groupId) {
    132    return html`<setting-group groupid=${groupId}></setting-group>`;
    133  }
    134 
    135  render() {
    136    return html`
    137      <moz-page-header
    138        data-l10n-id=${this.config.l10nId}
    139        .iconSrc=${this.config.iconSrc}
    140        .backButton=${this.isSubPane}
    141        @navigate-back=${this.goBack}
    142      ></moz-page-header>
    143      ${this.config.groupIds.map(groupId => this.groupTemplate(groupId))}
    144    `;
    145  }
    146 }
    147 customElements.define("setting-pane", SettingPane);