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

Side by Side Diff: lib/src/iron-list/iron-list.html

Issue 1418513006: update elements and fix some bugs (Closed) Base URL: git@github.com:dart-lang/polymer_elements.git@master
Patch Set: code review updates Created 5 years, 1 month 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 <!-- 1 <!--
2 @license 2 @license
3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. 3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt 4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt
5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt 6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt
7 Code distributed by Google as part of the polymer project is also 7 Code distributed by Google as part of the polymer project is also
8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt 8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt
9 --> 9 -->
10 10
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
62 <iron-ajax url="data.json" last-response="{{data}}" auto></iron-ajax> 62 <iron-ajax url="data.json" last-response="{{data}}" auto></iron-ajax>
63 <iron-list items="[[data]]" as="item"> 63 <iron-list items="[[data]]" as="item">
64 <template> 64 <template>
65 <div> 65 <div>
66 Name: <span>[[item.name]]</span> 66 Name: <span>[[item.name]]</span>
67 </div> 67 </div>
68 </template> 68 </template>
69 </iron-list> 69 </iron-list>
70 </template> 70 </template>
71 71
72 ### Styling
73
74 Use the `--iron-list-items-container` mixin to style the container of items, e.g .
75
76 iron-list {
77 --iron-list-items-container: {
78 margin: auto;
79 };
80 }
81
72 ### Resizing 82 ### Resizing
73 83
74 `iron-list` lays out the items when it recives a notification via the `resize` e vent. 84 `iron-list` lays out the items when it recives a notification via the `iron-resi ze` event.
75 This event is fired by any element that implements `IronResizableBehavior`. 85 This event is fired by any element that implements `IronResizableBehavior`.
76 86
77 By default, elements such as `iron-pages`, `paper-tabs` or `paper-dialog` will t rigger 87 By default, elements such as `iron-pages`, `paper-tabs` or `paper-dialog` will t rigger
78 this event automatically. If you hide the list manually (e.g. you use `display: none`) 88 this event automatically. If you hide the list manually (e.g. you use `display: none`)
79 you might want to implement `IronResizableBehavior` or fire this event manually right 89 you might want to implement `IronResizableBehavior` or fire this event manually right
80 after the list became visible again. e.g. 90 after the list became visible again. e.g.
81 91
82 document.querySelector('iron-list').fire('resize'); 92 document.querySelector('iron-list').fire('iron-resize');
83 93
84 94
85 @group Iron Element 95 @group Iron Element
86 @element iron-list 96 @element iron-list
87 @demo demo/index.html 97 @demo demo/index.html Simple list
98 @demo demo/selection.html Selection of items
99 @demo demo/collapse.html Collapsable items
88 --> 100 -->
89 101
90 <dom-module id="iron-list"> 102 <dom-module id="iron-list">
91 <style> 103 <template>
104 <style>
105 :host {
106 display: block;
107 }
92 108
93 :host { 109 :host(.has-scroller) {
94 display: block; 110 overflow: auto;
95 } 111 }
96 112
97 :host(.has-scroller) { 113 :host(:not(.has-scroller)) {
98 overflow: auto; 114 position: relative;
99 } 115 }
100 116
101 :host(:not(.has-scroller)) { 117 #items {
102 position: relative; 118 @apply(--iron-list-items-container);
103 } 119 position: relative;
120 }
104 121
105 #items { 122 #items > ::content > * {
106 position: relative; 123 width: 100%;
107 } 124 box-sizing: border-box;
108 125 position: absolute;
109 #items > ::content > * { 126 top: 0;
110 width: 100%; 127 will-change: transform;
111 box-sizing: border-box; 128 }
112 position: absolute; 129 </style>
113 top: 0;
114 will-change: transform;
115 }
116
117 </style>
118 <template>
119 130
120 <array-selector id="selector" items="{{items}}" 131 <array-selector id="selector" items="{{items}}"
121 selected="{{selectedItems}}" selected-item="{{selectedItem}}"> 132 selected="{{selectedItems}}" selected-item="{{selectedItem}}">
122 </array-selector> 133 </array-selector>
123 134
124 <div id="items"> 135 <div id="items">
125 <content></content> 136 <content></content>
126 </div> 137 </div>
127 138
128 </template> 139 </template>
129 </dom-module> 140 </dom-module>
130 141
131 <script> 142 <script>
(...skipping 23 matching lines...) Expand all
155 * The name of the variable to add to the binding scope for the array 166 * The name of the variable to add to the binding scope for the array
156 * element associated with a given template instance. 167 * element associated with a given template instance.
157 */ 168 */
158 as: { 169 as: {
159 type: String, 170 type: String,
160 value: 'item' 171 value: 'item'
161 }, 172 },
162 173
163 /** 174 /**
164 * The name of the variable to add to the binding scope with the index 175 * The name of the variable to add to the binding scope with the index
165 * for the row. If `sort` is provided, the index will reflect the 176 * for the row.
166 * sorted order (rather than the original array order).
167 */ 177 */
168 indexAs: { 178 indexAs: {
169 type: String, 179 type: String,
170 value: 'index' 180 value: 'index'
171 }, 181 },
172 182
173 /** 183 /**
174 * The name of the variable to add to the binding scope to indicate 184 * The name of the variable to add to the binding scope to indicate
175 * if the row is selected. 185 * if the row is selected.
176 */ 186 */
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
235 }, 245 },
236 246
237 /** 247 /**
238 * The ratio of hidden tiles that should remain in the scroll direction. 248 * The ratio of hidden tiles that should remain in the scroll direction.
239 * Recommended value ~0.5, so it will distribute tiles evely in both directi ons. 249 * Recommended value ~0.5, so it will distribute tiles evely in both directi ons.
240 */ 250 */
241 _ratio: 0.5, 251 _ratio: 0.5,
242 252
243 /** 253 /**
244 * The element that controls the scroll 254 * The element that controls the scroll
255 * @type {?Element}
245 */ 256 */
246 _scroller: null, 257 _scroller: null,
247 258
248 /** 259 /**
249 * The padding-top value of the `scroller` element 260 * The padding-top value of the `scroller` element
250 */ 261 */
251 _scrollerPaddingTop: 0, 262 _scrollerPaddingTop: 0,
252 263
253 /** 264 /**
254 * This value is the same as `scrollTop`. 265 * This value is the same as `scrollTop`.
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 */ 327 */
317 _scrollHeight: 0, 328 _scrollHeight: 0,
318 329
319 /** 330 /**
320 * The size of the viewport 331 * The size of the viewport
321 */ 332 */
322 _viewportSize: 0, 333 _viewportSize: 0,
323 334
324 /** 335 /**
325 * An array of DOM nodes that are currently in the tree 336 * An array of DOM nodes that are currently in the tree
337 * @type {?Array<!TemplatizerNode>}
326 */ 338 */
327 _physicalItems: null, 339 _physicalItems: null,
328 340
329 /** 341 /**
330 * An array of heights for each item in `_physicalItems` 342 * An array of heights for each item in `_physicalItems`
343 * @type {?Array<number>}
331 */ 344 */
332 _physicalSizes: null, 345 _physicalSizes: null,
333 346
334 /** 347 /**
335 * A cached value for the visible index. 348 * A cached value for the visible index.
336 * See `firstVisibleIndex` 349 * See `firstVisibleIndex`
350 * @type {?number}
337 */ 351 */
338 _firstVisibleIndexVal: null, 352 _firstVisibleIndexVal: null,
339 353
340 /** 354 /**
341 * A Polymer collection for the items. 355 * A Polymer collection for the items.
356 * @type {?Polymer.Collection}
342 */ 357 */
343 _collection: null, 358 _collection: null,
344 359
345 /** 360 /**
346 * True if the current item list was rendered for the first time 361 * True if the current item list was rendered for the first time
347 * after attached. 362 * after attached.
348 */ 363 */
349 _itemsRendered: false, 364 _itemsRendered: false,
350 365
351 /** 366 /**
352 * The bottom of the physical content. 367 * The bottom of the physical content.
353 */ 368 */
354 get _physicalBottom() { 369 get _physicalBottom() {
355 return this._physicalTop + this._physicalSize; 370 return this._physicalTop + this._physicalSize;
356 }, 371 },
357 372
358 /** 373 /**
374 * The bottom of the scroll.
375 */
376 get _scrollBottom() {
377 return this._scrollPosition + this._viewportSize;
378 },
379
380 /**
359 * The n-th item rendered in the last physical item. 381 * The n-th item rendered in the last physical item.
360 */ 382 */
361 get _virtualEnd() { 383 get _virtualEnd() {
362 return this._virtualStartVal + this._physicalCount - 1; 384 return this._virtualStartVal + this._physicalCount - 1;
363 }, 385 },
364 386
365 /** 387 /**
366 * The lowest n-th value for an item such that it can be rendered in `_physi calStart`. 388 * The lowest n-th value for an item such that it can be rendered in `_physi calStart`.
367 */ 389 */
368 _minVirtualStart: 0, 390 _minVirtualStart: 0,
369 391
370 /** 392 /**
371 * The largest n-th value for an item such that it can be rendered in `_phys icalStart`. 393 * The largest n-th value for an item such that it can be rendered in `_phys icalStart`.
372 */ 394 */
373 get _maxVirtualStart() { 395 get _maxVirtualStart() {
374 return this._virtualCount < this._physicalCount ? 396 return Math.max(0, this._virtualCount - this._physicalCount);
375 this._virtualCount : this._virtualCount - this._physicalCount;
376 }, 397 },
377 398
378 /** 399 /**
379 * The height of the physical content that isn't on the screen. 400 * The height of the physical content that isn't on the screen.
380 */ 401 */
381 get _hiddenContentSize() { 402 get _hiddenContentSize() {
382 return this._physicalSize - this._viewportSize; 403 return this._physicalSize - this._viewportSize;
383 }, 404 },
384 405
385 /** 406 /**
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
418 }, 439 },
419 440
420 /** 441 /**
421 * True if the current list is visible. 442 * True if the current list is visible.
422 */ 443 */
423 get _isVisible() { 444 get _isVisible() {
424 return this._scroller && Boolean(this._scroller.offsetWidth || this._scrol ler.offsetHeight); 445 return this._scroller && Boolean(this._scroller.offsetWidth || this._scrol ler.offsetHeight);
425 }, 446 },
426 447
427 /** 448 /**
428 * Gets the first visible item in the viewport. 449 * Gets the index of the first visible item in the viewport.
429 * 450 *
430 * @property firstVisibleIndex 451 * @type {number}
431 */ 452 */
432 get firstVisibleIndex() { 453 get firstVisibleIndex() {
433 var physicalOffset; 454 var physicalOffset;
434 455
435 if (this._firstVisibleIndexVal === null) { 456 if (this._firstVisibleIndexVal === null) {
436 physicalOffset = this._physicalTop; 457 physicalOffset = this._physicalTop;
437 458
438 this._firstVisibleIndexVal = this._iterateItems( 459 this._firstVisibleIndexVal = this._iterateItems(
439 function(pidx, vidx) { 460 function(pidx, vidx) {
440 physicalOffset += this._physicalSizes[pidx]; 461 physicalOffset += this._physicalSizes[pidx];
(...skipping 18 matching lines...) Expand all
459 }, 480 },
460 481
461 /** 482 /**
462 * When the element has been attached to the DOM tree. 483 * When the element has been attached to the DOM tree.
463 */ 484 */
464 attached: function() { 485 attached: function() {
465 // delegate to the parent's scroller 486 // delegate to the parent's scroller
466 // e.g. paper-scroll-header-panel 487 // e.g. paper-scroll-header-panel
467 var el = Polymer.dom(this); 488 var el = Polymer.dom(this);
468 489
469 if (el.parentNode && el.parentNode.scroller) { 490 var parentNode = /** @type {?{scroller: ?Element}} */ (el.parentNode);
470 this._scroller = el.parentNode.scroller; 491 if (parentNode && parentNode.scroller) {
492 this._scroller = parentNode.scroller;
471 } else { 493 } else {
472 this._scroller = this; 494 this._scroller = this;
473 this.classList.add('has-scroller'); 495 this.classList.add('has-scroller');
474 } 496 }
475 497
476 if (IOS_TOUCH_SCROLLING) { 498 if (IOS_TOUCH_SCROLLING) {
477 this._scroller.style.webkitOverflowScrolling = 'touch'; 499 this._scroller.style.webkitOverflowScrolling = 'touch';
478 } 500 }
479 501
480 this._scroller.addEventListener('scroll', this._scrollListener); 502 this._scroller.addEventListener('scroll', this._scrollListener);
(...skipping 13 matching lines...) Expand all
494 }, 516 },
495 517
496 /** 518 /**
497 * Invoke this method if you dynamically update the viewport's 519 * Invoke this method if you dynamically update the viewport's
498 * size or CSS padding. 520 * size or CSS padding.
499 * 521 *
500 * @method updateViewportBoundaries 522 * @method updateViewportBoundaries
501 */ 523 */
502 updateViewportBoundaries: function() { 524 updateViewportBoundaries: function() {
503 var scrollerStyle = window.getComputedStyle(this._scroller); 525 var scrollerStyle = window.getComputedStyle(this._scroller);
504 this._scrollerPaddingTop = parseInt(scrollerStyle['padding-top']); 526 this._scrollerPaddingTop = parseInt(scrollerStyle['padding-top'], 10);
505 this._viewportSize = this._scroller.offsetHeight; 527 this._viewportSize = this._scroller.offsetHeight;
506 }, 528 },
507 529
508 /** 530 /**
509 * Update the models, the position of the 531 * Update the models, the position of the
510 * items in the viewport and recycle tiles as needed. 532 * items in the viewport and recycle tiles as needed.
511 */ 533 */
512 _refresh: function() { 534 _refresh: function() {
513 var SCROLL_DIRECTION_UP = -1;
514 var SCROLL_DIRECTION_DOWN = 1;
515 var SCROLL_DIRECTION_NONE = 0;
516
517 // clamp the `scrollTop` value 535 // clamp the `scrollTop` value
518 // IE 10|11 scrollTop may go above `_maxScrollTop` 536 // IE 10|11 scrollTop may go above `_maxScrollTop`
519 // iOS `scrollTop` may go below 0 and above `_maxScrollTop` 537 // iOS `scrollTop` may go below 0 and above `_maxScrollTop`
520 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scroller.sc rollTop)); 538 var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scroller.sc rollTop));
521 539 var tileHeight, tileTop, kth, recycledTileSet, scrollBottom;
522 var tileHeight, kth, recycledTileSet;
523 var ratio = this._ratio; 540 var ratio = this._ratio;
524 var delta = scrollTop - this._scrollPosition; 541 var delta = scrollTop - this._scrollPosition;
525 var direction = SCROLL_DIRECTION_NONE;
526 var recycledTiles = 0; 542 var recycledTiles = 0;
527 var hiddenContentSize = this._hiddenContentSize; 543 var hiddenContentSize = this._hiddenContentSize;
528 var currentRatio = ratio; 544 var currentRatio = ratio;
529 var movingUp = []; 545 var movingUp = [];
530 546
531 // track the last `scrollTop` 547 // track the last `scrollTop`
532 this._scrollPosition = scrollTop; 548 this._scrollPosition = scrollTop;
533 549
534 // clear cached visible index 550 // clear cached visible index
535 this._firstVisibleIndexVal = null; 551 this._firstVisibleIndexVal = null;
536 552
553 scrollBottom = this._scrollBottom;
554
537 // random access 555 // random access
538 if (Math.abs(delta) > this._physicalSize) { 556 if (Math.abs(delta) > this._physicalSize) {
539 this._physicalTop += delta; 557 this._physicalTop += delta;
540 direction = SCROLL_DIRECTION_NONE;
541 recycledTiles = Math.round(delta / this._physicalAverage); 558 recycledTiles = Math.round(delta / this._physicalAverage);
542 } 559 }
543 // scroll up 560 // scroll up
544 else if (delta < 0) { 561 else if (delta < 0) {
545 var topSpace = scrollTop - this._physicalTop; 562 var topSpace = scrollTop - this._physicalTop;
546 var virtualStart = this._virtualStart; 563 var virtualStart = this._virtualStart;
564 var physicalBottom = this._physicalBottom;
547 565
548 direction = SCROLL_DIRECTION_UP;
549 recycledTileSet = []; 566 recycledTileSet = [];
550 567
551 kth = this._physicalEnd; 568 kth = this._physicalEnd;
552 currentRatio = topSpace / hiddenContentSize; 569 currentRatio = topSpace / hiddenContentSize;
553 570
554 // move tiles from bottom to top 571 // move tiles from bottom to top
555 while ( 572 while (
556 // approximate `currentRatio` to `ratio` 573 // approximate `currentRatio` to `ratio`
557 currentRatio < ratio && 574 currentRatio < ratio &&
558 // recycle less physical items than the total 575 // recycle less physical items than the total
559 recycledTiles < this._physicalCount && 576 recycledTiles < this._physicalCount &&
560 // ensure that these recycled tiles are needed 577 // ensure that these recycled tiles are needed
561 virtualStart - recycledTiles > 0 578 virtualStart - recycledTiles > 0 &&
579 // ensure that the tile is not visible
580 physicalBottom - this._physicalSizes[kth] > scrollBottom
562 ) { 581 ) {
563 582
564 tileHeight = this._physicalSizes[kth] || this._physicalAverage; 583 tileHeight = this._physicalSizes[kth];
565 currentRatio += tileHeight / hiddenContentSize; 584 currentRatio += tileHeight / hiddenContentSize;
566 585 physicalBottom -= tileHeight;
567 recycledTileSet.push(kth); 586 recycledTileSet.push(kth);
568 recycledTiles++; 587 recycledTiles++;
569 kth = (kth === 0) ? this._physicalCount - 1 : kth - 1; 588 kth = (kth === 0) ? this._physicalCount - 1 : kth - 1;
570 } 589 }
571 590
572 movingUp = recycledTileSet; 591 movingUp = recycledTileSet;
573 recycledTiles = -recycledTiles; 592 recycledTiles = -recycledTiles;
574
575 } 593 }
576 // scroll down 594 // scroll down
577 else if (delta > 0) { 595 else if (delta > 0) {
578 var bottomSpace = this._physicalBottom - (scrollTop + this._viewportSize ); 596 var bottomSpace = this._physicalBottom - scrollBottom;
579 var virtualEnd = this._virtualEnd; 597 var virtualEnd = this._virtualEnd;
580 var lastVirtualItemIndex = this._virtualCount-1; 598 var lastVirtualItemIndex = this._virtualCount-1;
581 599
582 direction = SCROLL_DIRECTION_DOWN;
583 recycledTileSet = []; 600 recycledTileSet = [];
584 601
585 kth = this._physicalStart; 602 kth = this._physicalStart;
586 currentRatio = bottomSpace / hiddenContentSize; 603 currentRatio = bottomSpace / hiddenContentSize;
587 604
588 // move tiles from top to bottom 605 // move tiles from top to bottom
589 while ( 606 while (
590 // approximate `currentRatio` to `ratio` 607 // approximate `currentRatio` to `ratio`
591 currentRatio < ratio && 608 currentRatio < ratio &&
592 // recycle less physical items than the total 609 // recycle less physical items than the total
593 recycledTiles < this._physicalCount && 610 recycledTiles < this._physicalCount &&
594 // ensure that these recycled tiles are needed 611 // ensure that these recycled tiles are needed
595 virtualEnd + recycledTiles < lastVirtualItemIndex 612 virtualEnd + recycledTiles < lastVirtualItemIndex &&
613 // ensure that the tile is not visible
614 this._physicalTop + this._physicalSizes[kth] < scrollTop
596 ) { 615 ) {
597 616
598 tileHeight = this._physicalSizes[kth] || this._physicalAverage; 617 tileHeight = this._physicalSizes[kth];
599 currentRatio += tileHeight / hiddenContentSize; 618 currentRatio += tileHeight / hiddenContentSize;
600 619
601 this._physicalTop += tileHeight; 620 this._physicalTop += tileHeight;
602 recycledTileSet.push(kth); 621 recycledTileSet.push(kth);
603 recycledTiles++; 622 recycledTiles++;
604 kth = (kth + 1) % this._physicalCount; 623 kth = (kth + 1) % this._physicalCount;
605 } 624 }
606 } 625 }
607 626
608 if (recycledTiles !== 0) { 627 if (recycledTiles === 0) {
628 // If the list ever reach this case, the physical average is not signifi cant enough
629 // to create all the items needed to cover the entire viewport.
630 // e.g. A few items have a height that differs from the average by serve ral order of magnitude.
631 if (this._increasePoolIfNeeded()) {
632 // yield and set models to the new items
633 this.async(this._update);
634 }
635 } else {
609 this._virtualStart = this._virtualStart + recycledTiles; 636 this._virtualStart = this._virtualStart + recycledTiles;
610 this._update(recycledTileSet, movingUp); 637 this._update(recycledTileSet, movingUp);
611 } 638 }
612 }, 639 },
613 640
614 /** 641 /**
615 * Update the list of items, starting from the `_virtualStartVal` item. 642 * Update the list of items, starting from the `_virtualStartVal` item.
643 * @param {!Array<number>=} itemSet
644 * @param {!Array<number>=} movingUp
616 */ 645 */
617 _update: function(itemSet, movingUp) { 646 _update: function(itemSet, movingUp) {
618 // update models 647 // update models
619 this._assignModels(itemSet); 648 this._assignModels(itemSet);
620 649
621 // measure heights 650 // measure heights
622 // TODO(blasten) pass `recycledTileSet` 651 this._updateMetrics(itemSet);
623 this._updateMetrics();
624 652
625 // adjust offset after measuring 653 // adjust offset after measuring
626 if (movingUp) { 654 if (movingUp) {
627 while (movingUp.length) { 655 while (movingUp.length) {
628 this._physicalTop -= this._physicalSizes[movingUp.pop()]; 656 this._physicalTop -= this._physicalSizes[movingUp.pop()];
629 } 657 }
630 } 658 }
631
632 // update the position of the items 659 // update the position of the items
633 this._positionItems(); 660 this._positionItems();
634 661
635 // set the scroller size 662 // set the scroller size
636 this._updateScrollerSize(); 663 this._updateScrollerSize();
637 664
638 // increase the pool of physical items if needed 665 // increase the pool of physical items if needed
639 if (itemSet = this._increasePoolIfNeeded()) { 666 if (this._increasePoolIfNeeded()) {
640 // set models to the new items 667 // yield set models to the new items
641 this.async(this._update.bind(this, itemSet)); 668 this.async(this._update);
642 } 669 }
643 }, 670 },
644 671
645 /** 672 /**
646 * Creates a pool of DOM elements and attaches them to the local dom. 673 * Creates a pool of DOM elements and attaches them to the local dom.
647 */ 674 */
648 _createPool: function(size) { 675 _createPool: function(size) {
649 var physicalItems = new Array(size); 676 var physicalItems = new Array(size);
650 677
651 this._ensureTemplatized(); 678 this._ensureTemplatized();
652 679
653 for (var i = 0; i < size; i++) { 680 for (var i = 0; i < size; i++) {
654 var inst = this.stamp(null); 681 var inst = this.stamp(null);
655 682
656 // First element child is item; Safari doesn't support children[0] 683 // First element child is item; Safari doesn't support children[0]
657 // on a doc fragment 684 // on a doc fragment
658 physicalItems[i] = inst.root.querySelector('*'); 685 physicalItems[i] = inst.root.querySelector('*');
659 Polymer.dom(this).appendChild(inst.root); 686 Polymer.dom(this).appendChild(inst.root);
660 } 687 }
661 688
662 return physicalItems; 689 return physicalItems;
663 }, 690 },
664 691
665 /** 692 /**
666 * Increases the pool size. That is, the physical items in the DOM. 693 * Increases the pool of physical items only if needed.
667 * This function will allocate additional physical items 694 * This function will allocate additional physical items
668 * (limited by `MAX_PHYSICAL_COUNT`) if the content size is shorter than 695 * (limited by `MAX_PHYSICAL_COUNT`) if the content size is shorter than
669 * `_optPhysicalSize` 696 * `_optPhysicalSize`
670 * 697 *
671 * @return Array 698 * @return boolean
672 */ 699 */
673 _increasePoolIfNeeded: function() { 700 _increasePoolIfNeeded: function() {
674 if (this._physicalSize >= this._optPhysicalSize || this._physicalAverage = == 0) { 701 if (this._physicalAverage === 0) {
675 return null; 702 return false;
676 } 703 }
704 if (this._physicalBottom < this._scrollBottom || this._physicalTop > this. _scrollPosition) {
705 return this._increasePool(1);
706 }
707 if (this._physicalSize < this._optPhysicalSize) {
708 return this._increasePool(Math.round((this._optPhysicalSize - this._phys icalSize) * 1.2 / this._physicalAverage));
709 }
710 return false;
711 },
677 712
678 // the estimated number of physical items that we will need to reach 713 /**
679 // the cap established by `_optPhysicalSize`. 714 * Increases the pool size.
680 var missingItems = Math.round( 715 */
681 (this._optPhysicalSize - this._physicalSize) * 1.2 / this._physicalAve rage 716 _increasePool: function(missingItems) {
682 );
683
684 // limit the size 717 // limit the size
685 var nextPhysicalCount = Math.min( 718 var nextPhysicalCount = Math.min(
686 this._physicalCount + missingItems, 719 this._physicalCount + missingItems,
687 this._virtualCount, 720 this._virtualCount,
688 MAX_PHYSICAL_COUNT 721 MAX_PHYSICAL_COUNT
689 ); 722 );
690 723
691 var prevPhysicalCount = this._physicalCount; 724 var prevPhysicalCount = this._physicalCount;
692 var delta = nextPhysicalCount - prevPhysicalCount; 725 var delta = nextPhysicalCount - prevPhysicalCount;
693 726
694 if (delta <= 0) { 727 if (delta <= 0) {
695 return null; 728 return false;
696 } 729 }
697 730
698 var newPhysicalItems = this._createPool(delta); 731 [].push.apply(this._physicalItems, this._createPool(delta));
699 var emptyArray = new Array(delta); 732 [].push.apply(this._physicalSizes, new Array(delta));
700
701 [].push.apply(this._physicalItems, newPhysicalItems);
702 [].push.apply(this._physicalSizes, emptyArray);
703 733
704 this._physicalCount = prevPhysicalCount + delta; 734 this._physicalCount = prevPhysicalCount + delta;
705 735
706 // fill the array with the new item pos 736 return true;
707 while (delta > 0) {
708 emptyArray[--delta] = prevPhysicalCount + delta;
709 }
710
711 return emptyArray;
712 }, 737 },
713 738
714 /** 739 /**
715 * Render a new list of items. This method does exactly the same as `update` , 740 * Render a new list of items. This method does exactly the same as `update` ,
716 * but it also ensures that only one `update` cycle is created. 741 * but it also ensures that only one `update` cycle is created.
717 */ 742 */
718 _render: function() { 743 _render: function() {
719 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0; 744 var requiresUpdate = this._virtualCount > 0 || this._physicalCount > 0;
720 745
721 if (this.isAttached && !this._itemsRendered && this._isVisible && requires Update) { 746 if (this.isAttached && !this._itemsRendered && this._isVisible && requires Update) {
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
851 this._virtualCount = this.items ? this.items.length : 0; 876 this._virtualCount = this.items ? this.items.length : 0;
852 877
853 this.debounce('refresh', this._render); 878 this.debounce('refresh', this._render);
854 879
855 } else { 880 } else {
856 // update a single item 881 // update a single item
857 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change. value); 882 this._forwardItemPath(change.path.split('.').slice(1).join('.'), change. value);
858 } 883 }
859 }, 884 },
860 885
886 /**
887 * @param {!Array<!PolymerSplice>} splices
888 */
861 _adjustVirtualIndex: function(splices) { 889 _adjustVirtualIndex: function(splices) {
862 var i, splice, idx; 890 var i, splice, idx;
863 891
864 for (i = 0; i < splices.length; i++) { 892 for (i = 0; i < splices.length; i++) {
865 splice = splices[i]; 893 splice = splices[i];
866 894
867 // deselect removed items 895 // deselect removed items
868 splice.removed.forEach(this.$.selector.deselect, this.$.selector); 896 splice.removed.forEach(this.$.selector.deselect, this.$.selector);
869 897
870 idx = splice.index; 898 idx = splice.index;
871 // We only need to care about changes happening above the current positi on 899 // We only need to care about changes happening above the current positi on
872 if (idx >= this._virtualStartVal) { 900 if (idx >= this._virtualStartVal) {
873 break; 901 break;
874 } 902 }
875 903
876 this._virtualStart = this._virtualStart + 904 this._virtualStart = this._virtualStart +
877 Math.max(splice.addedCount - splice.removed.length, idx - this._virt ualStartVal); 905 Math.max(splice.addedCount - splice.removed.length, idx - this._virt ualStartVal);
878 } 906 }
879 }, 907 },
880 908
881 _scrollHandler: function() { 909 _scrollHandler: function() {
882 this._refresh(); 910 this._refresh();
883 }, 911 },
884 912
885 /** 913 /**
886 * Executes a provided function per every physical index in `itemSet` 914 * Executes a provided function per every physical index in `itemSet`
887 * `itemSet` default value is equivalent to the entire set of physical index es. 915 * `itemSet` default value is equivalent to the entire set of physical index es.
916 *
917 * @param {!function(number, number)} fn
918 * @param {!Array<number>=} itemSet
888 */ 919 */
889 _iterateItems: function(fn, itemSet) { 920 _iterateItems: function(fn, itemSet) {
890 var pidx, vidx, rtn, i; 921 var pidx, vidx, rtn, i;
891 922
892 if (arguments.length === 2 && itemSet) { 923 if (arguments.length === 2 && itemSet) {
893 for (i = 0; i < itemSet.length; i++) { 924 for (i = 0; i < itemSet.length; i++) {
894 pidx = itemSet[i]; 925 pidx = itemSet[i];
895 if (pidx >= this._physicalStart) { 926 if (pidx >= this._physicalStart) {
896 vidx = this._virtualStartVal + (pidx - this._physicalStart); 927 vidx = this._virtualStartVal + (pidx - this._physicalStart);
897 } else { 928 } else {
(...skipping 18 matching lines...) Expand all
916 for (; pidx < this._physicalStart; pidx++, vidx++) { 947 for (; pidx < this._physicalStart; pidx++, vidx++) {
917 if ((rtn = fn.call(this, pidx, vidx)) != null) { 948 if ((rtn = fn.call(this, pidx, vidx)) != null) {
918 return rtn; 949 return rtn;
919 } 950 }
920 } 951 }
921 } 952 }
922 }, 953 },
923 954
924 /** 955 /**
925 * Assigns the data models to a given set of items. 956 * Assigns the data models to a given set of items.
957 * @param {!Array<number>=} itemSet
926 */ 958 */
927 _assignModels: function(itemSet) { 959 _assignModels: function(itemSet) {
928 this._iterateItems(function(pidx, vidx) { 960 this._iterateItems(function(pidx, vidx) {
929 var el = this._physicalItems[pidx]; 961 var el = this._physicalItems[pidx];
930 var inst = el._templateInstance; 962 var inst = el._templateInstance;
931 var item = this.items && this.items[vidx]; 963 var item = this.items && this.items[vidx];
932 964
933 if (item) { 965 if (item) {
934 inst[this.as] = item; 966 inst[this.as] = item;
935 inst.__key__ = this._collection.getKey(item); 967 inst.__key__ = this._collection.getKey(item);
936 inst[this.selectedAs] = this.$.selector.isSelected(item); 968 inst[this.selectedAs] =
969 /** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(it em);
937 inst[this.indexAs] = vidx; 970 inst[this.indexAs] = vidx;
938 el.removeAttribute('hidden'); 971 el.removeAttribute('hidden');
939 this._physicalIndexForKey[inst.__key__] = pidx; 972 this._physicalIndexForKey[inst.__key__] = pidx;
940 } else { 973 } else {
941 inst.__key__ = null; 974 inst.__key__ = null;
942 el.setAttribute('hidden', ''); 975 el.setAttribute('hidden', '');
943 } 976 }
944 977
945 }, itemSet); 978 }, itemSet);
946 }, 979 },
947 980
948 /** 981 /**
949 * Updates the height for a given set of items. 982 * Updates the height for a given set of items.
983 *
984 * @param {!Array<number>=} itemSet
950 */ 985 */
951 _updateMetrics: function() { 986 _updateMetrics: function(itemSet) {
952 var total = 0; 987 var newPhysicalSize = 0;
988 var oldPhysicalSize = 0;
953 var prevAvgCount = this._physicalAverageCount; 989 var prevAvgCount = this._physicalAverageCount;
954 var prevPhysicalAvg = this._physicalAverage; 990 var prevPhysicalAvg = this._physicalAverage;
955
956 // Make sure we distributed all the physical items 991 // Make sure we distributed all the physical items
957 // so we can measure them 992 // so we can measure them
958 Polymer.dom.flush(); 993 Polymer.dom.flush();
959 994
960 for (var i = 0; i < this._physicalCount; i++) { 995 this._iterateItems(function(pidx, vidx) {
961 this._physicalSizes[i] = this._physicalItems[i].offsetHeight; 996 oldPhysicalSize += this._physicalSizes[pidx] || 0;
962 total += this._physicalSizes[i]; 997 this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight;
963 this._physicalAverageCount += this._physicalSizes[i] ? 1 : 0; 998 newPhysicalSize += this._physicalSizes[pidx];
964 } 999 this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0;
1000 }, itemSet);
965 1001
966 this._physicalSize = total; 1002 this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSiz e;
967 this._viewportSize = this._scroller.offsetHeight; 1003 this._viewportSize = this._scroller.offsetHeight;
968 1004
1005 // update the average if we measured something
969 if (this._physicalAverageCount !== prevAvgCount) { 1006 if (this._physicalAverageCount !== prevAvgCount) {
970 this._physicalAverage = Math.round( 1007 this._physicalAverage = Math.round(
971 ((prevPhysicalAvg * prevAvgCount) + total) / 1008 ((prevPhysicalAvg * prevAvgCount) + newPhysicalSize) /
972 this._physicalAverageCount); 1009 this._physicalAverageCount);
973 } 1010 }
974 }, 1011 },
975 1012
976 /** 1013 /**
977 * Updates the position of the physical items. 1014 * Updates the position of the physical items.
978 */ 1015 */
979 _positionItems: function(itemSet) { 1016 _positionItems: function() {
980 this._adjustScrollPosition(); 1017 this._adjustScrollPosition();
981 1018
982 var y = this._physicalTop; 1019 var y = this._physicalTop;
983 1020
984 this._iterateItems(function(pidx) { 1021 this._iterateItems(function(pidx) {
985 1022
986 this.transform('translate3d(0, ' + y + 'px, 0)', this._physicalItems[pid x]); 1023 this.transform('translate3d(0, ' + y + 'px, 0)', this._physicalItems[pid x]);
987 y += this._physicalSizes[pidx]; 1024 y += this._physicalSizes[pidx];
988 1025
989 }, itemSet); 1026 });
990 }, 1027 },
991 1028
992 /** 1029 /**
993 * Adjusts the scroll position when it was overestimated. 1030 * Adjusts the scroll position when it was overestimated.
994 */ 1031 */
995 _adjustScrollPosition: function() { 1032 _adjustScrollPosition: function() {
996 var deltaHeight = this._virtualStartVal === 0 ? this._physicalTop : 1033 var deltaHeight = this._virtualStartVal === 0 ? this._physicalTop :
997 Math.min(this._scrollPosition + this._physicalTop, 0); 1034 Math.min(this._scrollPosition + this._physicalTop, 0);
998 1035
999 if (deltaHeight) { 1036 if (deltaHeight) {
(...skipping 11 matching lines...) Expand all
1011 */ 1048 */
1012 _resetScrollPosition: function(pos) { 1049 _resetScrollPosition: function(pos) {
1013 if (this._scroller) { 1050 if (this._scroller) {
1014 this._scroller.scrollTop = pos; 1051 this._scroller.scrollTop = pos;
1015 this._scrollPosition = this._scroller.scrollTop; 1052 this._scrollPosition = this._scroller.scrollTop;
1016 } 1053 }
1017 }, 1054 },
1018 1055
1019 /** 1056 /**
1020 * Sets the scroll height, that's the height of the content, 1057 * Sets the scroll height, that's the height of the content,
1058 *
1059 * @param {boolean=} forceUpdate If true, updates the height no matter what.
1021 */ 1060 */
1022 _updateScrollerSize: function(forceUpdate) { 1061 _updateScrollerSize: function(forceUpdate) {
1023 this._estScrollHeight = (this._physicalBottom + 1062 this._estScrollHeight = (this._physicalBottom +
1024 Math.max(this._virtualCount - this._physicalCount - this._virtualStart Val, 0) * this._physicalAverage); 1063 Math.max(this._virtualCount - this._physicalCount - this._virtualStart Val, 0) * this._physicalAverage);
1025 1064
1026 forceUpdate = forceUpdate || this._scrollHeight === 0; 1065 forceUpdate = forceUpdate || this._scrollHeight === 0;
1027 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize; 1066 forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize;
1028 1067
1029 // amortize height adjustment, so it won't trigger repaints very often 1068 // amortize height adjustment, so it won't trigger repaints very often
1030 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) { 1069 if (forceUpdate || Math.abs(this._estScrollHeight - this._scrollHeight) >= this._optPhysicalSize) {
1031 this.$.items.style.height = this._estScrollHeight + 'px'; 1070 this.$.items.style.height = this._estScrollHeight + 'px';
1032 this._scrollHeight = this._estScrollHeight; 1071 this._scrollHeight = this._estScrollHeight;
1033 } 1072 }
1034 }, 1073 },
1035 1074
1036 /** 1075 /**
1037 * Scroll to a specific item in the virtual list regardless 1076 * Scroll to a specific item in the virtual list regardless
1038 * of the physical items in the DOM tree. 1077 * of the physical items in the DOM tree.
1039 * 1078 *
1040 * @method scrollToIndex 1079 * @method scrollToIndex
1041 * @param {number} idx The index of the item 1080 * @param {number} idx The index of the item
1042 */ 1081 */
1043 scrollToIndex: function(idx) { 1082 scrollToIndex: function(idx) {
1044 if (typeof idx !== 'number') { 1083 if (typeof idx !== 'number') {
1045 return; 1084 return;
1046 } 1085 }
1047 1086
1048 var itemSet;
1049 var firstVisible = this.firstVisibleIndex; 1087 var firstVisible = this.firstVisibleIndex;
1050 1088
1051 idx = Math.min(Math.max(idx, 0), this._virtualCount-1); 1089 idx = Math.min(Math.max(idx, 0), this._virtualCount-1);
1052 1090
1053 // start at the previous virtual item 1091 // start at the previous virtual item
1054 // so we have a item above the first visible item 1092 // so we have a item above the first visible item
1055 this._virtualStart = idx - 1; 1093 this._virtualStart = idx - 1;
1056 1094
1057 // assign new models 1095 // assign new models
1058 this._assignModels(); 1096 this._assignModels();
(...skipping 19 matching lines...) Expand all
1078 // update the scroller size 1116 // update the scroller size
1079 this._updateScrollerSize(true); 1117 this._updateScrollerSize(true);
1080 1118
1081 // update the position of the items 1119 // update the position of the items
1082 this._positionItems(); 1120 this._positionItems();
1083 1121
1084 // set the new scroll position 1122 // set the new scroll position
1085 this._resetScrollPosition(this._physicalTop + targetOffsetTop + 1); 1123 this._resetScrollPosition(this._physicalTop + targetOffsetTop + 1);
1086 1124
1087 // increase the pool of physical items if needed 1125 // increase the pool of physical items if needed
1088 if (itemSet = this._increasePoolIfNeeded()) { 1126 if (this._increasePoolIfNeeded()) {
1089 // set models to the new items 1127 // yield set models to the new items
1090 this.async(this._update.bind(this, itemSet)); 1128 this.async(this._update);
1091 } 1129 }
1092
1093 // clear cached visible index 1130 // clear cached visible index
1094 this._firstVisibleIndexVal = null; 1131 this._firstVisibleIndexVal = null;
1095 }, 1132 },
1096 1133
1097 /** 1134 /**
1098 * Reset the physical average and the average count. 1135 * Reset the physical average and the average count.
1099 */ 1136 */
1100 _resetAverage: function() { 1137 _resetAverage: function() {
1101 this._physicalAverage = 0; 1138 this._physicalAverage = 0;
1102 this._physicalAverageCount = 0; 1139 this._physicalAverageCount = 0;
1103 }, 1140 },
1104 1141
1105 /** 1142 /**
1106 * A handler for the `resize` event triggered by `IronResizableBehavior` 1143 * A handler for the `iron-resize` event triggered by `IronResizableBehavior `
1107 * when the element is resized. 1144 * when the element is resized.
1108 */ 1145 */
1109 _resizeHandler: function() { 1146 _resizeHandler: function() {
1110 this.debounce('resize', function() { 1147 this.debounce('resize', function() {
1111 this._render(); 1148 this._render();
1112 if (this._itemsRendered && this._physicalItems && this._isVisible) { 1149 if (this._itemsRendered && this._physicalItems && this._isVisible) {
1113 this._resetAverage(); 1150 this._resetAverage();
1114 this.updateViewportBoundaries(); 1151 this.updateViewportBoundaries();
1115 this.scrollToIndex(this.firstVisibleIndex); 1152 this.scrollToIndex(this.firstVisibleIndex);
1116 } 1153 }
1117 }); 1154 });
1118 }, 1155 },
1119 1156
1120 _getModelFromItem: function(item) { 1157 _getModelFromItem: function(item) {
1121 var key = this._collection.getKey(item); 1158 var key = this._collection.getKey(item);
1122 var pidx = this._physicalIndexForKey[key]; 1159 var pidx = this._physicalIndexForKey[key];
1123 1160
1124 if (pidx !== undefined) { 1161 if (pidx !== undefined) {
1125 return this._physicalItems[pidx]._templateInstance; 1162 return this._physicalItems[pidx]._templateInstance;
1126 } 1163 }
1127 return null; 1164 return null;
1128 }, 1165 },
1129 1166
1130 /** 1167 /**
1168 * Gets a valid item instance from its index or the object value.
1169 *
1170 * @param {(Object|number)} item The item object or its index
1171 */
1172 _getNormalizedItem: function(item) {
1173 if (typeof item === 'number') {
1174 item = this.items[item];
1175 if (!item) {
1176 throw new RangeError('<item> not found');
1177 }
1178 } else if (this._collection.getKey(item) === undefined) {
1179 throw new TypeError('<item> should be a valid item');
1180 }
1181 return item;
1182 },
1183
1184 /**
1131 * Select the list item at the given index. 1185 * Select the list item at the given index.
1132 * 1186 *
1133 * @method selectItem 1187 * @method selectItem
1134 * @param {(Object|number)} item the item object or its index 1188 * @param {(Object|number)} item The item object or its index
1135 */ 1189 */
1136 selectItem: function(item) { 1190 selectItem: function(item) {
1137 if (typeof item === 'number') { 1191 item = this._getNormalizedItem(item);
1138 item = this.items[item];
1139 if (!item) {
1140 throw new RangeError('<item> not found');
1141 }
1142 } else {
1143 if (this._collection.getKey(item) === undefined) {
1144 throw new TypeError('<item> should be a valid item');
1145 }
1146 }
1147
1148 var model = this._getModelFromItem(item); 1192 var model = this._getModelFromItem(item);
1149 1193
1150 if (!this.multiSelection && this.selectedItem) { 1194 if (!this.multiSelection && this.selectedItem) {
1151 this.deselectItem(this.selectedItem); 1195 this.deselectItem(this.selectedItem);
1152 } 1196 }
1153 if (model) { 1197 if (model) {
1154 model[this.selectedAs] = true; 1198 model[this.selectedAs] = true;
1155 } 1199 }
1156 this.$.selector.select(item); 1200 this.$.selector.select(item);
1157 }, 1201 },
1158 1202
1159 /** 1203 /**
1160 * Deselects the given item list if it is already selected. 1204 * Deselects the given item list if it is already selected.
1161 * 1205 *
1206
1162 * @method deselect 1207 * @method deselect
1163 * @param {(Object|number)} item the item object or its index 1208 * @param {(Object|number)} item The item object or its index
1164 */ 1209 */
1165 deselectItem: function(item) { 1210 deselectItem: function(item) {
1166 if (typeof item === 'number') { 1211 item = this._getNormalizedItem(item);
1167 item = this.items[item];
1168 if (!item) {
1169 throw new RangeError('<item> not found');
1170 }
1171 } else {
1172 if (this._collection.getKey(item) === undefined) {
1173 throw new TypeError('<item> should be a valid item');
1174 }
1175 }
1176
1177 var model = this._getModelFromItem(item); 1212 var model = this._getModelFromItem(item);
1178 1213
1179 if (model) { 1214 if (model) {
1180 model[this.selectedAs] = false; 1215 model[this.selectedAs] = false;
1181 } 1216 }
1182 this.$.selector.deselect(item); 1217 this.$.selector.deselect(item);
1183 }, 1218 },
1184 1219
1185 /** 1220 /**
1186 * Select or deselect a given item depending on whether the item 1221 * Select or deselect a given item depending on whether the item
1187 * has already been selected. 1222 * has already been selected.
1188 * 1223 *
1189 * @method toggleSelectionForItem 1224 * @method toggleSelectionForItem
1190 * @param {(Object|number)} item the item object or its index 1225 * @param {(Object|number)} item The item object or its index
1191 */ 1226 */
1192 toggleSelectionForItem: function(item) { 1227 toggleSelectionForItem: function(item) {
1193 var item = typeof item === 'number' ? this.items[item] : item; 1228 item = this._getNormalizedItem(item);
1194 if (this.$.selector.isSelected(item)) { 1229 if (/** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item )) {
1195 this.deselectItem(item); 1230 this.deselectItem(item);
1196 } else { 1231 } else {
1197 this.selectItem(item); 1232 this.selectItem(item);
1198 } 1233 }
1199 }, 1234 },
1200 1235
1201 /** 1236 /**
1202 * Clears the current selection state of the list. 1237 * Clears the current selection state of the list.
1203 * 1238 *
1204 * @method clearSelection 1239 * @method clearSelection
1205 */ 1240 */
1206 clearSelection: function() { 1241 clearSelection: function() {
1207 function unselect(item) { 1242 function unselect(item) {
1208 var model = this._getModelFromItem(item); 1243 var model = this._getModelFromItem(item);
1209 if (model) { 1244 if (model) {
1210 model[this.selectedAs] = false; 1245 model[this.selectedAs] = false;
1211 } 1246 }
1212 } 1247 }
1213 1248
1214 if (Array.isArray(this.selectedItems)) { 1249 if (Array.isArray(this.selectedItems)) {
1215 this.selectedItems.forEach(unselect, this); 1250 this.selectedItems.forEach(unselect, this);
1216 } else if (this.selectedItem) { 1251 } else if (this.selectedItem) {
1217 unselect.call(this, this.selectedItem); 1252 unselect.call(this, this.selectedItem);
1218 } 1253 }
1219 1254
1220 this.$.selector.clearSelection(); 1255 /** @type {!ArraySelectorElement} */ (this.$.selector).clearSelection();
1221 }, 1256 },
1222 1257
1223 /** 1258 /**
1224 * Add an event listener to `tap` if `selectionEnabled` is true, 1259 * Add an event listener to `tap` if `selectionEnabled` is true,
1225 * it will remove the listener otherwise. 1260 * it will remove the listener otherwise.
1226 */ 1261 */
1227 _selectionEnabledChanged: function(selectionEnabled) { 1262 _selectionEnabledChanged: function(selectionEnabled) {
1228 if (selectionEnabled) { 1263 if (selectionEnabled) {
1229 this.listen(this, 'tap', '_selectionHandler'); 1264 this.listen(this, 'tap', '_selectionHandler');
1265 this.listen(this, 'keypress', '_selectionHandler');
1230 } else { 1266 } else {
1231 this.unlisten(this, 'tap', '_selectionHandler'); 1267 this.unlisten(this, 'tap', '_selectionHandler');
1268 this.unlisten(this, 'keypress', '_selectionHandler');
1232 } 1269 }
1233 }, 1270 },
1234 1271
1235 /** 1272 /**
1236 * Select an item from an event object. 1273 * Select an item from an event object.
1237 */ 1274 */
1238 _selectionHandler: function(e) { 1275 _selectionHandler: function(e) {
1239 var model = this.modelForElement(e.target); 1276 if (e.type !== 'keypress' || e.keyCode === 13) {
1240 if (model) { 1277 var model = this.modelForElement(e.target);
1241 this.toggleSelectionForItem(model[this.as]); 1278 if (model) {
1279 this.toggleSelectionForItem(model[this.as]);
1280 }
1242 } 1281 }
1243 }, 1282 },
1244 1283
1245 _multiSelectionChanged: function(multiSelection) { 1284 _multiSelectionChanged: function(multiSelection) {
1246 this.clearSelection(); 1285 this.clearSelection();
1247 this.$.selector.multi = multiSelection; 1286 this.$.selector.multi = multiSelection;
1287 },
1288
1289 /**
1290 * Updates the size of an item.
1291 *
1292 * @method updateSizeForItem
1293 * @param {(Object|number)} item The item object or its index
1294 */
1295 updateSizeForItem: function(item) {
1296 item = this._getNormalizedItem(item);
1297 var key = this._collection.getKey(item);
1298 var pidx = this._physicalIndexForKey[key];
1299
1300 if (pidx !== undefined) {
1301 this._updateMetrics([pidx]);
1302 this._positionItems();
1303 }
1248 } 1304 }
1249 }); 1305 });
1250 1306
1251 })(); 1307 })();
1252 1308
1253 </script> 1309 </script>
OLDNEW
« no previous file with comments | « lib/src/iron-iconset/test/iron-iconset.html ('k') | lib/src/iron-list/test/different-heights.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698