diff options
Diffstat (limited to 'catapult/third_party/polymer/components/iron-dropdown/iron-dropdown.html')
-rw-r--r-- | catapult/third_party/polymer/components/iron-dropdown/iron-dropdown.html | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/catapult/third_party/polymer/components/iron-dropdown/iron-dropdown.html b/catapult/third_party/polymer/components/iron-dropdown/iron-dropdown.html new file mode 100644 index 00000000..82290a3c --- /dev/null +++ b/catapult/third_party/polymer/components/iron-dropdown/iron-dropdown.html @@ -0,0 +1,353 @@ +<!-- +@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-resizable-behavior/iron-resizable-behavior.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-overlay-behavior/iron-overlay-behavior.html"> +<link rel="import" href="../neon-animation/neon-animation-runner-behavior.html"> +<link rel="import" href="../neon-animation/animations/opaque-animation.html"> +<link rel="import" href="iron-dropdown-scroll-manager.html"> + +<!-- +`<iron-dropdown>` is a generalized element that is useful when you have +hidden content (`.dropdown-content`) that is revealed due to some change in +state that should cause it to do so. + +Note that this is a low-level element intended to be used as part of other +composite elements that cause dropdowns to be revealed. + +Examples of elements that might be implemented using an `iron-dropdown` +include comboboxes, menubuttons, selects. The list goes on. + +The `<iron-dropdown>` element exposes attributes that allow the position +of the `.dropdown-content` relative to the `.dropdown-trigger` to be +configured. + + <iron-dropdown horizontal-align="right" vertical-align="top"> + <div class="dropdown-content">Hello!</div> + </iron-dropdown> + +In the above example, the `<div>` with class `.dropdown-content` will be +hidden until the dropdown element has `opened` set to true, or when the `open` +method is called on the element. + +@demo demo/index.html +--> + +<dom-module id="iron-dropdown"> + <template> + <style> + :host { + position: fixed; + } + + #contentWrapper ::content > * { + overflow: auto; + } + + #contentWrapper.animating ::content > * { + overflow: hidden; + } + </style> + + <div id="contentWrapper"> + <content id="content" select=".dropdown-content"></content> + </div> + </template> + + <script> + (function() { + 'use strict'; + + Polymer({ + is: 'iron-dropdown', + + behaviors: [ + Polymer.IronControlState, + Polymer.IronA11yKeysBehavior, + Polymer.IronOverlayBehavior, + Polymer.NeonAnimationRunnerBehavior + ], + + properties: { + /** + * The orientation against which to align the dropdown content + * horizontally relative to the dropdown trigger. + * Overridden from `Polymer.IronFitBehavior`. + */ + horizontalAlign: { + type: String, + value: 'left', + reflectToAttribute: true + }, + + /** + * The orientation against which to align the dropdown content + * vertically relative to the dropdown trigger. + * Overridden from `Polymer.IronFitBehavior`. + */ + verticalAlign: { + type: String, + value: 'top', + reflectToAttribute: true + }, + + /** + * An animation config. If provided, this will be used to animate the + * opening of the dropdown. Pass an Array for multiple animations. + * See `neon-animation` documentation for more animation configuration + * details. + */ + openAnimationConfig: { + type: Object + }, + + /** + * An animation config. If provided, this will be used to animate the + * closing of the dropdown. Pass an Array for multiple animations. + * See `neon-animation` documentation for more animation configuration + * details. + */ + closeAnimationConfig: { + type: Object + }, + + /** + * If provided, this will be the element that will be focused when + * the dropdown opens. + */ + focusTarget: { + type: Object + }, + + /** + * Set to true to disable animations when opening and closing the + * dropdown. + */ + noAnimations: { + type: Boolean, + value: false + }, + + /** + * 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 + }, + + /** + * Callback for scroll events. + * @type {Function} + * @private + */ + _boundOnCaptureScroll: { + type: Function, + value: function() { + return this._onCaptureScroll.bind(this); + } + } + }, + + listeners: { + 'neon-animation-finish': '_onNeonAnimationFinish' + }, + + observers: [ + '_updateOverlayPosition(positionTarget, verticalAlign, horizontalAlign, verticalOffset, horizontalOffset)' + ], + + /** + * The element that is contained by the dropdown, if any. + */ + get containedElement() { + return Polymer.dom(this.$.content).getDistributedNodes()[0]; + }, + + /** + * The element that should be focused when the dropdown opens. + * @deprecated + */ + get _focusTarget() { + return this.focusTarget || this.containedElement; + }, + + ready: function() { + // Memoized scrolling position, used to block scrolling outside. + this._scrollTop = 0; + this._scrollLeft = 0; + // Used to perform a non-blocking refit on scroll. + this._refitOnScrollRAF = null; + }, + + attached: function () { + if (!this.sizingTarget || this.sizingTarget === this) { + this.sizingTarget = this.containedElement || this; + } + }, + + detached: function() { + this.cancelAnimation(); + document.removeEventListener('scroll', this._boundOnCaptureScroll); + Polymer.IronDropdownScrollManager.removeScrollLock(this); + }, + + /** + * Called when the value of `opened` changes. + * Overridden from `IronOverlayBehavior` + */ + _openedChanged: function() { + if (this.opened && this.disabled) { + this.cancel(); + } else { + this.cancelAnimation(); + this._updateAnimationConfig(); + this._saveScrollPosition(); + if (this.opened) { + document.addEventListener('scroll', this._boundOnCaptureScroll); + !this.allowOutsideScroll && Polymer.IronDropdownScrollManager.pushScrollLock(this); + } else { + document.removeEventListener('scroll', this._boundOnCaptureScroll); + Polymer.IronDropdownScrollManager.removeScrollLock(this); + } + Polymer.IronOverlayBehaviorImpl._openedChanged.apply(this, arguments); + } + }, + + /** + * Overridden from `IronOverlayBehavior`. + */ + _renderOpened: function() { + if (!this.noAnimations && this.animationConfig.open) { + this.$.contentWrapper.classList.add('animating'); + this.playAnimation('open'); + } else { + Polymer.IronOverlayBehaviorImpl._renderOpened.apply(this, arguments); + } + }, + + /** + * Overridden from `IronOverlayBehavior`. + */ + _renderClosed: function() { + + if (!this.noAnimations && this.animationConfig.close) { + this.$.contentWrapper.classList.add('animating'); + this.playAnimation('close'); + } else { + Polymer.IronOverlayBehaviorImpl._renderClosed.apply(this, arguments); + } + }, + + /** + * Called when animation finishes on the dropdown (when opening or + * closing). Responsible for "completing" the process of opening or + * closing the dropdown by positioning it or setting its display to + * none. + */ + _onNeonAnimationFinish: function() { + this.$.contentWrapper.classList.remove('animating'); + if (this.opened) { + this._finishRenderOpened(); + } else { + this._finishRenderClosed(); + } + }, + + _onCaptureScroll: function() { + if (!this.allowOutsideScroll) { + this._restoreScrollPosition(); + } else { + this._refitOnScrollRAF && window.cancelAnimationFrame(this._refitOnScrollRAF); + this._refitOnScrollRAF = window.requestAnimationFrame(this.refit.bind(this)); + } + }, + + /** + * Memoizes the scroll position of the outside scrolling element. + * @private + */ + _saveScrollPosition: function() { + if (document.scrollingElement) { + this._scrollTop = document.scrollingElement.scrollTop; + this._scrollLeft = document.scrollingElement.scrollLeft; + } else { + // Since we don't know if is the body or html, get max. + this._scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop); + this._scrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft); + } + }, + + /** + * Resets the scroll position of the outside scrolling element. + * @private + */ + _restoreScrollPosition: function() { + if (document.scrollingElement) { + document.scrollingElement.scrollTop = this._scrollTop; + document.scrollingElement.scrollLeft = this._scrollLeft; + } else { + // Since we don't know if is the body or html, set both. + document.documentElement.scrollTop = this._scrollTop; + document.documentElement.scrollLeft = this._scrollLeft; + document.body.scrollTop = this._scrollTop; + document.body.scrollLeft = this._scrollLeft; + } + }, + + /** + * Constructs the final animation config from different properties used + * to configure specific parts of the opening and closing animations. + */ + _updateAnimationConfig: function() { + // Update the animation node to be the containedElement. + var animationNode = this.containedElement; + var animations = [].concat(this.openAnimationConfig || []).concat(this.closeAnimationConfig || []); + for (var i = 0; i < animations.length; i++) { + animations[i].node = animationNode; + } + this.animationConfig = { + open: this.openAnimationConfig, + close: this.closeAnimationConfig + }; + }, + + /** + * Updates the overlay position based on configured horizontal + * and vertical alignment. + */ + _updateOverlayPosition: function() { + if (this.isAttached) { + // This triggers iron-resize, and iron-overlay-behavior will call refit if needed. + this.notifyResize(); + } + }, + + /** + * Apply focus to focusTarget or containedElement + */ + _applyFocus: function () { + var focusTarget = this.focusTarget || this.containedElement; + if (focusTarget && this.opened && !this.noAutoFocus) { + focusTarget.focus(); + } else { + Polymer.IronOverlayBehaviorImpl._applyFocus.apply(this, arguments); + } + } + }); + })(); + </script> +</dom-module> |