OLD | NEW |
1 <!-- | 1 <!-- |
2 @license | 2 @license |
3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | 3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |
4 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt | 4 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt |
5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | 5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
6 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt | 6 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt |
7 Code distributed by Google as part of the polymer project is also | 7 Code distributed by Google as part of the polymer project is also |
8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt | 8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt |
9 --> | 9 --> |
10 | 10 |
11 <link rel="import" href="../polymer/polymer.html"> | 11 <link rel="import" href="../polymer/polymer.html"> |
12 <link rel="import" href="../iron-selector/iron-selectable.html"> | 12 <link rel="import" href="../iron-menu-behavior/iron-menu-behavior.html"> |
13 <link rel="import" href="../paper-styles/paper-styles.html"> | 13 <link rel="import" href="../paper-styles/paper-styles.html"> |
14 | 14 |
15 <!-- | 15 <!-- |
16 @element paper-menu | 16 @element paper-menu |
17 --> | 17 --> |
18 | 18 |
19 <dom-module id="paper-menu"> | 19 <dom-module id="paper-menu"> |
20 | 20 |
21 <style> | 21 <style> |
22 | 22 |
23 :host { | 23 :host { |
| 24 display: block; |
24 padding: 8px 0; | 25 padding: 8px 0; |
25 | 26 |
26 background: var(--primary-background-color); | 27 background: var(--primary-background-color); |
27 color: var(--primary-text-color); | 28 color: var(--primary-text-color); |
28 | 29 |
29 mixin(--paper-menu); | 30 mixin(--paper-menu); |
30 } | 31 } |
31 | 32 |
32 :host > ::content > [disabled] { | 33 :host > ::content > [disabled] { |
33 color: var(--disabled-text-color); | 34 color: var(--disabled-text-color); |
34 } | 35 } |
35 | 36 |
36 :host > ::content > .iron-selected { | 37 :host > ::content > .iron-selected { |
37 position: relative; | 38 position: relative; |
38 } | 39 } |
39 | 40 |
40 :host > ::content > .iron-selected::after { | 41 :host > ::content > .iron-selected::after { |
41 mixin(--fit); | 42 mixin(--layout-fit); |
42 | 43 |
43 background: currentColor; | 44 background: currentColor; |
44 /* FIXME move to paper-styles for next widget */ | 45 /* FIXME move to paper-styles for next widget */ |
45 opacity: 0.12; | 46 opacity: 0.12; |
46 content: ''; | 47 content: ''; |
47 | 48 |
48 mixin(--paper-menu-selected-item); | 49 mixin(--paper-menu-selected-item); |
49 } | 50 } |
50 | 51 |
51 :host > ::content > .iron-selected[colored]::after { | 52 :host > ::content > .iron-selected[colored]::after { |
52 opacity: 0.26; | 53 opacity: 0.26; |
53 | 54 |
54 mixin(--paper-menu-colored-selected-item); | 55 mixin(--paper-menu-colored-selected-item); |
55 } | 56 } |
56 | 57 |
57 </style> | 58 </style> |
58 | 59 |
59 <template> | 60 <template> |
60 | 61 |
61 <content></content> | 62 <content></content> |
62 | 63 |
63 </template> | 64 </template> |
64 | 65 |
65 </dom-module> | 66 </dom-module> |
66 | 67 |
67 <script> | 68 <script> |
68 | 69 |
69 (function() { | 70 (function() { |
70 | 71 |
71 // FIXME menu control should be refactored to a more general element | |
72 | |
73 Polymer({ | 72 Polymer({ |
74 | 73 |
75 is: 'paper-menu', | 74 is: 'paper-menu', |
76 | 75 |
77 enableCustomStyleProperties: true, | 76 enableCustomStyleProperties: true, |
78 | 77 |
79 mixins: [ | 78 behaviors: [ |
80 Polymer.Core.Selectable | 79 Polymer.IronMenuBehavior |
81 ], | 80 ] |
82 | |
83 properties: { | |
84 | |
85 /** | |
86 * Returns the currently focused item. | |
87 * | |
88 * @attribute focusedItem | |
89 * @type Object | |
90 */ | |
91 focusedItem: { | |
92 observer: 'focusedItemChanged', | |
93 readOnly: true, | |
94 type: Object | |
95 }, | |
96 | |
97 /** | |
98 * The attribute to use on menu items to look up the item title. Typing th
e first | |
99 * letter of an item when the menu is open focuses that item. If unset, `t
extContent` | |
100 * will be used. | |
101 * | |
102 * @attribute attrForItemTitle | |
103 * @type String | |
104 */ | |
105 attrForItemTitle: { | |
106 type: String | |
107 }, | |
108 | |
109 /***********************************************************************/ | |
110 /* Polymer.Core.Selectable */ | |
111 /***********************************************************************/ | |
112 | |
113 /** | |
114 * If you want to use the attribute value of an element for `selected` ins
tead of the index, | |
115 * set this to the name of the attribute. | |
116 * | |
117 * @attribute attrForSelected | |
118 * @type String | |
119 */ | |
120 attrForSelected: { | |
121 type: String | |
122 }, | |
123 | |
124 /** | |
125 * If true, multiple selections are allowed. | |
126 * | |
127 * @attribute multi | |
128 * @type Boolean | |
129 * @default false | |
130 */ | |
131 multi: { | |
132 observer: 'multiChanged', | |
133 type: Boolean, | |
134 value: false | |
135 }, | |
136 | |
137 /** | |
138 * Gets or sets the selected element. The default is to use the index of t
he item. In | |
139 * multi-selection this should be an array of values. | |
140 * | |
141 * @attribute selected | |
142 * @type String|Array | |
143 */ | |
144 selected: { | |
145 notify: true, | |
146 observer: 'selectedChanged', | |
147 type: String | |
148 }, | |
149 | |
150 /** | |
151 * Returns the currently selected item. In multi-selection this returns an
array of | |
152 * selected items. | |
153 * | |
154 * @attribute selectedItem | |
155 * @type Object|Array | |
156 */ | |
157 selectedItem: { | |
158 notify: true, | |
159 observer: 'selectedItemChanged', | |
160 readOnly: true, | |
161 type: Object | |
162 }, | |
163 | |
164 /** | |
165 * The event that would be fired from the item to indicate it is being sel
ected. Set this | |
166 * to empty string or null if you don't want to listen for any events. | |
167 * | |
168 * @attribute activateEvent | |
169 * @type String | |
170 * @default 'click' | |
171 */ | |
172 activateEvent: { | |
173 observer: 'activateEventChanged', | |
174 type: String, | |
175 value: 'click' | |
176 }, | |
177 | |
178 /** | |
179 * If this is set, only items with local name that matches the `selectable
` are selectable. | |
180 * | |
181 * @attribute selectable | |
182 * @type String | |
183 */ | |
184 selectable: { | |
185 type: String | |
186 } | |
187 | |
188 }, | |
189 | |
190 hostAttributes: { | |
191 'role': 'menu', | |
192 'tabindex': '0' | |
193 }, | |
194 | |
195 listeners: { | |
196 'focus': 'onFocus', | |
197 'keydown': 'onKeydown' | |
198 }, | |
199 | |
200 created: function() { | |
201 this._bindActivateHandler = this.activateHandler.bind(this); | |
202 }, | |
203 | |
204 attached: function() { | |
205 this.selectableAttached(); | |
206 }, | |
207 | |
208 detached: function() { | |
209 this.selectableDetached(); | |
210 this.removeListener(this.activateEvent); | |
211 }, | |
212 | |
213 addListener: function(eventName) { | |
214 if (eventName) { | |
215 this.addEventListener(eventName, this._bindActivateHandler); | |
216 } | |
217 }, | |
218 | |
219 removeListener: function(eventName) { | |
220 if (eventName) { | |
221 this.removeEventListener(eventName, this._bindActivateHandler); | |
222 } | |
223 }, | |
224 | |
225 activateEventChanged: function(eventName, old) { | |
226 this.removeListener(old); | |
227 this.addListener(eventName); | |
228 }, | |
229 | |
230 focusedItemChanged: function(focusedItem, old) { | |
231 old && old.setAttribute('tabindex', '-1'); | |
232 if (focusedItem) { | |
233 focusedItem.setAttribute('tabindex', '0'); | |
234 focusedItem.focus(); | |
235 } | |
236 }, | |
237 | |
238 multiChanged: function(multi) { | |
239 this.selection.multi = multi; | |
240 this.selectedChanged(this.selected); | |
241 }, | |
242 | |
243 selectedChanged: function(selected, old) { | |
244 this._selectedChanged(selected, old); | |
245 }, | |
246 | |
247 selectedItemChanged: function(selectedItem) { | |
248 this._setFocusedItem(Array.isArray(selectedItem) ? selectedItem[0] : selec
tedItem); | |
249 }, | |
250 | |
251 activateHandler: function(e) { | |
252 var t = e.target; | |
253 var items = this.items; | |
254 while (t && t != this) { | |
255 var i = items.indexOf(t); | |
256 if (i >= 0) { | |
257 if (t.hasAttribute('disabled')) { | |
258 return; | |
259 } | |
260 var value = this.indexToValue(i); | |
261 if (!this.fire('iron-activate', {selected: value, item: t}).defaultPre
vented) { | |
262 this.select(value); | |
263 } | |
264 return; | |
265 } | |
266 t = t.parentNode; | |
267 } | |
268 }, | |
269 | |
270 onFocus: function(event) { | |
271 // clear the cached focus item | |
272 this._setFocusedItem(null); | |
273 // focus the selected item when the menu receives focus, or the first item | |
274 // if no item is selected | |
275 var selectedItem = this.selectedItem; | |
276 selectedItem = Array.isArray(selectedItem) ? selectedItem[0] : selectedIte
m; | |
277 if (selectedItem) { | |
278 this._setFocusedItem(selectedItem); | |
279 } else { | |
280 this._setFocusedItem(this.items[0]); | |
281 } | |
282 }, | |
283 | |
284 onKeydown: function(event) { | |
285 // FIXME want to define these somewhere, core-a11y-keys? | |
286 var DOWN = 40; | |
287 var UP = 38; | |
288 var ESC = 27; | |
289 var ENTER = 13; | |
290 if (event.keyCode === DOWN) { | |
291 // up and down arrows moves the focus | |
292 this.focusNext(); | |
293 } else if (event.keyCode === UP) { | |
294 this.focusPrevious(); | |
295 } else if (event.keyCode === ESC) { | |
296 // esc blurs the control | |
297 this.focusedItem.blur(); | |
298 } else if (event.keyCode === ENTER) { | |
299 // enter activates the item | |
300 this.activateHandler(event); | |
301 } else { | |
302 // all other keys focus the menu item starting with that character | |
303 for (var i = 0, item; item = this.items[i]; i++) { | |
304 var attr = this.attrForItemTitle || 'textContent'; | |
305 var title = item[attr] || item.getAttribute(attr); | |
306 if (title && title.charAt(0).toLowerCase() === String.fromCharCode(eve
nt.keyCode).toLowerCase()) { | |
307 this._setFocusedItem(item); | |
308 break; | |
309 } | |
310 } | |
311 } | |
312 }, | |
313 | |
314 focusPrevious: function() { | |
315 var length = this.items.length; | |
316 var index = (Number(this.indexOf(this.focusedItem)) - 1 + length) % length
; | |
317 this._setFocusedItem(this.items[index]); | |
318 }, | |
319 | |
320 focusNext: function() { | |
321 var index = (Number(this.indexOf(this.focusedItem)) + 1) % this.items.leng
th; | |
322 this._setFocusedItem(this.items[index]); | |
323 } | |
324 | 81 |
325 }); | 82 }); |
326 | 83 |
327 })(); | 84 })(); |
328 | 85 |
329 </script> | 86 </script> |
OLD | NEW |