OLD | NEW |
1 | 1 |
2 | 2 |
3 (function() { | 3 (function() { |
4 | 4 |
5 // FIXME menu control should be refactored to a more general element | |
6 | |
7 Polymer({ | 5 Polymer({ |
8 | 6 |
9 is: 'paper-menu', | 7 is: 'paper-menu', |
10 | 8 |
11 enableCustomStyleProperties: true, | 9 enableCustomStyleProperties: true, |
12 | 10 |
13 mixins: [ | 11 behaviors: [ |
14 Polymer.Core.Selectable | 12 Polymer.IronMenuBehavior |
15 ], | 13 ] |
16 | |
17 properties: { | |
18 | |
19 /** | |
20 * Returns the currently focused item. | |
21 * | |
22 * @attribute focusedItem | |
23 * @type Object | |
24 */ | |
25 focusedItem: { | |
26 observer: 'focusedItemChanged', | |
27 readOnly: true, | |
28 type: Object | |
29 }, | |
30 | |
31 /** | |
32 * The attribute to use on menu items to look up the item title. Typing th
e first | |
33 * letter of an item when the menu is open focuses that item. If unset, `t
extContent` | |
34 * will be used. | |
35 * | |
36 * @attribute attrForItemTitle | |
37 * @type String | |
38 */ | |
39 attrForItemTitle: { | |
40 type: String | |
41 }, | |
42 | |
43 /***********************************************************************/ | |
44 /* Polymer.Core.Selectable */ | |
45 /***********************************************************************/ | |
46 | |
47 /** | |
48 * If you want to use the attribute value of an element for `selected` ins
tead of the index, | |
49 * set this to the name of the attribute. | |
50 * | |
51 * @attribute attrForSelected | |
52 * @type String | |
53 */ | |
54 attrForSelected: { | |
55 type: String | |
56 }, | |
57 | |
58 /** | |
59 * If true, multiple selections are allowed. | |
60 * | |
61 * @attribute multi | |
62 * @type Boolean | |
63 * @default false | |
64 */ | |
65 multi: { | |
66 observer: 'multiChanged', | |
67 type: Boolean, | |
68 value: false | |
69 }, | |
70 | |
71 /** | |
72 * Gets or sets the selected element. The default is to use the index of t
he item. In | |
73 * multi-selection this should be an array of values. | |
74 * | |
75 * @attribute selected | |
76 * @type String|Array | |
77 */ | |
78 selected: { | |
79 notify: true, | |
80 observer: 'selectedChanged', | |
81 type: String | |
82 }, | |
83 | |
84 /** | |
85 * Returns the currently selected item. In multi-selection this returns an
array of | |
86 * selected items. | |
87 * | |
88 * @attribute selectedItem | |
89 * @type Object|Array | |
90 */ | |
91 selectedItem: { | |
92 notify: true, | |
93 observer: 'selectedItemChanged', | |
94 readOnly: true, | |
95 type: Object | |
96 }, | |
97 | |
98 /** | |
99 * The event that would be fired from the item to indicate it is being sel
ected. Set this | |
100 * to empty string or null if you don't want to listen for any events. | |
101 * | |
102 * @attribute activateEvent | |
103 * @type String | |
104 * @default 'click' | |
105 */ | |
106 activateEvent: { | |
107 observer: 'activateEventChanged', | |
108 type: String, | |
109 value: 'click' | |
110 }, | |
111 | |
112 /** | |
113 * If this is set, only items with local name that matches the `selectable
` are selectable. | |
114 * | |
115 * @attribute selectable | |
116 * @type String | |
117 */ | |
118 selectable: { | |
119 type: String | |
120 } | |
121 | |
122 }, | |
123 | |
124 hostAttributes: { | |
125 'role': 'menu', | |
126 'tabindex': '0' | |
127 }, | |
128 | |
129 listeners: { | |
130 'focus': 'onFocus', | |
131 'keydown': 'onKeydown' | |
132 }, | |
133 | |
134 created: function() { | |
135 this._bindActivateHandler = this.activateHandler.bind(this); | |
136 }, | |
137 | |
138 attached: function() { | |
139 this.selectableAttached(); | |
140 }, | |
141 | |
142 detached: function() { | |
143 this.selectableDetached(); | |
144 this.removeListener(this.activateEvent); | |
145 }, | |
146 | |
147 addListener: function(eventName) { | |
148 if (eventName) { | |
149 this.addEventListener(eventName, this._bindActivateHandler); | |
150 } | |
151 }, | |
152 | |
153 removeListener: function(eventName) { | |
154 if (eventName) { | |
155 this.removeEventListener(eventName, this._bindActivateHandler); | |
156 } | |
157 }, | |
158 | |
159 activateEventChanged: function(eventName, old) { | |
160 this.removeListener(old); | |
161 this.addListener(eventName); | |
162 }, | |
163 | |
164 focusedItemChanged: function(focusedItem, old) { | |
165 old && old.setAttribute('tabindex', '-1'); | |
166 if (focusedItem) { | |
167 focusedItem.setAttribute('tabindex', '0'); | |
168 focusedItem.focus(); | |
169 } | |
170 }, | |
171 | |
172 multiChanged: function(multi) { | |
173 this.selection.multi = multi; | |
174 this.selectedChanged(this.selected); | |
175 }, | |
176 | |
177 selectedChanged: function(selected, old) { | |
178 this._selectedChanged(selected, old); | |
179 }, | |
180 | |
181 selectedItemChanged: function(selectedItem) { | |
182 this._setFocusedItem(Array.isArray(selectedItem) ? selectedItem[0] : selec
tedItem); | |
183 }, | |
184 | |
185 activateHandler: function(e) { | |
186 var t = e.target; | |
187 var items = this.items; | |
188 while (t && t != this) { | |
189 var i = items.indexOf(t); | |
190 if (i >= 0) { | |
191 if (t.hasAttribute('disabled')) { | |
192 return; | |
193 } | |
194 var value = this.indexToValue(i); | |
195 if (!this.fire('iron-activate', {selected: value, item: t}).defaultPre
vented) { | |
196 this.select(value); | |
197 } | |
198 return; | |
199 } | |
200 t = t.parentNode; | |
201 } | |
202 }, | |
203 | |
204 onFocus: function(event) { | |
205 // clear the cached focus item | |
206 this._setFocusedItem(null); | |
207 // focus the selected item when the menu receives focus, or the first item | |
208 // if no item is selected | |
209 var selectedItem = this.selectedItem; | |
210 selectedItem = Array.isArray(selectedItem) ? selectedItem[0] : selectedIte
m; | |
211 if (selectedItem) { | |
212 this._setFocusedItem(selectedItem); | |
213 } else { | |
214 this._setFocusedItem(this.items[0]); | |
215 } | |
216 }, | |
217 | |
218 onKeydown: function(event) { | |
219 // FIXME want to define these somewhere, core-a11y-keys? | |
220 var DOWN = 40; | |
221 var UP = 38; | |
222 var ESC = 27; | |
223 var ENTER = 13; | |
224 if (event.keyCode === DOWN) { | |
225 // up and down arrows moves the focus | |
226 this.focusNext(); | |
227 } else if (event.keyCode === UP) { | |
228 this.focusPrevious(); | |
229 } else if (event.keyCode === ESC) { | |
230 // esc blurs the control | |
231 this.focusedItem.blur(); | |
232 } else if (event.keyCode === ENTER) { | |
233 // enter activates the item | |
234 this.activateHandler(event); | |
235 } else { | |
236 // all other keys focus the menu item starting with that character | |
237 for (var i = 0, item; item = this.items[i]; i++) { | |
238 var attr = this.attrForItemTitle || 'textContent'; | |
239 var title = item[attr] || item.getAttribute(attr); | |
240 if (title && title.charAt(0).toLowerCase() === String.fromCharCode(eve
nt.keyCode).toLowerCase()) { | |
241 this._setFocusedItem(item); | |
242 break; | |
243 } | |
244 } | |
245 } | |
246 }, | |
247 | |
248 focusPrevious: function() { | |
249 var length = this.items.length; | |
250 var index = (Number(this.indexOf(this.focusedItem)) - 1 + length) % length
; | |
251 this._setFocusedItem(this.items[index]); | |
252 }, | |
253 | |
254 focusNext: function() { | |
255 var index = (Number(this.indexOf(this.focusedItem)) + 1) % this.items.leng
th; | |
256 this._setFocusedItem(this.items[index]); | |
257 } | |
258 | 14 |
259 }); | 15 }); |
260 | 16 |
261 })(); | 17 })(); |
262 | 18 |
OLD | NEW |