UNPKG

chrome-devtools-frontend

Version:
227 lines (209 loc) 9.44 kB
// Copyright 2017 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as Common from '../../core/common/common.js'; import * as i18n from '../../core/i18n/i18n.js'; import * as Platform from '../../core/platform/platform.js'; import * as SDK from '../../core/sdk/sdk.js'; import * as Badges from '../../models/badges/badges.js'; import * as Lit from '../../ui/lit/lit.js'; import * as VisualLogging from '../../ui/visual_logging/visual_logging.js'; import {ThrottlingManager} from './ThrottlingManager.js'; import {type NetworkThrottlingConditionsGroup, ThrottlingPresets} from './ThrottlingPresets.js'; const {render, html, Directives} = Lit; const UIStrings = { /** * @description Text to indicate something is not enabled */ disabled: 'Disabled', /** * @description Title for a group of configuration options */ presets: 'Presets', /** * @description Text in Network Throttling Selector of the Network panel */ custom: 'Custom', /** *@description Text with two placeholders separated by a colon *@example {Node removed} PH1 *@example {div#id1} PH2 */ sS: '{PH1}: {PH2}', /** *@description Accessibility label for custom add network throttling option *@example {Custom} PH1 */ addS: 'Add {PH1}', /** *@description Text in Throttling Manager of the Network panel */ add: 'Add…', /** * @description Text label for a selection box showing that a specific option is recommended for CPU or Network throttling. * @example {Fast 4G} PH1 * @example {4x slowdown} PH1 */ recommendedThrottling: '{PH1} – recommended', } as const; const str_ = i18n.i18n.registerUIStrings('panels/mobile_throttling/NetworkThrottlingSelector.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); interface ViewInput { recommendedConditions: SDK.NetworkManager.Conditions|null; selectedConditions: SDK.NetworkManager.Conditions; throttlingGroups: NetworkThrottlingConditionsGroup[]; customConditionsGroup: NetworkThrottlingConditionsGroup; jslogContext: string; title: string; onSelect: (conditions: SDK.NetworkManager.Conditions) => void; onAddCustomConditions: () => void; } export type ViewFunction = (input: ViewInput, output: object, target: HTMLElement) => void; export const DEFAULT_VIEW: ViewFunction = (input, output, target) => { // The title is usually an i18nLazyString except for custom values that are stored in the local storage in the form of a string. const title = (conditions: SDK.NetworkManager.Conditions): string => typeof conditions.title === 'function' ? conditions.title() : conditions.title; const jslog = (group: NetworkThrottlingConditionsGroup, condition: SDK.NetworkManager.Conditions): string => `${VisualLogging.item(Platform.StringUtilities.toKebabCase(condition.i18nTitleKey || title(condition))).track({ click: true })}`; const optionsMap = new WeakMap<HTMLOptionElement, SDK.NetworkManager.Conditions>(); let selectedConditions = input.selectedConditions; function onSelect(event: Event): void { const element = (event.target as HTMLSelectElement | null); if (!element) { return; } const option = element.selectedOptions[0]; if (!option) { return; } if (option === element.options[element.options.length - 1]) { input.onAddCustomConditions(); event.consume(true); element.value = title(selectedConditions); } else { const conditions = optionsMap.get(option); if (conditions) { selectedConditions = conditions; input.onSelect(conditions); } } } render( // clang-format off html`<select aria-label=${input.title} jslog=${VisualLogging.dropDown().track({change: true}).context(input.jslogContext)} @change=${onSelect}> ${input.throttlingGroups.map( group => html`<optgroup label=${group.title}> ${group.items.map(condition => html`<option ${Directives.ref(option => option && optionsMap.set(option as HTMLOptionElement, condition))} ?selected=${SDK.NetworkManager.networkConditionsEqual(condition, selectedConditions)} value=${title(condition)} aria-label=${i18nString(UIStrings.sS, {PH1: group.title, PH2: title(condition)})} jslog=${jslog(group, condition)}> ${condition === input.recommendedConditions ? i18nString(UIStrings.recommendedThrottling, {PH1: title(condition)}): title(condition)} </option>`)} </optgroup>`)} <optgroup label=${input.customConditionsGroup.title}> ${input.customConditionsGroup.items.map(condition => html`<option ${Directives.ref(option => option && optionsMap.set(option as HTMLOptionElement, condition))} ?selected=${SDK.NetworkManager.networkConditionsEqual(condition, selectedConditions)} value=${title(condition)} aria-label=${i18nString(UIStrings.sS, {PH1: input.customConditionsGroup.title, PH2: title(condition)})} jslog=${VisualLogging.item('custom-network-throttling-item').track({click: true})}> ${condition === input.recommendedConditions ? i18nString(UIStrings.recommendedThrottling, {PH1: title(condition)}): title(condition)} </option>`)} <option value=${i18nString(UIStrings.add)} aria-label=${i18nString(UIStrings.addS, {PH1: input.customConditionsGroup.title})} jslog=${VisualLogging.action('add').track({click: true})}> ${i18nString(UIStrings.add)} </option> </optgroup> </select>`, // clang-format on target); }; export const enum Events { CONDITIONS_CHANGED = 'conditionsChanged', } export interface EventTypes { [Events.CONDITIONS_CHANGED]: SDK.NetworkManager.Conditions; } export class NetworkThrottlingSelect extends Common.ObjectWrapper.ObjectWrapper<EventTypes> { #recommendedConditions: SDK.NetworkManager.Conditions|null = null; readonly #element: HTMLElement; readonly #jslogContext: string; #currentConditions: SDK.NetworkManager.Conditions; readonly #title: string; readonly #view: ViewFunction; static createForGlobalConditions(element: HTMLElement, title: string): NetworkThrottlingSelect { ThrottlingManager.instance(); // Instantiate the throttling manager to connect network manager with the setting const select = new NetworkThrottlingSelect( element, title, SDK.NetworkManager.activeNetworkThrottlingKeySetting().name, SDK.NetworkManager.MultitargetNetworkManager.instance().networkConditions()); select.addEventListener( Events.CONDITIONS_CHANGED, ev => SDK.NetworkManager.MultitargetNetworkManager.instance().setNetworkConditions(ev.data)); SDK.NetworkManager.MultitargetNetworkManager.instance().addEventListener( SDK.NetworkManager.MultitargetNetworkManager.Events.CONDITIONS_CHANGED, () => { select.currentConditions = SDK.NetworkManager.MultitargetNetworkManager.instance().networkConditions(); }); return select; } constructor( element: HTMLElement, title: string, jslogContext: string, currentConditions: SDK.NetworkManager.Conditions, view = DEFAULT_VIEW) { super(); SDK.NetworkManager.customUserNetworkConditionsSetting().addChangeListener(this.#performUpdate, this); this.#element = element; this.#jslogContext = jslogContext; this.#currentConditions = currentConditions; this.#title = title; this.#view = view; this.#performUpdate(); } set recommendedConditions(recommendedConditions: SDK.NetworkManager.Conditions|null) { this.#recommendedConditions = recommendedConditions; this.#performUpdate(); } set currentConditions(currentConditions: SDK.NetworkManager.Conditions) { this.#currentConditions = currentConditions; this.#performUpdate(); } #performUpdate(): void { const customNetworkConditionsSetting = SDK.NetworkManager.customUserNetworkConditionsSetting(); const customNetworkConditions = customNetworkConditionsSetting.get(); const onAddCustomConditions = (): void => { void Common.Revealer.reveal(SDK.NetworkManager.customUserNetworkConditionsSetting()); }; const onSelect = (conditions: SDK.NetworkManager.Conditions): void => { this.dispatchEventToListeners(Events.CONDITIONS_CHANGED, conditions); Badges.UserBadges.instance().recordAction(Badges.BadgeAction.NETWORK_SPEED_THROTTLED); }; const throttlingGroups = [ {title: i18nString(UIStrings.disabled), items: [SDK.NetworkManager.NoThrottlingConditions]}, {title: i18nString(UIStrings.presets), items: ThrottlingPresets.networkPresets}, ]; const customConditionsGroup = {title: i18nString(UIStrings.custom), items: customNetworkConditions}; const viewInput: ViewInput = { recommendedConditions: this.#recommendedConditions, selectedConditions: this.#currentConditions, jslogContext: this.#jslogContext, title: this.#title, onSelect, onAddCustomConditions, throttlingGroups, customConditionsGroup, }; this.#view(viewInput, {}, this.#element); } }