| 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) {}, |
| 37 }; | 39 }; |
| 38 | 40 |
| 39 /** @enum {symbol} */ | 41 /** @enum {symbol} */ |
| 40 UI.ListMode = { | 42 UI.ListMode = { |
| 41 Grow: Symbol('UI.ListMode.Grow'), | 43 NonViewport: Symbol('UI.ListMode.NonViewport'), |
| 42 ViewportFixedItems: Symbol('UI.ListMode.ViewportFixedItems'), | 44 EqualHeightItems: Symbol('UI.ListMode.EqualHeightItems'), |
| 43 ViewportFixedItemsMeasured: Symbol('UI.ListMode.ViewportFixedItemsMeasured'), | 45 VariousHeightItems: Symbol('UI.ListMode.VariousHeightItems') |
| 44 ViewportVariableItems: Symbol('UI.ListMode.ViewportVariableItems') | |
| 45 }; | 46 }; |
| 46 | 47 |
| 47 /** | 48 /** |
| 48 * @template T | 49 * @template T |
| 49 */ | 50 */ |
| 50 UI.ListControl = class { | 51 UI.ListControl = class { |
| 51 /** | 52 /** |
| 52 * @param {!UI.ListDelegate<T>} delegate | 53 * @param {!UI.ListDelegate<T>} delegate |
| 53 * @param {!UI.ListMode=} mode | 54 * @param {!UI.ListMode=} mode |
| 54 */ | 55 */ |
| 55 constructor(delegate, mode) { | 56 constructor(delegate, mode) { |
| 56 this.element = createElement('div'); | 57 this.element = createElement('div'); |
| 57 this.element.style.overflowY = 'auto'; | 58 this.element.style.overflowY = 'auto'; |
| 58 this._topElement = this.element.createChild('div'); | 59 this._topElement = this.element.createChild('div'); |
| 59 this._bottomElement = this.element.createChild('div'); | 60 this._bottomElement = this.element.createChild('div'); |
| 60 this._firstIndex = 0; | 61 this._firstIndex = 0; |
| 61 this._lastIndex = 0; | 62 this._lastIndex = 0; |
| 62 this._renderedHeight = 0; | 63 this._renderedHeight = 0; |
| 63 this._topHeight = 0; | 64 this._topHeight = 0; |
| 64 this._bottomHeight = 0; | 65 this._bottomHeight = 0; |
| 65 | 66 |
| 66 /** @type {!Array<T>} */ | 67 /** @type {!Array<T>} */ |
| 67 this._items = []; | 68 this._items = []; |
| 68 /** @type {!Map<T, !Element>} */ | 69 /** @type {!Map<T, !Element>} */ |
| 69 this._itemToElement = new Map(); | 70 this._itemToElement = new Map(); |
| 70 this._selectedIndex = -1; | 71 this._selectedIndex = -1; |
| 71 | 72 |
| 72 this._boundKeyDown = event => { | 73 this.element.addEventListener('click', this._onClick.bind(this), false); |
| 73 if (this.onKeyDown(event)) | 74 this.element.addEventListener('keydown', this._onKeyDown.bind(this), false); |
| 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._updateViewport(this.element.scrollTop, this.element.offsetHeight); | |
| 82 }; | |
| 83 | 75 |
| 84 this._delegate = delegate; | 76 this._delegate = delegate; |
| 85 this._mode = mode || UI.ListMode.ViewportFixedItemsMeasured; | 77 this._mode = mode || UI.ListMode.EqualHeightItems; |
| 86 this._fixedHeight = 0; | 78 this._fixedHeight = 0; |
| 87 this._variableOffsets = new Int32Array(0); | 79 this._variableOffsets = new Int32Array(0); |
| 88 this._clearContents(); | 80 this._clearContents(); |
| 89 if (this._mode !== UI.ListMode.Grow) | |
| 90 this.element.addEventListener('scroll', this._boundScroll, false); | |
| 91 } | |
| 92 | 81 |
| 93 /** | 82 if (this._mode !== UI.ListMode.NonViewport) { |
| 94 * @param {boolean} handleInput | 83 this.element.addEventListener('scroll', () => { |
| 95 */ | 84 this._updateViewport(this.element.scrollTop, this.element.offsetHeight); |
| 96 setHandleInput(handleInput) { | 85 }, false); |
| 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 } | 86 } |
| 104 } | 87 } |
| 105 | 88 |
| 106 /** | 89 /** |
| 107 * @return {number} | 90 * @return {number} |
| 108 */ | 91 */ |
| 109 length() { | 92 length() { |
| 110 return this._items.length; | 93 return this._items.length; |
| 111 } | 94 } |
| 112 | 95 |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 190 | 173 |
| 191 /** | 174 /** |
| 192 * @param {number} from | 175 * @param {number} from |
| 193 * @param {number} to | 176 * @param {number} to |
| 194 */ | 177 */ |
| 195 invalidateRange(from, to) { | 178 invalidateRange(from, to) { |
| 196 this._invalidate(from, to, to - from); | 179 this._invalidate(from, to, to - from); |
| 197 } | 180 } |
| 198 | 181 |
| 199 viewportResized() { | 182 viewportResized() { |
| 200 if (this._mode === UI.ListMode.Grow) | 183 if (this._mode === UI.ListMode.NonViewport) |
| 201 return; | 184 return; |
| 202 // TODO(dgozman): try to keep visible scrollTop the same. | 185 // TODO(dgozman): try to keep visible scrollTop the same. |
| 203 var scrollTop = this.element.scrollTop; | 186 var scrollTop = this.element.scrollTop; |
| 204 var viewportHeight = this.element.offsetHeight; | 187 var viewportHeight = this.element.offsetHeight; |
| 205 this._clearViewport(); | 188 this._clearViewport(); |
| 206 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi
ewportHeight), viewportHeight); | 189 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi
ewportHeight), viewportHeight); |
| 207 } | 190 } |
| 208 | 191 |
| 209 fixedHeightChanged() { | 192 invalidateItemHeight() { |
| 210 if (this._mode !== UI.ListMode.ViewportFixedItemsMeasured && this._mode !==
UI.ListMode.ViewportFixedItems) | 193 if (this._mode !== UI.ListMode.EqualHeightItems) |
| 211 throw 'Only supported in fixed height items modes'; | 194 throw 'Only supported in equal height items mode'; |
| 212 this._fixedHeight = 0; | 195 this._fixedHeight = 0; |
| 213 if (this._items.length) { | 196 if (this._items.length) { |
| 214 this._itemToElement.clear(); | 197 this._itemToElement.clear(); |
| 215 this._invalidate(0, this._items.length, this._items.length); | 198 this._invalidate(0, this._items.length, this._items.length); |
| 216 } | 199 } |
| 217 } | 200 } |
| 218 | 201 |
| 219 /** | 202 /** |
| 220 * @param {number} index | 203 * @param {!Element} element |
| 204 * @return {?T} |
| 221 */ | 205 */ |
| 222 scrollItemAtIndexIntoView(index) { | 206 itemForElement(element) { |
| 223 if (this._mode === UI.ListMode.Grow) { | 207 var index = this._items.findIndex(item => this._itemToElement.get(item) ===
element); |
| 224 this._elementAtIndex(index).scrollIntoViewIfNeeded(false); | 208 return index !== -1 ? this._items[index] : null; |
| 225 return; | |
| 226 } | |
| 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 } | 209 } |
| 236 | 210 |
| 237 /** | 211 /** |
| 238 * @param {number} index | 212 * @param {T} item |
| 239 * @param {boolean=} scrollIntoView | 213 * @param {boolean=} center |
| 240 */ | 214 */ |
| 241 selectItemAtIndex(index, scrollIntoView) { | 215 scrollItemIntoView(item, center) { |
| 242 if (index !== -1 && !this._delegate.isItemSelectable(this._items[index])) | 216 var index = this._items.indexOf(item); |
| 243 throw 'Attempt to select non-selectable item'; | 217 if (index === -1) |
| 244 this._select(index); | 218 throw 'Attempt to scroll onto missing item'; |
| 245 if (index !== -1 && !!scrollIntoView) | 219 this._scrollIntoView(index, center); |
| 246 this.scrollItemAtIndexIntoView(index); | |
| 247 } | |
| 248 | |
| 249 /** | |
| 250 * @return {number} | |
| 251 */ | |
| 252 selectedIndex() { | |
| 253 return this._selectedIndex; | |
| 254 } | 220 } |
| 255 | 221 |
| 256 /** | 222 /** |
| 257 * @return {?T} | 223 * @return {?T} |
| 258 */ | 224 */ |
| 259 selectedItem() { | 225 selectedItem() { |
| 260 return this._selectedIndex === -1 ? null : this._items[this._selectedIndex]; | 226 return this._selectedIndex === -1 ? null : this._items[this._selectedIndex]; |
| 261 } | 227 } |
| 262 | 228 |
| 263 /** | 229 /** |
| 264 * @param {!Event} event | 230 * @param {?T} item |
| 231 * @param {boolean=} center |
| 232 */ |
| 233 selectItem(item, center) { |
| 234 var index = -1; |
| 235 if (item !== null) { |
| 236 index = this._items.indexOf(item); |
| 237 if (index === -1) |
| 238 throw 'Attempt to select missing item'; |
| 239 if (!this._delegate.isItemSelectable(item)) |
| 240 throw 'Attempt to select non-selectable item'; |
| 241 } |
| 242 this._select(index); |
| 243 if (index !== -1) |
| 244 this._scrollIntoView(index, center); |
| 245 } |
| 246 |
| 247 /** |
| 248 * @param {boolean=} canWrap |
| 249 * @param {boolean=} center |
| 265 * @return {boolean} | 250 * @return {boolean} |
| 266 */ | 251 */ |
| 267 onKeyDown(event) { | 252 selectPreviousItem(canWrap, center) { |
| 268 var index = -1; | 253 if (this._selectedIndex === -1 && !canWrap) |
| 269 switch (event.key) { | 254 return false; |
| 270 case 'ArrowUp': | 255 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; | 256 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) { | 257 if (index !== -1) { |
| 294 this.scrollItemAtIndexIntoView(index); | 258 this._scrollIntoView(index, center); |
| 295 this._select(index); | 259 this._select(index); |
| 296 return true; | 260 return true; |
| 297 } | 261 } |
| 262 return false; |
| 263 } |
| 264 |
| 265 /** |
| 266 * @param {boolean=} canWrap |
| 267 * @param {boolean=} center |
| 268 * @return {boolean} |
| 269 */ |
| 270 selectNextItem(canWrap, center) { |
| 271 if (this._selectedIndex === -1 && !canWrap) |
| 272 return false; |
| 273 var index = this._selectedIndex === -1 ? 0 : this._selectedIndex + 1; |
| 274 index = this._findFirstSelectable(index, +1, !!canWrap); |
| 275 if (index !== -1) { |
| 276 this._scrollIntoView(index, center); |
| 277 this._select(index); |
| 278 return true; |
| 279 } |
| 280 return false; |
| 281 } |
| 282 |
| 283 /** |
| 284 * @param {boolean=} center |
| 285 * @return {boolean} |
| 286 */ |
| 287 selectItemPreviousPage(center) { |
| 288 if (this._mode === UI.ListMode.NonViewport) |
| 289 return false; |
| 290 var index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele
ctedIndex; |
| 291 index = this._findPageSelectable(index, -1); |
| 292 if (index !== -1) { |
| 293 this._scrollIntoView(index, center); |
| 294 this._select(index); |
| 295 return true; |
| 296 } |
| 297 return false; |
| 298 } |
| 299 |
| 300 /** |
| 301 * @param {boolean=} center |
| 302 * @return {boolean} |
| 303 */ |
| 304 selectItemNextPage(center) { |
| 305 if (this._mode === UI.ListMode.NonViewport) |
| 306 return false; |
| 307 var index = this._selectedIndex === -1 ? 0 : this._selectedIndex; |
| 308 index = this._findPageSelectable(index, +1); |
| 309 if (index !== -1) { |
| 310 this._scrollIntoView(index, center); |
| 311 this._select(index); |
| 312 return true; |
| 313 } |
| 298 return false; | 314 return false; |
| 299 } | 315 } |
| 300 | 316 |
| 301 /** | 317 /** |
| 318 * @param {number} index |
| 319 * @param {boolean=} center |
| 320 */ |
| 321 _scrollIntoView(index, center) { |
| 322 if (this._mode === UI.ListMode.NonViewport) { |
| 323 this._elementAtIndex(index).scrollIntoViewIfNeeded(!!center); |
| 324 return; |
| 325 } |
| 326 |
| 327 var top = this._offsetAtIndex(index); |
| 328 var bottom = this._offsetAtIndex(index + 1); |
| 329 var viewportHeight = this.element.offsetHeight; |
| 330 if (center) { |
| 331 var scrollTo = (top + bottom) / 2 - viewportHeight / 2; |
| 332 this._updateViewport(Number.constrain(scrollTo, 0, this._totalHeight() - v
iewportHeight), viewportHeight); |
| 333 return; |
| 334 } |
| 335 |
| 336 var scrollTop = this.element.scrollTop; |
| 337 if (top < scrollTop) |
| 338 this._updateViewport(top, viewportHeight); |
| 339 else if (bottom > scrollTop + viewportHeight) |
| 340 this._updateViewport(bottom - viewportHeight, viewportHeight); |
| 341 } |
| 342 |
| 343 /** |
| 302 * @param {!Event} event | 344 * @param {!Event} event |
| 303 * @return {boolean} | |
| 304 */ | 345 */ |
| 305 onClick(event) { | 346 _onClick(event) { |
| 306 var node = event.target; | 347 var node = event.target; |
| 307 while (node && node.parentNodeOrShadowHost() !== this.element) | 348 while (node && node.parentNodeOrShadowHost() !== this.element) |
| 308 node = node.parentNodeOrShadowHost(); | 349 node = node.parentNodeOrShadowHost(); |
| 309 if (!node) | 350 if (!node) |
| 310 return false; | 351 return; |
| 311 var index = this._items.findIndex(item => this._itemToElement.get(item) ===
node); | 352 var element = /** @type {!Element} */ (node); |
| 312 if (index === -1 || !this._delegate.isItemSelectable(this._items[index])) | 353 var item = this.itemForElement(element); |
| 313 return false; | 354 if (item) |
| 314 this._select(index); | 355 this.selectItem(item); |
| 315 return true; | |
| 316 } | 356 } |
| 317 | 357 |
| 318 /** | 358 /** |
| 359 * @param {!Event} event |
| 360 */ |
| 361 _onKeyDown(event) { |
| 362 var selected = false; |
| 363 switch (event.key) { |
| 364 case 'ArrowUp': |
| 365 selected = this.selectPreviousItem(true, false); |
| 366 break; |
| 367 case 'ArrowDown': |
| 368 selected = this.selectNextItem(true, false); |
| 369 break; |
| 370 case 'PageUp': |
| 371 selected = this.selectItemPreviousPage(false); |
| 372 break; |
| 373 case 'PageDown': |
| 374 selected = this.selectItemNextPage(false); |
| 375 break; |
| 376 } |
| 377 if (selected) |
| 378 event.consume(); |
| 379 } |
| 380 |
| 381 /** |
| 319 * @return {number} | 382 * @return {number} |
| 320 */ | 383 */ |
| 321 _totalHeight() { | 384 _totalHeight() { |
| 322 return this._offsetAtIndex(this._items.length); | 385 return this._offsetAtIndex(this._items.length); |
| 323 } | 386 } |
| 324 | 387 |
| 325 /** | 388 /** |
| 326 * @param {number} offset | 389 * @param {number} offset |
| 327 * @return {number} | 390 * @return {number} |
| 328 */ | 391 */ |
| 329 _indexAtOffset(offset) { | 392 _indexAtOffset(offset) { |
| 330 if (this._mode === UI.ListMode.Grow) | 393 if (this._mode === UI.ListMode.NonViewport) |
| 331 throw 'There should be no offset conversions in grow mode'; | 394 throw 'There should be no offset conversions in non-viewport mode'; |
| 332 if (!this._items.length || offset < 0) | 395 if (!this._items.length || offset < 0) |
| 333 return 0; | 396 return 0; |
| 334 if (this._mode === UI.ListMode.ViewportVariableItems) { | 397 if (this._mode === UI.ListMode.VariousHeightItems) { |
| 335 return Math.min( | 398 return Math.min( |
| 336 this._items.length - 1, this._variableOffsets.lowerBound(offset, undef
ined, 0, this._items.length)); | 399 this._items.length - 1, this._variableOffsets.lowerBound(offset, undef
ined, 0, this._items.length)); |
| 337 } | 400 } |
| 338 if (!this._fixedHeight) | 401 if (!this._fixedHeight) |
| 339 this._measureHeight(); | 402 this._measureHeight(); |
| 340 return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeigh
t)); | 403 return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeigh
t)); |
| 341 } | 404 } |
| 342 | 405 |
| 343 /** | 406 /** |
| 344 * @param {number} index | 407 * @param {number} index |
| 345 * @return {!Element} | 408 * @return {!Element} |
| 346 */ | 409 */ |
| 347 _elementAtIndex(index) { | 410 _elementAtIndex(index) { |
| 348 var item = this._items[index]; | 411 var item = this._items[index]; |
| 349 var element = this._itemToElement.get(item); | 412 var element = this._itemToElement.get(item); |
| 350 if (!element) { | 413 if (!element) { |
| 351 element = this._delegate.createElementForItem(item); | 414 element = this._delegate.createElementForItem(item); |
| 352 this._itemToElement.set(item, element); | 415 this._itemToElement.set(item, element); |
| 353 } | 416 } |
| 354 return element; | 417 return element; |
| 355 } | 418 } |
| 356 | 419 |
| 357 /** | 420 /** |
| 358 * @param {number} index | 421 * @param {number} index |
| 359 * @return {number} | 422 * @return {number} |
| 360 */ | 423 */ |
| 361 _offsetAtIndex(index) { | 424 _offsetAtIndex(index) { |
| 362 if (this._mode === UI.ListMode.Grow) | 425 if (this._mode === UI.ListMode.NonViewport) |
| 363 throw 'There should be no offset conversions in grow mode'; | 426 throw 'There should be no offset conversions in non-viewport mode'; |
| 364 if (!this._items.length) | 427 if (!this._items.length) |
| 365 return 0; | 428 return 0; |
| 366 if (this._mode === UI.ListMode.ViewportVariableItems) | 429 if (this._mode === UI.ListMode.VariousHeightItems) |
| 367 return this._variableOffsets[index]; | 430 return this._variableOffsets[index]; |
| 368 if (!this._fixedHeight) | 431 if (!this._fixedHeight) |
| 369 this._measureHeight(); | 432 this._measureHeight(); |
| 370 return index * this._fixedHeight; | 433 return index * this._fixedHeight; |
| 371 } | 434 } |
| 372 | 435 |
| 373 _measureHeight() { | 436 _measureHeight() { |
| 374 if (this._mode === UI.ListMode.ViewportFixedItemsMeasured) | 437 this._fixedHeight = this._delegate.heightForItem(this._items[0]); |
| 438 if (!this._fixedHeight) |
| 375 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this.
element).height; | 439 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this.
element).height; |
| 376 else | |
| 377 this._fixedHeight = this._delegate.heightForItem(this._items[0]); | |
| 378 } | 440 } |
| 379 | 441 |
| 380 /** | 442 /** |
| 381 * @param {number} index | 443 * @param {number} index |
| 382 * @param {?T=} oldItem | 444 * @param {?T=} oldItem |
| 383 * @param {?Element=} oldElement | 445 * @param {?Element=} oldElement |
| 384 */ | 446 */ |
| 385 _select(index, oldItem, oldElement) { | 447 _select(index, oldItem, oldElement) { |
| 386 if (oldItem === undefined) | 448 if (oldItem === undefined) |
| 387 oldItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] :
null; | 449 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; | 514 this._variableOffsets = variableOffsets; |
| 453 } | 515 } |
| 454 } | 516 } |
| 455 | 517 |
| 456 /** | 518 /** |
| 457 * @param {number} from | 519 * @param {number} from |
| 458 * @param {number} to | 520 * @param {number} to |
| 459 * @param {number} inserted | 521 * @param {number} inserted |
| 460 */ | 522 */ |
| 461 _invalidate(from, to, inserted) { | 523 _invalidate(from, to, inserted) { |
| 462 if (this._mode === UI.ListMode.Grow) { | 524 if (this._mode === UI.ListMode.NonViewport) { |
| 463 this._invalidateGrowMode(from, to - from, inserted); | 525 this._invalidateNonViewportMode(from, to - from, inserted); |
| 464 return; | 526 return; |
| 465 } | 527 } |
| 466 | 528 |
| 467 if (this._mode === UI.ListMode.ViewportVariableItems) { | 529 if (this._mode === UI.ListMode.VariousHeightItems) { |
| 468 this._reallocateVariableOffsets(this._items.length + 1, from + 1); | 530 this._reallocateVariableOffsets(this._items.length + 1, from + 1); |
| 469 for (var i = from + 1; i <= this._items.length; i++) | 531 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]); | 532 this._variableOffsets[i] = this._variableOffsets[i - 1] + this._delegate
.heightForItem(this._items[i - 1]); |
| 471 } | 533 } |
| 472 | 534 |
| 473 var viewportHeight = this.element.offsetHeight; | 535 var viewportHeight = this.element.offsetHeight; |
| 474 var totalHeight = this._totalHeight(); | 536 var totalHeight = this._totalHeight(); |
| 475 var scrollTop = this.element.scrollTop; | 537 var scrollTop = this.element.scrollTop; |
| 476 | 538 |
| 477 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) { | 539 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) { |
| (...skipping 27 matching lines...) Expand all Loading... |
| 505 // when invalidating after firstIndex but before first visible element. | 567 // when invalidating after firstIndex but before first visible element. |
| 506 this._clearViewport(); | 568 this._clearViewport(); |
| 507 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewportHe
ight), viewportHeight); | 569 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewportHe
ight), viewportHeight); |
| 508 } | 570 } |
| 509 | 571 |
| 510 /** | 572 /** |
| 511 * @param {number} start | 573 * @param {number} start |
| 512 * @param {number} remove | 574 * @param {number} remove |
| 513 * @param {number} add | 575 * @param {number} add |
| 514 */ | 576 */ |
| 515 _invalidateGrowMode(start, remove, add) { | 577 _invalidateNonViewportMode(start, remove, add) { |
| 516 var startElement = this._topElement; | 578 var startElement = this._topElement; |
| 517 for (var index = 0; index < start; index++) | 579 for (var index = 0; index < start; index++) |
| 518 startElement = startElement.nextElementSibling; | 580 startElement = startElement.nextElementSibling; |
| 519 while (remove--) | 581 while (remove--) |
| 520 startElement.nextElementSibling.remove(); | 582 startElement.nextElementSibling.remove(); |
| 521 while (add--) | 583 while (add--) |
| 522 this.element.insertBefore(this._elementAtIndex(start + add), startElement.
nextElementSibling); | 584 this.element.insertBefore(this._elementAtIndex(start + add), startElement.
nextElementSibling); |
| 523 } | 585 } |
| 524 | 586 |
| 525 _clearViewport() { | 587 _clearViewport() { |
| 526 if (this._mode === UI.ListMode.Grow) | 588 if (this._mode === UI.ListMode.NonViewport) |
| 527 throw 'There should be no viewport updates in grow mode'; | 589 throw 'There should be no viewport updates in non-viewport mode'; |
| 528 this._firstIndex = 0; | 590 this._firstIndex = 0; |
| 529 this._lastIndex = 0; | 591 this._lastIndex = 0; |
| 530 this._renderedHeight = 0; | 592 this._renderedHeight = 0; |
| 531 this._topHeight = 0; | 593 this._topHeight = 0; |
| 532 this._bottomHeight = 0; | 594 this._bottomHeight = 0; |
| 533 this._clearContents(); | 595 this._clearContents(); |
| 534 } | 596 } |
| 535 | 597 |
| 536 _clearContents() { | 598 _clearContents() { |
| 537 // Note: this method should not force layout. Be careful. | 599 // Note: this method should not force layout. Be careful. |
| 538 this._topElement.style.height = '0'; | 600 this._topElement.style.height = '0'; |
| 539 this._bottomElement.style.height = '0'; | 601 this._bottomElement.style.height = '0'; |
| 540 this.element.removeChildren(); | 602 this.element.removeChildren(); |
| 541 this.element.appendChild(this._topElement); | 603 this.element.appendChild(this._topElement); |
| 542 this.element.appendChild(this._bottomElement); | 604 this.element.appendChild(this._bottomElement); |
| 543 } | 605 } |
| 544 | 606 |
| 545 /** | 607 /** |
| 546 * @param {number} scrollTop | 608 * @param {number} scrollTop |
| 547 * @param {number} viewportHeight | 609 * @param {number} viewportHeight |
| 548 */ | 610 */ |
| 549 _updateViewport(scrollTop, viewportHeight) { | 611 _updateViewport(scrollTop, viewportHeight) { |
| 550 // Note: this method should not force layout. Be careful. | 612 // Note: this method should not force layout. Be careful. |
| 551 if (this._mode === UI.ListMode.Grow) | 613 if (this._mode === UI.ListMode.NonViewport) |
| 552 throw 'There should be no viewport updates in grow mode'; | 614 throw 'There should be no viewport updates in non-viewport mode'; |
| 553 | 615 |
| 554 var totalHeight = this._totalHeight(); | 616 var totalHeight = this._totalHeight(); |
| 555 if (!totalHeight) { | 617 if (!totalHeight) { |
| 556 this._firstIndex = 0; | 618 this._firstIndex = 0; |
| 557 this._lastIndex = 0; | 619 this._lastIndex = 0; |
| 558 this._topHeight = 0; | 620 this._topHeight = 0; |
| 559 this._bottomHeight = 0; | 621 this._bottomHeight = 0; |
| 560 this._renderedHeight = 0; | 622 this._renderedHeight = 0; |
| 561 this._topElement.style.height = '0'; | 623 this._topElement.style.height = '0'; |
| 562 this._bottomElement.style.height = '0'; | 624 this._bottomElement.style.height = '0'; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 589 this._firstIndex = firstIndex; | 651 this._firstIndex = firstIndex; |
| 590 this._lastIndex = lastIndex; | 652 this._lastIndex = lastIndex; |
| 591 this._topHeight = this._offsetAtIndex(firstIndex); | 653 this._topHeight = this._offsetAtIndex(firstIndex); |
| 592 this._topElement.style.height = this._topHeight + 'px'; | 654 this._topElement.style.height = this._topHeight + 'px'; |
| 593 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex); | 655 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex); |
| 594 this._bottomElement.style.height = this._bottomHeight + 'px'; | 656 this._bottomElement.style.height = this._bottomHeight + 'px'; |
| 595 this._renderedHeight = totalHeight; | 657 this._renderedHeight = totalHeight; |
| 596 this.element.scrollTop = scrollTop; | 658 this.element.scrollTop = scrollTop; |
| 597 } | 659 } |
| 598 }; | 660 }; |
| OLD | NEW |