4 Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or shown, and displays
5 on top of other content. It includes an optional backdrop, and can be used to implement a variety
6 of UI controls including dialogs and drop downs. Multiple overlays may be displayed at once.
8 ### Closing and canceling
10 A dialog may be hidden by closing or canceling. The difference between close and cancel is user
11 intent. Closing generally implies that the user acknowledged the content on the overlay. By default,
12 it will cancel whenever the user taps outside it or presses the escape key. This behavior is
13 configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties.
14 `close()` should be called explicitly by the implementer when the user interacts with a control
15 in the overlay element.
19 By default the element is sized and positioned to fit and centered inside the window. You can
20 position and size it manually using CSS. See `Polymer.IronFitBehavior`.
24 Set the `with-backdrop` attribute to display a backdrop behind the overlay. The backdrop is
25 appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page for styling
30 The element is styled to appear on top of other content by setting its `z-index` property. You
31 must ensure no element has a stacking context with a higher `z-index` than its parent stacking
32 context. You should place this element as a child of `<body>` whenever possible.
35 @polymerBehavior Polymer.IronOverlayBehavior
38 Polymer
.IronOverlayBehaviorImpl
= {
43 * True if the overlay is currently displayed.
46 observer
: '_openedChanged',
53 * True if the overlay was canceled when it was last closed.
56 observer
: '_canceledChanged',
63 * Set to true to display a backdrop behind the overlay.
71 * Set to true to disable auto-focusing the overlay or child nodes with
72 * the `autofocus` attribute` when the overlay is opened.
80 * Set to true to disable canceling the overlay with the ESC key.
88 * Set to true to disable canceling the overlay by clicking outside it.
90 noCancelOnOutsideClick
: {
96 * Returns the reason this dialog was last closed.
99 // was a getter before, but needs to be a property so other
100 // behaviors can override this.
106 value
: Polymer
.IronOverlayManager
109 _boundOnCaptureClick
: {
112 return this._onCaptureClick
.bind(this);
116 _boundOnCaptureKeydown
: {
119 return this._onCaptureKeydown
.bind(this);
127 'iron-resize': '_onIronResize'
131 * The backdrop element.
134 get backdropElement() {
135 return this._backdrop
;
139 return Polymer
.dom(this).querySelector('[autofocus]') || this;
142 registered: function() {
143 this._backdrop
= document
.createElement('iron-overlay-backdrop');
148 if (this._callOpenedWhenReady
) {
149 this._openedChanged();
153 detached: function() {
155 this._completeBackdrop();
156 this._manager
.removeOverlay(this);
160 * Toggle the opened state of the overlay.
163 this.opened
= !this.opened
;
171 this.closingReason
= {canceled
: false};
179 this._setCanceled(false);
183 * Cancels the overlay.
187 this._setCanceled(true);
190 _ensureSetup: function() {
191 if (this._overlaySetup
) {
194 this._overlaySetup
= true;
195 this.style
.outline
= 'none';
196 this.style
.display
= 'none';
199 _openedChanged: function() {
201 this.removeAttribute('aria-hidden');
203 this.setAttribute('aria-hidden', 'true');
206 // wait to call after ready only if we're initially open
207 if (!this._overlaySetup
) {
208 this._callOpenedWhenReady
= this.opened
;
211 if (this._openChangedAsync
) {
212 this.cancelAsync(this._openChangedAsync
);
215 this._toggleListeners();
218 this._prepareRenderOpened();
221 // async here to allow overlay layer to become visible.
222 this._openChangedAsync
= this.async(function() {
223 // overlay becomes visible here
224 this.style
.display
= '';
225 // force layout to ensure transitions will go
226 /** @suppress {suspiciousCode} */ this.offsetWidth
;
228 this._renderOpened();
230 this._renderClosed();
232 this._openChangedAsync
= null;
237 _canceledChanged: function() {
238 this.closingReason
= this.closingReason
|| {};
239 this.closingReason
.canceled
= this.canceled
;
242 _toggleListener: function(enable
, node
, event
, boundListener
, capture
) {
244 node
.addEventListener(event
, boundListener
, capture
);
246 node
.removeEventListener(event
, boundListener
, capture
);
250 _toggleListeners: function() {
251 if (this._toggleListenersAsync
) {
252 this.cancelAsync(this._toggleListenersAsync
);
254 // async so we don't auto-close immediately via a click.
255 this._toggleListenersAsync
= this.async(function() {
256 this._toggleListener(this.opened
, document
, 'click', this._boundOnCaptureClick
, true);
257 this._toggleListener(this.opened
, document
, 'keydown', this._boundOnCaptureKeydown
, true);
258 this._toggleListenersAsync
= null;
262 // tasks which must occur before opening; e.g. making the element visible
263 _prepareRenderOpened: function() {
264 this._manager
.addOverlay(this);
266 if (this.withBackdrop
) {
267 this.backdropElement
.prepare();
268 this._manager
.trackBackdrop(this);
271 this._preparePositioning();
273 this._finishPositioning();
276 // tasks which cause the overlay to actually open; typically play an
278 _renderOpened: function() {
279 if (this.withBackdrop
) {
280 this.backdropElement
.open();
282 this._finishRenderOpened();
285 _renderClosed: function() {
286 if (this.withBackdrop
) {
287 this.backdropElement
.close();
289 this._finishRenderClosed();
292 _onTransitionend: function(event
) {
293 // make sure this is our transition event.
294 if (event
&& event
.target
!== this) {
298 this._finishRenderOpened();
300 this._finishRenderClosed();
304 _finishRenderOpened: function() {
305 // focus the child node with [autofocus]
306 if (!this.noAutoFocus
) {
307 this._focusNode
.focus();
310 this.fire('iron-overlay-opened');
312 this._squelchNextResize
= true;
313 this.async(this.notifyResize
);
316 _finishRenderClosed: function() {
317 // hide the overlay and remove the backdrop
319 this.style
.display
= 'none';
320 this._completeBackdrop();
321 this._manager
.removeOverlay(this);
323 this._focusNode
.blur();
324 // focus the next overlay, if there is one
325 this._manager
.focusOverlay();
327 this.fire('iron-overlay-closed', this.closingReason
);
329 this._squelchNextResize
= true;
330 this.async(this.notifyResize
);
333 _completeBackdrop: function() {
334 if (this.withBackdrop
) {
335 this._manager
.trackBackdrop(this);
336 this.backdropElement
.complete();
340 _preparePositioning: function() {
341 this.style
.transition
= this.style
.webkitTransition
= 'none';
342 this.style
.transform
= this.style
.webkitTransform
= 'none';
343 this.style
.display
= '';
346 _finishPositioning: function() {
347 this.style
.display
= 'none';
348 this.style
.transform
= this.style
.webkitTransform
= '';
349 // force layout to avoid application of transform
350 /** @suppress {suspiciousCode} */ this.offsetWidth
;
351 this.style
.transition
= this.style
.webkitTransition
= '';
354 _applyFocus: function() {
356 if (!this.noAutoFocus
) {
357 this._focusNode
.focus();
360 this._focusNode
.blur();
361 this._manager
.focusOverlay();
365 _onCaptureClick: function(event
) {
366 // attempt to close asynchronously and prevent the close of a tap event is immediately heard
367 // on target. This is because in shadow dom due to event retargetting event.target is not
369 if (!this.noCancelOnOutsideClick
&& (this._manager
.currentOverlay() == this)) {
370 this._cancelJob
= this.async(function() {
376 _onClick: function(event
) {
377 if (this._cancelJob
) {
378 this.cancelAsync(this._cancelJob
);
379 this._cancelJob
= null;
383 _onCaptureKeydown: function(event
) {
385 if (!this.noCancelOnEscKey
&& (event
.keyCode
=== ESC
)) {
387 event
.stopPropagation();
391 _onIronResize: function() {
392 if (this._squelchNextResize
) {
393 this._squelchNextResize
= false;
402 * Fired after the `iron-overlay` opens.
403 * @event iron-overlay-opened
407 * Fired after the `iron-overlay` closes.
408 * @event iron-overlay-closed
409 * @param {{canceled: (boolean|undefined)}} set to the `closingReason` attribute
413 /** @polymerBehavior */
414 Polymer
.IronOverlayBehavior
= [Polymer
.IronFitBehavior
, Polymer
.IronResizableBehavior
, Polymer
.IronOverlayBehaviorImpl
];