chrome-devtools-frontend
Version:
Chrome DevTools UI
185 lines (161 loc) • 5.83 kB
text/typescript
// 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;
}
}