| OLD | NEW |
| 1 <!-- | 1 <!-- |
| 2 @license | 2 @license |
| 3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | 3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |
| 4 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt | 4 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt |
| 5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | 5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
| 6 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt | 6 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt |
| 7 Code distributed by Google as part of the polymer project is also | 7 Code distributed by Google as part of the polymer project is also |
| 8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt | 8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt |
| 9 --> | 9 --> |
| 10 | 10 |
| 11 <link rel="import" href="../polymer/polymer.html"> | 11 <link rel="import" href="../polymer/polymer.html"> |
| 12 <link rel="import" href="../iron-fit-behavior/iron-fit-behavior.html"> | 12 <link rel="import" href="../iron-fit-behavior/iron-fit-behavior.html"> |
| 13 <link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html
"> | 13 <link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html
"> |
| 14 <link rel="import" href="iron-overlay-manager.html"> | 14 <link rel="import" href="iron-overlay-manager.html"> |
| 15 | 15 |
| 16 <script> | 16 <script> |
| 17 (function() { | 17 (function() { |
| 18 'use strict'; | 18 'use strict'; |
| 19 | 19 |
| 20 /** | 20 /** |
| 21 Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or
shown, and displays | 21 Use `Polymer.IronOverlayBehavior` to implement an element that can be hidden or
shown, and displays |
| 22 on top of other content. It includes an optional backdrop, and can be used to im
plement a variety | 22 on top of other content. It includes an optional backdrop, and can be used to im
plement a variety |
| 23 of UI controls including dialogs and drop downs. Multiple overlays may be displa
yed at once. | 23 of UI controls including dialogs and drop downs. Multiple overlays may be displa
yed at once. |
| 24 | 24 |
| 25 See the [demo source code](https://github.com/PolymerElements/iron-overlay-behav
ior/blob/master/demo/simple-overlay.html) |
| 26 for an example. |
| 27 |
| 25 ### Closing and canceling | 28 ### Closing and canceling |
| 26 | 29 |
| 27 A dialog may be hidden by closing or canceling. The difference between close and
cancel is user | 30 An overlay may be hidden by closing or canceling. The difference between close a
nd cancel is user |
| 28 intent. Closing generally implies that the user acknowledged the content on the
overlay. By default, | 31 intent. Closing generally implies that the user acknowledged the content on the
overlay. By default, |
| 29 it will cancel whenever the user taps outside it or presses the escape key. This
behavior is | 32 it will cancel whenever the user taps outside it or presses the escape key. This
behavior is |
| 30 configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click
` properties. | 33 configurable with the `no-cancel-on-esc-key` and the `no-cancel-on-outside-click
` properties. |
| 31 `close()` should be called explicitly by the implementer when the user interacts
with a control | 34 `close()` should be called explicitly by the implementer when the user interacts
with a control |
| 32 in the overlay element. When the dialog is canceled, the overlay fires an 'iron-
overlay-canceled' | 35 in the overlay element. When the dialog is canceled, the overlay fires an 'iron-
overlay-canceled' |
| 33 event. Call `preventDefault` on this event to prevent the overlay from closing. | 36 event. Call `preventDefault` on this event to prevent the overlay from closing. |
| 34 | 37 |
| 35 ### Positioning | 38 ### Positioning |
| 36 | 39 |
| 37 By default the element is sized and positioned to fit and centered inside the wi
ndow. You can | 40 By default the element is sized and positioned to fit and centered inside the wi
ndow. You can |
| 38 position and size it manually using CSS. See `Polymer.IronFitBehavior`. | 41 position and size it manually using CSS. See `Polymer.IronFitBehavior`. |
| 39 | 42 |
| 40 ### Backdrop | 43 ### Backdrop |
| 41 | 44 |
| 42 Set the `with-backdrop` attribute to display a backdrop behind the overlay. The
backdrop is | 45 Set the `with-backdrop` attribute to display a backdrop behind the overlay. The
backdrop is |
| 43 appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page
for styling | 46 appended to `<body>` and is of type `<iron-overlay-backdrop>`. See its doc page
for styling |
| 44 options. | 47 options. |
| 45 | 48 |
| 49 In addition, `with-backdrop` will wrap the focus within the content in the light
DOM. |
| 50 Override the [`_focusableNodes` getter](#Polymer.IronOverlayBehavior:property-_f
ocusableNodes) |
| 51 to achieve a different behavior. |
| 52 |
| 46 ### Limitations | 53 ### Limitations |
| 47 | 54 |
| 48 The element is styled to appear on top of other content by setting its `z-index`
property. You | 55 The element is styled to appear on top of other content by setting its `z-index`
property. You |
| 49 must ensure no element has a stacking context with a higher `z-index` than its p
arent stacking | 56 must ensure no element has a stacking context with a higher `z-index` than its p
arent stacking |
| 50 context. You should place this element as a child of `<body>` whenever possible. | 57 context. You should place this element as a child of `<body>` whenever possible. |
| 51 | 58 |
| 52 @demo demo/index.html | 59 @demo demo/index.html |
| 53 @polymerBehavior Polymer.IronOverlayBehavior | 60 @polymerBehavior Polymer.IronOverlayBehavior |
| 54 */ | 61 */ |
| 55 | 62 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 71 * True if the overlay was canceled when it was last closed. | 78 * True if the overlay was canceled when it was last closed. |
| 72 */ | 79 */ |
| 73 canceled: { | 80 canceled: { |
| 74 observer: '_canceledChanged', | 81 observer: '_canceledChanged', |
| 75 readOnly: true, | 82 readOnly: true, |
| 76 type: Boolean, | 83 type: Boolean, |
| 77 value: false | 84 value: false |
| 78 }, | 85 }, |
| 79 | 86 |
| 80 /** | 87 /** |
| 81 * Set to true to display a backdrop behind the overlay. | 88 * Set to true to display a backdrop behind the overlay. It traps the focu
s |
| 89 * within the light DOM of the overlay. |
| 82 */ | 90 */ |
| 83 withBackdrop: { | 91 withBackdrop: { |
| 84 observer: '_withBackdropChanged', | 92 observer: '_withBackdropChanged', |
| 85 type: Boolean | 93 type: Boolean |
| 86 }, | 94 }, |
| 87 | 95 |
| 88 /** | 96 /** |
| 89 * Set to true to disable auto-focusing the overlay or child nodes with | 97 * Set to true to disable auto-focusing the overlay or child nodes with |
| 90 * the `autofocus` attribute` when the overlay is opened. | 98 * the `autofocus` attribute` when the overlay is opened. |
| 91 */ | 99 */ |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 this.removeAttribute('aria-hidden'); | 317 this.removeAttribute('aria-hidden'); |
| 310 } else { | 318 } else { |
| 311 this.setAttribute('aria-hidden', 'true'); | 319 this.setAttribute('aria-hidden', 'true'); |
| 312 } | 320 } |
| 313 | 321 |
| 314 // wait to call after ready only if we're initially open | 322 // wait to call after ready only if we're initially open |
| 315 if (!this._overlaySetup) { | 323 if (!this._overlaySetup) { |
| 316 return; | 324 return; |
| 317 } | 325 } |
| 318 | 326 |
| 319 this._manager.addOrRemoveOverlay(this); | |
| 320 | |
| 321 if (this.__openChangedAsync) { | 327 if (this.__openChangedAsync) { |
| 322 window.cancelAnimationFrame(this.__openChangedAsync); | 328 window.cancelAnimationFrame(this.__openChangedAsync); |
| 323 } | 329 } |
| 324 | 330 |
| 331 // Synchronously remove the overlay. |
| 332 // The adding is done asynchronously to go out of the scope of the event |
| 333 // which might have generated the opening. |
| 334 if (!this.opened) { |
| 335 this._manager.removeOverlay(this); |
| 336 } |
| 337 |
| 325 // Defer any animation-related code on attached | 338 // Defer any animation-related code on attached |
| 326 // (_openedChanged gets called again on attached). | 339 // (_openedChanged gets called again on attached). |
| 327 if (!this.isAttached) { | 340 if (!this.isAttached) { |
| 328 return; | 341 return; |
| 329 } | 342 } |
| 330 | 343 |
| 331 this.__isAnimating = true; | 344 this.__isAnimating = true; |
| 332 | 345 |
| 333 // requestAnimationFrame for non-blocking rendering | 346 // requestAnimationFrame for non-blocking rendering |
| 334 this.__openChangedAsync = window.requestAnimationFrame(function() { | 347 this.__openChangedAsync = window.requestAnimationFrame(function() { |
| 335 this.__openChangedAsync = null; | 348 this.__openChangedAsync = null; |
| 336 if (this.opened) { | 349 if (this.opened) { |
| 350 this._manager.addOverlay(this); |
| 337 this._prepareRenderOpened(); | 351 this._prepareRenderOpened(); |
| 338 this._renderOpened(); | 352 this._renderOpened(); |
| 339 } else { | 353 } else { |
| 340 this._renderClosed(); | 354 this._renderClosed(); |
| 341 } | 355 } |
| 342 }.bind(this)); | 356 }.bind(this)); |
| 343 }, | 357 }, |
| 344 | 358 |
| 345 _canceledChanged: function() { | 359 _canceledChanged: function() { |
| 346 this.closingReason = this.closingReason || {}; | 360 this.closingReason = this.closingReason || {}; |
| 347 this.closingReason.canceled = this.canceled; | 361 this.closingReason.canceled = this.canceled; |
| 348 }, | 362 }, |
| 349 | 363 |
| 350 _withBackdropChanged: function() { | 364 _withBackdropChanged: function() { |
| 351 // If tabindex is already set, no need to override it. | 365 // If tabindex is already set, no need to override it. |
| 352 if (this.withBackdrop && !this.hasAttribute('tabindex')) { | 366 if (this.withBackdrop && !this.hasAttribute('tabindex')) { |
| 353 this.setAttribute('tabindex', '-1'); | 367 this.setAttribute('tabindex', '-1'); |
| 354 this.__shouldRemoveTabIndex = true; | 368 this.__shouldRemoveTabIndex = true; |
| 355 } else if (this.__shouldRemoveTabIndex) { | 369 } else if (this.__shouldRemoveTabIndex) { |
| 356 this.removeAttribute('tabindex'); | 370 this.removeAttribute('tabindex'); |
| 357 this.__shouldRemoveTabIndex = false; | 371 this.__shouldRemoveTabIndex = false; |
| 358 } | 372 } |
| 359 if (this.opened) { | 373 if (this.opened && this.isAttached) { |
| 360 this._manager.trackBackdrop(); | 374 this._manager.trackBackdrop(); |
| 361 } | 375 } |
| 362 }, | 376 }, |
| 363 | 377 |
| 364 /** | 378 /** |
| 365 * tasks which must occur before opening; e.g. making the element visible. | 379 * tasks which must occur before opening; e.g. making the element visible. |
| 366 * @protected | 380 * @protected |
| 367 */ | 381 */ |
| 368 _prepareRenderOpened: function() { | 382 _prepareRenderOpened: function() { |
| 369 | 383 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 399 /** | 413 /** |
| 400 * Tasks to be performed at the end of open action. Will fire `iron-overlay-
opened`. | 414 * Tasks to be performed at the end of open action. Will fire `iron-overlay-
opened`. |
| 401 * @protected | 415 * @protected |
| 402 */ | 416 */ |
| 403 _finishRenderOpened: function() { | 417 _finishRenderOpened: function() { |
| 404 // Focus the child node with [autofocus] | 418 // Focus the child node with [autofocus] |
| 405 this._applyFocus(); | 419 this._applyFocus(); |
| 406 | 420 |
| 407 this.notifyResize(); | 421 this.notifyResize(); |
| 408 this.__isAnimating = false; | 422 this.__isAnimating = false; |
| 423 |
| 424 // Store it so we don't query too much. |
| 425 var focusableNodes = this._focusableNodes; |
| 426 this.__firstFocusableNode = focusableNodes[0]; |
| 427 this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; |
| 428 |
| 409 this.fire('iron-overlay-opened'); | 429 this.fire('iron-overlay-opened'); |
| 410 }, | 430 }, |
| 411 | 431 |
| 412 /** | 432 /** |
| 413 * Tasks to be performed at the end of close action. Will fire `iron-overlay
-closed`. | 433 * Tasks to be performed at the end of close action. Will fire `iron-overlay
-closed`. |
| 414 * @protected | 434 * @protected |
| 415 */ | 435 */ |
| 416 _finishRenderClosed: function() { | 436 _finishRenderClosed: function() { |
| 417 // Hide the overlay and remove the backdrop. | 437 // Hide the overlay and remove the backdrop. |
| 418 this.style.display = 'none'; | 438 this.style.display = 'none'; |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 503 } | 523 } |
| 504 }, | 524 }, |
| 505 | 525 |
| 506 /** | 526 /** |
| 507 * Handles TAB key events to track focus changes. | 527 * Handles TAB key events to track focus changes. |
| 508 * Will wrap focus for overlays withBackdrop. | 528 * Will wrap focus for overlays withBackdrop. |
| 509 * @param {!Event} event | 529 * @param {!Event} event |
| 510 * @protected | 530 * @protected |
| 511 */ | 531 */ |
| 512 _onCaptureTab: function(event) { | 532 _onCaptureTab: function(event) { |
| 533 if (!this.withBackdrop) { |
| 534 return; |
| 535 } |
| 513 // TAB wraps from last to first focusable. | 536 // TAB wraps from last to first focusable. |
| 514 // Shift + TAB wraps from first to last focusable. | 537 // Shift + TAB wraps from first to last focusable. |
| 515 var shift = event.shiftKey; | 538 var shift = event.shiftKey; |
| 516 var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusable
Node; | 539 var nodeToCheck = shift ? this.__firstFocusableNode : this.__lastFocusable
Node; |
| 517 var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNo
de; | 540 var nodeToSet = shift ? this.__lastFocusableNode : this.__firstFocusableNo
de; |
| 518 if (this.withBackdrop && this._focusedChild === nodeToCheck) { | 541 var shouldWrap = false; |
| 542 if (nodeToCheck === nodeToSet) { |
| 543 // If nodeToCheck is the same as nodeToSet, it means we have an overlay |
| 544 // with 0 or 1 focusables; in either case we still need to trap the |
| 545 // focus within the overlay. |
| 546 shouldWrap = true; |
| 547 } else { |
| 548 // In dom=shadow, the manager will receive focus changes on the main |
| 549 // root but not the ones within other shadow roots, so we can't rely on |
| 550 // _focusedChild, but we should check the deepest active element. |
| 551 var focusedNode = this._manager.deepActiveElement; |
| 552 // If the active element is not the nodeToCheck but the overlay itself, |
| 553 // it means the focus is about to go outside the overlay, hence we |
| 554 // should prevent that (e.g. user opens the overlay and hit Shift+TAB). |
| 555 shouldWrap = (focusedNode === nodeToCheck || focusedNode === this); |
| 556 } |
| 557 |
| 558 if (shouldWrap) { |
| 519 // When the overlay contains the last focusable element of the document | 559 // When the overlay contains the last focusable element of the document |
| 520 // and it's already focused, pressing TAB would move the focus outside | 560 // and it's already focused, pressing TAB would move the focus outside |
| 521 // the document (e.g. to the browser search bar). Similarly, when the | 561 // the document (e.g. to the browser search bar). Similarly, when the |
| 522 // overlay contains the first focusable element of the document and it's | 562 // overlay contains the first focusable element of the document and it's |
| 523 // already focused, pressing Shift+TAB would move the focus outside the | 563 // already focused, pressing Shift+TAB would move the focus outside the |
| 524 // document (e.g. to the browser search bar). | 564 // document (e.g. to the browser search bar). |
| 525 // In both cases, we would not receive a focus event, but only a blur. | 565 // In both cases, we would not receive a focus event, but only a blur. |
| 526 // In order to achieve focus wrapping, we prevent this TAB event and | 566 // In order to achieve focus wrapping, we prevent this TAB event and |
| 527 // force the focus. This will also prevent the focus to temporarily move | 567 // force the focus. This will also prevent the focus to temporarily move |
| 528 // outside the overlay, which might cause scrolling. | 568 // outside the overlay, which might cause scrolling. |
| (...skipping 22 matching lines...) Expand all Loading... |
| 551 | 591 |
| 552 /** | 592 /** |
| 553 * Will call notifyResize if overlay is opened. | 593 * Will call notifyResize if overlay is opened. |
| 554 * Can be overridden in order to avoid multiple observers on the same node. | 594 * Can be overridden in order to avoid multiple observers on the same node. |
| 555 * @protected | 595 * @protected |
| 556 */ | 596 */ |
| 557 _onNodesChange: function() { | 597 _onNodesChange: function() { |
| 558 if (this.opened && !this.__isAnimating) { | 598 if (this.opened && !this.__isAnimating) { |
| 559 this.notifyResize(); | 599 this.notifyResize(); |
| 560 } | 600 } |
| 561 // Store it so we don't query too much. | |
| 562 var focusableNodes = this._focusableNodes; | |
| 563 this.__firstFocusableNode = focusableNodes[0]; | |
| 564 this.__lastFocusableNode = focusableNodes[focusableNodes.length - 1]; | |
| 565 } | 601 } |
| 566 }; | 602 }; |
| 567 | 603 |
| 568 /** @polymerBehavior */ | 604 /** @polymerBehavior */ |
| 569 Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableB
ehavior, Polymer.IronOverlayBehaviorImpl]; | 605 Polymer.IronOverlayBehavior = [Polymer.IronFitBehavior, Polymer.IronResizableB
ehavior, Polymer.IronOverlayBehaviorImpl]; |
| 570 | 606 |
| 571 /** | 607 /** |
| 572 * Fired after the overlay opens. | 608 * Fired after the overlay opens. |
| 573 * @event iron-overlay-opened | 609 * @event iron-overlay-opened |
| 574 */ | 610 */ |
| 575 | 611 |
| 576 /** | 612 /** |
| 577 * Fired when the overlay is canceled, but before it is closed. | 613 * Fired when the overlay is canceled, but before it is closed. |
| 578 * @event iron-overlay-canceled | 614 * @event iron-overlay-canceled |
| 579 * @param {Event} event The closing of the overlay can be prevented | 615 * @param {Event} event The closing of the overlay can be prevented |
| 580 * by calling `event.preventDefault()`. The `event.detail` is the original eve
nt that | 616 * by calling `event.preventDefault()`. The `event.detail` is the original eve
nt that |
| 581 * originated the canceling (e.g. ESC keyboard event or click event outside th
e overlay). | 617 * originated the canceling (e.g. ESC keyboard event or click event outside th
e overlay). |
| 582 */ | 618 */ |
| 583 | 619 |
| 584 /** | 620 /** |
| 585 * Fired after the overlay closes. | 621 * Fired after the overlay closes. |
| 586 * @event iron-overlay-closed | 622 * @event iron-overlay-closed |
| 587 * @param {Event} event The `event.detail` is the `closingReason` property | 623 * @param {Event} event The `event.detail` is the `closingReason` property |
| 588 * (contains `canceled`, whether the overlay was canceled). | 624 * (contains `canceled`, whether the overlay was canceled). |
| 589 */ | 625 */ |
| 590 | 626 |
| 591 })(); | 627 })(); |
| 592 </script> | 628 </script> |
| OLD | NEW |