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="../paper-styles/default-theme.html"> | 12 <link rel="import" href="../paper-styles/default-theme.html"> |
13 <link rel="import" href="../paper-input/paper-input.html"> | 13 <link rel="import" href="../paper-input/paper-input.html"> |
14 <link rel="import" href="../paper-menu-button/paper-menu-button.html"> | 14 <link rel="import" href="../paper-menu-button/paper-menu-button.html"> |
15 <link rel="import" href="../paper-ripple/paper-ripple.html"> | 15 <link rel="import" href="../paper-ripple/paper-ripple.html"> |
16 <link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html
"> | 16 <link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html
"> |
17 <link rel="import" href="../iron-behaviors/iron-control-state.html"> | 17 <link rel="import" href="../iron-behaviors/iron-control-state.html"> |
18 <link rel="import" href="../iron-behaviors/iron-button-state.html"> | 18 <link rel="import" href="../iron-behaviors/iron-button-state.html"> |
19 <link rel="import" href="../iron-icons/iron-icons.html"> | 19 <link rel="import" href="../iron-icons/iron-icons.html"> |
20 <link rel="import" href="../iron-icon/iron-icon.html"> | 20 <link rel="import" href="../iron-icon/iron-icon.html"> |
21 <link rel="import" href="../iron-selector/iron-selectable.html"> | 21 <link rel="import" href="../iron-selector/iron-selectable.html"> |
| 22 <link rel="import" href="../iron-form-element-behavior/iron-form-element-behavio
r.html"> |
| 23 <link rel="import" href="../iron-validatable-behavior/iron-validatable-behavior.
html"> |
22 | 24 |
23 <!-- | 25 <!-- |
| 26 Material design: [Dropdown menus](https://www.google.com/design/spec/components/
buttons.html#buttons-dropdown-buttons) |
| 27 |
24 `paper-dropdown-menu` is similar to a native browser select element. | 28 `paper-dropdown-menu` is similar to a native browser select element. |
25 `paper-dropdown-menu` works with selectable content. The currently selected | 29 `paper-dropdown-menu` works with selectable content. The currently selected |
26 item is displayed in the control. If no item is selected, the `label` is | 30 item is displayed in the control. If no item is selected, the `label` is |
27 displayed instead. | 31 displayed instead. |
28 | 32 |
29 The child element with the class `dropdown-content` will be used as the dropdown | 33 The child element with the class `dropdown-content` will be used as the dropdown |
30 menu. It could be a `paper-menu` or element that triggers `iron-select` when | 34 menu. It could be a `paper-menu` or element that triggers `iron-select` when |
31 selecting its children. | 35 selecting its children. |
32 | 36 |
33 Example: | 37 Example: |
34 | 38 |
35 <paper-dropdown-menu label="Your favourite pastry"> | 39 <paper-dropdown-menu label="Your favourite pastry"> |
36 <paper-menu class="dropdown-content"> | 40 <paper-menu class="dropdown-content"> |
37 <paper-item>Croissant</paper-item> | 41 <paper-item>Croissant</paper-item> |
38 <paper-item>Donut</paper-item> | 42 <paper-item>Donut</paper-item> |
39 <paper-item>Financier</paper-item> | 43 <paper-item>Financier</paper-item> |
40 <paper-item>Madeleine</paper-item> | 44 <paper-item>Madeleine</paper-item> |
41 </paper-menu> | 45 </paper-menu> |
42 </paper-dropdown-menu> | 46 </paper-dropdown-menu> |
43 | 47 |
44 This example renders a dropdown menu with 4 options. | 48 This example renders a dropdown menu with 4 options. |
45 | 49 |
| 50 Similarly to using `iron-select`, `iron-deselect` events will cause the |
| 51 current selection of the `paper-dropdown-menu` to be cleared. |
| 52 |
46 ### Styling | 53 ### Styling |
47 | 54 |
48 The following custom properties and mixins are also available for styling: | 55 The following custom properties and mixins are also available for styling: |
49 | 56 |
50 Custom property | Description | Default | 57 Custom property | Description | Default |
51 ----------------|-------------|---------- | 58 ----------------|-------------|---------- |
52 `--paper-dropdown-menu` | A mixin that is applied to the element host | `{}` | 59 `--paper-dropdown-menu` | A mixin that is applied to the element host | `{}` |
53 `--paper-dropdown-menu-disabled` | A mixin that is applied to the element host w
hen disabled | `{}` | 60 `--paper-dropdown-menu-disabled` | A mixin that is applied to the element host w
hen disabled | `{}` |
54 `--paper-dropdown-menu-ripple` | A mixin that is applied to the internal ripple
| `{}` | 61 `--paper-dropdown-menu-ripple` | A mixin that is applied to the internal ripple
| `{}` |
55 `--paper-dropdown-menu-button` | A mixin that is applied to the internal menu bu
tton | `{}` | 62 `--paper-dropdown-menu-button` | A mixin that is applied to the internal menu bu
tton | `{}` |
(...skipping 11 matching lines...) Expand all Loading... |
67 --> | 74 --> |
68 | 75 |
69 <dom-module id="paper-dropdown-menu"> | 76 <dom-module id="paper-dropdown-menu"> |
70 <style> | 77 <style> |
71 :host { | 78 :host { |
72 display: inline-block; | 79 display: inline-block; |
73 position: relative; | 80 position: relative; |
74 text-align: left; | 81 text-align: left; |
75 cursor: pointer; | 82 cursor: pointer; |
76 | 83 |
| 84 /* NOTE(cdata): Both values are needed, since some phones require the |
| 85 * value to be `transparent`. |
| 86 */ |
| 87 -webkit-tap-highlight-color: rgba(0,0,0,0); |
| 88 -webkit-tap-highlight-color: transparent; |
| 89 |
77 --paper-input-container-input: { | 90 --paper-input-container-input: { |
78 overflow: hidden; | 91 overflow: hidden; |
79 white-space: nowrap; | 92 white-space: nowrap; |
80 text-overflow: ellipsis; | 93 text-overflow: ellipsis; |
81 max-width: 100%; | 94 max-width: 100%; |
82 box-sizing: border-box; | 95 box-sizing: border-box; |
83 cursor: pointer; | 96 cursor: pointer; |
84 }; | 97 }; |
85 | 98 |
86 @apply(--paper-dropdown-menu); | 99 @apply(--paper-dropdown-menu); |
87 } | 100 } |
88 | 101 |
89 :host([disabled]) { | 102 :host([disabled]) { |
90 @apply(--paper-dropdown-menu-disabled); | 103 @apply(--paper-dropdown-menu-disabled); |
91 } | 104 } |
92 | 105 |
93 :host([noink]) paper-ripple { | 106 :host([noink]) paper-ripple { |
94 display: none; | 107 display: none; |
95 } | 108 } |
96 | 109 |
97 :host([no-label-float]) paper-ripple { | 110 :host([no-label-float]) paper-ripple { |
98 top: 8px; | 111 top: 8px; |
99 } | 112 } |
100 | 113 |
101 paper-ripple { | 114 paper-ripple { |
102 top: 20px; | 115 top: 12px; |
103 left: 8px; | 116 left: 0px; |
104 bottom: 16px; | 117 bottom: 8px; |
105 right: 8px; | 118 right: 0px; |
106 | 119 |
107 @apply(--paper-dropdown-menu-ripple); | 120 @apply(--paper-dropdown-menu-ripple); |
108 } | 121 } |
109 | 122 |
110 paper-menu-button { | 123 paper-menu-button { |
| 124 display: block; |
| 125 padding: 0; |
111 @apply(--paper-dropdown-menu-button); | 126 @apply(--paper-dropdown-menu-button); |
112 } | 127 } |
113 | 128 |
114 paper-input { | 129 paper-input { |
115 @apply(--paper-dropdown-menu-input); | 130 @apply(--paper-dropdown-menu-input); |
116 } | 131 } |
117 | 132 |
118 iron-icon { | 133 iron-icon { |
119 color: var(--disabled-text-color); | 134 color: var(--disabled-text-color); |
120 | 135 |
121 @apply(--paper-dropdown-menu-icon); | 136 @apply(--paper-dropdown-menu-icon); |
122 } | 137 } |
123 | 138 |
124 </style> | 139 </style> |
125 <template> | 140 <template> |
126 <paper-menu-button | 141 <paper-menu-button |
127 id="menuButton" | 142 id="menuButton" |
128 vertical-align="top" | 143 vertical-align="top" |
129 horizontal-align="right" | 144 horizontal-align="right" |
130 vertical-offset="[[_computeMenuVerticalOffset(noLabelFloat)]]" | 145 vertical-offset="[[_computeMenuVerticalOffset(noLabelFloat)]]" |
131 disabled="[[disabled]]" | 146 disabled="[[disabled]]" |
132 no-animations="[[noAnimations]]" | 147 no-animations="[[noAnimations]]" |
133 on-iron-select="_onIronSelect" | 148 on-iron-select="_onIronSelect" |
| 149 on-iron-deselect="_onIronDeselect" |
134 opened="{{opened}}"> | 150 opened="{{opened}}"> |
135 <div class="dropdown-trigger"> | 151 <div class="dropdown-trigger"> |
136 <paper-ripple></paper-ripple> | 152 <paper-ripple></paper-ripple> |
137 <paper-input | 153 <paper-input |
| 154 invalid="[[invalid]]" |
138 readonly | 155 readonly |
139 disabled="[[disabled]]" | 156 disabled="[[disabled]]" |
140 value="[[selectedItemLabel]]" | 157 value="[[selectedItemLabel]]" |
141 placeholder="[[placeholder]]" | 158 placeholder="[[placeholder]]" |
142 always-float-label="[[alwaysFloatLabel]]" | 159 always-float-label="[[alwaysFloatLabel]]" |
143 no-label-float="[[noLabelFloat]]" | 160 no-label-float="[[noLabelFloat]]" |
144 label="[[label]]"> | 161 label="[[label]]"> |
145 <iron-icon icon="arrow-drop-down" suffix></iron-icon> | 162 <iron-icon icon="arrow-drop-down" suffix></iron-icon> |
146 </paper-input> | 163 </paper-input> |
147 </div> | 164 </div> |
(...skipping 15 matching lines...) Expand all Loading... |
163 */ | 180 */ |
164 | 181 |
165 /** | 182 /** |
166 * Fired when the dropdown closes. | 183 * Fired when the dropdown closes. |
167 * | 184 * |
168 * @event paper-dropdown-close | 185 * @event paper-dropdown-close |
169 */ | 186 */ |
170 | 187 |
171 behaviors: [ | 188 behaviors: [ |
172 Polymer.IronControlState, | 189 Polymer.IronControlState, |
173 Polymer.IronButtonState | 190 Polymer.IronButtonState, |
| 191 Polymer.IronFormElementBehavior, |
| 192 Polymer.IronValidatableBehavior |
174 ], | 193 ], |
175 | 194 |
176 properties: { | 195 properties: { |
177 /** | 196 /** |
178 * The derived "label" of the currently selected item. This value | 197 * The derived "label" of the currently selected item. This value |
179 * is the `label` property on the selected item if set, or else the | 198 * is the `label` property on the selected item if set, or else the |
180 * trimmed text content of the selected item. | 199 * trimmed text content of the selected item. |
181 */ | 200 */ |
182 selectedItemLabel: { | 201 selectedItemLabel: { |
183 type: String, | 202 type: String, |
184 notify: true, | 203 notify: true, |
185 computed: '_computeSelectedItemLabel(selectedItem)' | 204 readOnly: true |
186 }, | 205 }, |
187 | 206 |
188 /** | 207 /** |
189 * The last selected item. An item is selected if the dropdown menu has | 208 * The last selected item. An item is selected if the dropdown menu has |
190 * a child with class `dropdown-content`, and that child triggers an | 209 * a child with class `dropdown-content`, and that child triggers an |
191 * `iron-select` event with the selected `item` in the `detail`. | 210 * `iron-select` event with the selected `item` in the `detail`. |
| 211 * |
| 212 * @type {?Object} |
192 */ | 213 */ |
193 selectedItem: { | 214 selectedItem: { |
194 type: Object, | 215 type: Object, |
195 notify: true, | 216 notify: true, |
196 readOnly: true | 217 readOnly: true |
197 }, | 218 }, |
198 | 219 |
199 /** | 220 /** |
| 221 * The value for this element that will be used when submitting in |
| 222 * a form. It is read only, and will always have the same value |
| 223 * as `selectedItemLabel`. |
| 224 */ |
| 225 value: { |
| 226 type: String, |
| 227 notify: true, |
| 228 readOnly: true |
| 229 }, |
| 230 |
| 231 /** |
200 * The label for the dropdown. | 232 * The label for the dropdown. |
201 */ | 233 */ |
202 label: { | 234 label: { |
203 type: String | 235 type: String |
204 }, | 236 }, |
205 | 237 |
206 /** | 238 /** |
207 * The placeholder for the dropdown. | 239 * The placeholder for the dropdown. |
208 */ | 240 */ |
209 placeholder: { | 241 placeholder: { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
255 keyBindings: { | 287 keyBindings: { |
256 'up down': 'open', | 288 'up down': 'open', |
257 'esc': 'close' | 289 'esc': 'close' |
258 }, | 290 }, |
259 | 291 |
260 hostAttributes: { | 292 hostAttributes: { |
261 role: 'group', | 293 role: 'group', |
262 'aria-haspopup': 'true' | 294 'aria-haspopup': 'true' |
263 }, | 295 }, |
264 | 296 |
| 297 observers: [ |
| 298 '_selectedItemChanged(selectedItem)' |
| 299 ], |
| 300 |
265 attached: function() { | 301 attached: function() { |
266 // NOTE(cdata): Due to timing, a preselected value in a `IronSelectable` | 302 // NOTE(cdata): Due to timing, a preselected value in a `IronSelectable` |
267 // child will cause an `iron-select` event to fire while the element is | 303 // child will cause an `iron-select` event to fire while the element is |
268 // still in a `DocumentFragment`. This has the effect of causing | 304 // still in a `DocumentFragment`. This has the effect of causing |
269 // handlers not to fire. So, we double check this value on attached: | 305 // handlers not to fire. So, we double check this value on attached: |
270 var contentElement = this.contentElement; | 306 var contentElement = this.contentElement; |
271 if (contentElement && contentElement.selectedItem) { | 307 if (contentElement && contentElement.selectedItem) { |
272 this._setSelectedItem(contentElement.selectedItem); | 308 this._setSelectedItem(contentElement.selectedItem); |
273 } | 309 } |
274 }, | 310 }, |
(...skipping 22 matching lines...) Expand all Loading... |
297 /** | 333 /** |
298 * A handler that is called when `iron-select` is fired. | 334 * A handler that is called when `iron-select` is fired. |
299 * | 335 * |
300 * @param {CustomEvent} event An `iron-select` event. | 336 * @param {CustomEvent} event An `iron-select` event. |
301 */ | 337 */ |
302 _onIronSelect: function(event) { | 338 _onIronSelect: function(event) { |
303 this._setSelectedItem(event.detail.item); | 339 this._setSelectedItem(event.detail.item); |
304 }, | 340 }, |
305 | 341 |
306 /** | 342 /** |
| 343 * A handler that is called when `iron-deselect` is fired. |
| 344 * |
| 345 * @param {CustomEvent} event An `iron-deselect` event. |
| 346 */ |
| 347 _onIronDeselect: function(event) { |
| 348 this._setSelectedItem(null); |
| 349 }, |
| 350 |
| 351 /** |
307 * A handler that is called when the dropdown is tapped. | 352 * A handler that is called when the dropdown is tapped. |
308 * | 353 * |
309 * @param {CustomEvent} event A tap event. | 354 * @param {CustomEvent} event A tap event. |
310 */ | 355 */ |
311 _onTap: function(event) { | 356 _onTap: function(event) { |
312 if (Polymer.Gestures.findOriginalTarget(event) === this) { | 357 if (Polymer.Gestures.findOriginalTarget(event) === this) { |
313 this.open(); | 358 this.open(); |
314 } | 359 } |
315 }, | 360 }, |
316 | 361 |
317 /** | 362 /** |
318 * Compute the label for the dropdown given a selected item. | 363 * Compute the label for the dropdown given a selected item. |
319 * | 364 * |
320 * @param {Element} selectedItem A selected Element item, with an | 365 * @param {Element} selectedItem A selected Element item, with an |
321 * optional `label` property. | 366 * optional `label` property. |
322 */ | 367 */ |
323 _computeSelectedItemLabel: function(selectedItem) { | 368 _selectedItemChanged: function(selectedItem) { |
| 369 var value = ''; |
324 if (!selectedItem) { | 370 if (!selectedItem) { |
325 return ''; | 371 value = ''; |
| 372 } else { |
| 373 value = selectedItem.label || selectedItem.textContent.trim(); |
326 } | 374 } |
327 | 375 |
328 return selectedItem.label || selectedItem.textContent.trim(); | 376 this._setValue(value); |
| 377 this._setSelectedItemLabel(value); |
329 }, | 378 }, |
330 | 379 |
331 /** | 380 /** |
332 * Compute the vertical offset of the menu based on the value of | 381 * Compute the vertical offset of the menu based on the value of |
333 * `noLabelFloat`. | 382 * `noLabelFloat`. |
334 * | 383 * |
335 * @param {boolean} noLabelFloat True if the label should not float | 384 * @param {boolean} noLabelFloat True if the label should not float |
336 * above the input, otherwise false. | 385 * above the input, otherwise false. |
337 */ | 386 */ |
338 _computeMenuVerticalOffset: function(noLabelFloat) { | 387 _computeMenuVerticalOffset: function(noLabelFloat) { |
339 // NOTE(cdata): These numbers are somewhat magical because they are | 388 // NOTE(cdata): These numbers are somewhat magical because they are |
340 // derived from the metrics of elements internal to `paper-input`'s | 389 // derived from the metrics of elements internal to `paper-input`'s |
341 // template. The metrics will change depending on whether or not the | 390 // template. The metrics will change depending on whether or not the |
342 // input has a floating label. | 391 // input has a floating label. |
343 return noLabelFloat ? -4 : 16; | 392 return noLabelFloat ? -4 : 8; |
| 393 }, |
| 394 |
| 395 /** |
| 396 * Returns false if the element is required and does not have a selection, |
| 397 * and true otherwise. |
| 398 * @return {Boolean} true if `required` is false, or if `required` is true |
| 399 * and the element has a valid selection. |
| 400 */ |
| 401 _getValidity: function() { |
| 402 return this.disabled || !this.required || (this.required && this.value); |
344 } | 403 } |
345 }); | 404 }); |
346 })(); | 405 })(); |
347 </script> | 406 </script> |
348 | |
OLD | NEW |