2015-06-19 09:36:51 -07:00
|
|
|
<!--
|
|
|
|
@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-fit-behavior/iron-fit-behavior.html">
|
|
|
|
<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
|
|
|
|
<link rel="import" href="iron-overlay-backdrop.html">
|
|
|
|
<link rel="import" href="iron-overlay-manager.html">
|
|
|
|
|
|
|
|
<script>
|
2016-04-04 18:23:42 -07:00
|
|
|
// IIFE to help scripts concatenation.
|
|
|
|
(function() {
|
|
|
|
'use strict';
|
2015-06-19 09:36:51 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or shown, and displays
|
|
|
|
on top of other content. It includes an optional backdrop, and can be used to implement a variety
|
|
|
|
of UI controls including dialogs and drop downs. Multiple overlays may be displayed at once.
|
|
|
|
|
|
|
|
### Closing and canceling
|
|
|
|
|
|
|
|
A dialog may be hidden by closing or canceling. The difference between close and cancel is user
|
|
|
|
intent. Closing generally implies that the user acknowledged the content on the overlay. By default,
|
|
|
|
it will cancel whenever the user taps outside it or presses the escape key. This behavior is
|
|
|
|
configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties.
|
|
|
|
`close()` should be called explicitly by the implementer when the user interacts with a control
|
2015-10-01 23:14:04 -07:00
|
|
|
in the overlay element. When the dialog is canceled, the overlay fires an 'iron-overlay-canceled'
|
|
|
|
event. Call `preventDefault` on this event to prevent the overlay from closing.
|
2015-06-19 09:36:51 -07:00
|
|
|
|
|
|
|
### Positioning
|
|
|
|
|
|
|
|
By default the element is sized and positioned to fit and centered inside the window. You can
|
|
|
|
position and size it manually using CSS. See `Polymer.IronFitBehavior`.
|
|
|
|
|
|
|
|
### Backdrop
|
|
|
|
|
|
|
|
Set the `with-backdrop` attribute to display a backdrop behind the overlay. The backdrop is
|
|
|
|
appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page for styling
|
|
|
|
options.
|
|
|
|
|
|
|
|
### Limitations
|
|
|
|
|
|
|
|
The element is styled to appear on top of other content by setting its `z-index` property. You
|
|
|
|
must ensure no element has a stacking context with a higher `z-index` than its parent stacking
|
|
|
|
context. You should place this element as a child of `<body>` whenever possible.
|
|
|
|
|
|
|
|
@demo demo/index.html
|
|
|
|
@polymerBehavior Polymer.IronOverlayBehavior
|
|
|
|
*/
|
|
|
|
|
|
|
|
Polymer.IronOverlayBehaviorImpl = {
|
|
|
|
|
|
|
|
properties: {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True if the overlay is currently displayed.
|
|
|
|
*/
|
|
|
|
opened: {
|
|
|
|
observer: '_openedChanged',
|
|
|
|
type: Boolean,
|
2015-06-25 18:13:51 -07:00
|
|
|
value: false,
|
|
|
|
notify: true
|
2015-06-19 09:36:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* True if the overlay was canceled when it was last closed.
|
|
|
|
*/
|
|
|
|
canceled: {
|
|
|
|
observer: '_canceledChanged',
|
|
|
|
readOnly: true,
|
|
|
|
type: Boolean,
|
|
|
|
value: false
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set to true to display a backdrop behind the overlay.
|
|
|
|
*/
|
|
|
|
withBackdrop: {
|
2016-01-29 19:43:11 -07:00
|
|
|
observer: '_withBackdropChanged',
|
|
|
|
type: Boolean
|
2015-06-19 09:36:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set to true to disable auto-focusing the overlay or child nodes with
|
|
|
|
* the `autofocus` attribute` when the overlay is opened.
|
|
|
|
*/
|
|
|
|
noAutoFocus: {
|
|
|
|
type: Boolean,
|
|
|
|
value: false
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set to true to disable canceling the overlay with the ESC key.
|
|
|
|
*/
|
|
|
|
noCancelOnEscKey: {
|
|
|
|
type: Boolean,
|
|
|
|
value: false
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set to true to disable canceling the overlay by clicking outside it.
|
|
|
|
*/
|
|
|
|
noCancelOnOutsideClick: {
|
|
|
|
type: Boolean,
|
|
|
|
value: false
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2016-04-26 21:22:32 -07:00
|
|
|
* Contains the reason(s) this overlay was last closed (see `iron-overlay-closed`).
|
|
|
|
* `IronOverlayBehavior` provides the `canceled` reason; implementers of the
|
|
|
|
* behavior can provide other reasons in addition to `canceled`.
|
2015-06-19 09:36:51 -07:00
|
|
|
*/
|
|
|
|
closingReason: {
|
|
|
|
// was a getter before, but needs to be a property so other
|
|
|
|
// behaviors can override this.
|
|
|
|
type: Object
|
|
|
|
},
|
|
|
|
|
2016-02-23 22:36:48 -07:00
|
|
|
/**
|
|
|
|
* Set to true to enable restoring of focus when overlay is closed.
|
|
|
|
*/
|
|
|
|
restoreFocusOnClose: {
|
|
|
|
type: Boolean,
|
|
|
|
value: false
|
|
|
|
},
|
|
|
|
|
2016-03-30 19:00:05 -07:00
|
|
|
/**
|
|
|
|
* Set to true to keep overlay always on top.
|
|
|
|
*/
|
|
|
|
alwaysOnTop: {
|
|
|
|
type: Boolean
|
|
|
|
},
|
|
|
|
|
2016-03-11 20:29:37 -07:00
|
|
|
/**
|
|
|
|
* Shortcut to access to the overlay manager.
|
|
|
|
* @private
|
|
|
|
* @type {Polymer.IronOverlayManagerClass}
|
|
|
|
*/
|
2015-06-19 09:36:51 -07:00
|
|
|
_manager: {
|
|
|
|
type: Object,
|
|
|
|
value: Polymer.IronOverlayManager
|
|
|
|
},
|
|
|
|
|
2016-02-22 12:31:28 -07:00
|
|
|
/**
|
|
|
|
* The node being focused.
|
|
|
|
* @type {?Node}
|
|
|
|
*/
|
2016-02-05 23:33:34 -07:00
|
|
|
_focusedChild: {
|
|
|
|
type: Object
|
2015-06-19 09:36:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
listeners: {
|
|
|
|
'iron-resize': '_onIronResize'
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The backdrop element.
|
2016-03-11 20:29:37 -07:00
|
|
|
* @type {Element}
|
2015-06-19 09:36:51 -07:00
|
|
|
*/
|
|
|
|
get backdropElement() {
|
2016-01-29 19:43:11 -07:00
|
|
|
return this._manager.backdropElement;
|
2015-06-19 09:36:51 -07:00
|
|
|
},
|
|
|
|
|
2016-02-22 12:31:28 -07:00
|
|
|
/**
|
|
|
|
* Returns the node to give focus to.
|
|
|
|
* @type {Node}
|
|
|
|
*/
|
2015-06-19 09:36:51 -07:00
|
|
|
get _focusNode() {
|
2016-02-23 22:36:48 -07:00
|
|
|
return this._focusedChild || Polymer.dom(this).querySelector('[autofocus]') || this;
|
2016-02-22 12:31:28 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of nodes that can receive focus (overlay included), ordered by `tabindex`.
|
|
|
|
* This is used to retrieve which is the first and last focusable nodes in order
|
|
|
|
* to wrap the focus for overlays `with-backdrop`.
|
|
|
|
*
|
|
|
|
* If you know what is your content (specifically the first and last focusable children),
|
|
|
|
* you can override this method to return only `[firstFocusable, lastFocusable];`
|
2016-03-11 20:29:37 -07:00
|
|
|
* @type {Array<Node>}
|
2016-02-22 12:31:28 -07:00
|
|
|
* @protected
|
|
|
|
*/
|
|
|
|
get _focusableNodes() {
|
|
|
|
// Elements that can be focused even if they have [disabled] attribute.
|
|
|
|
var FOCUSABLE_WITH_DISABLED = [
|
|
|
|
'a[href]',
|
|
|
|
'area[href]',
|
|
|
|
'iframe',
|
|
|
|
'[tabindex]',
|
|
|
|
'[contentEditable=true]'
|
|
|
|
];
|
|
|
|
|
|
|
|
// Elements that cannot be focused if they have [disabled] attribute.
|
|
|
|
var FOCUSABLE_WITHOUT_DISABLED = [
|
|
|
|
'input',
|
|
|
|
'select',
|
|
|
|
'textarea',
|
|
|
|
'button'
|
|
|
|
];
|
|
|
|
|
|
|
|
// Discard elements with tabindex=-1 (makes them not focusable).
|
|
|
|
var selector = FOCUSABLE_WITH_DISABLED.join(':not([tabindex="-1"]),') +
|
|
|
|
':not([tabindex="-1"]),' +
|
|
|
|
FOCUSABLE_WITHOUT_DISABLED.join(':not([disabled]):not([tabindex="-1"]),') +
|
|
|
|
':not([disabled]):not([tabindex="-1"])';
|
|
|
|
|
|
|
|
var focusables = Polymer.dom(this).querySelectorAll(selector);
|
|
|
|
if (this.tabIndex >= 0) {
|
|
|
|
// Insert at the beginning because we might have all elements with tabIndex = 0,
|
|
|
|
// and the overlay should be the first of the list.
|
|
|
|
focusables.splice(0, 0, this);
|
|
|
|
}
|
|
|
|
// Sort by tabindex.
|
|
|
|
return focusables.sort(function (a, b) {
|
|
|
|
if (a.tabIndex === b.tabIndex) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
});
|
2015-06-19 09:36:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
ready: function() {
|
2016-03-08 11:47:36 -07:00
|
|
|
// Used to skip calls to notifyResize and refit while the overlay is animating.
|
|
|
|
this.__isAnimating = false;
|
2016-02-23 22:36:48 -07:00
|
|
|
// with-backdrop needs tabindex to be set in order to trap the focus.
|
2016-02-05 23:33:34 -07:00
|
|
|
// If it is not set, IronOverlayBehavior will set it, and remove it if with-backdrop = false.
|
|
|
|
this.__shouldRemoveTabIndex = false;
|
2016-02-22 12:31:28 -07:00
|
|
|
// Used for wrapping the focus on TAB / Shift+TAB.
|
|
|
|
this.__firstFocusableNode = this.__lastFocusableNode = null;
|
2016-04-04 18:23:42 -07:00
|
|
|
// Used for requestAnimationFrame when opened changes.
|
|
|
|
this.__openChangedAsync = null;
|
|
|
|
// Used for requestAnimationFrame when iron-resize is fired.
|
|
|
|
this.__onIronResizeAsync = null;
|
2015-06-19 09:36:51 -07:00
|
|
|
this._ensureSetup();
|
2015-12-14 08:43:03 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
attached: function() {
|
|
|
|
// Call _openedChanged here so that position can be computed correctly.
|
2016-02-23 22:36:48 -07:00
|
|
|
if (this.opened) {
|
2015-06-19 09:36:51 -07:00
|
|
|
this._openedChanged();
|
|
|
|
}
|
2016-02-22 10:20:38 -07:00
|
|
|
this._observer = Polymer.dom(this).observeNodes(this._onNodesChange);
|
2015-06-19 09:36:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
detached: function() {
|
2016-02-22 10:20:38 -07:00
|
|
|
Polymer.dom(this).unobserveNodes(this._observer);
|
|
|
|
this._observer = null;
|
2015-06-19 09:36:51 -07:00
|
|
|
this.opened = false;
|
2016-03-19 12:39:52 -07:00
|
|
|
if (this.withBackdrop) {
|
|
|
|
// Allow user interactions right away.
|
|
|
|
this.backdropElement.close();
|
|
|
|
}
|
2015-06-19 09:36:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggle the opened state of the overlay.
|
|
|
|
*/
|
|
|
|
toggle: function() {
|
2016-01-29 19:43:11 -07:00
|
|
|
this._setCanceled(false);
|
2015-06-19 09:36:51 -07:00
|
|
|
this.opened = !this.opened;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open the overlay.
|
|
|
|
*/
|
|
|
|
open: function() {
|
2016-01-29 19:43:11 -07:00
|
|
|
this._setCanceled(false);
|
2015-06-19 09:36:51 -07:00
|
|
|
this.opened = true;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Close the overlay.
|
|
|
|
*/
|
|
|
|
close: function() {
|
|
|
|
this._setCanceled(false);
|
2016-01-29 19:43:11 -07:00
|
|
|
this.opened = false;
|
2015-06-19 09:36:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2016-02-22 10:20:38 -07:00
|
|
|
* Cancels the overlay.
|
2016-03-11 20:29:37 -07:00
|
|
|
* @param {Event=} event The original event
|
2015-06-19 09:36:51 -07:00
|
|
|
*/
|
2016-02-18 11:20:10 -07:00
|
|
|
cancel: function(event) {
|
|
|
|
var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: true});
|
2015-10-01 23:14:04 -07:00
|
|
|
if (cancelEvent.defaultPrevented) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-19 09:36:51 -07:00
|
|
|
this._setCanceled(true);
|
2016-01-29 19:43:11 -07:00
|
|
|
this.opened = false;
|
2015-06-19 09:36:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
_ensureSetup: function() {
|
|
|
|
if (this._overlaySetup) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this._overlaySetup = true;
|
|
|
|
this.style.outline = 'none';
|
|
|
|
this.style.display = 'none';
|
|
|
|
},
|
|
|
|
|
|
|
|
_openedChanged: function() {
|
|
|
|
if (this.opened) {
|
|
|
|
this.removeAttribute('aria-hidden');
|
|
|
|
} else {
|
|
|
|
this.setAttribute('aria-hidden', 'true');
|
|
|
|
}
|
|
|
|
|
|
|
|
// wait to call after ready only if we're initially open
|
|
|
|
if (!this._overlaySetup) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-22 20:04:42 -07:00
|
|
|
this._manager.addOrRemoveOverlay(this);
|
|
|
|
|
2016-04-04 18:23:42 -07:00
|
|
|
if (this.__openChangedAsync) {
|
2016-04-15 12:20:04 -07:00
|
|
|
window.cancelAnimationFrame(this.__openChangedAsync);
|
2016-01-29 19:43:11 -07:00
|
|
|
}
|
2016-04-26 21:22:32 -07:00
|
|
|
|
|
|
|
// Defer any animation-related code on attached
|
|
|
|
// (_openedChanged gets called again on attached).
|
|
|
|
if (!this.isAttached) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.__isAnimating = true;
|
|
|
|
|
2016-04-04 18:23:42 -07:00
|
|
|
if (this.opened) {
|
|
|
|
if (this.withBackdrop) {
|
|
|
|
this.backdropElement.prepare();
|
2015-06-19 09:36:51 -07:00
|
|
|
}
|
2016-04-26 21:22:32 -07:00
|
|
|
// requestAnimationFrame for non-blocking rendering
|
2016-04-15 12:20:04 -07:00
|
|
|
this.__openChangedAsync = window.requestAnimationFrame(function() {
|
2016-04-04 18:23:42 -07:00
|
|
|
this.__openChangedAsync = null;
|
|
|
|
this._prepareRenderOpened();
|
|
|
|
this._renderOpened();
|
|
|
|
}.bind(this));
|
|
|
|
} else {
|
|
|
|
this._renderClosed();
|
|
|
|
}
|
2015-06-19 09:36:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
_canceledChanged: function() {
|
|
|
|
this.closingReason = this.closingReason || {};
|
|
|
|
this.closingReason.canceled = this.canceled;
|
|
|
|
},
|
|
|
|
|
2016-01-29 19:43:11 -07:00
|
|
|
_withBackdropChanged: function() {
|
2016-02-05 23:33:34 -07:00
|
|
|
// If tabindex is already set, no need to override it.
|
|
|
|
if (this.withBackdrop && !this.hasAttribute('tabindex')) {
|
|
|
|
this.setAttribute('tabindex', '-1');
|
|
|
|
this.__shouldRemoveTabIndex = true;
|
|
|
|
} else if (this.__shouldRemoveTabIndex) {
|
|
|
|
this.removeAttribute('tabindex');
|
|
|
|
this.__shouldRemoveTabIndex = false;
|
|
|
|
}
|
2016-01-29 19:43:11 -07:00
|
|
|
if (this.opened) {
|
2016-03-22 20:04:42 -07:00
|
|
|
this._manager.trackBackdrop();
|
2016-01-29 19:43:11 -07:00
|
|
|
if (this.withBackdrop) {
|
|
|
|
this.backdropElement.prepare();
|
|
|
|
// Give time to be added to document.
|
|
|
|
this.async(function(){
|
|
|
|
this.backdropElement.open();
|
|
|
|
}, 1);
|
|
|
|
} else {
|
|
|
|
this.backdropElement.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-03-19 12:39:52 -07:00
|
|
|
/**
|
|
|
|
* tasks which must occur before opening; e.g. making the element visible.
|
|
|
|
* @protected
|
|
|
|
*/
|
2015-06-19 09:36:51 -07:00
|
|
|
_prepareRenderOpened: function() {
|
2016-02-18 11:20:10 -07:00
|
|
|
|
2016-02-22 10:20:38 -07:00
|
|
|
// Needed to calculate the size of the overlay so that transitions on its size
|
|
|
|
// will have the correct starting points.
|
2015-06-19 09:36:51 -07:00
|
|
|
this._preparePositioning();
|
2016-03-08 11:47:36 -07:00
|
|
|
this.refit();
|
2015-06-19 09:36:51 -07:00
|
|
|
this._finishPositioning();
|
2016-01-29 19:43:11 -07:00
|
|
|
|
2016-02-18 11:20:10 -07:00
|
|
|
// Safari will apply the focus to the autofocus element when displayed for the first time,
|
|
|
|
// so we blur it. Later, _applyFocus will set the focus if necessary.
|
|
|
|
if (this.noAutoFocus && document.activeElement === this._focusNode) {
|
|
|
|
this._focusNode.blur();
|
|
|
|
}
|
2015-06-19 09:36:51 -07:00
|
|
|
},
|
|
|
|
|
2016-03-19 12:39:52 -07:00
|
|
|
/**
|
|
|
|
* Tasks which cause the overlay to actually open; typically play an animation.
|
|
|
|
* @protected
|
|
|
|
*/
|
2015-06-19 09:36:51 -07:00
|
|
|
_renderOpened: function() {
|
|
|
|
if (this.withBackdrop) {
|
|
|
|
this.backdropElement.open();
|
|
|
|
}
|
|
|
|
this._finishRenderOpened();
|
|
|
|
},
|
|
|
|
|
2016-03-19 12:39:52 -07:00
|
|
|
/**
|
|
|
|
* Tasks which cause the overlay to actually close; typically play an animation.
|
|
|
|
* @protected
|
|
|
|
*/
|
2015-06-19 09:36:51 -07:00
|
|
|
_renderClosed: function() {
|
|
|
|
if (this.withBackdrop) {
|
|
|
|
this.backdropElement.close();
|
|
|
|
}
|
|
|
|
this._finishRenderClosed();
|
|
|
|
},
|
|
|
|
|
2016-03-19 12:39:52 -07:00
|
|
|
/**
|
|
|
|
* Tasks to be performed at the end of open action. Will fire `iron-overlay-opened`.
|
|
|
|
* @protected
|
|
|
|
*/
|
2015-06-19 09:36:51 -07:00
|
|
|
_finishRenderOpened: function() {
|
2016-02-22 10:20:38 -07:00
|
|
|
// Focus the child node with [autofocus]
|
2016-01-29 19:43:11 -07:00
|
|
|
this._applyFocus();
|
2015-06-19 09:36:51 -07:00
|
|
|
|
2016-03-08 11:47:36 -07:00
|
|
|
this.notifyResize();
|
|
|
|
this.__isAnimating = false;
|
2015-06-19 09:36:51 -07:00
|
|
|
this.fire('iron-overlay-opened');
|
|
|
|
},
|
|
|
|
|
2016-03-19 12:39:52 -07:00
|
|
|
/**
|
|
|
|
* Tasks to be performed at the end of close action. Will fire `iron-overlay-closed`.
|
|
|
|
* @protected
|
|
|
|
*/
|
2015-06-19 09:36:51 -07:00
|
|
|
_finishRenderClosed: function() {
|
2016-02-22 10:20:38 -07:00
|
|
|
// Hide the overlay and remove the backdrop.
|
2015-06-19 09:36:51 -07:00
|
|
|
this.style.display = 'none';
|
2016-04-04 18:23:42 -07:00
|
|
|
// Reset z-index only at the end of the animation.
|
|
|
|
this.style.zIndex = '';
|
2015-06-19 09:36:51 -07:00
|
|
|
|
2016-01-29 19:43:11 -07:00
|
|
|
this._applyFocus();
|
2016-02-22 10:20:38 -07:00
|
|
|
|
2016-03-08 11:47:36 -07:00
|
|
|
this.notifyResize();
|
|
|
|
this.__isAnimating = false;
|
2015-06-19 09:36:51 -07:00
|
|
|
this.fire('iron-overlay-closed', this.closingReason);
|
|
|
|
},
|
|
|
|
|
|
|
|
_preparePositioning: function() {
|
|
|
|
this.style.transition = this.style.webkitTransition = 'none';
|
|
|
|
this.style.transform = this.style.webkitTransform = 'none';
|
|
|
|
this.style.display = '';
|
|
|
|
},
|
|
|
|
|
2015-06-25 18:13:51 -07:00
|
|
|
_finishPositioning: function() {
|
2016-04-04 18:23:42 -07:00
|
|
|
// First, make it invisible & reactivate animations.
|
2015-06-19 09:36:51 -07:00
|
|
|
this.style.display = 'none';
|
2016-04-04 18:23:42 -07:00
|
|
|
// Force reflow before re-enabling animations so that they don't start.
|
|
|
|
// Set scrollTop to itself so that Closure Compiler doesn't remove this.
|
|
|
|
this.scrollTop = this.scrollTop;
|
2015-06-19 09:36:51 -07:00
|
|
|
this.style.transition = this.style.webkitTransition = '';
|
2016-04-04 18:23:42 -07:00
|
|
|
this.style.transform = this.style.webkitTransform = '';
|
|
|
|
// Now that animations are enabled, make it visible again
|
|
|
|
this.style.display = '';
|
|
|
|
// Force reflow, so that following animations are properly started.
|
|
|
|
// Set scrollTop to itself so that Closure Compiler doesn't remove this.
|
|
|
|
this.scrollTop = this.scrollTop;
|
2015-06-19 09:36:51 -07:00
|
|
|
},
|
|
|
|
|
2016-03-19 12:39:52 -07:00
|
|
|
/**
|
|
|
|
* Applies focus according to the opened state.
|
|
|
|
* @protected
|
|
|
|
*/
|
2015-06-19 09:36:51 -07:00
|
|
|
_applyFocus: function() {
|
|
|
|
if (this.opened) {
|
|
|
|
if (!this.noAutoFocus) {
|
|
|
|
this._focusNode.focus();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this._focusNode.blur();
|
2016-02-22 10:20:38 -07:00
|
|
|
this._focusedChild = null;
|
2015-06-19 09:36:51 -07:00
|
|
|
this._manager.focusOverlay();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-03-19 12:39:52 -07:00
|
|
|
/**
|
|
|
|
* Cancels (closes) the overlay. Call when click happens outside the overlay.
|
|
|
|
* @param {!Event} event
|
|
|
|
* @protected
|
|
|
|
*/
|
2015-06-19 09:36:51 -07:00
|
|
|
_onCaptureClick: function(event) {
|
2016-03-19 12:39:52 -07:00
|
|
|
if (!this.noCancelOnOutsideClick) {
|
|
|
|
this.cancel(event);
|
2015-06-19 09:36:51 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-03-19 12:39:52 -07:00
|
|
|
/**
|
|
|
|
* Keeps track of the focused child. If withBackdrop, traps focus within overlay.
|
|
|
|
* @param {!Event} event
|
|
|
|
* @protected
|
|
|
|
*/
|
2016-02-05 23:33:34 -07:00
|
|
|
_onCaptureFocus: function (event) {
|
2016-03-19 12:39:52 -07:00
|
|
|
if (!this.withBackdrop) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var path = Polymer.dom(event).path;
|
|
|
|
if (path.indexOf(this) === -1) {
|
|
|
|
event.stopPropagation();
|
|
|
|
this._applyFocus();
|
|
|
|
} else {
|
|
|
|
this._focusedChild = path[0];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handles the ESC key event and cancels (closes) the overlay.
|
|
|
|
* @param {!Event} event
|
|
|
|
* @protected
|
|
|
|
*/
|
|
|
|
_onCaptureEsc: function(event) {
|
|
|
|
if (!this.noCancelOnEscKey) {
|
|
|
|
this.cancel(event);
|
2016-02-05 23:33:34 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-03-19 12:39:52 -07:00
|
|
|
/**
|
|
|
|
* Handles TAB key events to track focus changes.
|
|
|
|
* Will wrap focus for overlays withBackdrop.
|
|
|
|
* @param {!Event} event
|
|
|
|
* @protected
|
|
|
|
*/
|
|
|
|
_onCaptureTab: function(event) {
|
|
|
|
// TAB wraps from last to first focusable.
|
|
|
|
// Shift + TAB wraps from first to last focusable.
|
|
|
|
var shift = event.shiftKey;
|
|
|
|
var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusableNode;
|
|
|
|
var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNode;
|
|
|
|
if (this.withBackdrop && this._focusedChild === nodeToCheck) {
|
|
|
|
// We set here the _focusedChild so that _onCaptureFocus will handle the
|
|
|
|
// wrapping of the focus (the next event after tab is focus).
|
|
|
|
this._focusedChild = nodeToSet;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Refits if the overlay is opened and not animating.
|
|
|
|
* @protected
|
|
|
|
*/
|
2015-06-19 09:36:51 -07:00
|
|
|
_onIronResize: function() {
|
2016-04-04 18:23:42 -07:00
|
|
|
if (this.__onIronResizeAsync) {
|
2016-04-15 12:20:04 -07:00
|
|
|
window.cancelAnimationFrame(this.__onIronResizeAsync);
|
2016-04-04 18:23:42 -07:00
|
|
|
this.__onIronResizeAsync = null;
|
2016-03-08 11:47:36 -07:00
|
|
|
}
|
2016-04-04 18:23:42 -07:00
|
|
|
if (this.opened && !this.__isAnimating) {
|
2016-04-15 12:20:04 -07:00
|
|
|
this.__onIronResizeAsync = window.requestAnimationFrame(function() {
|
2016-04-04 18:23:42 -07:00
|
|
|
this.__onIronResizeAsync = null;
|
|
|
|
this.refit();
|
|
|
|
}.bind(this));
|
2015-06-19 09:36:51 -07:00
|
|
|
}
|
2016-02-22 10:20:38 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Will call notifyResize if overlay is opened.
|
|
|
|
* Can be overridden in order to avoid multiple observers on the same node.
|
2016-03-19 12:39:52 -07:00
|
|
|
* @protected
|
2016-02-22 10:20:38 -07:00
|
|
|
*/
|
|
|
|
_onNodesChange: function() {
|
2016-03-08 11:47:36 -07:00
|
|
|
if (this.opened && !this.__isAnimating) {
|
2016-02-22 10:20:38 -07:00
|
|
|
this.notifyResize();
|
|
|
|
}
|
2016-02-22 12:31:28 -07:00
|
|
|
// Store it so we don't query too much.
|
|
|
|
var focusableNodes = this._focusableNodes;
|
|
|
|
this.__firstFocusableNode = focusableNodes[0];
|
|
|
|
this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1];
|
2015-06-19 09:36:51 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** @polymerBehavior */
|
2016-03-21 19:06:21 -07:00
|
|
|
Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl];
|
2015-06-19 09:36:51 -07:00
|
|
|
|
2016-02-23 22:36:48 -07:00
|
|
|
/**
|
2016-04-26 21:22:32 -07:00
|
|
|
* Fired after the overlay opens.
|
|
|
|
* @event iron-overlay-opened
|
|
|
|
*/
|
2016-02-23 22:36:48 -07:00
|
|
|
|
|
|
|
/**
|
2016-04-26 21:22:32 -07:00
|
|
|
* Fired when the overlay is canceled, but before it is closed.
|
|
|
|
* @event iron-overlay-canceled
|
|
|
|
* @param {Event} event The closing of the overlay can be prevented
|
|
|
|
* by calling `event.preventDefault()`.
|
|
|
|
* @param {Event} event.detail It is the original event that originated
|
|
|
|
* the canceling (e.g. ESC keyboard event or click event outside the overlay).
|
|
|
|
*/
|
2016-02-23 22:36:48 -07:00
|
|
|
|
|
|
|
/**
|
2016-04-26 21:22:32 -07:00
|
|
|
* Fired after the overlay closes.
|
|
|
|
* @event iron-overlay-closed
|
|
|
|
* @param {Event} event The event
|
|
|
|
* @param {Object} event.detail It is the `closingReason` property (contains
|
|
|
|
* `canceled`, whether the overlay was canceled).
|
|
|
|
*/
|
2015-06-19 09:36:51 -07:00
|
|
|
|
2016-04-04 18:23:42 -07:00
|
|
|
})();
|
2015-06-19 09:36:51 -07:00
|
|
|
</script>
|