Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 * @template T | 6 * @template T |
| 7 * @interface | 7 * @interface |
| 8 */ | 8 */ |
| 9 UI.ListDelegate = function() {}; | 9 UI.ListDelegate = function() {}; |
| 10 | 10 |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 57 this.element = createElement('div'); | 57 this.element = createElement('div'); |
| 58 this.element.style.overflowY = 'auto'; | 58 this.element.style.overflowY = 'auto'; |
| 59 this._topElement = this.element.createChild('div'); | 59 this._topElement = this.element.createChild('div'); |
| 60 this._bottomElement = this.element.createChild('div'); | 60 this._bottomElement = this.element.createChild('div'); |
| 61 this._firstIndex = 0; | 61 this._firstIndex = 0; |
| 62 this._lastIndex = 0; | 62 this._lastIndex = 0; |
| 63 this._renderedHeight = 0; | 63 this._renderedHeight = 0; |
| 64 this._topHeight = 0; | 64 this._topHeight = 0; |
| 65 this._bottomHeight = 0; | 65 this._bottomHeight = 0; |
| 66 | 66 |
| 67 /** @type {!Array<T>} */ | 67 /** @type {!Common.List<T>} */ |
| 68 this._items = []; | 68 this._source = new Common.List(); |
|
einbinder
2017/05/31 23:23:08
Shouldn't this be passed in by the client?
dgozman
2017/06/01 01:03:02
Well, it's convenient for most of the clients to n
| |
| 69 this._source.addEventListener(Common.List.Events.ItemsReplaced, this._replac edItemsInRange, this); | |
| 70 | |
| 69 /** @type {!Map<T, !Element>} */ | 71 /** @type {!Map<T, !Element>} */ |
| 70 this._itemToElement = new Map(); | 72 this._itemToElement = new Map(); |
| 71 this._selectedIndex = -1; | 73 this._selectedIndex = -1; |
| 74 /** @type {?T} */ | |
| 75 this._selectedItem = null; | |
| 72 | 76 |
| 73 this.element.tabIndex = -1; | 77 this.element.tabIndex = -1; |
| 74 this.element.addEventListener('click', this._onClick.bind(this), false); | 78 this.element.addEventListener('click', this._onClick.bind(this), false); |
| 75 this.element.addEventListener('keydown', this._onKeyDown.bind(this), false); | 79 this.element.addEventListener('keydown', this._onKeyDown.bind(this), false); |
| 76 | 80 |
| 77 this._delegate = delegate; | 81 this._delegate = delegate; |
| 78 this._mode = mode || UI.ListMode.EqualHeightItems; | 82 this._mode = mode || UI.ListMode.EqualHeightItems; |
| 79 this._fixedHeight = 0; | 83 this._fixedHeight = 0; |
| 80 this._variableOffsets = new Int32Array(0); | 84 this._variableOffsets = new Int32Array(0); |
| 81 this._clearContents(); | 85 this._clearContents(); |
| 82 | 86 |
| 83 if (this._mode !== UI.ListMode.NonViewport) { | 87 if (this._mode !== UI.ListMode.NonViewport) { |
| 84 this.element.addEventListener('scroll', () => { | 88 this.element.addEventListener('scroll', () => { |
| 85 this._updateViewport(this.element.scrollTop, this.element.offsetHeight); | 89 this._updateViewport(this.element.scrollTop, this.element.offsetHeight); |
| 86 }, false); | 90 }, false); |
| 87 } | 91 } |
| 88 } | 92 } |
| 89 | 93 |
| 90 /** | 94 /** |
| 91 * @return {number} | 95 * @param {!Common.List<T>} source |
| 92 */ | 96 */ |
| 93 length() { | 97 setSource(source) { |
| 94 return this._items.length; | 98 this._itemToElement.clear(); |
| 99 var length = this._source.length(); | |
| 100 this._source.removeEventListener(Common.List.Events.ItemsReplaced, this._rep lacedItemsInRange, this); | |
| 101 this._source = source; | |
| 102 this._source.addEventListener(Common.List.Events.ItemsReplaced, this._replac edItemsInRange, this); | |
| 103 this.invalidateRange(0, length); | |
| 95 } | 104 } |
| 96 | 105 |
| 97 /** | 106 /** |
| 98 * @param {number} index | 107 * @return {!Common.List<T>} |
| 99 * @return {T} | |
| 100 */ | 108 */ |
| 101 itemAtIndex(index) { | 109 source() { |
| 102 return this._items[index]; | 110 return this._source; |
| 103 } | 111 } |
| 104 | 112 |
| 105 /** | 113 /** |
| 106 * @param {T} item | 114 * @param {!Common.Event} event |
| 107 */ | 115 */ |
| 108 pushItem(item) { | 116 _replacedItemsInRange(event) { |
| 109 this.replaceItemsInRange(this._items.length, this._items.length, [item]); | 117 var data = /** @type {{index: number, removed: !Array<T>, inserted: number}} */ (event.data); |
| 110 } | 118 var from = data.index; |
| 119 var to = from + data.removed.length; | |
| 111 | 120 |
| 112 /** | 121 var oldSelectedItem = this._selectedItem; |
| 113 * @return {T} | |
| 114 */ | |
| 115 popItem() { | |
| 116 return this.removeItemAtIndex(this._items.length - 1); | |
| 117 } | |
| 118 | |
| 119 /** | |
| 120 * @param {number} index | |
| 121 * @param {T} item | |
| 122 */ | |
| 123 insertItemAtIndex(index, item) { | |
| 124 this.replaceItemsInRange(index, index, [item]); | |
| 125 } | |
| 126 | |
| 127 /** | |
| 128 * @param {T} item | |
| 129 * @param {function(T, T):number} comparator | |
| 130 */ | |
| 131 insertItemWithComparator(item, comparator) { | |
| 132 var index = this._items.lowerBound(item, comparator); | |
| 133 this.insertItemAtIndex(index, item); | |
| 134 } | |
| 135 | |
| 136 /** | |
| 137 * @param {T} item | |
| 138 * @return {number} | |
| 139 */ | |
| 140 indexOfItem(item) { | |
| 141 return this._items.indexOf(item); | |
| 142 } | |
| 143 | |
| 144 /** | |
| 145 * @param {number} index | |
| 146 * @return {T} | |
| 147 */ | |
| 148 removeItemAtIndex(index) { | |
| 149 var result = this._items[index]; | |
| 150 this.replaceItemsInRange(index, index + 1, []); | |
| 151 return result; | |
| 152 } | |
| 153 | |
| 154 /** | |
| 155 * @param {T} item | |
| 156 */ | |
| 157 removeItem(item) { | |
| 158 var index = this._items.indexOf(item); | |
| 159 if (index === -1) { | |
| 160 console.error('Attempt to remove non-existing item'); | |
| 161 return; | |
| 162 } | |
| 163 this.removeItemAtIndex(index); | |
| 164 } | |
| 165 | |
| 166 /** | |
| 167 * @param {number} from | |
| 168 * @param {number} to | |
| 169 * @param {!Array<T>} items | |
| 170 */ | |
| 171 replaceItemsInRange(from, to, items) { | |
| 172 var oldSelectedItem = this._selectedIndex !== -1 ? this._items[this._selecte dIndex] : null; | |
| 173 var oldSelectedElement = oldSelectedItem ? (this._itemToElement.get(oldSelec tedItem) || null) : null; | 122 var oldSelectedElement = oldSelectedItem ? (this._itemToElement.get(oldSelec tedItem) || null) : null; |
| 174 | 123 for (var i = 0; i < data.removed.length; i++) |
| 175 for (var i = from; i < to; i++) | 124 this._itemToElement.delete(data.removed[i]); |
| 176 this._itemToElement.delete(this._items[i]); | 125 this._invalidate(from, to, data.inserted); |
| 177 if (items.length < 10000) { | |
| 178 this._items.splice.bind(this._items, from, to - from).apply(null, items); | |
| 179 } else { | |
| 180 // Splice may fail with too many arguments. | |
| 181 var before = this._items.slice(0, from); | |
| 182 var after = this._items.slice(to); | |
| 183 this._items = [].concat(before, items, after); | |
| 184 } | |
| 185 this._invalidate(from, to, items.length); | |
| 186 | 126 |
| 187 if (this._selectedIndex >= to) { | 127 if (this._selectedIndex >= to) { |
| 188 this._selectedIndex += items.length - (to - from); | 128 this._selectedIndex += data.inserted - (to - from); |
| 129 this._selectedItem = this._source.itemAtIndex(this._selectedIndex); | |
| 189 } else if (this._selectedIndex >= from) { | 130 } else if (this._selectedIndex >= from) { |
| 190 var index = this._findFirstSelectable(from + items.length, +1, false); | 131 var index = this._findFirstSelectable(from + data.inserted, +1, false); |
| 191 if (index === -1) | 132 if (index === -1) |
| 192 index = this._findFirstSelectable(from - 1, -1, false); | 133 index = this._findFirstSelectable(from - 1, -1, false); |
| 193 this._select(index, oldSelectedItem, oldSelectedElement); | 134 this._select(index, oldSelectedItem, oldSelectedElement); |
| 194 } | 135 } |
| 195 } | 136 } |
| 196 | 137 |
| 197 /** | 138 /** |
| 198 * @param {!Array<T>} items | 139 * @param {T} item |
| 199 */ | 140 */ |
| 200 replaceAllItems(items) { | 141 refreshItem(item) { |
| 201 this.replaceItemsInRange(0, this._items.length, items); | 142 var index = this.source().indexOfItem(item); |
| 202 } | 143 if (index === -1) { |
| 203 | 144 console.error('Item to refresh is not present'); |
| 204 refreshAllItems() { | 145 return; |
| 205 this.refreshItemsInRange(0, this._items.length); | 146 } |
| 206 } | 147 this._itemToElement.delete(item); |
| 207 | 148 this.invalidateRange(index, index + 1); |
| 208 /** | |
| 209 * @param {number} from | |
| 210 * @param {number} to | |
| 211 */ | |
| 212 refreshItemsInRange(from, to) { | |
| 213 for (var i = from; i < to; i++) | |
| 214 this._itemToElement.delete(this._items[i]); | |
| 215 this.invalidateRange(from, to); | |
| 216 if (this._selectedIndex !== -1) | 149 if (this._selectedIndex !== -1) |
| 217 this._select(this._selectedIndex, null, null); | 150 this._select(this._selectedIndex, null, null); |
| 218 } | 151 } |
| 219 | 152 |
| 220 /** | 153 /** |
| 221 * @param {number} from | 154 * @param {number} from |
| 222 * @param {number} to | 155 * @param {number} to |
| 223 */ | 156 */ |
| 224 invalidateRange(from, to) { | 157 invalidateRange(from, to) { |
| 225 this._invalidate(from, to, to - from); | 158 this._invalidate(from, to, to - from); |
| 226 } | 159 } |
| 227 | 160 |
| 228 viewportResized() { | 161 viewportResized() { |
| 229 if (this._mode === UI.ListMode.NonViewport) | 162 if (this._mode === UI.ListMode.NonViewport) |
| 230 return; | 163 return; |
| 231 // TODO(dgozman): try to keep visible scrollTop the same. | 164 // TODO(dgozman): try to keep visible scrollTop the same. |
| 232 var scrollTop = this.element.scrollTop; | 165 var scrollTop = this.element.scrollTop; |
| 233 var viewportHeight = this.element.offsetHeight; | 166 var viewportHeight = this.element.offsetHeight; |
| 234 this._clearViewport(); | 167 this._clearViewport(); |
| 235 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi ewportHeight), viewportHeight); | 168 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi ewportHeight), viewportHeight); |
| 236 } | 169 } |
| 237 | 170 |
| 238 invalidateItemHeight() { | 171 invalidateItemHeight() { |
| 239 if (this._mode !== UI.ListMode.EqualHeightItems) { | 172 if (this._mode !== UI.ListMode.EqualHeightItems) { |
| 240 console.error('Only supported in equal height items mode'); | 173 console.error('Only supported in equal height items mode'); |
| 241 return; | 174 return; |
| 242 } | 175 } |
| 243 this._fixedHeight = 0; | 176 this._fixedHeight = 0; |
| 244 if (this._items.length) { | 177 if (this._source.length()) { |
| 245 this._itemToElement.clear(); | 178 this._itemToElement.clear(); |
| 246 this._invalidate(0, this._items.length, this._items.length); | 179 this._invalidate(0, this._source.length(), this._source.length()); |
| 247 } | 180 } |
| 248 } | 181 } |
| 249 | 182 |
| 250 /** | 183 /** |
| 251 * @param {?Node} node | 184 * @param {?Node} node |
| 252 * @return {?T} | 185 * @return {?T} |
| 253 */ | 186 */ |
| 254 itemForNode(node) { | 187 itemForNode(node) { |
| 255 while (node && node.parentNodeOrShadowHost() !== this.element) | 188 while (node && node.parentNodeOrShadowHost() !== this.element) |
| 256 node = node.parentNodeOrShadowHost(); | 189 node = node.parentNodeOrShadowHost(); |
| 257 if (!node) | 190 if (!node) |
| 258 return null; | 191 return null; |
| 259 var element = /** @type {!Element} */ (node); | 192 var element = /** @type {!Element} */ (node); |
| 260 var index = this._items.findIndex(item => this._itemToElement.get(item) === element); | 193 var index = this._source.findIndex(item => this._itemToElement.get(item) === element); |
| 261 return index !== -1 ? this._items[index] : null; | 194 return index !== -1 ? this._source.itemAtIndex(index) : null; |
| 262 } | 195 } |
| 263 | 196 |
| 264 /** | 197 /** |
| 265 * @param {T} item | 198 * @param {T} item |
| 266 * @param {boolean=} center | 199 * @param {boolean=} center |
| 267 */ | 200 */ |
| 268 scrollItemIntoView(item, center) { | 201 scrollItemIntoView(item, center) { |
| 269 var index = this._items.indexOf(item); | 202 var index = this._source.indexOfItem(item); |
| 270 if (index === -1) { | 203 if (index === -1) { |
| 271 console.error('Attempt to scroll onto missing item'); | 204 console.error('Attempt to scroll onto missing item'); |
| 272 return; | 205 return; |
| 273 } | 206 } |
| 274 this._scrollIntoView(index, center); | 207 this._scrollIntoView(index, center); |
| 275 } | 208 } |
| 276 | 209 |
| 277 /** | 210 /** |
| 278 * @return {?T} | 211 * @return {?T} |
| 279 */ | 212 */ |
| 280 selectedItem() { | 213 selectedItem() { |
| 281 return this._selectedIndex === -1 ? null : this._items[this._selectedIndex]; | 214 return this._selectedItem; |
| 282 } | 215 } |
| 283 | 216 |
| 284 /** | 217 /** |
| 285 * @return {number} | 218 * @return {number} |
| 286 */ | 219 */ |
| 287 selectedIndex() { | 220 selectedIndex() { |
| 288 return this._selectedIndex; | 221 return this._selectedIndex; |
| 289 } | 222 } |
| 290 | 223 |
| 291 /** | 224 /** |
| 292 * @param {?T} item | 225 * @param {?T} item |
| 293 * @param {boolean=} center | 226 * @param {boolean=} center |
| 294 * @param {boolean=} dontScroll | 227 * @param {boolean=} dontScroll |
| 295 */ | 228 */ |
| 296 selectItem(item, center, dontScroll) { | 229 selectItem(item, center, dontScroll) { |
| 297 var index = -1; | 230 var index = -1; |
| 298 if (item !== null) { | 231 if (item !== null) { |
| 299 index = this._items.indexOf(item); | 232 index = this._source.indexOfItem(item); |
| 300 if (index === -1) { | 233 if (index === -1) { |
| 301 console.error('Attempt to select missing item'); | 234 console.error('Attempt to select missing item'); |
| 302 return; | 235 return; |
| 303 } | 236 } |
| 304 if (!this._delegate.isItemSelectable(item)) { | 237 if (!this._delegate.isItemSelectable(item)) { |
| 305 console.error('Attempt to select non-selectable item'); | 238 console.error('Attempt to select non-selectable item'); |
| 306 return; | 239 return; |
| 307 } | 240 } |
| 308 } | 241 } |
| 309 if (this._selectedIndex !== index) | 242 if (this._selectedIndex !== index) |
| 310 this._select(index); | 243 this._select(index); |
| 311 if (index !== -1 && !dontScroll) | 244 if (index !== -1 && !dontScroll) |
| 312 this._scrollIntoView(index, center); | 245 this._scrollIntoView(index, center); |
| 313 } | 246 } |
| 314 | 247 |
| 315 /** | 248 /** |
| 316 * @param {boolean=} canWrap | 249 * @param {boolean=} canWrap |
| 317 * @param {boolean=} center | 250 * @param {boolean=} center |
| 318 * @return {boolean} | 251 * @return {boolean} |
| 319 */ | 252 */ |
| 320 selectPreviousItem(canWrap, center) { | 253 selectPreviousItem(canWrap, center) { |
| 321 if (this._selectedIndex === -1 && !canWrap) | 254 if (this._selectedIndex === -1 && !canWrap) |
| 322 return false; | 255 return false; |
| 323 var index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex - 1; | 256 var index = this._selectedIndex === -1 ? this._source.length() - 1 : this._s electedIndex - 1; |
| 324 index = this._findFirstSelectable(index, -1, !!canWrap); | 257 index = this._findFirstSelectable(index, -1, !!canWrap); |
| 325 if (index !== -1) { | 258 if (index !== -1) { |
| 326 this._scrollIntoView(index, center); | 259 this._scrollIntoView(index, center); |
| 327 this._select(index); | 260 this._select(index); |
| 328 return true; | 261 return true; |
| 329 } | 262 } |
| 330 return false; | 263 return false; |
| 331 } | 264 } |
| 332 | 265 |
| 333 /** | 266 /** |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 348 return false; | 281 return false; |
| 349 } | 282 } |
| 350 | 283 |
| 351 /** | 284 /** |
| 352 * @param {boolean=} center | 285 * @param {boolean=} center |
| 353 * @return {boolean} | 286 * @return {boolean} |
| 354 */ | 287 */ |
| 355 selectItemPreviousPage(center) { | 288 selectItemPreviousPage(center) { |
| 356 if (this._mode === UI.ListMode.NonViewport) | 289 if (this._mode === UI.ListMode.NonViewport) |
| 357 return false; | 290 return false; |
| 358 var index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex; | 291 var index = this._selectedIndex === -1 ? this._source.length() - 1 : this._s electedIndex; |
| 359 index = this._findPageSelectable(index, -1); | 292 index = this._findPageSelectable(index, -1); |
| 360 if (index !== -1) { | 293 if (index !== -1) { |
| 361 this._scrollIntoView(index, center); | 294 this._scrollIntoView(index, center); |
| 362 this._select(index); | 295 this._select(index); |
| 363 return true; | 296 return true; |
| 364 } | 297 } |
| 365 return false; | 298 return false; |
| 366 } | 299 } |
| 367 | 300 |
| 368 /** | 301 /** |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 437 break; | 370 break; |
| 438 } | 371 } |
| 439 if (selected) | 372 if (selected) |
| 440 event.consume(); | 373 event.consume(); |
| 441 } | 374 } |
| 442 | 375 |
| 443 /** | 376 /** |
| 444 * @return {number} | 377 * @return {number} |
| 445 */ | 378 */ |
| 446 _totalHeight() { | 379 _totalHeight() { |
| 447 return this._offsetAtIndex(this._items.length); | 380 return this._offsetAtIndex(this._source.length()); |
| 448 } | 381 } |
| 449 | 382 |
| 450 /** | 383 /** |
| 451 * @param {number} offset | 384 * @param {number} offset |
| 452 * @return {number} | 385 * @return {number} |
| 453 */ | 386 */ |
| 454 _indexAtOffset(offset) { | 387 _indexAtOffset(offset) { |
| 455 if (this._mode === UI.ListMode.NonViewport) | 388 if (this._mode === UI.ListMode.NonViewport) |
| 456 throw 'There should be no offset conversions in non-viewport mode'; | 389 throw 'There should be no offset conversions in non-viewport mode'; |
| 457 if (!this._items.length || offset < 0) | 390 if (!this._source.length() || offset < 0) |
| 458 return 0; | 391 return 0; |
| 459 if (this._mode === UI.ListMode.VariousHeightItems) { | 392 if (this._mode === UI.ListMode.VariousHeightItems) { |
| 460 return Math.min( | 393 return Math.min( |
| 461 this._items.length - 1, this._variableOffsets.lowerBound(offset, undef ined, 0, this._items.length)); | 394 this._source.length() - 1, this._variableOffsets.lowerBound(offset, un defined, 0, this._source.length())); |
| 462 } | 395 } |
| 463 if (!this._fixedHeight) | 396 if (!this._fixedHeight) |
| 464 this._measureHeight(); | 397 this._measureHeight(); |
| 465 return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeigh t)); | 398 return Math.min(this._source.length() - 1, Math.floor(offset / this._fixedHe ight)); |
| 466 } | 399 } |
| 467 | 400 |
| 468 /** | 401 /** |
| 469 * @param {number} index | 402 * @param {number} index |
| 470 * @return {!Element} | 403 * @return {!Element} |
| 471 */ | 404 */ |
| 472 _elementAtIndex(index) { | 405 _elementAtIndex(index) { |
| 473 var item = this._items[index]; | 406 var item = this._source.itemAtIndex(index); |
| 474 var element = this._itemToElement.get(item); | 407 var element = this._itemToElement.get(item); |
| 475 if (!element) { | 408 if (!element) { |
| 476 element = this._delegate.createElementForItem(item); | 409 element = this._delegate.createElementForItem(item); |
| 477 this._itemToElement.set(item, element); | 410 this._itemToElement.set(item, element); |
| 478 } | 411 } |
| 479 return element; | 412 return element; |
| 480 } | 413 } |
| 481 | 414 |
| 482 /** | 415 /** |
| 483 * @param {number} index | 416 * @param {number} index |
| 484 * @return {number} | 417 * @return {number} |
| 485 */ | 418 */ |
| 486 _offsetAtIndex(index) { | 419 _offsetAtIndex(index) { |
| 487 if (this._mode === UI.ListMode.NonViewport) | 420 if (this._mode === UI.ListMode.NonViewport) |
| 488 throw 'There should be no offset conversions in non-viewport mode'; | 421 throw 'There should be no offset conversions in non-viewport mode'; |
| 489 if (!this._items.length) | 422 if (!this._source.length()) |
| 490 return 0; | 423 return 0; |
| 491 if (this._mode === UI.ListMode.VariousHeightItems) | 424 if (this._mode === UI.ListMode.VariousHeightItems) |
| 492 return this._variableOffsets[index]; | 425 return this._variableOffsets[index]; |
| 493 if (!this._fixedHeight) | 426 if (!this._fixedHeight) |
| 494 this._measureHeight(); | 427 this._measureHeight(); |
| 495 return index * this._fixedHeight; | 428 return index * this._fixedHeight; |
| 496 } | 429 } |
| 497 | 430 |
| 498 _measureHeight() { | 431 _measureHeight() { |
| 499 this._fixedHeight = this._delegate.heightForItem(this._items[0]); | 432 this._fixedHeight = this._delegate.heightForItem(this._source.itemAtIndex(0) ); |
| 500 if (!this._fixedHeight) | 433 if (!this._fixedHeight) |
| 501 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this. element).height; | 434 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this. element).height; |
| 502 } | 435 } |
| 503 | 436 |
| 504 /** | 437 /** |
| 505 * @param {number} index | 438 * @param {number} index |
| 506 * @param {?T=} oldItem | 439 * @param {?T=} oldItem |
| 507 * @param {?Element=} oldElement | 440 * @param {?Element=} oldElement |
| 508 */ | 441 */ |
| 509 _select(index, oldItem, oldElement) { | 442 _select(index, oldItem, oldElement) { |
| 510 if (oldItem === undefined) | 443 if (oldItem === undefined) |
| 511 oldItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; | 444 oldItem = this._selectedItem; |
| 512 if (oldElement === undefined) | 445 if (oldElement === undefined) |
| 513 oldElement = this._itemToElement.get(oldItem) || null; | 446 oldElement = this._itemToElement.get(oldItem) || null; |
| 514 this._selectedIndex = index; | 447 this._selectedIndex = index; |
| 515 var newItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; | 448 this._selectedItem = index === -1 ? null : this._source.itemAtIndex(index); |
| 449 var newItem = this._selectedItem; | |
| 516 var newElement = this._selectedIndex !== -1 ? this._elementAtIndex(index) : null; | 450 var newElement = this._selectedIndex !== -1 ? this._elementAtIndex(index) : null; |
| 517 this._delegate.selectedItemChanged(oldItem, newItem, /** @type {?Element} */ (oldElement), newElement); | 451 this._delegate.selectedItemChanged(oldItem, newItem, /** @type {?Element} */ (oldElement), newElement); |
| 518 } | 452 } |
| 519 | 453 |
| 520 /** | 454 /** |
| 521 * @param {number} index | 455 * @param {number} index |
| 522 * @param {number} direction | 456 * @param {number} direction |
| 523 * @param {boolean} canWrap | 457 * @param {boolean} canWrap |
| 524 * @return {number} | 458 * @return {number} |
| 525 */ | 459 */ |
| 526 _findFirstSelectable(index, direction, canWrap) { | 460 _findFirstSelectable(index, direction, canWrap) { |
| 527 var length = this._items.length; | 461 var length = this._source.length(); |
| 528 if (!length) | 462 if (!length) |
| 529 return -1; | 463 return -1; |
| 530 for (var step = 0; step <= length; step++) { | 464 for (var step = 0; step <= length; step++) { |
| 531 if (index < 0 || index >= length) { | 465 if (index < 0 || index >= length) { |
| 532 if (!canWrap) | 466 if (!canWrap) |
| 533 return -1; | 467 return -1; |
| 534 index = (index + length) % length; | 468 index = (index + length) % length; |
| 535 } | 469 } |
| 536 if (this._delegate.isItemSelectable(this._items[index])) | 470 if (this._delegate.isItemSelectable(this._source.itemAtIndex(index))) |
| 537 return index; | 471 return index; |
| 538 index += direction; | 472 index += direction; |
| 539 } | 473 } |
| 540 return -1; | 474 return -1; |
| 541 } | 475 } |
| 542 | 476 |
| 543 /** | 477 /** |
| 544 * @param {number} index | 478 * @param {number} index |
| 545 * @param {number} direction | 479 * @param {number} direction |
| 546 * @return {number} | 480 * @return {number} |
| 547 */ | 481 */ |
| 548 _findPageSelectable(index, direction) { | 482 _findPageSelectable(index, direction) { |
| 549 var lastSelectable = -1; | 483 var lastSelectable = -1; |
| 550 var startOffset = this._offsetAtIndex(index); | 484 var startOffset = this._offsetAtIndex(index); |
| 551 // Compensate for zoom rounding errors with -1. | 485 // Compensate for zoom rounding errors with -1. |
| 552 var viewportHeight = this.element.offsetHeight - 1; | 486 var viewportHeight = this.element.offsetHeight - 1; |
| 553 while (index >= 0 && index < this._items.length) { | 487 while (index >= 0 && index < this._source.length()) { |
| 554 if (this._delegate.isItemSelectable(this._items[index])) { | 488 if (this._delegate.isItemSelectable(this._source.itemAtIndex(index))) { |
| 555 if (Math.abs(this._offsetAtIndex(index) - startOffset) >= viewportHeight ) | 489 if (Math.abs(this._offsetAtIndex(index) - startOffset) >= viewportHeight ) |
| 556 return index; | 490 return index; |
| 557 lastSelectable = index; | 491 lastSelectable = index; |
| 558 } | 492 } |
| 559 index += direction; | 493 index += direction; |
| 560 } | 494 } |
| 561 return lastSelectable; | 495 return lastSelectable; |
| 562 } | 496 } |
| 563 | 497 |
| 564 /** | 498 /** |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 582 * @param {number} to | 516 * @param {number} to |
| 583 * @param {number} inserted | 517 * @param {number} inserted |
| 584 */ | 518 */ |
| 585 _invalidate(from, to, inserted) { | 519 _invalidate(from, to, inserted) { |
| 586 if (this._mode === UI.ListMode.NonViewport) { | 520 if (this._mode === UI.ListMode.NonViewport) { |
| 587 this._invalidateNonViewportMode(from, to - from, inserted); | 521 this._invalidateNonViewportMode(from, to - from, inserted); |
| 588 return; | 522 return; |
| 589 } | 523 } |
| 590 | 524 |
| 591 if (this._mode === UI.ListMode.VariousHeightItems) { | 525 if (this._mode === UI.ListMode.VariousHeightItems) { |
| 592 this._reallocateVariableOffsets(this._items.length + 1, from + 1); | 526 this._reallocateVariableOffsets(this._source.length() + 1, from + 1); |
| 593 for (var i = from + 1; i <= this._items.length; i++) | 527 for (var i = from + 1; i <= this._source.length(); i++) { |
| 594 this._variableOffsets[i] = this._variableOffsets[i - 1] + this._delegate .heightForItem(this._items[i - 1]); | 528 this._variableOffsets[i] = |
| 529 this._variableOffsets[i - 1] + this._delegate.heightForItem(this._so urce.itemAtIndex(i - 1)); | |
| 530 } | |
| 595 } | 531 } |
| 596 | 532 |
| 597 var viewportHeight = this.element.offsetHeight; | 533 var viewportHeight = this.element.offsetHeight; |
| 598 var totalHeight = this._totalHeight(); | 534 var totalHeight = this._totalHeight(); |
| 599 var scrollTop = this.element.scrollTop; | 535 var scrollTop = this.element.scrollTop; |
| 600 | 536 |
| 601 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) { | 537 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) { |
| 602 this._clearViewport(); | 538 this._clearViewport(); |
| 603 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewport Height), viewportHeight); | 539 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewport Height), viewportHeight); |
| 604 return; | 540 return; |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 716 this._firstIndex = firstIndex; | 652 this._firstIndex = firstIndex; |
| 717 this._lastIndex = lastIndex; | 653 this._lastIndex = lastIndex; |
| 718 this._topHeight = this._offsetAtIndex(firstIndex); | 654 this._topHeight = this._offsetAtIndex(firstIndex); |
| 719 this._topElement.style.height = this._topHeight + 'px'; | 655 this._topElement.style.height = this._topHeight + 'px'; |
| 720 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex); | 656 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex); |
| 721 this._bottomElement.style.height = this._bottomHeight + 'px'; | 657 this._bottomElement.style.height = this._bottomHeight + 'px'; |
| 722 this._renderedHeight = totalHeight; | 658 this._renderedHeight = totalHeight; |
| 723 this.element.scrollTop = scrollTop; | 659 this.element.scrollTop = scrollTop; |
| 724 } | 660 } |
| 725 }; | 661 }; |
| OLD | NEW |