aboutsummaryrefslogtreecommitdiff
path: root/catapult/third_party/polymer/components/iron-dropdown/iron-dropdown.html
diff options
context:
space:
mode:
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.html353
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>