blob: 5d995761f21dc676a8f8b15b7b69a3c668600292 [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371// Copyright (c) 2015 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.
Tim van der Lippe97611c92020-02-12 16:56:584
Tim van der Lippe76961572021-04-06 10:48:075import * as Common from '../../core/common/common.js';
Tim van der Lippebb352e62021-04-01 17:57:286import * as i18n from '../../core/i18n/i18n.js';
Tim van der Lippeaa1ed7a2021-03-31 14:38:277import * as Platform from '../../core/platform/platform.js';
Tim van der Lippee00b92f2021-03-31 16:52:178import * as SDK from '../../core/sdk/sdk.js';
Tim van der Lippeaa61faf2021-04-07 15:32:079import * as UI from '../../ui/legacy/legacy.js';
Tim van der Lippe97611c92020-02-12 16:56:5810
Kriti Sapra40ca8da2021-07-20 09:33:5911import classesPaneWidgetStyles from './classesPaneWidget.css.js';
Tim van der Lippeaabc8302019-12-10 15:34:4512import {ElementsPanel} from './ElementsPanel.js';
13
Simon Zündfbfd1072021-03-01 07:38:5314const UIStrings = {
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4915 /**
Peter Marshallcc995412021-02-19 08:07:4316 * @description Prompt text for a text field in the Classes Pane Widget of the Elements panel.
17 * Class refers to a CSS class.
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4918 */
19 addNewClass: 'Add new class',
20 /**
Peter Marshallcc995412021-02-19 08:07:4321 * @description Screen reader announcement string when adding a CSS class via the Classes Pane Widget.
22 * @example {vbox flex-auto} PH1
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4923 */
Peter Marshallcc995412021-02-19 08:07:4324 classesSAdded: 'Classes {PH1} added',
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4925 /**
Peter Marshallcc995412021-02-19 08:07:4326 * @description Screen reader announcement string when adding a class via the Classes Pane Widget.
27 * @example {title-container} PH1
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4928 */
Peter Marshallcc995412021-02-19 08:07:4329 classSAdded: 'Class {PH1} added',
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4930 /**
Peter Marshallcc995412021-02-19 08:07:4331 * @description Accessible title read by screen readers for the Classes Pane Widget of the Elements
32 * panel. Element is a HTML DOM Element and classes refers to CSS classes.
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4933 */
34 elementClasses: 'Element Classes',
35};
Jan Scheffler29905852021-04-14 11:30:3136const str_ = i18n.i18n.registerUIStrings('panels/elements/ClassesPaneWidget.ts', UIStrings);
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:4937const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Tim van der Lippe97611c92020-02-12 16:56:5838export class ClassesPaneWidget extends UI.Widget.Widget {
Jan Scheffler77646c72021-08-13 18:40:3839 private input: HTMLElement;
40 private readonly classesContainer: HTMLElement;
41 private readonly prompt: ClassNamePrompt;
42 private readonly mutatingNodes: Set<SDK.DOMModel.DOMNode>;
43 private readonly pendingNodeClasses: Map<SDK.DOMModel.DOMNode, string>;
44 private readonly updateNodeThrottler: Common.Throttler.Throttler;
45 private previousTarget: SDK.DOMModel.DOMNode|null;
Jan Scheffler29905852021-04-14 11:30:3146
Blink Reformat4c46d092018-04-07 15:32:3747 constructor() {
48 super(true);
Blink Reformat4c46d092018-04-07 15:32:3749 this.contentElement.className = 'styles-element-classes-pane';
50 const container = this.contentElement.createChild('div', 'title-container');
Jan Scheffler77646c72021-08-13 18:40:3851 this.input = container.createChild('div', 'new-class-input monospace');
52 this.setDefaultFocusedElement(this.input);
53 this.classesContainer = this.contentElement.createChild('div', 'source-code');
54 this.classesContainer.classList.add('styles-element-classes-container');
55 this.prompt = new ClassNamePrompt(this.nodeClasses.bind(this));
56 this.prompt.setAutocompletionTimeout(0);
57 this.prompt.renderAsBlock();
Blink Reformat4c46d092018-04-07 15:32:3758
Jan Scheffler77646c72021-08-13 18:40:3859 const proxyElement = (this.prompt.attach(this.input) as HTMLElement);
60 this.prompt.setPlaceholder(i18nString(UIStrings.addNewClass));
61 this.prompt.addEventListener(UI.TextPrompt.Events.TextChanged, this.onTextChanged, this);
62 proxyElement.addEventListener('keydown', this.onKeyDown.bind(this), false);
Blink Reformat4c46d092018-04-07 15:32:3763
Sigurd Schneiderb9f6c792021-05-31 10:57:2464 SDK.TargetManager.TargetManager.instance().addModelListener(
Jan Scheffler77646c72021-08-13 18:40:3865 SDK.DOMModel.DOMModel, SDK.DOMModel.Events.DOMMutated, this.onDOMMutated, this);
66 this.mutatingNodes = new Set();
67 this.pendingNodeClasses = new Map();
68 this.updateNodeThrottler = new Common.Throttler.Throttler(0);
69 this.previousTarget = null;
70 UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this.onSelectedNodeChanged, this);
Blink Reformat4c46d092018-04-07 15:32:3771 }
72
Jan Scheffler77646c72021-08-13 18:40:3873 private splitTextIntoClasses(text: string): string[] {
Mathias Bynens3abc0952020-04-20 14:15:5274 return text.split(/[,\s]/).map(className => className.trim()).filter(className => className.length);
Blink Reformat4c46d092018-04-07 15:32:3775 }
76
Jan Scheffler77646c72021-08-13 18:40:3877 private onKeyDown(event: KeyboardEvent): void {
Tim van der Lippebcd6b5c2021-01-13 12:31:5178 if (!(event.key === 'Enter') && !isEscKey(event)) {
Blink Reformat4c46d092018-04-07 15:32:3779 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:3480 }
Blink Reformat4c46d092018-04-07 15:32:3781
Tim van der Lippebcd6b5c2021-01-13 12:31:5182 if (event.key === 'Enter') {
Blink Reformat4c46d092018-04-07 15:32:3783 event.consume();
Jan Scheffler77646c72021-08-13 18:40:3884 if (this.prompt.acceptAutoComplete()) {
Blink Reformat4c46d092018-04-07 15:32:3785 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:3486 }
Blink Reformat4c46d092018-04-07 15:32:3787 }
88
Jan Scheffler29905852021-04-14 11:30:3189 const eventTarget = (event.target as HTMLElement);
90 let text: ''|string = (eventTarget.textContent as string);
Blink Reformat4c46d092018-04-07 15:32:3791 if (isEscKey(event)) {
Simon Zündda7058f2020-02-28 13:57:2892 if (!Platform.StringUtilities.isWhitespace(text)) {
Blink Reformat4c46d092018-04-07 15:32:3793 event.consume(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:3494 }
Blink Reformat4c46d092018-04-07 15:32:3795 text = '';
96 }
97
Jan Scheffler77646c72021-08-13 18:40:3898 this.prompt.clearAutocomplete();
Tim van der Lippe32f760f2020-10-01 10:52:1599 eventTarget.textContent = '';
Blink Reformat4c46d092018-04-07 15:32:37100
Tim van der Lipped1a00aa2020-08-19 16:03:56101 const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34102 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37103 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34104 }
Blink Reformat4c46d092018-04-07 15:32:37105
Jan Scheffler77646c72021-08-13 18:40:38106 const classNames = this.splitTextIntoClasses(text);
Michael Liaocafccfd2020-04-02 17:11:59107 if (!classNames.length) {
Jan Scheffler77646c72021-08-13 18:40:38108 this.installNodeClasses(node);
Michael Liaocafccfd2020-04-02 17:11:59109 return;
110 }
111
Tim van der Lippe1d6e57a2019-09-30 11:55:34112 for (const className of classNames) {
Jan Scheffler77646c72021-08-13 18:40:38113 this.toggleClass(node, className, true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34114 }
Michael Liaocafccfd2020-04-02 17:11:59115
116 // annoucementString is used for screen reader to announce that the class(es) has been added successfully.
117 const joinClassString = classNames.join(' ');
Vidal Guillermo Diazleal Ortega0cfa0ed2021-02-17 20:35:49118 const announcementString = classNames.length > 1 ? i18nString(UIStrings.classesSAdded, {PH1: joinClassString}) :
119 i18nString(UIStrings.classSAdded, {PH1: joinClassString});
Michael Liao7322dee2021-04-07 18:33:30120 UI.ARIAUtils.alert(announcementString);
Michael Liaocafccfd2020-04-02 17:11:59121
Jan Scheffler77646c72021-08-13 18:40:38122 this.installNodeClasses(node);
123 this.update();
Blink Reformat4c46d092018-04-07 15:32:37124 }
125
Jan Scheffler77646c72021-08-13 18:40:38126 private onTextChanged(): void {
Tim van der Lipped1a00aa2020-08-19 16:03:56127 const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34128 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37129 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34130 }
Jan Scheffler77646c72021-08-13 18:40:38131 this.installNodeClasses(node);
Blink Reformat4c46d092018-04-07 15:32:37132 }
133
Jan Scheffler77646c72021-08-13 18:40:38134 private onDOMMutated(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode>): void {
Simon Zünd37cf1f52021-07-30 09:53:35135 const node = event.data;
Jan Scheffler77646c72021-08-13 18:40:38136 if (this.mutatingNodes.has(node)) {
Blink Reformat4c46d092018-04-07 15:32:37137 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34138 }
Tim van der Lippe32f760f2020-10-01 10:52:15139 cachedClassesMap.delete(node);
Jan Scheffler77646c72021-08-13 18:40:38140 this.update();
Blink Reformat4c46d092018-04-07 15:32:37141 }
142
Simon Zündf0aa2b42021-09-20 09:18:02143 private onSelectedNodeChanged(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode|null>): void {
Jan Scheffler77646c72021-08-13 18:40:38144 if (this.previousTarget && this.prompt.text()) {
145 this.input.textContent = '';
146 this.installNodeClasses(this.previousTarget);
Blink Reformat4c46d092018-04-07 15:32:37147 }
Simon Zündf0aa2b42021-09-20 09:18:02148 this.previousTarget = event.data;
Jan Scheffler77646c72021-08-13 18:40:38149 this.update();
Blink Reformat4c46d092018-04-07 15:32:37150 }
151
Jan Scheffler29905852021-04-14 11:30:31152 wasShown(): void {
Kriti Saprab2b29f22021-06-29 12:59:56153 super.wasShown();
Jan Scheffler77646c72021-08-13 18:40:38154 this.update();
Kriti Sapra40ca8da2021-07-20 09:33:59155 this.registerCSSFiles([classesPaneWidgetStyles]);
Blink Reformat4c46d092018-04-07 15:32:37156 }
157
Jan Scheffler77646c72021-08-13 18:40:38158 private update(): void {
Tim van der Lippe1d6e57a2019-09-30 11:55:34159 if (!this.isShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37160 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34161 }
Blink Reformat4c46d092018-04-07 15:32:37162
Tim van der Lipped1a00aa2020-08-19 16:03:56163 let node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34164 if (node) {
Blink Reformat4c46d092018-04-07 15:32:37165 node = node.enclosingElementOrSelf();
Tim van der Lippe1d6e57a2019-09-30 11:55:34166 }
Blink Reformat4c46d092018-04-07 15:32:37167
Jan Scheffler77646c72021-08-13 18:40:38168 this.classesContainer.removeChildren();
Jan Schefflera222c632021-08-13 12:46:15169 // @ts-ignore this.input is a div, not an input element. So this line makes no sense at all
Jan Scheffler77646c72021-08-13 18:40:38170 this.input.disabled = !node;
Blink Reformat4c46d092018-04-07 15:32:37171
Tim van der Lippe1d6e57a2019-09-30 11:55:34172 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37173 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34174 }
Blink Reformat4c46d092018-04-07 15:32:37175
Jan Scheffler77646c72021-08-13 18:40:38176 const classes = this.nodeClasses(node);
Simon Zündf27be3d2020-02-11 14:46:27177 const keys = [...classes.keys()];
Tim van der Lippe32f760f2020-10-01 10:52:15178 keys.sort(Platform.StringUtilities.caseInsensetiveComparator);
179 for (const className of keys) {
Tim van der Lippe97611c92020-02-12 16:56:58180 const label = UI.UIUtils.CheckboxLabel.create(className, classes.get(className));
Blink Reformat4c46d092018-04-07 15:32:37181 label.classList.add('monospace');
Jan Scheffler77646c72021-08-13 18:40:38182 label.checkboxElement.addEventListener('click', this.onClick.bind(this, className), false);
183 this.classesContainer.appendChild(label);
Blink Reformat4c46d092018-04-07 15:32:37184 }
185 }
186
Jan Scheffler77646c72021-08-13 18:40:38187 private onClick(className: string, event: Event): void {
Tim van der Lipped1a00aa2020-08-19 16:03:56188 const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34189 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37190 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34191 }
Jan Scheffler29905852021-04-14 11:30:31192 const enabled = (event.target as HTMLInputElement).checked;
Jan Scheffler77646c72021-08-13 18:40:38193 this.toggleClass(node, className, enabled);
194 this.installNodeClasses(node);
Blink Reformat4c46d092018-04-07 15:32:37195 }
196
Jan Scheffler77646c72021-08-13 18:40:38197 private nodeClasses(node: SDK.DOMModel.DOMNode): Map<string, boolean> {
Tim van der Lippe32f760f2020-10-01 10:52:15198 let result = cachedClassesMap.get(node);
Blink Reformat4c46d092018-04-07 15:32:37199 if (!result) {
200 const classAttribute = node.getAttribute('class') || '';
201 const classes = classAttribute.split(/\s/);
202 result = new Map();
203 for (let i = 0; i < classes.length; ++i) {
204 const className = classes[i].trim();
Tim van der Lippe1d6e57a2019-09-30 11:55:34205 if (!className.length) {
Blink Reformat4c46d092018-04-07 15:32:37206 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34207 }
Blink Reformat4c46d092018-04-07 15:32:37208 result.set(className, true);
209 }
Tim van der Lippe32f760f2020-10-01 10:52:15210 cachedClassesMap.set(node, result);
Blink Reformat4c46d092018-04-07 15:32:37211 }
212 return result;
213 }
214
Jan Scheffler77646c72021-08-13 18:40:38215 private toggleClass(node: SDK.DOMModel.DOMNode, className: string, enabled: boolean): void {
216 const classes = this.nodeClasses(node);
Blink Reformat4c46d092018-04-07 15:32:37217 classes.set(className, enabled);
218 }
219
Jan Scheffler77646c72021-08-13 18:40:38220 private installNodeClasses(node: SDK.DOMModel.DOMNode): void {
221 const classes = this.nodeClasses(node);
Jan Scheffler29905852021-04-14 11:30:31222 const activeClasses = new Set<string>();
Blink Reformat4c46d092018-04-07 15:32:37223 for (const className of classes.keys()) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34224 if (classes.get(className)) {
Blink Reformat4c46d092018-04-07 15:32:37225 activeClasses.add(className);
Tim van der Lippe1d6e57a2019-09-30 11:55:34226 }
Blink Reformat4c46d092018-04-07 15:32:37227 }
228
Jan Scheffler77646c72021-08-13 18:40:38229 const additionalClasses = this.splitTextIntoClasses(this.prompt.textWithCurrentSuggestion());
Tim van der Lippe1d6e57a2019-09-30 11:55:34230 for (const className of additionalClasses) {
Blink Reformat4c46d092018-04-07 15:32:37231 activeClasses.add(className);
Tim van der Lippe1d6e57a2019-09-30 11:55:34232 }
Blink Reformat4c46d092018-04-07 15:32:37233
Simon Zünda0d40622020-02-12 13:16:42234 const newClasses = [...activeClasses.values()].sort();
Blink Reformat4c46d092018-04-07 15:32:37235
Jan Scheffler77646c72021-08-13 18:40:38236 this.pendingNodeClasses.set(node, newClasses.join(' '));
Tim van der Lippe2d9a95c2022-01-04 15:18:03237 void this.updateNodeThrottler.schedule(this.flushPendingClasses.bind(this));
Blink Reformat4c46d092018-04-07 15:32:37238 }
239
Jan Scheffler77646c72021-08-13 18:40:38240 private async flushPendingClasses(): Promise<void> {
Blink Reformat4c46d092018-04-07 15:32:37241 const promises = [];
Jan Scheffler77646c72021-08-13 18:40:38242 for (const node of this.pendingNodeClasses.keys()) {
243 this.mutatingNodes.add(node);
244 const promise = node.setAttributeValuePromise('class', (this.pendingNodeClasses.get(node) as string))
Blink Reformat4c46d092018-04-07 15:32:37245 .then(onClassValueUpdated.bind(this, node));
246 promises.push(promise);
247 }
Jan Scheffler77646c72021-08-13 18:40:38248 this.pendingNodeClasses.clear();
Tim van der Lippe32f760f2020-10-01 10:52:15249 await Promise.all(promises);
Blink Reformat4c46d092018-04-07 15:32:37250
Jan Scheffler29905852021-04-14 11:30:31251 function onClassValueUpdated(this: ClassesPaneWidget, node: SDK.DOMModel.DOMNode): void {
Jan Scheffler77646c72021-08-13 18:40:38252 this.mutatingNodes.delete(node);
Blink Reformat4c46d092018-04-07 15:32:37253 }
254 }
Tim van der Lippe13f71fb2019-11-29 11:17:39255}
Blink Reformat4c46d092018-04-07 15:32:37256
Jan Scheffler29905852021-04-14 11:30:31257const cachedClassesMap = new WeakMap<SDK.DOMModel.DOMNode, Map<string, boolean>>();
Blink Reformat4c46d092018-04-07 15:32:37258
Jan Scheffler29905852021-04-14 11:30:31259let buttonProviderInstance: ButtonProvider;
Andres Olivares43c2d9f2021-02-10 16:48:39260
Jan Scheffler29905852021-04-14 11:30:31261export class ButtonProvider implements UI.Toolbar.Provider {
Jan Scheffler77646c72021-08-13 18:40:38262 private readonly button: UI.Toolbar.ToolbarToggle;
263 private view: ClassesPaneWidget;
Jan Scheffler29905852021-04-14 11:30:31264 private constructor() {
Jan Scheffler77646c72021-08-13 18:40:38265 this.button = new UI.Toolbar.ToolbarToggle(i18nString(UIStrings.elementClasses), '');
266 this.button.setText('.cls');
267 this.button.element.classList.add('monospace');
268 this.button.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this.clicked, this);
269 this.view = new ClassesPaneWidget();
Blink Reformat4c46d092018-04-07 15:32:37270 }
271
Jan Scheffler29905852021-04-14 11:30:31272 static instance(opts: {
273 forceNew: boolean|null,
274 } = {forceNew: null}): ButtonProvider {
Andres Olivares43c2d9f2021-02-10 16:48:39275 const {forceNew} = opts;
276 if (!buttonProviderInstance || forceNew) {
277 buttonProviderInstance = new ButtonProvider();
278 }
279
280 return buttonProviderInstance;
281 }
282
Jan Scheffler77646c72021-08-13 18:40:38283 private clicked(): void {
284 ElementsPanel.instance().showToolbarPane(!this.view.isShowing() ? this.view : null, this.button);
Blink Reformat4c46d092018-04-07 15:32:37285 }
286
Jan Scheffler29905852021-04-14 11:30:31287 item(): UI.Toolbar.ToolbarItem {
Jan Scheffler77646c72021-08-13 18:40:38288 return this.button;
Blink Reformat4c46d092018-04-07 15:32:37289 }
Tim van der Lippe13f71fb2019-11-29 11:17:39290}
Blink Reformat4c46d092018-04-07 15:32:37291
Tim van der Lippe97611c92020-02-12 16:56:58292export class ClassNamePrompt extends UI.TextPrompt.TextPrompt {
Jan Scheffler77646c72021-08-13 18:40:38293 private readonly nodeClasses: (arg0: SDK.DOMModel.DOMNode) => Map<string, boolean>;
294 private selectedFrameId: string|null;
295 private classNamesPromise: Promise<string[]>|null;
Jan Scheffler29905852021-04-14 11:30:31296 constructor(nodeClasses: (arg0: SDK.DOMModel.DOMNode) => Map<string, boolean>) {
Blink Reformat4c46d092018-04-07 15:32:37297 super();
Jan Scheffler77646c72021-08-13 18:40:38298 this.nodeClasses = nodeClasses;
299 this.initialize(this.buildClassNameCompletions.bind(this), ' ');
Blink Reformat4c46d092018-04-07 15:32:37300 this.disableDefaultSuggestionForEmptyInput();
Jan Scheffler77646c72021-08-13 18:40:38301 this.selectedFrameId = '';
302 this.classNamesPromise = null;
Blink Reformat4c46d092018-04-07 15:32:37303 }
304
Jan Scheffler77646c72021-08-13 18:40:38305 private async getClassNames(selectedNode: SDK.DOMModel.DOMNode): Promise<string[]> {
Blink Reformat4c46d092018-04-07 15:32:37306 const promises = [];
Jan Scheffler29905852021-04-14 11:30:31307 const completions = new Set<string>();
Jan Scheffler77646c72021-08-13 18:40:38308 this.selectedFrameId = selectedNode.frameId();
Blink Reformat4c46d092018-04-07 15:32:37309
310 const cssModel = selectedNode.domModel().cssModel();
311 const allStyleSheets = cssModel.allStyleSheets();
312 for (const stylesheet of allStyleSheets) {
Jan Scheffler77646c72021-08-13 18:40:38313 if (stylesheet.frameId !== this.selectedFrameId) {
Blink Reformat4c46d092018-04-07 15:32:37314 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34315 }
Johan Bay04d50fc2022-03-01 12:07:04316 const cssPromise = cssModel.getClassNames(stylesheet.id).then(classes => {
Mathias Bynens3abc0952020-04-20 14:15:52317 for (const className of classes) {
318 completions.add(className);
319 }
320 });
Blink Reformat4c46d092018-04-07 15:32:37321 promises.push(cssPromise);
322 }
323
Jan Scheffler29905852021-04-14 11:30:31324 const ownerDocumentId = ((selectedNode.ownerDocument as SDK.DOMModel.DOMDocument).id);
Tim van der Lippe32f760f2020-10-01 10:52:15325
326 const domPromise = selectedNode.domModel().classNamesPromise(ownerDocumentId).then(classes => {
Mathias Bynens3abc0952020-04-20 14:15:52327 for (const className of classes) {
328 completions.add(className);
329 }
330 });
Blink Reformat4c46d092018-04-07 15:32:37331 promises.push(domPromise);
Mathias Bynens3abc0952020-04-20 14:15:52332 await Promise.all(promises);
333 return [...completions];
Blink Reformat4c46d092018-04-07 15:32:37334 }
335
Jan Scheffler77646c72021-08-13 18:40:38336 private async buildClassNameCompletions(expression: string, prefix: string, force?: boolean):
Jan Scheffler29905852021-04-14 11:30:31337 Promise<UI.SuggestBox.Suggestions> {
Tim van der Lippe1d6e57a2019-09-30 11:55:34338 if (!prefix || force) {
Jan Scheffler77646c72021-08-13 18:40:38339 this.classNamesPromise = null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34340 }
Blink Reformat4c46d092018-04-07 15:32:37341
Tim van der Lipped1a00aa2020-08-19 16:03:56342 const selectedNode = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34343 if (!selectedNode || (!prefix && !force && !expression.trim())) {
Mathias Bynens41ea2632020-12-24 05:52:49344 return [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34345 }
Blink Reformat4c46d092018-04-07 15:32:37346
Jan Scheffler77646c72021-08-13 18:40:38347 if (!this.classNamesPromise || this.selectedFrameId !== selectedNode.frameId()) {
348 this.classNamesPromise = this.getClassNames(selectedNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34349 }
Blink Reformat4c46d092018-04-07 15:32:37350
Jan Scheffler77646c72021-08-13 18:40:38351 let completions: string[] = await this.classNamesPromise;
352 const classesMap = this.nodeClasses((selectedNode as SDK.DOMModel.DOMNode));
Mathias Bynens41ea2632020-12-24 05:52:49353 completions = completions.filter(value => !classesMap.get(value));
Blink Reformat4c46d092018-04-07 15:32:37354
Mathias Bynens41ea2632020-12-24 05:52:49355 if (prefix[0] === '.') {
356 completions = completions.map(value => '.' + value);
357 }
358 return completions.filter(value => value.startsWith(prefix)).sort().map(completion => {
359 return {
360 text: completion,
361 title: undefined,
362 subtitle: undefined,
363 iconType: undefined,
364 priority: undefined,
365 isSecondary: undefined,
366 subtitleRenderer: undefined,
367 selectionRange: undefined,
368 hideGhostText: undefined,
369 iconElement: undefined,
370 };
Blink Reformat4c46d092018-04-07 15:32:37371 });
372 }
Tim van der Lippe13f71fb2019-11-29 11:17:39373}