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