@vaadin/overlay 24.1.5 → 24.2.0-alpha10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -8
- package/src/vaadin-overlay-mixin.d.ts +81 -0
- package/src/vaadin-overlay-mixin.js +472 -0
- package/src/vaadin-overlay-stack-mixin.d.ts +22 -0
- package/src/vaadin-overlay-stack-mixin.js +103 -0
- package/src/vaadin-overlay-styles.js +66 -0
- package/src/vaadin-overlay.d.ts +3 -74
- package/src/vaadin-overlay.js +7 -611
package/src/vaadin-overlay.js
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
* Copyright (c) 2017 - 2023 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { afterNextRender } from '@polymer/polymer/lib/utils/render-status.js';
|
|
7
6
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
8
|
-
import { isIOS } from '@vaadin/component-base/src/browser-utils.js';
|
|
9
7
|
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
10
8
|
import { processTemplates } from '@vaadin/component-base/src/templates.js';
|
|
11
|
-
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
12
|
-
import {
|
|
9
|
+
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
10
|
+
import { OverlayMixin } from './vaadin-overlay-mixin.js';
|
|
11
|
+
import { overlayStyles } from './vaadin-overlay-styles.js';
|
|
12
|
+
|
|
13
|
+
registerStyles('vaadin-overlay', overlayStyles, { moduleId: 'vaadin-overlay-styles' });
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* `<vaadin-overlay>` is a Web Component for creating overlays. The content of the overlay
|
|
@@ -73,76 +74,11 @@ import { OverlayFocusMixin } from './vaadin-overlay-focus-mixin.js';
|
|
|
73
74
|
* @extends HTMLElement
|
|
74
75
|
* @mixes ThemableMixin
|
|
75
76
|
* @mixes DirMixin
|
|
76
|
-
* @mixes
|
|
77
|
+
* @mixes OverlayMixin
|
|
77
78
|
*/
|
|
78
|
-
class Overlay extends
|
|
79
|
+
class Overlay extends OverlayMixin(ThemableMixin(DirMixin(PolymerElement))) {
|
|
79
80
|
static get template() {
|
|
80
81
|
return html`
|
|
81
|
-
<style>
|
|
82
|
-
:host {
|
|
83
|
-
z-index: 200;
|
|
84
|
-
position: fixed;
|
|
85
|
-
|
|
86
|
-
/* Despite of what the names say, <vaadin-overlay> is just a container
|
|
87
|
-
for position/sizing/alignment. The actual overlay is the overlay part. */
|
|
88
|
-
|
|
89
|
-
/* Default position constraints: the entire viewport. Note: themes can
|
|
90
|
-
override this to introduce gaps between the overlay and the viewport. */
|
|
91
|
-
top: 0;
|
|
92
|
-
right: 0;
|
|
93
|
-
bottom: var(--vaadin-overlay-viewport-bottom);
|
|
94
|
-
left: 0;
|
|
95
|
-
|
|
96
|
-
/* Use flexbox alignment for the overlay part. */
|
|
97
|
-
display: flex;
|
|
98
|
-
flex-direction: column; /* makes dropdowns sizing easier */
|
|
99
|
-
/* Align to center by default. */
|
|
100
|
-
align-items: center;
|
|
101
|
-
justify-content: center;
|
|
102
|
-
|
|
103
|
-
/* Allow centering when max-width/max-height applies. */
|
|
104
|
-
margin: auto;
|
|
105
|
-
|
|
106
|
-
/* The host is not clickable, only the overlay part is. */
|
|
107
|
-
pointer-events: none;
|
|
108
|
-
|
|
109
|
-
/* Remove tap highlight on touch devices. */
|
|
110
|
-
-webkit-tap-highlight-color: transparent;
|
|
111
|
-
|
|
112
|
-
/* CSS API for host */
|
|
113
|
-
--vaadin-overlay-viewport-bottom: 0;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
:host([hidden]),
|
|
117
|
-
:host(:not([opened]):not([closing])) {
|
|
118
|
-
display: none !important;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
[part='overlay'] {
|
|
122
|
-
-webkit-overflow-scrolling: touch;
|
|
123
|
-
overflow: auto;
|
|
124
|
-
pointer-events: auto;
|
|
125
|
-
|
|
126
|
-
/* Prevent overflowing the host in MSIE 11 */
|
|
127
|
-
max-width: 100%;
|
|
128
|
-
box-sizing: border-box;
|
|
129
|
-
|
|
130
|
-
-webkit-tap-highlight-color: initial; /* reenable tap highlight inside */
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
[part='backdrop'] {
|
|
134
|
-
z-index: -1;
|
|
135
|
-
content: '';
|
|
136
|
-
background: rgba(0, 0, 0, 0.5);
|
|
137
|
-
position: fixed;
|
|
138
|
-
top: 0;
|
|
139
|
-
left: 0;
|
|
140
|
-
bottom: 0;
|
|
141
|
-
right: 0;
|
|
142
|
-
pointer-events: auto;
|
|
143
|
-
}
|
|
144
|
-
</style>
|
|
145
|
-
|
|
146
82
|
<div id="backdrop" part="backdrop" hidden$="[[!withBackdrop]]"></div>
|
|
147
83
|
<div part="overlay" id="overlay" tabindex="0">
|
|
148
84
|
<div part="content" id="content">
|
|
@@ -156,553 +92,13 @@ class Overlay extends OverlayFocusMixin(ThemableMixin(DirMixin(PolymerElement)))
|
|
|
156
92
|
return 'vaadin-overlay';
|
|
157
93
|
}
|
|
158
94
|
|
|
159
|
-
static get properties() {
|
|
160
|
-
return {
|
|
161
|
-
/**
|
|
162
|
-
* When true, the overlay is visible and attached to body.
|
|
163
|
-
*/
|
|
164
|
-
opened: {
|
|
165
|
-
type: Boolean,
|
|
166
|
-
notify: true,
|
|
167
|
-
observer: '_openedChanged',
|
|
168
|
-
reflectToAttribute: true,
|
|
169
|
-
},
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Owner element passed with renderer function
|
|
173
|
-
* @type {HTMLElement}
|
|
174
|
-
*/
|
|
175
|
-
owner: Element,
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Custom function for rendering the content of the overlay.
|
|
179
|
-
* Receives three arguments:
|
|
180
|
-
*
|
|
181
|
-
* - `root` The root container DOM element. Append your content to it.
|
|
182
|
-
* - `owner` The host element of the renderer function.
|
|
183
|
-
* - `model` The object with the properties related with rendering.
|
|
184
|
-
* @type {OverlayRenderer | null | undefined}
|
|
185
|
-
*/
|
|
186
|
-
renderer: Function,
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* When true the overlay has backdrop on top of content when opened.
|
|
190
|
-
* @type {boolean}
|
|
191
|
-
*/
|
|
192
|
-
withBackdrop: {
|
|
193
|
-
type: Boolean,
|
|
194
|
-
value: false,
|
|
195
|
-
reflectToAttribute: true,
|
|
196
|
-
},
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Object with properties that is passed to `renderer` function
|
|
200
|
-
*/
|
|
201
|
-
model: Object,
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* When true the overlay won't disable the main content, showing
|
|
205
|
-
* it doesn't change the functionality of the user interface.
|
|
206
|
-
* @type {boolean}
|
|
207
|
-
*/
|
|
208
|
-
modeless: {
|
|
209
|
-
type: Boolean,
|
|
210
|
-
value: false,
|
|
211
|
-
reflectToAttribute: true,
|
|
212
|
-
observer: '_modelessChanged',
|
|
213
|
-
},
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* When set to true, the overlay is hidden. This also closes the overlay
|
|
217
|
-
* immediately in case there is a closing animation in progress.
|
|
218
|
-
* @type {boolean}
|
|
219
|
-
*/
|
|
220
|
-
hidden: {
|
|
221
|
-
type: Boolean,
|
|
222
|
-
reflectToAttribute: true,
|
|
223
|
-
observer: '_hiddenChanged',
|
|
224
|
-
},
|
|
225
|
-
|
|
226
|
-
/** @private */
|
|
227
|
-
_mouseDownInside: {
|
|
228
|
-
type: Boolean,
|
|
229
|
-
},
|
|
230
|
-
|
|
231
|
-
/** @private */
|
|
232
|
-
_mouseUpInside: {
|
|
233
|
-
type: Boolean,
|
|
234
|
-
},
|
|
235
|
-
|
|
236
|
-
/** @private */
|
|
237
|
-
_oldOwner: Element,
|
|
238
|
-
|
|
239
|
-
/** @private */
|
|
240
|
-
_oldModel: Object,
|
|
241
|
-
|
|
242
|
-
/** @private */
|
|
243
|
-
_oldRenderer: Object,
|
|
244
|
-
|
|
245
|
-
/** @private */
|
|
246
|
-
_oldOpened: Boolean,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
static get observers() {
|
|
251
|
-
return ['_rendererOrDataChanged(renderer, owner, model, opened)'];
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Returns all attached overlays in visual stacking order.
|
|
256
|
-
* @private
|
|
257
|
-
*/
|
|
258
|
-
static get __attachedInstances() {
|
|
259
|
-
return Array.from(document.body.children)
|
|
260
|
-
.filter((el) => el instanceof Overlay && !el.hasAttribute('closing'))
|
|
261
|
-
.sort((a, b) => a.__zIndex - b.__zIndex || 0);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
constructor() {
|
|
265
|
-
super();
|
|
266
|
-
this._boundMouseDownListener = this._mouseDownListener.bind(this);
|
|
267
|
-
this._boundMouseUpListener = this._mouseUpListener.bind(this);
|
|
268
|
-
this._boundOutsideClickListener = this._outsideClickListener.bind(this);
|
|
269
|
-
this._boundKeydownListener = this._keydownListener.bind(this);
|
|
270
|
-
|
|
271
|
-
/* c8 ignore next 3 */
|
|
272
|
-
if (isIOS) {
|
|
273
|
-
this._boundIosResizeListener = () => this._detectIosNavbar();
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Returns true if this is the last one in the opened overlays stack
|
|
279
|
-
* @return {boolean}
|
|
280
|
-
* @protected
|
|
281
|
-
*/
|
|
282
|
-
get _last() {
|
|
283
|
-
return this === Overlay.__attachedInstances.pop();
|
|
284
|
-
}
|
|
285
|
-
|
|
286
95
|
/** @protected */
|
|
287
96
|
ready() {
|
|
288
97
|
super.ready();
|
|
289
98
|
|
|
290
|
-
// Need to add dummy click listeners to this and the backdrop or else
|
|
291
|
-
// the document click event listener (_outsideClickListener) may never
|
|
292
|
-
// get invoked on iOS Safari (reproducible in <vaadin-dialog>
|
|
293
|
-
// and <vaadin-context-menu>).
|
|
294
|
-
this.addEventListener('click', () => {});
|
|
295
|
-
this.$.backdrop.addEventListener('click', () => {});
|
|
296
|
-
|
|
297
99
|
processTemplates(this);
|
|
298
100
|
}
|
|
299
101
|
|
|
300
|
-
/** @private */
|
|
301
|
-
_detectIosNavbar() {
|
|
302
|
-
/* c8 ignore next 15 */
|
|
303
|
-
if (!this.opened) {
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const innerHeight = window.innerHeight;
|
|
308
|
-
const innerWidth = window.innerWidth;
|
|
309
|
-
|
|
310
|
-
const landscape = innerWidth > innerHeight;
|
|
311
|
-
|
|
312
|
-
const clientHeight = document.documentElement.clientHeight;
|
|
313
|
-
|
|
314
|
-
if (landscape && clientHeight > innerHeight) {
|
|
315
|
-
this.style.setProperty('--vaadin-overlay-viewport-bottom', `${clientHeight - innerHeight}px`);
|
|
316
|
-
} else {
|
|
317
|
-
this.style.setProperty('--vaadin-overlay-viewport-bottom', '0');
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* @param {Event=} sourceEvent
|
|
323
|
-
*/
|
|
324
|
-
close(sourceEvent) {
|
|
325
|
-
const evt = new CustomEvent('vaadin-overlay-close', {
|
|
326
|
-
bubbles: true,
|
|
327
|
-
cancelable: true,
|
|
328
|
-
detail: { sourceEvent },
|
|
329
|
-
});
|
|
330
|
-
this.dispatchEvent(evt);
|
|
331
|
-
if (!evt.defaultPrevented) {
|
|
332
|
-
this.opened = false;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/** @protected */
|
|
337
|
-
connectedCallback() {
|
|
338
|
-
super.connectedCallback();
|
|
339
|
-
|
|
340
|
-
/* c8 ignore next 3 */
|
|
341
|
-
if (this._boundIosResizeListener) {
|
|
342
|
-
this._detectIosNavbar();
|
|
343
|
-
window.addEventListener('resize', this._boundIosResizeListener);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/** @protected */
|
|
348
|
-
disconnectedCallback() {
|
|
349
|
-
super.disconnectedCallback();
|
|
350
|
-
|
|
351
|
-
/* c8 ignore next 3 */
|
|
352
|
-
if (this._boundIosResizeListener) {
|
|
353
|
-
window.removeEventListener('resize', this._boundIosResizeListener);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Requests an update for the content of the overlay.
|
|
359
|
-
* While performing the update, it invokes the renderer passed in the `renderer` property.
|
|
360
|
-
*
|
|
361
|
-
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
362
|
-
*/
|
|
363
|
-
requestContentUpdate() {
|
|
364
|
-
if (this.renderer) {
|
|
365
|
-
this.renderer.call(this.owner, this, this.owner, this.model);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/** @private */
|
|
370
|
-
_mouseDownListener(event) {
|
|
371
|
-
this._mouseDownInside = event.composedPath().indexOf(this.$.overlay) >= 0;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/** @private */
|
|
375
|
-
_mouseUpListener(event) {
|
|
376
|
-
this._mouseUpInside = event.composedPath().indexOf(this.$.overlay) >= 0;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Whether to close the overlay on outside click or not.
|
|
381
|
-
* Override this method to customize the closing logic.
|
|
382
|
-
*
|
|
383
|
-
* @param {Event} _event
|
|
384
|
-
* @return {boolean}
|
|
385
|
-
* @protected
|
|
386
|
-
*/
|
|
387
|
-
_shouldCloseOnOutsideClick(_event) {
|
|
388
|
-
return this._last;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Outside click listener used in capture phase to close the overlay before
|
|
393
|
-
* propagating the event to the listener on the element that triggered it.
|
|
394
|
-
* Otherwise, calling `open()` would result in closing and re-opening.
|
|
395
|
-
*
|
|
396
|
-
* @private
|
|
397
|
-
*/
|
|
398
|
-
_outsideClickListener(event) {
|
|
399
|
-
if (event.composedPath().includes(this.$.overlay) || this._mouseDownInside || this._mouseUpInside) {
|
|
400
|
-
this._mouseDownInside = false;
|
|
401
|
-
this._mouseUpInside = false;
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
if (!this._shouldCloseOnOutsideClick(event)) {
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
const evt = new CustomEvent('vaadin-overlay-outside-click', {
|
|
410
|
-
bubbles: true,
|
|
411
|
-
cancelable: true,
|
|
412
|
-
detail: { sourceEvent: event },
|
|
413
|
-
});
|
|
414
|
-
this.dispatchEvent(evt);
|
|
415
|
-
|
|
416
|
-
if (this.opened && !evt.defaultPrevented) {
|
|
417
|
-
this.close(event);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Listener used to close whe overlay on Escape press, if it is the last one.
|
|
423
|
-
* @private
|
|
424
|
-
*/
|
|
425
|
-
_keydownListener(event) {
|
|
426
|
-
if (!this._last) {
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// Only close modeless overlay on Esc press when it contains focus
|
|
431
|
-
if (this.modeless && !event.composedPath().includes(this.$.overlay)) {
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
if (event.key === 'Escape') {
|
|
436
|
-
const evt = new CustomEvent('vaadin-overlay-escape-press', {
|
|
437
|
-
bubbles: true,
|
|
438
|
-
cancelable: true,
|
|
439
|
-
detail: { sourceEvent: event },
|
|
440
|
-
});
|
|
441
|
-
this.dispatchEvent(evt);
|
|
442
|
-
|
|
443
|
-
if (this.opened && !evt.defaultPrevented) {
|
|
444
|
-
this.close(event);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
/** @private */
|
|
450
|
-
_openedChanged(opened, wasOpened) {
|
|
451
|
-
if (opened) {
|
|
452
|
-
this._saveFocus();
|
|
453
|
-
|
|
454
|
-
this._animatedOpening();
|
|
455
|
-
|
|
456
|
-
afterNextRender(this, () => {
|
|
457
|
-
this._trapFocus();
|
|
458
|
-
|
|
459
|
-
const evt = new CustomEvent('vaadin-overlay-open', { bubbles: true });
|
|
460
|
-
this.dispatchEvent(evt);
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
document.addEventListener('keydown', this._boundKeydownListener);
|
|
464
|
-
|
|
465
|
-
if (!this.modeless) {
|
|
466
|
-
this._addGlobalListeners();
|
|
467
|
-
}
|
|
468
|
-
} else if (wasOpened) {
|
|
469
|
-
this._resetFocus();
|
|
470
|
-
|
|
471
|
-
this._animatedClosing();
|
|
472
|
-
|
|
473
|
-
document.removeEventListener('keydown', this._boundKeydownListener);
|
|
474
|
-
|
|
475
|
-
if (!this.modeless) {
|
|
476
|
-
this._removeGlobalListeners();
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/** @private */
|
|
482
|
-
_hiddenChanged(hidden) {
|
|
483
|
-
if (hidden && this.hasAttribute('closing')) {
|
|
484
|
-
this._flushAnimation('closing');
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* @return {boolean}
|
|
490
|
-
* @private
|
|
491
|
-
*/
|
|
492
|
-
_shouldAnimate() {
|
|
493
|
-
const style = getComputedStyle(this);
|
|
494
|
-
const name = style.getPropertyValue('animation-name');
|
|
495
|
-
const hidden = style.getPropertyValue('display') === 'none';
|
|
496
|
-
return !hidden && name && name !== 'none';
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
/**
|
|
500
|
-
* @param {string} type
|
|
501
|
-
* @param {Function} callback
|
|
502
|
-
* @private
|
|
503
|
-
*/
|
|
504
|
-
_enqueueAnimation(type, callback) {
|
|
505
|
-
const handler = `__${type}Handler`;
|
|
506
|
-
const listener = (event) => {
|
|
507
|
-
if (event && event.target !== this) {
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
callback();
|
|
511
|
-
this.removeEventListener('animationend', listener);
|
|
512
|
-
delete this[handler];
|
|
513
|
-
};
|
|
514
|
-
this[handler] = listener;
|
|
515
|
-
this.addEventListener('animationend', listener);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
/**
|
|
519
|
-
* @param {string} type
|
|
520
|
-
* @protected
|
|
521
|
-
*/
|
|
522
|
-
_flushAnimation(type) {
|
|
523
|
-
const handler = `__${type}Handler`;
|
|
524
|
-
if (typeof this[handler] === 'function') {
|
|
525
|
-
this[handler]();
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/** @private */
|
|
530
|
-
_animatedOpening() {
|
|
531
|
-
if (this.parentNode === document.body && this.hasAttribute('closing')) {
|
|
532
|
-
this._flushAnimation('closing');
|
|
533
|
-
}
|
|
534
|
-
this._attachOverlay();
|
|
535
|
-
if (!this.modeless) {
|
|
536
|
-
this._enterModalState();
|
|
537
|
-
}
|
|
538
|
-
this.setAttribute('opening', '');
|
|
539
|
-
|
|
540
|
-
if (this._shouldAnimate()) {
|
|
541
|
-
this._enqueueAnimation('opening', () => {
|
|
542
|
-
this._finishOpening();
|
|
543
|
-
});
|
|
544
|
-
} else {
|
|
545
|
-
this._finishOpening();
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
/** @private */
|
|
550
|
-
_attachOverlay() {
|
|
551
|
-
this._placeholder = document.createComment('vaadin-overlay-placeholder');
|
|
552
|
-
this.parentNode.insertBefore(this._placeholder, this);
|
|
553
|
-
document.body.appendChild(this);
|
|
554
|
-
this.bringToFront();
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
/** @private */
|
|
558
|
-
_finishOpening() {
|
|
559
|
-
this.removeAttribute('opening');
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/** @private */
|
|
563
|
-
_finishClosing() {
|
|
564
|
-
this._detachOverlay();
|
|
565
|
-
this.$.overlay.style.removeProperty('pointer-events');
|
|
566
|
-
this.removeAttribute('closing');
|
|
567
|
-
this.dispatchEvent(new CustomEvent('vaadin-overlay-closed'));
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
/** @private */
|
|
571
|
-
_animatedClosing() {
|
|
572
|
-
if (this.hasAttribute('opening')) {
|
|
573
|
-
this._flushAnimation('opening');
|
|
574
|
-
}
|
|
575
|
-
if (this._placeholder) {
|
|
576
|
-
this._exitModalState();
|
|
577
|
-
this.setAttribute('closing', '');
|
|
578
|
-
this.dispatchEvent(new CustomEvent('vaadin-overlay-closing'));
|
|
579
|
-
|
|
580
|
-
if (this._shouldAnimate()) {
|
|
581
|
-
this._enqueueAnimation('closing', () => {
|
|
582
|
-
this._finishClosing();
|
|
583
|
-
});
|
|
584
|
-
} else {
|
|
585
|
-
this._finishClosing();
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
/** @private */
|
|
591
|
-
_detachOverlay() {
|
|
592
|
-
this._placeholder.parentNode.insertBefore(this, this._placeholder);
|
|
593
|
-
this._placeholder.parentNode.removeChild(this._placeholder);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
/** @private */
|
|
597
|
-
_modelessChanged(modeless) {
|
|
598
|
-
if (!modeless) {
|
|
599
|
-
if (this.opened) {
|
|
600
|
-
this._addGlobalListeners();
|
|
601
|
-
this._enterModalState();
|
|
602
|
-
}
|
|
603
|
-
} else {
|
|
604
|
-
this._removeGlobalListeners();
|
|
605
|
-
this._exitModalState();
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
/** @private */
|
|
610
|
-
_addGlobalListeners() {
|
|
611
|
-
document.addEventListener('mousedown', this._boundMouseDownListener);
|
|
612
|
-
document.addEventListener('mouseup', this._boundMouseUpListener);
|
|
613
|
-
// Firefox leaks click to document on contextmenu even if prevented
|
|
614
|
-
// https://bugzilla.mozilla.org/show_bug.cgi?id=990614
|
|
615
|
-
document.documentElement.addEventListener('click', this._boundOutsideClickListener, true);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
/** @private */
|
|
619
|
-
_enterModalState() {
|
|
620
|
-
if (document.body.style.pointerEvents !== 'none') {
|
|
621
|
-
// Set body pointer-events to 'none' to disable mouse interactions with
|
|
622
|
-
// other document nodes.
|
|
623
|
-
this._previousDocumentPointerEvents = document.body.style.pointerEvents;
|
|
624
|
-
document.body.style.pointerEvents = 'none';
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// Disable pointer events in other attached overlays
|
|
628
|
-
Overlay.__attachedInstances.forEach((el) => {
|
|
629
|
-
if (el !== this) {
|
|
630
|
-
el.shadowRoot.querySelector('[part="overlay"]').style.pointerEvents = 'none';
|
|
631
|
-
}
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
/** @private */
|
|
636
|
-
_removeGlobalListeners() {
|
|
637
|
-
document.removeEventListener('mousedown', this._boundMouseDownListener);
|
|
638
|
-
document.removeEventListener('mouseup', this._boundMouseUpListener);
|
|
639
|
-
document.documentElement.removeEventListener('click', this._boundOutsideClickListener, true);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
/** @private */
|
|
643
|
-
_exitModalState() {
|
|
644
|
-
if (this._previousDocumentPointerEvents !== undefined) {
|
|
645
|
-
// Restore body pointer-events
|
|
646
|
-
document.body.style.pointerEvents = this._previousDocumentPointerEvents;
|
|
647
|
-
delete this._previousDocumentPointerEvents;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
// Restore pointer events in the previous overlay(s)
|
|
651
|
-
const instances = Overlay.__attachedInstances;
|
|
652
|
-
let el;
|
|
653
|
-
// Use instances.pop() to ensure the reverse order
|
|
654
|
-
while ((el = instances.pop())) {
|
|
655
|
-
if (el === this) {
|
|
656
|
-
// Skip the current instance
|
|
657
|
-
continue;
|
|
658
|
-
}
|
|
659
|
-
el.shadowRoot.querySelector('[part="overlay"]').style.removeProperty('pointer-events');
|
|
660
|
-
if (!el.modeless) {
|
|
661
|
-
// Stop after the last modal
|
|
662
|
-
break;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
/** @private */
|
|
668
|
-
_rendererOrDataChanged(renderer, owner, model, opened) {
|
|
669
|
-
const ownerOrModelChanged = this._oldOwner !== owner || this._oldModel !== model;
|
|
670
|
-
this._oldModel = model;
|
|
671
|
-
this._oldOwner = owner;
|
|
672
|
-
|
|
673
|
-
const rendererChanged = this._oldRenderer !== renderer;
|
|
674
|
-
this._oldRenderer = renderer;
|
|
675
|
-
|
|
676
|
-
const openedChanged = this._oldOpened !== opened;
|
|
677
|
-
this._oldOpened = opened;
|
|
678
|
-
|
|
679
|
-
if (rendererChanged) {
|
|
680
|
-
this.innerHTML = '';
|
|
681
|
-
// Whenever a Lit-based renderer is used, it assigns a Lit part to the node it was rendered into.
|
|
682
|
-
// When clearing the rendered content, this part needs to be manually disposed of.
|
|
683
|
-
// Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
|
|
684
|
-
delete this._$litPart$;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
if (opened && renderer && (rendererChanged || openedChanged || ownerOrModelChanged)) {
|
|
688
|
-
this.requestContentUpdate();
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
/**
|
|
693
|
-
* Brings the overlay as visually the frontmost one
|
|
694
|
-
*/
|
|
695
|
-
bringToFront() {
|
|
696
|
-
let zIndex = '';
|
|
697
|
-
const frontmost = Overlay.__attachedInstances.filter((o) => o !== this).pop();
|
|
698
|
-
if (frontmost) {
|
|
699
|
-
const frontmostZIndex = frontmost.__zIndex;
|
|
700
|
-
zIndex = frontmostZIndex + 1;
|
|
701
|
-
}
|
|
702
|
-
this.style.zIndex = zIndex;
|
|
703
|
-
this.__zIndex = zIndex || parseFloat(getComputedStyle(this).zIndex);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
102
|
/**
|
|
707
103
|
* @event vaadin-overlay-open
|
|
708
104
|
* Fired after the overlay is opened.
|