3
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
4
This code may only be used under the BSD style license found at http://polymer.github.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/CONTRIBUTORS.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/PATENTS.txt
11
<link rel="import" href="../polymer/polymer.html">
12
<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
13
<link rel="import" href="../iron-behaviors/iron-button-state.html">
14
<link rel="import" href="../iron-behaviors/iron-control-state.html">
15
<link rel="import" href="../iron-form-element-behavior/iron-form-element-behavior.html">
16
<link rel="import" href="../iron-validatable-behavior/iron-validatable-behavior.html">
17
<link rel="import" href="../paper-menu-button/paper-menu-button.html">
18
<link rel="import" href="../paper-behaviors/paper-ripple-behavior.html">
19
<link rel="import" href="../paper-styles/default-theme.html">
21
<link rel="import" href="paper-dropdown-menu-icons.html">
22
<link rel="import" href="paper-dropdown-menu-shared-styles.html">
25
Material design: [Dropdown menus](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons)
27
This is a faster, lighter version of `paper-dropdown-menu`, that does not
28
use a `<paper-input>` internally. Use this element if you're concerned about
29
the performance of this element, i.e., if you plan on using many dropdowns on
30
the same page. Note that this element has a slightly different styling API
31
than `paper-dropdown-menu`.
33
`paper-dropdown-menu-light` is similar to a native browser select element.
34
`paper-dropdown-menu-light` works with selectable content. The currently selected
35
item is displayed in the control. If no item is selected, the `label` is
40
<paper-dropdown-menu-light label="Your favourite pastry">
41
<paper-listbox class="dropdown-content">
42
<paper-item>Croissant</paper-item>
43
<paper-item>Donut</paper-item>
44
<paper-item>Financier</paper-item>
45
<paper-item>Madeleine</paper-item>
47
</paper-dropdown-menu-light>
49
This example renders a dropdown menu with 4 options.
51
The child element with the class `dropdown-content` is used as the dropdown
52
menu. This can be a [`paper-listbox`](paper-listbox), or any other or
53
element that acts like an [`iron-selector`](iron-selector).
55
Specifically, the menu child must fire an
56
[`iron-select`](iron-selector#event-iron-select) event when one of its
57
children is selected, and an [`iron-deselect`](iron-selector#event-iron-deselect)
58
event when a child is deselected. The selected or deselected item must
59
be passed as the event's `detail.item` property.
61
Applications can listen for the `iron-select` and `iron-deselect` events
62
to react when options are selected and deselected.
66
The following custom properties and mixins are also available for styling:
68
Custom property | Description | Default
69
----------------|-------------|----------
70
`--paper-dropdown-menu` | A mixin that is applied to the element host | `{}`
71
`--paper-dropdown-menu-disabled` | A mixin that is applied to the element host when disabled | `{}`
72
`--paper-dropdown-menu-ripple` | A mixin that is applied to the internal ripple | `{}`
73
`--paper-dropdown-menu-button` | A mixin that is applied to the internal menu button | `{}`
74
`--paper-dropdown-menu-icon` | A mixin that is applied to the internal icon | `{}`
75
`--paper-dropdown-menu-disabled-opacity` | The opacity of the dropdown when disabled | `0.33`
76
`--paper-dropdown-menu-color` | The color of the input/label/underline when the dropdown is unfocused | `--primary-text-color`
77
`--paper-dropdown-menu-focus-color` | The color of the label/underline when the dropdown is focused | `--primary-color`
78
`--paper-dropdown-error-color` | The color of the label/underline when the dropdown is invalid | `--error-color`
79
`--paper-dropdown-menu-label` | Mixin applied to the label | `{}`
80
`--paper-dropdown-menu-input` | Mixin appled to the input | `{}`
82
Note that in this element, the underline is just the bottom border of the "input".
85
<style is=custom-style>
86
paper-dropdown-menu-light.custom {
87
--paper-dropdown-menu-input: {
88
border-bottom: 2px dashed lavender;
93
@element paper-dropdown-menu-light
98
<dom-module id="paper-dropdown-menu-light">
100
<style include="paper-dropdown-menu-shared-styles">
102
* All of these styles below are for styling the fake-input display
105
box-sizing: border-box;
108
padding: 16px 0 8px 0;
111
:host([disabled]) .dropdown-trigger {
112
pointer-events: none;
113
opacity: var(--paper-dropdown-menu-disabled-opacity, 0.33);
116
:host([no-label-float]) .dropdown-trigger {
117
padding-top: 8px; /* If there's no label, we need less space up top. */
121
@apply(--paper-font-subhead);
122
@apply(--paper-font-common-nowrap);
123
border-bottom: 1px solid var(--paper-dropdown-menu-color, --secondary-text-color);
124
color: var(--paper-dropdown-menu-color, --primary-text-color);
125
width: 200px; /* Default size of an <input> */
127
box-sizing: border-box;
128
padding: 12px 20px 0 0; /* Right padding so that text doesn't overlap the icon */
130
@apply(--paper-dropdown-menu-input);
133
:host-context([dir="rtl"]) #input {
138
:host([disabled]) #input {
139
border-bottom: 1px dashed var(--paper-dropdown-menu-color, --secondary-text-color);
142
:host([invalid]) #input {
143
border-bottom: 2px solid var(--paper-dropdown-error-color, --error-color);
146
:host([no-label-float]) #input {
147
padding-top: 0; /* If there's no label, we need less space up top. */
151
@apply(--paper-font-subhead);
152
@apply(--paper-font-common-nowrap);
159
* The container has a 16px top padding, and there's 12px of padding
160
* between the input and the label (from the input's padding-top)
163
box-sizing: border-box;
165
padding-right: 20px; /* Right padding so that text doesn't overlap the icon */
167
transition-duration: .2s;
168
transition-timing-function: cubic-bezier(.4,0,.2,1);
169
color: var(--paper-dropdown-menu-color, --secondary-text-color);
170
@apply(--paper-dropdown-menu-label);
173
:host-context([dir="rtl"]) label {
178
:host([no-label-float]) label {
182
label.label-is-floating {
187
label.label-is-hidden {
191
:host([focused]) label.label-is-floating {
192
color: var(--paper-dropdown-menu-focus-color, --primary-color);
195
:host([invalid]) label.label-is-floating {
196
color: var(--paper-dropdown-error-color, --error-color);
200
* Sets up the focused underline. It's initially hidden, and becomes
201
* visible when it's focused.
204
background-color: var(--paper-dropdown-menu-focus-color, --primary-color);
205
bottom: 8px; /* The container has an 8px bottom padding */
210
transition-duration: .2s;
211
transition-timing-function: cubic-bezier(.4,0,.2,1);
217
:host([invalid]) label:after {
218
background-color: var(--paper-dropdown-error-color, --error-color);
221
:host([no-label-float]) label:after {
222
bottom: 8px; /* The container has a 8px bottom padding */
225
:host([focused]:not([disabled])) label:after {
234
bottom: 8px; /* The container has an 8px bottom padding */
235
@apply(--paper-font-subhead);
237
color: var(--disabled-text-color);
238
@apply(--paper-dropdown-menu-icon);
241
:host-context([dir="rtl"]) iron-icon {
246
:host([no-label-float]) iron-icon {
251
display: inline-block;
253
color: var(--paper-dropdown-error-color, --error-color);
254
@apply(--paper-font-caption);
261
:host([invalid]) .error {
266
<!-- this div fulfills an a11y requirement for combobox, do not remove -->
267
<div role="button"></div>
270
vertical-align="[[verticalAlign]]"
271
horizontal-align="[[horizontalAlign]]"
272
vertical-offset="[[_computeMenuVerticalOffset(noLabelFloat)]]"
273
disabled="[[disabled]]"
274
no-animations="[[noAnimations]]"
275
on-iron-select="_onIronSelect"
276
on-iron-deselect="_onIronDeselect"
278
<div class="dropdown-trigger">
279
<label hidden$="[[!label]]"
280
class$="[[_computeLabelClass(noLabelFloat,alwaysFloatLabel,hasContent)]]">[[label]]</label>
281
<div id="input" tabindex="-1"> </div>
282
<iron-icon icon="paper-dropdown-menu:arrow-drop-down"></iron-icon>
283
<span class="error">[[errorMessage]]</span>
285
<content id="content" select=".dropdown-content"></content>
294
is: 'paper-dropdown-menu-light',
297
Polymer.IronButtonState,
298
Polymer.IronControlState,
299
Polymer.PaperRippleBehavior,
300
Polymer.IronFormElementBehavior,
301
Polymer.IronValidatableBehavior
306
* The derived "label" of the currently selected item. This value
307
* is the `label` property on the selected item if set, or else the
308
* trimmed text content of the selected item.
317
* The last selected item. An item is selected if the dropdown menu has
318
* a child with class `dropdown-content`, and that child triggers an
319
* `iron-select` event with the selected `item` in the `detail`.
330
* The value for this element that will be used when submitting in
331
* a form. It is read only, and will always have the same value
332
* as `selectedItemLabel`.
338
observer: '_valueChanged',
342
* The label for the dropdown.
349
* The placeholder for the dropdown.
356
* True if the dropdown is open. Otherwise, false.
362
observer: '_openedChanged'
366
* Set to true to disable the floating label. Bind this to the
367
* `<paper-input-container>`'s `noLabelFloat` property.
372
reflectToAttribute: true
376
* Set to true to always float the label. Bind this to the
377
* `<paper-input-container>`'s `alwaysFloatLabel` property.
385
* Set to true to disable animations when opening and closing the
394
* The orientation against which to align the menu dropdown
395
* horizontally relative to the dropdown trigger.
403
* The orientation against which to align the menu dropdown
404
* vertically relative to the dropdown trigger.
428
'aria-autocomplete': 'none',
429
'aria-haspopup': 'true'
433
'_selectedItemChanged(selectedItem)'
436
attached: function() {
437
// NOTE(cdata): Due to timing, a preselected value in a `IronSelectable`
438
// child will cause an `iron-select` event to fire while the element is
439
// still in a `DocumentFragment`. This has the effect of causing
440
// handlers not to fire. So, we double check this value on attached:
441
var contentElement = this.contentElement;
442
if (contentElement && contentElement.selectedItem) {
443
this._setSelectedItem(contentElement.selectedItem);
448
* The content element that is contained by the dropdown menu, if any.
450
get contentElement() {
451
return Polymer.dom(this.$.content).getDistributedNodes()[0];
455
* Show the dropdown content.
458
this.$.menuButton.open();
462
* Hide the dropdown content.
465
this.$.menuButton.close();
469
* A handler that is called when `iron-select` is fired.
471
* @param {CustomEvent} event An `iron-select` event.
473
_onIronSelect: function(event) {
474
this._setSelectedItem(event.detail.item);
478
* A handler that is called when `iron-deselect` is fired.
480
* @param {CustomEvent} event An `iron-deselect` event.
482
_onIronDeselect: function(event) {
483
this._setSelectedItem(null);
487
* A handler that is called when the dropdown is tapped.
489
* @param {CustomEvent} event A tap event.
491
_onTap: function(event) {
492
if (Polymer.Gestures.findOriginalTarget(event) === this) {
498
* Compute the label for the dropdown given a selected item.
500
* @param {Element} selectedItem A selected Element item, with an
501
* optional `label` property.
503
_selectedItemChanged: function(selectedItem) {
508
value = selectedItem.label || selectedItem.textContent.trim();
511
this._setValue(value);
512
this._setSelectedItemLabel(value);
516
* Compute the vertical offset of the menu based on the value of
519
* @param {boolean} noLabelFloat True if the label should not float
520
* above the input, otherwise false.
522
_computeMenuVerticalOffset: function(noLabelFloat) {
523
// NOTE(cdata): These numbers are somewhat magical because they are
524
// derived from the metrics of elements internal to `paper-input`'s
525
// template. The metrics will change depending on whether or not the
526
// input has a floating label.
527
return noLabelFloat ? -4 : 8;
531
* Returns false if the element is required and does not have a selection,
532
* and true otherwise.
533
* @param {*=} _value Ignored.
534
* @return {boolean} true if `required` is false, or if `required` is true
535
* and the element has a valid selection.
537
_getValidity: function(_value) {
538
return this.disabled || !this.required || (this.required && !!this.value);
541
_openedChanged: function() {
542
var openState = this.opened ? 'true' : 'false';
543
var e = this.contentElement;
545
e.setAttribute('aria-expanded', openState);
549
_computeLabelClass: function(noLabelFloat, alwaysFloatLabel, hasContent) {
551
if (noLabelFloat === true) {
552
return hasContent ? 'label-is-hidden' : '';
555
if (hasContent || alwaysFloatLabel === true) {
556
cls += ' label-is-floating';
561
_valueChanged: function() {
562
// Only update if it's actually different.
563
if (this.$.input && this.$.input.textContent !== this.value) {
564
this.$.input.textContent = this.value;
566
this._setHasContent(!!this.value);