// Copyright 2023 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 i18n from '../../../core/i18n/i18n.js'; import * 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 * as VisualLogging from '../../../ui/visual_logging/visual_logging.js'; import type * as Extensions from '../extensions/extensions.js'; import * as Models from '../models/models.js'; import {PlayRecordingSpeed} from '../models/RecordingPlayer.js'; import * as Actions from '../recorder-actions/recorder-actions.js'; import replaySectionStyles from './replaySection.css.js'; const {html, Directives: {ifDefined, repeat}} = Lit; const UIStrings = { /** * @description Replay button label */ Replay: 'Replay', /** * @description Button label for the normal speed replay option */ ReplayNormalButtonLabel: 'Normal speed', /** * @description Item label for the normal speed replay option */ ReplayNormalItemLabel: 'Normal (Default)', /** * @description Button label for the slow speed replay option */ ReplaySlowButtonLabel: 'Slow speed', /** * @description Item label for the slow speed replay option */ ReplaySlowItemLabel: 'Slow', /** * @description Button label for the very slow speed replay option */ ReplayVerySlowButtonLabel: 'Very slow speed', /** * @description Item label for the very slow speed replay option */ ReplayVerySlowItemLabel: 'Very slow', /** * @description Button label for the extremely slow speed replay option */ ReplayExtremelySlowButtonLabel: 'Extremely slow speed', /** * @description Item label for the slow speed replay option */ ReplayExtremelySlowItemLabel: 'Extremely slow', /** * @description Label for a group of items in the replay menu that indicate various replay speeds (e.g., Normal, Fast, Slow). */ speedGroup: 'Speed', /** * @description Label for a group of items in the replay menu that indicate various extensions that can be used for replay. */ extensionGroup: 'Extensions', } as const; const str_ = i18n.i18n.registerUIStrings( 'panels/recorder/components/ReplaySection.ts', UIStrings, ); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); const REPLAY_EXTENSION_PREFIX = 'extension'; interface Item { value: string; buttonIconName: string; buttonLabel?: () => Platform.UIString.LocalizedString; label: () => Platform.UIString.LocalizedString; } interface Group { name: string; items: Item[]; } interface ViewInput { disabled: boolean; groups: Group[]; selectedItem: Item; actionTitle: string; onButtonClick: () => void; onItemSelected: (item: string) => void; } export type ViewOutput = undefined; export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTMLElement): void => { const {disabled, groups, selectedItem, actionTitle, onButtonClick, onItemSelected} = input; const buttonVariant = Buttons.Button.Variant.PRIMARY; const handleClick = (ev: Event): void => { ev.stopPropagation(); onButtonClick(); }; const handleSelectMenuSelect = ( event: Event, ): void => { if (event.target instanceof HTMLSelectElement) { onItemSelected(event.target.value); } }; // clang-format off Lit.render( html`
${i18nString(UIStrings.Replay)}
`, target, ); // clang-format on }; /** * This presenter combines built-in replay speeds and extensions into a single * select menu + a button. */ export class ReplaySection extends UI.Widget.Widget { onStartReplay?: (speed: PlayRecordingSpeed, extension?: Extensions.ExtensionManager.Extension) => void; #disabled = false; #settings?: Models.RecorderSettings.RecorderSettings; #replayExtensions: Extensions.ExtensionManager.Extension[] = []; #view: typeof DEFAULT_VIEW; #groups: Group[] = []; constructor(element?: HTMLElement, view?: typeof DEFAULT_VIEW) { super(element, {useShadowDom: true}); this.#view = view || DEFAULT_VIEW; this.#groups = this.#computeGroups(); } set settings(settings: Models.RecorderSettings.RecorderSettings|undefined) { this.#settings = settings; this.performUpdate(); } set replayExtensions(replayExtensions: Extensions.ExtensionManager.Extension[]) { this.#replayExtensions = replayExtensions; this.#groups = this.#computeGroups(); this.performUpdate(); } get disabled(): boolean { return this.#disabled; } set disabled(disabled: boolean) { this.#disabled = disabled; this.performUpdate(); } override wasShown(): void { super.wasShown(); this.performUpdate(); } override performUpdate(): void { const selectedItem = this.#getSelectedItem(); this.#view( { disabled: this.#disabled, groups: this.#groups, selectedItem, actionTitle: Models.Tooltip.getTooltipForActions(selectedItem.label(), Actions.RecorderActions.REPLAY_RECORDING), onButtonClick: () => this.#onStartReplay(), onItemSelected: (item: string) => this.#onItemSelected(item), }, undefined, this.contentElement, ); } #computeGroups(): Group[] { const groups: Group[] = [{ name: i18nString(UIStrings.speedGroup), items: [ { value: PlayRecordingSpeed.NORMAL, buttonIconName: 'play', buttonLabel: () => i18nString(UIStrings.ReplayNormalButtonLabel), label: () => i18nString(UIStrings.ReplayNormalItemLabel), }, { value: PlayRecordingSpeed.SLOW, buttonIconName: 'play', buttonLabel: () => i18nString(UIStrings.ReplaySlowButtonLabel), label: () => i18nString(UIStrings.ReplaySlowItemLabel), }, { value: PlayRecordingSpeed.VERY_SLOW, buttonIconName: 'play', buttonLabel: () => i18nString(UIStrings.ReplayVerySlowButtonLabel), label: () => i18nString(UIStrings.ReplayVerySlowItemLabel), }, { value: PlayRecordingSpeed.EXTREMELY_SLOW, buttonIconName: 'play', buttonLabel: () => i18nString(UIStrings.ReplayExtremelySlowButtonLabel), label: () => i18nString(UIStrings.ReplayExtremelySlowItemLabel), }, ] }]; if (this.#replayExtensions.length) { groups.push({ name: i18nString(UIStrings.extensionGroup), items: this.#replayExtensions.map((extension, idx) => { return { value: (REPLAY_EXTENSION_PREFIX + idx), buttonIconName: 'play', buttonLabel: () => extension.getName() as Platform.UIString.LocalizedString, label: () => extension.getName() as Platform.UIString.LocalizedString, }; }), }); } return groups; } #getSelectedItem(): Item { const value = this.#settings?.replayExtension || this.#settings?.speed || ''; for (const group of this.#groups) { for (const item of group.items) { if (item.value === value) { return item; } } } return this.#groups[0].items[0]; } #onStartReplay(): void { const value = this.#settings?.replayExtension || this.#settings?.speed || ''; if (value?.startsWith(REPLAY_EXTENSION_PREFIX)) { const extensionIdx = Number( value.substring(REPLAY_EXTENSION_PREFIX.length), ); const extension = this.#replayExtensions[extensionIdx]; if (this.#settings) { this.#settings.replayExtension = REPLAY_EXTENSION_PREFIX + extensionIdx; } if (this.onStartReplay) { this.onStartReplay(PlayRecordingSpeed.NORMAL, extension); } } else if (this.onStartReplay) { this.onStartReplay(this.#settings ? this.#settings.speed : PlayRecordingSpeed.NORMAL); } this.performUpdate(); } #onItemSelected(item: string): void { const speed = item as PlayRecordingSpeed; if (this.#settings && speed) { this.#settings.speed = speed; this.#settings.replayExtension = ''; } this.performUpdate(); } }