@vaadin/overlay 25.0.0-alpha7 → 25.0.0-alpha9
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 +8 -8
- package/src/styles/vaadin-overlay-base-styles.js +61 -56
- package/src/styles/vaadin-overlay-core-styles.js +8 -0
- package/src/vaadin-overlay-focus-mixin.js +23 -5
- package/src/vaadin-overlay-mixin.js +49 -24
- package/src/vaadin-overlay-position-mixin.js +5 -5
- package/src/vaadin-overlay-stack-mixin.js +56 -45
- package/src/vaadin-overlay-utils.d.ts +6 -0
- package/src/vaadin-overlay-utils.js +29 -0
- package/src/vaadin-overlay.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/overlay",
|
|
3
|
-
"version": "25.0.0-
|
|
3
|
+
"version": "25.0.0-alpha9",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -36,17 +36,17 @@
|
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
39
|
-
"@vaadin/a11y-base": "25.0.0-
|
|
40
|
-
"@vaadin/component-base": "25.0.0-
|
|
41
|
-
"@vaadin/vaadin-lumo-styles": "25.0.0-
|
|
42
|
-
"@vaadin/vaadin-themable-mixin": "25.0.0-
|
|
39
|
+
"@vaadin/a11y-base": "25.0.0-alpha9",
|
|
40
|
+
"@vaadin/component-base": "25.0.0-alpha9",
|
|
41
|
+
"@vaadin/vaadin-lumo-styles": "25.0.0-alpha9",
|
|
42
|
+
"@vaadin/vaadin-themable-mixin": "25.0.0-alpha9",
|
|
43
43
|
"lit": "^3.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@vaadin/chai-plugins": "25.0.0-
|
|
47
|
-
"@vaadin/test-runner-commands": "25.0.0-
|
|
46
|
+
"@vaadin/chai-plugins": "25.0.0-alpha9",
|
|
47
|
+
"@vaadin/test-runner-commands": "25.0.0-alpha9",
|
|
48
48
|
"@vaadin/testing-helpers": "^2.0.0",
|
|
49
49
|
"sinon": "^18.0.0"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "bbe4720721e0955ffc87a79b412bee38b1f0eb1e"
|
|
52
52
|
}
|
|
@@ -7,76 +7,81 @@ import '@vaadin/component-base/src/style-props.js';
|
|
|
7
7
|
import { css } from 'lit';
|
|
8
8
|
|
|
9
9
|
export const overlayStyles = css`
|
|
10
|
-
|
|
11
|
-
:
|
|
12
|
-
|
|
13
|
-
position: fixed;
|
|
10
|
+
:host {
|
|
11
|
+
z-index: 200;
|
|
12
|
+
position: fixed;
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
/* Despite of what the names say, <vaadin-overlay> is just a container
|
|
16
15
|
for position/sizing/alignment. The actual overlay is the overlay part. */
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
/* Default position constraints. Themes can
|
|
19
18
|
override this to adjust the gap between the overlay and the viewport. */
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
inset: 8px;
|
|
20
|
+
bottom: var(--vaadin-overlay-viewport-bottom);
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
/* Override native [popover] user agent styles */
|
|
23
|
+
width: auto;
|
|
24
|
+
height: auto;
|
|
25
|
+
border: none;
|
|
26
|
+
padding: 0;
|
|
27
|
+
background-color: transparent;
|
|
28
|
+
overflow: visible;
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
/* Use flexbox alignment for the overlay part. */
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column; /* makes dropdowns sizing easier */
|
|
33
|
+
/* Align to center by default. */
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: center;
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
/* Allow centering when max-width/max-height applies. */
|
|
38
|
+
margin: auto;
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
/* The host is not clickable, only the overlay part is. */
|
|
41
|
+
pointer-events: none;
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
43
|
+
/* Remove tap highlight on touch devices. */
|
|
44
|
+
-webkit-tap-highlight-color: transparent;
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
:
|
|
45
|
-
|
|
46
|
-
display: none !important;
|
|
47
|
-
}
|
|
46
|
+
/* CSS API for host */
|
|
47
|
+
--vaadin-overlay-viewport-bottom: 8px;
|
|
48
|
+
}
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
box-sizing: border-box;
|
|
55
|
-
max-width: 100%;
|
|
56
|
-
overflow: auto;
|
|
57
|
-
overscroll-behavior: contain;
|
|
58
|
-
pointer-events: auto;
|
|
59
|
-
-webkit-tap-highlight-color: initial;
|
|
60
|
-
}
|
|
50
|
+
:host([hidden]),
|
|
51
|
+
:host(:not([opened]):not([closing])),
|
|
52
|
+
:host(:not([opened]):not([closing])) [part='overlay'] {
|
|
53
|
+
display: none !important;
|
|
54
|
+
}
|
|
61
55
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
56
|
+
[part='overlay'] {
|
|
57
|
+
background: var(--vaadin-overlay-background, var(--vaadin-background-color));
|
|
58
|
+
border: var(--vaadin-overlay-border-width, 1px) solid var(--vaadin-overlay-border-color, var(--vaadin-border-color));
|
|
59
|
+
border-radius: var(--vaadin-overlay-border-radius, var(--vaadin-radius-m));
|
|
60
|
+
box-shadow: var(--vaadin-overlay-box-shadow, 0 8px 24px -4px rgba(0, 0, 0, 0.3));
|
|
61
|
+
box-sizing: border-box;
|
|
62
|
+
max-width: 100%;
|
|
63
|
+
overflow: auto;
|
|
64
|
+
overscroll-behavior: contain;
|
|
65
|
+
pointer-events: auto;
|
|
66
|
+
-webkit-tap-highlight-color: initial;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
[part='backdrop'] {
|
|
70
|
+
background: var(--vaadin-overlay-backdrop-background, rgba(0, 0, 0, 0.5));
|
|
71
|
+
content: '';
|
|
72
|
+
inset: 0;
|
|
73
|
+
pointer-events: auto;
|
|
74
|
+
position: fixed;
|
|
75
|
+
z-index: -1;
|
|
76
|
+
}
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
78
|
+
[part='overlay']:focus-visible {
|
|
79
|
+
outline: var(--vaadin-focus-ring-width) solid var(--vaadin-focus-ring-color);
|
|
80
|
+
}
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
82
|
+
@media (forced-colors: active) {
|
|
83
|
+
[part='overlay'] {
|
|
84
|
+
border: 3px solid;
|
|
80
85
|
}
|
|
81
86
|
}
|
|
82
87
|
`;
|
|
@@ -18,6 +18,14 @@ export const overlayStyles = css`
|
|
|
18
18
|
inset: 0;
|
|
19
19
|
bottom: var(--vaadin-overlay-viewport-bottom);
|
|
20
20
|
|
|
21
|
+
/* Override native [popover] user agent styles */
|
|
22
|
+
width: auto;
|
|
23
|
+
height: auto;
|
|
24
|
+
border: none;
|
|
25
|
+
padding: 0;
|
|
26
|
+
background-color: transparent;
|
|
27
|
+
overflow: visible;
|
|
28
|
+
|
|
21
29
|
/* Use flexbox alignment for the overlay part. */
|
|
22
30
|
display: flex;
|
|
23
31
|
flex-direction: column; /* makes dropdowns sizing easier */
|
|
@@ -48,11 +48,20 @@ export const OverlayFocusMixin = (superClass) =>
|
|
|
48
48
|
constructor() {
|
|
49
49
|
super();
|
|
50
50
|
|
|
51
|
-
this.__ariaModalController = new AriaModalController(this);
|
|
51
|
+
this.__ariaModalController = new AriaModalController(this, () => this._modalRoot);
|
|
52
52
|
this.__focusTrapController = new FocusTrapController(this);
|
|
53
53
|
this.__focusRestorationController = new FocusRestorationController();
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Override to specify another element used as a content root,
|
|
58
|
+
* e.g. slotted into the overlay, rather than overlay itself.
|
|
59
|
+
* @protected
|
|
60
|
+
*/
|
|
61
|
+
get _contentRoot() {
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
|
|
56
65
|
/** @protected */
|
|
57
66
|
ready() {
|
|
58
67
|
super.ready();
|
|
@@ -62,6 +71,15 @@ export const OverlayFocusMixin = (superClass) =>
|
|
|
62
71
|
this.addController(this.__focusRestorationController);
|
|
63
72
|
}
|
|
64
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Override to specify another element used as a modality root,
|
|
76
|
+
* e.g. the overlay's owner element, rather than overlay itself.
|
|
77
|
+
* @protected
|
|
78
|
+
*/
|
|
79
|
+
get _modalRoot() {
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
|
|
65
83
|
/**
|
|
66
84
|
* Release focus and restore focus after the overlay is closed.
|
|
67
85
|
*
|
|
@@ -127,15 +145,15 @@ export const OverlayFocusMixin = (superClass) =>
|
|
|
127
145
|
* @protected
|
|
128
146
|
*/
|
|
129
147
|
_deepContains(node) {
|
|
130
|
-
if (this.contains(node)) {
|
|
148
|
+
if (this._contentRoot.contains(node)) {
|
|
131
149
|
return true;
|
|
132
150
|
}
|
|
133
151
|
let n = node;
|
|
134
152
|
const doc = node.ownerDocument;
|
|
135
|
-
// Walk from node to
|
|
136
|
-
while (n && n !== doc && n !== this) {
|
|
153
|
+
// Walk from node to content root or `document`
|
|
154
|
+
while (n && n !== doc && n !== this._contentRoot) {
|
|
137
155
|
n = n.parentNode || n.host;
|
|
138
156
|
}
|
|
139
|
-
return n === this;
|
|
157
|
+
return n === this._contentRoot;
|
|
140
158
|
}
|
|
141
159
|
};
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { isIOS } from '@vaadin/component-base/src/browser-utils.js';
|
|
7
7
|
import { OverlayFocusMixin } from './vaadin-overlay-focus-mixin.js';
|
|
8
8
|
import { OverlayStackMixin } from './vaadin-overlay-stack-mixin.js';
|
|
9
|
+
import { setOverlayStateAttribute } from './vaadin-overlay-utils.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @polymerMixin
|
|
@@ -91,6 +92,7 @@ export const OverlayMixin = (superClass) =>
|
|
|
91
92
|
type: Boolean,
|
|
92
93
|
value: false,
|
|
93
94
|
reflectToAttribute: true,
|
|
95
|
+
observer: '_withBackdropChanged',
|
|
94
96
|
sync: true,
|
|
95
97
|
},
|
|
96
98
|
};
|
|
@@ -115,15 +117,20 @@ export const OverlayMixin = (superClass) =>
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
/** @protected */
|
|
118
|
-
|
|
119
|
-
super.
|
|
120
|
+
firstUpdated() {
|
|
121
|
+
super.firstUpdated();
|
|
122
|
+
|
|
123
|
+
// Set popover in firstUpdated before opened observers are called
|
|
124
|
+
this.popover = 'manual';
|
|
120
125
|
|
|
121
126
|
// Need to add dummy click listeners to this and the backdrop or else
|
|
122
127
|
// the document click event listener (_outsideClickListener) may never
|
|
123
128
|
// get invoked on iOS Safari (reproducible in <vaadin-dialog>
|
|
124
129
|
// and <vaadin-context-menu>).
|
|
125
130
|
this.addEventListener('click', () => {});
|
|
126
|
-
this.$.backdrop
|
|
131
|
+
if (this.$.backdrop) {
|
|
132
|
+
this.$.backdrop.addEventListener('click', () => {});
|
|
133
|
+
}
|
|
127
134
|
|
|
128
135
|
this.addEventListener('mouseup', () => {
|
|
129
136
|
// In Chrome, focus moves to body on overlay content mousedown
|
|
@@ -163,7 +170,7 @@ export const OverlayMixin = (superClass) =>
|
|
|
163
170
|
*/
|
|
164
171
|
requestContentUpdate() {
|
|
165
172
|
if (this.renderer) {
|
|
166
|
-
this.renderer.call(this.owner, this, this.owner, this.model);
|
|
173
|
+
this.renderer.call(this.owner, this._contentRoot, this.owner, this.model);
|
|
167
174
|
}
|
|
168
175
|
}
|
|
169
176
|
|
|
@@ -171,13 +178,17 @@ export const OverlayMixin = (superClass) =>
|
|
|
171
178
|
* @param {Event=} sourceEvent
|
|
172
179
|
*/
|
|
173
180
|
close(sourceEvent) {
|
|
174
|
-
|
|
181
|
+
// Dispatch the event on the overlay. Not using composed, as propagating the event through shadow roots could have
|
|
182
|
+
// side effects when nesting overlays
|
|
183
|
+
const event = new CustomEvent('vaadin-overlay-close', {
|
|
175
184
|
bubbles: true,
|
|
176
185
|
cancelable: true,
|
|
177
186
|
detail: { sourceEvent },
|
|
178
187
|
});
|
|
179
|
-
this.dispatchEvent(
|
|
180
|
-
|
|
188
|
+
this.dispatchEvent(event);
|
|
189
|
+
// To allow listening for the event globally, also dispatch it on the document body
|
|
190
|
+
document.body.dispatchEvent(event);
|
|
191
|
+
if (!event.defaultPrevented) {
|
|
181
192
|
this.opened = false;
|
|
182
193
|
}
|
|
183
194
|
}
|
|
@@ -256,11 +267,11 @@ export const OverlayMixin = (superClass) =>
|
|
|
256
267
|
this._oldOpened = opened;
|
|
257
268
|
|
|
258
269
|
if (rendererChanged && hasOldRenderer) {
|
|
259
|
-
this.innerHTML = '';
|
|
270
|
+
this._contentRoot.innerHTML = '';
|
|
260
271
|
// Whenever a Lit-based renderer is used, it assigns a Lit part to the node it was rendered into.
|
|
261
272
|
// When clearing the rendered content, this part needs to be manually disposed of.
|
|
262
273
|
// Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
|
|
263
|
-
delete this._$litPart$;
|
|
274
|
+
delete this._contentRoot._$litPart$;
|
|
264
275
|
}
|
|
265
276
|
|
|
266
277
|
if (opened && renderer && (rendererChanged || openedChanged || ownerOrModelChanged)) {
|
|
@@ -279,11 +290,23 @@ export const OverlayMixin = (superClass) =>
|
|
|
279
290
|
this._removeGlobalListeners();
|
|
280
291
|
this._exitModalState();
|
|
281
292
|
}
|
|
293
|
+
setOverlayStateAttribute(this, 'modeless', modeless);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** @private */
|
|
297
|
+
_withBackdropChanged(withBackdrop) {
|
|
298
|
+
setOverlayStateAttribute(this, 'with-backdrop', withBackdrop);
|
|
282
299
|
}
|
|
283
300
|
|
|
284
301
|
/** @private */
|
|
285
302
|
_openedChanged(opened, wasOpened) {
|
|
286
303
|
if (opened) {
|
|
304
|
+
// Prevent possible errors on setting `opened` to `true` while disconnected
|
|
305
|
+
if (!this.isConnected) {
|
|
306
|
+
this.opened = false;
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
287
310
|
this._saveFocus();
|
|
288
311
|
|
|
289
312
|
this._animatedOpening();
|
|
@@ -292,7 +315,12 @@ export const OverlayMixin = (superClass) =>
|
|
|
292
315
|
setTimeout(() => {
|
|
293
316
|
this._trapFocus();
|
|
294
317
|
|
|
295
|
-
|
|
318
|
+
// Dispatch the event on the overlay. Not using composed, as propagating the event through shadow roots
|
|
319
|
+
// could have side effects when nesting overlays
|
|
320
|
+
const event = new CustomEvent('vaadin-overlay-open', { bubbles: true });
|
|
321
|
+
this.dispatchEvent(event);
|
|
322
|
+
// To allow listening for the event globally, also dispatch it on the document body
|
|
323
|
+
document.body.dispatchEvent(event);
|
|
296
324
|
});
|
|
297
325
|
});
|
|
298
326
|
|
|
@@ -369,14 +397,16 @@ export const OverlayMixin = (superClass) =>
|
|
|
369
397
|
|
|
370
398
|
/** @private */
|
|
371
399
|
_animatedOpening() {
|
|
372
|
-
if (this.
|
|
400
|
+
if (this._isAttached && this.hasAttribute('closing')) {
|
|
373
401
|
this._flushAnimation('closing');
|
|
374
402
|
}
|
|
375
403
|
this._attachOverlay();
|
|
404
|
+
this._appendAttachedInstance();
|
|
405
|
+
this.bringToFront();
|
|
376
406
|
if (!this.modeless) {
|
|
377
407
|
this._enterModalState();
|
|
378
408
|
}
|
|
379
|
-
this
|
|
409
|
+
setOverlayStateAttribute(this, 'opening', true);
|
|
380
410
|
|
|
381
411
|
if (this._shouldAnimate()) {
|
|
382
412
|
this._enqueueAnimation('opening', () => {
|
|
@@ -389,22 +419,20 @@ export const OverlayMixin = (superClass) =>
|
|
|
389
419
|
|
|
390
420
|
/** @private */
|
|
391
421
|
_attachOverlay() {
|
|
392
|
-
this.
|
|
393
|
-
this.parentNode.insertBefore(this._placeholder, this);
|
|
394
|
-
document.body.appendChild(this);
|
|
395
|
-
this.bringToFront();
|
|
422
|
+
this.showPopover();
|
|
396
423
|
}
|
|
397
424
|
|
|
398
425
|
/** @private */
|
|
399
426
|
_finishOpening() {
|
|
400
|
-
this
|
|
427
|
+
setOverlayStateAttribute(this, 'opening', false);
|
|
401
428
|
}
|
|
402
429
|
|
|
403
430
|
/** @private */
|
|
404
431
|
_finishClosing() {
|
|
405
432
|
this._detachOverlay();
|
|
433
|
+
this._removeAttachedInstance();
|
|
406
434
|
this.$.overlay.style.removeProperty('pointer-events');
|
|
407
|
-
this
|
|
435
|
+
setOverlayStateAttribute(this, 'closing', false);
|
|
408
436
|
this.dispatchEvent(new CustomEvent('vaadin-overlay-closed'));
|
|
409
437
|
}
|
|
410
438
|
|
|
@@ -413,9 +441,9 @@ export const OverlayMixin = (superClass) =>
|
|
|
413
441
|
if (this.hasAttribute('opening')) {
|
|
414
442
|
this._flushAnimation('opening');
|
|
415
443
|
}
|
|
416
|
-
if (this.
|
|
444
|
+
if (this._isAttached) {
|
|
417
445
|
this._exitModalState();
|
|
418
|
-
this
|
|
446
|
+
setOverlayStateAttribute(this, 'closing', true);
|
|
419
447
|
this.dispatchEvent(new CustomEvent('vaadin-overlay-closing'));
|
|
420
448
|
|
|
421
449
|
if (this._shouldAnimate()) {
|
|
@@ -430,8 +458,7 @@ export const OverlayMixin = (superClass) =>
|
|
|
430
458
|
|
|
431
459
|
/** @private */
|
|
432
460
|
_detachOverlay() {
|
|
433
|
-
this.
|
|
434
|
-
this._placeholder.parentNode.removeChild(this._placeholder);
|
|
461
|
+
this.hidePopover();
|
|
435
462
|
}
|
|
436
463
|
|
|
437
464
|
/** @private */
|
|
@@ -475,7 +502,6 @@ export const OverlayMixin = (superClass) =>
|
|
|
475
502
|
}
|
|
476
503
|
|
|
477
504
|
const evt = new CustomEvent('vaadin-overlay-outside-click', {
|
|
478
|
-
bubbles: true,
|
|
479
505
|
cancelable: true,
|
|
480
506
|
detail: { sourceEvent: event },
|
|
481
507
|
});
|
|
@@ -502,7 +528,6 @@ export const OverlayMixin = (superClass) =>
|
|
|
502
528
|
|
|
503
529
|
if (event.key === 'Escape') {
|
|
504
530
|
const evt = new CustomEvent('vaadin-overlay-escape-press', {
|
|
505
|
-
bubbles: true,
|
|
506
531
|
cancelable: true,
|
|
507
532
|
detail: { sourceEvent: event },
|
|
508
533
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { getAncestorRootNodes } from '@vaadin/component-base/src/dom-utils.js';
|
|
7
|
-
import { observeMove } from './vaadin-overlay-utils.js';
|
|
7
|
+
import { observeMove, setOverlayStateAttribute } from './vaadin-overlay-utils.js';
|
|
8
8
|
|
|
9
9
|
const PROP_NAMES_VERTICAL = {
|
|
10
10
|
start: 'top',
|
|
@@ -271,11 +271,11 @@ export const PositionMixin = (superClass) =>
|
|
|
271
271
|
// Apply the positioning properties to the overlay
|
|
272
272
|
Object.assign(this.style, verticalProps, horizontalProps);
|
|
273
273
|
|
|
274
|
-
this
|
|
275
|
-
this
|
|
274
|
+
setOverlayStateAttribute(this, 'bottom-aligned', !shouldAlignStartVertically);
|
|
275
|
+
setOverlayStateAttribute(this, 'top-aligned', shouldAlignStartVertically);
|
|
276
276
|
|
|
277
|
-
this
|
|
278
|
-
this
|
|
277
|
+
setOverlayStateAttribute(this, 'end-aligned', !flexStart);
|
|
278
|
+
setOverlayStateAttribute(this, 'start-aligned', flexStart);
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
__shouldAlignStartHorizontally(targetRect, rtl) {
|
|
@@ -4,21 +4,32 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/** @type {Set<HTMLElement>} */
|
|
8
|
+
const attachedInstances = new Set();
|
|
9
|
+
|
|
7
10
|
/**
|
|
8
11
|
* Returns all attached overlays in visual stacking order.
|
|
9
12
|
* @private
|
|
10
13
|
*/
|
|
11
|
-
const getAttachedInstances = () =>
|
|
12
|
-
Array.from(document.body.children)
|
|
13
|
-
.filter((el) => el instanceof HTMLElement && el._hasOverlayStackMixin && !el.hasAttribute('closing'))
|
|
14
|
-
.sort((a, b) => a.__zIndex - b.__zIndex || 0);
|
|
14
|
+
const getAttachedInstances = () => [...attachedInstances].filter((el) => !el.hasAttribute('closing'));
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Returns all
|
|
18
|
-
* which only needs to be in the stack for zIndex but not pointer-events.
|
|
17
|
+
* Returns true if all the instances on top of the overlay are nested overlays.
|
|
19
18
|
* @private
|
|
20
19
|
*/
|
|
21
|
-
const
|
|
20
|
+
const hasOnlyNestedOverlays = (overlay) => {
|
|
21
|
+
const instances = getAttachedInstances();
|
|
22
|
+
const next = instances[instances.indexOf(overlay) + 1];
|
|
23
|
+
if (!next) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!overlay._deepContains(next)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return hasOnlyNestedOverlays(next);
|
|
32
|
+
};
|
|
22
33
|
|
|
23
34
|
/**
|
|
24
35
|
* Returns true if the overlay is the last one in the opened overlays stack.
|
|
@@ -28,38 +39,15 @@ const getOverlayInstances = () => getAttachedInstances().filter((el) => el.$.ove
|
|
|
28
39
|
* @protected
|
|
29
40
|
*/
|
|
30
41
|
export const isLastOverlay = (overlay, filter = (_overlay) => true) => {
|
|
31
|
-
const filteredOverlays =
|
|
42
|
+
const filteredOverlays = getAttachedInstances().filter(filter);
|
|
32
43
|
return overlay === filteredOverlays.pop();
|
|
33
44
|
};
|
|
34
45
|
|
|
35
|
-
const overlayMap = new WeakMap();
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Stores the reference to the nested overlay for given parent,
|
|
39
|
-
* or removes it when the nested overlay is null.
|
|
40
|
-
* @param {HTMLElement} parent
|
|
41
|
-
* @param {HTMLElement} nested
|
|
42
|
-
* @protected
|
|
43
|
-
*/
|
|
44
|
-
export const setNestedOverlay = (parent, nested) => {
|
|
45
|
-
if (nested != null) {
|
|
46
|
-
overlayMap.set(parent, nested);
|
|
47
|
-
} else {
|
|
48
|
-
overlayMap.delete(parent);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
46
|
/**
|
|
53
47
|
* @polymerMixin
|
|
54
48
|
*/
|
|
55
49
|
export const OverlayStackMixin = (superClass) =>
|
|
56
50
|
class OverlayStackMixin extends superClass {
|
|
57
|
-
constructor() {
|
|
58
|
-
super();
|
|
59
|
-
|
|
60
|
-
this._hasOverlayStackMixin = true;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
51
|
/**
|
|
64
52
|
* Returns true if this is the last one in the opened overlays stack.
|
|
65
53
|
*
|
|
@@ -70,25 +58,36 @@ export const OverlayStackMixin = (superClass) =>
|
|
|
70
58
|
return isLastOverlay(this);
|
|
71
59
|
}
|
|
72
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Returns true if this is overlay is attached.
|
|
63
|
+
*
|
|
64
|
+
* @return {boolean}
|
|
65
|
+
* @protected
|
|
66
|
+
*/
|
|
67
|
+
get _isAttached() {
|
|
68
|
+
return attachedInstances.has(this);
|
|
69
|
+
}
|
|
70
|
+
|
|
73
71
|
/**
|
|
74
72
|
* Brings the overlay as visually the frontmost one.
|
|
75
73
|
*/
|
|
76
74
|
bringToFront() {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const frontmostZIndex = frontmost.__zIndex;
|
|
83
|
-
zIndex = frontmostZIndex + 1;
|
|
75
|
+
// If the overlay is the last one, or if all other overlays shown above
|
|
76
|
+
// are nested overlays (e.g. date-picker inside a dialog), do not call
|
|
77
|
+
// `showPopover()` unnecessarily to avoid scroll position being reset.
|
|
78
|
+
if (isLastOverlay(this) || hasOnlyNestedOverlays(this)) {
|
|
79
|
+
return;
|
|
84
80
|
}
|
|
85
|
-
this.style.zIndex = zIndex;
|
|
86
|
-
this.__zIndex = zIndex || parseFloat(getComputedStyle(this).zIndex);
|
|
87
81
|
|
|
88
|
-
//
|
|
89
|
-
if (
|
|
90
|
-
|
|
82
|
+
// Update stacking order of native popover-based overlays
|
|
83
|
+
if (this.matches(':popover-open')) {
|
|
84
|
+
this.hidePopover();
|
|
85
|
+
this.showPopover();
|
|
91
86
|
}
|
|
87
|
+
|
|
88
|
+
// Update order of attached instances
|
|
89
|
+
this._removeAttachedInstance();
|
|
90
|
+
this._appendAttachedInstance();
|
|
92
91
|
}
|
|
93
92
|
|
|
94
93
|
/** @protected */
|
|
@@ -101,7 +100,7 @@ export const OverlayStackMixin = (superClass) =>
|
|
|
101
100
|
}
|
|
102
101
|
|
|
103
102
|
// Disable pointer events in other attached overlays
|
|
104
|
-
|
|
103
|
+
getAttachedInstances().forEach((el) => {
|
|
105
104
|
if (el !== this) {
|
|
106
105
|
el.$.overlay.style.pointerEvents = 'none';
|
|
107
106
|
}
|
|
@@ -117,7 +116,7 @@ export const OverlayStackMixin = (superClass) =>
|
|
|
117
116
|
}
|
|
118
117
|
|
|
119
118
|
// Restore pointer events in the previous overlay(s)
|
|
120
|
-
const instances =
|
|
119
|
+
const instances = getAttachedInstances();
|
|
121
120
|
|
|
122
121
|
let el;
|
|
123
122
|
// Use instances.pop() to ensure the reverse order
|
|
@@ -133,4 +132,16 @@ export const OverlayStackMixin = (superClass) =>
|
|
|
133
132
|
}
|
|
134
133
|
}
|
|
135
134
|
}
|
|
135
|
+
|
|
136
|
+
/** @protected */
|
|
137
|
+
_appendAttachedInstance() {
|
|
138
|
+
attachedInstances.add(this);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** @protected */
|
|
142
|
+
_removeAttachedInstance() {
|
|
143
|
+
if (this._isAttached) {
|
|
144
|
+
attachedInstances.delete(this);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
136
147
|
};
|
|
@@ -11,3 +11,9 @@
|
|
|
11
11
|
* https://github.com/floating-ui/floating-ui/blob/58ed169/packages/dom/src/autoUpdate.ts#L45
|
|
12
12
|
*/
|
|
13
13
|
export function observeMove(element: HTMLElement, callback: () => void): () => void;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Toggle the state attribute on the overlay element and also its owner element. This allows targeting state attributes
|
|
17
|
+
* in the light DOM in case the overlay is in the shadow DOM of its owner.
|
|
18
|
+
*/
|
|
19
|
+
export function setOverlayStateAttribute(overlay: HTMLElement, name: string, value: string | boolean): void;
|
|
@@ -87,3 +87,32 @@ export function observeMove(element, callback) {
|
|
|
87
87
|
|
|
88
88
|
return cleanup;
|
|
89
89
|
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Toggle the state attribute on the overlay element and also its owner element. This allows targeting state attributes
|
|
93
|
+
* in the light DOM in case the overlay is in the shadow DOM of its owner.
|
|
94
|
+
* @param {HTMLElement} overlay The overlay element on which to toggle the attribute.
|
|
95
|
+
* @param {string} name The name of the attribute to toggle.
|
|
96
|
+
* @param {string|boolean} value The value of the attribute. If a string is provided, it will be set as the attribute
|
|
97
|
+
* value. Otherwise, the attribute will be added or removed depending on whether `value` is truthy or falsy.
|
|
98
|
+
*/
|
|
99
|
+
export function setOverlayStateAttribute(overlay, name, value) {
|
|
100
|
+
const elements = [overlay];
|
|
101
|
+
if (overlay.owner) {
|
|
102
|
+
elements.push(overlay.owner);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (typeof value === 'string') {
|
|
106
|
+
elements.forEach((element) => {
|
|
107
|
+
element.setAttribute(name, value);
|
|
108
|
+
});
|
|
109
|
+
} else if (value) {
|
|
110
|
+
elements.forEach((element) => {
|
|
111
|
+
element.setAttribute(name, '');
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
elements.forEach((element) => {
|
|
115
|
+
element.removeAttribute(name);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
package/src/vaadin-overlay.js
CHANGED
|
@@ -77,7 +77,7 @@ import { OverlayMixin } from './vaadin-overlay-mixin.js';
|
|
|
77
77
|
* @mixes DirMixin
|
|
78
78
|
* @mixes OverlayMixin
|
|
79
79
|
*/
|
|
80
|
-
class Overlay extends OverlayMixin(DirMixin(ThemableMixin(LumoInjectionMixin(
|
|
80
|
+
class Overlay extends OverlayMixin(DirMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))))) {
|
|
81
81
|
static get is() {
|
|
82
82
|
return 'vaadin-overlay';
|
|
83
83
|
}
|