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