| 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 28 matching lines...) Expand all Loading... |
| 39 */ | 39 */ |
| 40 applySuggestion(suggestion, isIntermediateSuggestion) {}, | 40 applySuggestion(suggestion, isIntermediateSuggestion) {}, |
| 41 | 41 |
| 42 /** | 42 /** |
| 43 * acceptSuggestion will be always called after call to applySuggestion with i
sIntermediateSuggestion being equal to false. | 43 * acceptSuggestion will be always called after call to applySuggestion with i
sIntermediateSuggestion being equal to false. |
| 44 */ | 44 */ |
| 45 acceptSuggestion() {}, | 45 acceptSuggestion() {}, |
| 46 }; | 46 }; |
| 47 | 47 |
| 48 /** | 48 /** |
| 49 * @implements {UI.ViewportControl.Provider} | |
| 50 * @unrestricted | 49 * @unrestricted |
| 51 */ | 50 */ |
| 52 UI.SuggestBox = class { | 51 UI.SuggestBox = class { |
| 53 /** | 52 /** |
| 54 * @param {!UI.SuggestBoxDelegate} suggestBoxDelegate | 53 * @param {!UI.SuggestBoxDelegate} suggestBoxDelegate |
| 55 * @param {number=} maxItemsHeight | 54 * @param {number=} maxItemsHeight |
| 56 * @param {boolean=} captureEnter | 55 * @param {boolean=} captureEnter |
| 57 */ | 56 */ |
| 58 constructor(suggestBoxDelegate, maxItemsHeight, captureEnter) { | 57 constructor(suggestBoxDelegate, maxItemsHeight, captureEnter) { |
| 59 this._suggestBoxDelegate = suggestBoxDelegate; | 58 this._suggestBoxDelegate = suggestBoxDelegate; |
| 60 this._length = 0; | |
| 61 this._selectedIndex = -1; | 59 this._selectedIndex = -1; |
| 62 this._selectedElement = null; | 60 this._selectedElement = null; |
| 63 this._maxItemsHeight = maxItemsHeight; | 61 this._maxItemsHeight = maxItemsHeight; |
| 64 this._maybeHideBound = this._maybeHide.bind(this); | 62 this._maybeHideBound = this._maybeHide.bind(this); |
| 65 this._container = createElementWithClass('div', 'suggest-box-container'); | 63 this._container = createElementWithClass('div', 'suggest-box-container'); |
| 66 this._viewport = new UI.ViewportControl(this); | 64 /** @type {!UI.ViewportControl<!UI.SuggestBox.Suggestion>} */ |
| 65 this._viewport = new UI.ViewportControl(this._createItemElement.bind(this)); |
| 67 this._element = this._viewport.element; | 66 this._element = this._viewport.element; |
| 68 this._element.classList.add('suggest-box'); | 67 this._element.classList.add('suggest-box'); |
| 69 this._container.appendChild(this._element); | 68 this._container.appendChild(this._element); |
| 70 this._element.addEventListener('mousedown', this._onBoxMouseDown.bind(this),
true); | 69 this._element.addEventListener('mousedown', this._onBoxMouseDown.bind(this),
true); |
| 71 this._detailsPopup = this._container.createChild('div', 'suggest-box details
-popup monospace'); | 70 this._detailsPopup = this._container.createChild('div', 'suggest-box details
-popup monospace'); |
| 72 this._detailsPopup.classList.add('hidden'); | 71 this._detailsPopup.classList.add('hidden'); |
| 73 this._asyncDetailsCallback = null; | 72 this._asyncDetailsCallback = null; |
| 74 /** @type {!Map<number, !Promise<{detail: string, description: string}>>} */ | 73 /** @type {!Map<number, !Promise<{detail: string, description: string}>>} */ |
| 75 this._asyncDetailsPromises = new Map(); | 74 this._asyncDetailsPromises = new Map(); |
| 76 this._userInteracted = false; | 75 this._userInteracted = false; |
| 77 this._captureEnter = captureEnter; | 76 this._captureEnter = captureEnter; |
| 78 /** @type {!Array<!Element>} */ | |
| 79 this._elementList = []; | |
| 80 this._rowHeight = 17; | |
| 81 this._viewportWidth = '100vw'; | 77 this._viewportWidth = '100vw'; |
| 82 this._hasVerticalScroll = false; | 78 this._hasVerticalScroll = false; |
| 83 this._userEnteredText = ''; | 79 this._userEnteredText = ''; |
| 84 /** @type {!UI.SuggestBox.Suggestions} */ | |
| 85 this._items = []; | |
| 86 } | 80 } |
| 87 | 81 |
| 88 /** | 82 /** |
| 89 * @return {boolean} | 83 * @return {boolean} |
| 90 */ | 84 */ |
| 91 visible() { | 85 visible() { |
| 92 return !!this._container.parentElement; | 86 return !!this._container.parentElement; |
| 93 } | 87 } |
| 94 | 88 |
| 95 /** | 89 /** |
| 96 * @param {!AnchorBox} anchorBox | 90 * @param {!AnchorBox} anchorBox |
| 97 */ | 91 */ |
| 98 setPosition(anchorBox) { | 92 setPosition(anchorBox) { |
| 99 this._updateBoxPosition(anchorBox); | 93 this._updateBoxPosition(anchorBox, this._viewport.length()); |
| 100 } | 94 } |
| 101 | 95 |
| 102 /** | 96 /** |
| 103 * @param {!AnchorBox} anchorBox | 97 * @param {!AnchorBox} anchorBox |
| 98 * @param {number} length |
| 104 */ | 99 */ |
| 105 _updateBoxPosition(anchorBox) { | 100 _updateBoxPosition(anchorBox, length) { |
| 106 console.assert(this._overlay); | 101 console.assert(this._overlay); |
| 107 if (this._lastAnchorBox && this._lastAnchorBox.equals(anchorBox) && this._la
stItemCount === this.itemCount()) | 102 if (this._lastAnchorBox && this._lastAnchorBox.equals(anchorBox) && this._la
stItemCount === length) |
| 108 return; | 103 return; |
| 109 this._lastItemCount = this.itemCount(); | 104 this._lastItemCount = length; |
| 110 this._lastAnchorBox = anchorBox; | 105 this._lastAnchorBox = anchorBox; |
| 111 | 106 |
| 112 // Position relative to main DevTools element. | 107 // Position relative to main DevTools element. |
| 113 var container = UI.Dialog.modalHostView().element; | 108 var container = UI.Dialog.modalHostView().element; |
| 114 anchorBox = anchorBox.relativeToElement(container); | 109 anchorBox = anchorBox.relativeToElement(container); |
| 115 var totalHeight = container.offsetHeight; | 110 var totalHeight = container.offsetHeight; |
| 116 var aboveHeight = anchorBox.y; | 111 var aboveHeight = anchorBox.y; |
| 117 var underHeight = totalHeight - anchorBox.y - anchorBox.height; | 112 var underHeight = totalHeight - anchorBox.y - anchorBox.height; |
| 118 | 113 |
| 119 this._overlay.setLeftOffset(anchorBox.x); | 114 this._overlay.setLeftOffset(anchorBox.x); |
| 120 | 115 |
| 121 var under = underHeight >= aboveHeight; | 116 var under = underHeight >= aboveHeight; |
| 122 if (under) | 117 if (under) |
| 123 this._overlay.setVerticalOffset(anchorBox.y + anchorBox.height, true); | 118 this._overlay.setVerticalOffset(anchorBox.y + anchorBox.height, true); |
| 124 else | 119 else |
| 125 this._overlay.setVerticalOffset(totalHeight - anchorBox.y, false); | 120 this._overlay.setVerticalOffset(totalHeight - anchorBox.y, false); |
| 126 | 121 |
| 127 var spacer = 6; | 122 var spacer = 6; |
| 128 var maxHeight = Math.min( | 123 var maxHeight = Math.min( |
| 129 Math.max(underHeight, aboveHeight) - spacer, | 124 Math.max(underHeight, aboveHeight) - spacer, |
| 130 this._maxItemsHeight ? this._maxItemsHeight * this._rowHeight : Infinity
); | 125 this._maxItemsHeight ? this._maxItemsHeight * this._rowHeight : Infinity
); |
| 131 var height = this._rowHeight * this._items.length; | 126 var height = this._rowHeight * length; |
| 132 this._hasVerticalScroll = height > maxHeight; | 127 this._hasVerticalScroll = height > maxHeight; |
| 133 this._element.style.height = Math.min(maxHeight, height) + 'px'; | 128 this._element.style.height = Math.min(maxHeight, height) + 'px'; |
| 134 } | 129 } |
| 135 | 130 |
| 136 _updateWidth() { | 131 _updateWidth() { |
| 137 if (this._hasVerticalScroll) { | 132 if (this._hasVerticalScroll) { |
| 138 this._element.style.width = '100vw'; | 133 this._element.style.width = '100vw'; |
| 139 return; | 134 return; |
| 140 } | 135 } |
| 136 if (!this._viewport.length()) |
| 137 return; |
| 141 // If there are no scrollbars, set the width to the width of the largest row
. | 138 // If there are no scrollbars, set the width to the width of the largest row
. |
| 139 var maxItem = this._viewport.itemAtIndex(0); |
| 142 var maxIndex = 0; | 140 var maxIndex = 0; |
| 143 for (var i = 0; i < this._items.length; i++) { | 141 for (var i = 1; i < this._viewport.length(); i++) { |
| 144 if (this._items[i].title.length > this._items[maxIndex].title.length) | 142 var item = this._viewport.itemAtIndex(i); |
| 143 if (item.title.length > maxItem.title.length) { |
| 144 maxItem = item; |
| 145 maxIndex = i; | 145 maxIndex = i; |
| 146 } |
| 146 } | 147 } |
| 147 var element = /** @type {!Element} */ (this.itemElement(maxIndex)); | 148 this._element.style.width = |
| 148 this._element.style.width = UI.measurePreferredSize(element, this._element).
width + 'px'; | 149 UI.measurePreferredSize(this._viewport.elementAtIndex(maxIndex), this._e
lement).width + 'px'; |
| 149 } | 150 } |
| 150 | 151 |
| 151 /** | 152 /** |
| 152 * @param {!Event} event | 153 * @param {!Event} event |
| 153 */ | 154 */ |
| 154 _onBoxMouseDown(event) { | 155 _onBoxMouseDown(event) { |
| 155 if (this._hideTimeoutId) { | 156 if (this._hideTimeoutId) { |
| 156 window.clearTimeout(this._hideTimeoutId); | 157 window.clearTimeout(this._hideTimeoutId); |
| 157 delete this._hideTimeoutId; | 158 delete this._hideTimeoutId; |
| 158 } | 159 } |
| 159 event.preventDefault(); | 160 event.preventDefault(); |
| 160 } | 161 } |
| 161 | 162 |
| 162 _maybeHide() { | 163 _maybeHide() { |
| 163 if (!this._hideTimeoutId) | 164 if (!this._hideTimeoutId) |
| 164 this._hideTimeoutId = window.setTimeout(this.hide.bind(this), 0); | 165 this._hideTimeoutId = window.setTimeout(this.hide.bind(this), 0); |
| 165 } | 166 } |
| 166 | 167 |
| 167 /** | 168 /** |
| 168 * // FIXME: make SuggestBox work for multiple documents. | 169 * // FIXME: make SuggestBox work for multiple documents. |
| 169 * @suppressGlobalPropertiesCheck | 170 * @suppressGlobalPropertiesCheck |
| 170 */ | 171 */ |
| 171 _show() { | 172 _show() { |
| 172 if (this.visible()) | 173 if (this.visible()) |
| 173 return; | 174 return; |
| 174 this._bodyElement = document.body; | 175 this._bodyElement = document.body; |
| 175 this._bodyElement.addEventListener('mousedown', this._maybeHideBound, true); | 176 this._bodyElement.addEventListener('mousedown', this._maybeHideBound, true); |
| 176 this._overlay = new UI.SuggestBox.Overlay(); | 177 this._overlay = new UI.SuggestBox.Overlay(); |
| 177 this._overlay.setContentElement(this._container); | 178 this._overlay.setContentElement(this._container); |
| 178 var measuringElement = this._createItemElement('1', '12'); | 179 this._rowHeight = |
| 179 this._viewport.element.appendChild(measuringElement); | 180 UI.measurePreferredSize(this._createItemElement({title: '1', subtitle: '
12'}), this._element).height; |
| 180 this._rowHeight = measuringElement.getBoundingClientRect().height; | |
| 181 measuringElement.remove(); | |
| 182 } | 181 } |
| 183 | 182 |
| 184 hide() { | 183 hide() { |
| 185 if (!this.visible()) | 184 if (!this.visible()) |
| 186 return; | 185 return; |
| 187 | 186 |
| 188 this._userInteracted = false; | 187 this._userInteracted = false; |
| 189 this._bodyElement.removeEventListener('mousedown', this._maybeHideBound, tru
e); | 188 this._bodyElement.removeEventListener('mousedown', this._maybeHideBound, tru
e); |
| 190 delete this._bodyElement; | 189 delete this._bodyElement; |
| 191 this._container.remove(); | 190 this._container.remove(); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 234 | 233 |
| 235 return true; | 234 return true; |
| 236 } | 235 } |
| 237 | 236 |
| 238 /** | 237 /** |
| 239 * @param {number} shift | 238 * @param {number} shift |
| 240 * @param {boolean=} isCircular | 239 * @param {boolean=} isCircular |
| 241 * @return {boolean} is changed | 240 * @return {boolean} is changed |
| 242 */ | 241 */ |
| 243 _selectClosest(shift, isCircular) { | 242 _selectClosest(shift, isCircular) { |
| 244 if (!this._length) | 243 var length = this._viewport.length(); |
| 244 if (!length) |
| 245 return false; | 245 return false; |
| 246 | 246 |
| 247 this._userInteracted = true; | 247 this._userInteracted = true; |
| 248 | 248 |
| 249 if (this._selectedIndex === -1 && shift < 0) | 249 if (this._selectedIndex === -1 && shift < 0) |
| 250 shift += 1; | 250 shift += 1; |
| 251 | 251 |
| 252 var index = this._selectedIndex + shift; | 252 var index = this._selectedIndex + shift; |
| 253 | 253 |
| 254 if (isCircular) | 254 if (isCircular) |
| 255 index = (this._length + index) % this._length; | 255 index = (length + index) % length; |
| 256 else | 256 else |
| 257 index = Number.constrain(index, 0, this._length - 1); | 257 index = Number.constrain(index, 0, length - 1); |
| 258 | 258 |
| 259 this._selectItem(index); | 259 this._selectItem(index); |
| 260 return true; | 260 return true; |
| 261 } | 261 } |
| 262 | 262 |
| 263 /** | 263 /** |
| 264 * @param {!Event} event | 264 * @param {!Event} event |
| 265 */ | 265 */ |
| 266 _onItemMouseDown(event) { | 266 _onItemMouseDown(event) { |
| 267 this._selectedElement = event.currentTarget; | 267 this._selectedElement = event.currentTarget; |
| 268 this.acceptSuggestion(); | 268 this.acceptSuggestion(); |
| 269 event.consume(true); | 269 event.consume(true); |
| 270 } | 270 } |
| 271 | 271 |
| 272 /** | 272 /** |
| 273 * @param {string} query | 273 * @param {!UI.SuggestBox.Suggestion} item |
| 274 * @param {string} title | |
| 275 * @param {string=} subtitle | |
| 276 * @param {string=} iconType | |
| 277 * @param {boolean=} isSecondary | |
| 278 * @return {!Element} | 274 * @return {!Element} |
| 279 */ | 275 */ |
| 280 _createItemElement(query, title, subtitle, iconType, isSecondary) { | 276 _createItemElement(item) { |
| 277 var query = this._userEnteredText; |
| 281 var element = createElementWithClass('div', 'suggest-box-content-item source
-code'); | 278 var element = createElementWithClass('div', 'suggest-box-content-item source
-code'); |
| 282 if (iconType) { | 279 if (item.iconType) { |
| 283 var icon = UI.Icon.create(iconType, 'suggestion-icon'); | 280 var icon = UI.Icon.create(item.iconType, 'suggestion-icon'); |
| 284 element.appendChild(icon); | 281 element.appendChild(icon); |
| 285 } | 282 } |
| 286 if (isSecondary) | 283 if (item.isSecondary) |
| 287 element.classList.add('secondary'); | 284 element.classList.add('secondary'); |
| 288 element.tabIndex = -1; | 285 element.tabIndex = -1; |
| 289 var displayText = title.trimEnd(50 + query.length); | 286 var displayText = item.title.trimEnd(50 + query.length); |
| 290 | 287 |
| 291 var titleElement = element.createChild('span', 'suggestion-title'); | 288 var titleElement = element.createChild('span', 'suggestion-title'); |
| 292 var index = displayText.toLowerCase().indexOf(query.toLowerCase()); | 289 var index = displayText.toLowerCase().indexOf(query.toLowerCase()); |
| 293 if (index > 0) | 290 if (index > 0) |
| 294 titleElement.createChild('span').textContent = displayText.substring(0, in
dex); | 291 titleElement.createChild('span').textContent = displayText.substring(0, in
dex); |
| 295 if (index > -1) | 292 if (index > -1) |
| 296 titleElement.createChild('span', 'query').textContent = displayText.substr
ing(index, index + query.length); | 293 titleElement.createChild('span', 'query').textContent = displayText.substr
ing(index, index + query.length); |
| 297 titleElement.createChild('span').textContent = displayText.substring(index >
-1 ? index + query.length : 0); | 294 titleElement.createChild('span').textContent = displayText.substring(index >
-1 ? index + query.length : 0); |
| 298 titleElement.createChild('span', 'spacer'); | 295 titleElement.createChild('span', 'spacer'); |
| 299 if (subtitle) { | 296 if (item.subtitle) { |
| 300 var subtitleElement = element.createChild('span', 'suggestion-subtitle'); | 297 var subtitleElement = element.createChild('span', 'suggestion-subtitle'); |
| 301 subtitleElement.textContent = subtitle.trimEnd(15); | 298 subtitleElement.textContent = item.subtitle.trimEnd(15); |
| 302 } | 299 } |
| 303 element.__fullValue = title; | 300 element.__fullValue = item.title; |
| 304 element.addEventListener('mousedown', this._onItemMouseDown.bind(this), fals
e); | 301 element.addEventListener('mousedown', this._onItemMouseDown.bind(this), fals
e); |
| 305 return element; | 302 return element; |
| 306 } | 303 } |
| 307 | 304 |
| 308 /** | 305 /** |
| 309 * @param {!UI.SuggestBox.Suggestions} items | |
| 310 * @param {string} userEnteredText | |
| 311 * @param {function(number): !Promise<{detail:string, description:string}>=} a
syncDetails | |
| 312 */ | |
| 313 _updateItems(items, userEnteredText, asyncDetails) { | |
| 314 this._length = items.length; | |
| 315 this._asyncDetailsPromises.clear(); | |
| 316 this._asyncDetailsCallback = asyncDetails; | |
| 317 this._elementList = []; | |
| 318 delete this._selectedElement; | |
| 319 | |
| 320 this._userEnteredText = userEnteredText; | |
| 321 this._items = items; | |
| 322 } | |
| 323 | |
| 324 /** | |
| 325 * @param {number} index | 306 * @param {number} index |
| 326 * @return {!Promise<?{detail: string, description: string}>} | 307 * @return {!Promise<?{detail: string, description: string}>} |
| 327 */ | 308 */ |
| 328 _asyncDetails(index) { | 309 _asyncDetails(index) { |
| 329 if (!this._asyncDetailsCallback) | 310 if (!this._asyncDetailsCallback) |
| 330 return Promise.resolve(/** @type {?{description: string, detail: string}}
*/ (null)); | 311 return Promise.resolve(/** @type {?{description: string, detail: string}}
*/ (null)); |
| 331 if (!this._asyncDetailsPromises.has(index)) | 312 if (!this._asyncDetailsPromises.has(index)) |
| 332 this._asyncDetailsPromises.set(index, this._asyncDetailsCallback(index)); | 313 this._asyncDetailsPromises.set(index, this._asyncDetailsCallback(index)); |
| 333 return /** @type {!Promise<?{detail: string, description: string}>} */ (this
._asyncDetailsPromises.get(index)); | 314 return /** @type {!Promise<?{detail: string, description: string}>} */ (this
._asyncDetailsPromises.get(index)); |
| 334 } | 315 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 351 _selectItem(index) { | 332 _selectItem(index) { |
| 352 if (this._selectedElement) { | 333 if (this._selectedElement) { |
| 353 this._selectedElement.classList.remove('selected'); | 334 this._selectedElement.classList.remove('selected'); |
| 354 this._selectedElement.classList.remove('force-white-icons'); | 335 this._selectedElement.classList.remove('force-white-icons'); |
| 355 } | 336 } |
| 356 | 337 |
| 357 this._selectedIndex = index; | 338 this._selectedIndex = index; |
| 358 if (index < 0) | 339 if (index < 0) |
| 359 return; | 340 return; |
| 360 | 341 |
| 361 this._selectedElement = this.itemElement(index); | 342 this._selectedElement = this._viewport.elementAtIndex(index); |
| 362 this._selectedElement.classList.add('selected'); | 343 this._selectedElement.classList.add('selected'); |
| 363 this._selectedElement.classList.add('force-white-icons'); | 344 this._selectedElement.classList.add('force-white-icons'); |
| 364 this._detailsPopup.classList.add('hidden'); | 345 this._detailsPopup.classList.add('hidden'); |
| 365 var elem = this._selectedElement; | 346 var elem = this._selectedElement; |
| 366 this._asyncDetails(index).then(showDetails.bind(this), function() {}); | 347 this._asyncDetails(index).then(showDetails.bind(this), function() {}); |
| 367 | 348 |
| 368 this._viewport.scrollItemIntoView(index); | 349 this._viewport.scrollItemAtIndexIntoView(index); |
| 369 this._applySuggestion(true); | 350 this._applySuggestion(true); |
| 370 | 351 |
| 371 /** | 352 /** |
| 372 * @param {?{detail: string, description: string}} details | 353 * @param {?{detail: string, description: string}} details |
| 373 * @this {UI.SuggestBox} | 354 * @this {UI.SuggestBox} |
| 374 */ | 355 */ |
| 375 function showDetails(details) { | 356 function showDetails(details) { |
| 376 if (elem === this._selectedElement) | 357 if (elem === this._selectedElement) |
| 377 this._showDetailsPopup(details); | 358 this._showDetailsPopup(details); |
| 378 } | 359 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 391 if (completions.length > 1) | 372 if (completions.length > 1) |
| 392 return true; | 373 return true; |
| 393 | 374 |
| 394 if (!completions[0].title.startsWith(userEnteredText)) | 375 if (!completions[0].title.startsWith(userEnteredText)) |
| 395 return true; | 376 return true; |
| 396 | 377 |
| 397 // Do not show a single suggestion if it is the same as user-entered query,
even if allowed to show single-item suggest boxes. | 378 // Do not show a single suggestion if it is the same as user-entered query,
even if allowed to show single-item suggest boxes. |
| 398 return canShowForSingleItem && completions[0].title !== userEnteredText; | 379 return canShowForSingleItem && completions[0].title !== userEnteredText; |
| 399 } | 380 } |
| 400 | 381 |
| 401 _ensureRowCountPerViewport() { | |
| 402 if (this._rowCountPerViewport) | |
| 403 return; | |
| 404 if (!this._items.length) | |
| 405 return; | |
| 406 | |
| 407 this._rowCountPerViewport = Math.floor(this._element.getBoundingClientRect()
.height / this._rowHeight); | |
| 408 } | |
| 409 | |
| 410 /** | 382 /** |
| 411 * @param {!AnchorBox} anchorBox | 383 * @param {!AnchorBox} anchorBox |
| 412 * @param {!UI.SuggestBox.Suggestions} completions | 384 * @param {!UI.SuggestBox.Suggestions} completions |
| 413 * @param {boolean} selectHighestPriority | 385 * @param {boolean} selectHighestPriority |
| 414 * @param {boolean} canShowForSingleItem | 386 * @param {boolean} canShowForSingleItem |
| 415 * @param {string} userEnteredText | 387 * @param {string} userEnteredText |
| 416 * @param {function(number): !Promise<{detail:string, description:string}>=} a
syncDetails | 388 * @param {function(number): !Promise<{detail:string, description:string}>=} a
syncDetails |
| 417 */ | 389 */ |
| 418 updateSuggestions( | 390 updateSuggestions( |
| 419 anchorBox, | 391 anchorBox, |
| 420 completions, | 392 completions, |
| 421 selectHighestPriority, | 393 selectHighestPriority, |
| 422 canShowForSingleItem, | 394 canShowForSingleItem, |
| 423 userEnteredText, | 395 userEnteredText, |
| 424 asyncDetails) { | 396 asyncDetails) { |
| 425 delete this._onlyCompletion; | 397 delete this._onlyCompletion; |
| 426 if (this._canShowBox(completions, canShowForSingleItem, userEnteredText)) { | 398 if (this._canShowBox(completions, canShowForSingleItem, userEnteredText)) { |
| 427 this._updateItems(completions, userEnteredText, asyncDetails); | 399 this._asyncDetailsPromises.clear(); |
| 400 this._asyncDetailsCallback = asyncDetails; |
| 401 delete this._selectedElement; |
| 402 this._userEnteredText = userEnteredText; |
| 428 this._show(); | 403 this._show(); |
| 429 this._updateBoxPosition(anchorBox); | 404 this._updateBoxPosition(anchorBox, completions.length); |
| 405 this._viewport.setFixedHeight(this._rowHeight); |
| 406 this._viewport.replaceAllItems(completions); |
| 430 this._updateWidth(); | 407 this._updateWidth(); |
| 431 this._viewport.refresh(); | |
| 432 var highestPriorityItem = -1; | 408 var highestPriorityItem = -1; |
| 433 if (selectHighestPriority) { | 409 if (selectHighestPriority) { |
| 434 var highestPriority = -Infinity; | 410 var highestPriority = -Infinity; |
| 435 for (var i = 0; i < completions.length; i++) { | 411 for (var i = 0; i < completions.length; i++) { |
| 436 var priority = completions[i].priority || 0; | 412 var priority = completions[i].priority || 0; |
| 437 if (highestPriority < priority) { | 413 if (highestPriority < priority) { |
| 438 highestPriority = priority; | 414 highestPriority = priority; |
| 439 highestPriorityItem = i; | 415 highestPriorityItem = i; |
| 440 } | 416 } |
| 441 } | 417 } |
| 442 } | 418 } |
| 443 this._selectItem(highestPriorityItem); | 419 this._selectItem(highestPriorityItem); |
| 444 delete this._rowCountPerViewport; | |
| 445 } else { | 420 } else { |
| 446 if (completions.length === 1) { | 421 if (completions.length === 1) { |
| 447 this._onlyCompletion = completions[0].title; | 422 this._onlyCompletion = completions[0].title; |
| 448 this._applySuggestion(true); | 423 this._applySuggestion(true); |
| 449 } | 424 } |
| 450 this.hide(); | 425 this.hide(); |
| 451 } | 426 } |
| 452 } | 427 } |
| 453 | 428 |
| 454 /** | 429 /** |
| (...skipping 27 matching lines...) Expand all Loading... |
| 482 * @return {boolean} | 457 * @return {boolean} |
| 483 */ | 458 */ |
| 484 downKeyPressed() { | 459 downKeyPressed() { |
| 485 return this._selectClosest(1, true); | 460 return this._selectClosest(1, true); |
| 486 } | 461 } |
| 487 | 462 |
| 488 /** | 463 /** |
| 489 * @return {boolean} | 464 * @return {boolean} |
| 490 */ | 465 */ |
| 491 pageUpKeyPressed() { | 466 pageUpKeyPressed() { |
| 492 this._ensureRowCountPerViewport(); | 467 var rowCount = Math.floor(this._viewport.element.offsetHeight / this._rowHei
ght); |
| 493 return this._selectClosest(-this._rowCountPerViewport, false); | 468 return this._selectClosest(-rowCount, false); |
| 494 } | 469 } |
| 495 | 470 |
| 496 /** | 471 /** |
| 497 * @return {boolean} | 472 * @return {boolean} |
| 498 */ | 473 */ |
| 499 pageDownKeyPressed() { | 474 pageDownKeyPressed() { |
| 500 this._ensureRowCountPerViewport(); | 475 var rowCount = Math.floor(this._viewport.element.offsetHeight / this._rowHei
ght); |
| 501 return this._selectClosest(this._rowCountPerViewport, false); | 476 return this._selectClosest(rowCount, false); |
| 502 } | 477 } |
| 503 | 478 |
| 504 /** | 479 /** |
| 505 * @return {boolean} | 480 * @return {boolean} |
| 506 */ | 481 */ |
| 507 enterKeyPressed() { | 482 enterKeyPressed() { |
| 508 if (!this._userInteracted && this._captureEnter) | 483 if (!this._userInteracted && this._captureEnter) |
| 509 return false; | 484 return false; |
| 510 | 485 |
| 511 var hasSelectedItem = !!this._selectedElement || this._onlyCompletion; | 486 var hasSelectedItem = !!this._selectedElement || this._onlyCompletion; |
| 512 this.acceptSuggestion(); | 487 this.acceptSuggestion(); |
| 513 | 488 |
| 514 // Report the event as non-handled if there is no selected item, | 489 // Report the event as non-handled if there is no selected item, |
| 515 // to commit the input or handle it otherwise. | 490 // to commit the input or handle it otherwise. |
| 516 return hasSelectedItem; | 491 return hasSelectedItem; |
| 517 } | 492 } |
| 518 | |
| 519 /** | |
| 520 * @override | |
| 521 * @param {number} index | |
| 522 * @return {number} | |
| 523 */ | |
| 524 fastItemHeight(index) { | |
| 525 return this._rowHeight; | |
| 526 } | |
| 527 | |
| 528 /** | |
| 529 * @override | |
| 530 * @return {number} | |
| 531 */ | |
| 532 itemCount() { | |
| 533 return this._items.length; | |
| 534 } | |
| 535 | |
| 536 /** | |
| 537 * @override | |
| 538 * @param {number} index | |
| 539 * @return {?Element} | |
| 540 */ | |
| 541 itemElement(index) { | |
| 542 if (!this._elementList[index]) { | |
| 543 this._elementList[index] = this._createItemElement( | |
| 544 this._userEnteredText, this._items[index].title, this._items[index].su
btitle, this._items[index].iconType, | |
| 545 this._items[index].isSecondary); | |
| 546 } | |
| 547 return this._elementList[index]; | |
| 548 } | |
| 549 }; | 493 }; |
| 550 | 494 |
| 551 /** | 495 /** |
| 552 * @typedef {!Array.<{title: string, subtitle: (string|undefined), iconType: (st
ring|undefined), priority: (number|undefined), isSecondary: (boolean|undefined)}
>} | 496 * @typedef {!{title: string, subtitle: (string|undefined), iconType: (string|un
defined), priority: (number|undefined), isSecondary: (boolean|undefined)}} |
| 497 */ |
| 498 UI.SuggestBox.Suggestion; |
| 499 |
| 500 /** |
| 501 * @typedef {!Array<!UI.SuggestBox.Suggestion>} |
| 553 */ | 502 */ |
| 554 UI.SuggestBox.Suggestions; | 503 UI.SuggestBox.Suggestions; |
| 555 | 504 |
| 556 /** | 505 /** |
| 557 * @unrestricted | 506 * @unrestricted |
| 558 */ | 507 */ |
| 559 UI.SuggestBox.Overlay = class { | 508 UI.SuggestBox.Overlay = class { |
| 560 /** | 509 /** |
| 561 * // FIXME: make SuggestBox work for multiple documents. | 510 * // FIXME: make SuggestBox work for multiple documents. |
| 562 * @suppressGlobalPropertiesCheck | 511 * @suppressGlobalPropertiesCheck |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 609 this.element.style.left = containerBox.x + 'px'; | 558 this.element.style.left = containerBox.x + 'px'; |
| 610 this.element.style.top = containerBox.y + 'px'; | 559 this.element.style.top = containerBox.y + 'px'; |
| 611 this.element.style.height = containerBox.height + 'px'; | 560 this.element.style.height = containerBox.height + 'px'; |
| 612 this.element.style.width = containerBox.width + 'px'; | 561 this.element.style.width = containerBox.width + 'px'; |
| 613 } | 562 } |
| 614 | 563 |
| 615 dispose() { | 564 dispose() { |
| 616 this.element.remove(); | 565 this.element.remove(); |
| 617 } | 566 } |
| 618 }; | 567 }; |
| OLD | NEW |