UNPKG

chrome-devtools-frontend

Version:
199 lines (179 loc) • 6.86 kB
// Copyright 2025 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /* eslint-disable rulesdir/no-lit-render-outside-of-view */ /* eslint-disable rulesdir/no-imperative-dom-api */ import * as Host from '../../core/host/host.js'; import * as i18n from '../../core/i18n/i18n.js'; import type * as Platform from '../../core/platform/platform.js'; import * as Buttons from '../../ui/components/buttons/buttons.js'; import * as UI from '../../ui/legacy/legacy.js'; import * as Lit from '../../ui/lit/lit.js'; import commonStyles from './common.css.js'; const {html} = Lit; const UIStrings = { /** *@description Header text for the feature reminder dialog. */ thingsToConsider: 'Things to consider', /** *@description Text for the learn more button in the feature reminder dialog. */ learnMore: 'Learn more', /** *@description Text for the cancel button in the feature reminder dialog. */ cancel: 'Cancel', /** *@description Text for the cancel button in the "type to allow" dialog. */ allow: 'Allow', /** *@description Text for the got it button in the feature reminder dialog. */ gotIt: 'Got it', } as const; const str_ = i18n.i18n.registerUIStrings('panels/common/common.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); export class FreDialog { static show({header, reminderItems, onLearnMoreClick, ariaLabel, learnMoreButtonTitle}: { header: { iconName: string, text: Platform.UIString.LocalizedString, }, reminderItems: Array<{ iconName: string, content: Platform.UIString.LocalizedString|Lit.LitTemplate, }>, onLearnMoreClick: () => void, ariaLabel?: string, learnMoreButtonTitle?: string, }): Promise<boolean> { const dialog = new UI.Dialog.Dialog(); if (ariaLabel) { dialog.setAriaLabel(ariaLabel); } const result = Promise.withResolvers<boolean>(); // clang-format off Lit.render(html` <div class="fre-disclaimer"> <style> ${commonStyles} </style> <header> <div class="header-icon-container"> <devtools-icon name=${header.iconName}></devtools-icon> </div> <h2 tabindex="-1"> ${header.text} </h2> </header> <main class="reminder-container"> <h3>${i18nString(UIStrings.thingsToConsider)}</h3> ${reminderItems.map(reminderItem => html` <div class="reminder-item"> <devtools-icon class="reminder-icon" name=${reminderItem.iconName}></devtools-icon> <span>${reminderItem.content}</span> </div> `)} </main> <footer> <devtools-button @click=${onLearnMoreClick} .jslogContext=${'fre-disclaimer.learn-more'} .variant=${Buttons.Button.Variant.OUTLINED}> ${learnMoreButtonTitle ?? i18nString(UIStrings.learnMore)} </devtools-button> <div class="right-buttons"> <devtools-button @click=${() => { dialog.hide(); result.resolve(false); }} .jslogContext=${'fre-disclaimer.cancel'} .variant=${Buttons.Button.Variant.TONAL}> ${i18nString(UIStrings.cancel)} </devtools-button> <devtools-button @click=${() => { dialog.hide(); result.resolve(true); }} .jslogContext=${'fre-disclaimer.continue'} .variant=${Buttons.Button.Variant.PRIMARY}> ${i18nString(UIStrings.gotIt)} </devtools-button> </div> </footer> </div>`, dialog.contentElement); // clang-format on dialog.setOutsideClickCallback(ev => { ev.consume(true); // true = preventDefault() dialog.hide(); result.resolve(false); }); dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.MEASURE_CONTENT); dialog.setDimmed(true); dialog.show(); return result.promise; } private constructor() { } } export class TypeToAllowDialog { static async show(options: { jslogContext: { dialog: string, input: string, }, header: Platform.UIString.LocalizedString, message: Platform.UIString.LocalizedString, typePhrase: Platform.UIString.LocalizedString, inputPlaceholder: Platform.UIString.LocalizedString, }): Promise<boolean> { const dialog = new UI.Dialog.Dialog(options.jslogContext.dialog); dialog.setMaxContentSize(new UI.Geometry.Size(504, 340)); dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.SET_EXACT_WIDTH_MAX_HEIGHT); dialog.setDimmed(true); const shadowRoot = UI.UIUtils.createShadowRootWithCoreStyles(dialog.contentElement, {cssFile: commonStyles}); const content = shadowRoot.createChild('div', 'type-to-allow-dialog'); const result = await new Promise<boolean>(resolve => { const header = content.createChild('div', 'header'); header.createChild('div', 'title').textContent = options.header; const closeButton = header.createChild('dt-close-button', 'dialog-close-button'); closeButton.setTabbable(true); self.onInvokeElement(closeButton, event => { dialog.hide(); event.consume(true); resolve(false); }); closeButton.setSize(Buttons.Button.Size.SMALL); content.createChild('div', 'message').textContent = options.message; const input = UI.UIUtils.createInput('text-input', 'text', options.jslogContext.input); input.placeholder = options.inputPlaceholder; content.appendChild(input); const buttonsBar = content.createChild('div', 'button'); const cancelButton = UI.UIUtils.createTextButton(i18nString(UIStrings.cancel), () => resolve(false), {jslogContext: 'cancel'}); const allowButton = UI.UIUtils.createTextButton(i18nString(UIStrings.allow), () => { resolve(input.value === options.typePhrase); }, {jslogContext: 'confirm', variant: Buttons.Button.Variant.PRIMARY}); allowButton.disabled = true; buttonsBar.appendChild(allowButton); buttonsBar.appendChild(cancelButton); input.addEventListener('input', () => { allowButton.disabled = !Boolean(input.value); }, false); input.addEventListener('paste', e => e.preventDefault()); input.addEventListener('drop', e => e.preventDefault()); dialog.setOutsideClickCallback(event => { event.consume(); resolve(false); }); dialog.show(); Host.userMetrics.actionTaken(Host.UserMetrics.Action.SelfXssWarningDialogShown); }); dialog.hide(); return result; } }