| OLD | NEW |
| (Empty) | |
| 1 |
| 2 |
| 3 (function() { |
| 4 |
| 5 // FIXME menu control should be refactored to a more general element |
| 6 |
| 7 Polymer({ |
| 8 |
| 9 is: 'paper-menu', |
| 10 |
| 11 enableCustomStyleProperties: true, |
| 12 |
| 13 mixins: [ |
| 14 Polymer.Core.Selectable |
| 15 ], |
| 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 |
| 259 }); |
| 260 |
| 261 })(); |
| 262 |
| OLD | NEW |