<!-- @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-overlay-behavior/iron-overlay-behavior.html"> <link rel="import" href="../paper-styles/paper-styles.html"> <script> /** Use `Polymer.PaperDialogBehavior` and `paper-dialog-common.css` to implement a Material Design dialog. For example, if `<paper-dialog-impl>` implements this behavior: <paper-dialog-impl> <h2>Header</h2> <div>Dialog body</div> <div class="buttons"> <paper-button dialog-dismiss>Cancel</paper-button> <paper-button dialog-confirm>Accept</paper-button> </div> </paper-dialog-impl> `paper-dialog-common.css` provide styles for a header, content area, and an action area for buttons. Use the `<h2>` tag for the header and the `buttons` class for the action area. You can use the `paper-dialog-scrollable` element (in its own repository) if you need a scrolling content area. Use the `dialog-dismiss` and `dialog-confirm` attributes on interactive controls to close the dialog. If the user dismisses the dialog with `dialog-confirm`, the `closingReason` will update to include `confirmed: true`. ### Styling The following custom properties and mixins are available for styling. Custom property | Description | Default ----------------|-------------|---------- `--paper-dialog-background-color` | Dialog background color | `--primary-background-color` `--paper-dialog-color` | Dialog foreground color | `--primary-text-color` `--paper-dialog` | Mixin applied to the dialog | `{}` `--paper-dialog-title` | Mixin applied to the title (`<h2>`) element | `{}` `--paper-dialog-button-color` | Button area foreground color | `--default-primary-color` ### Accessibility This element has `role="dialog"` by default. Depending on the context, it may be more appropriate to override this attribute with `role="alertdialog"`. If `modal` is set, the element will set `aria-modal` and prevent the focus from exiting the element. It will also ensure that focus remains in the dialog. The `aria-labelledby` attribute will be set to the header element, if one exists. @hero hero.svg @demo demo/index.html @polymerBehavior Polymer.PaperDialogBehavior */ Polymer.PaperDialogBehaviorImpl = { hostAttributes: { 'role': 'dialog', 'tabindex': '-1' }, properties: { /** * If `modal` is true, this implies `no-cancel-on-outside-click` and `with-backdrop`. */ modal: { observer: '_modalChanged', type: Boolean, value: false }, /** @type {?Node} */ _lastFocusedElement: { type: Object }, _boundOnFocus: { type: Function, value: function() { return this._onFocus.bind(this); } }, _boundOnBackdropClick: { type: Function, value: function() { return this._onBackdropClick.bind(this); } } }, listeners: { 'tap': '_onDialogClick', 'iron-overlay-opened': '_onIronOverlayOpened', 'iron-overlay-closed': '_onIronOverlayClosed' }, attached: function() { this._observer = this._observe(this); this._updateAriaLabelledBy(); }, detached: function() { if (this._observer) { this._observer.disconnect(); } }, _observe: function(node) { var observer = new MutationObserver(function() { this._updateAriaLabelledBy(); }.bind(this)); observer.observe(node, { childList: true, subtree: true }); return observer; }, _modalChanged: function() { if (this.modal) { this.setAttribute('aria-modal', 'true'); } else { this.setAttribute('aria-modal', 'false'); } // modal implies noCancelOnOutsideClick and withBackdrop if true, don't overwrite // those properties otherwise. if (this.modal) { this.noCancelOnOutsideClick = true; this.withBackdrop = true; } }, _updateAriaLabelledBy: function() { var header = Polymer.dom(this).querySelector('h2'); if (!header) { this.removeAttribute('aria-labelledby'); return; } var headerId = header.getAttribute('id'); if (headerId && this.getAttribute('aria-labelledby') === headerId) { return; } // set aria-describedBy to the header element var labelledById; if (headerId) { labelledById = headerId; } else { labelledById = 'paper-dialog-header-' + new Date().getUTCMilliseconds(); header.setAttribute('id', labelledById); } this.setAttribute('aria-labelledby', labelledById); }, _updateClosingReasonConfirmed: function(confirmed) { this.closingReason = this.closingReason || {}; this.closingReason.confirmed = confirmed; }, _onDialogClick: function(event) { var target = event.target; while (target && target !== this) { if (target.hasAttribute) { if (target.hasAttribute('dialog-dismiss')) { this._updateClosingReasonConfirmed(false); this.close(); break; } else if (target.hasAttribute('dialog-confirm')) { this._updateClosingReasonConfirmed(true); this.close(); break; } } target = target.parentNode; } }, _onIronOverlayOpened: function() { if (this.modal) { document.body.addEventListener('focus', this._boundOnFocus, true); this.backdropElement.addEventListener('click', this._boundOnBackdropClick); } }, _onIronOverlayClosed: function() { document.body.removeEventListener('focus', this._boundOnFocus, true); this.backdropElement.removeEventListener('click', this._boundOnBackdropClick); }, _onFocus: function(event) { if (this.modal) { var target = event.target; while (target && target !== this && target !== document.body) { target = target.parentNode; } if (target) { if (target === document.body) { if (this._lastFocusedElement) { this._lastFocusedElement.focus(); } else { this._focusNode.focus(); } } else { this._lastFocusedElement = event.target; } } } }, _onBackdropClick: function() { if (this.modal) { if (this._lastFocusedElement) { this._lastFocusedElement.focus(); } else { this._focusNode.focus(); } } } }; /** @polymerBehavior */ Polymer.PaperDialogBehavior = [Polymer.IronOverlayBehavior, Polymer.PaperDialogBehaviorImpl]; </script>