diff options
Diffstat (limited to 'catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-light.html')
-rw-r--r-- | catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-light.html | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-light.html b/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-light.html new file mode 100644 index 00000000..edaabf85 --- /dev/null +++ b/catapult/third_party/polymer/components/paper-dropdown-menu/paper-dropdown-menu-light.html @@ -0,0 +1,597 @@ +<!-- +@license +Copyright (c) 2016 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt +Code distributed by Google as part of the polymer project is also +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt +--> + +<link rel="import" href="../polymer/polymer.html"> +<link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html"> +<link rel="import" href="../iron-behaviors/iron-button-state.html"> +<link rel="import" href="../iron-behaviors/iron-control-state.html"> +<link rel="import" href="../iron-form-element-behavior/iron-form-element-behavior.html"> +<link rel="import" href="../iron-icon/iron-icon.html"> +<link rel="import" href="../iron-validatable-behavior/iron-validatable-behavior.html"> +<link rel="import" href="../paper-behaviors/paper-ripple-behavior.html"> +<link rel="import" href="../paper-menu-button/paper-menu-button.html"> +<link rel="import" href="../paper-styles/default-theme.html"> + +<link rel="import" href="paper-dropdown-menu-icons.html"> +<link rel="import" href="paper-dropdown-menu-shared-styles.html"> + +<!-- +Material design: [Dropdown menus](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons) + +This is a faster, lighter version of `paper-dropdown-menu`, that does not +use a `<paper-input>` internally. Use this element if you're concerned about +the performance of this element, i.e., if you plan on using many dropdowns on +the same page. Note that this element has a slightly different styling API +than `paper-dropdown-menu`. + +`paper-dropdown-menu-light` is similar to a native browser select element. +`paper-dropdown-menu-light` works with selectable content. The currently selected +item is displayed in the control. If no item is selected, the `label` is +displayed instead. + +Example: + + <paper-dropdown-menu-light label="Your favourite pastry"> + <paper-listbox class="dropdown-content"> + <paper-item>Croissant</paper-item> + <paper-item>Donut</paper-item> + <paper-item>Financier</paper-item> + <paper-item>Madeleine</paper-item> + </paper-listbox> + </paper-dropdown-menu-light> + +This example renders a dropdown menu with 4 options. + +The child element with the class `dropdown-content` is used as the dropdown +menu. This can be a [`paper-listbox`](paper-listbox), or any other or +element that acts like an [`iron-selector`](iron-selector). + +Specifically, the menu child must fire an +[`iron-select`](iron-selector#event-iron-select) event when one of its +children is selected, and an [`iron-deselect`](iron-selector#event-iron-deselect) +event when a child is deselected. The selected or deselected item must +be passed as the event's `detail.item` property. + +Applications can listen for the `iron-select` and `iron-deselect` events +to react when options are selected and deselected. + +### Styling + +The following custom properties and mixins are also available for styling: + +Custom property | Description | Default +----------------|-------------|---------- +`--paper-dropdown-menu` | A mixin that is applied to the element host | `{}` +`--paper-dropdown-menu-disabled` | A mixin that is applied to the element host when disabled | `{}` +`--paper-dropdown-menu-ripple` | A mixin that is applied to the internal ripple | `{}` +`--paper-dropdown-menu-button` | A mixin that is applied to the internal menu button | `{}` +`--paper-dropdown-menu-icon` | A mixin that is applied to the internal icon | `{}` +`--paper-dropdown-menu-disabled-opacity` | The opacity of the dropdown when disabled | `0.33` +`--paper-dropdown-menu-color` | The color of the input/label/underline when the dropdown is unfocused | `--primary-text-color` +`--paper-dropdown-menu-focus-color` | The color of the label/underline when the dropdown is focused | `--primary-color` +`--paper-dropdown-error-color` | The color of the label/underline when the dropdown is invalid | `--error-color` +`--paper-dropdown-menu-label` | Mixin applied to the label | `{}` +`--paper-dropdown-menu-input` | Mixin appled to the input | `{}` + +Note that in this element, the underline is just the bottom border of the "input". +To style it: + + <style is=custom-style> + paper-dropdown-menu-light.custom { + --paper-dropdown-menu-input: { + border-bottom: 2px dashed lavender; + }; + </style> + +@group Paper Elements +@element paper-dropdown-menu-light +@hero hero.svg +@demo demo/index.html +--> + +<dom-module id="paper-dropdown-menu-light"> + <template> + <style include="paper-dropdown-menu-shared-styles"> + :host(:focus) { + outline: none; + } + + :host { + width: 200px; /* Default size of an <input> */ + } + + /** + * All of these styles below are for styling the fake-input display + */ + .dropdown-trigger { + box-sizing: border-box; + position: relative; + width: 100%; + padding: 16px 0 8px 0; + } + + :host([disabled]) .dropdown-trigger { + pointer-events: none; + opacity: var(--paper-dropdown-menu-disabled-opacity, 0.33); + } + + :host([no-label-float]) .dropdown-trigger { + padding-top: 8px; /* If there's no label, we need less space up top. */ + } + + #input { + @apply(--paper-font-subhead); + @apply(--paper-font-common-nowrap); + line-height: 1.5; + border-bottom: 1px solid var(--paper-dropdown-menu-color, --secondary-text-color); + color: var(--paper-dropdown-menu-color, --primary-text-color); + width: 100%; + box-sizing: border-box; + padding: 12px 20px 0 0; /* Right padding so that text doesn't overlap the icon */ + outline: none; + @apply(--paper-dropdown-menu-input); + } + + :host-context([dir="rtl"]) #input { + padding-right: 0px; + padding-left: 20px; + } + + :host([disabled]) #input { + border-bottom: 1px dashed var(--paper-dropdown-menu-color, --secondary-text-color); + } + + :host([invalid]) #input { + border-bottom: 2px solid var(--paper-dropdown-error-color, --error-color); + } + + :host([no-label-float]) #input { + padding-top: 0; /* If there's no label, we need less space up top. */ + } + + label { + @apply(--paper-font-subhead); + @apply(--paper-font-common-nowrap); + display: block; + position: absolute; + bottom: 0; + left: 0; + right: 0; + /** + * The container has a 16px top padding, and there's 12px of padding + * between the input and the label (from the input's padding-top) + */ + top: 28px; + box-sizing: border-box; + width: 100%; + padding-right: 20px; /* Right padding so that text doesn't overlap the icon */ + text-align: left; + transition-duration: .2s; + transition-timing-function: cubic-bezier(.4,0,.2,1); + color: var(--paper-dropdown-menu-color, --secondary-text-color); + @apply(--paper-dropdown-menu-label); + } + + :host-context([dir="rtl"]) label { + padding-right: 0px; + padding-left: 20px; + } + + :host([no-label-float]) label { + top: 8px; + /* Since the label doesn't need to float, remove the animation duration + which slows down visibility changes (i.e. when a selection is made) */ + transition-duration: 0s; + } + + label.label-is-floating { + font-size: 12px; + top: 8px; + } + + label.label-is-hidden { + visibility: hidden; + } + + :host([focused]) label.label-is-floating { + color: var(--paper-dropdown-menu-focus-color, --primary-color); + } + + :host([invalid]) label.label-is-floating { + color: var(--paper-dropdown-error-color, --error-color); + } + + /** + * Sets up the focused underline. It's initially hidden, and becomes + * visible when it's focused. + */ + label:after { + background-color: var(--paper-dropdown-menu-focus-color, --primary-color); + bottom: 7px; /* The container has an 8px bottom padding */ + content: ''; + height: 2px; + left: 45%; + position: absolute; + transition-duration: .2s; + transition-timing-function: cubic-bezier(.4,0,.2,1); + visibility: hidden; + width: 8px; + z-index: 10; + } + + :host([invalid]) label:after { + background-color: var(--paper-dropdown-error-color, --error-color); + } + + :host([no-label-float]) label:after { + bottom: 7px; /* The container has a 8px bottom padding */ + } + + :host([focused]:not([disabled])) label:after { + left: 0; + visibility: visible; + width: 100%; + } + + iron-icon { + position: absolute; + right: 0px; + bottom: 8px; /* The container has an 8px bottom padding */ + @apply(--paper-font-subhead); + color: var(--disabled-text-color); + @apply(--paper-dropdown-menu-icon); + } + + :host-context([dir="rtl"]) iron-icon { + left: 0; + right: auto; + } + + :host([no-label-float]) iron-icon { + margin-top: 0px; + } + + .error { + display: inline-block; + visibility: hidden; + color: var(--paper-dropdown-error-color, --error-color); + @apply(--paper-font-caption); + position: absolute; + left:0; + right:0; + bottom: -12px; + } + + :host([invalid]) .error { + visibility: visible; + } + </style> + + <!-- this div fulfills an a11y requirement for combobox, do not remove --> + <span role="button"></span> + <paper-menu-button + id="menuButton" + vertical-align="[[verticalAlign]]" + horizontal-align="[[horizontalAlign]]" + vertical-offset="[[_computeMenuVerticalOffset(noLabelFloat)]]" + disabled="[[disabled]]" + no-animations="[[noAnimations]]" + on-iron-select="_onIronSelect" + on-iron-deselect="_onIronDeselect" + opened="{{opened}}" + close-on-activate + allow-outside-scroll="[[allowOutsideScroll]]"> + <div class="dropdown-trigger"> + <label class$="[[_computeLabelClass(noLabelFloat,alwaysFloatLabel,hasContent)]]"> + [[label]] + </label> + <div id="input" tabindex="-1"> </div> + <iron-icon icon="paper-dropdown-menu:arrow-drop-down"></iron-icon> + <span class="error">[[errorMessage]]</span> + </div> + <content id="content" select=".dropdown-content"></content> + </paper-menu-button> + </template> + + <script> + (function() { + 'use strict'; + + Polymer({ + is: 'paper-dropdown-menu-light', + + behaviors: [ + Polymer.IronButtonState, + Polymer.IronControlState, + Polymer.PaperRippleBehavior, + Polymer.IronFormElementBehavior, + Polymer.IronValidatableBehavior + ], + + properties: { + /** + * The derived "label" of the currently selected item. This value + * is the `label` property on the selected item if set, or else the + * trimmed text content of the selected item. + */ + selectedItemLabel: { + type: String, + notify: true, + readOnly: true + }, + + /** + * The last selected item. An item is selected if the dropdown menu has + * a child with class `dropdown-content`, and that child triggers an + * `iron-select` event with the selected `item` in the `detail`. + * + * @type {?Object} + */ + selectedItem: { + type: Object, + notify: true, + readOnly: true + }, + + /** + * The value for this element that will be used when submitting in + * a form. It is read only, and will always have the same value + * as `selectedItemLabel`. + */ + value: { + type: String, + notify: true, + readOnly: true, + observer: '_valueChanged', + }, + + /** + * The label for the dropdown. + */ + label: { + type: String + }, + + /** + * The placeholder for the dropdown. + */ + placeholder: { + type: String + }, + + /** + * True if the dropdown is open. Otherwise, false. + */ + opened: { + type: Boolean, + notify: true, + value: false, + observer: '_openedChanged' + }, + + /** + * By default, the dropdown will constrain scrolling on the page + * to itself when opened. + * Set to true in order to prevent scroll from being constrained + * to the dropdown when it opens. + */ + allowOutsideScroll: { + type: Boolean, + value: false + }, + + /** + * Set to true to disable the floating label. Bind this to the + * `<paper-input-container>`'s `noLabelFloat` property. + */ + noLabelFloat: { + type: Boolean, + value: false, + reflectToAttribute: true + }, + + /** + * Set to true to always float the label. Bind this to the + * `<paper-input-container>`'s `alwaysFloatLabel` property. + */ + alwaysFloatLabel: { + type: Boolean, + value: false + }, + + /** + * Set to true to disable animations when opening and closing the + * dropdown. + */ + noAnimations: { + type: Boolean, + value: false + }, + + /** + * The orientation against which to align the menu dropdown + * horizontally relative to the dropdown trigger. + */ + horizontalAlign: { + type: String, + value: 'right' + }, + + /** + * The orientation against which to align the menu dropdown + * vertically relative to the dropdown trigger. + */ + verticalAlign: { + type: String, + value: 'top' + }, + + hasContent: { + type: Boolean, + readOnly: true + } + }, + + listeners: { + 'tap': '_onTap' + }, + + keyBindings: { + 'up down': 'open', + 'esc': 'close' + }, + + hostAttributes: { + tabindex: 0, + role: 'combobox', + 'aria-autocomplete': 'none', + 'aria-haspopup': 'true' + }, + + observers: [ + '_selectedItemChanged(selectedItem)' + ], + + attached: function() { + // NOTE(cdata): Due to timing, a preselected value in a `IronSelectable` + // child will cause an `iron-select` event to fire while the element is + // still in a `DocumentFragment`. This has the effect of causing + // handlers not to fire. So, we double check this value on attached: + var contentElement = this.contentElement; + if (contentElement && contentElement.selectedItem) { + this._setSelectedItem(contentElement.selectedItem); + } + }, + + /** + * The content element that is contained by the dropdown menu, if any. + */ + get contentElement() { + return Polymer.dom(this.$.content).getDistributedNodes()[0]; + }, + + /** + * Show the dropdown content. + */ + open: function() { + this.$.menuButton.open(); + }, + + /** + * Hide the dropdown content. + */ + close: function() { + this.$.menuButton.close(); + }, + + /** + * A handler that is called when `iron-select` is fired. + * + * @param {CustomEvent} event An `iron-select` event. + */ + _onIronSelect: function(event) { + this._setSelectedItem(event.detail.item); + }, + + /** + * A handler that is called when `iron-deselect` is fired. + * + * @param {CustomEvent} event An `iron-deselect` event. + */ + _onIronDeselect: function(event) { + this._setSelectedItem(null); + }, + + /** + * A handler that is called when the dropdown is tapped. + * + * @param {CustomEvent} event A tap event. + */ + _onTap: function(event) { + if (Polymer.Gestures.findOriginalTarget(event) === this) { + this.open(); + } + }, + + /** + * Compute the label for the dropdown given a selected item. + * + * @param {Element} selectedItem A selected Element item, with an + * optional `label` property. + */ + _selectedItemChanged: function(selectedItem) { + var value = ''; + if (!selectedItem) { + value = ''; + } else { + value = selectedItem.label || selectedItem.getAttribute('label') || selectedItem.textContent.trim(); + } + + this._setValue(value); + this._setSelectedItemLabel(value); + }, + + /** + * Compute the vertical offset of the menu based on the value of + * `noLabelFloat`. + * + * @param {boolean} noLabelFloat True if the label should not float + * above the input, otherwise false. + */ + _computeMenuVerticalOffset: function(noLabelFloat) { + // NOTE(cdata): These numbers are somewhat magical because they are + // derived from the metrics of elements internal to `paper-input`'s + // template. The metrics will change depending on whether or not the + // input has a floating label. + return noLabelFloat ? -4 : 8; + }, + + /** + * Returns false if the element is required and does not have a selection, + * and true otherwise. + * @param {*=} _value Ignored. + * @return {boolean} true if `required` is false, or if `required` is true + * and the element has a valid selection. + */ + _getValidity: function(_value) { + return this.disabled || !this.required || (this.required && !!this.value); + }, + + _openedChanged: function() { + var openState = this.opened ? 'true' : 'false'; + var e = this.contentElement; + if (e) { + e.setAttribute('aria-expanded', openState); + } + }, + + _computeLabelClass: function(noLabelFloat, alwaysFloatLabel, hasContent) { + var cls = ''; + if (noLabelFloat === true) { + return hasContent ? 'label-is-hidden' : ''; + } + + if (hasContent || alwaysFloatLabel === true) { + cls += ' label-is-floating'; + } + return cls; + }, + + _valueChanged: function() { + // Only update if it's actually different. + if (this.$.input && this.$.input.textContent !== this.value) { + this.$.input.textContent = this.value; + } + this._setHasContent(!!this.value); + }, + }); + })(); + </script> +</dom-module> |