@vaadin/overlay 25.0.0-alpha8 → 25.0.0-beta1
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 +9 -12
- package/src/styles/vaadin-overlay-base-styles.js +19 -6
- package/src/vaadin-overlay-focus-mixin.js +8 -12
- package/src/vaadin-overlay-mixin.js +61 -19
- package/src/vaadin-overlay-position-mixin.js +51 -13
- package/src/vaadin-overlay-stack-mixin.js +5 -55
- package/src/vaadin-overlay-utils.js +11 -14
- package/src/vaadin-overlay.d.ts +6 -6
- package/src/vaadin-overlay.js +5 -5
- package/vaadin-overlay.js +1 -1
- package/src/styles/vaadin-overlay-core-styles.js +0 -74
- package/theme/lumo/vaadin-overlay-styles.d.ts +0 -1
- package/theme/lumo/vaadin-overlay-styles.js +0 -4
- package/theme/lumo/vaadin-overlay.d.ts +0 -2
- package/theme/lumo/vaadin-overlay.js +0 -2
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-beta1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -21,9 +21,6 @@
|
|
|
21
21
|
"type": "module",
|
|
22
22
|
"files": [
|
|
23
23
|
"src",
|
|
24
|
-
"!src/styles/*-base-styles.d.ts",
|
|
25
|
-
"!src/styles/*-base-styles.js",
|
|
26
|
-
"theme",
|
|
27
24
|
"vaadin-*.d.ts",
|
|
28
25
|
"vaadin-*.js"
|
|
29
26
|
],
|
|
@@ -36,17 +33,17 @@
|
|
|
36
33
|
],
|
|
37
34
|
"dependencies": {
|
|
38
35
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
39
|
-
"@vaadin/a11y-base": "25.0.0-
|
|
40
|
-
"@vaadin/component-base": "25.0.0-
|
|
41
|
-
"@vaadin/vaadin-
|
|
42
|
-
"@vaadin/vaadin-themable-mixin": "25.0.0-alpha8",
|
|
36
|
+
"@vaadin/a11y-base": "25.0.0-beta1",
|
|
37
|
+
"@vaadin/component-base": "25.0.0-beta1",
|
|
38
|
+
"@vaadin/vaadin-themable-mixin": "25.0.0-beta1",
|
|
43
39
|
"lit": "^3.0.0"
|
|
44
40
|
},
|
|
45
41
|
"devDependencies": {
|
|
46
|
-
"@vaadin/chai-plugins": "25.0.0-
|
|
47
|
-
"@vaadin/test-runner-commands": "25.0.0-
|
|
42
|
+
"@vaadin/chai-plugins": "25.0.0-beta1",
|
|
43
|
+
"@vaadin/test-runner-commands": "25.0.0-beta1",
|
|
48
44
|
"@vaadin/testing-helpers": "^2.0.0",
|
|
49
|
-
"
|
|
45
|
+
"@vaadin/vaadin-lumo-styles": "25.0.0-beta1",
|
|
46
|
+
"sinon": "^21.0.0"
|
|
50
47
|
},
|
|
51
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "1d20cf54e582d1f2e209126d4586f8b4c01c50e0"
|
|
52
49
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright (c) 2017 - 2025 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import '@vaadin/component-base/src/style-props.js';
|
|
6
|
+
import '@vaadin/component-base/src/styles/style-props.js';
|
|
7
7
|
import { css } from 'lit';
|
|
8
8
|
|
|
9
9
|
export const overlayStyles = css`
|
|
@@ -16,7 +16,7 @@ export const overlayStyles = css`
|
|
|
16
16
|
|
|
17
17
|
/* Default position constraints. Themes can
|
|
18
18
|
override this to adjust the gap between the overlay and the viewport. */
|
|
19
|
-
inset: 8px;
|
|
19
|
+
inset: var(--vaadin-overlay-viewport-inset, 8px);
|
|
20
20
|
bottom: var(--vaadin-overlay-viewport-bottom);
|
|
21
21
|
|
|
22
22
|
/* Override native [popover] user agent styles */
|
|
@@ -55,19 +55,32 @@ export const overlayStyles = css`
|
|
|
55
55
|
|
|
56
56
|
[part='overlay'] {
|
|
57
57
|
background: var(--vaadin-overlay-background, var(--vaadin-background-color));
|
|
58
|
-
border: var(--vaadin-overlay-border-width, 1px) solid
|
|
58
|
+
border: var(--vaadin-overlay-border-width, 1px) solid
|
|
59
|
+
var(--vaadin-overlay-border-color, var(--vaadin-border-color-secondary));
|
|
59
60
|
border-radius: var(--vaadin-overlay-border-radius, var(--vaadin-radius-m));
|
|
60
|
-
box-shadow: var(--vaadin-overlay-
|
|
61
|
+
box-shadow: var(--vaadin-overlay-shadow, 0 8px 24px -4px rgba(0, 0, 0, 0.3));
|
|
61
62
|
box-sizing: border-box;
|
|
62
63
|
max-width: 100%;
|
|
63
64
|
overflow: auto;
|
|
64
65
|
overscroll-behavior: contain;
|
|
65
66
|
pointer-events: auto;
|
|
66
67
|
-webkit-tap-highlight-color: initial;
|
|
68
|
+
|
|
69
|
+
/* CSS reset for font styles */
|
|
70
|
+
color: initial;
|
|
71
|
+
font: initial;
|
|
72
|
+
letter-spacing: initial;
|
|
73
|
+
text-align: initial;
|
|
74
|
+
text-decoration: initial;
|
|
75
|
+
text-indent: initial;
|
|
76
|
+
text-transform: initial;
|
|
77
|
+
user-select: text;
|
|
78
|
+
white-space: initial;
|
|
79
|
+
word-spacing: initial;
|
|
67
80
|
}
|
|
68
81
|
|
|
69
82
|
[part='backdrop'] {
|
|
70
|
-
background: var(--vaadin-overlay-backdrop-background, rgba(0, 0, 0, 0.
|
|
83
|
+
background: var(--vaadin-overlay-backdrop-background, rgba(0, 0, 0, 0.2));
|
|
71
84
|
content: '';
|
|
72
85
|
inset: 0;
|
|
73
86
|
pointer-events: auto;
|
|
@@ -81,7 +94,7 @@ export const overlayStyles = css`
|
|
|
81
94
|
|
|
82
95
|
@media (forced-colors: active) {
|
|
83
96
|
[part='overlay'] {
|
|
84
|
-
border: 3px solid;
|
|
97
|
+
border: 3px solid !important;
|
|
85
98
|
}
|
|
86
99
|
}
|
|
87
100
|
`;
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Copyright (c) 2017 - 2025 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { AriaModalController } from '@vaadin/a11y-base/src/aria-modal-controller.js';
|
|
7
6
|
import { FocusRestorationController } from '@vaadin/a11y-base/src/focus-restoration-controller.js';
|
|
8
7
|
import { FocusTrapController } from '@vaadin/a11y-base/src/focus-trap-controller.js';
|
|
9
8
|
import { getDeepActiveElement, isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
|
|
@@ -48,7 +47,6 @@ export const OverlayFocusMixin = (superClass) =>
|
|
|
48
47
|
constructor() {
|
|
49
48
|
super();
|
|
50
49
|
|
|
51
|
-
this.__ariaModalController = new AriaModalController(this, () => this._modalRoot);
|
|
52
50
|
this.__focusTrapController = new FocusTrapController(this);
|
|
53
51
|
this.__focusRestorationController = new FocusRestorationController();
|
|
54
52
|
}
|
|
@@ -66,18 +64,17 @@ export const OverlayFocusMixin = (superClass) =>
|
|
|
66
64
|
ready() {
|
|
67
65
|
super.ready();
|
|
68
66
|
|
|
69
|
-
this.addController(this.__ariaModalController);
|
|
70
67
|
this.addController(this.__focusTrapController);
|
|
71
68
|
this.addController(this.__focusRestorationController);
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
/**
|
|
75
|
-
* Override to specify another element used as a
|
|
76
|
-
* e.g. the overlay's owner element, rather than overlay
|
|
72
|
+
* Override to specify another element used as a focus trap root,
|
|
73
|
+
* e.g. the overlay's owner element, rather than overlay part.
|
|
77
74
|
* @protected
|
|
78
75
|
*/
|
|
79
|
-
get
|
|
80
|
-
return this;
|
|
76
|
+
get _focusTrapRoot() {
|
|
77
|
+
return this.$.overlay;
|
|
81
78
|
}
|
|
82
79
|
|
|
83
80
|
/**
|
|
@@ -87,13 +84,13 @@ export const OverlayFocusMixin = (superClass) =>
|
|
|
87
84
|
*/
|
|
88
85
|
_resetFocus() {
|
|
89
86
|
if (this.focusTrap) {
|
|
90
|
-
this.__ariaModalController.close();
|
|
91
87
|
this.__focusTrapController.releaseFocus();
|
|
92
88
|
}
|
|
93
89
|
|
|
94
90
|
if (this.restoreFocusOnClose && this._shouldRestoreFocus()) {
|
|
95
|
-
const
|
|
96
|
-
|
|
91
|
+
const focusVisible = isKeyboardActive();
|
|
92
|
+
const preventScroll = !focusVisible;
|
|
93
|
+
this.__focusRestorationController.restoreFocus({ preventScroll, focusVisible });
|
|
97
94
|
}
|
|
98
95
|
}
|
|
99
96
|
|
|
@@ -115,8 +112,7 @@ export const OverlayFocusMixin = (superClass) =>
|
|
|
115
112
|
*/
|
|
116
113
|
_trapFocus() {
|
|
117
114
|
if (this.focusTrap) {
|
|
118
|
-
this.
|
|
119
|
-
this.__focusTrapController.trapFocus(this.$.overlay);
|
|
115
|
+
this.__focusTrapController.trapFocus(this._focusTrapRoot);
|
|
120
116
|
}
|
|
121
117
|
}
|
|
122
118
|
|
|
@@ -117,8 +117,11 @@ export const OverlayMixin = (superClass) =>
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
/** @protected */
|
|
120
|
-
|
|
121
|
-
super.
|
|
120
|
+
firstUpdated() {
|
|
121
|
+
super.firstUpdated();
|
|
122
|
+
|
|
123
|
+
// Set popover in firstUpdated before opened observers are called
|
|
124
|
+
this.popover = 'manual';
|
|
122
125
|
|
|
123
126
|
// Need to add dummy click listeners to this and the backdrop or else
|
|
124
127
|
// the document click event listener (_outsideClickListener) may never
|
|
@@ -153,6 +156,11 @@ export const OverlayMixin = (superClass) =>
|
|
|
153
156
|
disconnectedCallback() {
|
|
154
157
|
super.disconnectedCallback();
|
|
155
158
|
|
|
159
|
+
if (this.__scheduledOpen) {
|
|
160
|
+
cancelAnimationFrame(this.__scheduledOpen);
|
|
161
|
+
this.__scheduledOpen = null;
|
|
162
|
+
}
|
|
163
|
+
|
|
156
164
|
/* c8 ignore next 3 */
|
|
157
165
|
if (this._boundIosResizeListener) {
|
|
158
166
|
window.removeEventListener('resize', this._boundIosResizeListener);
|
|
@@ -175,13 +183,17 @@ export const OverlayMixin = (superClass) =>
|
|
|
175
183
|
* @param {Event=} sourceEvent
|
|
176
184
|
*/
|
|
177
185
|
close(sourceEvent) {
|
|
178
|
-
|
|
186
|
+
// Dispatch the event on the overlay. Not using composed, as propagating the event through shadow roots could have
|
|
187
|
+
// side effects when nesting overlays
|
|
188
|
+
const event = new CustomEvent('vaadin-overlay-close', {
|
|
179
189
|
bubbles: true,
|
|
180
190
|
cancelable: true,
|
|
181
|
-
detail: { sourceEvent },
|
|
191
|
+
detail: { overlay: this, sourceEvent },
|
|
182
192
|
});
|
|
183
|
-
this.dispatchEvent(
|
|
184
|
-
|
|
193
|
+
this.dispatchEvent(event);
|
|
194
|
+
// To allow listening for the event globally, also dispatch it on the document body
|
|
195
|
+
document.body.dispatchEvent(event);
|
|
196
|
+
if (!event.defaultPrevented) {
|
|
185
197
|
this.opened = false;
|
|
186
198
|
}
|
|
187
199
|
}
|
|
@@ -230,8 +242,25 @@ export const OverlayMixin = (superClass) =>
|
|
|
230
242
|
}
|
|
231
243
|
}
|
|
232
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Whether to add global listeners for closing on outside click.
|
|
247
|
+
* By default, listeners are not added for a modeless overlay.
|
|
248
|
+
*
|
|
249
|
+
* @return {boolean}
|
|
250
|
+
* @protected
|
|
251
|
+
*/
|
|
252
|
+
_shouldAddGlobalListeners() {
|
|
253
|
+
return !this.modeless;
|
|
254
|
+
}
|
|
255
|
+
|
|
233
256
|
/** @private */
|
|
234
257
|
_addGlobalListeners() {
|
|
258
|
+
if (this.__hasGlobalListeners) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
this.__hasGlobalListeners = true;
|
|
263
|
+
|
|
235
264
|
document.addEventListener('mousedown', this._boundMouseDownListener);
|
|
236
265
|
document.addEventListener('mouseup', this._boundMouseUpListener);
|
|
237
266
|
// Firefox leaks click to document on contextmenu even if prevented
|
|
@@ -241,6 +270,12 @@ export const OverlayMixin = (superClass) =>
|
|
|
241
270
|
|
|
242
271
|
/** @private */
|
|
243
272
|
_removeGlobalListeners() {
|
|
273
|
+
if (!this.__hasGlobalListeners) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
this.__hasGlobalListeners = false;
|
|
278
|
+
|
|
244
279
|
document.removeEventListener('mousedown', this._boundMouseDownListener);
|
|
245
280
|
document.removeEventListener('mouseup', this._boundMouseUpListener);
|
|
246
281
|
document.documentElement.removeEventListener('click', this._boundOutsideClickListener, true);
|
|
@@ -274,13 +309,20 @@ export const OverlayMixin = (superClass) =>
|
|
|
274
309
|
|
|
275
310
|
/** @private */
|
|
276
311
|
_modelessChanged(modeless) {
|
|
312
|
+
if (this.opened) {
|
|
313
|
+
// Add / remove listeners if modeless is changed while opened
|
|
314
|
+
if (this._shouldAddGlobalListeners()) {
|
|
315
|
+
this._addGlobalListeners();
|
|
316
|
+
} else {
|
|
317
|
+
this._removeGlobalListeners();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
277
321
|
if (!modeless) {
|
|
278
322
|
if (this.opened) {
|
|
279
|
-
this._addGlobalListeners();
|
|
280
323
|
this._enterModalState();
|
|
281
324
|
}
|
|
282
325
|
} else {
|
|
283
|
-
this._removeGlobalListeners();
|
|
284
326
|
this._exitModalState();
|
|
285
327
|
}
|
|
286
328
|
setOverlayStateAttribute(this, 'modeless', modeless);
|
|
@@ -308,13 +350,18 @@ export const OverlayMixin = (superClass) =>
|
|
|
308
350
|
setTimeout(() => {
|
|
309
351
|
this._trapFocus();
|
|
310
352
|
|
|
311
|
-
|
|
353
|
+
// Dispatch the event on the overlay. Not using composed, as propagating the event through shadow roots
|
|
354
|
+
// could have side effects when nesting overlays
|
|
355
|
+
const event = new CustomEvent('vaadin-overlay-open', { detail: { overlay: this }, bubbles: true });
|
|
356
|
+
this.dispatchEvent(event);
|
|
357
|
+
// To allow listening for the event globally, also dispatch it on the document body
|
|
358
|
+
document.body.dispatchEvent(event);
|
|
312
359
|
});
|
|
313
360
|
});
|
|
314
361
|
|
|
315
362
|
document.addEventListener('keydown', this._boundKeydownListener);
|
|
316
363
|
|
|
317
|
-
if (
|
|
364
|
+
if (this._shouldAddGlobalListeners()) {
|
|
318
365
|
this._addGlobalListeners();
|
|
319
366
|
}
|
|
320
367
|
} else if (wasOpened) {
|
|
@@ -329,7 +376,7 @@ export const OverlayMixin = (superClass) =>
|
|
|
329
376
|
|
|
330
377
|
document.removeEventListener('keydown', this._boundKeydownListener);
|
|
331
378
|
|
|
332
|
-
if (
|
|
379
|
+
if (this._shouldAddGlobalListeners()) {
|
|
333
380
|
this._removeGlobalListeners();
|
|
334
381
|
}
|
|
335
382
|
}
|
|
@@ -407,9 +454,7 @@ export const OverlayMixin = (superClass) =>
|
|
|
407
454
|
|
|
408
455
|
/** @private */
|
|
409
456
|
_attachOverlay() {
|
|
410
|
-
this.
|
|
411
|
-
this.parentNode.insertBefore(this._placeholder, this);
|
|
412
|
-
document.body.appendChild(this);
|
|
457
|
+
this.showPopover();
|
|
413
458
|
}
|
|
414
459
|
|
|
415
460
|
/** @private */
|
|
@@ -448,8 +493,7 @@ export const OverlayMixin = (superClass) =>
|
|
|
448
493
|
|
|
449
494
|
/** @private */
|
|
450
495
|
_detachOverlay() {
|
|
451
|
-
this.
|
|
452
|
-
this._placeholder.parentNode.removeChild(this._placeholder);
|
|
496
|
+
this.hidePopover();
|
|
453
497
|
}
|
|
454
498
|
|
|
455
499
|
/** @private */
|
|
@@ -493,7 +537,6 @@ export const OverlayMixin = (superClass) =>
|
|
|
493
537
|
}
|
|
494
538
|
|
|
495
539
|
const evt = new CustomEvent('vaadin-overlay-outside-click', {
|
|
496
|
-
bubbles: true,
|
|
497
540
|
cancelable: true,
|
|
498
541
|
detail: { sourceEvent: event },
|
|
499
542
|
});
|
|
@@ -514,13 +557,12 @@ export const OverlayMixin = (superClass) =>
|
|
|
514
557
|
}
|
|
515
558
|
|
|
516
559
|
// Only close modeless overlay on Esc press when it contains focus
|
|
517
|
-
if (this.
|
|
560
|
+
if (!this._shouldAddGlobalListeners() && !event.composedPath().includes(this._focusTrapRoot)) {
|
|
518
561
|
return;
|
|
519
562
|
}
|
|
520
563
|
|
|
521
564
|
if (event.key === 'Escape') {
|
|
522
565
|
const evt = new CustomEvent('vaadin-overlay-escape-press', {
|
|
523
|
-
bubbles: true,
|
|
524
566
|
cancelable: true,
|
|
525
567
|
detail: { sourceEvent: event },
|
|
526
568
|
});
|
|
@@ -116,13 +116,6 @@ export const PositionMixin = (superClass) =>
|
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
static get observers() {
|
|
120
|
-
return [
|
|
121
|
-
'__positionSettingsChanged(horizontalAlign, verticalAlign, noHorizontalOverlap, noVerticalOverlap, requiredVerticalSpace)',
|
|
122
|
-
'__overlayOpenedChanged(opened, positionTarget)',
|
|
123
|
-
];
|
|
124
|
-
}
|
|
125
|
-
|
|
126
119
|
constructor() {
|
|
127
120
|
super();
|
|
128
121
|
|
|
@@ -145,6 +138,36 @@ export const PositionMixin = (superClass) =>
|
|
|
145
138
|
this.__removeUpdatePositionEventListeners();
|
|
146
139
|
}
|
|
147
140
|
|
|
141
|
+
/** @protected */
|
|
142
|
+
updated(props) {
|
|
143
|
+
super.updated(props);
|
|
144
|
+
|
|
145
|
+
if (props.has('positionTarget')) {
|
|
146
|
+
const oldTarget = props.get('positionTarget');
|
|
147
|
+
|
|
148
|
+
// 1. When position target is removed, always reset position settings
|
|
149
|
+
// 2. When position target is set, reset if overlay was opened before
|
|
150
|
+
if ((!this.positionTarget && oldTarget) || (this.positionTarget && !oldTarget && !!this.__margins)) {
|
|
151
|
+
this.__resetPosition();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (props.has('opened') || props.has('positionTarget')) {
|
|
156
|
+
this.__updatePositionSettings(this.opened, this.positionTarget);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const positionProps = [
|
|
160
|
+
'horizontalAlign',
|
|
161
|
+
'verticalAlign',
|
|
162
|
+
'noHorizontalOverlap',
|
|
163
|
+
'noVerticalOverlap',
|
|
164
|
+
'requiredVerticalSpace',
|
|
165
|
+
];
|
|
166
|
+
if (positionProps.some((prop) => props.has(prop))) {
|
|
167
|
+
this._updatePosition();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
148
171
|
/** @private */
|
|
149
172
|
__addUpdatePositionEventListeners() {
|
|
150
173
|
window.visualViewport.addEventListener('resize', this._updatePosition);
|
|
@@ -181,7 +204,7 @@ export const PositionMixin = (superClass) =>
|
|
|
181
204
|
}
|
|
182
205
|
|
|
183
206
|
/** @private */
|
|
184
|
-
|
|
207
|
+
__updatePositionSettings(opened, positionTarget) {
|
|
185
208
|
this.__removeUpdatePositionEventListeners();
|
|
186
209
|
|
|
187
210
|
if (positionTarget) {
|
|
@@ -210,20 +233,35 @@ export const PositionMixin = (superClass) =>
|
|
|
210
233
|
}
|
|
211
234
|
}
|
|
212
235
|
|
|
213
|
-
__positionSettingsChanged() {
|
|
214
|
-
this._updatePosition();
|
|
215
|
-
}
|
|
216
|
-
|
|
217
236
|
/** @private */
|
|
218
237
|
__onScroll(e) {
|
|
219
238
|
// If the scroll event occurred inside the overlay, ignore it.
|
|
220
|
-
if (e.target instanceof Node && this.
|
|
239
|
+
if (e.target instanceof Node && this._deepContains(e.target)) {
|
|
221
240
|
return;
|
|
222
241
|
}
|
|
223
242
|
|
|
224
243
|
this._updatePosition();
|
|
225
244
|
}
|
|
226
245
|
|
|
246
|
+
/** @private */
|
|
247
|
+
__resetPosition() {
|
|
248
|
+
this.__margins = null;
|
|
249
|
+
|
|
250
|
+
Object.assign(this.style, {
|
|
251
|
+
justifyContent: '',
|
|
252
|
+
alignItems: '',
|
|
253
|
+
top: '',
|
|
254
|
+
bottom: '',
|
|
255
|
+
left: '',
|
|
256
|
+
right: '',
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
setOverlayStateAttribute(this, 'bottom-aligned', false);
|
|
260
|
+
setOverlayStateAttribute(this, 'top-aligned', false);
|
|
261
|
+
setOverlayStateAttribute(this, 'end-aligned', false);
|
|
262
|
+
setOverlayStateAttribute(this, 'start-aligned', false);
|
|
263
|
+
}
|
|
264
|
+
|
|
227
265
|
_updatePosition() {
|
|
228
266
|
if (!this.positionTarget || !this.opened || !this.__margins) {
|
|
229
267
|
return;
|
|
@@ -11,23 +11,13 @@ const attachedInstances = new Set();
|
|
|
11
11
|
* Returns all attached overlays in visual stacking order.
|
|
12
12
|
* @private
|
|
13
13
|
*/
|
|
14
|
-
const getAttachedInstances = () =>
|
|
15
|
-
[...attachedInstances].filter(
|
|
16
|
-
(el) => el instanceof HTMLElement && el._hasOverlayStackMixin && !el.hasAttribute('closing'),
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Returns all attached overlay instances excluding notification container,
|
|
21
|
-
* which only needs to be in the stack for zIndex but not pointer-events.
|
|
22
|
-
* @private
|
|
23
|
-
*/
|
|
24
|
-
const getOverlayInstances = () => getAttachedInstances().filter((el) => el.$.overlay);
|
|
14
|
+
const getAttachedInstances = () => [...attachedInstances].filter((el) => !el.hasAttribute('closing'));
|
|
25
15
|
|
|
26
16
|
/**
|
|
27
17
|
* Returns true if all the instances on top of the overlay are nested overlays.
|
|
28
18
|
* @private
|
|
29
19
|
*/
|
|
30
|
-
const hasOnlyNestedOverlays = (overlay) => {
|
|
20
|
+
export const hasOnlyNestedOverlays = (overlay) => {
|
|
31
21
|
const instances = getAttachedInstances();
|
|
32
22
|
const next = instances[instances.indexOf(overlay) + 1];
|
|
33
23
|
if (!next) {
|
|
@@ -49,38 +39,15 @@ const hasOnlyNestedOverlays = (overlay) => {
|
|
|
49
39
|
* @protected
|
|
50
40
|
*/
|
|
51
41
|
export const isLastOverlay = (overlay, filter = (_overlay) => true) => {
|
|
52
|
-
const filteredOverlays =
|
|
42
|
+
const filteredOverlays = getAttachedInstances().filter(filter);
|
|
53
43
|
return overlay === filteredOverlays.pop();
|
|
54
44
|
};
|
|
55
45
|
|
|
56
|
-
const overlayMap = new WeakMap();
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Stores the reference to the nested overlay for given parent,
|
|
60
|
-
* or removes it when the nested overlay is null.
|
|
61
|
-
* @param {HTMLElement} parent
|
|
62
|
-
* @param {HTMLElement} nested
|
|
63
|
-
* @protected
|
|
64
|
-
*/
|
|
65
|
-
export const setNestedOverlay = (parent, nested) => {
|
|
66
|
-
if (nested != null) {
|
|
67
|
-
overlayMap.set(parent, nested);
|
|
68
|
-
} else {
|
|
69
|
-
overlayMap.delete(parent);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
46
|
/**
|
|
74
47
|
* @polymerMixin
|
|
75
48
|
*/
|
|
76
49
|
export const OverlayStackMixin = (superClass) =>
|
|
77
50
|
class OverlayStackMixin extends superClass {
|
|
78
|
-
constructor() {
|
|
79
|
-
super();
|
|
80
|
-
|
|
81
|
-
this._hasOverlayStackMixin = true;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
51
|
/**
|
|
85
52
|
* Returns true if this is the last one in the opened overlays stack.
|
|
86
53
|
*
|
|
@@ -105,18 +72,6 @@ export const OverlayStackMixin = (superClass) =>
|
|
|
105
72
|
* Brings the overlay as visually the frontmost one.
|
|
106
73
|
*/
|
|
107
74
|
bringToFront() {
|
|
108
|
-
// Update z-index to be the highest among all attached overlays
|
|
109
|
-
// TODO: Can be removed after switching all overlays to be based on native popover
|
|
110
|
-
let zIndex = '';
|
|
111
|
-
const frontmost = getAttachedInstances()
|
|
112
|
-
.filter((o) => o !== this)
|
|
113
|
-
.pop();
|
|
114
|
-
if (frontmost) {
|
|
115
|
-
const frontmostZIndex = parseFloat(getComputedStyle(frontmost).zIndex);
|
|
116
|
-
zIndex = frontmostZIndex + 1;
|
|
117
|
-
}
|
|
118
|
-
this.style.zIndex = zIndex;
|
|
119
|
-
|
|
120
75
|
// If the overlay is the last one, or if all other overlays shown above
|
|
121
76
|
// are nested overlays (e.g. date-picker inside a dialog), do not call
|
|
122
77
|
// `showPopover()` unnecessarily to avoid scroll position being reset.
|
|
@@ -133,11 +88,6 @@ export const OverlayStackMixin = (superClass) =>
|
|
|
133
88
|
// Update order of attached instances
|
|
134
89
|
this._removeAttachedInstance();
|
|
135
90
|
this._appendAttachedInstance();
|
|
136
|
-
|
|
137
|
-
// If there is a nested overlay, call `bringToFront()` for it as well.
|
|
138
|
-
if (overlayMap.has(this)) {
|
|
139
|
-
overlayMap.get(this).bringToFront();
|
|
140
|
-
}
|
|
141
91
|
}
|
|
142
92
|
|
|
143
93
|
/** @protected */
|
|
@@ -150,7 +100,7 @@ export const OverlayStackMixin = (superClass) =>
|
|
|
150
100
|
}
|
|
151
101
|
|
|
152
102
|
// Disable pointer events in other attached overlays
|
|
153
|
-
|
|
103
|
+
getAttachedInstances().forEach((el) => {
|
|
154
104
|
if (el !== this) {
|
|
155
105
|
el.$.overlay.style.pointerEvents = 'none';
|
|
156
106
|
}
|
|
@@ -166,7 +116,7 @@ export const OverlayStackMixin = (superClass) =>
|
|
|
166
116
|
}
|
|
167
117
|
|
|
168
118
|
// Restore pointer events in the previous overlay(s)
|
|
169
|
-
const instances =
|
|
119
|
+
const instances = getAttachedInstances();
|
|
170
120
|
|
|
171
121
|
let el;
|
|
172
122
|
// Use instances.pop() to ensure the reverse order
|
|
@@ -16,10 +16,12 @@
|
|
|
16
16
|
*/
|
|
17
17
|
export function observeMove(element, callback) {
|
|
18
18
|
let io = null;
|
|
19
|
+
let timeout;
|
|
19
20
|
|
|
20
21
|
const root = document.documentElement;
|
|
21
22
|
|
|
22
23
|
function cleanup() {
|
|
24
|
+
timeout && clearTimeout(timeout);
|
|
23
25
|
io && io.disconnect();
|
|
24
26
|
io = null;
|
|
25
27
|
}
|
|
@@ -52,27 +54,22 @@ export function observeMove(element, callback) {
|
|
|
52
54
|
let isFirstUpdate = true;
|
|
53
55
|
|
|
54
56
|
function handleObserve(entries) {
|
|
55
|
-
|
|
57
|
+
const ratio = entries[0].intersectionRatio;
|
|
56
58
|
|
|
57
59
|
if (ratio !== threshold) {
|
|
58
60
|
if (!isFirstUpdate) {
|
|
59
61
|
return refresh();
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// moves beneath _that_ new value, the user will get notified.
|
|
71
|
-
if (ratio === 0.0) {
|
|
72
|
-
ratio = 0.0000001; // Just needs to be non-zero
|
|
64
|
+
if (!ratio) {
|
|
65
|
+
// If the reference is clipped, the ratio is 0. Throttle the refresh
|
|
66
|
+
// to prevent an infinite loop of updates.
|
|
67
|
+
timeout = setTimeout(() => {
|
|
68
|
+
refresh(false, 1e-7);
|
|
69
|
+
}, 1000);
|
|
70
|
+
} else {
|
|
71
|
+
refresh(false, ratio);
|
|
73
72
|
}
|
|
74
|
-
|
|
75
|
-
refresh(false, ratio);
|
|
76
73
|
}
|
|
77
74
|
|
|
78
75
|
isFirstUpdate = false;
|
package/src/vaadin-overlay.d.ts
CHANGED
|
@@ -17,13 +17,13 @@ export type OverlayOpenedChangedEvent = CustomEvent<{ value: boolean }>;
|
|
|
17
17
|
/**
|
|
18
18
|
* Fired after the overlay is opened.
|
|
19
19
|
*/
|
|
20
|
-
export type OverlayOpenEvent = CustomEvent
|
|
20
|
+
export type OverlayOpenEvent = CustomEvent<{ overlay: HTMLElement }>;
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Fired when the opened overlay is about to be closed.
|
|
24
24
|
* Calling `preventDefault()` on the event cancels the closing.
|
|
25
25
|
*/
|
|
26
|
-
export type OverlayCloseEvent = CustomEvent
|
|
26
|
+
export type OverlayCloseEvent = CustomEvent<{ overlay: HTMLElement }>;
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Fired after the overlay is closed.
|
|
@@ -98,10 +98,10 @@ export type OverlayEventMap = HTMLElementEventMap & OverlayCustomEventMap;
|
|
|
98
98
|
*
|
|
99
99
|
* The following state attributes are available for styling:
|
|
100
100
|
*
|
|
101
|
-
* Attribute | Description
|
|
102
|
-
*
|
|
103
|
-
* `opening` | Applied just after the overlay is
|
|
104
|
-
* `closing` | Applied just before the overlay is
|
|
101
|
+
* Attribute | Description
|
|
102
|
+
* ----------|------------
|
|
103
|
+
* `opening` | Applied just after the overlay is opened. You can apply a CSS animation for this state.
|
|
104
|
+
* `closing` | Applied just before the overlay is closed. You can apply a CSS animation for this state.
|
|
105
105
|
*
|
|
106
106
|
* The following custom CSS properties are available for styling:
|
|
107
107
|
*
|
package/src/vaadin-overlay.js
CHANGED
|
@@ -9,7 +9,7 @@ import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
|
9
9
|
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
10
10
|
import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
|
|
11
11
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
12
|
-
import { overlayStyles } from './styles/vaadin-overlay-
|
|
12
|
+
import { overlayStyles } from './styles/vaadin-overlay-base-styles.js';
|
|
13
13
|
import { OverlayMixin } from './vaadin-overlay-mixin.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -50,10 +50,10 @@ import { OverlayMixin } from './vaadin-overlay-mixin.js';
|
|
|
50
50
|
*
|
|
51
51
|
* The following state attributes are available for styling:
|
|
52
52
|
*
|
|
53
|
-
* Attribute | Description
|
|
54
|
-
*
|
|
55
|
-
* `opening` | Applied just after the overlay is
|
|
56
|
-
* `closing` | Applied just before the overlay is
|
|
53
|
+
* Attribute | Description
|
|
54
|
+
* ----------|------------
|
|
55
|
+
* `opening` | Applied just after the overlay is opened. You can apply a CSS animation for this state.
|
|
56
|
+
* `closing` | Applied just before the overlay is closed. You can apply a CSS animation for this state.
|
|
57
57
|
*
|
|
58
58
|
* The following custom CSS properties are available for styling:
|
|
59
59
|
*
|
package/vaadin-overlay.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import './
|
|
1
|
+
import './src/vaadin-overlay.js';
|
|
2
2
|
export * from './src/vaadin-overlay.js';
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) 2017 - 2025 Vaadin Ltd.
|
|
4
|
-
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
-
*/
|
|
6
|
-
import { css } from 'lit';
|
|
7
|
-
|
|
8
|
-
export const overlayStyles = css`
|
|
9
|
-
:host {
|
|
10
|
-
z-index: 200;
|
|
11
|
-
position: fixed;
|
|
12
|
-
|
|
13
|
-
/* Despite of what the names say, <vaadin-overlay> is just a container
|
|
14
|
-
for position/sizing/alignment. The actual overlay is the overlay part. */
|
|
15
|
-
|
|
16
|
-
/* Default position constraints: the entire viewport. Note: themes can
|
|
17
|
-
override this to introduce gaps between the overlay and the viewport. */
|
|
18
|
-
inset: 0;
|
|
19
|
-
bottom: var(--vaadin-overlay-viewport-bottom);
|
|
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
|
-
|
|
29
|
-
/* Use flexbox alignment for the overlay part. */
|
|
30
|
-
display: flex;
|
|
31
|
-
flex-direction: column; /* makes dropdowns sizing easier */
|
|
32
|
-
/* Align to center by default. */
|
|
33
|
-
align-items: center;
|
|
34
|
-
justify-content: center;
|
|
35
|
-
|
|
36
|
-
/* Allow centering when max-width/max-height applies. */
|
|
37
|
-
margin: auto;
|
|
38
|
-
|
|
39
|
-
/* The host is not clickable, only the overlay part is. */
|
|
40
|
-
pointer-events: none;
|
|
41
|
-
|
|
42
|
-
/* Remove tap highlight on touch devices. */
|
|
43
|
-
-webkit-tap-highlight-color: transparent;
|
|
44
|
-
|
|
45
|
-
/* CSS API for host */
|
|
46
|
-
--vaadin-overlay-viewport-bottom: 0;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
:host([hidden]),
|
|
50
|
-
:host(:not([opened]):not([closing])),
|
|
51
|
-
:host(:not([opened]):not([closing])) [part='overlay'] {
|
|
52
|
-
display: none !important;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
[part='overlay'] {
|
|
56
|
-
overflow: auto;
|
|
57
|
-
pointer-events: auto;
|
|
58
|
-
|
|
59
|
-
/* Prevent overflowing the host */
|
|
60
|
-
max-width: 100%;
|
|
61
|
-
box-sizing: border-box;
|
|
62
|
-
|
|
63
|
-
-webkit-tap-highlight-color: initial; /* reenable tap highlight inside */
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
[part='backdrop'] {
|
|
67
|
-
z-index: -1;
|
|
68
|
-
content: '';
|
|
69
|
-
background: rgba(0, 0, 0, 0.5);
|
|
70
|
-
position: fixed;
|
|
71
|
-
inset: 0;
|
|
72
|
-
pointer-events: auto;
|
|
73
|
-
}
|
|
74
|
-
`;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|