├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE.md └── workflows │ └── tests.yml ├── .gitignore ├── .npmignore ├── CONTRIBUTING.md ├── README.md ├── bower.json ├── demo ├── index.html └── polymer.svg ├── formatconfig.json ├── hero.svg ├── manifest.json ├── package-lock.json ├── package.json ├── paper-menu-button-animations.js ├── paper-menu-button.js ├── test ├── index.html └── paper-menu-button.html └── wct.conf.json /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @bicknellr 2 | /.travis.yml @azakus 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | ### Description 3 | 4 | 5 | ### Expected outcome 6 | 7 | 8 | 9 | ### Actual outcome 10 | 11 | 12 | 13 | ### Live Demo 14 | 15 | 16 | ### Steps to reproduce 17 | 18 | 23 | 24 | ### Browsers Affected 25 | 26 | - [ ] Chrome 27 | - [ ] Firefox 28 | - [ ] Safari 9 29 | - [ ] Safari 8 30 | - [ ] Safari 7 31 | - [ ] Edge 32 | - [ ] IE 11 33 | - [ ] IE 10 34 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | wct-local: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions/setup-node@v1 12 | with: 13 | node-version: 14 14 | 15 | - name: Install the Polymer CLI 16 | run: npm install -g polymer-cli 17 | 18 | - name: Install NPM dependencies 19 | run: npm ci 20 | 21 | - name: Check formatting 22 | run: >- 23 | npm run format && git diff --exit-code || (echo -e 24 | '\n\033[31mERROR:\033[0m Project is not formatted. Please run "npm run 25 | format".' && false) 26 | 27 | - name: Test in local browsers (Chrome/Firefox) 28 | run: polymer test --module-resolution node --npm 29 | 30 | wct-sauce: 31 | # Skip if this is from a forked repository because we can't access Sauce 32 | # secrets. 33 | if: github.event.pull_request.base.repo.id == github.event.pull_request.head.repo.id 34 | 35 | runs-on: ubuntu-latest 36 | 37 | steps: 38 | - uses: actions/checkout@v2 39 | - uses: actions/setup-node@v1 40 | with: 41 | node-version: 14 42 | 43 | - name: Install the Polymer CLI 44 | run: npm install -g polymer-cli 45 | 46 | - name: Install NPM dependencies 47 | run: npm ci 48 | 49 | - name: Check formatting 50 | run: >- 51 | npm run format && git diff --exit-code || (echo -e 52 | '\n\033[31mERROR:\033[0m Project is not formatted. Please run "npm run 53 | format".' && false) 54 | 55 | - name: Test in Sauce Labs browsers (Safari/Edge/IE11/Chrome 41) 56 | env: 57 | SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} 58 | SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} 59 | run: >- 60 | polymer test --module-resolution node --npm 61 | --sauce 'Windows 10/microsoftedge' 62 | --sauce 'Windows 10/internet explorer@11' 63 | --sauce 'macOS 10.13/safari@11' 64 | --sauce 'OS X 10.11/safari@10' 65 | --sauce 'OS X 10.11/safari@9' 66 | --sauce 'Linux/chrome' 67 | --sauce 'Windows 10/chrome@beta' 68 | --sauce 'Windows 10/chrome' 69 | --sauce 'Windows 10/firefox' 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components* 2 | bower-*.json 3 | node_modules 4 | *.d.ts 5 | *.tgz 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.tgz 2 | .github 3 | formatconfig.json 4 | gen-tsd.json 5 | test/ 6 | wct.conf.json 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | # Polymer Elements 15 | ## Guide for Contributors 16 | 17 | Polymer Elements are built in the open, and the Polymer authors eagerly encourage any and all forms of community contribution. When contributing, please follow these guidelines: 18 | 19 | ### Filing Issues 20 | 21 | **If you are filing an issue to request a feature**, please provide a clear description of the feature. It can be helpful to describe answers to the following questions: 22 | 23 | 1. **Who will use the feature?** _“As someone filling out a form…”_ 24 | 2. **When will they use the feature?** _“When I enter an invalid value…”_ 25 | 3. **What is the user’s goal?** _“I want to be visually notified that the value needs to be corrected…”_ 26 | 27 | **If you are filing an issue to report a bug**, please provide: 28 | 29 | 1. **A clear description of the bug and related expectations.** Consider using the following example template for reporting a bug: 30 | 31 | ```markdown 32 | The `paper-foo` element causes the page to turn pink when clicked. 33 | 34 | ## Expected outcome 35 | 36 | The page stays the same color. 37 | 38 | ## Actual outcome 39 | 40 | The page turns pink. 41 | 42 | ## Steps to reproduce 43 | 44 | 1. Put a `paper-foo` element in the page. 45 | 2. Open the page in a web browser. 46 | 3. Click the `paper-foo` element. 47 | ``` 48 | 49 | 2. **A reduced test case that demonstrates the problem.** If possible, please include the test case as a JSBin. Start with this template to easily import and use relevant Polymer Elements: [https://jsbin.com/cagaye/edit?html,output](https://jsbin.com/cagaye/edit?html,output). 50 | 51 | 3. **A list of browsers where the problem occurs.** This can be skipped if the problem is the same across all browsers. 52 | 53 | ### Submitting Pull Requests 54 | 55 | **Before creating a pull request**, please ensure that an issue exists for the corresponding change in the pull request that you intend to make. **If an issue does not exist, please create one per the guidelines above**. The goal is to discuss the design and necessity of the proposed change with Polymer authors and community before diving into a pull request. 56 | 57 | When submitting pull requests, please provide: 58 | 59 | 1. **A reference to the corresponding issue** or issues that will be closed by the pull request. Please refer to these issues in the pull request description using the following syntax: 60 | 61 | ```markdown 62 | (For a single issue) 63 | Fixes #20 64 | 65 | (For multiple issues) 66 | Fixes #32, fixes #40 67 | ``` 68 | 69 | 2. **A succinct description of the design** used to fix any related issues. For example: 70 | 71 | ```markdown 72 | This fixes #20 by removing styles that leaked which would cause the page to turn pink whenever `paper-foo` is clicked. 73 | ``` 74 | 75 | 3. **At least one test for each bug fixed or feature added** as part of the pull request. Pull requests that fix bugs or add features without accompanying tests will not be considered. 76 | 77 | If a proposed change contains multiple commits, please [squash commits](https://www.google.com/url?q=http://blog.steveklabnik.com/posts/2012-11-08-how-to-squash-commits-in-a-github-pull-request) to as few as is necessary to succinctly express the change. A Polymer author can help you squash commits, so don’t be afraid to ask us if you need help with that! 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Published on NPM](https://img.shields.io/npm/v/@polymer/paper-menu-button.svg)](https://www.npmjs.com/package/@polymer/paper-menu-button) 2 | [![Build status](https://travis-ci.org/PolymerElements/paper-menu-button.svg?branch=master)](https://travis-ci.org/PolymerElements/paper-menu-button) 3 | [![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://webcomponents.org/element/@polymer/paper-menu-button) 4 | 5 | ## <paper-menu-button> 6 | 7 | `paper-menu-button` allows one to compose a designated "trigger" element with 8 | another element that represents "content", to create a dropdown menu that 9 | displays the "content" when the "trigger" is clicked. 10 | 11 | The child element assigned to the `dropdown-trigger` slot will be used as the 12 | "trigger" element. The child element assigned to the `dropdown-content` slot will be 13 | used as the "content" element. 14 | 15 | The `paper-menu-button` is sensitive to its content's `iron-select` events. If 16 | the "content" element triggers an `iron-select` event, the `paper-menu-button` 17 | will close automatically. 18 | 19 | ### Styling 20 | 21 | The following custom properties and mixins are also available for styling: 22 | 23 | | Custom property | Description | Default | 24 | | --- | --- | --- | 25 | | `--paper-menu-button-dropdown-background` | Background color of the paper-menu-button dropdown | `--primary-background-color` | 26 | | `--paper-menu-button` | Mixin applied to the paper-menu-button | `{}` | 27 | | `--paper-menu-button-disabled` | Mixin applied to the paper-menu-button when disabled | `{}` | 28 | | `--paper-menu-button-dropdown` | Mixin applied to the paper-menu-button dropdown | `{}` | 29 | | `--paper-menu-button-content` | Mixin applied to the paper-menu-button content | `{}` | 30 | 31 | ## paper-menu-button-animations.js 32 | 33 | Defines these animations: 34 | - <paper-menu-grow-height-animation> 35 | - <paper-menu-grow-width-animation> 36 | - <paper-menu-shrink-height-animation> 37 | - <paper-menu-shrink-width-animation> 38 | 39 | See: [Documentation](https://www.webcomponents.org/element/@polymer/paper-menu-button), 40 | [Demo](https://www.webcomponents.org/element/@polymer/paper-menu-button/demo/demo/index.html). 41 | 42 | ## Usage 43 | 44 | ### Installation 45 | ``` 46 | npm install --save @polymer/paper-menu-button 47 | ``` 48 | 49 | ### In an html file 50 | ```html 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | Share 65 | Settings 66 | Help 67 | 68 | 69 | 70 | 71 | ``` 72 | ### In a Polymer 3 element 73 | ```js 74 | import {PolymerElement, html} from '@polymer/polymer'; 75 | import '@polymer/paper-icon-button/paper-icon-button.js'; 76 | import '@polymer/paper-item/paper-item.js'; 77 | import '@polymer/paper-listbox/paper-listbox.js'; 78 | import '@polymer/paper-menu-button/paper-menu-button.js'; 79 | 80 | class SampleElement extends PolymerElement { 81 | static get template() { 82 | return html` 83 | 84 | 85 | 86 | Share 87 | Settings 88 | Help 89 | 90 | 91 | `; 92 | } 93 | } 94 | customElements.define('sample-element', SampleElement); 95 | ``` 96 | 97 | ## Contributing 98 | If you want to send a PR to this element, here are 99 | the instructions for running the tests and demo locally: 100 | 101 | ### Installation 102 | ```sh 103 | git clone https://github.com/PolymerElements/paper-menu-button 104 | cd paper-menu-button 105 | npm install 106 | npm install -g polymer-cli 107 | ``` 108 | 109 | ### Running the demo locally 110 | ```sh 111 | polymer serve --npm 112 | open http://127.0.0.1:/demo/ 113 | ``` 114 | 115 | ### Running the tests 116 | ```sh 117 | polymer test --npm 118 | ``` 119 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paper-menu-button", 3 | "license": "http://polymer.github.io/LICENSE.txt" 4 | } 5 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | paper-menu-button 21 | 22 | 23 | 24 | 25 | 26 | 43 | 44 | 45 | 78 | 79 | 80 | 81 | 82 | 83 |

