<!-- @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-dropdown/iron-dropdown.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="../paper-material/paper-material.html"> <link rel="import" href="../paper-styles/default-theme.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-menu-button-animations.html"> <!-- `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 | `#fff` `--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 | `{}` @hero hero.svg @demo demo/index.html --> <dom-module id="paper-menu-button"> <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); } :host([vertical-align="top"]) paper-material { margin-bottom: 20px; margin-top: -10px; top: 10px; } :host([vertical-align="bottom"]) paper-material { bottom: 10px; margin-bottom: -10px; margin-top: 20px; } paper-material { border-radius: 2px; background-color: var(--paper-menu-button-dropdown-background, --primary-background-color); @apply(--paper-menu-button-dropdown); } </style> <template> <div id="trigger" on-tap="open"> <content select=".dropdown-trigger"></content> </div> <iron-dropdown id="dropdown" opened="{{opened}}" horizontal-align="[[horizontalAlign]]" vertical-align="[[verticalAlign]]" horizontal-offset="[[horizontalOffset]]" vertical-offset="[[verticalOffset]]" open-animation-config="[[openAnimationConfig]]" close-animation-config="[[closeAnimationConfig]]" no-animations="[[noAnimations]]" focus-target="[[_dropdownContent]]"> <paper-material class="dropdown-content"> <content id="content" select=".dropdown-content"></content> </paper-material> </iron-dropdown> </template> </dom-module> <script> (function() { 'use strict'; 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 }, /** * 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 }, /** * 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 }, /** * 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: PaperMenuButton.ANIMATION_CUBIC_BEZIER } }, { name: 'paper-menu-grow-height-animation', timing: { delay: 100, duration: 275, easing: PaperMenuButton.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: PaperMenuButton.ANIMATION_CUBIC_BEZIER } }, { name: 'paper-menu-shrink-height-animation', timing: { duration: 200, easing: 'ease-in' } }]; } }, /** * 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-select': '_onIronSelect' }, /** * The content element that is contained by the menu button, if any. */ get contentElement() { return Polymer.dom(this.$.content).getDistributedNodes()[0]; }, /** * 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(); } }, /** * 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(); } } }); PaperMenuButton.ANIMATION_CUBIC_BEZIER = 'cubic-bezier(.3,.95,.5,1)'; PaperMenuButton.MAX_ANIMATION_TIME_MS = 400; Polymer.PaperMenuButton = PaperMenuButton; })(); </script>