| 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 |