| OLD | NEW |
| 1 (function() { | 1 (function() { |
| 2 'use strict'; | 2 'use strict'; |
| 3 | 3 |
| 4 /** | 4 /** @polymerBehavior */ |
| 5 Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or
shown, and displays | |
| 6 on top of other content. It includes an optional backdrop, and can be used to im
plement a variety | |
| 7 of UI controls including dialogs and drop downs. Multiple overlays may be displa
yed at once. | |
| 8 | |
| 9 See the [demo source code](https://github.com/PolymerElements/iron-overlay-behav
ior/blob/master/demo/simple-overlay.html) | |
| 10 for an example. | |
| 11 | |
| 12 ### Closing and canceling | |
| 13 | |
| 14 An overlay may be hidden by closing or canceling. The difference between close a
nd cancel is user | |
| 15 intent. Closing generally implies that the user acknowledged the content on the
overlay. By default, | |
| 16 it will cancel whenever the user taps outside it or presses the escape key. This
behavior is | |
| 17 configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click
` properties. | |
| 18 `close()` should be called explicitly by the implementer when the user interacts
with a control | |
| 19 in the overlay element. When the dialog is canceled, the overlay fires an 'iron-
overlay-canceled' | |
| 20 event. Call `preventDefault` on this event to prevent the overlay from closing. | |
| 21 | |
| 22 ### Positioning | |
| 23 | |
| 24 By default the element is sized and positioned to fit and centered inside the wi
ndow. You can | |
| 25 position and size it manually using CSS. See `Polymer.IronFitBehavior`. | |
| 26 | |
| 27 ### Backdrop | |
| 28 | |
| 29 Set the `with-backdrop` attribute to display a backdrop behind the overlay. The
backdrop is | |
| 30 appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page
for styling | |
| 31 options. | |
| 32 | |
| 33 In addition, `with-backdrop` will wrap the focus within the content in the light
DOM. | |
| 34 Override the [`_focusableNodes` getter](#Polymer.IronOverlayBehavior:property-_f
ocusableNodes) | |
| 35 to achieve a different behavior. | |
| 36 | |
| 37 ### Limitations | |
| 38 | |
| 39 The element is styled to appear on top of other content by setting its `z-index`
property. You | |
| 40 must ensure no element has a stacking context with a higher `z-index` than its p
arent stacking | |
| 41 context. You should place this element as a child of `<body>` whenever possible. | |
| 42 | |
| 43 @demo demo/index.html | |
| 44 @polymerBehavior Polymer.IronOverlayBehavior | |
| 45 */ | |
| 46 | |
| 47 Polymer.IronOverlayBehaviorImpl = { | 5 Polymer.IronOverlayBehaviorImpl = { |
| 48 | 6 |
| 49 properties: { | 7 properties: { |
| 50 | 8 |
| 51 /** | 9 /** |
| 52 * True if the overlay is currently displayed. | 10 * True if the overlay is currently displayed. |
| 53 */ | 11 */ |
| 54 opened: { | 12 opened: { |
| 55 observer: '_openedChanged', | 13 observer: '_openedChanged', |
| 56 type: Boolean, | 14 type: Boolean, |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 172 * Array of nodes that can receive focus (overlay included), ordered by `tab
index`. | 130 * Array of nodes that can receive focus (overlay included), ordered by `tab
index`. |
| 173 * This is used to retrieve which is the first and last focusable nodes in o
rder | 131 * This is used to retrieve which is the first and last focusable nodes in o
rder |
| 174 * to wrap the focus for overlays `with-backdrop`. | 132 * to wrap the focus for overlays `with-backdrop`. |
| 175 * | 133 * |
| 176 * If you know what is your content (specifically the first and last focusab
le children), | 134 * If you know what is your content (specifically the first and last focusab
le children), |
| 177 * you can override this method to return only `[firstFocusable, lastFocusab
le];` | 135 * you can override this method to return only `[firstFocusable, lastFocusab
le];` |
| 178 * @type {Array<Node>} | 136 * @type {Array<Node>} |
| 179 * @protected | 137 * @protected |
| 180 */ | 138 */ |
| 181 get _focusableNodes() { | 139 get _focusableNodes() { |
| 182 // Elements that can be focused even if they have [disabled] attribute. | 140 return Polymer.IronFocusablesHelper.getTabbableNodes(this); |
| 183 var FOCUSABLE_WITH_DISABLED = [ | |
| 184 'a[href]', | |
| 185 'area[href]', | |
| 186 'iframe', | |
| 187 '[tabindex]', | |
| 188 '[contentEditable=true]' | |
| 189 ]; | |
| 190 | |
| 191 // Elements that cannot be focused if they have [disabled] attribute. | |
| 192 var FOCUSABLE_WITHOUT_DISABLED = [ | |
| 193 'input', | |
| 194 'select', | |
| 195 'textarea', | |
| 196 'button' | |
| 197 ]; | |
| 198 | |
| 199 // Discard elements with tabindex=-1 (makes them not focusable). | |
| 200 var selector = FOCUSABLE_WITH_DISABLED.join(':not([tabindex="-1"]),') + | |
| 201 ':not([tabindex="-1"]),' + | |
| 202 FOCUSABLE_WITHOUT_DISABLED.join(':not([disabled]):not([tabindex="-1"]),'
) + | |
| 203 ':not([disabled]):not([tabindex="-1"])'; | |
| 204 | |
| 205 var focusables = Polymer.dom(this).querySelectorAll(selector); | |
| 206 if (this.tabIndex >= 0) { | |
| 207 // Insert at the beginning because we might have all elements with tabIn
dex = 0, | |
| 208 // and the overlay should be the first of the list. | |
| 209 focusables.splice(0, 0, this); | |
| 210 } | |
| 211 // Sort by tabindex. | |
| 212 return focusables.sort(function (a, b) { | |
| 213 if (a.tabIndex === b.tabIndex) { | |
| 214 return 0; | |
| 215 } | |
| 216 if (a.tabIndex === 0 || a.tabIndex > b.tabIndex) { | |
| 217 return 1; | |
| 218 } | |
| 219 return -1; | |
| 220 }); | |
| 221 }, | 141 }, |
| 222 | 142 |
| 223 ready: function() { | 143 ready: function() { |
| 224 // Used to skip calls to notifyResize and refit while the overlay is anima
ting. | 144 // Used to skip calls to notifyResize and refit while the overlay is anima
ting. |
| 225 this.__isAnimating = false; | 145 this.__isAnimating = false; |
| 226 // with-backdrop needs tabindex to be set in order to trap the focus. | 146 // with-backdrop needs tabindex to be set in order to trap the focus. |
| 227 // If it is not set, IronOverlayBehavior will set it, and remove it if wit
h-backdrop = false. | 147 // If it is not set, IronOverlayBehavior will set it, and remove it if wit
h-backdrop = false. |
| 228 this.__shouldRemoveTabIndex = false; | 148 this.__shouldRemoveTabIndex = false; |
| 229 // Used for wrapping the focus on TAB / Shift+TAB. | 149 // Used for wrapping the focus on TAB / Shift+TAB. |
| 230 this.__firstFocusableNode = this.__lastFocusableNode = null; | 150 this.__firstFocusableNode = this.__lastFocusableNode = null; |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 284 cancel: function(event) { | 204 cancel: function(event) { |
| 285 var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: t
rue}); | 205 var cancelEvent = this.fire('iron-overlay-canceled', event, {cancelable: t
rue}); |
| 286 if (cancelEvent.defaultPrevented) { | 206 if (cancelEvent.defaultPrevented) { |
| 287 return; | 207 return; |
| 288 } | 208 } |
| 289 | 209 |
| 290 this._setCanceled(true); | 210 this._setCanceled(true); |
| 291 this.opened = false; | 211 this.opened = false; |
| 292 }, | 212 }, |
| 293 | 213 |
| 214 /** |
| 215 * Invalidates the cached tabbable nodes. To be called when any of the focus
able |
| 216 * content changes (e.g. a button is disabled). |
| 217 */ |
| 218 invalidateTabbables: function() { |
| 219 this.__firstFocusableNode = this.__lastFocusableNode = null; |
| 220 }, |
| 221 |
| 294 _ensureSetup: function() { | 222 _ensureSetup: function() { |
| 295 if (this._overlaySetup) { | 223 if (this._overlaySetup) { |
| 296 return; | 224 return; |
| 297 } | 225 } |
| 298 this._overlaySetup = true; | 226 this._overlaySetup = true; |
| 299 this.style.outline = 'none'; | 227 this.style.outline = 'none'; |
| 300 this.style.display = 'none'; | 228 this.style.display = 'none'; |
| 301 }, | 229 }, |
| 302 | 230 |
| 303 /** | 231 /** |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 382 }, | 310 }, |
| 383 | 311 |
| 384 /** | 312 /** |
| 385 * Tasks to be performed at the end of open action. Will fire `iron-overlay-
opened`. | 313 * Tasks to be performed at the end of open action. Will fire `iron-overlay-
opened`. |
| 386 * @protected | 314 * @protected |
| 387 */ | 315 */ |
| 388 _finishRenderOpened: function() { | 316 _finishRenderOpened: function() { |
| 389 this.notifyResize(); | 317 this.notifyResize(); |
| 390 this.__isAnimating = false; | 318 this.__isAnimating = false; |
| 391 | 319 |
| 392 // Store it so we don't query too much. | |
| 393 var focusableNodes = this._focusableNodes; | |
| 394 this.__firstFocusableNode = focusableNodes[0]; | |
| 395 this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; | |
| 396 | |
| 397 this.fire('iron-overlay-opened'); | 320 this.fire('iron-overlay-opened'); |
| 398 }, | 321 }, |
| 399 | 322 |
| 400 /** | 323 /** |
| 401 * Tasks to be performed at the end of close action. Will fire `iron-overlay
-closed`. | 324 * Tasks to be performed at the end of close action. Will fire `iron-overlay
-closed`. |
| 402 * @protected | 325 * @protected |
| 403 */ | 326 */ |
| 404 _finishRenderClosed: function() { | 327 _finishRenderClosed: function() { |
| 405 // Hide the overlay. | 328 // Hide the overlay. |
| 406 this.style.display = 'none'; | 329 this.style.display = 'none'; |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 503 /** | 426 /** |
| 504 * Handles TAB key events to track focus changes. | 427 * Handles TAB key events to track focus changes. |
| 505 * Will wrap focus for overlays withBackdrop. | 428 * Will wrap focus for overlays withBackdrop. |
| 506 * @param {!Event} event | 429 * @param {!Event} event |
| 507 * @protected | 430 * @protected |
| 508 */ | 431 */ |
| 509 _onCaptureTab: function(event) { | 432 _onCaptureTab: function(event) { |
| 510 if (!this.withBackdrop) { | 433 if (!this.withBackdrop) { |
| 511 return; | 434 return; |
| 512 } | 435 } |
| 436 this.__ensureFirstLastFocusables(); |
| 513 // TAB wraps from last to first focusable. | 437 // TAB wraps from last to first focusable. |
| 514 // Shift + TAB wraps from first to last focusable. | 438 // Shift + TAB wraps from first to last focusable. |
| 515 var shift = event.shiftKey; | 439 var shift = event.shiftKey; |
| 516 var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusable
Node; | 440 var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusable
Node; |
| 517 var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNo
de; | 441 var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNo
de; |
| 518 var shouldWrap = false; | 442 var shouldWrap = false; |
| 519 if (nodeToCheck === nodeToSet) { | 443 if (nodeToCheck === nodeToSet) { |
| 520 // If nodeToCheck is the same as nodeToSet, it means we have an overlay | 444 // If nodeToCheck is the same as nodeToSet, it means we have an overlay |
| 521 // with 0 or 1 focusables; in either case we still need to trap the | 445 // with 0 or 1 focusables; in either case we still need to trap the |
| 522 // focus within the overlay. | 446 // focus within the overlay. |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 559 } | 483 } |
| 560 }, | 484 }, |
| 561 | 485 |
| 562 /** | 486 /** |
| 563 * Will call notifyResize if overlay is opened. | 487 * Will call notifyResize if overlay is opened. |
| 564 * Can be overridden in order to avoid multiple observers on the same node. | 488 * Can be overridden in order to avoid multiple observers on the same node. |
| 565 * @protected | 489 * @protected |
| 566 */ | 490 */ |
| 567 _onNodesChange: function() { | 491 _onNodesChange: function() { |
| 568 if (this.opened && !this.__isAnimating) { | 492 if (this.opened && !this.__isAnimating) { |
| 493 // It might have added focusable nodes, so invalidate cached values. |
| 494 this.invalidateTabbables(); |
| 569 this.notifyResize(); | 495 this.notifyResize(); |
| 570 } | 496 } |
| 571 }, | 497 }, |
| 572 | 498 |
| 573 /** | 499 /** |
| 500 * Will set first and last focusable nodes if any of them is not set. |
| 501 * @private |
| 502 */ |
| 503 __ensureFirstLastFocusables: function() { |
| 504 if (!this.__firstFocusableNode || !this.__lastFocusableNode) { |
| 505 var focusableNodes = this._focusableNodes; |
| 506 this.__firstFocusableNode = focusableNodes[0]; |
| 507 this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; |
| 508 } |
| 509 }, |
| 510 |
| 511 /** |
| 574 * Tasks executed when opened changes: prepare for the opening, move the | 512 * Tasks executed when opened changes: prepare for the opening, move the |
| 575 * focus, update the manager, render opened/closed. | 513 * focus, update the manager, render opened/closed. |
| 576 * @private | 514 * @private |
| 577 */ | 515 */ |
| 578 __openedChanged: function() { | 516 __openedChanged: function() { |
| 579 if (this.opened) { | 517 if (this.opened) { |
| 580 // Make overlay visible, then add it to the manager. | 518 // Make overlay visible, then add it to the manager. |
| 581 this._prepareRenderOpened(); | 519 this._prepareRenderOpened(); |
| 582 this._manager.addOverlay(this); | 520 this._manager.addOverlay(this); |
| 583 // Move the focus to the child node with [autofocus]. | 521 // Move the focus to the child node with [autofocus]. |
| (...skipping 23 matching lines...) Expand all Loading... |
| 607 } | 545 } |
| 608 var self = this; | 546 var self = this; |
| 609 this.__raf = window.requestAnimationFrame(function nextAnimationFrame() { | 547 this.__raf = window.requestAnimationFrame(function nextAnimationFrame() { |
| 610 self.__raf = null; | 548 self.__raf = null; |
| 611 callback.call(self); | 549 callback.call(self); |
| 612 }); | 550 }); |
| 613 } | 551 } |
| 614 | 552 |
| 615 }; | 553 }; |
| 616 | 554 |
| 617 /** @polymerBehavior */ | 555 /** |
| 556 Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden o
r shown, and displays |
| 557 on top of other content. It includes an optional backdrop, and can be used to
implement a variety |
| 558 of UI controls including dialogs and drop downs. Multiple overlays may be disp
layed at once. |
| 559 |
| 560 See the [demo source code](https://github.com/PolymerElements/iron-overlay-beh
avior/blob/master/demo/simple-overlay.html) |
| 561 for an example. |
| 562 |
| 563 ### Closing and canceling |
| 564 |
| 565 An overlay may be hidden by closing or canceling. The difference between close
and cancel is user |
| 566 intent. Closing generally implies that the user acknowledged the content on th
e overlay. By default, |
| 567 it will cancel whenever the user taps outside it or presses the escape key. Th
is behavior is |
| 568 configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-cli
ck` properties. |
| 569 `close()` should be called explicitly by the implementer when the user interac
ts with a control |
| 570 in the overlay element. When the dialog is canceled, the overlay fires an 'iro
n-overlay-canceled' |
| 571 event. Call `preventDefault` on this event to prevent the overlay from closing
. |
| 572 |
| 573 ### Positioning |
| 574 |
| 575 By default the element is sized and positioned to fit and centered inside the
window. You can |
| 576 position and size it manually using CSS. See `Polymer.IronFitBehavior`. |
| 577 |
| 578 ### Backdrop |
| 579 |
| 580 Set the `with-backdrop` attribute to display a backdrop behind the overlay. Th
e backdrop is |
| 581 appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc pag
e for styling |
| 582 options. |
| 583 |
| 584 In addition, `with-backdrop` will wrap the focus within the content in the lig
ht DOM. |
| 585 Override the [`_focusableNodes` getter](#Polymer.IronOverlayBehavior:property-
_focusableNodes) |
| 586 to achieve a different behavior. |
| 587 |
| 588 ### Limitations |
| 589 |
| 590 The element is styled to appear on top of other content by setting its `z-inde
x` property. You |
| 591 must ensure no element has a stacking context with a higher `z-index` than its
parent stacking |
| 592 context. You should place this element as a child of `<body>` whenever possibl
e. |
| 593 |
| 594 @demo demo/index.html |
| 595 @polymerBehavior |
| 596 */ |
| 618 Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableB
ehavior, Polymer.IronOverlayBehaviorImpl]; | 597 Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableB
ehavior, Polymer.IronOverlayBehaviorImpl]; |
| 619 | 598 |
| 620 /** | 599 /** |
| 621 * Fired after the overlay opens. | 600 * Fired after the overlay opens. |
| 622 * @event iron-overlay-opened | 601 * @event iron-overlay-opened |
| 623 */ | 602 */ |
| 624 | 603 |
| 625 /** | 604 /** |
| 626 * Fired when the overlay is canceled, but before it is closed. | 605 * Fired when the overlay is canceled, but before it is closed. |
| 627 * @event iron-overlay-canceled | 606 * @event iron-overlay-canceled |
| 628 * @param {Event} event The closing of the overlay can be prevented | 607 * @param {Event} event The closing of the overlay can be prevented |
| 629 * by calling `event.preventDefault()`. The `event.detail` is the original eve
nt that | 608 * by calling `event.preventDefault()`. The `event.detail` is the original eve
nt that |
| 630 * originated the canceling (e.g. ESC keyboard event or click event outside th
e overlay). | 609 * originated the canceling (e.g. ESC keyboard event or click event outside th
e overlay). |
| 631 */ | 610 */ |
| 632 | 611 |
| 633 /** | 612 /** |
| 634 * Fired after the overlay closes. | 613 * Fired after the overlay closes. |
| 635 * @event iron-overlay-closed | 614 * @event iron-overlay-closed |
| 636 * @param {Event} event The `event.detail` is the `closingReason` property | 615 * @param {Event} event The `event.detail` is the `closingReason` property |
| 637 * (contains `canceled`, whether the overlay was canceled). | 616 * (contains `canceled`, whether the overlay was canceled). |
| 638 */ | 617 */ |
| 639 | 618 |
| 640 })(); | 619 })(); |
| OLD | NEW |