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

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

Issue 2343773002: DevTools: Autocomplete class names in ClassesPaneWidget (Closed)
Patch Set: Simplified Created 4 years, 3 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 | third_party/WebKit/Source/devtools/front_end/elements/elementsPanel.css » ('j') | 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 /** 5 /**
6 * @constructor 6 * @constructor
7 * @extends {WebInspector.Widget} 7 * @extends {WebInspector.Widget}
8 */ 8 */
9 WebInspector.ClassesPaneWidget = function() 9 WebInspector.ClassesPaneWidget = function()
10 { 10 {
11 WebInspector.Widget.call(this); 11 WebInspector.Widget.call(this);
12 this.element.className = "styles-element-classes-pane"; 12 this.element.className = "styles-element-classes-pane";
13 var container = this.element.createChild("div", "title-container"); 13 var container = this.element.createChild("div", "title-container");
14 this._input = container.createChild("input", "new-class-input monospace"); 14 this._input = container.createChild("div", "new-class-input monospace");
15 this._input.placeholder = WebInspector.UIString("Add new class"); 15 this._input.setAttribute("placeholder" , WebInspector.UIString("Add new clas s"));
16 this._input.addEventListener("keydown", this._onKeyDown.bind(this), false);
17 this.setDefaultFocusedElement(this._input); 16 this.setDefaultFocusedElement(this._input);
17
18 this._classesContainer = this.element.createChild("div", "source-code"); 18 this._classesContainer = this.element.createChild("div", "source-code");
19 this._classesContainer.classList.add("styles-element-classes-container"); 19 this._classesContainer.classList.add("styles-element-classes-container");
20 this._prompt = new WebInspector.ClassesPaneWidget.ClassNamePrompt();
21 this._prompt.setAutocompletionTimeout(0);
22 this._prompt.renderAsBlock();
23
24 this._frameClasses = new Map();
25 this._selectedNode = null;
26 var proxyElement = this._prompt.attach(this._input);
27 proxyElement.addEventListener("keydown", this._onKeyDown.bind(this), false);
20 28
21 WebInspector.targetManager.addModelListener(WebInspector.DOMModel, WebInspec tor.DOMModel.Events.DOMMutated, this._onDOMMutated, this); 29 WebInspector.targetManager.addModelListener(WebInspector.DOMModel, WebInspec tor.DOMModel.Events.DOMMutated, this._onDOMMutated, this);
30 WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Event s.MainFrameNavigated, this._mainFrameNavigated, this);
31
22 /** @type {!Set<!WebInspector.DOMNode>} */ 32 /** @type {!Set<!WebInspector.DOMNode>} */
23 this._mutatingNodes = new Set(); 33 this._mutatingNodes = new Set();
24 WebInspector.context.addFlavorChangeListener(WebInspector.DOMNode, this._upd ate, this); 34 WebInspector.context.addFlavorChangeListener(WebInspector.DOMNode, this._upd ate, this);
25 } 35 }
26 36
27 WebInspector.ClassesPaneWidget._classesSymbol = Symbol("WebInspector.ClassesPane Widget._classesSymbol"); 37 WebInspector.ClassesPaneWidget._classesSymbol = Symbol("WebInspector.ClassesPane Widget._classesSymbol");
28 38
29 WebInspector.ClassesPaneWidget.prototype = { 39 WebInspector.ClassesPaneWidget.prototype = {
30 /** 40 /**
31 * @param {!Event} event 41 * @param {!Event} event
32 */ 42 */
33 _onKeyDown: function(event) 43 _onKeyDown: function(event)
34 { 44 {
35 var text = event.target.value; 45 var text = event.target.innerText;
36 if (isEscKey(event)) { 46 if (isEscKey(event)) {
37 event.target.value = ""; 47 this._hidePrompt();
48 event.target.innerText = "";
38 if (!text.isWhitespace()) 49 if (!text.isWhitespace())
39 event.consume(true); 50 event.consume(true);
40 return; 51 return;
41 } 52 }
42 53 if (!this._prompt.isSuggestBoxVisible()) {
54 this._collectFrameClasses(this._selectedFrameId());
55 }
43 if (!isEnterKey(event)) 56 if (!isEnterKey(event))
44 return; 57 return;
45 var node = WebInspector.context.flavor(WebInspector.DOMNode); 58 var node = WebInspector.context.flavor(WebInspector.DOMNode);
46 if (!node) 59 if (!node)
47 return; 60 return;
48 61 this._hidePrompt();
49 event.target.value = ""; 62 event.target.innerText = "";
50 var classNames = text.split(/[.,\s]/); 63 var classNames = text.split(/[.,\s]/);
51 for (var className of classNames) { 64 for (var className of classNames) {
52 var className = className.trim(); 65 var className = className.trim();
53 if (!className.length) 66 if (!className.length)
54 continue; 67 continue;
55 this._toggleClass(node, className, true); 68 this._toggleClass(node, className, true);
56 } 69 }
57 this._installNodeClasses(node); 70 this._installNodeClasses(node);
58 this._update(); 71 this._update();
59 event.consume(true); 72 event.consume(true);
60 }, 73 },
61 74
62 /** 75 /**
63 * @param {!WebInspector.Event} event 76 * @param {!WebInspector.Event} event
64 */ 77 */
78 _mainFrameNavigated: function(event)
79 {
80 this._selectedNode = null;
81 this._frameClasses = new Map();
82 this._prompt.clearCompletions();
83 },
84
85 _hidePrompt: function()
lushnikov 2016/09/20 17:11:09 let's inline this
86 {
87 this._prompt.clearAutoComplete();
88 },
89
90 _getDomClasses: function()
lushnikov 2016/09/20 16:46:21 Can we move all the logic inside the ClassNameProm
ahmetemirercin 2016/09/20 18:38:40 Correct me if I'm wrong: cssModel will collect sty
91 {
92 if (!this._selectedNode)
93 return;
94
95 var inspectedDocument = this._selectedNode.ownerDocument;
96
97 if (!inspectedDocument)
98 return;
99
100 var frameId = this._getNodeFrameId(inspectedDocument);
101 /**
102 * @param {!Array.<string>} classNames
103 * @this {!WebInspector.ClassesPaneWidget}
104 */
105 function classNamesCallback(classNames)
106 {
107 this._addFrameClasses(frameId, classNames);
108 }
109
110 inspectedDocument.domModel().classNamesPromise(inspectedDocument.id).the n(classNamesCallback.bind(this));
111 },
112
113 _getCssClasses: function()
114 {
115 if (!this._selectedNode)
116 return;
117
118 var frameId = this._selectedFrameId();
119 var cssModel = WebInspector.CSSModel.fromTarget(this._selectedNode.targe t());
120 var headers = cssModel.allStyleSheets();
121 for (var stylesheet of cssModel.allStyleSheets())
122 if (stylesheet.frameId === frameId)
123 this._getStylesheetClasses(stylesheet);
124 },
125
126 /**
127 * @param {?PageAgent.FrameId} frameId
128 * @return {!Set<string>}
129 */
130 _getFrameClasses: function(frameId)
131 {
132 if (!this._frameClasses.get(frameId))
133 this._frameClasses.set(frameId, new Set());
134
135 return this._frameClasses.get(frameId);
136 },
137
138 /**
139 * @return {?PageAgent.FrameId}
140 */
141 _selectedFrameId: function()
142 {
143 return this._getNodeFrameId(this._selectedNode);
144 },
145
146 /**
147 * @param {?WebInspector.DOMNode} node
148 * @return {?PageAgent.FrameId}
149 */
150 _getNodeFrameId: function(node){
151 return node ? node.frameId() || WebInspector.ResourceTreeModel.fromTarge t(node.target()).mainFrame.id : 0;
152 },
153
154 /**
155 * @param {!WebInspector.CSSStyleSheetHeader} stylesheet
156 */
157 _getStylesheetClasses: function(stylesheet)
158 {
159 /**
160 * @param {!WebInspector.CSSStyleSheetHeader} stylsheet
161 * @param {!Array.<string>} classNames
162 * @this {!WebInspector.ClassesPaneWidget}
163 */
164 function classNamesCallback(stylsheet, classNames)
165 {
166 this._addFrameClasses(stylsheet.frameId, classNames);
167 }
168
169 var cssModel = stylesheet.cssModel();
170 cssModel.classNamesPromise(stylesheet.id).then(classNamesCallback.bind(t his, stylesheet));
171 },
172
173 /**
174 * @param {?PageAgent.FrameId} frameId
175 * @param {!Array.<string>} classNames
176 */
177 _addFrameClasses: function(frameId, classNames)
178 {
179 if (!classNames)
180 return;
181 this._getFrameClasses(frameId).addAll(classNames);
182 this._updateCompletions(frameId);
183 },
184
185 /**
186 * @param {?PageAgent.FrameId} frameId
187 */
188 _updateCompletions: function(frameId)
189 {
190 if (frameId === this._selectedFrameId())
191 this._prompt.updateCompletions(this._getFrameClasses(frameId).values Array().sort());
192 },
193
194 /**
195 * @param {?PageAgent.FrameId} frameId
196 */
197 _collectFrameClasses: function(frameId)
198 {
199 this._getDomClasses();
200 this._getCssClasses();
201 },
202
203 /**
204 * @param {!WebInspector.Event} event
205 */
65 _onDOMMutated: function(event) 206 _onDOMMutated: function(event)
66 { 207 {
67 var node = /** @type {!WebInspector.DOMNode} */(event.data); 208 var node = /** @type {!WebInspector.DOMNode} */(event.data);
68 if (this._mutatingNodes.has(node)) 209 if (this._mutatingNodes.has(node))
69 return; 210 return;
70 delete node[WebInspector.ClassesPaneWidget._classesSymbol]; 211 delete node[WebInspector.ClassesPaneWidget._classesSymbol];
71 this._update(); 212 this._update();
72 }, 213 },
73 214
74 /** 215 /**
75 * @override 216 * @override
76 */ 217 */
77 wasShown: function() 218 wasShown: function()
78 { 219 {
79 this._update(); 220 this._update();
80 }, 221 },
81 222
82 _update: function() 223 _update: function()
83 { 224 {
84 if (!this.isShowing()) 225 if (!this.isShowing())
85 return; 226 return;
86
87 var node = WebInspector.context.flavor(WebInspector.DOMNode); 227 var node = WebInspector.context.flavor(WebInspector.DOMNode);
88 if (node) 228 if (node)
89 node = node.enclosingElementOrSelf(); 229 node = node.enclosingElementOrSelf();
90 230
91 this._classesContainer.removeChildren(); 231 this._classesContainer.removeChildren();
92 this._input.disabled = !node; 232 this._input.disabled = !node;
93 233
94 if (!node) 234 if (!node)
95 return; 235 return;
96 236
237 this._selectedNode = node;
97 var classes = this._nodeClasses(node); 238 var classes = this._nodeClasses(node);
98 var keys = classes.keysArray(); 239 var keys = classes.keysArray();
99 keys.sort(String.caseInsensetiveComparator); 240 keys.sort(String.caseInsensetiveComparator);
100 for (var i = 0; i < keys.length; ++i) { 241 for (var i = 0; i < keys.length; ++i) {
101 var className = keys[i]; 242 var className = keys[i];
102 var label = createCheckboxLabel(className, classes.get(className)); 243 var label = createCheckboxLabel(className, classes.get(className));
103 label.visualizeFocus = true; 244 label.visualizeFocus = true;
104 label.classList.add("monospace"); 245 label.classList.add("monospace");
105 label.checkboxElement.addEventListener("click", this._onClick.bind(t his, className), false); 246 label.checkboxElement.addEventListener("click", this._onClick.bind(t his, className), false);
106 this._classesContainer.appendChild(label); 247 this._classesContainer.appendChild(label);
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
204 345
205 /** 346 /**
206 * @override 347 * @override
207 * @return {!WebInspector.ToolbarItem} 348 * @return {!WebInspector.ToolbarItem}
208 */ 349 */
209 item: function() 350 item: function()
210 { 351 {
211 return this._button; 352 return this._button;
212 } 353 }
213 } 354 }
355
356 /**
357 * @constructor
358 * @extends {WebInspector.TextPrompt}
359 */
360 WebInspector.ClassesPaneWidget.ClassNamePrompt = function()
361 {
362 WebInspector.TextPrompt.call(this, this._buildClassNameCompletions.bind(this ), " ");
363 this.setSuggestBoxEnabled(true);
364 this.disableDefaultSuggestionForEmptyInput();
365 this._classNameCompletions = [];
366 }
367
368 WebInspector.ClassesPaneWidget.ClassNamePrompt.prototype = {
369 /**
370 * @override
371 * @param {!Event} event
372 */
373 onKeyDown: function(event)
374 {
375 switch (event.key) {
376 case "Enter":
377 // Accept any available autocompletions and advance to the next fiel d.
378 if (this.autoCompleteElement && this.autoCompleteElement.textContent .length) {
379 this.acceptAutoComplete();
380 return;
381 }
382 break;
383 }
384
385 WebInspector.TextPrompt.prototype.onKeyDown.call(this, event);
386 },
387
388 /**
389 * @param {!Array.<string>} classNameList
390 */
391 updateCompletions: function(classNameList)
392 {
393 this._classNameCompletions = classNameList;
394 },
395
396 clearCompletions: function()
397 {
398 this._classNameCompletions = [];
399 },
400
401 /**
402 * @param {!Element} proxyElement
403 * @param {!Range} wordRange
404 * @param {boolean} force
405 * @param {function(!Array.<string>, number=)} completionsReadyCallback
406 */
407 _buildClassNameCompletions: function(proxyElement, wordRange, force, complet ionsReadyCallback)
408 {
409 var prefix = wordRange.toString();
410
411 if (!prefix && !force && !proxyElement.textContent.length) {
412 completionsReadyCallback([]);
413 return;
414 }
415
416 var results = this._classNameCompletions.filter((value) => value.startsW ith(prefix));
417 var selectedIndex = 0;
418 completionsReadyCallback(results, selectedIndex);
419 },
420
421 __proto__: WebInspector.TextPrompt.prototype
422 }
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/Source/devtools/front_end/elements/elementsPanel.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698