| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| * @fileoverview |
| * `settings-toggle-button` is a toggle that controls a supplied preference. |
| */ |
| import '//resources/cr_elements/cr_actionable_row_style.css.js'; |
| import '//resources/cr_elements/cr_shared_style.css.js'; |
| import '//resources/cr_elements/cr_shared_vars.css.js'; |
| import '//resources/cr_elements/action_link.css.js'; |
| import '//resources/cr_elements/cr_toggle/cr_toggle.js'; |
| import '/shared/settings/controls/cr_policy_pref_indicator.js'; |
| import '//resources/cr_elements/cr_icon/cr_icon.js'; |
| |
| import type {CrToggleElement} from '//resources/cr_elements/cr_toggle/cr_toggle.js'; |
| import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js'; |
| import {SettingsBooleanControlMixin} from '/shared/settings/controls/settings_boolean_control_mixin.js'; |
| import {assert} from 'chrome://resources/js/assert.js'; |
| import {sanitizeInnerHtml} from 'chrome://resources/js/parse_html_subset.js'; |
| |
| import {getTemplate} from './settings_toggle_button.html.js'; |
| |
| |
| export interface SettingsToggleButtonElement { |
| $: { |
| control: CrToggleElement, |
| labelWrapper: HTMLElement, |
| }; |
| } |
| |
| const SettingsToggleButtonElementBase = |
| SettingsBooleanControlMixin(PolymerElement); |
| |
| export class SettingsToggleButtonElement extends |
| SettingsToggleButtonElementBase { |
| static get is() { |
| return 'settings-toggle-button'; |
| } |
| |
| static get template() { |
| return getTemplate(); |
| } |
| |
| static get properties() { |
| return { |
| ariaLabel: { |
| type: String, |
| reflectToAttribute: false, // Handled by #control. |
| observer: 'onAriaLabelSet_', |
| value: '', |
| }, |
| |
| ariaShowLabel: { |
| type: Boolean, |
| reflectToAttribute: true, |
| value: false, |
| }, |
| |
| ariaShowSublabel: { |
| type: Boolean, |
| reflectToAttribute: true, |
| value: false, |
| }, |
| |
| elideLabel: { |
| type: Boolean, |
| reflectToAttribute: true, |
| }, |
| |
| learnMoreUrl: { |
| type: String, |
| reflectToAttribute: true, |
| }, |
| |
| subLabelWithLink: { |
| type: String, |
| reflectToAttribute: true, |
| }, |
| |
| learnMoreAriaLabel: { |
| type: String, |
| value: '', |
| }, |
| |
| icon: String, |
| |
| subLabelIcon: String, |
| |
| /** |
| * If true, the host element does not get a click event handler and the |
| * client is responsible for determining their own click logic. Thus when |
| * true, clicking on the setting row does not toggle the setting pref. |
| * Note, this boolean is only used on ready() callback, and any changes |
| * after that have no effect. |
| */ |
| noToggleOnHostClick: { |
| type: Boolean, |
| value: false, |
| }, |
| }; |
| } |
| |
| static get observers() { |
| return [ |
| 'onDisableOrPrefChange_(disabled, pref.*)', |
| ]; |
| } |
| |
| declare ariaLabel: string; |
| declare ariaShowLabel: boolean; |
| declare ariaShowSublabel: boolean; |
| declare elideLabel: boolean; |
| declare icon: string; |
| declare learnMoreAriaLabel: string; |
| declare learnMoreUrl: string; |
| declare subLabelWithLink: string; |
| declare subLabelIcon: string; |
| declare noToggleOnHostClick: boolean; |
| |
| override ready() { |
| super.ready(); |
| |
| // If the settings toggle is noToggleOnHostClick then do not update the |
| // setting pref on click. Instead let parent code use a custom click |
| // handler as needed. |
| if (!this.noToggleOnHostClick) { |
| this.addEventListener('click', this.onHostClick_); |
| } |
| } |
| |
| private fire_(eventName: string, detail?: any) { |
| this.dispatchEvent( |
| new CustomEvent(eventName, {detail, bubbles: true, composed: true})); |
| } |
| |
| override focus() { |
| this.$.control.focus(); |
| } |
| |
| /** |
| * Removes the aria-label attribute if it's added by $i18n{...}. |
| */ |
| private onAriaLabelSet_() { |
| if (this.hasAttribute('aria-label')) { |
| const ariaLabel = this.ariaLabel; |
| this.removeAttribute('aria-label'); |
| this.ariaLabel = ariaLabel; |
| } |
| } |
| |
| private getAriaLabel_(): string { |
| return this.ariaLabel || this.label; |
| } |
| |
| private getLearnMoreAriaLabelledBy_(): string { |
| return this.learnMoreAriaLabel ? 'learn-more-aria-label' : |
| 'sub-label-text learn-more'; |
| } |
| |
| getBubbleAnchor() { |
| const anchor = this.shadowRoot!.querySelector<HTMLElement>('#control'); |
| assert(anchor); |
| return anchor; |
| } |
| |
| private onDisableOrPrefChange_() { |
| this.toggleAttribute('effectively-disabled_', this.controlDisabled()); |
| } |
| |
| /** |
| * Handles non cr-toggle button clicks (cr-toggle handles its own click events |
| * which don't bubble). |
| */ |
| private onHostClick_(e: Event) { |
| assert(!this.noToggleOnHostClick); |
| e.stopPropagation(); |
| if (this.controlDisabled()) { |
| return; |
| } |
| this.updateCheckedAndNotify_(!this.checked); |
| } |
| |
| private onLearnMoreClick_(e: CustomEvent<boolean>) { |
| e.stopPropagation(); |
| this.fire_('learn-more-clicked'); |
| } |
| |
| /** |
| * Set up the contents of sub label with link. |
| */ |
| private getSubLabelWithLinkContent_(contents: string) { |
| return sanitizeInnerHtml(contents, { |
| attrs: [ |
| 'id', |
| 'is', |
| 'aria-description', |
| 'aria-hidden', |
| 'aria-label', |
| 'aria-labelledby', |
| 'tabindex', |
| ], |
| }); |
| } |
| |
| private onSubLabelTextWithLinkClick_(e: Event) { |
| const target = e.target as HTMLElement; |
| if (target.tagName === 'A') { |
| this.fire_('sub-label-link-clicked', target.id); |
| e.preventDefault(); |
| e.stopPropagation(); |
| } |
| } |
| |
| private onChange_(e: CustomEvent<boolean>) { |
| // Prevent cr-toggle's change event from propagating to the parent since |
| // this element fires its own 'change' event. |
| e.stopPropagation(); |
| this.updateCheckedAndNotify_(e.detail); |
| } |
| |
| private updateCheckedAndNotify_(checked: boolean) { |
| this.checked = checked; |
| this.notifyChangedByUserInteraction(); |
| this.fire_('change', this.checked); |
| } |
| } |
| |
| declare global { |
| interface HTMLElementTagNameMap { |
| 'settings-toggle-button': SettingsToggleButtonElement; |
| } |
| } |
| |
| customElements.define( |
| SettingsToggleButtonElement.is, SettingsToggleButtonElement); |