Paper Icon Button + Paper Menu

84 | 85 | 97 | 98 | 99 |

Paper Menu with multi selection

100 | 101 | 113 | 114 | 115 |

Disabled

116 | 117 | 129 | 130 | 131 |

Alternate Alignment

132 | 133 | 145 | 146 | 147 |

Alternate Button

148 | 149 | 197 | 198 | 199 |

Alternate Content

200 | 201 | 207 | 208 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /demo/polymer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /formatconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "style": { 3 | "ReflowComments": false 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /hero.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "paper-menu-button.html": { 4 | "convertedUrl": "paper-menu-button.js", 5 | "exports": { 6 | "Polymer.PaperMenuButton": "PaperMenuButton" 7 | } 8 | }, 9 | "paper-menu-button-animations.html": { 10 | "convertedUrl": "paper-menu-button-animations.js", 11 | "exports": {} 12 | }, 13 | "index.html": { 14 | "convertedUrl": "index.html", 15 | "exports": {} 16 | }, 17 | "demo/index.html": { 18 | "convertedUrl": "demo/index.html", 19 | "exports": {} 20 | }, 21 | "test/index.html": { 22 | "convertedUrl": "test/index.html", 23 | "exports": {} 24 | }, 25 | "test/paper-menu-button.html": { 26 | "convertedUrl": "test/paper-menu-button.html", 27 | "exports": {} 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "A material design element that composes a trigger and a dropdown menu", 3 | "keywords": [ 4 | "web-components", 5 | "web-component", 6 | "polymer", 7 | "menu", 8 | "button" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/PolymerElements/paper-menu-button.git" 13 | }, 14 | "homepage": "https://github.com/PolymerElements/paper-menu-button", 15 | "name": "@polymer/paper-menu-button", 16 | "license": "BSD-3-Clause", 17 | "devDependencies": { 18 | "@polymer/gen-typescript-declarations": "^1.5.1", 19 | "@polymer/iron-demo-helpers": "^3.0.1", 20 | "@polymer/iron-icon": "^3.0.0-pre.26", 21 | "@polymer/iron-icons": "^3.0.0-pre.26", 22 | "@polymer/iron-image": "^3.0.0-pre.26", 23 | "@polymer/iron-test-helpers": "^3.0.0-pre.26", 24 | "@polymer/paper-button": "^3.0.0-pre.26", 25 | "@polymer/paper-icon-button": "^3.0.0-pre.26", 26 | "@polymer/paper-item": "^3.0.0-pre.26", 27 | "@polymer/paper-listbox": "^3.0.0-pre.26", 28 | "@webcomponents/webcomponentsjs": "^2.0.0", 29 | "wct-browser-legacy": "^1.0.1", 30 | "webmat": "^0.2.0" 31 | }, 32 | "scripts": { 33 | "format": "webmat", 34 | "generate-types": "gen-typescript-declarations --deleteExisting --outDir . --verify", 35 | "prepare": "npm run generate-types" 36 | }, 37 | "version": "3.1.0", 38 | "main": "paper-menu-button.js", 39 | "author": "The Polymer Authors", 40 | "dependencies": { 41 | "@polymer/iron-a11y-keys-behavior": "^3.0.0-pre.26", 42 | "@polymer/iron-behaviors": "^3.0.0-pre.26", 43 | "@polymer/iron-dropdown": "^3.0.0-pre.26", 44 | "@polymer/iron-fit-behavior": "^3.1.0", 45 | "@polymer/neon-animation": "^3.0.0-pre.26", 46 | "@polymer/paper-styles": "^3.0.0-pre.26", 47 | "@polymer/polymer": "^3.0.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /paper-menu-button-animations.js: -------------------------------------------------------------------------------- 1 | /** 2 | @license 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 5 | http://polymer.github.io/LICENSE.txt The complete set of authors may be found at 6 | http://polymer.github.io/AUTHORS.txt The complete set of contributors may be 7 | found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as 8 | part of the polymer project is also subject to an additional IP rights grant 9 | found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | import '@polymer/polymer/polymer-legacy.js'; 12 | 13 | import {NeonAnimationBehavior} from '@polymer/neon-animation/neon-animation-behavior.js'; 14 | import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js'; 15 | Polymer({ 16 | is: 'paper-menu-grow-height-animation', 17 | /** @override */ 18 | _template: null, 19 | 20 | behaviors: [NeonAnimationBehavior], 21 | 22 | configure: function(config) { 23 | var node = config.node; 24 | var rect = node.getBoundingClientRect(); 25 | var height = rect.height; 26 | 27 | this._effect = new KeyframeEffect( 28 | node, 29 | [{height: (height / 2) + 'px'}, {height: height + 'px'}], 30 | this.timingFromConfig(config)); 31 | 32 | return this._effect; 33 | } 34 | }); 35 | 36 | Polymer({ 37 | is: 'paper-menu-grow-width-animation', 38 | /** @override */ 39 | _template: null, 40 | 41 | behaviors: [NeonAnimationBehavior], 42 | 43 | configure: function(config) { 44 | var node = config.node; 45 | var rect = node.getBoundingClientRect(); 46 | var width = rect.width; 47 | 48 | this._effect = new KeyframeEffect( 49 | node, 50 | [{width: (width / 2) + 'px'}, {width: width + 'px'}], 51 | this.timingFromConfig(config)); 52 | 53 | return this._effect; 54 | } 55 | }); 56 | 57 | Polymer({ 58 | is: 'paper-menu-shrink-width-animation', 59 | /** @override */ 60 | _template: null, 61 | 62 | behaviors: [NeonAnimationBehavior], 63 | 64 | configure: function(config) { 65 | var node = config.node; 66 | var rect = node.getBoundingClientRect(); 67 | var width = rect.width; 68 | 69 | this._effect = new KeyframeEffect( 70 | node, 71 | [{width: width + 'px'}, {width: width - (width / 20) + 'px'}], 72 | this.timingFromConfig(config)); 73 | 74 | return this._effect; 75 | } 76 | }); 77 | 78 | Polymer({ 79 | is: 'paper-menu-shrink-height-animation', 80 | /** @override */ 81 | _template: null, 82 | 83 | behaviors: [NeonAnimationBehavior], 84 | 85 | configure: function(config) { 86 | var node = config.node; 87 | var rect = node.getBoundingClientRect(); 88 | var height = rect.height; 89 | 90 | this.setPrefixedProperty(node, 'transformOrigin', '0 0'); 91 | 92 | this._effect = new KeyframeEffect( 93 | node, 94 | [ 95 | {height: height + 'px', transform: 'translateY(0)'}, 96 | {height: height / 2 + 'px', transform: 'translateY(-20px)'} 97 | ], 98 | this.timingFromConfig(config)); 99 | 100 | return this._effect; 101 | } 102 | }); 103 | -------------------------------------------------------------------------------- /paper-menu-button.js: -------------------------------------------------------------------------------- 1 | /** 2 | @license 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 5 | http://polymer.github.io/LICENSE.txt The complete set of authors may be found at 6 | http://polymer.github.io/AUTHORS.txt The complete set of contributors may be 7 | found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as 8 | part of the polymer project is also subject to an additional IP rights grant 9 | found at http://polymer.github.io/PATENTS.txt 10 | */ 11 | import '@polymer/polymer/polymer-legacy.js'; 12 | import '@polymer/iron-dropdown/iron-dropdown.js'; 13 | import '@polymer/neon-animation/animations/fade-in-animation.js'; 14 | import '@polymer/neon-animation/animations/fade-out-animation.js'; 15 | import '@polymer/paper-styles/default-theme.js'; 16 | import '@polymer/paper-styles/shadow.js'; 17 | import './paper-menu-button-animations.js'; 18 | 19 | import {IronA11yKeysBehavior} from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js'; 20 | import {IronControlState} from '@polymer/iron-behaviors/iron-control-state.js'; 21 | import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js'; 22 | import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js'; 23 | import {html} from '@polymer/polymer/lib/utils/html-tag.js'; 24 | 25 | var config = { 26 | ANIMATION_CUBIC_BEZIER: 'cubic-bezier(.3,.95,.5,1)', 27 | MAX_ANIMATION_TIME_MS: 400 28 | }; 29 | 30 | /** 31 | Material design: [Dropdown 32 | buttons](https://www.google.com/design/spec/components/buttons.html#buttons-dropdown-buttons) 33 | 34 | `paper-menu-button` allows one to compose a designated "trigger" element with 35 | another element that represents "content", to create a dropdown menu that 36 | displays the "content" when the "trigger" is clicked. 37 | 38 | The child element assigned to the `dropdown-trigger` slot will be used as the 39 | "trigger" element. The child element assigned to the `dropdown-content` slot 40 | will be used as the "content" element. 41 | 42 | The `paper-menu-button` is sensitive to its content's `iron-select` events. If 43 | the "content" element triggers an `iron-select` event, the `paper-menu-button` 44 | will close automatically. 45 | 46 | Example: 47 | 48 | 49 | Share 52 | Settings 53 | Help 54 | 55 | 56 | 57 | ### Styling 58 | 59 | The following custom properties and mixins are also available for styling: 60 | 61 | Custom property | Description | Default 62 | ----------------|-------------|---------- 63 | `--paper-menu-button-dropdown-background` | Background color of the paper-menu-button dropdown | `--primary-background-color` 64 | `--paper-menu-button` | Mixin applied to the paper-menu-button | `{}` 65 | `--paper-menu-button-disabled` | Mixin applied to the paper-menu-button when disabled | `{}` 66 | `--paper-menu-button-dropdown` | Mixin applied to the paper-menu-button dropdown | `{}` 67 | `--paper-menu-button-content` | Mixin applied to the paper-menu-button content | `{}` 68 | 69 | @demo demo/index.html 70 | */ 71 | export const PaperMenuButton = Polymer({ 72 | /** @override */ 73 | _template: html` 74 | 121 | 122 |
123 | 124 |
125 | 126 | 127 | 130 | 131 | `, 132 | 133 | is: 'paper-menu-button', 134 | 135 | /** 136 | * Fired when the dropdown opens. 137 | * 138 | * @event paper-dropdown-open 139 | */ 140 | 141 | /** 142 | * Fired when the dropdown closes. 143 | * 144 | * @event paper-dropdown-close 145 | */ 146 | 147 | behaviors: [IronA11yKeysBehavior, IronControlState], 148 | 149 | properties: { 150 | /** 151 | * True if the content is currently displayed. 152 | */ 153 | opened: 154 | {type: Boolean, value: false, notify: true, observer: '_openedChanged'}, 155 | 156 | /** 157 | * The orientation against which to align the menu dropdown 158 | * horizontally relative to the dropdown trigger. 159 | */ 160 | horizontalAlign: {type: String, value: 'left', reflectToAttribute: true}, 161 | 162 | /** 163 | * The orientation against which to align the menu dropdown 164 | * vertically relative to the dropdown trigger. 165 | */ 166 | verticalAlign: {type: String, value: 'top', reflectToAttribute: true}, 167 | 168 | /** 169 | * If true, the `horizontalAlign` and `verticalAlign` properties will 170 | * be considered preferences instead of strict requirements when 171 | * positioning the dropdown and may be changed if doing so reduces 172 | * the area of the dropdown falling outside of `fitInto`. 173 | */ 174 | dynamicAlign: {type: Boolean}, 175 | 176 | /** 177 | * A pixel value that will be added to the position calculated for the 178 | * given `horizontalAlign`. Use a negative value to offset to the 179 | * left, or a positive value to offset to the right. 180 | */ 181 | horizontalOffset: {type: Number, value: 0, notify: true}, 182 | 183 | /** 184 | * A pixel value that will be added to the position calculated for the 185 | * given `verticalAlign`. Use a negative value to offset towards the 186 | * top, or a positive value to offset towards the bottom. 187 | */ 188 | verticalOffset: {type: Number, value: 0, notify: true}, 189 | 190 | /** 191 | * If true, the dropdown will be positioned so that it doesn't overlap 192 | * the button. 193 | */ 194 | noOverlap: {type: Boolean}, 195 | 196 | /** 197 | * Set to true to disable animations when opening and closing the 198 | * dropdown. 199 | */ 200 | noAnimations: {type: Boolean, value: false}, 201 | 202 | /** 203 | * Set to true to disable automatically closing the dropdown after 204 | * a selection has been made. 205 | */ 206 | ignoreSelect: {type: Boolean, value: false}, 207 | 208 | /** 209 | * Set to true to enable automatically closing the dropdown after an 210 | * item has been activated, even if the selection did not change. 211 | */ 212 | closeOnActivate: {type: Boolean, value: false}, 213 | 214 | /** 215 | * An animation config. If provided, this will be used to animate the 216 | * opening of the dropdown. 217 | */ 218 | openAnimationConfig: { 219 | type: Object, 220 | value: function() { 221 | return [ 222 | {name: 'fade-in-animation', timing: {delay: 100, duration: 200}}, 223 | { 224 | name: 'paper-menu-grow-width-animation', 225 | timing: { 226 | delay: 100, 227 | duration: 150, 228 | easing: config.ANIMATION_CUBIC_BEZIER 229 | } 230 | }, 231 | { 232 | name: 'paper-menu-grow-height-animation', 233 | timing: { 234 | delay: 100, 235 | duration: 275, 236 | easing: config.ANIMATION_CUBIC_BEZIER 237 | } 238 | } 239 | ]; 240 | } 241 | }, 242 | 243 | /** 244 | * An animation config. If provided, this will be used to animate the 245 | * closing of the dropdown. 246 | */ 247 | closeAnimationConfig: { 248 | type: Object, 249 | value: function() { 250 | return [ 251 | {name: 'fade-out-animation', timing: {duration: 150}}, 252 | { 253 | name: 'paper-menu-shrink-width-animation', 254 | timing: { 255 | delay: 100, 256 | duration: 50, 257 | easing: config.ANIMATION_CUBIC_BEZIER 258 | } 259 | }, 260 | { 261 | name: 'paper-menu-shrink-height-animation', 262 | timing: {duration: 200, easing: 'ease-in'} 263 | } 264 | ]; 265 | } 266 | }, 267 | 268 | /** 269 | * By default, the dropdown will constrain scrolling on the page 270 | * to itself when opened. 271 | * Set to true in order to prevent scroll from being constrained 272 | * to the dropdown when it opens. 273 | */ 274 | allowOutsideScroll: {type: Boolean, value: false}, 275 | 276 | /** 277 | * Whether focus should be restored to the button when the menu closes. 278 | */ 279 | restoreFocusOnClose: {type: Boolean, value: true}, 280 | 281 | /** 282 | * If true and scrollbars are added to the dropdown after it is positioned, 283 | * the size of the added scrollbars will be added to its `maxWidth` and 284 | * `maxHeight`. 285 | */ 286 | expandSizingTargetForScrollbars: {type: Boolean, value: false}, 287 | 288 | /** 289 | * This is the element intended to be bound as the focus target 290 | * for the `iron-dropdown` contained by `paper-menu-button`. 291 | */ 292 | _dropdownContent: {type: Object} 293 | }, 294 | 295 | hostAttributes: {role: 'group', 'aria-haspopup': 'true'}, 296 | 297 | listeners: 298 | {'iron-activate': '_onIronActivate', 'iron-select': '_onIronSelect'}, 299 | 300 | /** 301 | * The content element that is contained by the menu button, if any. 302 | */ 303 | get contentElement() { 304 | // Polymer 2.x returns slot.assignedNodes which can contain text nodes. 305 | var nodes = dom(this.$.content).getDistributedNodes(); 306 | for (var i = 0, l = nodes.length; i < l; i++) { 307 | if (nodes[i].nodeType === Node.ELEMENT_NODE) { 308 | return nodes[i]; 309 | } 310 | } 311 | }, 312 | 313 | /** 314 | * Toggles the dropdown content between opened and closed. 315 | */ 316 | toggle: function() { 317 | if (this.opened) { 318 | this.close(); 319 | } else { 320 | this.open(); 321 | } 322 | }, 323 | 324 | /** 325 | * Make the dropdown content appear as an overlay positioned relative 326 | * to the dropdown trigger. 327 | */ 328 | open: function() { 329 | if (this.disabled) { 330 | return; 331 | } 332 | 333 | this.$.dropdown.open(); 334 | }, 335 | 336 | /** 337 | * Hide the dropdown content. 338 | */ 339 | close: function() { 340 | this.$.dropdown.close(); 341 | }, 342 | 343 | /** 344 | * When an `iron-select` event is received, the dropdown should 345 | * automatically close on the assumption that a value has been chosen. 346 | * 347 | * @param {CustomEvent} event A CustomEvent instance with type 348 | * set to `"iron-select"`. 349 | */ 350 | _onIronSelect: function(event) { 351 | if (!this.ignoreSelect) { 352 | this.close(); 353 | } 354 | }, 355 | 356 | /** 357 | * Closes the dropdown when an `iron-activate` event is received if 358 | * `closeOnActivate` is true. 359 | * 360 | * @param {CustomEvent} event A CustomEvent of type 'iron-activate'. 361 | */ 362 | _onIronActivate: function(event) { 363 | if (this.closeOnActivate) { 364 | this.close(); 365 | } 366 | }, 367 | 368 | /** 369 | * When the dropdown opens, the `paper-menu-button` fires `paper-open`. 370 | * When the dropdown closes, the `paper-menu-button` fires `paper-close`. 371 | * 372 | * @param {boolean} opened True if the dropdown is opened, otherwise false. 373 | * @param {boolean} oldOpened The previous value of `opened`. 374 | */ 375 | _openedChanged: function(opened, oldOpened) { 376 | if (opened) { 377 | // TODO(cdata): Update this when we can measure changes in distributed 378 | // children in an idiomatic way. 379 | // We poke this property in case the element has changed. This will 380 | // cause the focus target for the `iron-dropdown` to be updated as 381 | // necessary: 382 | this._dropdownContent = this.contentElement; 383 | this.fire('paper-dropdown-open'); 384 | } else if (oldOpened != null) { 385 | this.fire('paper-dropdown-close'); 386 | } 387 | }, 388 | 389 | /** 390 | * If the dropdown is open when disabled becomes true, close the 391 | * dropdown. 392 | * 393 | * @param {boolean} disabled True if disabled, otherwise false. 394 | */ 395 | _disabledChanged: function(disabled) { 396 | IronControlState._disabledChanged.apply(this, arguments); 397 | if (disabled && this.opened) { 398 | this.close(); 399 | } 400 | }, 401 | 402 | __onIronOverlayCanceled: function(event) { 403 | var uiEvent = event.detail; 404 | var trigger = this.$.trigger; 405 | var path = dom(uiEvent).path; 406 | 407 | if (path.indexOf(trigger) > -1) { 408 | event.preventDefault(); 409 | } 410 | } 411 | }); 412 | 413 | Object.keys(config).forEach(function(key) { 414 | PaperMenuButton[key] = config[key]; 415 | }); 416 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | paper-menu-button tests 13 | 14 | 15 | 16 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/paper-menu-button.html: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | paper-menu-button basic tests 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | 42 | 43 | 44 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /wct.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "local": { 4 | "browserOptions": { 5 | "chrome": [ 6 | "no-sandbox", 7 | "headless", 8 | "disable-gpu" 9 | ], 10 | "firefox": [ 11 | "-headless" 12 | ] 13 | } 14 | } 15 | } 16 | } --------------------------------------------------------------------------------