2 Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or shown, and displays
3 on top of other content. It includes an optional backdrop, and can be used to implement a variety
4 of UI controls including dialogs and drop downs. Multiple overlays may be displayed at once.
6 ### Closing and canceling
8 A dialog may be hidden by closing or canceling. The difference between close and cancel is user
9 intent. Closing generally implies that the user acknowledged the content on the overlay. By default,
10 it will cancel whenever the user taps outside it or presses the escape key. This behavior is
11 configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click` properties.
12 `close()` should be called explicitly by the implementer when the user interacts with a control
13 in the overlay element.
17 By default the element is sized and positioned to fit and centered inside the window. You can
18 position and size it manually using CSS. See `Polymer.IronFitBehavior`.
22 Set the `with-backdrop` attribute to display a backdrop behind the overlay. The backdrop is
23 appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page for styling
28 The element is styled to appear on top of other content by setting its `z-index` property. You
29 must ensure no element has a stacking context with a higher `z-index` than its parent stacking
30 context. You should place this element as a child of `<body>` whenever possible.
33 @polymerBehavior Polymer.IronOverlayBehavior
36 Polymer.IronOverlayBehaviorImpl = {
41 * True if the overlay is currently displayed.
44 observer: '_openedChanged',
51 * True if the overlay was canceled when it was last closed.
54 observer: '_canceledChanged',
61 * Set to true to display a backdrop behind the overlay.
69 * Set to true to disable auto-focusing the overlay or child nodes with
70 * the `autofocus` attribute` when the overlay is opened.
78 * Set to true to disable canceling the overlay with the ESC key.
86 * Set to true to disable canceling the overlay by clicking outside it.
88 noCancelOnOutsideClick: {
94 * Returns the reason this dialog was last closed.
97 // was a getter before, but needs to be a property so other
98 // behaviors can override this.
104 value: Polymer.IronOverlayManager
107 _boundOnCaptureClick: {
110 return this._onCaptureClick.bind(this);
114 _boundOnCaptureKeydown: {
117 return this._onCaptureKeydown.bind(this);
125 'iron-resize': '_onIronResize'
129 * The backdrop element.
132 get backdropElement() {
133 return this._backdrop;
137 return Polymer.dom(this).querySelector('[autofocus]') || this;
140 registered: function() {
141 this._backdrop = document.createElement('iron-overlay-backdrop');
146 if (this._callOpenedWhenReady) {
147 this._openedChanged();
151 detached: function() {
153 this._completeBackdrop();
154 this._manager.removeOverlay(this);
158 * Toggle the opened state of the overlay.
161 this.opened = !this.opened;
169 this.closingReason = {canceled: false};
177 this._setCanceled(false);
181 * Cancels the overlay.
185 this._setCanceled(true);
188 _ensureSetup: function() {
189 if (this._overlaySetup) {
192 this._overlaySetup = true;
193 this.style.outline = 'none';
194 this.style.display = 'none';
197 _openedChanged: function() {
199 this.removeAttribute('aria-hidden');
201 this.setAttribute('aria-hidden', 'true');
204 // wait to call after ready only if we're initially open
205 if (!this._overlaySetup) {
206 this._callOpenedWhenReady = this.opened;
209 if (this._openChangedAsync) {
210 this.cancelAsync(this._openChangedAsync);
213 this._toggleListeners();
216 this._prepareRenderOpened();
219 // async here to allow overlay layer to become visible.
220 this._openChangedAsync = this.async(function() {
221 // overlay becomes visible here
222 this.style.display = '';
223 // force layout to ensure transitions will go
224 /** @suppress {suspiciousCode} */ this.offsetWidth;
226 this._renderOpened();
228 this._renderClosed();
230 this._openChangedAsync = null;
235 _canceledChanged: function() {
236 this.closingReason = this.closingReason || {};
237 this.closingReason.canceled = this.canceled;
240 _toggleListener: function(enable, node, event, boundListener, capture) {
242 node.addEventListener(event, boundListener, capture);
244 node.removeEventListener(event, boundListener, capture);
248 _toggleListeners: function() {
249 if (this._toggleListenersAsync) {
250 this.cancelAsync(this._toggleListenersAsync);
252 // async so we don't auto-close immediately via a click.
253 this._toggleListenersAsync = this.async(function() {
254 this._toggleListener(this.opened, document, 'click', this._boundOnCaptureClick, true);
255 this._toggleListener(this.opened, document, 'keydown', this._boundOnCaptureKeydown, true);
256 this._toggleListenersAsync = null;
260 // tasks which must occur before opening; e.g. making the element visible
261 _prepareRenderOpened: function() {
262 this._manager.addOverlay(this);
264 if (this.withBackdrop) {
265 this.backdropElement.prepare();
266 this._manager.trackBackdrop(this);
269 this._preparePositioning();
271 this._finishPositioning();
274 // tasks which cause the overlay to actually open; typically play an
276 _renderOpened: function() {
277 if (this.withBackdrop) {
278 this.backdropElement.open();
280 this._finishRenderOpened();
283 _renderClosed: function() {
284 if (this.withBackdrop) {
285 this.backdropElement.close();
287 this._finishRenderClosed();
290 _onTransitionend: function(event) {
291 // make sure this is our transition event.
292 if (event && event.target !== this) {
296 this._finishRenderOpened();
298 this._finishRenderClosed();
302 _finishRenderOpened: function() {
303 // focus the child node with [autofocus]
304 if (!this.noAutoFocus) {
305 this._focusNode.focus();
308 this.fire('iron-overlay-opened');
310 this._squelchNextResize = true;
311 this.async(this.notifyResize);
314 _finishRenderClosed: function() {
315 // hide the overlay and remove the backdrop
317 this.style.display = 'none';
318 this._completeBackdrop();
319 this._manager.removeOverlay(this);
321 this._focusNode.blur();
322 // focus the next overlay, if there is one
323 this._manager.focusOverlay();
325 this.fire('iron-overlay-closed', this.closingReason);
327 this._squelchNextResize = true;
328 this.async(this.notifyResize);
331 _completeBackdrop: function() {
332 if (this.withBackdrop) {
333 this._manager.trackBackdrop(this);
334 this.backdropElement.complete();
338 _preparePositioning: function() {
339 this.style.transition = this.style.webkitTransition = 'none';
340 this.style.transform = this.style.webkitTransform = 'none';
341 this.style.display = '';
344 _finishPositioning: function() {
345 this.style.display = 'none';
346 this.style.transform = this.style.webkitTransform = '';
347 // force layout to avoid application of transform
348 /** @suppress {suspiciousCode} */ this.offsetWidth;
349 this.style.transition = this.style.webkitTransition = '';
352 _applyFocus: function() {
354 if (!this.noAutoFocus) {
355 this._focusNode.focus();
358 this._focusNode.blur();
359 this._manager.focusOverlay();
363 _onCaptureClick: function(event) {
364 // attempt to close asynchronously and prevent the close of a tap event is immediately heard
365 // on target. This is because in shadow dom due to event retargetting event.target is not
367 if (!this.noCancelOnOutsideClick && (this._manager.currentOverlay() == this)) {
368 this._cancelJob = this.async(function() {
374 _onClick: function(event) {
375 if (this._cancelJob) {
376 this.cancelAsync(this._cancelJob);
377 this._cancelJob = null;
381 _onCaptureKeydown: function(event) {
383 if (!this.noCancelOnEscKey && (event.keyCode === ESC)) {
385 event.stopPropagation();
389 _onIronResize: function() {
390 if (this._squelchNextResize) {
391 this._squelchNextResize = false;
400 * Fired after the `iron-overlay` opens.
401 * @event iron-overlay-opened
405 * Fired after the `iron-overlay` closes.
406 * @event iron-overlay-closed
407 * @param {{canceled: (boolean|undefined)}} set to the `closingReason` attribute
411 /** @polymerBehavior */
412 Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableBehavior, Polymer.IronOverlayBehaviorImpl];