OLD | NEW |
1 <!-- | 1 <!-- |
2 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | 2 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |
3 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt | 3 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt |
4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | 4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
5 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt | 5 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt |
6 Code distributed by Google as part of the polymer project is also | 6 Code distributed by Google as part of the polymer project is also |
7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt | 7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt |
8 --> | 8 --> |
9 | 9 |
10 <link rel="import" href="../polymer/polymer.html"> | 10 <link rel="import" href="../polymer/polymer.html"> |
11 <link rel="import" href="iron-selection.html"> | 11 <link rel="import" href="iron-selection.html"> |
12 | 12 |
13 <script> | 13 <script> |
14 | 14 |
15 /** @polymerBehavior */ | 15 /** @polymerBehavior */ |
16 Polymer.IronSelectableBehavior = { | 16 Polymer.IronSelectableBehavior = { |
17 | 17 |
| 18 /** |
| 19 * Fired when iron-selector is activated (selected or deselected). |
| 20 * It is fired before the selected items are changed. |
| 21 * Cancel the event to abort selection. |
| 22 * |
| 23 * @event iron-activate |
| 24 */ |
| 25 |
| 26 /** |
| 27 * Fired when an item is selected |
| 28 * |
| 29 * @event iron-select |
| 30 */ |
| 31 |
| 32 /** |
| 33 * Fired when an item is deselected |
| 34 * |
| 35 * @event iron-deselect |
| 36 */ |
| 37 |
| 38 /** |
| 39 * Fired when the list of selectable items changes (e.g., items are |
| 40 * added or removed). The detail of the event is a list of mutation |
| 41 * records that describe what changed. |
| 42 * |
| 43 * @event iron-items-changed |
| 44 */ |
| 45 |
18 properties: { | 46 properties: { |
19 | 47 |
20 /** | 48 /** |
21 * If you want to use the attribute value of an element for `selected` ins
tead of the index, | 49 * If you want to use the attribute value of an element for `selected` ins
tead of the index, |
22 * set this to the name of the attribute. | 50 * set this to the name of the attribute. |
23 * | |
24 * @attribute attrForSelected | |
25 * @type {string} | |
26 */ | 51 */ |
27 attrForSelected: { | 52 attrForSelected: { |
28 type: String, | 53 type: String, |
29 value: null | 54 value: null |
30 }, | 55 }, |
31 | 56 |
32 /** | 57 /** |
33 * Gets or sets the selected element. The default is to use the index of t
he item. | 58 * Gets or sets the selected element. The default is to use the index of t
he item. |
34 * | |
35 * @attribute selected | |
36 * @type {string} | |
37 */ | 59 */ |
38 selected: { | 60 selected: { |
39 type: String, | 61 type: String, |
40 notify: true | 62 notify: true |
41 }, | 63 }, |
42 | 64 |
43 /** | 65 /** |
44 * Returns the currently selected item. | 66 * Returns the currently selected item. |
45 * | 67 * |
46 * @attribute selectedItem | 68 * @type {?Object} |
47 * @type {Object} | |
48 */ | 69 */ |
49 selectedItem: { | 70 selectedItem: { |
50 type: Object, | 71 type: Object, |
51 readOnly: true, | 72 readOnly: true, |
52 notify: true | 73 notify: true |
53 }, | 74 }, |
54 | 75 |
55 /** | 76 /** |
56 * The event that fires from items when they are selected. Selectable | 77 * The event that fires from items when they are selected. Selectable |
57 * will listen for this event from items and update the selection state. | 78 * will listen for this event from items and update the selection state. |
58 * Set to empty string to listen to no events. | 79 * Set to empty string to listen to no events. |
59 * | |
60 * @attribute activateEvent | |
61 * @type {string} | |
62 * @default 'tap' | |
63 */ | 80 */ |
64 activateEvent: { | 81 activateEvent: { |
65 type: String, | 82 type: String, |
66 value: 'tap', | 83 value: 'tap', |
67 observer: '_activateEventChanged' | 84 observer: '_activateEventChanged' |
68 }, | 85 }, |
69 | 86 |
70 /** | 87 /** |
71 * This is a CSS selector sting. If this is set, only items that matches
the CSS selector | 88 * This is a CSS selector string. If this is set, only items that match t
he CSS selector |
72 * are selectable. | 89 * are selectable. |
73 * | |
74 * @attribute selectable | |
75 * @type {string} | |
76 */ | 90 */ |
77 selectable: String, | 91 selectable: String, |
78 | 92 |
79 /** | 93 /** |
80 * The class to set on elements when selected. | 94 * The class to set on elements when selected. |
81 * | |
82 * @attribute selectedClass | |
83 * @type {string} | |
84 */ | 95 */ |
85 selectedClass: { | 96 selectedClass: { |
86 type: String, | 97 type: String, |
87 value: 'iron-selected' | 98 value: 'iron-selected' |
88 }, | 99 }, |
89 | 100 |
90 /** | 101 /** |
91 * The attribute to set on elements when selected. | 102 * The attribute to set on elements when selected. |
92 * | |
93 * @attribute selectedAttribute | |
94 * @type {string} | |
95 */ | 103 */ |
96 selectedAttribute: { | 104 selectedAttribute: { |
97 type: String, | 105 type: String, |
98 value: null | 106 value: null |
| 107 }, |
| 108 |
| 109 /** |
| 110 * The list of items from which a selection can be made. |
| 111 */ |
| 112 items: { |
| 113 type: Array, |
| 114 readOnly: true, |
| 115 value: function() { |
| 116 return []; |
| 117 } |
| 118 }, |
| 119 |
| 120 /** |
| 121 * The set of excluded elements where the key is the `localName` |
| 122 * of the element that will be ignored from the item list. |
| 123 * |
| 124 * @default {template: 1} |
| 125 */ |
| 126 _excludedLocalNames: { |
| 127 type: Object, |
| 128 value: function() { |
| 129 return { |
| 130 'template': 1 |
| 131 }; |
| 132 } |
99 } | 133 } |
100 | |
101 }, | 134 }, |
102 | 135 |
103 observers: [ | 136 observers: [ |
104 '_updateSelected(attrForSelected, selected)' | 137 '_updateSelected(attrForSelected, selected)' |
105 ], | 138 ], |
106 | 139 |
107 excludedLocalNames: { | |
108 'template': 1 | |
109 }, | |
110 | |
111 created: function() { | 140 created: function() { |
112 this._bindFilterItem = this._filterItem.bind(this); | 141 this._bindFilterItem = this._filterItem.bind(this); |
113 this._selection = new Polymer.IronSelection(this._applySelection.bind(this
)); | 142 this._selection = new Polymer.IronSelection(this._applySelection.bind(this
)); |
114 }, | 143 }, |
115 | 144 |
116 attached: function() { | 145 attached: function() { |
117 this._observer = this._observeItems(this); | 146 this._observer = this._observeItems(this); |
118 this._contentObserver = this._observeContent(this); | 147 this._updateItems(); |
| 148 if (!this._shouldUpdateSelection) { |
| 149 this._updateSelected(this.attrForSelected,this.selected) |
| 150 } |
| 151 this._addListener(this.activateEvent); |
119 }, | 152 }, |
120 | 153 |
121 detached: function() { | 154 detached: function() { |
122 if (this._observer) { | 155 if (this._observer) { |
123 this._observer.disconnect(); | 156 Polymer.dom(this).unobserveNodes(this._observer); |
124 } | |
125 if (this._contentObserver) { | |
126 this._contentObserver.disconnect(); | |
127 } | 157 } |
128 this._removeListener(this.activateEvent); | 158 this._removeListener(this.activateEvent); |
129 }, | 159 }, |
130 | 160 |
131 /** | 161 /** |
132 * Returns an array of selectable items. | |
133 * | |
134 * @property items | |
135 * @type Array | |
136 */ | |
137 get items() { | |
138 var nodes = Polymer.dom(this).queryDistributedElements(this.selectable ||
'*'); | |
139 return Array.prototype.filter.call(nodes, this._bindFilterItem); | |
140 }, | |
141 | |
142 /** | |
143 * Returns the index of the given item. | 162 * Returns the index of the given item. |
144 * | 163 * |
145 * @method indexOf | 164 * @method indexOf |
146 * @param {Object} item | 165 * @param {Object} item |
147 * @returns Returns the index of the item | 166 * @returns Returns the index of the item |
148 */ | 167 */ |
149 indexOf: function(item) { | 168 indexOf: function(item) { |
150 return this.items.indexOf(item); | 169 return this.items.indexOf(item); |
151 }, | 170 }, |
152 | 171 |
(...skipping 21 matching lines...) Expand all Loading... |
174 /** | 193 /** |
175 * Selects the next item. | 194 * Selects the next item. |
176 * | 195 * |
177 * @method selectNext | 196 * @method selectNext |
178 */ | 197 */ |
179 selectNext: function() { | 198 selectNext: function() { |
180 var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.l
ength; | 199 var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.l
ength; |
181 this.selected = this._indexToValue(index); | 200 this.selected = this._indexToValue(index); |
182 }, | 201 }, |
183 | 202 |
| 203 get _shouldUpdateSelection() { |
| 204 return this.selected != null; |
| 205 }, |
| 206 |
184 _addListener: function(eventName) { | 207 _addListener: function(eventName) { |
185 this.listen(this, eventName, '_activateHandler'); | 208 this.listen(this, eventName, '_activateHandler'); |
186 }, | 209 }, |
187 | 210 |
188 _removeListener: function(eventName) { | 211 _removeListener: function(eventName) { |
189 // There is no unlisten yet... | 212 this.unlisten(this, eventName, '_activateHandler'); |
190 // https://github.com/Polymer/polymer/issues/1639 | |
191 //this.removeEventListener(eventName, this._bindActivateHandler); | |
192 }, | 213 }, |
193 | 214 |
194 _activateEventChanged: function(eventName, old) { | 215 _activateEventChanged: function(eventName, old) { |
195 this._removeListener(old); | 216 this._removeListener(old); |
196 this._addListener(eventName); | 217 this._addListener(eventName); |
197 }, | 218 }, |
198 | 219 |
| 220 _updateItems: function() { |
| 221 var nodes = Polymer.dom(this).queryDistributedElements(this.selectable ||
'*'); |
| 222 nodes = Array.prototype.filter.call(nodes, this._bindFilterItem); |
| 223 this._setItems(nodes); |
| 224 }, |
| 225 |
199 _updateSelected: function() { | 226 _updateSelected: function() { |
200 this._selectSelected(this.selected); | 227 this._selectSelected(this.selected); |
201 }, | 228 }, |
202 | 229 |
203 _selectSelected: function(selected) { | 230 _selectSelected: function(selected) { |
204 this._selection.select(this._valueToItem(this.selected)); | 231 this._selection.select(this._valueToItem(this.selected)); |
205 }, | 232 }, |
206 | 233 |
207 _filterItem: function(node) { | 234 _filterItem: function(node) { |
208 return !this.excludedLocalNames[node.localName]; | 235 return !this._excludedLocalNames[node.localName]; |
209 }, | 236 }, |
210 | 237 |
211 _valueToItem: function(value) { | 238 _valueToItem: function(value) { |
212 return (value == null) ? null : this.items[this._valueToIndex(value)]; | 239 return (value == null) ? null : this.items[this._valueToIndex(value)]; |
213 }, | 240 }, |
214 | 241 |
215 _valueToIndex: function(value) { | 242 _valueToIndex: function(value) { |
216 if (this.attrForSelected) { | 243 if (this.attrForSelected) { |
217 for (var i = 0, item; item = this.items[i]; i++) { | 244 for (var i = 0, item; item = this.items[i]; i++) { |
218 if (this._valueForItem(item) == value) { | 245 if (this._valueForItem(item) == value) { |
(...skipping 28 matching lines...) Expand all Loading... |
247 this.toggleAttribute(this.selectedAttribute, isSelected, item); | 274 this.toggleAttribute(this.selectedAttribute, isSelected, item); |
248 } | 275 } |
249 this._selectionChange(); | 276 this._selectionChange(); |
250 this.fire('iron-' + (isSelected ? 'select' : 'deselect'), {item: item}); | 277 this.fire('iron-' + (isSelected ? 'select' : 'deselect'), {item: item}); |
251 }, | 278 }, |
252 | 279 |
253 _selectionChange: function() { | 280 _selectionChange: function() { |
254 this._setSelectedItem(this._selection.get()); | 281 this._setSelectedItem(this._selection.get()); |
255 }, | 282 }, |
256 | 283 |
257 // observe content changes under the given node. | |
258 _observeContent: function(node) { | |
259 var content = node.querySelector('content'); | |
260 if (content && content.parentElement === node) { | |
261 return this._observeItems(node.domHost); | |
262 } | |
263 }, | |
264 | |
265 // observe items change under the given node. | 284 // observe items change under the given node. |
266 _observeItems: function(node) { | 285 _observeItems: function(node) { |
267 var observer = new MutationObserver(function() { | 286 return Polymer.dom(node).observeNodes(function(mutations) { |
268 if (this.selected != null) { | 287 // Let other interested parties know about the change so that |
| 288 // we don't have to recreate mutation observers everywher. |
| 289 this.fire('iron-items-changed', mutations, { |
| 290 bubbles: false, |
| 291 cancelable: false |
| 292 }); |
| 293 |
| 294 this._updateItems(); |
| 295 |
| 296 if (this._shouldUpdateSelection) { |
269 this._updateSelected(); | 297 this._updateSelected(); |
270 } | 298 } |
271 }.bind(this)); | |
272 observer.observe(node, { | |
273 childList: true, | |
274 subtree: true | |
275 }); | 299 }); |
276 return observer; | |
277 }, | 300 }, |
278 | 301 |
279 _activateHandler: function(e) { | 302 _activateHandler: function(e) { |
280 // TODO: remove this when https://github.com/Polymer/polymer/issues/1639 i
s fixed so we | |
281 // can just remove the old event listener. | |
282 if (e.type !== this.activateEvent) { | |
283 return; | |
284 } | |
285 var t = e.target; | 303 var t = e.target; |
286 var items = this.items; | 304 var items = this.items; |
287 while (t && t != this) { | 305 while (t && t != this) { |
288 var i = items.indexOf(t); | 306 var i = items.indexOf(t); |
289 if (i >= 0) { | 307 if (i >= 0) { |
290 var value = this._indexToValue(i); | 308 var value = this._indexToValue(i); |
291 this._itemActivate(value, t); | 309 this._itemActivate(value, t); |
292 return; | 310 return; |
293 } | 311 } |
294 t = t.parentNode; | 312 t = t.parentNode; |
295 } | 313 } |
296 }, | 314 }, |
297 | 315 |
298 _itemActivate: function(value, item) { | 316 _itemActivate: function(value, item) { |
299 if (!this.fire('iron-activate', | 317 if (!this.fire('iron-activate', |
300 {selected: value, item: item}, {cancelable: true}).defaultPrevented) { | 318 {selected: value, item: item}, {cancelable: true}).defaultPrevented) { |
301 this.select(value); | 319 this.select(value); |
302 } | 320 } |
303 } | 321 } |
304 | 322 |
305 }; | 323 }; |
306 | 324 |
307 </script> | 325 </script> |
OLD | NEW |