OLD | NEW |
(Empty) | |
| 1 <!-- |
| 2 @license |
| 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 |
| 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 |
| 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 |
| 9 --> |
| 10 |
| 11 <link rel="import" href="../polymer/polymer.html"> |
| 12 <link rel="import" href="../iron-selector/iron-selectable.html"> |
| 13 <link rel="import" href="../paper-styles/paper-styles.html"> |
| 14 |
| 15 <!-- |
| 16 @element paper-menu |
| 17 --> |
| 18 |
| 19 <dom-module id="paper-menu"> |
| 20 |
| 21 <style> |
| 22 |
| 23 :host { |
| 24 padding: 8px 0; |
| 25 |
| 26 background: var(--primary-background-color); |
| 27 color: var(--primary-text-color); |
| 28 |
| 29 mixin(--paper-menu); |
| 30 } |
| 31 |
| 32 :host > ::content > [disabled] { |
| 33 color: var(--disabled-text-color); |
| 34 } |
| 35 |
| 36 :host > ::content > .iron-selected { |
| 37 position: relative; |
| 38 } |
| 39 |
| 40 :host > ::content > .iron-selected::after { |
| 41 mixin(--fit); |
| 42 |
| 43 background: currentColor; |
| 44 /* FIXME move to paper-styles for next widget */ |
| 45 opacity: 0.12; |
| 46 content: ''; |
| 47 |
| 48 mixin(--paper-menu-selected-item); |
| 49 } |
| 50 |
| 51 :host > ::content > .iron-selected[colored]::after { |
| 52 opacity: 0.26; |
| 53 |
| 54 mixin(--paper-menu-colored-selected-item); |
| 55 } |
| 56 |
| 57 </style> |
| 58 |
| 59 <template> |
| 60 |
| 61 <content></content> |
| 62 |
| 63 </template> |
| 64 |
| 65 </dom-module> |
| 66 |
| 67 <script> |
| 68 |
| 69 (function() { |
| 70 |
| 71 // FIXME menu control should be refactored to a more general element |
| 72 |
| 73 Polymer({ |
| 74 |
| 75 is: 'paper-menu', |
| 76 |
| 77 enableCustomStyleProperties: true, |
| 78 |
| 79 mixins: [ |
| 80 Polymer.Core.Selectable |
| 81 ], |
| 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 |
| 325 }); |
| 326 |
| 327 })(); |
| 328 |
| 329 </script> |
OLD | NEW |