Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/elements/ClassesPaneWidget.js

Issue 2646283002: ClassesPaneWidget - Add ability to quickly preview autocompleted CSS classes. (Closed)
Patch Set: Add missing jsdoc Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. 1 // 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 /** 4 /**
5 * @unrestricted 5 * @unrestricted
6 */ 6 */
7 Elements.ClassesPaneWidget = class extends UI.Widget { 7 Elements.ClassesPaneWidget = class extends UI.Widget {
8 constructor() { 8 constructor() {
9 super(); 9 super();
10 this.element.className = 'styles-element-classes-pane'; 10 this.element.className = 'styles-element-classes-pane';
11 var container = this.element.createChild('div', 'title-container'); 11 var container = this.element.createChild('div', 'title-container');
12 this._input = container.createChild('div', 'new-class-input monospace'); 12 this._input = container.createChild('div', 'new-class-input monospace');
13 this.setDefaultFocusedElement(this._input); 13 this.setDefaultFocusedElement(this._input);
14 this._classesContainer = this.element.createChild('div', 'source-code'); 14 this._classesContainer = this.element.createChild('div', 'source-code');
15 this._classesContainer.classList.add('styles-element-classes-container'); 15 this._classesContainer.classList.add('styles-element-classes-container');
16 this._prompt = new Elements.ClassesPaneWidget.ClassNamePrompt(); 16 this._prompt = new Elements.ClassesPaneWidget.ClassNamePrompt();
17 this._prompt.setAutocompletionTimeout(0); 17 this._prompt.setAutocompletionTimeout(0);
18 this._prompt.addEventListener(UI.TextPrompt.Events.ItemApplied, this._applyF reeFlowClassListTextEdit.bind(this), this);
19 this._prompt.addEventListener(UI.TextPrompt.Events.ItemAccepted, this._apply FreeFlowClassListTextEdit.bind(this), this);
18 this._prompt.renderAsBlock(); 20 this._prompt.renderAsBlock();
21 /** @type {!Set<string>} */
22 this._previewClasses = new Set();
lushnikov 2017/02/07 22:01:37 Why do we need a set? afaiu there's only one sugge
kdzwinel 2017/02/07 23:20:05 One suggestion, but there can be multiple classes
19 23
20 var proxyElement = this._prompt.attach(this._input); 24 var proxyElement = this._prompt.attach(this._input);
21 this._prompt.setPlaceholder(Common.UIString('Add new class')); 25 this._prompt.setPlaceholder(Common.UIString('Add new class'));
22 proxyElement.addEventListener('keydown', this._onKeyDown.bind(this), false); 26 proxyElement.addEventListener('keydown', this._onKeyDown.bind(this), false);
23 27
24 SDK.targetManager.addModelListener(SDK.DOMModel, SDK.DOMModel.Events.DOMMuta ted, this._onDOMMutated, this); 28 SDK.targetManager.addModelListener(SDK.DOMModel, SDK.DOMModel.Events.DOMMuta ted, this._onDOMMutated, this);
25 /** @type {!Set<!SDK.DOMNode>} */ 29 /** @type {!Set<!SDK.DOMNode>} */
26 this._mutatingNodes = new Set(); 30 this._mutatingNodes = new Set();
27 UI.context.addFlavorChangeListener(SDK.DOMNode, this._update, this); 31 UI.context.addFlavorChangeListener(SDK.DOMNode, this._update, this);
28 } 32 }
29 33
30 /** 34 /**
31 * @param {!Event} event 35 * @param {!Event} event
32 */ 36 */
33 _onKeyDown(event) { 37 _onKeyDown(event) {
34 var text = event.target.textContent; 38 const text = event.target.textContent;
lushnikov 2017/02/07 22:01:37 nit: we don't use consts so far, let's use "var"
kdzwinel 2017/02/07 23:20:06 AFAIK `let` is not yet used, but `const` is fine.
39 const node = UI.context.flavor(SDK.DOMNode);
40 if (!node)
41 return;
42
35 if (isEscKey(event)) { 43 if (isEscKey(event)) {
36 event.target.textContent = ''; 44 event.target.textContent = '';
37 if (!text.isWhitespace()) 45 if (!text.isWhitespace())
38 event.consume(true); 46 event.consume(true);
47 this._previewClasses.clear();
48 this._installNodeClasses(node);
39 return; 49 return;
40 } 50 }
41 51
42 if (!isEnterKey(event)) 52 if (!isEnterKey(event))
43 return; 53 return;
44 var node = UI.context.flavor(SDK.DOMNode);
45 if (!node)
46 return;
47 54
48 this._prompt.clearAutocomplete(); 55 this._prompt.clearAutocomplete();
56 this._previewClasses.clear();
49 event.target.textContent = ''; 57 event.target.textContent = '';
50 var classNames = text.split(/[.,\s]/); 58 const classNames = text.split(/[.,\s]/);
51 for (var className of classNames) { 59 for (var className of classNames) {
52 var className = className.trim(); 60 className = className.trim();
53 if (!className.length) 61 if (!className.length)
54 continue; 62 continue;
55 this._toggleClass(node, className, true); 63 this._toggleClass(node, className, true);
56 } 64 }
57 this._installNodeClasses(node); 65 this._installNodeClasses(node);
58 this._update(); 66 this._update();
59 event.consume(true); 67 event.consume(true);
60 } 68 }
61 69
62 /** 70 /**
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
110 */ 118 */
111 _onClick(className, event) { 119 _onClick(className, event) {
112 var node = UI.context.flavor(SDK.DOMNode); 120 var node = UI.context.flavor(SDK.DOMNode);
113 if (!node) 121 if (!node)
114 return; 122 return;
115 var enabled = event.target.checked; 123 var enabled = event.target.checked;
116 this._toggleClass(node, className, enabled); 124 this._toggleClass(node, className, enabled);
117 this._installNodeClasses(node); 125 this._installNodeClasses(node);
118 } 126 }
119 127
128 _applyFreeFlowClassListTextEdit() {
129 const text = this._prompt.textWithCurrentSuggestion();
130 const node = UI.context.flavor(SDK.DOMNode);
131 if (!node)
132 return;
133
134 this._previewClasses.clear();
135 const classNames = text.split(/[.,\s]/);
lushnikov 2017/02/07 22:01:37 that's unfortunate we had to copy the regex over h
kdzwinel 2017/02/07 23:20:05 Good point! It should probably be extracted to a c
136 for (var className of classNames) {
137 className = className.trim();
138 if (!className.length)
139 continue;
140 this._previewClasses.add(className);
141 }
142 this._installNodeClasses(node);
143 }
144
120 /** 145 /**
121 * @param {!SDK.DOMNode} node 146 * @param {!SDK.DOMNode} node
147 * @return {!Array<string>}
148 */
149 _getActiveClasses(node) {
lushnikov 2017/02/07 22:01:37 why do you need to extract this?
kdzwinel 2017/02/07 23:20:05 No real need, I did it just for the readability of
150 const classes = this._nodeClasses(node);
151 const activeClasses = new Set();
152 for (var className of classes.keys()) {
153 if (classes.get(className))
154 activeClasses.add(className);
155 }
156 for (className of this._previewClasses)
157 activeClasses.add(className);
158
159 return activeClasses.valuesArray().sort();
160 }
161
162 /**
163 * @param {!SDK.DOMNode} node
122 * @return {!Map<string, boolean>} 164 * @return {!Map<string, boolean>}
123 */ 165 */
124 _nodeClasses(node) { 166 _nodeClasses(node) {
125 var result = node[Elements.ClassesPaneWidget._classesSymbol]; 167 var result = node[Elements.ClassesPaneWidget._classesSymbol];
126 if (!result) { 168 if (!result) {
127 var classAttribute = node.getAttribute('class') || ''; 169 var classAttribute = node.getAttribute('class') || '';
128 var classes = classAttribute.split(/\s/); 170 var classes = classAttribute.split(/\s/);
129 result = new Map(); 171 result = new Map();
130 for (var i = 0; i < classes.length; ++i) { 172 for (var i = 0; i < classes.length; ++i) {
131 var className = classes[i].trim(); 173 var className = classes[i].trim();
(...skipping 13 matching lines...) Expand all
145 */ 187 */
146 _toggleClass(node, className, enabled) { 188 _toggleClass(node, className, enabled) {
147 var classes = this._nodeClasses(node); 189 var classes = this._nodeClasses(node);
148 classes.set(className, enabled); 190 classes.set(className, enabled);
149 } 191 }
150 192
151 /** 193 /**
152 * @param {!SDK.DOMNode} node 194 * @param {!SDK.DOMNode} node
153 */ 195 */
154 _installNodeClasses(node) { 196 _installNodeClasses(node) {
155 var classes = this._nodeClasses(node); 197 const activeClasses = this._getActiveClasses(node);
156 var activeClasses = new Set();
157 for (var className of classes.keys()) {
158 if (classes.get(className))
159 activeClasses.add(className);
160 }
161
162 var newClasses = activeClasses.valuesArray();
163 newClasses.sort();
164 this._mutatingNodes.add(node); 198 this._mutatingNodes.add(node);
165 node.setAttributeValue('class', newClasses.join(' '), onClassNameUpdated.bin d(this)); 199 node.setAttributeValue('class', activeClasses.join(' '), onClassNameUpdated. bind(this));
166 200
167 /** 201 /**
168 * @this {Elements.ClassesPaneWidget} 202 * @this {Elements.ClassesPaneWidget}
169 */ 203 */
170 function onClassNameUpdated() { 204 function onClassNameUpdated() {
171 this._mutatingNodes.delete(node); 205 this._mutatingNodes.delete(node);
172 } 206 }
173 } 207 }
174 }; 208 };
175 209
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
255 if (!this._classNamesPromise || this._selectedFrameId !== selectedNode.frame Id()) 289 if (!this._classNamesPromise || this._selectedFrameId !== selectedNode.frame Id())
256 this._classNamesPromise = this._getClassNames(selectedNode); 290 this._classNamesPromise = this._getClassNames(selectedNode);
257 291
258 return this._classNamesPromise.then(completions => { 292 return this._classNamesPromise.then(completions => {
259 if (prefix[0] === '.') 293 if (prefix[0] === '.')
260 completions = completions.map(value => '.' + value); 294 completions = completions.map(value => '.' + value);
261 return completions.filter(value => value.startsWith(prefix)).map(completio n => ({title: completion})); 295 return completions.filter(value => value.startsWith(prefix)).map(completio n => ({title: completion}));
262 }); 296 });
263 } 297 }
264 }; 298 };
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698