OLD | NEW |
| (Empty) |
1 | |
2 | |
3 /** @polymerBehavior */ | |
4 Polymer.IronSelectableBehavior = { | |
5 | |
6 properties: { | |
7 | |
8 /** | |
9 * If you want to use the attribute value of an element for `selected` ins
tead of the index, | |
10 * set this to the name of the attribute. | |
11 * | |
12 * @attribute attrForSelected | |
13 * @type {string} | |
14 */ | |
15 attrForSelected: { | |
16 type: String, | |
17 value: null | |
18 }, | |
19 | |
20 /** | |
21 * Gets or sets the selected element. The default is to use the index of t
he item. | |
22 * | |
23 * @attribute selected | |
24 * @type {string} | |
25 */ | |
26 selected: { | |
27 type: String, | |
28 notify: true | |
29 }, | |
30 | |
31 /** | |
32 * Returns the currently selected item. | |
33 * | |
34 * @attribute selectedItem | |
35 * @type {Object} | |
36 */ | |
37 selectedItem: { | |
38 type: Object, | |
39 readOnly: true, | |
40 notify: true | |
41 }, | |
42 | |
43 /** | |
44 * The event that fires from items when they are selected. Selectable | |
45 * will listen for this event from items and update the selection state. | |
46 * Set to empty string to listen to no events. | |
47 * | |
48 * @attribute activateEvent | |
49 * @type {string} | |
50 * @default 'tap' | |
51 */ | |
52 activateEvent: { | |
53 type: String, | |
54 value: 'tap', | |
55 observer: '_activateEventChanged' | |
56 }, | |
57 | |
58 /** | |
59 * This is a CSS selector sting. If this is set, only items that matches
the CSS selector | |
60 * are selectable. | |
61 * | |
62 * @attribute selectable | |
63 * @type {string} | |
64 */ | |
65 selectable: String, | |
66 | |
67 /** | |
68 * The class to set on elements when selected. | |
69 * | |
70 * @attribute selectedClass | |
71 * @type {string} | |
72 */ | |
73 selectedClass: { | |
74 type: String, | |
75 value: 'iron-selected' | |
76 }, | |
77 | |
78 /** | |
79 * The attribute to set on elements when selected. | |
80 * | |
81 * @attribute selectedAttribute | |
82 * @type {string} | |
83 */ | |
84 selectedAttribute: { | |
85 type: String, | |
86 value: null | |
87 } | |
88 | |
89 }, | |
90 | |
91 observers: [ | |
92 '_updateSelected(attrForSelected, selected)' | |
93 ], | |
94 | |
95 excludedLocalNames: { | |
96 'template': 1 | |
97 }, | |
98 | |
99 created: function() { | |
100 this._bindFilterItem = this._filterItem.bind(this); | |
101 this._selection = new Polymer.IronSelection(this._applySelection.bind(this
)); | |
102 }, | |
103 | |
104 attached: function() { | |
105 this._observer = this._observeItems(this); | |
106 this._contentObserver = this._observeContent(this); | |
107 }, | |
108 | |
109 detached: function() { | |
110 if (this._observer) { | |
111 this._observer.disconnect(); | |
112 } | |
113 if (this._contentObserver) { | |
114 this._contentObserver.disconnect(); | |
115 } | |
116 this._removeListener(this.activateEvent); | |
117 }, | |
118 | |
119 /** | |
120 * Returns an array of selectable items. | |
121 * | |
122 * @property items | |
123 * @type Array | |
124 */ | |
125 get items() { | |
126 var nodes = Polymer.dom(this).queryDistributedElements(this.selectable ||
'*'); | |
127 return Array.prototype.filter.call(nodes, this._bindFilterItem); | |
128 }, | |
129 | |
130 /** | |
131 * Returns the index of the given item. | |
132 * | |
133 * @method indexOf | |
134 * @param {Object} item | |
135 * @returns Returns the index of the item | |
136 */ | |
137 indexOf: function(item) { | |
138 return this.items.indexOf(item); | |
139 }, | |
140 | |
141 /** | |
142 * Selects the given value. | |
143 * | |
144 * @method select | |
145 * @param {string} value the value to select. | |
146 */ | |
147 select: function(value) { | |
148 this.selected = value; | |
149 }, | |
150 | |
151 /** | |
152 * Selects the previous item. | |
153 * | |
154 * @method selectPrevious | |
155 */ | |
156 selectPrevious: function() { | |
157 var length = this.items.length; | |
158 var index = (Number(this._valueToIndex(this.selected)) - 1 + length) % len
gth; | |
159 this.selected = this._indexToValue(index); | |
160 }, | |
161 | |
162 /** | |
163 * Selects the next item. | |
164 * | |
165 * @method selectNext | |
166 */ | |
167 selectNext: function() { | |
168 var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.l
ength; | |
169 this.selected = this._indexToValue(index); | |
170 }, | |
171 | |
172 _addListener: function(eventName) { | |
173 this.listen(this, eventName, '_activateHandler'); | |
174 }, | |
175 | |
176 _removeListener: function(eventName) { | |
177 // There is no unlisten yet... | |
178 // https://github.com/Polymer/polymer/issues/1639 | |
179 //this.removeEventListener(eventName, this._bindActivateHandler); | |
180 }, | |
181 | |
182 _activateEventChanged: function(eventName, old) { | |
183 this._removeListener(old); | |
184 this._addListener(eventName); | |
185 }, | |
186 | |
187 _updateSelected: function() { | |
188 this._selectSelected(this.selected); | |
189 }, | |
190 | |
191 _selectSelected: function(selected) { | |
192 this._selection.select(this._valueToItem(this.selected)); | |
193 }, | |
194 | |
195 _filterItem: function(node) { | |
196 return !this.excludedLocalNames[node.localName]; | |
197 }, | |
198 | |
199 _valueToItem: function(value) { | |
200 return (value == null) ? null : this.items[this._valueToIndex(value)]; | |
201 }, | |
202 | |
203 _valueToIndex: function(value) { | |
204 if (this.attrForSelected) { | |
205 for (var i = 0, item; item = this.items[i]; i++) { | |
206 if (this._valueForItem(item) == value) { | |
207 return i; | |
208 } | |
209 } | |
210 } else { | |
211 return Number(value); | |
212 } | |
213 }, | |
214 | |
215 _indexToValue: function(index) { | |
216 if (this.attrForSelected) { | |
217 var item = this.items[index]; | |
218 if (item) { | |
219 return this._valueForItem(item); | |
220 } | |
221 } else { | |
222 return index; | |
223 } | |
224 }, | |
225 | |
226 _valueForItem: function(item) { | |
227 return item[this.attrForSelected] || item.getAttribute(this.attrForSelecte
d); | |
228 }, | |
229 | |
230 _applySelection: function(item, isSelected) { | |
231 if (this.selectedClass) { | |
232 this.toggleClass(this.selectedClass, isSelected, item); | |
233 } | |
234 if (this.selectedAttribute) { | |
235 this.toggleAttribute(this.selectedAttribute, isSelected, item); | |
236 } | |
237 this._selectionChange(); | |
238 this.fire('iron-' + (isSelected ? 'select' : 'deselect'), {item: item}); | |
239 }, | |
240 | |
241 _selectionChange: function() { | |
242 this._setSelectedItem(this._selection.get()); | |
243 }, | |
244 | |
245 // observe content changes under the given node. | |
246 _observeContent: function(node) { | |
247 var content = node.querySelector('content'); | |
248 if (content && content.parentElement === node) { | |
249 return this._observeItems(node.domHost); | |
250 } | |
251 }, | |
252 | |
253 // observe items change under the given node. | |
254 _observeItems: function(node) { | |
255 var observer = new MutationObserver(function() { | |
256 if (this.selected != null) { | |
257 this._updateSelected(); | |
258 } | |
259 }.bind(this)); | |
260 observer.observe(node, { | |
261 childList: true, | |
262 subtree: true | |
263 }); | |
264 return observer; | |
265 }, | |
266 | |
267 _activateHandler: function(e) { | |
268 // TODO: remove this when https://github.com/Polymer/polymer/issues/1639 i
s fixed so we | |
269 // can just remove the old event listener. | |
270 if (e.type !== this.activateEvent) { | |
271 return; | |
272 } | |
273 var t = e.target; | |
274 var items = this.items; | |
275 while (t && t != this) { | |
276 var i = items.indexOf(t); | |
277 if (i >= 0) { | |
278 var value = this._indexToValue(i); | |
279 this._itemActivate(value, t); | |
280 return; | |
281 } | |
282 t = t.parentNode; | |
283 } | |
284 }, | |
285 | |
286 _itemActivate: function(value, item) { | |
287 if (!this.fire('iron-activate', | |
288 {selected: value, item: item}, {cancelable: true}).defaultPrevented) { | |
289 this.select(value); | |
290 } | |
291 } | |
292 | |
293 }; | |
294 | |
OLD | NEW |