| <!-- |
| @license |
| Copyright (c) 2015 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-control-state.html"> |
| <link rel="import" href="../iron-dropdown/iron-dropdown.html"> |
| <link rel="import" href="../neon-animation/animations/fade-in-animation.html"> |
| <link rel="import" href="../neon-animation/animations/fade-out-animation.html"> |
| <link rel="import" href="../paper-styles/default-theme.html"> |
| <link rel="import" href="../paper-styles/shadow.html"> |
| <link rel="import" href="paper-menu-button-animations.html"> |
| |
| <!-- |
| Material design: [Dropdown buttons](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons) |
| |
| `paper-menu-button` allows one to compose a designated "trigger" element with |
| another element that represents "content", to create a dropdown menu that |
| displays the "content" when the "trigger" is clicked. |
| |
| The child element with the class `dropdown-trigger` will be used as the |
| "trigger" element. The child element with the class `dropdown-content` will be |
| used as the "content" element. |
| |
| The `paper-menu-button` is sensitive to its content's `iron-select` events. If |
| the "content" element triggers an `iron-select` event, the `paper-menu-button` |
| will close automatically. |
| |
| Example: |
| |
| <paper-menu-button> |
| <paper-icon-button icon="menu" class="dropdown-trigger"></paper-icon-button> |
| <paper-menu class="dropdown-content"> |
| <paper-item>Share</paper-item> |
| <paper-item>Settings</paper-item> |
| <paper-item>Help</paper-item> |
| </paper-menu> |
| </paper-menu-button> |
| |
| ### Styling |
| |
| The following custom properties and mixins are also available for styling: |
| |
| Custom property | Description | Default |
| ----------------|-------------|---------- |
| `--paper-menu-button-dropdown-background` | Background color of the paper-menu-button dropdown | `--primary-background-color` |
| `--paper-menu-button` | Mixin applied to the paper-menu-button | `{}` |
| `--paper-menu-button-disabled` | Mixin applied to the paper-menu-button when disabled | `{}` |
| `--paper-menu-button-dropdown` | Mixin applied to the paper-menu-button dropdown | `{}` |
| `--paper-menu-button-content` | Mixin applied to the paper-menu-button content | `{}` |
| |
| @hero hero.svg |
| @demo demo/index.html |
| --> |
| |
| <dom-module id="paper-menu-button"> |
| <template> |
| <style> |
| :host { |
| display: inline-block; |
| position: relative; |
| padding: 8px; |
| outline: none; |
| |
| @apply(--paper-menu-button); |
| } |
| |
| :host([disabled]) { |
| cursor: auto; |
| color: var(--disabled-text-color); |
| |
| @apply(--paper-menu-button-disabled); |
| } |
| |
| iron-dropdown { |
| @apply(--paper-menu-button-dropdown); |
| } |
| |
| .dropdown-content { |
| @apply(--shadow-elevation-2dp); |
| |
| position: relative; |
| border-radius: 2px; |
| background-color: var(--paper-menu-button-dropdown-background, --primary-background-color); |
| |
| @apply(--paper-menu-button-content); |
| } |
| |
| :host([vertical-align="top"]) .dropdown-content { |
| margin-bottom: 20px; |
| margin-top: -10px; |
| top: 10px; |
| } |
| |
| :host([vertical-align="bottom"]) .dropdown-content { |
| bottom: 10px; |
| margin-bottom: -10px; |
| margin-top: 20px; |
| } |
| |
| #trigger { |
| cursor: pointer; |
| } |
| </style> |
| |
| <div id="trigger" on-tap="toggle"> |
| <content select=".dropdown-trigger"></content> |
| </div> |
| |
| <iron-dropdown |
| id="dropdown" |
| opened="{{opened}}" |
| horizontal-align="[[horizontalAlign]]" |
| vertical-align="[[verticalAlign]]" |
| dynamic-align="[[dynamicAlign]]" |
| horizontal-offset="[[horizontalOffset]]" |
| vertical-offset="[[verticalOffset]]" |
| no-overlap="[[noOverlap]]" |
| open-animation-config="[[openAnimationConfig]]" |
| close-animation-config="[[closeAnimationConfig]]" |
| no-animations="[[noAnimations]]" |
| focus-target="[[_dropdownContent]]" |
| allow-outside-scroll="[[allowOutsideScroll]]" |
| restore-focus-on-close="[[restoreFocusOnClose]]" |
| on-iron-overlay-canceled="__onIronOverlayCanceled"> |
| <div class="dropdown-content"> |
| <content id="content" select=".dropdown-content"></content> |
| </div> |
| </iron-dropdown> |
| </template> |
| |
| <script> |
| (function() { |
| 'use strict'; |
| |
| var config = { |
| ANIMATION_CUBIC_BEZIER: 'cubic-bezier(.3,.95,.5,1)', |
| MAX_ANIMATION_TIME_MS: 400 |
| }; |
| |
| var PaperMenuButton = Polymer({ |
| is: 'paper-menu-button', |
| |
| /** |
| * Fired when the dropdown opens. |
| * |
| * @event paper-dropdown-open |
| */ |
| |
| /** |
| * Fired when the dropdown closes. |
| * |
| * @event paper-dropdown-close |
| */ |
| |
| behaviors: [ |
| Polymer.IronA11yKeysBehavior, |
| Polymer.IronControlState |
| ], |
| |
| properties: { |
| /** |
| * True if the content is currently displayed. |
| */ |
| opened: { |
| type: Boolean, |
| value: false, |
| notify: true, |
| observer: '_openedChanged' |
| }, |
| |
| /** |
| * The orientation against which to align the menu dropdown |
| * horizontally relative to the dropdown trigger. |
| */ |
| horizontalAlign: { |
| type: String, |
| value: 'left', |
| reflectToAttribute: true |
| }, |
| |
| /** |
| * The orientation against which to align the menu dropdown |
| * vertically relative to the dropdown trigger. |
| */ |
| verticalAlign: { |
| type: String, |
| value: 'top', |
| reflectToAttribute: true |
| }, |
| |
| /** |
| * If true, the `horizontalAlign` and `verticalAlign` properties will |
| * be considered preferences instead of strict requirements when |
| * positioning the dropdown and may be changed if doing so reduces |
| * the area of the dropdown falling outside of `fitInto`. |
| */ |
| dynamicAlign: { |
| type: Boolean |
| }, |
| |
| /** |
| * A pixel value that will be added to the position calculated for the |
| * given `horizontalAlign`. Use a negative value to offset to the |
| * left, or a positive value to offset to the right. |
| */ |
| horizontalOffset: { |
| type: Number, |
| value: 0, |
| notify: true |
| }, |
| |
| /** |
| * A pixel value that will be added to the position calculated for the |
| * given `verticalAlign`. Use a negative value to offset towards the |
| * top, or a positive value to offset towards the bottom. |
| */ |
| verticalOffset: { |
| type: Number, |
| value: 0, |
| notify: true |
| }, |
| |
| /** |
| * If true, the dropdown will be positioned so that it doesn't overlap |
| * the button. |
| */ |
| noOverlap: { |
| type: Boolean |
| }, |
| |
| /** |
| * Set to true to disable animations when opening and closing the |
| * dropdown. |
| */ |
| noAnimations: { |
| type: Boolean, |
| value: false |
| }, |
| |
| /** |
| * Set to true to disable automatically closing the dropdown after |
| * a selection has been made. |
| */ |
| ignoreSelect: { |
| type: Boolean, |
| value: false |
| }, |
| |
| /** |
| * Set to true to enable automatically closing the dropdown after an |
| * item has been activated, even if the selection did not change. |
| */ |
| closeOnActivate: { |
| type: Boolean, |
| value: false |
| }, |
| |
| /** |
| * An animation config. If provided, this will be used to animate the |
| * opening of the dropdown. |
| */ |
| openAnimationConfig: { |
| type: Object, |
| value: function() { |
| return [{ |
| name: 'fade-in-animation', |
| timing: { |
| delay: 100, |
| duration: 200 |
| } |
| }, { |
| name: 'paper-menu-grow-width-animation', |
| timing: { |
| delay: 100, |
| duration: 150, |
| easing: config.ANIMATION_CUBIC_BEZIER |
| } |
| }, { |
| name: 'paper-menu-grow-height-animation', |
| timing: { |
| delay: 100, |
| duration: 275, |
| easing: config.ANIMATION_CUBIC_BEZIER |
| } |
| }]; |
| } |
| }, |
| |
| /** |
| * An animation config. If provided, this will be used to animate the |
| * closing of the dropdown. |
| */ |
| closeAnimationConfig: { |
| type: Object, |
| value: function() { |
| return [{ |
| name: 'fade-out-animation', |
| timing: { |
| duration: 150 |
| } |
| }, { |
| name: 'paper-menu-shrink-width-animation', |
| timing: { |
| delay: 100, |
| duration: 50, |
| easing: config.ANIMATION_CUBIC_BEZIER |
| } |
| }, { |
| name: 'paper-menu-shrink-height-animation', |
| timing: { |
| duration: 200, |
| easing: 'ease-in' |
| } |
| }]; |
| } |
| }, |
| |
| /** |
| * 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 |
| }, |
| |
| /** |
| * Whether focus should be restored to the button when the menu closes. |
| */ |
| restoreFocusOnClose: { |
| type: Boolean, |
| value: true |
| }, |
| |
| /** |
| * This is the element intended to be bound as the focus target |
| * for the `iron-dropdown` contained by `paper-menu-button`. |
| */ |
| _dropdownContent: { |
| type: Object |
| } |
| }, |
| |
| hostAttributes: { |
| role: 'group', |
| 'aria-haspopup': 'true' |
| }, |
| |
| listeners: { |
| 'iron-activate': '_onIronActivate', |
| 'iron-select': '_onIronSelect' |
| }, |
| |
| /** |
| * The content element that is contained by the menu button, if any. |
| */ |
| get contentElement() { |
| return Polymer.dom(this.$.content).getDistributedNodes()[0]; |
| }, |
| |
| /** |
| * Toggles the drowpdown content between opened and closed. |
| */ |
| toggle: function() { |
| if (this.opened) { |
| this.close(); |
| } else { |
| this.open(); |
| } |
| }, |
| |
| /** |
| * Make the dropdown content appear as an overlay positioned relative |
| * to the dropdown trigger. |
| */ |
| open: function() { |
| if (this.disabled) { |
| return; |
| } |
| |
| this.$.dropdown.open(); |
| }, |
| |
| /** |
| * Hide the dropdown content. |
| */ |
| close: function() { |
| this.$.dropdown.close(); |
| }, |
| |
| /** |
| * When an `iron-select` event is received, the dropdown should |
| * automatically close on the assumption that a value has been chosen. |
| * |
| * @param {CustomEvent} event A CustomEvent instance with type |
| * set to `"iron-select"`. |
| */ |
| _onIronSelect: function(event) { |
| if (!this.ignoreSelect) { |
| this.close(); |
| } |
| }, |
| |
| /** |
| * Closes the dropdown when an `iron-activate` event is received if |
| * `closeOnActivate` is true. |
| * |
| * @param {CustomEvent} event A CustomEvent of type 'iron-activate'. |
| */ |
| _onIronActivate: function(event) { |
| if (this.closeOnActivate) { |
| this.close(); |
| } |
| }, |
| |
| /** |
| * When the dropdown opens, the `paper-menu-button` fires `paper-open`. |
| * When the dropdown closes, the `paper-menu-button` fires `paper-close`. |
| * |
| * @param {boolean} opened True if the dropdown is opened, otherwise false. |
| * @param {boolean} oldOpened The previous value of `opened`. |
| */ |
| _openedChanged: function(opened, oldOpened) { |
| if (opened) { |
| // TODO(cdata): Update this when we can measure changes in distributed |
| // children in an idiomatic way. |
| // We poke this property in case the element has changed. This will |
| // cause the focus target for the `iron-dropdown` to be updated as |
| // necessary: |
| this._dropdownContent = this.contentElement; |
| this.fire('paper-dropdown-open'); |
| } else if (oldOpened != null) { |
| this.fire('paper-dropdown-close'); |
| } |
| }, |
| |
| /** |
| * If the dropdown is open when disabled becomes true, close the |
| * dropdown. |
| * |
| * @param {boolean} disabled True if disabled, otherwise false. |
| */ |
| _disabledChanged: function(disabled) { |
| Polymer.IronControlState._disabledChanged.apply(this, arguments); |
| if (disabled && this.opened) { |
| this.close(); |
| } |
| }, |
| |
| __onIronOverlayCanceled: function(event) { |
| var uiEvent = event.detail; |
| var target = Polymer.dom(uiEvent).rootTarget; |
| var trigger = this.$.trigger; |
| var path = Polymer.dom(uiEvent).path; |
| |
| if (path.indexOf(trigger) > -1) { |
| event.preventDefault(); |
| } |
| } |
| }); |
| |
| Object.keys(config).forEach(function (key) { |
| PaperMenuButton[key] = config[key]; |
| }); |
| |
| Polymer.PaperMenuButton = PaperMenuButton; |
| })(); |
| </script> |
| </dom-module> |