OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
50 */ | 50 */ |
51 UI.SuggestBox = class { | 51 UI.SuggestBox = class { |
52 /** | 52 /** |
53 * @param {!UI.SuggestBoxDelegate} suggestBoxDelegate | 53 * @param {!UI.SuggestBoxDelegate} suggestBoxDelegate |
54 * @param {number=} maxItemsHeight | 54 * @param {number=} maxItemsHeight |
55 * @param {boolean=} captureEnter | 55 * @param {boolean=} captureEnter |
56 */ | 56 */ |
57 constructor(suggestBoxDelegate, maxItemsHeight, captureEnter) { | 57 constructor(suggestBoxDelegate, maxItemsHeight, captureEnter) { |
58 this._suggestBoxDelegate = suggestBoxDelegate; | 58 this._suggestBoxDelegate = suggestBoxDelegate; |
59 this._maxItemsHeight = maxItemsHeight; | 59 this._maxItemsHeight = maxItemsHeight; |
60 this._captureEnter = captureEnter; | |
60 this._maybeHideBound = this._maybeHide.bind(this); | 61 this._maybeHideBound = this._maybeHide.bind(this); |
61 this._hideBound = this.hide.bind(this); | 62 this._hideBound = this.hide.bind(this); |
62 this._container = createElementWithClass('div', 'suggest-box-container'); | |
63 this._rowHeight = 17; | 63 this._rowHeight = 17; |
64 this._userInteracted = false; | |
65 this._userEnteredText = ''; | |
66 this._hideTimeoutId = 0; | |
67 /** @type {?string} */ | |
68 this._onlyCompletion = null; | |
69 | |
70 this._overlay = new UI.ModalOverlay(); | |
71 this._overlay.setBlockPointerEvents(false); | |
72 this._overlay.setAnchorBehavior(UI.ModalOverlay.AnchorBehavior.PreferTop); | |
73 var container = this._overlay.element.createChild('div'); | |
74 var shadowRoot = UI.createShadowRootWithCoreStyles(container, 'ui/suggestBox .css'); | |
75 | |
64 /** @type {!UI.ListControl<!UI.SuggestBox.Suggestion>} */ | 76 /** @type {!UI.ListControl<!UI.SuggestBox.Suggestion>} */ |
65 this._list = new UI.ListControl(this, UI.ListMode.EqualHeightItems); | 77 this._list = new UI.ListControl(this, UI.ListMode.EqualHeightItems); |
66 this._element = this._list.element; | 78 this._element = this._list.element; |
67 this._element.classList.add('suggest-box'); | 79 this._element.classList.add('suggest-box'); |
68 this._container.appendChild(this._element); | 80 shadowRoot.appendChild(this._element); |
69 this._element.addEventListener('mousedown', this._onBoxMouseDown.bind(this), true); | 81 this._element.addEventListener('mousedown', this._onBoxMouseDown.bind(this), true); |
70 this._userInteracted = false; | |
71 this._captureEnter = captureEnter; | |
72 this._hasVerticalScroll = false; | |
73 this._userEnteredText = ''; | |
74 | |
75 /** @type {?UI.SuggestBox.Overlay} */ | |
76 this._overlay = null; | |
77 /** @type {?AnchorBox} */ | |
78 this._lastAnchorBox = null; | |
79 this._lastItemCount = 0; | |
80 this._hideTimeoutId = 0; | |
81 /** @type {?Element} */ | |
82 this._bodyElement = null; | |
83 /** @type {?string} */ | |
84 this._onlyCompletion = null; | |
85 } | 82 } |
86 | 83 |
87 /** | 84 /** |
88 * @return {boolean} | 85 * @return {boolean} |
89 */ | 86 */ |
90 visible() { | 87 visible() { |
91 return !!this._container.parentElement; | 88 return this._overlay.visible(); |
92 } | 89 } |
93 | 90 |
94 /** | 91 /** |
95 * @param {!AnchorBox} anchorBox | 92 * @param {!AnchorBox} anchorBox |
96 */ | 93 */ |
97 setPosition(anchorBox) { | 94 setPosition(anchorBox) { |
98 this._updateBoxPosition(anchorBox, this._list.length()); | 95 this._overlay.setAnchorBox(anchorBox); |
99 } | 96 this._overlay.position(); |
100 | |
101 /** | |
102 * @param {!AnchorBox} anchorBox | |
103 * @param {number} length | |
104 */ | |
105 _updateBoxPosition(anchorBox, length) { | |
106 console.assert(this._overlay); | |
107 if (this._lastAnchorBox && this._lastAnchorBox.equals(anchorBox) && this._la stItemCount === length) | |
108 return; | |
109 this._lastItemCount = length; | |
110 this._lastAnchorBox = anchorBox; | |
111 | |
112 // Position relative to main DevTools element. | |
113 var container = UI.Dialog.modalHostView().element; | |
114 anchorBox = anchorBox.relativeToElement(container); | |
115 var totalHeight = container.offsetHeight; | |
116 var aboveHeight = anchorBox.y; | |
117 var underHeight = totalHeight - anchorBox.y - anchorBox.height; | |
118 | |
119 this._overlay.setLeftOffset(anchorBox.x); | |
120 | |
121 var under = underHeight >= aboveHeight; | |
122 if (under) | |
123 this._overlay.setVerticalOffset(anchorBox.y + anchorBox.height, true); | |
124 else | |
125 this._overlay.setVerticalOffset(totalHeight - anchorBox.y, false); | |
126 | |
127 var spacer = 6; | |
128 var maxHeight = Math.min( | |
129 Math.max(underHeight, aboveHeight) - spacer, | |
130 this._maxItemsHeight ? this._maxItemsHeight * this._rowHeight : Infinity ); | |
131 var height = this._rowHeight * length; | |
132 this._hasVerticalScroll = height > maxHeight; | |
133 this._element.style.height = Math.min(maxHeight, height) + 'px'; | |
134 } | 97 } |
135 | 98 |
136 /** | 99 /** |
137 * @param {!UI.SuggestBox.Suggestions} items | 100 * @param {!UI.SuggestBox.Suggestions} items |
138 */ | 101 */ |
139 _updateWidth(items) { | 102 _updateMaxSize(items) { |
140 if (this._hasVerticalScroll) { | 103 var maxWidth = this._maxWidth(items); |
141 this._element.style.width = '100vw'; | 104 var length = this._maxItemsHeight ? Math.min(this._maxItemsHeight, items.len gth) : items.length; |
142 return; | 105 var maxHeight = length * this._rowHeight; |
143 } | 106 this._overlay.setMaxSize(new UI.Size(maxWidth, maxHeight)); |
107 } | |
108 | |
109 /** | |
110 * @param {!UI.SuggestBox.Suggestions} items | |
111 * @return {number} | |
112 */ | |
113 _maxWidth(items) { | |
144 if (!items.length) | 114 if (!items.length) |
145 return; | 115 return 300; |
caseq
2017/01/31 21:27:20
please extract to a named constant.
dgozman
2017/01/31 23:39:49
Done.
| |
146 // If there are no scrollbars, set the width to the width of the largest row . | |
147 var maxItem; | 116 var maxItem; |
148 var maxLength = -Infinity; | 117 var maxLength = -Infinity; |
149 for (var i = 0; i < items.length; i++) { | 118 for (var i = 0; i < items.length; i++) { |
150 var length = items[i].title.length + (items[i].subtitle || '').length; | 119 var length = items[i].title.length + (items[i].subtitle || '').length; |
151 if (length > maxLength) { | 120 if (length > maxLength) { |
152 maxLength = length; | 121 maxLength = length; |
153 maxItem = items[i]; | 122 maxItem = items[i]; |
154 } | 123 } |
155 } | 124 } |
156 this._element.style.width = | 125 var element = this.createElementForItem(/** @type {!UI.SuggestBox.Suggestion } */ (maxItem)); |
157 UI.measurePreferredSize( | 126 return Math.min(300, UI.measurePreferredSize(element, this._element).width); |
158 this.createElementForItem(/** @type {!UI.SuggestBox.Suggestion} */ (maxItem)), this._element) | |
159 .width + | |
160 'px'; | |
161 } | 127 } |
162 | 128 |
163 /** | 129 /** |
164 * @param {!Event} event | 130 * @param {!Event} event |
165 */ | 131 */ |
166 _onBoxMouseDown(event) { | 132 _onBoxMouseDown(event) { |
167 if (this._hideTimeoutId) { | 133 if (this._hideTimeoutId) { |
168 window.clearTimeout(this._hideTimeoutId); | 134 window.clearTimeout(this._hideTimeoutId); |
169 this._hideTimeoutId = 0; | 135 this._hideTimeoutId = 0; |
170 } | 136 } |
171 event.preventDefault(); | 137 event.preventDefault(); |
172 } | 138 } |
173 | 139 |
174 _maybeHide() { | 140 _maybeHide() { |
175 if (!this._hideTimeoutId) | 141 if (!this._hideTimeoutId) |
176 this._hideTimeoutId = window.setTimeout(this._hideBound, 0); | 142 this._hideTimeoutId = window.setTimeout(this._hideBound, 0); |
177 } | 143 } |
178 | 144 |
179 /** | |
180 * // FIXME: make SuggestBox work for multiple documents. | |
181 * @suppressGlobalPropertiesCheck | |
182 */ | |
183 _show() { | 145 _show() { |
184 if (this.visible()) | 146 if (this.visible()) |
185 return; | 147 return; |
186 this._bodyElement = document.body; | 148 this._overlay.show(); |
187 this._bodyElement.addEventListener('mousedown', this._maybeHideBound, true); | 149 this._overlay.document().body.addEventListener('mousedown', this._maybeHideB ound, true); |
188 this._element.ownerDocument.defaultView.addEventListener('resize', this._hid eBound, false); | 150 this._overlay.document().defaultView.addEventListener('resize', this._hideBo und, false); |
189 this._overlay = new UI.SuggestBox.Overlay(); | |
190 this._overlay.setContentElement(this._container); | |
191 this._rowHeight = | 151 this._rowHeight = |
192 UI.measurePreferredSize(this.createElementForItem({title: '1', subtitle: '12'}), this._element).height; | 152 UI.measurePreferredSize(this.createElementForItem({title: '1', subtitle: '12'}), this._element).height; |
193 } | 153 } |
194 | 154 |
195 hide() { | 155 hide() { |
196 if (!this.visible()) | 156 if (!this.visible()) |
197 return; | 157 return; |
198 | |
199 this._userInteracted = false; | 158 this._userInteracted = false; |
200 this._bodyElement.removeEventListener('mousedown', this._maybeHideBound, tru e); | 159 this._overlay.document().body.removeEventListener('mousedown', this._maybeHi deBound, true); |
201 this._element.ownerDocument.defaultView.removeEventListener('resize', this._ hideBound, false); | 160 this._overlay.document().defaultView.removeEventListener('resize', this._hid eBound, false); |
202 this._bodyElement = null; | 161 this._overlay.hide(); |
203 this._container.remove(); | |
204 this._overlay.dispose(); | |
205 this._overlay = null; | |
206 this._lastAnchorBox = null; | |
207 } | 162 } |
208 | 163 |
209 /** | 164 /** |
210 * @param {boolean=} isIntermediateSuggestion | 165 * @param {boolean=} isIntermediateSuggestion |
211 * @return {boolean} | 166 * @return {boolean} |
212 */ | 167 */ |
213 _applySuggestion(isIntermediateSuggestion) { | 168 _applySuggestion(isIntermediateSuggestion) { |
214 if (this._onlyCompletion) { | 169 if (this._onlyCompletion) { |
215 this._suggestBoxDelegate.applySuggestion(this._onlyCompletion, isIntermedi ateSuggestion); | 170 this._suggestBoxDelegate.applySuggestion(this._onlyCompletion, isIntermedi ateSuggestion); |
216 return true; | 171 return true; |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
342 * @param {boolean} selectHighestPriority | 297 * @param {boolean} selectHighestPriority |
343 * @param {boolean} canShowForSingleItem | 298 * @param {boolean} canShowForSingleItem |
344 * @param {string} userEnteredText | 299 * @param {string} userEnteredText |
345 */ | 300 */ |
346 updateSuggestions(anchorBox, completions, selectHighestPriority, canShowForSin gleItem, userEnteredText) { | 301 updateSuggestions(anchorBox, completions, selectHighestPriority, canShowForSin gleItem, userEnteredText) { |
347 this._onlyCompletion = null; | 302 this._onlyCompletion = null; |
348 if (this._canShowBox(completions, canShowForSingleItem, userEnteredText)) { | 303 if (this._canShowBox(completions, canShowForSingleItem, userEnteredText)) { |
349 this._userEnteredText = userEnteredText; | 304 this._userEnteredText = userEnteredText; |
350 | 305 |
351 this._show(); | 306 this._show(); |
352 this._updateBoxPosition(anchorBox, completions.length); | 307 this._updateMaxSize(completions); |
353 this._updateWidth(completions); | 308 this._overlay.setAnchorBox(anchorBox); |
309 this._overlay.position(); | |
354 this._list.invalidateItemHeight(); | 310 this._list.invalidateItemHeight(); |
355 this._list.replaceAllItems(completions); | 311 this._list.replaceAllItems(completions); |
356 | 312 |
357 if (selectHighestPriority) { | 313 if (selectHighestPriority) { |
358 var highestPriorityItem = completions[0]; | 314 var highestPriorityItem = completions[0]; |
359 var highestPriority = completions[0].priority || 0; | 315 var highestPriority = completions[0].priority || 0; |
360 for (var i = 0; i < completions.length; i++) { | 316 for (var i = 0; i < completions.length; i++) { |
361 var priority = completions[i].priority || 0; | 317 var priority = completions[i].priority || 0; |
362 if (highestPriority < priority) { | 318 if (highestPriority < priority) { |
363 highestPriority = priority; | 319 highestPriority = priority; |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
424 | 380 |
425 /** | 381 /** |
426 * @typedef {!{title: string, subtitle: (string|undefined), iconType: (string|un defined), priority: (number|undefined), isSecondary: (boolean|undefined)}} | 382 * @typedef {!{title: string, subtitle: (string|undefined), iconType: (string|un defined), priority: (number|undefined), isSecondary: (boolean|undefined)}} |
427 */ | 383 */ |
428 UI.SuggestBox.Suggestion; | 384 UI.SuggestBox.Suggestion; |
429 | 385 |
430 /** | 386 /** |
431 * @typedef {!Array<!UI.SuggestBox.Suggestion>} | 387 * @typedef {!Array<!UI.SuggestBox.Suggestion>} |
432 */ | 388 */ |
433 UI.SuggestBox.Suggestions; | 389 UI.SuggestBox.Suggestions; |
434 | |
435 UI.SuggestBox.Overlay = class { | |
436 /** | |
437 * // FIXME: make SuggestBox work for multiple documents. | |
438 * @suppressGlobalPropertiesCheck | |
439 */ | |
440 constructor() { | |
441 this.element = createElementWithClass('div', 'suggest-box-overlay'); | |
442 var root = UI.createShadowRootWithCoreStyles(this.element, 'ui/suggestBox.cs s'); | |
443 this._leftSpacerElement = root.createChild('div', 'suggest-box-left-spacer') ; | |
444 this._horizontalElement = root.createChild('div', 'suggest-box-horizontal'); | |
445 this._topSpacerElement = this._horizontalElement.createChild('div', 'suggest -box-top-spacer'); | |
446 this._bottomSpacerElement = this._horizontalElement.createChild('div', 'sugg est-box-bottom-spacer'); | |
447 this._resize(); | |
448 document.body.appendChild(this.element); | |
449 } | |
450 | |
451 /** | |
452 * @param {number} offset | |
453 */ | |
454 setLeftOffset(offset) { | |
455 this._leftSpacerElement.style.flexBasis = offset + 'px'; | |
456 } | |
457 | |
458 /** | |
459 * @param {number} offset | |
460 * @param {boolean} isTopOffset | |
461 */ | |
462 setVerticalOffset(offset, isTopOffset) { | |
463 this.element.classList.toggle('under-anchor', isTopOffset); | |
464 | |
465 if (isTopOffset) { | |
466 this._bottomSpacerElement.style.flexBasis = 'auto'; | |
467 this._topSpacerElement.style.flexBasis = offset + 'px'; | |
468 } else { | |
469 this._bottomSpacerElement.style.flexBasis = offset + 'px'; | |
470 this._topSpacerElement.style.flexBasis = 'auto'; | |
471 } | |
472 } | |
473 | |
474 /** | |
475 * @param {!Element} element | |
476 */ | |
477 setContentElement(element) { | |
478 this._horizontalElement.insertBefore(element, this._bottomSpacerElement); | |
479 } | |
480 | |
481 _resize() { | |
482 var container = UI.Dialog.modalHostView().element; | |
483 var containerBox = container.boxInWindow(container.ownerDocument.defaultView ); | |
484 | |
485 this.element.style.left = containerBox.x + 'px'; | |
486 this.element.style.top = containerBox.y + 'px'; | |
487 this.element.style.height = containerBox.height + 'px'; | |
488 this.element.style.width = containerBox.width + 'px'; | |
489 } | |
490 | |
491 dispose() { | |
492 this.element.remove(); | |
493 } | |
494 }; | |
OLD | NEW |