Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(149)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/ui/ListControl.js

Issue 2605253002: [DevTools] Add grow mode to ListControl. (Closed)
Patch Set: works Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 19 matching lines...) Expand all
30 /** 30 /**
31 * @param {?T} from 31 * @param {?T} from
32 * @param {?T} to 32 * @param {?T} to
33 * @param {?Element} fromElement 33 * @param {?Element} fromElement
34 * @param {?Element} toElement 34 * @param {?Element} toElement
35 */ 35 */
36 selectedItemChanged(from, to, fromElement, toElement) {}, 36 selectedItemChanged(from, to, fromElement, toElement) {},
37 }; 37 };
38 38
39 /** @enum {symbol} */ 39 /** @enum {symbol} */
40 UI.ListHeightMode = { 40 UI.ListMode = {
41 Fixed: Symbol('UI.ListHeightMode.Fixed'), 41 Grow: Symbol('UI.ListMode.Grow'),
42 Measured: Symbol('UI.ListHeightMode.Measured'), 42 ViewportFixedItems: Symbol('UI.ListMode.ViewportFixedItems'),
43 Variable: Symbol('UI.ListHeightMode.Variable') 43 ViewportFixedItemsMeasured: Symbol('UI.ListMode.ViewportFixedItemsMeasured'),
44 ViewportVariableItems: Symbol('UI.ListMode.ViewportVariableItems')
44 }; 45 };
45 46
46 /** 47 /**
47 * @template T 48 * @template T
48 */ 49 */
49 UI.ListControl = class { 50 UI.ListControl = class {
50 /** 51 /**
51 * @param {!UI.ListDelegate<T>} delegate 52 * @param {!UI.ListDelegate<T>} delegate
52 */ 53 */
53 constructor(delegate) { 54 constructor(delegate) {
54 this.element = createElement('div'); 55 this.element = createElement('div');
55 this.element.style.overflow = 'auto'; 56 this.element.style.overflow = 'auto';
56 this._topElement = this.element.createChild('div'); 57 this._topElement = this.element.createChild('div');
57 this._bottomElement = this.element.createChild('div'); 58 this._bottomElement = this.element.createChild('div');
58 this._firstIndex = 0; 59 this._firstIndex = 0;
59 this._lastIndex = 0; 60 this._lastIndex = 0;
60 this._renderedHeight = 0; 61 this._renderedHeight = 0;
61 this._topHeight = 0; 62 this._topHeight = 0;
62 this._bottomHeight = 0; 63 this._bottomHeight = 0;
63 this._clearViewport();
64 64
65 /** @type {!Array<T>} */ 65 /** @type {!Array<T>} */
66 this._items = []; 66 this._items = [];
67 /** @type {!Map<T, !Element>} */ 67 /** @type {!Map<T, !Element>} */
68 this._itemToElement = new Map(); 68 this._itemToElement = new Map();
69 this._selectedIndex = -1; 69 this._selectedIndex = -1;
70 70
71 this._boundKeyDown = event => { 71 this._boundKeyDown = event => {
72 if (this.onKeyDown(event)) 72 if (this.onKeyDown(event))
73 event.consume(true); 73 event.consume(true);
74 }; 74 };
75 this._boundClick = event => { 75 this._boundClick = event => {
76 if (this.onClick(event)) 76 if (this.onClick(event))
77 event.consume(true); 77 event.consume(true);
78 }; 78 };
79 this._boundScroll = event => {
80 this._updateViewport(this.element.scrollTop, this.element.offsetHeight);
81 };
79 82
80 this._delegate = delegate; 83 this._delegate = delegate;
81 this._heightMode = UI.ListHeightMode.Measured; 84 this._mode = UI.ListMode.ViewportFixedItemsMeasured;
82 this._fixedHeight = 0; 85 this._fixedHeight = 0;
83 this._variableOffsets = new Int32Array(0); 86 this._variableOffsets = new Int32Array(0);
84 87 this._clearViewport();
85 this.element.addEventListener('scroll', this._onScroll.bind(this), false); 88 this.element.addEventListener('scroll', this._boundScroll, false);
86 } 89 }
87 90
88 /** 91 /**
89 * @param {!UI.ListHeightMode} mode 92 * @param {!UI.ListMode} mode
90 */ 93 */
91 setHeightMode(mode) { 94 setMode(mode) {
caseq 2016/12/29 19:25:31 Should we even allow changing mode on the fly? Per
dgozman 2016/12/29 20:38:44 Done.
92 this._heightMode = mode; 95 if (mode === UI.ListMode.Grow) {
93 this._fixedHeight = 0; 96 if (this._mode !== UI.ListMode.Grow)
94 if (this._items.length) { 97 this.element.removeEventListener('scroll', this._boundScroll, false);
caseq 2016/12/29 19:25:31 do it unconditionally just in case?
95 this._itemToElement.clear(); 98 this._mode = mode;
caseq 2016/12/29 19:25:30 this would go before if then
96 this._invalidate(0, this._items.length, this._items.length); 99 if (this._items.length) {
100 this._itemToElement.clear();
101 this._clearContents();
102 this._invalidateGrowMode(0, 0, this._items.length);
103 }
104 } else {
105 if (this._mode === UI.ListMode.Grow)
106 this.element.addEventListener('scroll', this._boundScroll, false);
107 this._mode = mode;
108 this._fixedHeight = 0;
109 if (this._items.length) {
110 this._itemToElement.clear();
111 if (this._mode === UI.ListMode.Grow)
caseq 2016/12/29 19:25:30 this can't be true :-)
112 this._clearViewport();
113 this._invalidate(0, this._items.length, this._items.length);
114 }
97 } 115 }
98 } 116 }
99 117
100 /** 118 /**
101 * @param {boolean} handleInput 119 * @param {boolean} handleInput
102 */ 120 */
103 setHandleInput(handleInput) { 121 setHandleInput(handleInput) {
104 if (handleInput) { 122 if (handleInput) {
105 this.element.addEventListener('keydown', this._boundKeyDown, false); 123 this.element.addEventListener('keydown', this._boundKeyDown, false);
106 this.element.addEventListener('click', this._boundClick, false); 124 this.element.addEventListener('click', this._boundClick, false);
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
174 // Splice may fail with too many arguments. 192 // Splice may fail with too many arguments.
175 var before = this._items.slice(0, from); 193 var before = this._items.slice(0, from);
176 var after = this._items.slice(to); 194 var after = this._items.slice(to);
177 this._items = [].concat(before, items, after); 195 this._items = [].concat(before, items, after);
178 } 196 }
179 this._invalidate(from, to, items.length); 197 this._invalidate(from, to, items.length);
180 198
181 if (this._selectedIndex >= to) { 199 if (this._selectedIndex >= to) {
182 this._selectedIndex += items.length - (to - from); 200 this._selectedIndex += items.length - (to - from);
183 } else if (this._selectedIndex >= from) { 201 } else if (this._selectedIndex >= from) {
184 var index = this._findClosestSelectable(from + items.length, +1, 0, false) ; 202 var index = this._findFirstSelectable(from + items.length, +1, false);
185 if (index === -1) 203 if (index === -1)
186 index = this._findClosestSelectable(from - 1, -1, 0, false); 204 index = this._findFirstSelectable(from - 1, -1, false);
187 this._select(index, oldSelectedItem, oldSelectedElement); 205 this._select(index, oldSelectedItem, oldSelectedElement);
188 } 206 }
189 } 207 }
190 208
191 /** 209 /**
192 * @param {!Array<T>} items 210 * @param {!Array<T>} items
193 */ 211 */
194 replaceAllItems(items) { 212 replaceAllItems(items) {
195 this.replaceItemsInRange(0, this._items.length, items); 213 this.replaceItemsInRange(0, this._items.length, items);
196 } 214 }
197 215
198 /** 216 /**
199 * @param {number} from 217 * @param {number} from
200 * @param {number} to 218 * @param {number} to
201 */ 219 */
202 invalidateRange(from, to) { 220 invalidateRange(from, to) {
203 this._invalidate(from, to, to - from); 221 this._invalidate(from, to, to - from);
204 } 222 }
205 223
206 viewportResized() { 224 viewportResized() {
207 // TODO(dgozman): try to keep the visible scrollTop the same 225 if (this._mode === UI.ListMode.Grow)
208 // when invalidating after firstIndex but before first visible element. 226 return;
227 // TODO(dgozman): try to keep visible scrollTop the same.
209 var scrollTop = this.element.scrollTop; 228 var scrollTop = this.element.scrollTop;
210 var viewportHeight = this.element.offsetHeight; 229 var viewportHeight = this.element.offsetHeight;
211 this._clearViewport(); 230 this._clearViewport();
212 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi ewportHeight), viewportHeight); 231 this._updateViewport(Number.constrain(scrollTop, 0, this._totalHeight() - vi ewportHeight), viewportHeight);
213 } 232 }
214 233
215 /** 234 /**
216 * @param {number} index 235 * @param {number} index
217 */ 236 */
218 scrollItemAtIndexIntoView(index) { 237 scrollItemAtIndexIntoView(index) {
238 if (this._mode === UI.ListMode.Grow) {
239 this._elementAtIndex(index).scrollIntoViewIfNeeded(false);
240 return;
241 }
219 var top = this._offsetAtIndex(index); 242 var top = this._offsetAtIndex(index);
220 var bottom = this._offsetAtIndex(index + 1); 243 var bottom = this._offsetAtIndex(index + 1);
221 var scrollTop = this.element.scrollTop; 244 var scrollTop = this.element.scrollTop;
222 var viewportHeight = this.element.offsetHeight; 245 var viewportHeight = this.element.offsetHeight;
223 if (top < scrollTop) 246 if (top < scrollTop)
224 this._updateViewport(top, viewportHeight); 247 this._updateViewport(top, viewportHeight);
225 else if (bottom > scrollTop + viewportHeight) 248 else if (bottom > scrollTop + viewportHeight)
226 this._updateViewport(bottom - viewportHeight, viewportHeight); 249 this._updateViewport(bottom - viewportHeight, viewportHeight);
227 } 250 }
228 251
(...skipping 24 matching lines...) Expand all
253 } 276 }
254 277
255 /** 278 /**
256 * @param {!Event} event 279 * @param {!Event} event
257 * @return {boolean} 280 * @return {boolean}
258 */ 281 */
259 onKeyDown(event) { 282 onKeyDown(event) {
260 var index = -1; 283 var index = -1;
261 switch (event.key) { 284 switch (event.key) {
262 case 'ArrowUp': 285 case 'ArrowUp':
263 index = this._selectedIndex === -1 ? this._findClosestSelectable(this._i tems.length - 1, -1, 0, true) : 286 index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex - 1;
264 this._findClosestSelectable(this._s electedIndex, -1, 1, true); 287 index = this._findFirstSelectable(index, -1, true);
265 break; 288 break;
266 case 'ArrowDown': 289 case 'ArrowDown':
267 index = this._selectedIndex === -1 ? this._findClosestSelectable(0, +1, 0, true) : 290 index = this._selectedIndex === -1 ? 0 : this._selectedIndex + 1;
268 this._findClosestSelectable(this._s electedIndex, +1, 1, true); 291 index = this._findFirstSelectable(index, +1, true);
269 break; 292 break;
270 case 'PageUp': 293 case 'PageUp':
294 if (this._mode === UI.ListMode.Grow)
295 return false;
271 index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex; 296 index = this._selectedIndex === -1 ? this._items.length - 1 : this._sele ctedIndex;
272 // Compensate for zoom rounding errors with -1. 297 index = this._findPageSelectable(index, -1);
273 index = this._findClosestSelectable(index, -1, this.element.offsetHeight - 1, false);
274 break; 298 break;
275 case 'PageDown': 299 case 'PageDown':
300 if (this._mode === UI.ListMode.Grow)
301 return false;
276 index = this._selectedIndex === -1 ? 0 : this._selectedIndex; 302 index = this._selectedIndex === -1 ? 0 : this._selectedIndex;
277 // Compensate for zoom rounding errors with -1. 303 index = this._findPageSelectable(index, +1);
278 index = this._findClosestSelectable(index, +1, this.element.offsetHeight - 1, false);
279 break; 304 break;
280 default: 305 default:
281 return false; 306 return false;
282 } 307 }
283 if (index !== -1) { 308 if (index !== -1) {
284 this.scrollItemAtIndexIntoView(index); 309 this.scrollItemAtIndexIntoView(index);
285 this._select(index); 310 this._select(index);
286 return true; 311 return true;
287 } 312 }
288 return false; 313 return false;
289 } 314 }
290 315
291 /** 316 /**
292 * @param {!Event} event 317 * @param {!Event} event
293 * @return {boolean} 318 * @return {boolean}
294 */ 319 */
295 onClick(event) { 320 onClick(event) {
296 var node = event.target; 321 var node = event.target;
297 while (node && node.parentNodeOrShadowHost() !== this.element) 322 while (node && node.parentNodeOrShadowHost() !== this.element)
298 node = node.parentNodeOrShadowHost(); 323 node = node.parentNodeOrShadowHost();
299 if (!node || node.nodeType !== Node.ELEMENT_NODE) 324 if (!node || node.nodeType !== Node.ELEMENT_NODE)
300 return false; 325 return false;
301 var offset = /** @type {!Element} */ (node).getBoundingClientRect().top; 326 var index = -1;
302 offset -= this.element.getBoundingClientRect().top; 327 if (this._mode === UI.ListMode.Grow) {
303 var index = this._indexAtOffset(offset + this.element.scrollTop); 328 for (var i = 0; i < this._items.length; i++) {
caseq 2016/12/29 19:25:31 use findIndex()?
dgozman 2016/12/29 20:38:44 Done.
329 if (this._itemToElement.get(this._items[i]) === node) {
330 index = i;
331 break;
332 }
333 }
334 } else {
335 var offset = /** @type {!Element} */ (node).getBoundingClientRect().top;
336 offset -= this.element.getBoundingClientRect().top;
337 index = this._indexAtOffset(offset + this.element.scrollTop);
338 }
304 if (index === -1 || !this._delegate.isItemSelectable(this._items[index])) 339 if (index === -1 || !this._delegate.isItemSelectable(this._items[index]))
305 return false; 340 return false;
306 this._select(index); 341 this._select(index);
307 return true; 342 return true;
308 } 343 }
309 344
310 /** 345 /**
311 * @return {number} 346 * @return {number}
312 */ 347 */
313 _totalHeight() { 348 _totalHeight() {
314 return this._offsetAtIndex(this._items.length); 349 return this._offsetAtIndex(this._items.length);
315 } 350 }
316 351
317 /** 352 /**
318 * @param {number} offset 353 * @param {number} offset
319 * @return {number} 354 * @return {number}
320 */ 355 */
321 _indexAtOffset(offset) { 356 _indexAtOffset(offset) {
357 if (this._mode === UI.ListMode.Grow)
358 throw 'There should be no offset conversions in grow mode';
322 if (!this._items.length || offset < 0) 359 if (!this._items.length || offset < 0)
323 return 0; 360 return 0;
324 if (this._heightMode === UI.ListHeightMode.Variable) { 361 if (this._mode === UI.ListMode.ViewportVariableItems) {
325 return Math.min( 362 return Math.min(
326 this._items.length - 1, this._variableOffsets.lowerBound(offset, undef ined, 0, this._items.length)); 363 this._items.length - 1, this._variableOffsets.lowerBound(offset, undef ined, 0, this._items.length));
327 } 364 }
328 if (!this._fixedHeight) 365 if (!this._fixedHeight)
329 this._measureHeight(); 366 this._measureHeight();
330 return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeigh t)); 367 return Math.min(this._items.length - 1, Math.floor(offset / this._fixedHeigh t));
331 } 368 }
332 369
333 /** 370 /**
334 * @param {number} index 371 * @param {number} index
335 * @return {!Element} 372 * @return {!Element}
336 */ 373 */
337 _elementAtIndex(index) { 374 _elementAtIndex(index) {
338 var item = this._items[index]; 375 var item = this._items[index];
339 var element = this._itemToElement.get(item); 376 var element = this._itemToElement.get(item);
340 if (!element) { 377 if (!element) {
341 element = this._delegate.createElementForItem(item); 378 element = this._delegate.createElementForItem(item);
342 this._itemToElement.set(item, element); 379 this._itemToElement.set(item, element);
343 } 380 }
344 return element; 381 return element;
345 } 382 }
346 383
347 /** 384 /**
348 * @param {number} index 385 * @param {number} index
349 * @return {number} 386 * @return {number}
350 */ 387 */
351 _offsetAtIndex(index) { 388 _offsetAtIndex(index) {
389 if (this._mode === UI.ListMode.Grow)
390 throw 'There should be no offset conversions in grow mode';
352 if (!this._items.length) 391 if (!this._items.length)
353 return 0; 392 return 0;
354 if (this._heightMode === UI.ListHeightMode.Variable) 393 if (this._mode === UI.ListMode.ViewportVariableItems)
355 return this._variableOffsets[index]; 394 return this._variableOffsets[index];
356 if (!this._fixedHeight) 395 if (!this._fixedHeight)
357 this._measureHeight(); 396 this._measureHeight();
358 return index * this._fixedHeight; 397 return index * this._fixedHeight;
359 } 398 }
360 399
361 _measureHeight() { 400 _measureHeight() {
362 if (this._heightMode === UI.ListHeightMode.Measured) 401 if (this._mode === UI.ListMode.ViewportFixedItemsMeasured)
363 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this. element).height; 402 this._fixedHeight = UI.measurePreferredSize(this._elementAtIndex(0), this. element).height;
364 else 403 else
365 this._fixedHeight = this._delegate.heightForItem(this._items[0]); 404 this._fixedHeight = this._delegate.heightForItem(this._items[0]);
366 } 405 }
367 406
368 /** 407 /**
369 * @param {number} index 408 * @param {number} index
370 * @param {?T=} oldItem 409 * @param {?T=} oldItem
371 * @param {?Element=} oldElement 410 * @param {?Element=} oldElement
372 */ 411 */
373 _select(index, oldItem, oldElement) { 412 _select(index, oldItem, oldElement) {
374 if (oldItem === undefined) 413 if (oldItem === undefined)
375 oldItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; 414 oldItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null;
376 if (oldElement === undefined) 415 if (oldElement === undefined)
377 oldElement = this._itemToElement.get(oldItem) || null; 416 oldElement = this._itemToElement.get(oldItem) || null;
378 this._selectedIndex = index; 417 this._selectedIndex = index;
379 var newItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null; 418 var newItem = this._selectedIndex !== -1 ? this._items[this._selectedIndex] : null;
380 var newElement = this._itemToElement.get(newItem) || null; 419 var newElement = this._itemToElement.get(newItem) || null;
381 this._delegate.selectedItemChanged(oldItem, newItem, /** @type {?Element} */ (oldElement), newElement); 420 this._delegate.selectedItemChanged(oldItem, newItem, /** @type {?Element} */ (oldElement), newElement);
382 } 421 }
383 422
384 /** 423 /**
385 * @param {number} index 424 * @param {number} index
386 * @param {number} direction 425 * @param {number} direction
387 * @param {number} minSkippedHeight
388 * @param {boolean} canWrap 426 * @param {boolean} canWrap
389 * @return {number} 427 * @return {number}
390 */ 428 */
391 _findClosestSelectable(index, direction, minSkippedHeight, canWrap) { 429 _findFirstSelectable(index, direction, canWrap) {
392 var length = this._items.length; 430 var length = this._items.length;
393 if (!length) 431 if (!length)
394 return -1; 432 return -1;
395 433 for (var step = 0; step <= length; step++) {
396 var lastSelectable = -1;
397 var start = -1;
398 var startOffset = this._offsetAtIndex(index);
399 while (true) {
400 if (index < 0 || index >= length) { 434 if (index < 0 || index >= length) {
401 if (!canWrap) 435 if (!canWrap)
402 return lastSelectable; 436 return -1;
403 index = (index + length) % length; 437 index = (index + length) % length;
404 } 438 }
439 if (this._delegate.isItemSelectable(this._items[index]))
440 return index;
441 index += direction;
442 }
443 return -1;
444 }
405 445
406 // Handle full wrap-around. 446 /**
407 if (index === start) 447 * @param {number} index
408 return lastSelectable; 448 * @param {number} direction
409 if (start === -1) { 449 * @return {number}
410 start = index; 450 */
411 startOffset = this._offsetAtIndex(index); 451 _findPageSelectable(index, direction) {
412 } 452 var lastSelectable = -1;
413 453 var startOffset = this._offsetAtIndex(index);
454 // Compensate for zoom rounding errors with -1.
455 var viewportHeight = this.element.offsetHeight - 1;
456 while (index >= 0 && index < this._items.length) {
414 if (this._delegate.isItemSelectable(this._items[index])) { 457 if (this._delegate.isItemSelectable(this._items[index])) {
415 if (Math.abs(this._offsetAtIndex(index) - startOffset) >= minSkippedHeig ht) 458 if (Math.abs(this._offsetAtIndex(index) - startOffset) >= viewportHeight )
416 return index; 459 return index;
417 lastSelectable = index; 460 lastSelectable = index;
418 } 461 }
419
420 index += direction; 462 index += direction;
421 } 463 }
464 return lastSelectable;
422 } 465 }
423 466
424 /** 467 /**
425 * @param {number} length 468 * @param {number} length
426 * @param {number} copyTo 469 * @param {number} copyTo
427 */ 470 */
428 _reallocateVariableOffsets(length, copyTo) { 471 _reallocateVariableOffsets(length, copyTo) {
429 if (this._variableOffsets.length < length) { 472 if (this._variableOffsets.length < length) {
430 var variableOffsets = new Int32Array(Math.max(length, this._variableOffset s.length * 2)); 473 var variableOffsets = new Int32Array(Math.max(length, this._variableOffset s.length * 2));
431 variableOffsets.set(this._variableOffsets.slice(0, copyTo), 0); 474 variableOffsets.set(this._variableOffsets.slice(0, copyTo), 0);
432 this._variableOffsets = variableOffsets; 475 this._variableOffsets = variableOffsets;
433 } else if (this._variableOffsets.length >= 2 * length) { 476 } else if (this._variableOffsets.length >= 2 * length) {
434 var variableOffsets = new Int32Array(length); 477 var variableOffsets = new Int32Array(length);
435 variableOffsets.set(this._variableOffsets.slice(0, copyTo), 0); 478 variableOffsets.set(this._variableOffsets.slice(0, copyTo), 0);
436 this._variableOffsets = variableOffsets; 479 this._variableOffsets = variableOffsets;
437 } 480 }
438 } 481 }
439 482
440 /** 483 /**
441 * @param {number} from 484 * @param {number} from
442 * @param {number} to 485 * @param {number} to
443 * @param {number} inserted 486 * @param {number} inserted
444 */ 487 */
445 _invalidate(from, to, inserted) { 488 _invalidate(from, to, inserted) {
446 if (this._heightMode === UI.ListHeightMode.Variable) { 489 if (this._mode === UI.ListMode.ViewportVariableItems) {
447 this._reallocateVariableOffsets(this._items.length + 1, from + 1); 490 this._reallocateVariableOffsets(this._items.length + 1, from + 1);
448 for (var i = from + 1; i <= this._items.length; i++) 491 for (var i = from + 1; i <= this._items.length; i++)
449 this._variableOffsets[i] = this._variableOffsets[i - 1] + this._delegate .heightForItem(this._items[i - 1]); 492 this._variableOffsets[i] = this._variableOffsets[i - 1] + this._delegate .heightForItem(this._items[i - 1]);
450 } 493 }
451 494
495 if (this._mode === UI.ListMode.Grow) {
caseq 2016/12/29 19:25:31 nit: move up
dgozman 2016/12/29 20:38:44 Done.
496 this._invalidateGrowMode(from, to - from, inserted);
497 return;
498 }
499
452 var viewportHeight = this.element.offsetHeight; 500 var viewportHeight = this.element.offsetHeight;
453 var totalHeight = this._totalHeight(); 501 var totalHeight = this._totalHeight();
454 var scrollTop = this.element.scrollTop; 502 var scrollTop = this.element.scrollTop;
455 503
456 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) { 504 if (this._renderedHeight < viewportHeight || totalHeight < viewportHeight) {
457 this._clearViewport(); 505 this._clearViewport();
458 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewport Height), viewportHeight); 506 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewport Height), viewportHeight);
459 return; 507 return;
460 } 508 }
461 509
(...skipping 11 matching lines...) Expand all
473 } 521 }
474 522
475 if (from >= this._lastIndex) { 523 if (from >= this._lastIndex) {
476 var bottomHeight = this._bottomHeight + heightDelta; 524 var bottomHeight = this._bottomHeight + heightDelta;
477 this._bottomElement.style.height = bottomHeight + 'px'; 525 this._bottomElement.style.height = bottomHeight + 'px';
478 this._bottomHeight = bottomHeight; 526 this._bottomHeight = bottomHeight;
479 this._renderedHeight = totalHeight; 527 this._renderedHeight = totalHeight;
480 return; 528 return;
481 } 529 }
482 530
483 // TODO(dgozman): try to keep the visible scrollTop the same 531 // TODO(dgozman): try to keep visible scrollTop the same
484 // when invalidating after firstIndex but before first visible element. 532 // when invalidating after firstIndex but before first visible element.
485 this._clearViewport(); 533 this._clearViewport();
486 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewportHe ight), viewportHeight); 534 this._updateViewport(Number.constrain(scrollTop, 0, totalHeight - viewportHe ight), viewportHeight);
487 } 535 }
488 536
537 /**
538 * @param {number} start
539 * @param {number} remove
540 * @param {number} add
541 */
542 _invalidateGrowMode(start, remove, add) {
543 var startElement = this._topElement;
544 for (var index = 0; index < start; index++)
545 startElement = startElement.nextSibling;
caseq 2016/12/29 19:25:31 nextElementSibling
dgozman 2016/12/29 20:38:44 Done.
546 for (var index = 0; index < remove; index++)
caseq 2016/12/29 19:25:30 nit: while (remove--)
dgozman 2016/12/29 20:38:44 Done.
547 startElement.nextSibling.remove();
caseq 2016/12/29 19:25:31 ditto.
548 for (var index = add - 1; index >= 0; index--) {
549 var element = this._elementAtIndex(start + index);
550 this.element.insertBefore(element, startElement.nextSibling);
551 }
552 }
553
489 _clearViewport() { 554 _clearViewport() {
555 if (this._mode === UI.ListMode.Grow)
556 throw 'There should be no viewport updates in grow mode';
490 this._firstIndex = 0; 557 this._firstIndex = 0;
491 this._lastIndex = 0; 558 this._lastIndex = 0;
492 this._renderedHeight = 0; 559 this._renderedHeight = 0;
493 this._topHeight = 0; 560 this._topHeight = 0;
494 this._bottomHeight = 0; 561 this._bottomHeight = 0;
562 this._clearContents();
563 }
564
565 _clearContents() {
566 // Note: this method should not force layout. Be careful.
495 this._topElement.style.height = '0'; 567 this._topElement.style.height = '0';
496 this._bottomElement.style.height = '0'; 568 this._bottomElement.style.height = '0';
497 this.element.removeChildren(); 569 this.element.removeChildren();
498 this.element.appendChild(this._topElement); 570 this.element.appendChild(this._topElement);
499 this.element.appendChild(this._bottomElement); 571 this.element.appendChild(this._bottomElement);
500 } 572 }
501 573
502 _onScroll() {
503 this._updateViewport(this.element.scrollTop, this.element.offsetHeight);
504 }
505
506 /** 574 /**
507 * @param {number} scrollTop 575 * @param {number} scrollTop
508 * @param {number} viewportHeight 576 * @param {number} viewportHeight
509 */ 577 */
510 _updateViewport(scrollTop, viewportHeight) { 578 _updateViewport(scrollTop, viewportHeight) {
511 // Note: this method should not force layout. Be careful. 579 // Note: this method should not force layout. Be careful.
580 if (this._mode === UI.ListMode.Grow)
581 throw 'There should be no viewport updates in grow mode';
512 582
513 var totalHeight = this._totalHeight(); 583 var totalHeight = this._totalHeight();
514 if (!totalHeight) { 584 if (!totalHeight) {
515 this._firstIndex = 0; 585 this._firstIndex = 0;
516 this._lastIndex = 0; 586 this._lastIndex = 0;
517 this._topHeight = 0; 587 this._topHeight = 0;
518 this._bottomHeight = 0; 588 this._bottomHeight = 0;
519 this._renderedHeight = 0; 589 this._renderedHeight = 0;
520 this._topElement.style.height = '0'; 590 this._topElement.style.height = '0';
521 this._bottomElement.style.height = '0'; 591 this._bottomElement.style.height = '0';
(...skipping 26 matching lines...) Expand all
548 this._firstIndex = firstIndex; 618 this._firstIndex = firstIndex;
549 this._lastIndex = lastIndex; 619 this._lastIndex = lastIndex;
550 this._topHeight = this._offsetAtIndex(firstIndex); 620 this._topHeight = this._offsetAtIndex(firstIndex);
551 this._topElement.style.height = this._topHeight + 'px'; 621 this._topElement.style.height = this._topHeight + 'px';
552 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex); 622 this._bottomHeight = totalHeight - this._offsetAtIndex(lastIndex);
553 this._bottomElement.style.height = this._bottomHeight + 'px'; 623 this._bottomElement.style.height = this._bottomHeight + 'px';
554 this._renderedHeight = totalHeight; 624 this._renderedHeight = totalHeight;
555 this.element.scrollTop = scrollTop; 625 this.element.scrollTop = scrollTop;
556 } 626 }
557 }; 627 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698