diff options
Diffstat (limited to 'catapult/third_party/polymer/components/iron-selector/iron-selectable.html')
-rw-r--r-- | catapult/third_party/polymer/components/iron-selector/iron-selectable.html | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/catapult/third_party/polymer/components/iron-selector/iron-selectable.html b/catapult/third_party/polymer/components/iron-selector/iron-selectable.html new file mode 100644 index 00000000..25cfe91e --- /dev/null +++ b/catapult/third_party/polymer/components/iron-selector/iron-selectable.html @@ -0,0 +1,388 @@ +<!-- +@license +Copyright (c) 2015 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +--> + +<link rel="import" href="../polymer/polymer.html"> +<link rel="import" href="iron-selection.html"> + +<script> + + /** @polymerBehavior */ + Polymer.IronSelectableBehavior = { + + /** + * Fired when iron-selector is activated (selected or deselected). + * It is fired before the selected items are changed. + * Cancel the event to abort selection. + * + * @event iron-activate + */ + + /** + * Fired when an item is selected + * + * @event iron-select + */ + + /** + * Fired when an item is deselected + * + * @event iron-deselect + */ + + /** + * Fired when the list of selectable items changes (e.g., items are + * added or removed). The detail of the event is a mutation record that + * describes what changed. + * + * @event iron-items-changed + */ + + properties: { + + /** + * If you want to use an attribute value or property of an element for + * `selected` instead of the index, set this to the name of the attribute + * or property. Hyphenated values are converted to camel case when used to + * look up the property of a selectable element. Camel cased values are + * *not* converted to hyphenated values for attribute lookup. It's + * recommended that you provide the hyphenated form of the name so that + * selection works in both cases. (Use `attr-or-property-name` instead of + * `attrOrPropertyName`.) + */ + attrForSelected: { + type: String, + value: null + }, + + /** + * Gets or sets the selected element. The default is to use the index of the item. + * @type {string|number} + */ + selected: { + type: String, + notify: true + }, + + /** + * Returns the currently selected item. + * + * @type {?Object} + */ + selectedItem: { + type: Object, + readOnly: true, + notify: true + }, + + /** + * The event that fires from items when they are selected. Selectable + * will listen for this event from items and update the selection state. + * Set to empty string to listen to no events. + */ + activateEvent: { + type: String, + value: 'tap', + observer: '_activateEventChanged' + }, + + /** + * This is a CSS selector string. If this is set, only items that match the CSS selector + * are selectable. + */ + selectable: String, + + /** + * The class to set on elements when selected. + */ + selectedClass: { + type: String, + value: 'iron-selected' + }, + + /** + * The attribute to set on elements when selected. + */ + selectedAttribute: { + type: String, + value: null + }, + + /** + * Default fallback if the selection based on selected with `attrForSelected` + * is not found. + */ + fallbackSelection: { + type: String, + value: null + }, + + /** + * The list of items from which a selection can be made. + */ + items: { + type: Array, + readOnly: true, + notify: true, + value: function() { + return []; + } + }, + + /** + * The set of excluded elements where the key is the `localName` + * of the element that will be ignored from the item list. + * + * @default {template: 1} + */ + _excludedLocalNames: { + type: Object, + value: function() { + return { + 'template': 1 + }; + } + } + }, + + observers: [ + '_updateAttrForSelected(attrForSelected)', + '_updateSelected(selected)', + '_checkFallback(fallbackSelection)' + ], + + created: function() { + this._bindFilterItem = this._filterItem.bind(this); + this._selection = new Polymer.IronSelection(this._applySelection.bind(this)); + }, + + attached: function() { + this._observer = this._observeItems(this); + this._updateItems(); + if (!this._shouldUpdateSelection) { + this._updateSelected(); + } + this._addListener(this.activateEvent); + }, + + detached: function() { + if (this._observer) { + Polymer.dom(this).unobserveNodes(this._observer); + } + this._removeListener(this.activateEvent); + }, + + /** + * Returns the index of the given item. + * + * @method indexOf + * @param {Object} item + * @returns Returns the index of the item + */ + indexOf: function(item) { + return this.items.indexOf(item); + }, + + /** + * Selects the given value. + * + * @method select + * @param {string|number} value the value to select. + */ + select: function(value) { + this.selected = value; + }, + + /** + * Selects the previous item. + * + * @method selectPrevious + */ + selectPrevious: function() { + var length = this.items.length; + var index = (Number(this._valueToIndex(this.selected)) - 1 + length) % length; + this.selected = this._indexToValue(index); + }, + + /** + * Selects the next item. + * + * @method selectNext + */ + selectNext: function() { + var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.length; + this.selected = this._indexToValue(index); + }, + + /** + * Selects the item at the given index. + * + * @method selectIndex + */ + selectIndex: function(index) { + this.select(this._indexToValue(index)); + }, + + /** + * Force a synchronous update of the `items` property. + * + * NOTE: Consider listening for the `iron-items-changed` event to respond to + * updates to the set of selectable items after updates to the DOM list and + * selection state have been made. + * + * WARNING: If you are using this method, you should probably consider an + * alternate approach. Synchronously querying for items is potentially + * slow for many use cases. The `items` property will update asynchronously + * on its own to reflect selectable items in the DOM. + */ + forceSynchronousItemUpdate: function() { + this._updateItems(); + }, + + get _shouldUpdateSelection() { + return this.selected != null; + }, + + _checkFallback: function() { + if (this._shouldUpdateSelection) { + this._updateSelected(); + } + }, + + _addListener: function(eventName) { + this.listen(this, eventName, '_activateHandler'); + }, + + _removeListener: function(eventName) { + this.unlisten(this, eventName, '_activateHandler'); + }, + + _activateEventChanged: function(eventName, old) { + this._removeListener(old); + this._addListener(eventName); + }, + + _updateItems: function() { + var nodes = Polymer.dom(this).queryDistributedElements(this.selectable || '*'); + nodes = Array.prototype.filter.call(nodes, this._bindFilterItem); + this._setItems(nodes); + }, + + _updateAttrForSelected: function() { + if (this._shouldUpdateSelection) { + this.selected = this._indexToValue(this.indexOf(this.selectedItem)); + } + }, + + _updateSelected: function() { + this._selectSelected(this.selected); + }, + + _selectSelected: function(selected) { + this._selection.select(this._valueToItem(this.selected)); + // Check for items, since this array is populated only when attached + // Since Number(0) is falsy, explicitly check for undefined + if (this.fallbackSelection && this.items.length && (this._selection.get() === undefined)) { + this.selected = this.fallbackSelection; + } + }, + + _filterItem: function(node) { + return !this._excludedLocalNames[node.localName]; + }, + + _valueToItem: function(value) { + return (value == null) ? null : this.items[this._valueToIndex(value)]; + }, + + _valueToIndex: function(value) { + if (this.attrForSelected) { + for (var i = 0, item; item = this.items[i]; i++) { + if (this._valueForItem(item) == value) { + return i; + } + } + } else { + return Number(value); + } + }, + + _indexToValue: function(index) { + if (this.attrForSelected) { + var item = this.items[index]; + if (item) { + return this._valueForItem(item); + } + } else { + return index; + } + }, + + _valueForItem: function(item) { + var propValue = item[Polymer.CaseMap.dashToCamelCase(this.attrForSelected)]; + return propValue != undefined ? propValue : item.getAttribute(this.attrForSelected); + }, + + _applySelection: function(item, isSelected) { + if (this.selectedClass) { + this.toggleClass(this.selectedClass, isSelected, item); + } + if (this.selectedAttribute) { + this.toggleAttribute(this.selectedAttribute, isSelected, item); + } + this._selectionChange(); + this.fire('iron-' + (isSelected ? 'select' : 'deselect'), {item: item}); + }, + + _selectionChange: function() { + this._setSelectedItem(this._selection.get()); + }, + + // observe items change under the given node. + _observeItems: function(node) { + return Polymer.dom(node).observeNodes(function(mutation) { + this._updateItems(); + + if (this._shouldUpdateSelection) { + this._updateSelected(); + } + + // Let other interested parties know about the change so that + // we don't have to recreate mutation observers everywhere. + this.fire('iron-items-changed', mutation, { + bubbles: false, + cancelable: false + }); + }); + }, + + _activateHandler: function(e) { + var t = e.target; + var items = this.items; + while (t && t != this) { + var i = items.indexOf(t); + if (i >= 0) { + var value = this._indexToValue(i); + this._itemActivate(value, t); + return; + } + t = t.parentNode; + } + }, + + _itemActivate: function(value, item) { + if (!this.fire('iron-activate', + {selected: value, item: item}, {cancelable: true}).defaultPrevented) { + this.select(value); + } + } + + }; + +</script> |