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

Unified Diff: third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js

Issue 1681053002: Unrestrict version of PolymerElements/iron-list and update it (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@fix-closure
Patch Set: and vulcanize Created 4 years, 10 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 side-by-side diff with in-line comments
Download patch
Index: third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js
diff --git a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js
index a7c75b88e84ab44b1ee88ad3d642fdcae8f86e4b..8a0ee8717684870bad9d3a5fb4e848f60407a7ad 100644
--- a/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js
+++ b/third_party/polymer/v1_0/components-chromium/iron-list/iron-list-extracted.js
@@ -4,6 +4,7 @@
var IOS_TOUCH_SCROLLING = IOS && IOS[1] >= 8;
var DEFAULT_PHYSICAL_COUNT = 3;
var MAX_PHYSICAL_COUNT = 500;
+ var HIDDEN_Y = '-10000px';
Polymer({
@@ -89,18 +90,27 @@
observers: [
'_itemsChanged(items.*)',
'_selectionEnabledChanged(selectionEnabled)',
- '_multiSelectionChanged(multiSelection)'
+ '_multiSelectionChanged(multiSelection)',
+ '_setOverflow(scrollTarget)'
],
behaviors: [
Polymer.Templatizer,
- Polymer.IronResizableBehavior
+ Polymer.IronResizableBehavior,
+ Polymer.IronA11yKeysBehavior,
+ Polymer.IronScrollTargetBehavior
],
listeners: {
'iron-resize': '_resizeHandler'
},
+ keyBindings: {
+ 'up': '_didMoveUp',
+ 'down': '_didMoveDown',
+ 'enter': '_didEnter'
+ },
+
/**
* The ratio of hidden tiles that should remain in the scroll direction.
* Recommended value ~0.5, so it will distribute tiles evely in both directions.
@@ -108,12 +118,6 @@
_ratio: 0.5,
/**
- * The element that controls the scroll
- * @type {?Element}
- */
- _scroller: null,
-
- /**
* The padding-top value of the `scroller` element
*/
_scrollerPaddingTop: 0,
@@ -144,7 +148,7 @@
_physicalSize: 0,
/**
- * The average `offsetHeight` of the tiles observed till now.
+ * The average `F` of the tiles observed till now.
*/
_physicalAverage: 0,
@@ -202,13 +206,21 @@
_physicalSizes: null,
/**
- * A cached value for the visible index.
+ * A cached value for the first visible index.
* See `firstVisibleIndex`
* @type {?number}
*/
_firstVisibleIndexVal: null,
/**
+ * A cached value for the last visible index.
+ * See `lastVisibleIndex`
+ * @type {?number}
+ */
+ _lastVisibleIndexVal: null,
+
+
+ /**
* A Polymer collection for the items.
* @type {?Polymer.Collection}
*/
@@ -231,6 +243,23 @@
_maxPages: 3,
/**
+ * The currently focused item index.
+ */
+ _focusedIndex: 0,
+
+ /**
+ * The the item that is focused if it is moved offscreen.
+ * @private {?TemplatizerNode}
+ */
+ _offscreenFocusedItem: null,
+
+ /**
+ * The item that backfills the `_offscreenFocusedItem` in the physical items
+ * list when that item is moved offscreen.
+ */
+ _focusBackfillItem: null,
+
+ /**
* The bottom of the physical content.
*/
get _physicalBottom() {
@@ -248,7 +277,7 @@
* The n-th item rendered in the last physical item.
*/
get _virtualEnd() {
- return this._virtualStartVal + this._physicalCount - 1;
+ return this._virtualStart + this._physicalCount - 1;
},
/**
@@ -283,8 +312,13 @@
set _virtualStart(val) {
// clamp the value so that _minVirtualStart <= val <= _maxVirtualStart
this._virtualStartVal = Math.min(this._maxVirtualStart, Math.max(this._minVirtualStart, val));
- this._physicalStart = this._virtualStartVal % this._physicalCount;
- this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount;
+ if (this._physicalCount === 0) {
+ this._physicalStart = 0;
+ this._physicalEnd = 0;
+ } else {
+ this._physicalStart = this._virtualStartVal % this._physicalCount;
+ this._physicalEnd = (this._physicalStart + this._physicalCount - 1) % this._physicalCount;
+ }
},
/**
@@ -309,7 +343,7 @@
* True if the current list is visible.
*/
get _isVisible() {
- return this._scroller && Boolean(this._scroller.offsetWidth || this._scroller.offsetHeight);
+ return this.scrollTarget && Boolean(this.scrollTarget.offsetWidth || this.scrollTarget.offsetHeight);
},
/**
@@ -318,10 +352,8 @@
* @type {number}
*/
get firstVisibleIndex() {
- var physicalOffset;
-
if (this._firstVisibleIndexVal === null) {
- physicalOffset = this._physicalTop;
+ var physicalOffset = this._physicalTop;
this._firstVisibleIndexVal = this._iterateItems(
function(pidx, vidx) {
@@ -332,54 +364,52 @@
}
}) || 0;
}
-
return this._firstVisibleIndexVal;
},
- ready: function() {
- if (IOS_TOUCH_SCROLLING) {
- this._scrollListener = function() {
- requestAnimationFrame(this._scrollHandler.bind(this));
- }.bind(this);
- } else {
- this._scrollListener = this._scrollHandler.bind(this);
- }
- },
-
/**
- * When the element has been attached to the DOM tree.
+ * Gets the index of the last visible item in the viewport.
+ *
+ * @type {number}
*/
- attached: function() {
- // delegate to the parent's scroller
- // e.g. paper-scroll-header-panel
- var el = Polymer.dom(this);
+ get lastVisibleIndex() {
+ if (this._lastVisibleIndexVal === null) {
+ var physicalOffset = this._physicalTop;
- var parentNode = /** @type {?{scroller: ?Element}} */ (el.parentNode);
- if (parentNode && parentNode.scroller) {
- this._scroller = parentNode.scroller;
- } else {
- this._scroller = this;
- this.classList.add('has-scroller');
- }
+ this._iterateItems(function(pidx, vidx) {
+ physicalOffset += this._physicalSizes[pidx];
- if (IOS_TOUCH_SCROLLING) {
- this._scroller.style.webkitOverflowScrolling = 'touch';
+ if(physicalOffset <= this._scrollBottom) {
+ this._lastVisibleIndexVal = vidx;
+ }
+ });
}
+ return this._lastVisibleIndexVal;
+ },
- this._scroller.addEventListener('scroll', this._scrollListener);
+ ready: function() {
+ this.addEventListener('focus', this._didFocus.bind(this), true);
+ },
+ attached: function() {
this.updateViewportBoundaries();
this._render();
},
- /**
- * When the element has been removed from the DOM tree.
- */
detached: function() {
this._itemsRendered = false;
- if (this._scroller) {
- this._scroller.removeEventListener('scroll', this._scrollListener);
- }
+ },
+
+ get _defaultScrollTarget() {
+ return this;
+ },
+
+ /**
+ * Set the overflow property if this element has its own scrolling region
+ */
+ _setOverflow: function(scrollTarget) {
+ this.style.webkitOverflowScrolling = scrollTarget === this ? 'touch' : '';
+ this.style.overflow = scrollTarget === this ? 'auto' : '';
},
/**
@@ -389,20 +419,20 @@
* @method updateViewportBoundaries
*/
updateViewportBoundaries: function() {
- var scrollerStyle = window.getComputedStyle(this._scroller);
+ var scrollerStyle = window.getComputedStyle(this.scrollTarget);
this._scrollerPaddingTop = parseInt(scrollerStyle['padding-top'], 10);
- this._viewportSize = this._scroller.offsetHeight;
+ this._viewportSize = this._scrollTargetHeight;
},
/**
* Update the models, the position of the
* items in the viewport and recycle tiles as needed.
*/
- _refresh: function() {
+ _scrollHandler: function() {
// clamp the `scrollTop` value
// IE 10|11 scrollTop may go above `_maxScrollTop`
// iOS `scrollTop` may go below 0 and above `_maxScrollTop`
- var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scroller.scrollTop));
+ var scrollTop = Math.max(0, Math.min(this._maxScrollTop, this._scrollTop));
var tileHeight, tileTop, kth, recycledTileSet, scrollBottom, physicalBottom;
var ratio = this._ratio;
var delta = scrollTop - this._scrollPosition;
@@ -416,6 +446,7 @@
// clear cached visible index
this._firstVisibleIndexVal = null;
+ this._lastVisibleIndexVal = null;
scrollBottom = this._scrollBottom;
physicalBottom = this._physicalBottom;
@@ -505,17 +536,21 @@
},
/**
- * Update the list of items, starting from the `_virtualStartVal` item.
+ * Update the list of items, starting from the `_virtualStart` item.
* @param {!Array<number>=} itemSet
* @param {!Array<number>=} movingUp
*/
_update: function(itemSet, movingUp) {
+ // manage focus
+ if (this._isIndexRendered(this._focusedIndex)) {
+ this._restoreFocusedItem();
+ } else {
+ this._createFocusBackfillItem();
+ }
// update models
this._assignModels(itemSet);
-
// measure heights
this._updateMetrics(itemSet);
-
// adjust offset after measuring
if (movingUp) {
while (movingUp.length) {
@@ -524,10 +559,8 @@
}
// update the position of the items
this._positionItems();
-
// set the scroller size
this._updateScrollerSize();
-
// increase the pool of physical items
this._increasePoolIfNeeded();
},
@@ -547,7 +580,6 @@
physicalItems[i] = inst.root.querySelector('*');
Polymer.dom(this).appendChild(inst.root);
}
-
return physicalItems;
},
@@ -557,24 +589,24 @@
* if the physical size is shorter than `_optPhysicalSize`
*/
_increasePoolIfNeeded: function() {
- if (this._viewportSize !== 0 && this._physicalSize < this._optPhysicalSize) {
- // 0 <= `currentPage` <= `_maxPages`
- var currentPage = Math.floor(this._physicalSize / this._viewportSize);
-
- if (currentPage === 0) {
- // fill the first page
- this.async(this._increasePool.bind(this, Math.round(this._physicalCount * 0.5)));
- } else if (this._lastPage !== currentPage) {
- // once a page is filled up, paint it and defer the next increase
- requestAnimationFrame(this._increasePool.bind(this, 1));
- } else {
- // fill the rest of the pages
- this.async(this._increasePool.bind(this, 1));
- }
- this._lastPage = currentPage;
- return true;
+ if (this._viewportSize === 0 || this._physicalSize >= this._optPhysicalSize) {
+ return false;
+ }
+ // 0 <= `currentPage` <= `_maxPages`
+ var currentPage = Math.floor(this._physicalSize / this._viewportSize);
+ if (currentPage === 0) {
+ // fill the first page
+ this._debounceTemplate(this._increasePool.bind(this, Math.round(this._physicalCount * 0.5)));
+ } else if (this._lastPage !== currentPage) {
+ // paint the page and defer the next increase
+ // wait 16ms which is rough enough to get paint cycle.
+ Polymer.dom.addDebouncer(this.debounce('_debounceTemplate', this._increasePool.bind(this, 1), 16));
+ } else {
+ // fill the rest of the pages
+ this._debounceTemplate(this._increasePool.bind(this, 1));
}
- return false;
+ this._lastPage = currentPage;
+ return true;
},
/**
@@ -584,7 +616,7 @@
// limit the size
var nextPhysicalCount = Math.min(
this._physicalCount + missingItems,
- this._virtualCount,
+ this._virtualCount - this._virtualStart,
MAX_PHYSICAL_COUNT
);
var prevPhysicalCount = this._physicalCount;
@@ -610,6 +642,7 @@
if (this.isAttached && !this._itemsRendered && this._isVisible && requiresUpdate) {
this._lastPage = 0;
this._update();
+ this._scrollHandler();
this._itemsRendered = true;
}
},
@@ -621,11 +654,11 @@
if (!this.ctor) {
// Template instance props that should be excluded from forwarding
var props = {};
-
props.__key__ = true;
props[this.as] = true;
props[this.indexAs] = true;
props[this.selectedAs] = true;
+ props.tabIndex = true;
this._instanceProps = props;
this._userTemplate = Polymer.dom(this).querySelector('template');
@@ -693,6 +726,10 @@
var key = path.substring(0, dot < 0 ? path.length : dot);
var idx = this._physicalIndexForKey[key];
var row = this._physicalItems[idx];
+
+ if (idx === this._focusedIndex && this._offscreenFocusedItem) {
+ row = this._offscreenFocusedItem;
+ }
if (row) {
var inst = row._templateInstance;
if (dot >= 0) {
@@ -711,17 +748,18 @@
*/
_itemsChanged: function(change) {
if (change.path === 'items') {
+
+ this._restoreFocusedItem();
// render the new set
this._itemsRendered = false;
-
// update the whole set
- this._virtualStartVal = 0;
+ this._virtualStart = 0;
this._physicalTop = 0;
this._virtualCount = this.items ? this.items.length : 0;
+ this._focusedIndex = 0;
this._collection = this.items ? Polymer.Collection.get(this.items) : null;
this._physicalIndexForKey = {};
- // scroll to the top
this._resetScrollPosition(0);
// create the initial physical items
@@ -730,17 +768,20 @@
this._physicalItems = this._createPool(this._physicalCount);
this._physicalSizes = new Array(this._physicalCount);
}
-
- this.debounce('refresh', this._render);
+ this._debounceTemplate(this._render);
} else if (change.path === 'items.splices') {
// render the new set
this._itemsRendered = false;
-
this._adjustVirtualIndex(change.value.indexSplices);
this._virtualCount = this.items ? this.items.length : 0;
- this.debounce('refresh', this._render);
+ this._debounceTemplate(this._render);
+
+ if (this._focusedIndex < 0 || this._focusedIndex >= this._virtualCount) {
+ this._focusedIndex = 0;
+ }
+ this._debounceTemplate(this._render);
} else {
// update a single item
@@ -762,19 +803,15 @@
idx = splice.index;
// We only need to care about changes happening above the current position
- if (idx >= this._virtualStartVal) {
+ if (idx >= this._virtualStart) {
break;
}
this._virtualStart = this._virtualStart +
- Math.max(splice.addedCount - splice.removed.length, idx - this._virtualStartVal);
+ Math.max(splice.addedCount - splice.removed.length, idx - this._virtualStart);
}
},
- _scrollHandler: function() {
- this._refresh();
- },
-
/**
* Executes a provided function per every physical index in `itemSet`
* `itemSet` default value is equivalent to the entire set of physical indexes.
@@ -789,9 +826,9 @@
for (i = 0; i < itemSet.length; i++) {
pidx = itemSet[i];
if (pidx >= this._physicalStart) {
- vidx = this._virtualStartVal + (pidx - this._physicalStart);
+ vidx = this._virtualStart + (pidx - this._physicalStart);
} else {
- vidx = this._virtualStartVal + (this._physicalCount - this._physicalStart) + pidx;
+ vidx = this._virtualStart + (this._physicalCount - this._physicalStart) + pidx;
}
if ((rtn = fn.call(this, pidx, vidx)) != null) {
return rtn;
@@ -799,17 +836,14 @@
}
} else {
pidx = this._physicalStart;
- vidx = this._virtualStartVal;
+ vidx = this._virtualStart;
for (; pidx < this._physicalCount; pidx++, vidx++) {
if ((rtn = fn.call(this, pidx, vidx)) != null) {
return rtn;
}
}
-
- pidx = 0;
-
- for (; pidx < this._physicalStart; pidx++, vidx++) {
+ for (pidx = 0; pidx < this._physicalStart; pidx++, vidx++) {
if ((rtn = fn.call(this, pidx, vidx)) != null) {
return rtn;
}
@@ -827,12 +861,12 @@
var inst = el._templateInstance;
var item = this.items && this.items[vidx];
- if (item) {
+ if (item !== undefined && item !== null) {
inst[this.as] = item;
inst.__key__ = this._collection.getKey(item);
- inst[this.selectedAs] =
- /** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item);
+ inst[this.selectedAs] = /** @type {!ArraySelectorElement} */ (this.$.selector).isSelected(item);
inst[this.indexAs] = vidx;
+ inst.tabIndex = vidx === this._focusedIndex ? 0 : -1;
el.removeAttribute('hidden');
this._physicalIndexForKey[inst.__key__] = pidx;
} else {
@@ -849,23 +883,26 @@
* @param {!Array<number>=} itemSet
*/
_updateMetrics: function(itemSet) {
+ // Make sure we distributed all the physical items
+ // so we can measure them
+ Polymer.dom.flush();
+
var newPhysicalSize = 0;
var oldPhysicalSize = 0;
var prevAvgCount = this._physicalAverageCount;
var prevPhysicalAvg = this._physicalAverage;
- // Make sure we distributed all the physical items
- // so we can measure them
- Polymer.dom.flush();
this._iterateItems(function(pidx, vidx) {
+
oldPhysicalSize += this._physicalSizes[pidx] || 0;
this._physicalSizes[pidx] = this._physicalItems[pidx].offsetHeight;
newPhysicalSize += this._physicalSizes[pidx];
this._physicalAverageCount += this._physicalSizes[pidx] ? 1 : 0;
+
}, itemSet);
this._physicalSize = this._physicalSize + newPhysicalSize - oldPhysicalSize;
- this._viewportSize = this._scroller.offsetHeight;
+ this._viewportSize = this._scrollTargetHeight;
// update the average if we measured something
if (this._physicalAverageCount !== prevAvgCount) {
@@ -885,7 +922,7 @@
this._iterateItems(function(pidx) {
- this.transform('translate3d(0, ' + y + 'px, 0)', this._physicalItems[pidx]);
+ this.translate3d(0, y + 'px', 0, this._physicalItems[pidx]);
y += this._physicalSizes[pidx];
});
@@ -895,15 +932,14 @@
* Adjusts the scroll position when it was overestimated.
*/
_adjustScrollPosition: function() {
- var deltaHeight = this._virtualStartVal === 0 ? this._physicalTop :
+ var deltaHeight = this._virtualStart === 0 ? this._physicalTop :
Math.min(this._scrollPosition + this._physicalTop, 0);
if (deltaHeight) {
this._physicalTop = this._physicalTop - deltaHeight;
-
// juking scroll position during interial scrolling on iOS is no bueno
if (!IOS_TOUCH_SCROLLING) {
- this._resetScrollPosition(this._scroller.scrollTop - deltaHeight);
+ this._resetScrollPosition(this._scrollTop - deltaHeight);
}
}
},
@@ -912,9 +948,9 @@
* Sets the position of the scroll.
*/
_resetScrollPosition: function(pos) {
- if (this._scroller) {
- this._scroller.scrollTop = pos;
- this._scrollPosition = this._scroller.scrollTop;
+ if (this.scrollTarget) {
+ this._scrollTop = pos;
+ this._scrollPosition = this._scrollTop;
}
},
@@ -925,7 +961,7 @@
*/
_updateScrollerSize: function(forceUpdate) {
this._estScrollHeight = (this._physicalBottom +
- Math.max(this._virtualCount - this._physicalCount - this._virtualStartVal, 0) * this._physicalAverage);
+ Math.max(this._virtualCount - this._physicalCount - this._virtualStart, 0) * this._physicalAverage);
forceUpdate = forceUpdate || this._scrollHeight === 0;
forceUpdate = forceUpdate || this._scrollPosition >= this._estScrollHeight - this._physicalSize;
@@ -949,20 +985,18 @@
return;
}
- var firstVisible = this.firstVisibleIndex;
+ Polymer.dom.flush();
+ var firstVisible = this.firstVisibleIndex;
idx = Math.min(Math.max(idx, 0), this._virtualCount-1);
// start at the previous virtual item
// so we have a item above the first visible item
this._virtualStart = idx - 1;
-
// assign new models
this._assignModels();
-
// measure the new sizes
this._updateMetrics();
-
// estimate new physical offset
this._physicalTop = this._virtualStart * this._physicalAverage;
@@ -977,21 +1011,17 @@
currentTopItem = (currentTopItem + 1) % this._physicalCount;
currentVirtualItem++;
}
-
// update the scroller size
this._updateScrollerSize(true);
-
// update the position of the items
this._positionItems();
-
// set the new scroll position
- this._resetScrollPosition(this._physicalTop + targetOffsetTop + 1);
-
+ this._resetScrollPosition(this._physicalTop + this._scrollerPaddingTop + targetOffsetTop + 1);
// increase the pool of physical items if needed
this._increasePoolIfNeeded();
-
// clear cached visible index
this._firstVisibleIndexVal = null;
+ this._lastVisibleIndexVal = null;
},
/**
@@ -1007,7 +1037,11 @@
* when the element is resized.
*/
_resizeHandler: function() {
- this.debounce('resize', function() {
+ // iOS fires the resize event when the address bar slides up
+ if (IOS && Math.abs(this._viewportSize - this._scrollTargetHeight) < 100) {
+ return;
+ }
+ this._debounceTemplate(function() {
this._render();
if (this._itemsRendered && this._physicalItems && this._isVisible) {
this._resetAverage();
@@ -1033,12 +1067,14 @@
* @param {(Object|number)} item The item object or its index
*/
_getNormalizedItem: function(item) {
- if (typeof item === 'number') {
- item = this.items[item];
- if (!item) {
- throw new RangeError('<item> not found');
+ if (this._collection.getKey(item) === undefined) {
+ if (typeof item === 'number') {
+ item = this.items[item];
+ if (!item) {
+ throw new RangeError('<item> not found');
+ }
+ return item;
}
- } else if (this._collection.getKey(item) === undefined) {
throw new TypeError('<item> should be a valid item');
}
return item;
@@ -1061,6 +1097,7 @@
model[this.selectedAs] = true;
}
this.$.selector.select(item);
+ this.updateSizeForItem(item);
},
/**
@@ -1078,6 +1115,7 @@
model[this.selectedAs] = false;
}
this.$.selector.deselect(item);
+ this.updateSizeForItem(item);
},
/**
@@ -1123,20 +1161,15 @@
* it will remove the listener otherwise.
*/
_selectionEnabledChanged: function(selectionEnabled) {
- if (selectionEnabled) {
- this.listen(this, 'tap', '_selectionHandler');
- this.listen(this, 'keypress', '_selectionHandler');
- } else {
- this.unlisten(this, 'tap', '_selectionHandler');
- this.unlisten(this, 'keypress', '_selectionHandler');
- }
+ var handler = selectionEnabled ? this.listen : this.unlisten;
+ handler.call(this, this, 'tap', '_selectionHandler');
},
/**
* Select an item from an event object.
*/
_selectionHandler: function(e) {
- if (e.type !== 'keypress' || e.keyCode === 13) {
+ if (this.selectionEnabled) {
var model = this.modelForElement(e.target);
if (model) {
this.toggleSelectionForItem(model[this.as]);
@@ -1164,7 +1197,136 @@
this._updateMetrics([pidx]);
this._positionItems();
}
+ },
+
+ _isIndexRendered: function(idx) {
+ return idx >= this._virtualStart && idx <= this._virtualEnd;
+ },
+
+ _getPhysicalItemForIndex: function(idx, force) {
+ if (!this._collection) {
+ return null;
+ }
+ if (!this._isIndexRendered(idx)) {
+ if (force) {
+ this.scrollToIndex(idx);
+ return this._getPhysicalItemForIndex(idx, false);
+ }
+ return null;
+ }
+ var item = this._getNormalizedItem(idx);
+ var physicalItem = this._physicalItems[this._physicalIndexForKey[this._collection.getKey(item)]];
+
+ return physicalItem || null;
+ },
+
+ _focusPhysicalItem: function(idx) {
+ this._restoreFocusedItem();
+
+ var physicalItem = this._getPhysicalItemForIndex(idx, true);
+ if (!physicalItem) {
+ return;
+ }
+ var SECRET = ~(Math.random() * 100);
+ var model = physicalItem._templateInstance;
+ var focusable;
+
+ model.tabIndex = SECRET;
+ // the focusable element could be the entire physical item
+ if (physicalItem.tabIndex === SECRET) {
+ focusable = physicalItem;
+ }
+ // the focusable element could be somewhere within the physical item
+ if (!focusable) {
+ focusable = Polymer.dom(physicalItem).querySelector('[tabindex="' + SECRET + '"]');
+ }
+ // restore the tab index
+ model.tabIndex = 0;
+ focusable && focusable.focus();
+ },
+
+ _restoreFocusedItem: function() {
+ if (!this._offscreenFocusedItem) {
+ return;
+ }
+ var item = this._getNormalizedItem(this._focusedIndex);
+ var pidx = this._physicalIndexForKey[this._collection.getKey(item)];
+
+ if (pidx !== undefined) {
+ this.translate3d(0, HIDDEN_Y, 0, this._physicalItems[pidx]);
+ this._physicalItems[pidx] = this._offscreenFocusedItem;
+ }
+ this._offscreenFocusedItem = null;
+ },
+
+ _removeFocusedItem: function() {
+ if (!this._offscreenFocusedItem) {
+ return;
+ }
+ Polymer.dom(this).removeChild(this._offscreenFocusedItem);
+ this._offscreenFocusedItem = null;
+ this._focusBackfillItem = null;
+ },
+
+ _createFocusBackfillItem: function() {
+ if (this._offscreenFocusedItem) {
+ return;
+ }
+ var item = this._getNormalizedItem(this._focusedIndex);
+ var pidx = this._physicalIndexForKey[this._collection.getKey(item)];
+
+ this._offscreenFocusedItem = this._physicalItems[pidx];
+ this.translate3d(0, HIDDEN_Y, 0, this._offscreenFocusedItem);
+
+ if (!this._focusBackfillItem) {
+ var stampedTemplate = this.stamp(null);
+ this._focusBackfillItem = stampedTemplate.root.querySelector('*');
+ Polymer.dom(this).appendChild(stampedTemplate.root);
+ }
+ this._physicalItems[pidx] = this._focusBackfillItem;
+ },
+
+ _didFocus: function(e) {
+ var targetModel = this.modelForElement(e.target);
+ var fidx = this._focusedIndex;
+
+ if (!targetModel) {
+ return;
+ }
+ this._restoreFocusedItem();
+
+ if (this.modelForElement(this._offscreenFocusedItem) === targetModel) {
+ this.scrollToIndex(fidx);
+ } else {
+ // restore tabIndex for the currently focused item
+ this._getModelFromItem(this._getNormalizedItem(fidx)).tabIndex = -1;
+ // set the tabIndex for the next focused item
+ targetModel.tabIndex = 0;
+ fidx = /** @type {{index: number}} */(targetModel).index;
+ this._focusedIndex = fidx;
+ // bring the item into view
+ if (fidx < this.firstVisibleIndex || fidx > this.lastVisibleIndex) {
+ this.scrollToIndex(fidx);
+ } else {
+ this._update();
+ }
+ }
+ },
+
+ _didMoveUp: function() {
+ this._focusPhysicalItem(Math.max(0, this._focusedIndex - 1));
+ },
+
+ _didMoveDown: function() {
+ this._focusPhysicalItem(Math.min(this._virtualCount, this._focusedIndex + 1));
+ },
+
+ _didEnter: function(e) {
+ // focus the currently focused physical item
+ this._focusPhysicalItem(this._focusedIndex);
+ // toggle selection
+ this._selectionHandler(/** @type {{keyboardEvent: Event}} */(e.detail).keyboardEvent);
}
});
-})();
+})();

Powered by Google App Engine
This is Rietveld 408576698