| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| /* eslint-disable @devtools/no-lit-render-outside-of-view, @devtools/enforce-custom-element-definitions-location */ |
| |
| import '../tooltips/tooltips.js'; |
| import './SettingDeprecationWarning.js'; |
| import '../../kit/kit.js'; |
| |
| import type * as Common from '../../../core/common/common.js'; |
| import * as Host from '../../../core/host/host.js'; |
| import * as i18n from '../../../core/i18n/i18n.js'; |
| import * as Lit from '../../lit/lit.js'; |
| import * as VisualLogging from '../../visual_logging/visual_logging.js'; |
| import * as Buttons from '../buttons/buttons.js'; |
| import * as Input from '../input/input.js'; |
| |
| import settingCheckboxStyles from './settingCheckbox.css.js'; |
| |
| const {html, Directives: {ifDefined}} = Lit; |
| |
| const UIStrings = { |
| /** |
| * @description Text that is usually a hyperlink to more documentation |
| */ |
| learnMore: 'Learn more', |
| } as const; |
| const str_ = i18n.i18n.registerUIStrings('ui/components/settings/SettingCheckbox.ts', UIStrings); |
| const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); |
| |
| export interface SettingCheckboxData { |
| setting: Common.Settings.Setting<boolean>; |
| textOverride?: string; |
| } |
| |
| /** |
| * A simple checkbox that is backed by a boolean setting. |
| */ |
| export class SettingCheckbox extends HTMLElement { |
| readonly #shadow = this.attachShadow({mode: 'open'}); |
| |
| #setting?: Common.Settings.Setting<boolean>; |
| #changeListenerDescriptor?: Common.EventTarget.EventDescriptor; |
| #textOverride?: string; |
| |
| set data(data: SettingCheckboxData) { |
| if (this.#changeListenerDescriptor && this.#setting) { |
| this.#setting.removeChangeListener(this.#changeListenerDescriptor.listener); |
| } |
| |
| this.#setting = data.setting; |
| this.#textOverride = data.textOverride; |
| |
| this.#changeListenerDescriptor = this.#setting.addChangeListener(() => { |
| this.#render(); |
| }); |
| this.#render(); |
| } |
| |
| icon(): Lit.TemplateResult|undefined { |
| if (!this.#setting) { |
| return undefined; |
| } |
| |
| if (this.#setting.deprecation) { |
| return html`<devtools-setting-deprecation-warning .data=${ |
| this.#setting.deprecation}></devtools-setting-deprecation-warning>`; |
| } |
| |
| const learnMore = this.#setting.learnMore(); |
| if (learnMore) { |
| const jsLogContext = `${this.#setting.name}-documentation`; |
| const data: Buttons.Button.ButtonData = { |
| iconName: 'info', |
| variant: Buttons.Button.Variant.ICON, |
| size: Buttons.Button.Size.SMALL, |
| jslogContext: jsLogContext, |
| }; |
| |
| const url = learnMore.url; |
| if (learnMore.tooltip) { |
| const id = `${this.#setting.name}-information`; |
| // clang-format off |
| return html` |
| <devtools-button |
| class="info-icon" |
| aria-details=${id} |
| .data=${data} |
| ></devtools-button> |
| <devtools-tooltip id=${id} variant="rich"> |
| <span>${learnMore.tooltip()}</span><br /> |
| ${url |
| ? html`<devtools-link |
| href=${url} |
| class="link" |
| .jslogContext=${jsLogContext} |
| >${i18nString(UIStrings.learnMore)}</devtools-link |
| >` |
| : Lit.nothing} |
| </devtools-tooltip> |
| `; |
| // clang-format on |
| } |
| if (url) { |
| const handleClick = (event: MouseEvent): void => { |
| Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab(url); |
| event.consume(); |
| }; |
| data.iconName = 'help'; |
| data.title = i18nString(UIStrings.learnMore); |
| // clang-format off |
| return html`<devtools-button |
| class="info-icon" |
| @click=${handleClick} |
| .data=${data} |
| ></devtools-button>`; |
| // clang-format on |
| } |
| } |
| |
| return undefined; |
| } |
| |
| get checked(): boolean { |
| if (!this.#setting || this.#setting.disabledReasons().length > 0) { |
| return false; |
| } |
| |
| return this.#setting.get(); |
| } |
| |
| #render(): void { |
| if (!this.#setting) { |
| throw new Error('No "Setting" object provided for rendering'); |
| } |
| |
| const icon = this.icon(); |
| const title = `${this.#setting.learnMore() ? this.#setting.learnMore()?.tooltip?.() : ''}`; |
| const disabledReasons = this.#setting.disabledReasons(); |
| const reason = disabledReasons.length ? |
| html` |
| <devtools-button class="disabled-reason" .iconName=${'info'} .variant=${Buttons.Button.Variant.ICON} .size=${ |
| Buttons.Button.Size.SMALL} title=${ifDefined(disabledReasons.join('\n'))} @click=${ |
| onclick}></devtools-button> |
| ` : |
| Lit.nothing; |
| Lit.render( |
| html` |
| <style>${Input.checkboxStyles}</style> |
| <style>${settingCheckboxStyles}</style> |
| <p> |
| <label title=${title}> |
| <input |
| type="checkbox" |
| .checked=${this.checked} |
| ?disabled=${this.#setting.disabled()} |
| @change=${this.#checkboxChanged} |
| jslog=${VisualLogging.toggle().track({change: true}).context(this.#setting.name)} |
| aria-label=${this.#setting.title()} |
| /> |
| ${this.#textOverride || this.#setting.title()}${reason} |
| </label> |
| ${icon} |
| </p>`, |
| this.#shadow, {host: this}); |
| } |
| |
| #checkboxChanged(e: Event): void { |
| this.#setting?.set((e.target as HTMLInputElement).checked); |
| this.dispatchEvent(new CustomEvent('change', { |
| bubbles: true, |
| composed: false, |
| })); |
| } |
| } |
| |
| // eslint-disable-next-line @devtools/enforce-custom-element-prefix |
| customElements.define('setting-checkbox', SettingCheckbox); |
| |
| declare global { |
| interface HTMLElementTagNameMap { |
| 'setting-checkbox': SettingCheckbox; |
| } |
| } |