Chromecast: registers old client_id metrics pref.
[chromium-blink-merge.git] / third_party / polymer / v0_8 / components-chromium / paper-dialog-behavior / paper-dialog-behavior-extracted.js
blob56dbb01e5a219ddc5f4708dacb1e9b9cf291f611
3 /*
4 Use `Polymer.PaperDialogBehavior` and `paper-dialog-common.css` to implement a Material Design
5 dialog.
7 For example, if `<paper-dialog-impl>` implements this behavior:
9     <paper-dialog-impl>
10         <h2>Header</h2>
11         <div>Dialog body</div>
12         <div class="buttons">
13             <paper-button dialog-dismiss>Cancel</paper-button>
14             <paper-button dialog-confirm>Accept</paper-button>
15         </div>
16     </paper-dialog-impl>
18 `paper-dialog-common.css` provide styles for a header, content area, and an action area for buttons.
19 Use the `<h2>` tag for the header and the `buttons` class for the action area. You can use the
20 `paper-dialog-scrollable` element (in its own repository) if you need a scrolling content area.
22 Use the `dialog-dismiss` and `dialog-confirm` attributes on interactive controls to close the
23 dialog. If the user dismisses the dialog with `dialog-confirm`, the `closingReason` will update
24 to include `confirmed: true`.
26 ### Styling
28 The following custom properties and mixins are available for styling.
30 Custom property | Description | Default
31 ----------------|-------------|----------
32 `--paper-dialog-background-color` | Dialog background color                     | `--primary-background-color`
33 `--paper-dialog-color`            | Dialog foreground color                     | `--primary-text-color`
34 `--paper-dialog`                  | Mixin applied to the dialog                 | `{}`
35 `--paper-dialog-title`            | Mixin applied to the title (`<h2>`) element | `{}`
36 `--paper-dialog-button-color`     | Button area foreground color                | `--default-primary-color`
38 ### Accessibility
40 This element has `role="dialog"` by default. Depending on the context, it may be more appropriate
41 to override this attribute with `role="alertdialog"`. The header (a `<h2>` element) will
43 If `modal` is set, the element will set `aria-modal` and prevent the focus from exiting the element.
44 It will also ensure that focus remains in the dialog.
46 The `aria-labelledby` attribute will be set to the header element, if one exists.
48 @hero hero.svg
49 @demo demo/index.html
50 @polymerBehavior Polymer.PaperDialogBehavior
53   Polymer.PaperDialogBehaviorImpl = {
55     hostAttributes: {
56       'role': 'dialog',
57       'tabindex': '-1'
58     },
60     properties: {
62       /**
63        * If `modal` is true, this implies `no-cancel-on-outside-click` and `with-backdrop`.
64        */
65       modal: {
66         observer: '_modalChanged',
67         type: Boolean,
68         value: false
69       },
71       _lastFocusedElement: {
72         type: Node
73       },
75       _boundOnFocus: {
76         type: Function,
77         value: function() {
78           return this._onFocus.bind(this);
79         }
80       },
82       _boundOnBackdropClick: {
83         type: Function,
84         value: function() {
85           return this._onBackdropClick.bind(this);
86         }
87       }
89     },
91     listeners: {
92       'click': '_onDialogClick',
93       'iron-overlay-opened': '_onIronOverlayOpened',
94       'iron-overlay-closed': '_onIronOverlayClosed'
95     },
97     attached: function() {
98       this._observer = this._observe(this);
99       this._updateAriaLabelledBy();
100     },
102     detached: function() {
103       if (this._observer) {
104         this._observer.disconnect();
105       }
106     },
108     _observe: function(node) {
109       var observer = new MutationObserver(function() {
110         this._updateAriaLabelledBy();
111       }.bind(this));
112       observer.observe(node, {
113         childList: true,
114         subtree: true
115       });
116       return observer;
117     },
119     _modalChanged: function() {
120       if (this.modal) {
121         this.setAttribute('aria-modal', 'true');
122       } else {
123         this.setAttribute('aria-modal', 'false');
124       }
125       // modal implies noCancelOnOutsideClick and withBackdrop if true, don't overwrite
126       // those properties otherwise.
127       if (this.modal) {
128         this.noCancelOnOutsideClick = true;
129         this.withBackdrop = true;
130       }
131     },
133     _updateAriaLabelledBy: function() {
134       var header = Polymer.dom(this).querySelector('h2');
135       if (!header) {
136         this.removeAttribute('aria-labelledby');
137         return;
138       }
139       var headerId = header.getAttribute('id');
140       if (headerId && this.getAttribute('aria-labelledby') === headerId) {
141         return;
142       }
143       // set aria-describedBy to the header element
144       var labelledById;
145       if (headerId) {
146         labelledById = headerId;
147       } else {
148         labelledById = 'paper-dialog-header-' + new Date().getUTCMilliseconds();
149         header.setAttribute('id', labelledById);
150       }
151       this.setAttribute('aria-labelledby', labelledById);
152     },
154     _updateClosingReasonConfirmed: function(confirmed) {
155       this.closingReason = this.closingReason || {};
156       this.closingReason.confirmed = confirmed;
157     },
159     _onDialogClick: function(event) {
160       var target = event.target;
161       while (target !== this) {
162         if (target.hasAttribute('dialog-dismiss')) {
163           this._updateClosingReasonConfirmed(false);
164           this.close();
165           break;
166         } else if (target.hasAttribute('dialog-confirm')) {
167           this._updateClosingReasonConfirmed(true);
168           this.close();
169           break;
170         }
171         target = target.parentNode;
172       }
173     },
175     _onIronOverlayOpened: function() {
176       if (this.modal) {
177         document.body.addEventListener('focus', this._boundOnFocus, true);
178         this.backdropElement.addEventListener('click', this._boundOnBackdropClick);
179       }
180     },
182     _onIronOverlayClosed: function() {
183       document.body.removeEventListener('focus', this._boundOnFocus, true);
184       this.backdropElement.removeEventListener('click', this._boundOnBackdropClick);
185     },
187     _onFocus: function(event) {
188       if (this.modal) {
189         var target = event.target;
190         while (target && target !== this && target !== document.body) {
191           target = target.parentNode;
192         }
193         if (target) {
194           if (target === document.body) {
195             if (this._lastFocusedElement) {
196               this._lastFocusedElement.focus();
197             } else {
198               this._focusNode.focus();
199             }
200           } else {
201             this._lastFocusedElement = event.target;
202           }
203         }
204       }
205     },
207     _onBackdropClick: function() {
208       if (this.modal) {
209         if (this._lastFocusedElement) {
210           this._lastFocusedElement.focus();
211         } else {
212           this._focusNode.focus();
213         }
214       }
215     }
217   };
219   /** @polymerBehavior */
220   Polymer.PaperDialogBehavior = [Polymer.IronOverlayBehavior, Polymer.PaperDialogBehaviorImpl];