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

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/quick_open/FilteredListWidget.js

Issue 2773583002: [DevTools] Introduce a sidebar with a drop-down
Patch Set: [DevTools] Introduce a sidebar with a drop-down Created 3 years, 8 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
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be 3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file. 4 * found in the LICENSE file.
5 */ 5 */
6 /** 6 /**
7 * @unrestricted 7 * @unrestricted
8 * @implements {UI.ListDelegate} 8 * @implements {UI.ListDelegate}
9 */ 9 */
10 QuickOpen.FilteredListWidget = class extends UI.VBox { 10 QuickOpen.FilteredListWidget = class extends UI.VBox {
11 /** 11 /**
12 * @param {?QuickOpen.FilteredListWidget.Provider} provider 12 * @param {?QuickOpen.FilteredListWidget.Provider} provider
13 * @param {!Array<string>=} promptHistory 13 * @param {!Array<string>=} promptHistory
14 * @param {function(string)=} queryChangedCallback 14 * @param {function(string)=} queryChangedCallback
15 */ 15 */
16 constructor(provider, promptHistory, queryChangedCallback) { 16 constructor(provider, promptHistory, queryChangedCallback) {
17 super(true); 17 super(true);
18 this._promptHistory = promptHistory || []; 18 this._promptHistory = promptHistory || [];
19 19
20 this.contentElement.classList.add('filtered-list-widget'); 20 this.contentElement.classList.add('filtered-list-widget');
21 this.contentElement.addEventListener('keydown', this._onKeyDown.bind(this), true); 21 this.contentElement.addEventListener('keydown', this._onKeyDown.bind(this), true);
22 this.registerRequiredCSS('quick_open/filteredListWidget.css'); 22 this.registerRequiredCSS('quick_open/filteredListWidget.css');
23 23
24 this._promptElement = this.contentElement.createChild('div', 'filtered-list- widget-input'); 24 var promptContainer = this.contentElement.createChild('div', 'filtered-list- widget-input-container');
25 this._iconContainer = promptContainer.createChild('div');
26 var container = promptContainer.createChild('div', 'filtered-list-widget-inp ut');
27 this._promptElement = container.createChild('div');
25 this._promptElement.setAttribute('spellcheck', 'false'); 28 this._promptElement.setAttribute('spellcheck', 'false');
26 this._promptElement.setAttribute('contenteditable', 'plaintext-only'); 29 this._promptElement.setAttribute('contenteditable', 'plaintext-only');
27 this._prompt = new UI.TextPrompt(); 30 this._prompt = new UI.TextPrompt();
28 this._prompt.initialize(() => Promise.resolve([])); 31 this._prompt.initialize(() => Promise.resolve([]));
29 this._prompt.renderAsBlock(); 32 this._prompt.renderAsBlock();
30 var promptProxy = this._prompt.attach(this._promptElement); 33 var promptProxy = this._prompt.attach(this._promptElement);
31 promptProxy.addEventListener('input', this._onInput.bind(this), false); 34 promptProxy.addEventListener('input', this._onInput.bind(this), false);
32 promptProxy.classList.add('filtered-list-widget-prompt-element'); 35 promptProxy.classList.add('filtered-list-widget-prompt-element');
33 36
34 this._bottomElementsContainer = this.contentElement.createChild('div', 'vbox '); 37 this._bottomElementsContainer = this.contentElement.createChild('div', 'vbox ');
35 this._progressElement = this._bottomElementsContainer.createChild('div', 'fi ltered-list-widget-progress'); 38 this._progressElement = this._bottomElementsContainer.createChild('div', 'fi ltered-list-widget-progress');
36 this._progressBarElement = this._progressElement.createChild('div', 'filtere d-list-widget-progress-bar'); 39 this._progressBarElement = this._progressElement.createChild('div', 'filtere d-list-widget-progress-bar');
37 40
38 /** @type {!UI.ListControl<number>} */ 41 /** @type {!UI.ListControl<number>} */
39 this._list = new UI.ListControl(this, UI.ListMode.EqualHeightItems); 42 this._list = new UI.ListControl(this, UI.ListMode.EqualHeightItems);
40 this._itemElementsContainer = this._list.element; 43 this._itemElementsContainer = this._list.element;
44 this._itemElementsContainer.addEventListener('mousemove', event => this._upd ateHover(event));
45 this._itemElementsContainer.addEventListener('mouseout', event => this._upda teHover(event));
41 this._itemElementsContainer.classList.add('container'); 46 this._itemElementsContainer.classList.add('container');
42 this._bottomElementsContainer.appendChild(this._itemElementsContainer); 47 this._bottomElementsContainer.appendChild(this._itemElementsContainer);
43 this._itemElementsContainer.addEventListener('click', this._onClick.bind(thi s), false); 48 this._itemElementsContainer.addEventListener('click', this._onClick.bind(thi s), false);
44 49
45 this._notFoundElement = this._bottomElementsContainer.createChild('div', 'no t-found-text'); 50 this._notFoundElement = this._bottomElementsContainer.createChild('div', 'no t-found-text');
46 this._notFoundElement.classList.add('hidden'); 51 this._notFoundElement.classList.add('hidden');
47 52
48 this.setDefaultFocusedElement(this._promptElement); 53 this.setDefaultFocusedElement(this._promptElement);
49 54
50 this._prefix = ''; 55 this._prefix = '';
51 this._provider = provider; 56 this._provider = provider;
52 this._queryChangedCallback = queryChangedCallback; 57 this._queryChangedCallback = queryChangedCallback;
58
59 /** @type {?number} */
60 this._selectedItemIndex = null;
53 } 61 }
54 62
55 /** 63 /**
56 * @param {string} query 64 * @param {string} query
57 * @return {!RegExp} 65 * @return {!RegExp}
58 */ 66 */
59 static filterRegex(query) { 67 static filterRegex(query) {
60 const toEscape = String.regexSpecialCharacters(); 68 const toEscape = String.regexSpecialCharacters();
61 var regexString = ''; 69 var regexString = '';
62 for (var i = 0; i < query.length; ++i) { 70 for (var i = 0; i < query.length; ++i) {
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 var ranges = rangesForMatch(text, query); 112 var ranges = rangesForMatch(text, query);
105 if (!ranges || caseInsensitive) 113 if (!ranges || caseInsensitive)
106 ranges = rangesForMatch(text.toUpperCase(), query.toUpperCase()); 114 ranges = rangesForMatch(text.toUpperCase(), query.toUpperCase());
107 if (ranges) { 115 if (ranges) {
108 UI.highlightRangesWithStyleClass(element, ranges, 'highlight'); 116 UI.highlightRangesWithStyleClass(element, ranges, 'highlight');
109 return true; 117 return true;
110 } 118 }
111 return false; 119 return false;
112 } 120 }
113 121
114 showAsDialog() { 122 /**
123 * @param {!AnchorBox=} anchorBox
124 * @param {!UI.GlassPane.AnchorBehavior=} anchorBehavior
125 */
126 showAsDialog(anchorBox, anchorBehavior) {
115 this._dialog = new UI.Dialog(); 127 this._dialog = new UI.Dialog();
128 if (anchorBox)
129 this._dialog.setContentAnchorBox(anchorBox);
130 else
131 this._dialog.setContentPosition(null, 22);
116 this._dialog.setMaxContentSize(new UI.Size(504, 340)); 132 this._dialog.setMaxContentSize(new UI.Size(504, 340));
117 this._dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.SetExactWidthMaxHeigh t); 133 this._dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.SetExactWidthMaxHeigh t);
118 this._dialog.setContentPosition(null, 22); 134 if (anchorBehavior)
135 this._dialog.setAnchorBehavior(anchorBehavior);
119 this.show(this._dialog.contentElement); 136 this.show(this._dialog.contentElement);
120 this._dialog.show(); 137 this._dialog.show();
121 } 138 }
122 139
123 /** 140 /**
124 * @param {string} prefix 141 * @param {string} prefix
125 */ 142 */
126 setPrefix(prefix) { 143 setPrefix(prefix) {
127 this._prefix = prefix; 144 this._prefix = prefix;
128 } 145 }
129 146
130 /** 147 /**
131 * @param {?QuickOpen.FilteredListWidget.Provider} provider 148 * @param {?QuickOpen.FilteredListWidget.Provider} provider
132 */ 149 */
133 setProvider(provider) { 150 setProvider(provider) {
134 if (provider === this._provider) 151 if (provider === this._provider)
135 return; 152 return;
136 153
137 if (this._provider) 154 if (this._provider)
138 this._provider.detach(); 155 this._provider.detach();
139 this._clearTimers(); 156 this._clearTimers();
140 157
141 this._provider = provider; 158 this._provider = provider;
142 if (this.isShowing()) 159 if (this.isShowing())
143 this._attachProvider(); 160 this._attachProvider();
144 } 161 }
145 162
163 /**
164 * @param {?string} icon
165 */
166 setInputIcon(icon) {
167 this._iconContainer.removeChildren();
168 if (icon)
169 this._iconContainer.appendChild(UI.Icon.create(icon, 'filtered-list-input- icon'));
170 }
171
146 _attachProvider() { 172 _attachProvider() {
147 if (this._provider) { 173 if (this._provider) {
148 this._provider.setRefreshCallback(this._itemsLoaded.bind(this, this._provi der)); 174 this._provider.setRefreshCallback(this._itemsLoaded.bind(this, this._provi der));
149 this._provider.attach(); 175 this._provider.attach();
150 } 176 }
151 this._itemsLoaded(this._provider); 177 this._itemsLoaded(this._provider);
152 } 178 }
153 179
154 /** 180 /**
155 * @return {string} 181 * @return {string}
156 */ 182 */
157 _value() { 183 _value() {
158 return this._prompt.text().trim(); 184 return this._prompt.text().trim();
159 } 185 }
160 186
161 _cleanValue() { 187 _cleanValue() {
162 return this._value().substring(this._prefix.length); 188 return this._value().substring(this._prefix.length);
163 } 189 }
164 190
165 /** 191 /**
192 * @param {!Event} event
193 */
194 _updateHover(event) {
195 var element = event.deepElementFromPoint();
dgozman 2017/04/05 20:52:55 Implement this similarly to click? var item = thi
eostroukhov 2017/05/03 00:33:46 Done.
196 var listItemElement = element && element.enclosingNodeOrSelfWithClass('filte red-list-widget-item');
197 if (listItemElement && listItemElement.classList.contains('hovered'))
198 return;
199 for (var child of this._list.element.getElementsByClassName('hovered'))
dgozman 2017/04/05 20:52:55 Introduce |this._hoverElement| and remove the clas
eostroukhov 2017/05/03 00:33:46 Done.
200 child.classList.remove('hovered');
201 var item = listItemElement && this._list.itemForNode(listItemElement);
202 if (listItemElement)
203 listItemElement.classList.add('hovered');
dgozman 2017/04/05 20:52:56 This should go to _hover.
eostroukhov 2017/05/03 00:33:46 Done.
204 this._hover(item);
205 }
206
207 /**
166 * @override 208 * @override
167 */ 209 */
168 wasShown() { 210 wasShown() {
169 this._list.invalidateItemHeight(); 211 this._list.invalidateItemHeight();
170 this._attachProvider(); 212 this._attachProvider();
171 } 213 }
172 214
173 /** 215 /**
174 * @override 216 * @override
175 */ 217 */
176 willHide() { 218 willHide() {
219 this._hover(null);
177 if (this._provider) 220 if (this._provider)
178 this._provider.detach(); 221 this._provider.detach();
179 this._clearTimers(); 222 this._clearTimers();
180 } 223 }
181 224
182 _clearTimers() { 225 _clearTimers() {
183 clearTimeout(this._filterTimer); 226 clearTimeout(this._filterTimer);
184 clearTimeout(this._scoringTimer); 227 clearTimeout(this._scoringTimer);
185 clearTimeout(this._loadTimeout); 228 clearTimeout(this._loadTimeout);
186 delete this._filterTimer; 229 delete this._filterTimer;
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
257 * @param {?number} from 300 * @param {?number} from
258 * @param {?number} to 301 * @param {?number} to
259 * @param {?Element} fromElement 302 * @param {?Element} fromElement
260 * @param {?Element} toElement 303 * @param {?Element} toElement
261 */ 304 */
262 selectedItemChanged(from, to, fromElement, toElement) { 305 selectedItemChanged(from, to, fromElement, toElement) {
263 if (fromElement) 306 if (fromElement)
264 fromElement.classList.remove('selected'); 307 fromElement.classList.remove('selected');
265 if (toElement) 308 if (toElement)
266 toElement.classList.add('selected'); 309 toElement.classList.add('selected');
310 this._hover(to);
dgozman 2017/04/05 20:52:55 Why this?
eostroukhov 2017/05/03 00:33:46 For keyboard navigation. It is extremely confusing
267 } 311 }
268 312
269 /** 313 /**
314 * @param {number} item
315 */
316 selectItem(item) {
dgozman 2017/04/05 20:52:55 setInitialSelection
eostroukhov 2017/05/03 00:33:46 Done.
317 if (this._list.length() > 0)
318 this._list.selectItem(item, true);
319 else
320 this._selectedItemIndex = item;
dgozman 2017/04/05 20:52:55 _initialSelectedItem
eostroukhov 2017/05/03 00:33:46 Done.
321 }
322
323 /**
270 * @param {!Event} event 324 * @param {!Event} event
271 */ 325 */
272 _onClick(event) { 326 _onClick(event) {
273 var item = this._list.itemForNode(/** @type {?Node} */ (event.target)); 327 var item = this._list.itemForNode(/** @type {?Node} */ (event.target));
274 if (item === null) 328 if (item === null)
275 return; 329 return;
276 330
277 event.consume(true); 331 event.consume(true);
278 // Detach dialog before allowing provider to override focus. 332 // Detach dialog before allowing provider to override focus.
279 if (this._dialog) 333 if (this._dialog)
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
418 * @param {!Array<number>} bestItems 472 * @param {!Array<number>} bestItems
419 * @param {!Array<number>} overflowItems 473 * @param {!Array<number>} overflowItems
420 * @param {!Array<number>} filteredItems 474 * @param {!Array<number>} filteredItems
421 */ 475 */
422 _refreshList(bestItems, overflowItems, filteredItems) { 476 _refreshList(bestItems, overflowItems, filteredItems) {
423 delete this._refreshListWithCurrentResult; 477 delete this._refreshListWithCurrentResult;
424 filteredItems = [].concat(bestItems, overflowItems, filteredItems); 478 filteredItems = [].concat(bestItems, overflowItems, filteredItems);
425 this._updateNotFoundMessage(!!filteredItems.length); 479 this._updateNotFoundMessage(!!filteredItems.length);
426 var oldHeight = this._list.element.offsetHeight; 480 var oldHeight = this._list.element.offsetHeight;
427 this._list.replaceAllItems(filteredItems); 481 this._list.replaceAllItems(filteredItems);
428 if (filteredItems.length) 482 if (filteredItems.length) {
429 this._list.selectItem(filteredItems[0]); 483 var selection = filteredItems[0];
484 if (this._selectedItemIndex !== null && filteredItems.includes(this._selec tedItemIndex))
485 selection = this._selectedItemIndex;
486 this._selectedItemIndex = null;
487 this._list.selectItem(selection, true);
488 }
430 if (this._list.element.offsetHeight !== oldHeight) 489 if (this._list.element.offsetHeight !== oldHeight)
431 this._list.viewportResized(); 490 this._list.viewportResized();
432 this._itemsFilteredForTest(); 491 this._itemsFilteredForTest();
433 } 492 }
434 493
435 /** 494 /**
436 * @param {boolean} hasItems 495 * @param {boolean} hasItems
437 */ 496 */
438 _updateNotFoundMessage(hasItems) { 497 _updateNotFoundMessage(hasItems) {
439 this._list.element.classList.toggle('hidden', !hasItems); 498 this._list.element.classList.toggle('hidden', !hasItems);
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
491 550
492 /** 551 /**
493 * @param {?number} itemIndex 552 * @param {?number} itemIndex
494 */ 553 */
495 _selectItem(itemIndex) { 554 _selectItem(itemIndex) {
496 this._promptHistory.push(this._value()); 555 this._promptHistory.push(this._value());
497 if (this._promptHistory.length > 100) 556 if (this._promptHistory.length > 100)
498 this._promptHistory.shift(); 557 this._promptHistory.shift();
499 this._provider.selectItem(itemIndex, this._cleanValue()); 558 this._provider.selectItem(itemIndex, this._cleanValue());
500 } 559 }
560
561 /**
562 * @param {?number} item
563 */
564 _hover(item) {
565 if (this._provider)
566 this._provider.hoverItem(item);
567 }
501 }; 568 };
502 569
503 570
504 /** 571 /**
505 * @unrestricted 572 * @unrestricted
506 */ 573 */
507 QuickOpen.FilteredListWidget.Provider = class { 574 QuickOpen.FilteredListWidget.Provider = class {
508 /** 575 /**
509 * @param {function():void} refreshCallback 576 * @param {function():void} refreshCallback
510 */ 577 */
(...skipping 22 matching lines...) Expand all
533 /** 600 /**
534 * @param {number} itemIndex 601 * @param {number} itemIndex
535 * @param {string} query 602 * @param {string} query
536 * @return {number} 603 * @return {number}
537 */ 604 */
538 itemScoreAt(itemIndex, query) { 605 itemScoreAt(itemIndex, query) {
539 return 1; 606 return 1;
540 } 607 }
541 608
542 /** 609 /**
610 * @param {?number} itemIndex
611 */
612 hoverItem(itemIndex) {
613 }
614
615 /**
543 * @param {number} itemIndex 616 * @param {number} itemIndex
544 * @param {string} query 617 * @param {string} query
545 * @param {!Element} titleElement 618 * @param {!Element} titleElement
546 * @param {!Element} subtitleElement 619 * @param {!Element} subtitleElement
547 */ 620 */
548 renderItem(itemIndex, query, titleElement, subtitleElement) { 621 renderItem(itemIndex, query, titleElement, subtitleElement) {
549 } 622 }
550 623
551 /** 624 /**
552 * @return {boolean} 625 * @return {boolean}
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
584 * @param {string} query 657 * @param {string} query
585 * @return {string} 658 * @return {string}
586 */ 659 */
587 notFoundText(query) { 660 notFoundText(query) {
588 return Common.UIString('No results found'); 661 return Common.UIString('No results found');
589 } 662 }
590 663
591 detach() { 664 detach() {
592 } 665 }
593 }; 666 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698