OLD | NEW |
| (Empty) |
1 | |
2 | |
3 /** | |
4 * `Polymer.IronMenuBehavior` implements accessible menu behavior. | |
5 * | |
6 * @demo demo/index.html | |
7 * @polymerBehavior Polymer.IronMenuBehavior | |
8 */ | |
9 Polymer.IronMenuBehaviorImpl = { | |
10 | |
11 properties: { | |
12 | |
13 /** | |
14 * Returns the currently focused item. | |
15 * | |
16 * @attribute focusedItem | |
17 * @type Object | |
18 */ | |
19 focusedItem: { | |
20 observer: '_focusedItemChanged', | |
21 readOnly: true, | |
22 type: Object | |
23 }, | |
24 | |
25 /** | |
26 * The attribute to use on menu items to look up the item title. Typing th
e first | |
27 * letter of an item when the menu is open focuses that item. If unset, `t
extContent` | |
28 * will be used. | |
29 * | |
30 * @attribute attrForItemTitle | |
31 * @type String | |
32 */ | |
33 attrForItemTitle: { | |
34 type: String | |
35 } | |
36 }, | |
37 | |
38 hostAttributes: { | |
39 'role': 'menu', | |
40 'tabindex': '0' | |
41 }, | |
42 | |
43 observers: [ | |
44 '_updateMultiselectable(multi)' | |
45 ], | |
46 | |
47 listeners: { | |
48 'focus': '_onFocus', | |
49 'keydown': '_onKeydown' | |
50 }, | |
51 | |
52 keyBindings: { | |
53 'up': '_onUpKey', | |
54 'down': '_onDownKey', | |
55 'esc': '_onEscKey', | |
56 'enter': '_onEnterKey', | |
57 'shift+tab:keydown': '_onShiftTabDown' | |
58 }, | |
59 | |
60 _updateMultiselectable: function(multi) { | |
61 if (multi) { | |
62 this.setAttribute('aria-multiselectable', 'true'); | |
63 } else { | |
64 this.removeAttribute('aria-multiselectable'); | |
65 } | |
66 }, | |
67 | |
68 _onShiftTabDown: function() { | |
69 var oldTabIndex; | |
70 | |
71 Polymer.IronMenuBehaviorImpl._shiftTabPressed = true; | |
72 | |
73 oldTabIndex = this.getAttribute('tabindex'); | |
74 | |
75 this.setAttribute('tabindex', '-1'); | |
76 | |
77 this.async(function() { | |
78 this.setAttribute('tabindex', oldTabIndex); | |
79 Polymer.IronMenuBehaviorImpl._shiftTabPressed = false; | |
80 // Note: polymer/polymer#1305 | |
81 }, 1); | |
82 }, | |
83 | |
84 _applySelection: function(item, isSelected) { | |
85 if (isSelected) { | |
86 item.setAttribute('aria-selected', 'true'); | |
87 } else { | |
88 item.removeAttribute('aria-selected'); | |
89 } | |
90 | |
91 Polymer.IronSelectableBehavior._applySelection.apply(this, arguments); | |
92 }, | |
93 | |
94 _focusedItemChanged: function(focusedItem, old) { | |
95 old && old.setAttribute('tabindex', '-1'); | |
96 if (focusedItem) { | |
97 focusedItem.setAttribute('tabindex', '0'); | |
98 focusedItem.focus(); | |
99 } | |
100 }, | |
101 | |
102 select: function(value) { | |
103 if (this._defaultFocusAsync) { | |
104 this.cancelAsync(this._defaultFocusAsync); | |
105 this._defaultFocusAsync = null; | |
106 } | |
107 var item = this._valueToItem(value); | |
108 this._setFocusedItem(item); | |
109 Polymer.IronMultiSelectableBehaviorImpl.select.apply(this, arguments); | |
110 }, | |
111 | |
112 _onFocus: function(event) { | |
113 if (Polymer.IronMenuBehaviorImpl._shiftTabPressed) { | |
114 return; | |
115 } | |
116 // do not focus the menu itself | |
117 this.blur(); | |
118 // clear the cached focus item | |
119 this._setFocusedItem(null); | |
120 this._defaultFocusAsync = this.async(function() { | |
121 // focus the selected item when the menu receives focus, or the first it
em | |
122 // if no item is selected | |
123 var selectedItem = this.multi ? (this.selectedItems && this.selectedItem
s[0]) : this.selectedItem; | |
124 if (selectedItem) { | |
125 this._setFocusedItem(selectedItem); | |
126 } else { | |
127 this._setFocusedItem(this.items[0]); | |
128 } | |
129 // async 100ms to wait for `select` to get called from `_itemActivate` | |
130 }, 100); | |
131 }, | |
132 | |
133 _onUpKey: function() { | |
134 // up and down arrows moves the focus | |
135 this._focusPrevious(); | |
136 }, | |
137 | |
138 _onDownKey: function() { | |
139 this._focusNext(); | |
140 }, | |
141 | |
142 _onEscKey: function() { | |
143 // esc blurs the control | |
144 this.focusedItem.blur(); | |
145 }, | |
146 | |
147 _onEnterKey: function(event) { | |
148 // enter activates the item unless it is disabled | |
149 this._activateFocused(event.detail.keyboardEvent); | |
150 }, | |
151 | |
152 _onKeydown: function(event) { | |
153 if (this.keyboardEventMatchesKeys(event, 'up down esc enter')) { | |
154 return; | |
155 } | |
156 | |
157 // all other keys focus the menu item starting with that character | |
158 this._focusWithKeyboardEvent(event); | |
159 }, | |
160 | |
161 _focusWithKeyboardEvent: function(event) { | |
162 for (var i = 0, item; item = this.items[i]; i++) { | |
163 var attr = this.attrForItemTitle || 'textContent'; | |
164 var title = item[attr] || item.getAttribute(attr); | |
165 if (title && title.trim().charAt(0).toLowerCase() === String.fromCharCod
e(event.keyCode).toLowerCase()) { | |
166 this._setFocusedItem(item); | |
167 break; | |
168 } | |
169 } | |
170 }, | |
171 | |
172 _activateFocused: function(event) { | |
173 if (!this.focusedItem.hasAttribute('disabled')) { | |
174 this._activateHandler(event); | |
175 } | |
176 }, | |
177 | |
178 _focusPrevious: function() { | |
179 var length = this.items.length; | |
180 var index = (Number(this.indexOf(this.focusedItem)) - 1 + length) % length
; | |
181 this._setFocusedItem(this.items[index]); | |
182 }, | |
183 | |
184 _focusNext: function() { | |
185 var index = (Number(this.indexOf(this.focusedItem)) + 1) % this.items.leng
th; | |
186 this._setFocusedItem(this.items[index]); | |
187 } | |
188 | |
189 }; | |
190 | |
191 Polymer.IronMenuBehaviorImpl._shiftTabPressed = false; | |
192 | |
193 /** @polymerBehavior Polymer.IronMenuBehavior */ | |
194 Polymer.IronMenuBehavior = [ | |
195 Polymer.IronMultiSelectableBehavior, | |
196 Polymer.IronA11yKeysBehavior, | |
197 Polymer.IronMenuBehaviorImpl | |
198 ]; | |
199 | |
OLD | NEW |