[go: nahoru, domu]

blob: d23a19c8ca399b41171fad990579974791736aae [file] [log] [blame]
Alex Rudenko128c7b32023-05-03 11:03:431// Copyright 2023 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import * as i18n from '../../../core/i18n/i18n.js';
6import * as Buttons from '../../../ui/components/buttons/buttons.js';
7import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
8import * as IconButton from '../../../ui/components/icon_button/icon_button.js';
9import * as LitHtml from '../../../ui/lit-html/lit-html.js';
10import * as Models from '../models/models.js';
11import * as Actions from '../recorder-actions.js'; // eslint-disable-line rulesdir/es_modules_import
12
13import recordingListViewStyles from './recordingListView.css.js';
14
15const UIStrings = {
16 /**
17 *@description The title of the page that contains a list of saved recordings that the user has..
18 */
19 savedRecordings: 'Saved recordings',
20 /**
21 * @description The title of the button that leads to create a new recording page.
22 */
23 createRecording: 'Create a new recording',
24 /**
25 * @description The title of the button that is shown next to each of the recordings and that triggers playing of the recording.
26 */
27 playRecording: 'Play recording',
28 /**
29 * @description The title of the button that is shown next to each of the recordings and that triggers deletion of the recording.
30 */
31 deleteRecording: 'Delete recording',
32 /**
33 * @description The title of the row corresponding to a recording. By clicking on the row, the user open the recording for editing.
34 */
35 openRecording: 'Open recording',
36};
37const str_ = i18n.i18n.registerUIStrings(
38 'panels/recorder/components/RecordingListView.ts',
39 UIStrings,
40);
41const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
42
43declare global {
44 interface HTMLElementTagNameMap {
45 'devtools-recording-list-view': RecordingListView;
46 }
47
48 interface HTMLElementEventMap {
49 openrecording: OpenRecordingEvent;
50 deleterecording: DeleteRecordingEvent;
51 }
52}
53
54export class CreateRecordingEvent extends Event {
55 static readonly eventName = 'createrecording';
56 constructor() {
57 super(CreateRecordingEvent.eventName);
58 }
59}
60
61export class DeleteRecordingEvent extends Event {
62 static readonly eventName = 'deleterecording';
63 constructor(public storageName: string) {
64 super(DeleteRecordingEvent.eventName);
65 }
66}
67
68export class OpenRecordingEvent extends Event {
69 static readonly eventName = 'openrecording';
70 constructor(public storageName: string) {
71 super(OpenRecordingEvent.eventName);
72 }
73}
74
75export class PlayRecordingEvent extends Event {
76 static readonly eventName = 'playrecording';
77 constructor(public storageName: string) {
78 super(PlayRecordingEvent.eventName);
79 }
80}
81
82interface Recording {
83 storageName: string;
84 name: string;
85}
86
87const pathIconUrl = new URL(
88 '../images/path_icon.svg',
89 import.meta.url,
90 )
91 .toString();
92const playIconUrl = new URL(
93 '../images/play_icon.svg',
94 import.meta.url,
95 )
96 .toString();
97const deleteIconUrl = new URL(
98 '../images/delete_icon.svg',
99 import.meta.url,
100 )
101 .toString();
102export class RecordingListView extends HTMLElement {
103 static readonly litTagName = LitHtml.literal`devtools-recording-list-view`;
104 readonly #shadow = this.attachShadow({mode: 'open'});
105 readonly #props: {recordings: Recording[], replayAllowed: boolean} = {
106 recordings: [],
107 replayAllowed: true,
108 };
109
110 connectedCallback(): void {
111 this.#shadow.adoptedStyleSheets = [recordingListViewStyles];
112 void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
113 }
114
115 set recordings(recordings: Recording[]) {
116 this.#props.recordings = recordings;
117 void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
118 }
119
120 set replayAllowed(value: boolean) {
121 this.#props.replayAllowed = value;
122 void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
123 }
124
125 #onCreateClick(): void {
126 this.dispatchEvent(new CreateRecordingEvent());
127 }
128
129 #onDeleteClick(storageName: string, event: Event): void {
130 event.stopPropagation();
131 this.dispatchEvent(new DeleteRecordingEvent(storageName));
132 }
133
134 #onOpenClick(storageName: string, event: Event): void {
135 event.stopPropagation();
136 this.dispatchEvent(new OpenRecordingEvent(storageName));
137 }
138
139 #onPlayRecordingClick(storageName: string, event: Event): void {
140 event.stopPropagation();
141 this.dispatchEvent(new PlayRecordingEvent(storageName));
142 }
143
144 #onKeyDown(storageName: string, event: Event): void {
145 if ((event as KeyboardEvent).key !== 'Enter') {
146 return;
147 }
148 this.#onOpenClick(storageName, event);
149 }
150
151 #stopPropagation(event: Event): void {
152 event.stopPropagation();
153 }
154
155 #render = (): void => {
156 // clang-format off
157 LitHtml.render(
158 LitHtml.html`
159 <div class="wrapper">
160 <div class="header">
161 <h1>${i18nString(UIStrings.savedRecordings)}</h1>
162 <${Buttons.Button.Button.litTagName}
163 .variant=${Buttons.Button.Variant.PRIMARY}
164 @click=${this.#onCreateClick}
165 title=${Models.Tooltip.getTooltipForActions(
166 i18nString(UIStrings.createRecording),
167 Actions.RecorderActions.CreateRecording,
168 )}
169 >
170 ${i18nString(UIStrings.createRecording)}
171 </${Buttons.Button.Button.litTagName}>
172 </div>
173 <div class="table">
174 ${this.#props.recordings.map(recording => {
175 return LitHtml.html`
176 <div role="button" tabindex="0" aria-label=${i18nString(
177 UIStrings.openRecording,
178 )} class="row" @keydown=${this.#onKeyDown.bind(
179 this,
180 recording.storageName,
181 )} @click=${this.#onOpenClick.bind(this, recording.storageName)}>
182 <div class="icon">
183 <${IconButton.Icon.Icon.litTagName} .data=${
184 {
185 iconPath: pathIconUrl,
186 color: 'var(--color-primary-old)',
187 } as IconButton.Icon.IconData
188 }>
189 </${IconButton.Icon.Icon.litTagName}>
190 </div>
191 <div class="title">${recording.name}</div>
192 <div class="actions">
193 ${
194 this.#props.replayAllowed
195 ? LitHtml.html`<button title=${i18nString(
196 UIStrings.playRecording,
197 )} @keydown=${
198 this.#stopPropagation
199 } @click=${this.#onPlayRecordingClick.bind(
200 this,
201 recording.storageName,
202 )}>
203 <${IconButton.Icon.Icon.litTagName} .data=${
204 {
205 iconPath: playIconUrl,
206 color: 'var(--color-text-primary)',
207 } as IconButton.Icon.IconData
208 }>
209 </${IconButton.Icon.Icon.litTagName}>
210 </button><div class="divider"></div>`
211 : ''
212 }
213 <button class="delete-recording-button" title=${i18nString(
214 UIStrings.deleteRecording,
215 )} @keydown=${
216 this.#stopPropagation
217 } @click=${this.#onDeleteClick.bind(this, recording.storageName)}>
218 <${IconButton.Icon.Icon.litTagName} .data=${
219 {
220 iconPath: deleteIconUrl,
221 color: 'var(--color-text-primary)',
222 } as IconButton.Icon.IconData
223 }>
224 </${IconButton.Icon.Icon.litTagName}>
225 </button>
226 </div>
227 </div>
228 `;
229 })}
230 </div>
231 </div>
232 `,
233 this.#shadow,
234 { host: this },
235 );
236 // clang-format on
237 };
238}
239
240ComponentHelpers.CustomElements.defineComponent(
241 'devtools-recording-list-view',
242 RecordingListView,
243);