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 |
| 11 UI.ListDelegate.prototype = { | 11 UI.ListDelegate.prototype = { |
| 12 /** | 12 /** |
| 13 * @param {T} item | 13 * @param {T} item |
| 14 * @return {!Element} | 14 * @return {!Element} |
| 15 */ | 15 */ |
| 16 createElementForItem(item) {}, | 16 createElementForItem(item) {}, |
| 17 | 17 |
| 18 /** | 18 /** |
| 19 * This method is not called in NonViewport mode. | |
| 20 * Return zero to make list measure the item (only works in SameHeight mode). | |
| 19 * @param {T} item | 21 * @param {T} item |
| 20 * @return {number} | 22 * @return {number} |
| 21 */ | 23 */ |
| 22 heightForItem(item) {}, | 24 heightForItem(item) {}, |
| 23 | 25 |
| 24 /** | 26 /** |
| 25 * @param {T} item | 27 * @param {T} item |
| 26 * @return {boolean} | 28 * @return {boolean} |
| 27 */ | 29 */ |
| 28 isItemSelectable(item) {}, | 30 isItemSelectable(item) {}, |
| 29 | 31 |
| 30 /** | 32 /** |
| 31 * @param {?T} from | 33 * @param {?T} from |
| 32 * @param {?T} to | 34 * @param {?T} to |
| 33 * @param {?Element} fromElement | 35 * @param {?Element} fromElement |
| 34 * @param {?Element} toElement | 36 * @param {?Element} toElement |
| 35 */ | 37 */ |
| 36 selectedItemChanged(from, to, fromElement, toElement) {}, | 38 selectedItemChanged(from, to, fromElement, toElement) {}, |
| 39 | |
| 40 /** | |
| 41 * @param {T} item | |
| 42 * @param {!Element} element | |
| 43 * @param {!Event} event | |
| 44 */ | |
| 45 itemElementClicked(item, element, event) {}, | |
|
pfeldman
2017/01/04 02:07:50
It does not help much in case you decide to listen
| |
| 37 }; | 46 }; |
| 38 | 47 |
| 39 /** @enum {symbol} */ | 48 /** @enum {symbol} */ |
| 40 UI.ListMode = { | 49 UI.ListMode = { |
| 41 Grow: Symbol('UI.ListMode.Grow'), | 50 NonViewport: Symbol('UI.ListMode.NonViewport'), |
| 42 ViewportFixedItems: Symbol('UI.ListMode.ViewportFixedItems'), | 51 SameHeightItems: Symbol('UI.ListMode.SameHeightItems'), |
|
pfeldman
2017/01/04 02:07:50
Equal/Various
| |
| 43 ViewportFixedItemsMeasured: Symbol('UI.ListMode.ViewportFixedItemsMeasured'), | 52 DifferentHeightItems: Symbol('UI.ListMode.DifferentHeightItems') |
| 44 ViewportVariableItems: Symbol('UI.ListMode.ViewportVariableItems') | |
| 45 }; | 53 }; |
| 46 | 54 |
| 47 /** | 55 /** |
| 48 * @template T | 56 * @template T |
| 49 */ | 57 */ |
| 50 UI.ListControl = class { | 58 UI.ListControl = class { |
| 51 /** | 59 /** |
| 52 * @param {!UI.ListDelegate<T>} delegate | 60 * @param {!UI.ListDelegate<T>} delegate |
| 53 * @param {!UI.ListMode=} mode | 61 * @param {!UI.ListMode=} mode |
| 54 */ | 62 */ |
| 55 constructor(delegate, mode) { | 63 constructor(delegate, mode) { |
| 56 this.element = createElement('div'); | 64 this.element = createElement('div'); |
| 57 this.element.style.overflow = 'auto'; | 65 this.element.style.overflow = 'auto'; |
| 58 this._topElement = this.element.createChild('div'); | 66 this._topElement = this.element.createChild('div'); |
| 59 this._bottomElement = this.element.createChild('div'); | 67 this._bottomElement = this.element.createChild('div'); |
| 60 this._firstIndex = 0; | 68 this._firstIndex = 0; |
| 61 this._lastIndex = 0; | 69 this._lastIndex = 0; |
| 62 this._renderedHeight = 0; | 70 this._renderedHeight = 0; |
| 63 this._topHeight = 0; | 71 this._topHeight = 0; |
| 64 this._bottomHeight = 0; | 72 this._bottomHeight = 0; |
| 65 | 73 |
| 66 /** @type {!Array<T>} */ | 74 /** @type {!Array<T>} */ |
| 67 this._items = []; | 75 this._items = []; |
| 68 /** @type {!Map<T, !Element>} */ | 76 /** @type {!Map<T, !Element>} */ |
| 69 this._itemToElement = new Map(); | 77 this._itemToElement = new Map(); |
| 70 this._selectedIndex = -1; | 78 this._selectedIndex = -1; |
| 71 | 79 |
| 72 this._boundKeyDown = event => { | 80 this.element.addEventListener('click', this._onClick.bind(this), false); |
| 73 if (this.onKeyDown(event)) | |
| 74 event.consume(true); | |
| 75 }; | |
| 76 this._boundClick = event => { | |
| 77 if (this.onClick(event)) | |
| 78 event.consume(true); | |
| 79 }; | |
| 80 this._boundScroll = event => { | 81 this._boundScroll = event => { |
| 81 this._updateViewport(this.element.scrollTop, this.element.offsetHeight); | 82 this._updateViewport(this.element.scrollTop, this.element.offsetHeight); |
| 82 }; | 83 }; |
| 83 | 84 |
| 84 this._delegate = delegate; | 85 this._delegate = delegate; |
| 85 this._mode = mode || UI.ListMode.ViewportFixedItemsMeasured; | 86 this._mode = mode || UI.ListMode.SameHeightItems; |
| 86 this._fixedHeight = 0; | 87 this._fixedHeight = 0; |
| 87 this._variableOffsets = new Int32Array(0); | 88 this._variableOffsets = new Int32Array(0); |
| 88 this._clearContents(); | 89 this._clearContents(); |
| 89 if (this._mode !== UI.ListMode.Grow) | 90 if (this._mode !== UI.ListMode.NonViewport) |
| 90 this.element.addEventListener('scroll', this._boundScroll, false); | 91 this.element.addEventListener('scroll', this._boundScroll, false); |
| 91 } | 92 } |
| 92 | 93 |
| 93 /** | 94 /** |
| 94 * @param {boolean} handleInput | |
| 95 */ | |
| 96 setHandleInput(handleInput) { | |
| 97 if (handleInput) { | |
| 98 this.element.addEventListener('keydown', this._boundKeyDown, false); | |
| 99 this.element.addEventListener('click', this._boundClick, false); | |
| 100 } else { | |
| 101 this.element.removeEventListener('keydown', this._boundKeyDown, false); | |
| 102 this.element.removeEventListener('click', this._boundClick, false); | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 /** | |
| 107 * @return {number} | 95 * @return {number} |
| 108 */ | 96 */ |
| 109 length() { | 97 length() { |
| 110 return this._items.length; | 98 return this._items.length; |
| 111 } | 99 } |
| 112 | 100 |
| 113 /** | 101 /** |
| 114 * @param {number} index | 102 * @param {number} index |
| 115 * @return {T} | 103 * @return {T} |
| 116 */ | 104 */ |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 190 | 178 |
| 191 /** | 179 /** |
| 192 * @param {number} from | 180 * @param {number} from |
| 193 * @param {number} to | 181 * @param {number} to |
| 194 */ | 182 */ |
| 195 invalidateRange(from, to) { | 183 invalidateRange(from, to) { |
| 196 this._invalidate(from, to, to - from); | 184 this._invalidate(from, to, to - from); |
| 197 } | 185 } |
| 198 | 186 |
| 199 viewportResized() { | 187 viewportResized() { |
| 200 if (this._mode === UI.ListMode.Grow) | 188 if (this._mode === UI.ListMode.NonViewport) |
| 201 return; | 189 return; |
| 202 // TODO(dgozman): try to keep visible scrollTop the same. | 190 // TODO(dgozman): try to keep visible scrollTop the same. |
| 203 var scrollTop = this.element.scrollTop; | 191 var scrollTop = this.element.scrollTop; |
| 204 var viewportHeight = this.element.offsetHeight; | 192 var viewportHeight = this.element.offsetHeight; |
| 205 this._clearViewport(); | 193 this._clearViewport(); |
| 206 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi ewportHeight), viewportHeight); | 194 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi ewportHeight), viewportHeight); |
| 207 } | 195 } |
| 208 | 196 |
| 209 fixedHeightChanged() { | 197 invalidateSameHeight() { |
|
pfeldman
2017/01/04 02:07:50
invalidateItemsHeight
| |
| 210 if (this._mode !== UI.ListMode.ViewportFixedItemsMeasured && this._mode !== UI.ListMode.ViewportFixedItems) | 198 if (this._mode !== UI.ListMode.SameHeightItems) |
| 211 throw 'Only supported in fixed height items modes'; | 199 throw 'Only supported in same height items mode'; |
| 212 this._fixedHeight = 0; | 200 this._fixedHeight = 0; |
| 213 if (this._items.length) { | 201 if (this._items.length) { |
| 214 this._itemToElement.clear(); | 202 this._itemToElement.clear(); |
| 215 this._invalidate(0, this._items.length, this._items.length); | 203 this._invalidate(0, this._items.length, this._items.length); |
| 216 } | 204 } |
| 217 } | 205 } |
| 218 | 206 |
| 219 /** | 207 /** |
| 220 * @param {number} index | 208 * @param {T} item |
| 209 * @param {boolean=} center | |
| 221 */ | 210 */ |
| 222 scrollItemAtIndexIntoView(index) { | 211 scrollItemIntoView(item, center) { |
| 223 if (this._mode === UI.ListMode.Grow) { | 212 var index = this._items.indexOf(item); |
| 224 this._elementAtIndex(index).scrollIntoViewIfNeeded(false); | 213 if (index === -1) |
| 225 return; | 214 throw 'Attempt to scroll onto missing item'; |
| 226 } | 215 this._scrollIntoView(index, center); |
| 227 var top = this._offsetAtIndex(index); | |
| 228 var bottom = this._offsetAtIndex(index + 1); | |
| 229 var scrollTop = this.element.scrollTop; | |
| 230 var viewportHeight = this.element.offsetHeight; | |
| 231 if (top < scrollTop) | |
| 232 this._updateViewport(top, viewportHeight); | |
| 233 else if (bottom > scrollTop + viewportHeight) | |
| 234 this._updateViewport(bottom - viewportHeight, viewportHeight); | |
| 235 } | |
| 236 | |
| 237 /** | |
| 238 * @param {number} index | |
| 239 * @param {boolean=} scrollIntoView | |
| 240 */ | |
| 241 selectItemAtIndex(index, scrollIntoView) { | |
| 242 if (index !== -1 && !this._delegate.isItemSelectable(this._items[index])) | |
| 243 throw 'Attempt to select non-selectable item'; | |
| 244 this._select(index); | |
| 245 if (index !== -1 && !!scrollIntoView) | |
| 246 this.scrollItemAtIndexIntoView(index); | |
| 247 } | |
| 248 | |
| 249 /** | |
| 250 * @return {number} | |
| 251 */ | |
| 252 selectedIndex() { | |
| 253 return this._selectedIndex; | |
| 254 } | 216 } |
| 255 | 217 |
| 256 /** | 218 /** |
| 257 * @return {?T} | 219 * @return {?T} |
| 258 */ | 220 */ |
| 259 selectedItem() { | 221 selectedItem() { |
| 260 return this._selectedIndex === -1 ? null : this._items[this._selectedIndex]; | 222 return this._selectedIndex === -1 ? null : this._items[this._selectedIndex]; |
| 261 } | 223 } |
| 262 | 224 |
| 263 /** | 225 /** |
| 264 * @param {!Event} event | 226 * @param {?T} item |
| 227 * @param {boolean=} center | |
| 228 */ | |
| 229 selectItem(item, center) { | |
| 230 var index = -1; | |
| 231 if (item !== null) { | |
| 232 index = this._items.indexOf(item); | |
| 233 if (index === -1) | |
| 234 throw 'Attempt to select missing item'; | |
| 235 if (!this._delegate.isItemSelectable(item)) | |
| 236 throw 'Attempt to select non-selectable item'; | |
| 237 } | |
| 238 this._select(index); | |
| 239 if (index !== -1) | |
| 240 this._scrollIntoView(index, center); | |
| 241 } | |
| 242 | |
| 243 /** | |
| 244 * @param {boolean=} canWrap | |
| 245 * @param {boolean=} center | |
| 265 * @return {boolean} | 246 * @return {boolean} |
| 266 */ | 247 */ |
| 267 onKeyDown(event) { | 248 selectPreviousItem(canWrap, center) { |
| 268 var index = -1; | 249 if (this._selectedIndex === -1 && !canWrap) |
| 269 switch (event.key) { | 250 return false; |
| 270 case 'ArrowUp': | 251 var index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex - 1; |
| 271 index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex - 1; | 252 index = this._findFirstSelectable(index, -1, !!canWrap); |
| 272 index = this._findFirstSelectable(index, -1, true); | |
| 273 break; | |
| 274 case 'ArrowDown': | |
| 275 index = this._selectedIndex === -1 ? 0 : this._selectedIndex + 1; | |
| 276 index = this._findFirstSelectable(index, +1, true); | |
| 277 break; | |
| 278 case 'PageUp': | |
| 279 if (this._mode === UI.ListMode.Grow) | |
| 280 return false; | |
| 281 index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex; | |
| 282 index = this._findPageSelectable(index, -1); | |
| 283 break; | |
| 284 case 'PageDown': | |
| 285 if (this._mode === UI.ListMode.Grow) | |
| 286 return false; | |
| 287 index = this._selectedIndex === -1 ? 0 : this._selectedIndex; | |
| 288 index = this._findPageSelectable(index, +1); | |
| 289 break; | |
| 290 default: | |
| 291 return false; | |
| 292 } | |
| 293 if (index !== -1) { | 253 if (index !== -1) { |
| 294 this.scrollItemAtIndexIntoView(index); | 254 this._scrollIntoView(index, center); |
| 295 this._select(index); | 255 this._select(index); |
| 296 return true; | 256 return true; |
| 297 } | 257 } |
| 258 return false; | |
| 259 } | |
| 260 | |
| 261 /** | |
| 262 * @param {boolean=} canWrap | |
| 263 * @param {boolean=} center | |
| 264 * @return {boolean} | |
| 265 */ | |
| 266 selectNextItem(canWrap, center) { | |
| 267 if (this._selectedIndex === -1 && !canWrap) | |
| 268 return false; | |
| 269 var index = this._selectedIndex === -1 ? 0 : this._selectedIndex + 1; | |
| 270 index = this._findFirstSelectable(index, +1, !!canWrap); | |
| 271 if (index !== -1) { | |
| 272 this._scrollIntoView(index, center); | |
| 273 this._select(index); | |
| 274 return true; | |
| 275 } | |
| 276 return false; | |
| 277 } | |
| 278 | |
| 279 /** | |
| 280 * @param {boolean=} center | |
| 281 * @return {boolean} | |
| 282 */ | |
| 283 selectItemPreviousPage(center) { | |
| 284 if (this._mode === UI.ListMode.NonViewport) | |
| 285 return false; | |
| 286 var index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex; | |
| 287 index = this._findPageSelectable(index, -1); | |
| 288 if (index !== -1) { | |
| 289 this._scrollIntoView(index, center); | |
| 290 this._select(index); | |
| 291 return true; | |
| 292 } | |
| 293 return false; | |
| 294 } | |
| 295 | |
| 296 /** | |
| 297 * @param {boolean=} center | |
| 298 * @return {boolean} | |
| 299 */ | |
| 300 selectItemNextPage(center) { | |
| 301 if (this._mode === UI.ListMode.NonViewport) | |
| 302 return false; | |
| 303 var index = this._selectedIndex === -1 ? 0 : this._selectedIndex; | |
| 304 index = this._findPageSelectable(index, +1); | |
| 305 if (index !== -1) { | |
| 306 this._scrollIntoView(index, center); | |
| 307 this._select(index); | |
| 308 return true; | |
| 309 } | |
| 298 return false; | 310 return false; |
| 299 } | 311 } |
| 300 | 312 |
| 301 /** | 313 /** |
| 314 * @param {number} index | |
| 315 * @param {boolean=} center | |
| 316 */ | |
| 317 _scrollIntoView(index, center) { | |
| 318 if (this._mode === UI.ListMode.NonViewport) { | |
| 319 this._elementAtIndex(index).scrollIntoViewIfNeeded(!!center); | |
| 320 return; | |
| 321 } | |
| 322 | |
| 323 var top = this._offsetAtIndex(index); | |
| 324 var bottom = this._offsetAtIndex(index + 1); | |
| 325 var viewportHeight = this.element.offsetHeight; | |
| 326 if (center) { | |
| 327 var scrollTo = (top + bottom) / 2 - viewportHeight / 2; | |
| 328 this._updateViewport(Number.constrain(scrollTo, 0, this._totalHeight() - v iewportHeight), viewportHeight); | |
| 329 return; | |
| 330 } | |
| 331 | |
| 332 var scrollTop = this.element.scrollTop; | |
| 333 if (top < scrollTop) | |
| 334 this._updateViewport(top, viewportHeight); | |
| 335 else if (bottom > scrollTop + viewportHeight) | |
| 336 this._updateViewport(bottom - viewportHeight, viewportHeight); | |
| 337 } | |
| 338 | |
| 339 /** | |
| 302 * @param {!Event} event | 340 * @param {!Event} event |
| 303 * @return {boolean} | |
| 304 */ | 341 */ |
| 305 onClick(event) { | 342 _onClick(event) { |
| 306 var node = event.target; | 343 var node = event.target; |
| 307 while (node && node.parentNodeOrShadowHost() !== this.element) | 344 while (node && node.parentNodeOrShadowHost() !== this.element) |
| 308 node = node.parentNodeOrShadowHost(); | 345 node = node.parentNodeOrShadowHost(); |
| 309 if (!node) | 346 if (!node) |
| 310 return false; | 347 return; |
| 311 var index = this._items.findIndex(item => this._itemToElement.get(item) === node); | 348 var element = /** @type {!Element} */ (node); |
| 312 if (index === -1 || !this._delegate.isItemSelectable(this._items[index])) | 349 var index = this._items.findIndex(item => this._itemToElement.get(item) === element); |
| 313 return false; | 350 if (index !== -1) |
| 314 this._select(index); | 351 this._delegate.itemElementClicked(this._items[index], element, event); |
| 315 return true; | |
| 316 } | 352 } |
| 317 | 353 |
| 318 /** | 354 /** |
| 319 * @return {number} | 355 * @return {number} |
| 320 */ | 356 */ |
| 321 _totalHeight() { | 357 _totalHeight() { |
| 322 return this._offsetAtIndex(this._items.length); | 358 return this._offsetAtIndex(this._items.length); |
| 323 } | 359 } |
| 324 | 360 |
| 325 /** | 361 /** |
| 326 * @param {number} offset | 362 * @param {number} offset |
| 327 * @return {number} | 363 * @return {number} |
| 328 */ | 364 */ |
| 329 _indexAtOffset(offset) { | 365 _indexAtOffset(offset) { |
| 330 if (this._mode === UI.ListMode.Grow) | 366 if (this._mode === UI.ListMode.NonViewport) |
| 331 throw 'There should be no offset conversions in grow mode'; | 367 throw 'There should be no offset conversions in non-viewport mode'; |
| 332 if (!this._items.length || offset < 0) | 368 if (!this._items.length || offset < 0) |
| 333 return 0; | 369 return 0; |
| 334 if (this._mode === UI.ListMode.ViewportVariableItems) { | 370 if (this._mode === UI.ListMode.DifferentHeightItems) { |
| 335 return Math.min( | 371 return Math.min( |
| 336 this._items.length - 1, this._variableOffsets.lowerBound(offset, undef ined, 0, this._items.length)); | 372 this._items.length - 1, this._variableOffsets.lowerBound(offset, undef ined, 0, this._items.length)); |
| 337 } | 373 } |
| 338 if (!this._fixedHeight) | 374 if (!this._fixedHeight) |
| 339 this._measureHeight(); | 375 this._measureHeight(); |
| 340 return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeigh t)); | 376 return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeigh t)); |
| 341 } | 377 } |
| 342 | 378 |
| 343 /** | 379 /** |
| 344 * @param {number} index | 380 * @param {number} index |
| 345 * @return {!Element} | 381 * @return {!Element} |
| 346 */ | 382 */ |
| 347 _elementAtIndex(index) { | 383 _elementAtIndex(index) { |
| 348 var item = this._items[index]; | 384 var item = this._items[index]; |
| 349 var element = this._itemToElement.get(item); | 385 var element = this._itemToElement.get(item); |
| 350 if (!element) { | 386 if (!element) { |
| 351 element = this._delegate.createElementForItem(item); | 387 element = this._delegate.createElementForItem(item); |
| 352 this._itemToElement.set(item, element); | 388 this._itemToElement.set(item, element); |
| 353 } | 389 } |
| 354 return element; | 390 return element; |
| 355 } | 391 } |
| 356 | 392 |
| 357 /** | 393 /** |
| 358 * @param {number} index | 394 * @param {number} index |
| 359 * @return {number} | 395 * @return {number} |
| 360 */ | 396 */ |
| 361 _offsetAtIndex(index) { | 397 _offsetAtIndex(index) { |
| 362 if (this._mode === UI.ListMode.Grow) | 398 if (this._mode === UI.ListMode.NonViewport) |
| 363 throw 'There should be no offset conversions in grow mode'; | 399 throw 'There should be no offset conversions in non-viewport mode'; |
| 364 if (!this._items.length) | 400 if (!this._items.length) |
| 365 return 0; | 401 return 0; |
| 366 if (this._mode === UI.ListMode.ViewportVariableItems) | 402 if (this._mode === UI.ListMode.DifferentHeightItems) |
| 367 return this._variableOffsets[index]; | 403 return this._variableOffsets[index]; |
| 368 if (!this._fixedHeight) | 404 if (!this._fixedHeight) |
| 369 this._measureHeight(); | 405 this._measureHeight(); |
| 370 return index * this._fixedHeight; | 406 return index * this._fixedHeight; |
| 371 } | 407 } |
| 372 | 408 |
| 373 _measureHeight() { | 409 _measureHeight() { |
| 374 if (this._mode === UI.ListMode.ViewportFixedItemsMeasured) | 410 this._fixedHeight = this._delegate.heightForItem(this._items[0]); |
| 411 if (!this._fixedHeight) | |
| 375 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this. element).height; | 412 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this. element).height; |
| 376 else | |
| 377 this._fixedHeight = this._delegate.heightForItem(this._items[0]); | |
| 378 } | 413 } |
| 379 | 414 |
| 380 /** | 415 /** |
| 381 * @param {number} index | 416 * @param {number} index |
| 382 * @param {?T=} oldItem | 417 * @param {?T=} oldItem |
| 383 * @param {?Element=} oldElement | 418 * @param {?Element=} oldElement |
| 384 */ | 419 */ |
| 385 _select(index, oldItem, oldElement) { | 420 _select(index, oldItem, oldElement) { |
| 386 if (oldItem === undefined) | 421 if (oldItem === undefined) |
| 387 oldItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; | 422 oldItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 452 this._variableOffsets = variableOffsets; | 487 this._variableOffsets = variableOffsets; |
| 453 } | 488 } |
| 454 } | 489 } |
| 455 | 490 |
| 456 /** | 491 /** |
| 457 * @param {number} from | 492 * @param {number} from |
| 458 * @param {number} to | 493 * @param {number} to |
| 459 * @param {number} inserted | 494 * @param {number} inserted |
| 460 */ | 495 */ |
| 461 _invalidate(from, to, inserted) { | 496 _invalidate(from, to, inserted) { |
| 462 if (this._mode === UI.ListMode.Grow) { | 497 if (this._mode === UI.ListMode.NonViewport) { |
| 463 this._invalidateGrowMode(from, to - from, inserted); | 498 this._invalidateNonViewportMode(from, to - from, inserted); |
| 464 return; | 499 return; |
| 465 } | 500 } |
| 466 | 501 |
| 467 if (this._mode === UI.ListMode.ViewportVariableItems) { | 502 if (this._mode === UI.ListMode.DifferentHeightItems) { |
| 468 this._reallocateVariableOffsets(this._items.length + 1, from + 1); | 503 this._reallocateVariableOffsets(this._items.length + 1, from + 1); |
| 469 for (var i = from + 1; i <= this._items.length; i++) | 504 for (var i = from + 1; i <= this._items.length; i++) |
| 470 this._variableOffsets[i] = this._variableOffsets[i - 1] + this._delegate .heightForItem(this._items[i - 1]); | 505 this._variableOffsets[i] = this._variableOffsets[i - 1] + this._delegate .heightForItem(this._items[i - 1]); |
| 471 } | 506 } |
| 472 | 507 |
| 473 var viewportHeight = this.element.offsetHeight; | 508 var viewportHeight = this.element.offsetHeight; |
| 474 var totalHeight = this._totalHeight(); | 509 var totalHeight = this._totalHeight(); |
| 475 var scrollTop = this.element.scrollTop; | 510 var scrollTop = this.element.scrollTop; |
| 476 | 511 |
| 477 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) { | 512 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) { |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 505 // when invalidating after firstIndex but before first visible element. | 540 // when invalidating after firstIndex but before first visible element. |
| 506 this._clearViewport(); | 541 this._clearViewport(); |
| 507 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewportHe ight), viewportHeight); | 542 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewportHe ight), viewportHeight); |
| 508 } | 543 } |
| 509 | 544 |
| 510 /** | 545 /** |
| 511 * @param {number} start | 546 * @param {number} start |
| 512 * @param {number} remove | 547 * @param {number} remove |
| 513 * @param {number} add | 548 * @param {number} add |
| 514 */ | 549 */ |
| 515 _invalidateGrowMode(start, remove, add) { | 550 _invalidateNonViewportMode(start, remove, add) { |
| 516 var startElement = this._topElement; | 551 var startElement = this._topElement; |
| 517 for (var index = 0; index < start; index++) | 552 for (var index = 0; index < start; index++) |
| 518 startElement = startElement.nextElementSibling; | 553 startElement = startElement.nextElementSibling; |
| 519 while (remove--) | 554 while (remove--) |
| 520 startElement.nextElementSibling.remove(); | 555 startElement.nextElementSibling.remove(); |
| 521 while (add--) | 556 while (add--) |
| 522 this.element.insertBefore(this._elementAtIndex(start + add), startElement. nextElementSibling); | 557 this.element.insertBefore(this._elementAtIndex(start + add), startElement. nextElementSibling); |
| 523 } | 558 } |
| 524 | 559 |
| 525 _clearViewport() { | 560 _clearViewport() { |
| 526 if (this._mode === UI.ListMode.Grow) | 561 if (this._mode === UI.ListMode.NonViewport) |
| 527 throw 'There should be no viewport updates in grow mode'; | 562 throw 'There should be no viewport updates in non-viewport mode'; |
| 528 this._firstIndex = 0; | 563 this._firstIndex = 0; |
| 529 this._lastIndex = 0; | 564 this._lastIndex = 0; |
| 530 this._renderedHeight = 0; | 565 this._renderedHeight = 0; |
| 531 this._topHeight = 0; | 566 this._topHeight = 0; |
| 532 this._bottomHeight = 0; | 567 this._bottomHeight = 0; |
| 533 this._clearContents(); | 568 this._clearContents(); |
| 534 } | 569 } |
| 535 | 570 |
| 536 _clearContents() { | 571 _clearContents() { |
| 537 // Note: this method should not force layout. Be careful. | 572 // Note: this method should not force layout. Be careful. |
| 538 this._topElement.style.height = '0'; | 573 this._topElement.style.height = '0'; |
| 539 this._bottomElement.style.height = '0'; | 574 this._bottomElement.style.height = '0'; |
| 540 this.element.removeChildren(); | 575 this.element.removeChildren(); |
| 541 this.element.appendChild(this._topElement); | 576 this.element.appendChild(this._topElement); |
| 542 this.element.appendChild(this._bottomElement); | 577 this.element.appendChild(this._bottomElement); |
| 543 } | 578 } |
| 544 | 579 |
| 545 /** | 580 /** |
| 546 * @param {number} scrollTop | 581 * @param {number} scrollTop |
| 547 * @param {number} viewportHeight | 582 * @param {number} viewportHeight |
| 548 */ | 583 */ |
| 549 _updateViewport(scrollTop, viewportHeight) { | 584 _updateViewport(scrollTop, viewportHeight) { |
| 550 // Note: this method should not force layout. Be careful. | 585 // Note: this method should not force layout. Be careful. |
| 551 if (this._mode === UI.ListMode.Grow) | 586 if (this._mode === UI.ListMode.NonViewport) |
| 552 throw 'There should be no viewport updates in grow mode'; | 587 throw 'There should be no viewport updates in non-viewport mode'; |
| 553 | 588 |
| 554 var totalHeight = this._totalHeight(); | 589 var totalHeight = this._totalHeight(); |
| 555 if (!totalHeight) { | 590 if (!totalHeight) { |
| 556 this._firstIndex = 0; | 591 this._firstIndex = 0; |
| 557 this._lastIndex = 0; | 592 this._lastIndex = 0; |
| 558 this._topHeight = 0; | 593 this._topHeight = 0; |
| 559 this._bottomHeight = 0; | 594 this._bottomHeight = 0; |
| 560 this._renderedHeight = 0; | 595 this._renderedHeight = 0; |
| 561 this._topElement.style.height = '0'; | 596 this._topElement.style.height = '0'; |
| 562 this._bottomElement.style.height = '0'; | 597 this._bottomElement.style.height = '0'; |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 589 this._firstIndex = firstIndex; | 624 this._firstIndex = firstIndex; |
| 590 this._lastIndex = lastIndex; | 625 this._lastIndex = lastIndex; |
| 591 this._topHeight = this._offsetAtIndex(firstIndex); | 626 this._topHeight = this._offsetAtIndex(firstIndex); |
| 592 this._topElement.style.height = this._topHeight + 'px'; | 627 this._topElement.style.height = this._topHeight + 'px'; |
| 593 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex); | 628 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex); |
| 594 this._bottomElement.style.height = this._bottomHeight + 'px'; | 629 this._bottomElement.style.height = this._bottomHeight + 'px'; |
| 595 this._renderedHeight = totalHeight; | 630 this._renderedHeight = totalHeight; |
| 596 this.element.scrollTop = scrollTop; | 631 this.element.scrollTop = scrollTop; |
| 597 } | 632 } |
| 598 }; | 633 }; |
| OLD | NEW |