This repository has been archived by the owner on Mar 13, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
core-list, a list element for displaying a lot of items.
- Loading branch information
0 parents
commit dac95e7
Showing
10 changed files
with
521 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
core-list | ||
============ | ||
|
||
A list for displaying a large number of items. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"name": "core-list", | ||
"private": true, | ||
"dependencies": { | ||
"polymer": "Polymer/polymer#master", | ||
"core-selection": "Polymer/core-selection#master" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
:host { | ||
display: block; | ||
overflow: auto; | ||
/*-webkit-overflow-scrolling: touch; | ||
-webkit-transform: translateZ(0); | ||
transform: translateZ(0);*/ | ||
} | ||
|
||
.core-list-viewport > * { | ||
overflow: hidden; | ||
} | ||
|
||
.core-list-viewport.horizontal { | ||
height: 100%; | ||
white-space: nowrap; | ||
} | ||
|
||
.core-list-viewport.horizontal > * { | ||
display: inline-block; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,317 @@ | ||
<!-- | ||
Copyright 2013 The Polymer Authors. All rights reserved. | ||
Use of this source code is governed by a BSD-style | ||
license that can be found in the LICENSE file. | ||
--> | ||
<!-- | ||
`core-list` displays a virtual, 'infinite' list. The template inside the | ||
`core-list` element represents the dom to create for each list item. The | ||
`data` property specifies an array of list item data. The `height` property | ||
represents the height of a list item. | ||
By default, the list supports selection via tapping. Styling the selection | ||
should be done via binding. The `selectedProperty` property is set on the | ||
list view data for each selected item. | ||
`core-list` manages a viewport of data based on the current scroll position. | ||
For performance reasons, not every item in the list is rendered at once. | ||
<core-list data="{{data}}" height="80"> | ||
<template repeat> | ||
<div class="{{ {selected: selected} | tokenList }}">List row: {{index}}</div> | ||
</template> | ||
</core-list> | ||
@group Polymer Core Elements | ||
@element core-list | ||
--> | ||
<link rel="import" href="../polymer/polymer.html"> | ||
<link rel="import" href="../core-selection/core-selection.html"> | ||
|
||
<polymer-element name="core-list" attributes="height data multi" on-tap="{{tapHandler}}" on-scroll="{{scrollHandler}}"> | ||
<template> | ||
<core-selection id="selection" multi="{{multi}}" on-core-select="{{selectedHandler}}"></core-selection> | ||
<link rel="stylesheet" href="core-list.css"> | ||
<div id="viewport" class="core-list-viewport"><content></content></div> | ||
</template> | ||
<script> | ||
(function() { | ||
|
||
Polymer('core-list', { | ||
/** | ||
* Fired when an item element is tapped. | ||
* | ||
* @event core-activate | ||
* @param {Object} detail | ||
* @param {Object} detail.item the item element | ||
*/ | ||
|
||
/** | ||
* | ||
* An array of source data for the list to display. | ||
* | ||
* @attribute data | ||
* @type array | ||
* @default null | ||
*/ | ||
data: null, | ||
|
||
/** | ||
* | ||
* The height of a list item. `core-list` currently supports only fixed-height | ||
* list items. This height must be specified via the height property. | ||
* | ||
* @attribute height | ||
* @type number | ||
* @default 80 | ||
*/ | ||
height: 80, | ||
|
||
/** | ||
* | ||
* The number of extra items rendered above the minimum set required to | ||
* fill the list's height. | ||
* | ||
* @attribute extraItems | ||
* @type number | ||
* @default 30 | ||
*/ | ||
extraItems: 30, | ||
|
||
/** | ||
* | ||
* The property set on the list view data to represent selection state. | ||
* This should set so that it does not conflict with other data properties. | ||
* Note, selection data is not stored on the data in given in the data property. | ||
* | ||
* @attribute selectedProperty | ||
* @type string | ||
* @default 'selected' | ||
*/ | ||
selectedProperty: 'selected', | ||
|
||
/** | ||
* | ||
* Set to true to support multiple selection. | ||
* | ||
* @attribute multi | ||
* @type boolean | ||
* @default false | ||
*/ | ||
multi: false, | ||
|
||
observe: { | ||
'data template': 'initialize' | ||
}, | ||
|
||
attached: function() { | ||
this.template = this.querySelector('template'); | ||
}, | ||
|
||
initialize: function() { | ||
if (!this.data || !this.template) { | ||
return; | ||
} | ||
this.initializeViewport(); | ||
this.initalizeData(); | ||
this.onMutation(this, this.initializeItems); | ||
}, | ||
|
||
initializeViewport: function() { | ||
this.$.viewport.style.height = this.height * this.data.length + 'px'; | ||
this._visibleCount = Math.ceil(this.offsetHeight / this.height); | ||
this._physicalCount = this._visibleCount + this.extraItems; | ||
this._physicalHeight = this.height * this._physicalCount; | ||
}, | ||
|
||
initalizeData: function() { | ||
var exampleDatum = this.data[0]; | ||
this._propertyNames = Object.getOwnPropertyNames(exampleDatum); | ||
this.selectedData = []; | ||
this._physicalData = new Array(this._physicalCount); | ||
for (var i = 0; i < this._physicalCount; ++i) { | ||
this._physicalData[i] = {}; | ||
this.updateItem(i, i); | ||
} | ||
this.template.model = this._physicalData; | ||
}, | ||
|
||
initializeItems: function() { | ||
this._physicalItems = new Array(this._physicalCount); | ||
for (var i = 0, item = this.template.nextElementSibling; | ||
item && i < this._physicalCount; | ||
++i, item = item.nextElementSibling) { | ||
this._physicalItems[i] = item; | ||
item._transformValue = 0; | ||
} | ||
}, | ||
|
||
updateItem: function(virtualIndex, physicalIndex) { | ||
var virtualDatum = this.data[virtualIndex]; | ||
var physicalDatum = this._physicalData[physicalIndex]; | ||
for (var i = 0; i < this._propertyNames.length; ++i) { | ||
var propertyName = this._propertyNames[i]; | ||
physicalDatum[propertyName] = virtualDatum[propertyName]; | ||
physicalDatum._physicalIndex = physicalIndex; | ||
physicalDatum._virtualIndex = virtualIndex; | ||
} | ||
if (this.selectedProperty) { | ||
physicalDatum[this.selectedProperty] = this.selectedData[virtualIndex]; | ||
} | ||
}, | ||
|
||
scrollHandler: function() { | ||
this.refresh(false); | ||
}, | ||
|
||
/** | ||
* Refresh the list at the current scroll position. | ||
* | ||
* @method refresh | ||
*/ | ||
refresh: function(force) { | ||
var firstVisibleIndex = Math.floor(this.scrollTop / this.height); | ||
var visibleMidpoint = firstVisibleIndex + this._visibleCount / 2; | ||
|
||
var firstReifiedIndex = Math.max(0, Math.floor(visibleMidpoint - | ||
this._physicalCount / 2)); | ||
firstReifiedIndex = Math.min(firstReifiedIndex, this.data.length - | ||
this._physicalCount); | ||
|
||
var firstPhysicalIndex = firstReifiedIndex % this._physicalCount; | ||
var baseVirtualIndex = firstReifiedIndex - firstPhysicalIndex; | ||
|
||
var baseTransformValue = Math.floor(this.height * baseVirtualIndex); | ||
var nextTransformValue = Math.floor(baseTransformValue + | ||
this._physicalHeight); | ||
|
||
var baseTransformString = 'translate3d(0,' + baseTransformValue + 'px,0)'; | ||
var nextTransformString = 'translate3d(0,' + nextTransformValue + 'px,0)'; | ||
|
||
for (var i = 0; i < firstPhysicalIndex; ++i) { | ||
var item = this._physicalItems[i]; | ||
if (force || item._transformValue != nextTransformValue) { | ||
this.updateItem(baseVirtualIndex + this._physicalCount + i, i); | ||
setTransform(item, nextTransformString, nextTransformValue); | ||
} | ||
} | ||
for (var i = firstPhysicalIndex; i < this._physicalCount; ++i) { | ||
var item = this._physicalItems[i]; | ||
if (force || item._transformValue != baseTransformValue) { | ||
this.updateItem(baseVirtualIndex + i, i); | ||
setTransform(item, baseTransformString, baseTransformValue); | ||
} | ||
} | ||
}, | ||
|
||
// list selection | ||
tapHandler: function(e) { | ||
if (e.target === this) { | ||
return; | ||
} | ||
var n = e.target; | ||
var model = n.templateInstance && n.templateInstance.model; | ||
if (model) { | ||
var vi = model._virtualIndex, pi = model._physicalIndex; | ||
var data = this.data[vi], item = this._physicalItems[pi]; | ||
this.$.selection.select(data); | ||
this.asyncFire('core-activate', {data: data, item: item}); | ||
} | ||
}, | ||
|
||
selectedHandler: function(e, detail) { | ||
if (this.selectedProperty) { | ||
var i$ = this.indexesForData(detail.item); | ||
// TODO(sorvell): we should be relying on selection to store the | ||
// selected data but we want to optimize for lookup. | ||
this.selectedData[i$.virtual] = detail.isSelected; | ||
if (i$.physical >= 0) { | ||
this.updateItem(i$.virtual, i$.physical); | ||
} | ||
} | ||
}, | ||
|
||
/** | ||
* Select the list item at the given index. | ||
* | ||
* @method selectItem | ||
* @param {number} index | ||
*/ | ||
selectItem: function(index) { | ||
var data = this.data[index]; | ||
if (data) { | ||
this.$.selection.select(data); | ||
} | ||
}, | ||
|
||
/** | ||
* Set the selected state of the list item at the given index. | ||
* | ||
* @method setItemSelected | ||
* @param {number} index | ||
* @param {boolean} isSelected | ||
*/ | ||
setItemSelected: function(index, isSelected) { | ||
var data = this.data[index]; | ||
if (data) { | ||
this.$.selection.setItemSelected(data, isSelected); | ||
} | ||
}, | ||
|
||
indexesForData: function(data) { | ||
var virtual = this.data.indexOf(data); | ||
var physical = this.virtualToPhysicalIndex(virtual); | ||
return { virtual: virtual, physical: physical }; | ||
}, | ||
|
||
virtualToPhysicalIndex: function(index) { | ||
for (var i=0, l=this._physicalData.length; i<l; i++) { | ||
if (this._physicalData[i]._virtualIndex === index) { | ||
return i; | ||
} | ||
} | ||
return -1; | ||
}, | ||
|
||
get selection() { | ||
return this.$.selection.getSelection(); | ||
}, | ||
|
||
selectedChanged: function() { | ||
this.$.selection.select(this.selected); | ||
}, | ||
|
||
clearSelection: function() { | ||
if (this.multi) { | ||
var s$ = this.selection; | ||
for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) { | ||
this.$.selection.setItemSelected(s, false); | ||
} | ||
} else { | ||
this.$.selection.setItemSelected(this.selection, false); | ||
} | ||
this.$.selection.clear(); | ||
}, | ||
|
||
scrollToItem: function(index) { | ||
this.scrollTop = index * this.height; | ||
} | ||
|
||
}); | ||
|
||
// determine proper transform mechanizm | ||
if (document.documentElement.style.transform !== undefined) { | ||
function setTransform(element, string, value) { | ||
element.style.transform = string; | ||
element._transformValue = value; | ||
} | ||
} else { | ||
function setTransform(element, string, value) { | ||
element.style.webkitTransform = string; | ||
element._transformValue = value; | ||
} | ||
} | ||
|
||
})(); | ||
</script> | ||
</polymer-element> |
Oops, something went wrong.