chromium/chrome/browser/resources/settings/privacy_page/collapse_radio_button.ts

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'chrome://resources/cr_elements/cr_collapse/cr_collapse.js';
import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button_style.css.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import '../settings_shared.css.js';

import type {CrExpandButtonElement} from 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
import {CrRadioButtonMixin} from 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button_mixin.js';
import {assert} from 'chrome://resources/js/assert.js';
import {PaperRippleMixin} from 'chrome://resources/polymer/v3_0/paper-behaviors/paper-ripple-mixin.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

import {getTemplate} from './collapse_radio_button.html.js';

export interface SettingsCollapseRadioButtonElement {
  $: {
    expandButton: CrExpandButtonElement,
  };
}
const SettingsCollapseRadioButtonElementBase =
    PaperRippleMixin(CrRadioButtonMixin(PolymerElement));

export class SettingsCollapseRadioButtonElement extends
    SettingsCollapseRadioButtonElementBase {
  static get is() {
    return 'settings-collapse-radio-button';
  }

  static get template() {
    return getTemplate();
  }

  static get properties() {
    return {
      expanded: {
        type: Boolean,
        notify: true,
        value: false,
      },

      noAutomaticCollapse: {
        type: Boolean,
        value: false,
      },

      noCollapse: Boolean,

      label: String,

      indicatorAriaLabel: String,

      icon: {
        type: String,
        value: null,
      },

      /*
       * The Preference associated with the radio group.
       */
      pref: Object,

      disabled: {
        type: Boolean,
        value: false,
        reflectToAttribute: true,
      },

      subLabel: {
        type: String,
        value: '',  // Allows the $hidden= binding to run without being set.
      },

      /*
       * The aria-label attribute associated with the expand button. Used by
       * screen readers when announcing the expand button.
       */
      expandAriaLabel: String,
    };
  }

  static get observers() {
    return [
      'onCheckedChanged_(checked)',
      'onPrefChanged_(pref.*)',
    ];
  }

  expanded: boolean;
  noAutomaticCollapse: boolean;
  noCollapse: boolean;
  override label: string;
  indicatorAriaLabel: string;
  icon: string;
  pref?: chrome.settingsPrivate.PrefObject;
  override disabled: boolean;
  subLabel: string;
  expandAriaLabel: string;
  private pendingUpdateCollapsed_: boolean;

  constructor() {
    super();

    /**
     * Tracks if this button was clicked but wasn't expanded.
     */
    this.pendingUpdateCollapsed_ = false;
  }

  // Overridden from CrRadioButtonMixin
  override getPaperRipple() {
    return this.getRipple();
  }

  // Overridden from PaperRippleMixin
  /* eslint-disable-next-line @typescript-eslint/naming-convention */
  override _createRipple() {
    this._rippleContainer = this.shadowRoot!.querySelector('.disc-wrapper');
    const ripple = super._createRipple();
    ripple.id = 'ink';
    ripple.setAttribute('recenters', '');
    ripple.classList.add('circle');
    return ripple;
  }

  /**
   * Updates the collapsed status of this radio button to reflect
   * the user selection actions.
   */
  updateCollapsed() {
    if (this.pendingUpdateCollapsed_) {
      this.pendingUpdateCollapsed_ = false;
      this.expanded = this.checked;
    }
  }

  getBubbleAnchor() {
    const anchor = this.shadowRoot!.querySelector<HTMLElement>('#button');
    assert(anchor);
    return anchor;
  }

  private onCheckedChanged_() {
    this.pendingUpdateCollapsed_ = true;
    if (!this.noAutomaticCollapse) {
      this.updateCollapsed();
    }
  }

  private onPrefChanged_() {
    // If the preference has been set, and is managed, this control should be
    // disabled. Unless the value associated with this control is present in
    // |pref.userSelectableValues|. This will override the disabled set on the
    // element externally.
    this.disabled = !!this.pref &&
        this.pref.enforcement === chrome.settingsPrivate.Enforcement.ENFORCED &&
        !(!!this.pref.userSelectableValues &&
          this.pref.userSelectableValues.includes(this.name));
  }

  private onExpandClicked_() {
    this.dispatchEvent(
        new CustomEvent('expand-clicked', {bubbles: true, composed: true}));
  }

  private onRadioFocus_() {
    this.getRipple().showAndHoldDown();
  }

  /**
   * Clear the ripple associated with the radio button when the expand button
   * is focused. Stop propagation to prevent the ripple being re-created.
   */
  private onNonRadioFocus_(e: Event) {
    this.getRipple().clear();
    e.stopPropagation();
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'settings-collapse-radio-button': SettingsCollapseRadioButtonElement;
  }
}

customElements.define(
    SettingsCollapseRadioButtonElement.is, SettingsCollapseRadioButtonElement);