OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2012 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 21 matching lines...) Expand all Loading... |
32 * @unrestricted | 32 * @unrestricted |
33 */ | 33 */ |
34 UI.Dialog = class extends UI.Widget { | 34 UI.Dialog = class extends UI.Widget { |
35 constructor() { | 35 constructor() { |
36 super(true); | 36 super(true); |
37 this.markAsRoot(); | 37 this.markAsRoot(); |
38 this.registerRequiredCSS('ui/dialog.css'); | 38 this.registerRequiredCSS('ui/dialog.css'); |
39 | 39 |
40 this.contentElement.createChild('content'); | 40 this.contentElement.createChild('content'); |
41 this.contentElement.tabIndex = 0; | 41 this.contentElement.tabIndex = 0; |
42 this.contentElement.addEventListener('focus', this._onFocus.bind(this), fals
e); | 42 this.contentElement.addEventListener('focus', this.focus.bind(this), false); |
43 this._keyDownBound = this._onKeyDown.bind(this); | 43 this.contentElement.addEventListener('keydown', this._onKeyDown.bind(this),
false); |
| 44 this._dimmed = false; |
| 45 this._wrapsContent = false; |
| 46 this._maxSize = null; |
| 47 /** @type {?number} */ |
| 48 this._positionX = null; |
| 49 /** @type {?number} */ |
| 50 this._positionY = null; |
44 | 51 |
45 this._wrapsContent = false; | |
46 this._dimmed = false; | |
47 /** @type {!Map<!HTMLElement, number>} */ | 52 /** @type {!Map<!HTMLElement, number>} */ |
48 this._tabIndexMap = new Map(); | 53 this._tabIndexMap = new Map(); |
49 } | 54 } |
50 | 55 |
51 /** | 56 /** |
52 * @return {boolean} | 57 * @return {boolean} |
53 */ | 58 */ |
54 static hasInstance() { | 59 static hasInstance() { |
55 return !!UI.Dialog._instance; | 60 return !!UI.Dialog._instance; |
56 } | 61 } |
57 | 62 |
58 /** | 63 /** |
59 * @param {!UI.Widget} view | |
60 */ | |
61 static setModalHostView(view) { | |
62 UI.Dialog._modalHostView = view; | |
63 } | |
64 | |
65 /** | |
66 * FIXME: make utility method in Dialog, so clients use it instead of this get
ter. | |
67 * Method should be like Dialog.showModalElement(position params, reposition c
allback). | |
68 * @return {?UI.Widget} | |
69 */ | |
70 static modalHostView() { | |
71 return UI.Dialog._modalHostView; | |
72 } | |
73 | |
74 static modalHostRepositioned() { | |
75 if (UI.Dialog._instance) | |
76 UI.Dialog._instance._position(); | |
77 } | |
78 | |
79 /** | |
80 * @override | 64 * @override |
| 65 * @suppressGlobalPropertiesCheck |
| 66 * TODO(dgozman): pass document in constructor. |
81 */ | 67 */ |
82 show() { | 68 show() { |
83 if (UI.Dialog._instance) | 69 if (UI.Dialog._instance) |
84 UI.Dialog._instance.detach(); | 70 UI.Dialog._instance.detach(); |
85 UI.Dialog._instance = this; | 71 UI.Dialog._instance = this; |
86 | 72 |
87 var document = /** @type {!Document} */ (UI.Dialog._modalHostView.element.ow
nerDocument); | |
88 this._disableTabIndexOnElements(document); | 73 this._disableTabIndexOnElements(document); |
89 | 74 |
90 this._glassPane = new UI.GlassPane(document, this._dimmed); | 75 this._glassPane = new UI.GlassPane(document, this._dimmed, true /* blockPoin
terEvents*/, event => { |
91 this._glassPane.element.addEventListener('click', this._onGlassPaneClick.bin
d(this), false); | 76 this.detach(); |
92 this.element.ownerDocument.body.addEventListener('keydown', this._keyDownBou
nd, false); | 77 event.consume(true); |
93 | 78 }); |
94 super.show(this._glassPane.element); | 79 this._glassPane.show(); |
95 | 80 super.show(this._glassPane.contentElement); |
96 this._position(); | 81 this._glassPane.setContentPosition(this._positionX, this._positionY); |
| 82 this._glassPane.setMaxContentSize(this._effectiveMaxSize()); |
97 this._focusRestorer = new UI.WidgetFocusRestorer(this); | 83 this._focusRestorer = new UI.WidgetFocusRestorer(this); |
98 } | 84 } |
99 | 85 |
100 /** | 86 /** |
101 * @override | 87 * @override |
102 */ | 88 */ |
103 detach() { | 89 detach() { |
104 this._focusRestorer.restore(); | 90 this._focusRestorer.restore(); |
105 | 91 |
106 this.element.ownerDocument.body.removeEventListener('keydown', this._keyDown
Bound, false); | |
107 super.detach(); | 92 super.detach(); |
108 | 93 this._glassPane.hide(); |
109 this._glassPane.dispose(); | |
110 delete this._glassPane; | 94 delete this._glassPane; |
111 | 95 |
112 this._restoreTabIndexOnElements(); | 96 this._restoreTabIndexOnElements(); |
113 | 97 |
114 delete UI.Dialog._instance; | 98 delete UI.Dialog._instance; |
115 } | 99 } |
116 | 100 |
117 addCloseButton() { | 101 addCloseButton() { |
118 var closeButton = this.contentElement.createChild('div', 'dialog-close-butto
n', 'dt-close-button'); | 102 var closeButton = this.contentElement.createChild('div', 'dialog-close-butto
n', 'dt-close-button'); |
119 closeButton.gray = true; | 103 closeButton.gray = true; |
120 closeButton.addEventListener('click', () => this.detach(), false); | 104 closeButton.addEventListener('click', () => this.detach(), false); |
121 } | 105 } |
122 | 106 |
123 /** | 107 /** |
124 * @param {number=} positionX | 108 * @param {?number} positionX |
125 * @param {number=} positionY | 109 * @param {?number} positionY |
126 */ | 110 */ |
127 setPosition(positionX, positionY) { | 111 setPosition(positionX, positionY) { |
128 this._defaultPositionX = positionX; | 112 this._positionX = positionX; |
129 this._defaultPositionY = positionY; | 113 this._positionY = positionY; |
130 } | 114 } |
131 | 115 |
132 /** | 116 /** |
133 * @param {!UI.Size} size | 117 * @param {!UI.Size} size |
134 */ | 118 */ |
135 setMaxSize(size) { | 119 setMaxSize(size) { |
136 this._maxSize = size; | 120 this._maxSize = size; |
137 } | 121 } |
138 | 122 |
139 /** | 123 /** |
| 124 * @return {?UI.Size} |
| 125 */ |
| 126 _effectiveMaxSize() { |
| 127 if (!this._wrapsContent) |
| 128 return this._maxSize; |
| 129 return new UI.Size(this.contentElement.offsetWidth, this.contentElement.offs
etHeight).clipTo(this._maxSize); |
| 130 } |
| 131 |
| 132 /** |
140 * @param {boolean} wraps | 133 * @param {boolean} wraps |
141 */ | 134 */ |
142 setWrapsContent(wraps) { | 135 setWrapsContent(wraps) { |
| 136 this._wrapsContent = wraps; |
143 this.element.classList.toggle('wraps-content', wraps); | 137 this.element.classList.toggle('wraps-content', wraps); |
144 this._wrapsContent = wraps; | |
145 } | 138 } |
146 | 139 |
147 /** | 140 /** |
148 * @param {boolean} dimmed | 141 * @param {boolean} dimmed |
149 */ | 142 */ |
150 setDimmed(dimmed) { | 143 setDimmed(dimmed) { |
151 this._dimmed = dimmed; | 144 this._dimmed = dimmed; |
152 } | 145 } |
153 | 146 |
154 contentResized() { | 147 contentResized() { |
155 if (this._wrapsContent) | 148 if (!this._wrapsContent || !this._glassPane) |
156 this._position(); | 149 return; |
| 150 this._glassPane.setMaxContentSize(this._effectiveMaxSize()); |
157 } | 151 } |
158 | 152 |
159 /** | 153 /** |
160 * @param {!Document} document | 154 * @param {!Document} document |
161 */ | 155 */ |
162 _disableTabIndexOnElements(document) { | 156 _disableTabIndexOnElements(document) { |
163 this._tabIndexMap.clear(); | 157 this._tabIndexMap.clear(); |
164 for (var node = document; node; node = node.traverseNextNode(document)) { | 158 for (var node = document; node; node = node.traverseNextNode(document)) { |
165 if (node instanceof HTMLElement) { | 159 if (node instanceof HTMLElement) { |
166 var element = /** @type {!HTMLElement} */ (node); | 160 var element = /** @type {!HTMLElement} */ (node); |
167 var tabIndex = element.tabIndex; | 161 var tabIndex = element.tabIndex; |
168 if (tabIndex >= 0) { | 162 if (tabIndex >= 0) { |
169 this._tabIndexMap.set(element, tabIndex); | 163 this._tabIndexMap.set(element, tabIndex); |
170 element.tabIndex = -1; | 164 element.tabIndex = -1; |
171 } | 165 } |
172 } | 166 } |
173 } | 167 } |
174 } | 168 } |
175 | 169 |
176 _restoreTabIndexOnElements() { | 170 _restoreTabIndexOnElements() { |
177 for (var element of this._tabIndexMap.keys()) | 171 for (var element of this._tabIndexMap.keys()) |
178 element.tabIndex = /** @type {number} */ (this._tabIndexMap.get(element)); | 172 element.tabIndex = /** @type {number} */ (this._tabIndexMap.get(element)); |
179 this._tabIndexMap.clear(); | 173 this._tabIndexMap.clear(); |
180 } | 174 } |
181 | 175 |
182 /** | 176 /** |
183 * @param {!Event} event | 177 * @param {!Event} event |
184 */ | 178 */ |
185 _onFocus(event) { | |
186 this.focus(); | |
187 } | |
188 | |
189 /** | |
190 * @param {!Event} event | |
191 */ | |
192 _onGlassPaneClick(event) { | |
193 if (!this.element.isSelfOrAncestor(/** @type {?Node} */ (event.target))) | |
194 this.detach(); | |
195 } | |
196 | |
197 _position() { | |
198 var container = UI.Dialog._modalHostView.element; | |
199 | |
200 var width = container.offsetWidth - 10; | |
201 var height = container.offsetHeight - 10; | |
202 | |
203 if (this._wrapsContent) { | |
204 width = Math.min(width, this.contentElement.offsetWidth); | |
205 height = Math.min(height, this.contentElement.offsetHeight); | |
206 } | |
207 | |
208 if (this._maxSize) { | |
209 width = Math.min(width, this._maxSize.width); | |
210 height = Math.min(height, this._maxSize.height); | |
211 } | |
212 | |
213 var positionX; | |
214 if (typeof this._defaultPositionX === 'number') { | |
215 positionX = this._defaultPositionX; | |
216 } else { | |
217 positionX = (container.offsetWidth - width) / 2; | |
218 positionX = Number.constrain(positionX, 0, container.offsetWidth - width); | |
219 } | |
220 | |
221 var positionY; | |
222 if (typeof this._defaultPositionY === 'number') { | |
223 positionY = this._defaultPositionY; | |
224 } else { | |
225 positionY = (container.offsetHeight - height) / 2; | |
226 positionY = Number.constrain(positionY, 0, container.offsetHeight - height
); | |
227 } | |
228 | |
229 this.element.style.width = width + 'px'; | |
230 this.element.style.height = height + 'px'; | |
231 this.element.positionAt(positionX, positionY, container); | |
232 } | |
233 | |
234 /** | |
235 * @param {!Event} event | |
236 */ | |
237 _onKeyDown(event) { | 179 _onKeyDown(event) { |
238 if (event.keyCode === UI.KeyboardShortcut.Keys.Esc.code) { | 180 if (event.keyCode === UI.KeyboardShortcut.Keys.Esc.code) { |
239 event.consume(true); | 181 event.consume(true); |
240 this.detach(); | 182 this.detach(); |
241 } | 183 } |
242 } | 184 } |
243 }; | 185 }; |
244 | |
245 | |
246 /** @type {?Element} */ | |
247 UI.Dialog._previousFocusedElement = null; | |
248 | |
249 /** @type {?UI.Widget} */ | |
250 UI.Dialog._modalHostView = null; | |
OLD | NEW |