@vaadin/overlay 25.3.0-dev.1fa5a51482 → 25.3.0-rc1
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/custom-elements.json +103 -6
- package/package.json +9 -9
- package/src/styles/vaadin-overlay-animation-base-styles.d.ts +10 -0
- package/src/styles/vaadin-overlay-animation-base-styles.js +103 -0
- package/src/styles/vaadin-overlay-base-styles.d.ts +8 -0
- package/src/styles/vaadin-overlay-base-styles.js +9 -1
- package/src/vaadin-overlay-focus-mixin.d.ts +20 -4
- package/src/vaadin-overlay-focus-mixin.js +42 -6
- package/src/vaadin-overlay-mixin.js +30 -49
- package/src/vaadin-overlay-stack-mixin.js +4 -4
- package/src/vaadin-overlay-utils.d.ts +14 -0
- package/src/vaadin-overlay-utils.js +23 -0
- package/src/vaadin-overlay.d.ts +2 -0
- package/src/vaadin-overlay.js +12 -0
package/custom-elements.json
CHANGED
|
@@ -26,6 +26,16 @@
|
|
|
26
26
|
"description": "",
|
|
27
27
|
"name": "OverlayFocusMixin",
|
|
28
28
|
"members": [
|
|
29
|
+
{
|
|
30
|
+
"kind": "field",
|
|
31
|
+
"name": "autofocus",
|
|
32
|
+
"privacy": "public",
|
|
33
|
+
"type": {
|
|
34
|
+
"text": "boolean"
|
|
35
|
+
},
|
|
36
|
+
"description": "Set to true to move focus into the overlay automatically on open.\n\nFocus moves to the first tabbable element in the tab order. This\ncan be the overlay itself if it has `tabindex` attribute set to `0`\non the host element or the `overlay` shadow DOM part.",
|
|
37
|
+
"attribute": "autofocus"
|
|
38
|
+
},
|
|
29
39
|
{
|
|
30
40
|
"kind": "field",
|
|
31
41
|
"name": "focusTrap",
|
|
@@ -33,7 +43,7 @@
|
|
|
33
43
|
"type": {
|
|
34
44
|
"text": "boolean"
|
|
35
45
|
},
|
|
36
|
-
"description": "
|
|
46
|
+
"description": "Set to true to move focus into the overlay automatically on open\nand keep it inside: Tab and Shift+Tab cycle through the overlay's\ncontent until the overlay is closed.\n\nFocus moves to the first tabbable element in the tab order. This\ncan be the overlay itself if it has `tabindex` attribute set to `0`\non the host element or the `overlay` shadow DOM part.",
|
|
37
47
|
"attribute": "focus-trap"
|
|
38
48
|
},
|
|
39
49
|
{
|
|
@@ -58,12 +68,20 @@
|
|
|
58
68
|
}
|
|
59
69
|
],
|
|
60
70
|
"attributes": [
|
|
71
|
+
{
|
|
72
|
+
"name": "autofocus",
|
|
73
|
+
"type": {
|
|
74
|
+
"text": "boolean"
|
|
75
|
+
},
|
|
76
|
+
"description": "Set to true to move focus into the overlay automatically on open.\n\nFocus moves to the first tabbable element in the tab order. This\ncan be the overlay itself if it has `tabindex` attribute set to `0`\non the host element or the `overlay` shadow DOM part.",
|
|
77
|
+
"fieldName": "autofocus"
|
|
78
|
+
},
|
|
61
79
|
{
|
|
62
80
|
"name": "focus-trap",
|
|
63
81
|
"type": {
|
|
64
82
|
"text": "boolean"
|
|
65
83
|
},
|
|
66
|
-
"description": "
|
|
84
|
+
"description": "Set to true to move focus into the overlay automatically on open\nand keep it inside: Tab and Shift+Tab cycle through the overlay's\ncontent until the overlay is closed.\n\nFocus moves to the first tabbable element in the tab order. This\ncan be the overlay itself if it has `tabindex` attribute set to `0`\non the host element or the `overlay` shadow DOM part.",
|
|
67
85
|
"fieldName": "focusTrap"
|
|
68
86
|
},
|
|
69
87
|
{
|
|
@@ -110,6 +128,20 @@
|
|
|
110
128
|
"description": "",
|
|
111
129
|
"name": "OverlayMixin",
|
|
112
130
|
"members": [
|
|
131
|
+
{
|
|
132
|
+
"kind": "field",
|
|
133
|
+
"name": "autofocus",
|
|
134
|
+
"privacy": "public",
|
|
135
|
+
"type": {
|
|
136
|
+
"text": "boolean"
|
|
137
|
+
},
|
|
138
|
+
"description": "Set to true to move focus into the overlay automatically on open.\n\nFocus moves to the first tabbable element in the tab order. This\ncan be the overlay itself if it has `tabindex` attribute set to `0`\non the host element or the `overlay` shadow DOM part.",
|
|
139
|
+
"attribute": "autofocus",
|
|
140
|
+
"inheritedFrom": {
|
|
141
|
+
"name": "OverlayFocusMixin",
|
|
142
|
+
"module": "src/vaadin-overlay-focus-mixin.js"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
113
145
|
{
|
|
114
146
|
"kind": "method",
|
|
115
147
|
"name": "bringToFront",
|
|
@@ -138,7 +170,7 @@
|
|
|
138
170
|
"type": {
|
|
139
171
|
"text": "boolean"
|
|
140
172
|
},
|
|
141
|
-
"description": "
|
|
173
|
+
"description": "Set to true to move focus into the overlay automatically on open\nand keep it inside: Tab and Shift+Tab cycle through the overlay's\ncontent until the overlay is closed.\n\nFocus moves to the first tabbable element in the tab order. This\ncan be the overlay itself if it has `tabindex` attribute set to `0`\non the host element or the `overlay` shadow DOM part.",
|
|
142
174
|
"attribute": "focus-trap",
|
|
143
175
|
"inheritedFrom": {
|
|
144
176
|
"name": "OverlayFocusMixin",
|
|
@@ -284,12 +316,24 @@
|
|
|
284
316
|
}
|
|
285
317
|
],
|
|
286
318
|
"attributes": [
|
|
319
|
+
{
|
|
320
|
+
"name": "autofocus",
|
|
321
|
+
"type": {
|
|
322
|
+
"text": "boolean"
|
|
323
|
+
},
|
|
324
|
+
"description": "Set to true to move focus into the overlay automatically on open.\n\nFocus moves to the first tabbable element in the tab order. This\ncan be the overlay itself if it has `tabindex` attribute set to `0`\non the host element or the `overlay` shadow DOM part.",
|
|
325
|
+
"fieldName": "autofocus",
|
|
326
|
+
"inheritedFrom": {
|
|
327
|
+
"name": "OverlayFocusMixin",
|
|
328
|
+
"module": "src/vaadin-overlay-focus-mixin.js"
|
|
329
|
+
}
|
|
330
|
+
},
|
|
287
331
|
{
|
|
288
332
|
"name": "focus-trap",
|
|
289
333
|
"type": {
|
|
290
334
|
"text": "boolean"
|
|
291
335
|
},
|
|
292
|
-
"description": "
|
|
336
|
+
"description": "Set to true to move focus into the overlay automatically on open\nand keep it inside: Tab and Shift+Tab cycle through the overlay's\ncontent until the overlay is closed.\n\nFocus moves to the first tabbable element in the tab order. This\ncan be the overlay itself if it has `tabindex` attribute set to `0`\non the host element or the `overlay` shadow DOM part.",
|
|
293
337
|
"fieldName": "focusTrap",
|
|
294
338
|
"inheritedFrom": {
|
|
295
339
|
"name": "OverlayFocusMixin",
|
|
@@ -593,6 +637,24 @@
|
|
|
593
637
|
"name": "rootMargin",
|
|
594
638
|
"default": "`${-insetTop}px ${-insetRight}px ${-insetBottom}px ${-insetLeft}px`"
|
|
595
639
|
},
|
|
640
|
+
{
|
|
641
|
+
"kind": "function",
|
|
642
|
+
"name": "getStateAnimations",
|
|
643
|
+
"parameters": [
|
|
644
|
+
{
|
|
645
|
+
"name": "element",
|
|
646
|
+
"type": {
|
|
647
|
+
"text": "HTMLElement"
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
],
|
|
651
|
+
"description": "Collect the animations that report the state of the given element, so that their end can be\nawaited before the element is hidden or removed. The animations are read instead of the\ncomputed style, so that the decision to wait cannot disagree with what the browser actually\ncreated: a keyframes rule that does not exist and an element that is not rendered both give\nan empty list.\n\nOnly CSS animations count, as the transitions and the script animations that `getAnimations()`\nalso returns do not report the state. Animations of any content are already left out, as\n`getAnimations()` only descends into the subtree when asked to. Animations that take no time\nare dropped as well, so that a delay on its own does not hold the state.",
|
|
652
|
+
"return": {
|
|
653
|
+
"type": {
|
|
654
|
+
"text": "!Array<!Animation>"
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
},
|
|
596
658
|
{
|
|
597
659
|
"kind": "function",
|
|
598
660
|
"name": "setOverlayStateAttribute",
|
|
@@ -631,6 +693,14 @@
|
|
|
631
693
|
"module": "src/vaadin-overlay-utils.js"
|
|
632
694
|
}
|
|
633
695
|
},
|
|
696
|
+
{
|
|
697
|
+
"kind": "js",
|
|
698
|
+
"name": "getStateAnimations",
|
|
699
|
+
"declaration": {
|
|
700
|
+
"name": "getStateAnimations",
|
|
701
|
+
"module": "src/vaadin-overlay-utils.js"
|
|
702
|
+
}
|
|
703
|
+
},
|
|
634
704
|
{
|
|
635
705
|
"kind": "js",
|
|
636
706
|
"name": "setOverlayStateAttribute",
|
|
@@ -650,6 +720,20 @@
|
|
|
650
720
|
"description": "`<vaadin-overlay>` is a Web Component for creating overlays. The content of the overlay\ncan be populated imperatively by using `renderer` callback function.\n\n### Rendering\n\nThe renderer function provides `root`, `owner`, `model` arguments when applicable.\nGenerate DOM content by using `model` object properties if needed, append it to the `root`\nelement and control the state of the host element by accessing `owner`. Before generating new\ncontent, users are able to check if there is already content in `root` for reusing it.\n\n```html\n<vaadin-overlay id=\"overlay\"></vaadin-overlay>\n```\n```js\nconst overlay = document.querySelector('#overlay');\noverlay.renderer = function(root) {\n root.textContent = \"Overlay content\";\n};\n```\n\nRenderer is called on the opening of the overlay and each time the related model is updated.\nDOM generated during the renderer call can be reused\nin the next renderer call and will be provided with the `root` argument.\nOn first call it will be empty.\n\n### Styling\n\nThe following Shadow DOM parts are available for styling:\n\nPart name | Description\n-----------|---------------------------------------------------------|\n`backdrop` | Backdrop of the overlay\n`overlay` | Container for position/sizing/alignment of the content\n`content` | Content of the overlay\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n----------|------------\n`opening` | Applied just after the overlay is opened. You can apply a CSS animation for this state.\n`closing` | Applied just before the overlay is closed. You can apply a CSS animation for this state.\n\nThe following custom CSS properties are available for styling:\n\nCustom CSS property | Description | Default value\n---|---|---\n`--vaadin-overlay-viewport-bottom` | Bottom offset of the visible viewport area | `0` or detected offset\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
|
|
651
721
|
"name": "Overlay",
|
|
652
722
|
"members": [
|
|
723
|
+
{
|
|
724
|
+
"kind": "field",
|
|
725
|
+
"name": "autofocus",
|
|
726
|
+
"privacy": "public",
|
|
727
|
+
"type": {
|
|
728
|
+
"text": "boolean"
|
|
729
|
+
},
|
|
730
|
+
"description": "Set to true to move focus into the overlay automatically on open.\n\nFocus moves to the first tabbable element in the tab order. This\ncan be the overlay itself if it has `tabindex` attribute set to `0`\non the host element or the `overlay` shadow DOM part.",
|
|
731
|
+
"attribute": "autofocus",
|
|
732
|
+
"inheritedFrom": {
|
|
733
|
+
"name": "OverlayFocusMixin",
|
|
734
|
+
"module": "src/vaadin-overlay-focus-mixin.js"
|
|
735
|
+
}
|
|
736
|
+
},
|
|
653
737
|
{
|
|
654
738
|
"kind": "method",
|
|
655
739
|
"name": "bringToFront",
|
|
@@ -682,7 +766,7 @@
|
|
|
682
766
|
"type": {
|
|
683
767
|
"text": "boolean"
|
|
684
768
|
},
|
|
685
|
-
"description": "
|
|
769
|
+
"description": "Set to true to move focus into the overlay automatically on open\nand keep it inside: Tab and Shift+Tab cycle through the overlay's\ncontent until the overlay is closed.\n\nFocus moves to the first tabbable element in the tab order. This\ncan be the overlay itself if it has `tabindex` attribute set to `0`\non the host element or the `overlay` shadow DOM part.",
|
|
686
770
|
"attribute": "focus-trap",
|
|
687
771
|
"inheritedFrom": {
|
|
688
772
|
"name": "OverlayFocusMixin",
|
|
@@ -936,13 +1020,26 @@
|
|
|
936
1020
|
},
|
|
937
1021
|
"tagName": "vaadin-overlay",
|
|
938
1022
|
"customElement": true,
|
|
1023
|
+
"deprecated": "`<vaadin-overlay>` is deprecated and will be removed in Vaadin 26. Consider using `OverlayMixin` and `PositionMixin` instead.",
|
|
939
1024
|
"attributes": [
|
|
1025
|
+
{
|
|
1026
|
+
"name": "autofocus",
|
|
1027
|
+
"type": {
|
|
1028
|
+
"text": "boolean"
|
|
1029
|
+
},
|
|
1030
|
+
"description": "Set to true to move focus into the overlay automatically on open.\n\nFocus moves to the first tabbable element in the tab order. This\ncan be the overlay itself if it has `tabindex` attribute set to `0`\non the host element or the `overlay` shadow DOM part.",
|
|
1031
|
+
"fieldName": "autofocus",
|
|
1032
|
+
"inheritedFrom": {
|
|
1033
|
+
"name": "OverlayFocusMixin",
|
|
1034
|
+
"module": "src/vaadin-overlay-focus-mixin.js"
|
|
1035
|
+
}
|
|
1036
|
+
},
|
|
940
1037
|
{
|
|
941
1038
|
"name": "focus-trap",
|
|
942
1039
|
"type": {
|
|
943
1040
|
"text": "boolean"
|
|
944
1041
|
},
|
|
945
|
-
"description": "
|
|
1042
|
+
"description": "Set to true to move focus into the overlay automatically on open\nand keep it inside: Tab and Shift+Tab cycle through the overlay's\ncontent until the overlay is closed.\n\nFocus moves to the first tabbable element in the tab order. This\ncan be the overlay itself if it has `tabindex` attribute set to `0`\non the host element or the `overlay` shadow DOM part.",
|
|
946
1043
|
"fieldName": "focusTrap",
|
|
947
1044
|
"inheritedFrom": {
|
|
948
1045
|
"name": "OverlayFocusMixin",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/overlay",
|
|
3
|
-
"version": "25.3.0-
|
|
3
|
+
"version": "25.3.0-rc1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -34,19 +34,19 @@
|
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
37
|
-
"@vaadin/a11y-base": "25.3.0-
|
|
38
|
-
"@vaadin/component-base": "25.3.0-
|
|
39
|
-
"@vaadin/vaadin-themable-mixin": "25.3.0-
|
|
37
|
+
"@vaadin/a11y-base": "25.3.0-rc1",
|
|
38
|
+
"@vaadin/component-base": "25.3.0-rc1",
|
|
39
|
+
"@vaadin/vaadin-themable-mixin": "25.3.0-rc1",
|
|
40
40
|
"lit": "^3.0.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@vaadin/aura": "25.3.0-
|
|
44
|
-
"@vaadin/chai-plugins": "25.3.0-
|
|
45
|
-
"@vaadin/test-runner-commands": "25.3.0-
|
|
43
|
+
"@vaadin/aura": "25.3.0-rc1",
|
|
44
|
+
"@vaadin/chai-plugins": "25.3.0-rc1",
|
|
45
|
+
"@vaadin/test-runner-commands": "25.3.0-rc1",
|
|
46
46
|
"@vaadin/testing-helpers": "^2.0.0",
|
|
47
|
-
"@vaadin/vaadin-lumo-styles": "25.3.0-
|
|
47
|
+
"@vaadin/vaadin-lumo-styles": "25.3.0-rc1",
|
|
48
48
|
"sinon": "^22.0.0"
|
|
49
49
|
},
|
|
50
50
|
"customElements": "custom-elements.json",
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "d4809590b2f61bd607e376ab3dec84f4bfb059a7"
|
|
52
52
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2017 - 2026 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import type { CSSResult } from 'lit';
|
|
7
|
+
|
|
8
|
+
export const overlayAnimationProperties: CSSResult;
|
|
9
|
+
|
|
10
|
+
export const overlayAnimationStyles: CSSResult;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2017 - 2026 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { css, unsafeCSS } from 'lit';
|
|
7
|
+
import { registerCSSProperty } from '@vaadin/component-base/src/css-utils.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The custom properties that make up the overlay animation CSS API. Both the
|
|
11
|
+
* property registrations and the `overlayAnimationProperties` snippet below are
|
|
12
|
+
* derived from this list, so a new property only needs to be added here.
|
|
13
|
+
*
|
|
14
|
+
* Only the closed state is part of the API. The opened state is left out of the
|
|
15
|
+
* keyframes so that it comes from the part itself, see the keyframes below.
|
|
16
|
+
*
|
|
17
|
+
* `--vaadin-overlay-transform-closed` has no initial value, which makes it resolve
|
|
18
|
+
* to `none` in the keyframes unless a theme sets it.
|
|
19
|
+
*/
|
|
20
|
+
const animationProperties = [
|
|
21
|
+
{ name: '--vaadin-overlay-animation-duration', syntax: '<time>', initialValue: '0s' },
|
|
22
|
+
{ name: '--vaadin-overlay-animation-delay', syntax: '<time>', initialValue: '0s' },
|
|
23
|
+
{ name: '--vaadin-overlay-animation-timing-function', syntax: '*', initialValue: 'ease' },
|
|
24
|
+
{ name: '--vaadin-overlay-opacity-closed', syntax: '<number>', initialValue: '0' },
|
|
25
|
+
{ name: '--vaadin-overlay-translate-closed', syntax: '<length>+ | <percentage>+', initialValue: '0px' },
|
|
26
|
+
{ name: '--vaadin-overlay-scale-closed', syntax: '<number> | <percentage>', initialValue: '1' },
|
|
27
|
+
{ name: '--vaadin-overlay-transform-closed', syntax: '*' },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
animationProperties.forEach((property) => {
|
|
31
|
+
registerCSSProperty({ inherits: false, ...property });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The overlay animation properties as a set of `inherit` declarations. The properties are
|
|
36
|
+
* registered as non-inheriting, so components with more than one shadow root boundary
|
|
37
|
+
* between the host and the overlay need to forward them explicitly.
|
|
38
|
+
*/
|
|
39
|
+
export const overlayAnimationProperties = unsafeCSS(
|
|
40
|
+
animationProperties.map(({ name }) => `${name}: inherit;`).join('\n'),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
export const overlayAnimationStyles = css`
|
|
44
|
+
:host,
|
|
45
|
+
[part='overlay'],
|
|
46
|
+
[part='backdrop'] {
|
|
47
|
+
${overlayAnimationProperties}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
:host(:where([opening], [closing])) {
|
|
51
|
+
/* This empty animation only reports the state, the parts run the visible animation */
|
|
52
|
+
animation-name: --no-op;
|
|
53
|
+
animation-duration: var(--vaadin-overlay-animation-duration);
|
|
54
|
+
animation-delay: var(--vaadin-overlay-animation-delay);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
:host(:where([closing])) [part='overlay'],
|
|
58
|
+
:host(:where([closing])) ::slotted(*) {
|
|
59
|
+
pointer-events: none !important;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
:host(:where([opening], [closing])) :is([part='overlay'], [part='backdrop']) {
|
|
63
|
+
animation-name: --fade, --transform;
|
|
64
|
+
animation-duration: var(--vaadin-overlay-animation-duration);
|
|
65
|
+
animation-timing-function: var(--vaadin-overlay-animation-timing-function);
|
|
66
|
+
animation-delay: var(--vaadin-overlay-animation-delay);
|
|
67
|
+
/* Fill backwards only, so the closed value applies during the delay without overriding theme styles */
|
|
68
|
+
animation-fill-mode: backwards;
|
|
69
|
+
|
|
70
|
+
@media (prefers-reduced-motion) {
|
|
71
|
+
animation-name: --fade;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
:host(:where([opening], [closing])) [part='backdrop'] {
|
|
76
|
+
animation-name: --fade;
|
|
77
|
+
animation-timing-function: linear;
|
|
78
|
+
--vaadin-overlay-opacity-closed: 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
:host(:where([closing])) :is([part='overlay'], [part='backdrop']) {
|
|
82
|
+
animation-direction: reverse;
|
|
83
|
+
animation-fill-mode: both;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@keyframes --no-op {
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Only the closed state is declared, so the animations end at the value the part already has */
|
|
90
|
+
@keyframes --transform {
|
|
91
|
+
0% {
|
|
92
|
+
transform: var(--vaadin-overlay-transform-closed);
|
|
93
|
+
translate: var(--vaadin-overlay-translate-closed);
|
|
94
|
+
scale: var(--vaadin-overlay-scale-closed);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@keyframes --fade {
|
|
99
|
+
0% {
|
|
100
|
+
opacity: var(--vaadin-overlay-opacity-closed);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import '@vaadin/component-base/src/styles/style-props.js';
|
|
7
7
|
import { css } from 'lit';
|
|
8
|
+
import { overlayAnimationStyles } from './vaadin-overlay-animation-base-styles.js';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
const overlayBase = css`
|
|
10
11
|
:host {
|
|
11
12
|
z-index: 200;
|
|
12
13
|
position: fixed;
|
|
@@ -55,6 +56,11 @@ export const overlayStyles = css`
|
|
|
55
56
|
display: none !important;
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
:host([suppressed]) [part='overlay'],
|
|
60
|
+
:host([suppressed]) ::slotted(*) {
|
|
61
|
+
pointer-events: none !important;
|
|
62
|
+
}
|
|
63
|
+
|
|
58
64
|
[part='overlay'] {
|
|
59
65
|
color: var(--vaadin-overlay-text-color, var(--vaadin-text-color));
|
|
60
66
|
background: var(--vaadin-overlay-background, var(--vaadin-background-color));
|
|
@@ -103,3 +109,5 @@ export const overlayStyles = css`
|
|
|
103
109
|
}
|
|
104
110
|
}
|
|
105
111
|
`;
|
|
112
|
+
|
|
113
|
+
export const overlayStyles = [overlayBase, overlayAnimationStyles];
|
|
@@ -11,12 +11,27 @@ export declare function OverlayFocusMixin<T extends Constructor<HTMLElement>>(
|
|
|
11
11
|
|
|
12
12
|
export declare class OverlayFocusMixinClass {
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Set to true to move focus into the overlay automatically on open
|
|
15
|
+
* and keep it inside: Tab and Shift+Tab cycle through the overlay's
|
|
16
|
+
* content until the overlay is closed.
|
|
17
|
+
*
|
|
18
|
+
* Focus moves to the first tabbable element in the tab order. This
|
|
19
|
+
* can be the overlay itself if it has `tabindex` attribute set to `0`
|
|
20
|
+
* on the host element or the `overlay` shadow DOM part.
|
|
21
|
+
*
|
|
16
22
|
* @attr {boolean} focus-trap
|
|
17
23
|
*/
|
|
18
24
|
focusTrap: boolean;
|
|
19
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Set to true to move focus into the overlay automatically on open.
|
|
28
|
+
*
|
|
29
|
+
* Focus moves to the first tabbable element in the tab order. This
|
|
30
|
+
* can be the overlay itself if it has `tabindex` attribute set to `0`
|
|
31
|
+
* on the host element or the `overlay` shadow DOM part.
|
|
32
|
+
*/
|
|
33
|
+
autofocus: boolean;
|
|
34
|
+
|
|
20
35
|
/**
|
|
21
36
|
* Set to true to enable restoring of focus when overlay is closed.
|
|
22
37
|
* @attr {boolean} restore-focus-on-close
|
|
@@ -40,8 +55,9 @@ export declare class OverlayFocusMixinClass {
|
|
|
40
55
|
protected _saveFocus(): void;
|
|
41
56
|
|
|
42
57
|
/**
|
|
43
|
-
* Sets up focus after the overlay opening has completed:
|
|
44
|
-
*
|
|
58
|
+
* Sets up focus after the overlay opening has completed: moves focus into
|
|
59
|
+
* the overlay if `autofocus` is enabled, and traps focus within the overlay
|
|
60
|
+
* if `focusTrap` is enabled.
|
|
45
61
|
*/
|
|
46
62
|
protected _initFocus(): void;
|
|
47
63
|
|
|
@@ -5,15 +5,27 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { FocusRestorationController } from '@vaadin/a11y-base/src/focus-restoration-controller.js';
|
|
7
7
|
import { FocusTrapController } from '@vaadin/a11y-base/src/focus-trap-controller.js';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
getDeepActiveElement,
|
|
10
|
+
getTabbableElements,
|
|
11
|
+
isElementFocused,
|
|
12
|
+
isElementHidden,
|
|
13
|
+
isKeyboardActive,
|
|
14
|
+
} from '@vaadin/a11y-base/src/focus-utils.js';
|
|
9
15
|
|
|
10
16
|
export const OverlayFocusMixin = (superClass) =>
|
|
11
17
|
class OverlayFocusMixin extends superClass {
|
|
12
18
|
static get properties() {
|
|
13
19
|
return {
|
|
14
20
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
21
|
+
* Set to true to move focus into the overlay automatically on open
|
|
22
|
+
* and keep it inside: Tab and Shift+Tab cycle through the overlay's
|
|
23
|
+
* content until the overlay is closed.
|
|
24
|
+
*
|
|
25
|
+
* Focus moves to the first tabbable element in the tab order. This
|
|
26
|
+
* can be the overlay itself if it has `tabindex` attribute set to `0`
|
|
27
|
+
* on the host element or the `overlay` shadow DOM part.
|
|
28
|
+
*
|
|
17
29
|
* @attr {boolean} focus-trap
|
|
18
30
|
*/
|
|
19
31
|
focusTrap: {
|
|
@@ -21,6 +33,18 @@ export const OverlayFocusMixin = (superClass) =>
|
|
|
21
33
|
value: false,
|
|
22
34
|
},
|
|
23
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Set to true to move focus into the overlay automatically on open.
|
|
38
|
+
*
|
|
39
|
+
* Focus moves to the first tabbable element in the tab order. This
|
|
40
|
+
* can be the overlay itself if it has `tabindex` attribute set to `0`
|
|
41
|
+
* on the host element or the `overlay` shadow DOM part.
|
|
42
|
+
*/
|
|
43
|
+
autofocus: {
|
|
44
|
+
type: Boolean,
|
|
45
|
+
value: false,
|
|
46
|
+
},
|
|
47
|
+
|
|
24
48
|
/**
|
|
25
49
|
* Set to true to enable restoring of focus when overlay is closed.
|
|
26
50
|
* @attr {boolean} restore-focus-on-close
|
|
@@ -103,13 +127,25 @@ export const OverlayFocusMixin = (superClass) =>
|
|
|
103
127
|
}
|
|
104
128
|
|
|
105
129
|
/**
|
|
106
|
-
* Sets up focus after the overlay opening has completed:
|
|
107
|
-
*
|
|
130
|
+
* Sets up focus after the overlay opening has completed: moves focus into
|
|
131
|
+
* the overlay if `autofocus` is enabled, and traps focus within the overlay
|
|
132
|
+
* if `focusTrap` is enabled.
|
|
108
133
|
*
|
|
109
134
|
* @protected
|
|
110
135
|
*/
|
|
111
136
|
_initFocus() {
|
|
112
|
-
if (
|
|
137
|
+
if (isElementHidden(this._focusRoot)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (this.autofocus) {
|
|
142
|
+
const tabbables = getTabbableElements(this._focusRoot);
|
|
143
|
+
if (!tabbables.some(isElementFocused)) {
|
|
144
|
+
tabbables[0]?.focus({ focusVisible: isKeyboardActive() });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (this.focusTrap) {
|
|
113
149
|
this.__focusTrapController.trapFocus(this._focusRoot);
|
|
114
150
|
}
|
|
115
151
|
}
|
|
@@ -6,7 +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
|
+
import { getStateAnimations, setOverlayStateAttribute } from './vaadin-overlay-utils.js';
|
|
10
10
|
|
|
11
11
|
export const OverlayMixin = (superClass) =>
|
|
12
12
|
class OverlayMixin extends OverlayFocusMixin(OverlayStackMixin(superClass)) {
|
|
@@ -132,19 +132,6 @@ export const OverlayMixin = (superClass) =>
|
|
|
132
132
|
if (this.$.backdrop) {
|
|
133
133
|
this.$.backdrop.addEventListener('click', () => {});
|
|
134
134
|
}
|
|
135
|
-
|
|
136
|
-
this.addEventListener('mouseup', () => {
|
|
137
|
-
// In Chrome, focus moves to body on overlay content mousedown
|
|
138
|
-
// See https://github.com/vaadin/flow-components/issues/5507
|
|
139
|
-
if (document.activeElement === document.body && this.$.overlay.getAttribute('tabindex') === '0') {
|
|
140
|
-
this.$.overlay.focus();
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
this.addEventListener('animationcancel', () => {
|
|
145
|
-
this._flushAnimation('opening');
|
|
146
|
-
this._flushAnimation('closing');
|
|
147
|
-
});
|
|
148
135
|
}
|
|
149
136
|
|
|
150
137
|
/** @protected */
|
|
@@ -401,37 +388,35 @@ export const OverlayMixin = (superClass) =>
|
|
|
401
388
|
}
|
|
402
389
|
|
|
403
390
|
/**
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
|
|
407
|
-
_shouldAnimate() {
|
|
408
|
-
const style = getComputedStyle(this);
|
|
409
|
-
const name = style.getPropertyValue('animation-name');
|
|
410
|
-
const hasDuration = style
|
|
411
|
-
.getPropertyValue('animation-duration')
|
|
412
|
-
.split(',')
|
|
413
|
-
.some((duration) => parseFloat(duration) > 0);
|
|
414
|
-
const hidden = style.getPropertyValue('display') === 'none';
|
|
415
|
-
return !hidden && name && name !== 'none' && hasDuration;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
391
|
+
* Run the callback once every animation reporting the state has ended, or right away when
|
|
392
|
+
* there is none.
|
|
393
|
+
*
|
|
419
394
|
* @param {string} type
|
|
420
395
|
* @param {Function} callback
|
|
421
396
|
* @private
|
|
422
397
|
*/
|
|
423
398
|
_enqueueAnimation(type, callback) {
|
|
399
|
+
const animations = getStateAnimations(this);
|
|
400
|
+
if (animations.length === 0) {
|
|
401
|
+
callback();
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
424
405
|
const handler = `__${type}Handler`;
|
|
425
|
-
const
|
|
426
|
-
|
|
406
|
+
const finish = () => {
|
|
407
|
+
// The phase may already have been finished by _flushAnimation(), or superseded by a
|
|
408
|
+
// later one of the same type, in which case this callback no longer applies
|
|
409
|
+
if (this[handler] !== finish) {
|
|
427
410
|
return;
|
|
428
411
|
}
|
|
429
|
-
callback
|
|
430
|
-
|
|
412
|
+
// Cleared before the callback, so that a listener reopening the overlay synchronously
|
|
413
|
+
// installs its own handler rather than having it deleted afterwards
|
|
431
414
|
delete this[handler];
|
|
415
|
+
callback();
|
|
432
416
|
};
|
|
433
|
-
this[handler] =
|
|
434
|
-
|
|
417
|
+
this[handler] = finish;
|
|
418
|
+
// A cancelled animation rejects, which ends the phase just as finishing does
|
|
419
|
+
Promise.all(animations.map((animation) => animation.finished)).then(finish, finish);
|
|
435
420
|
}
|
|
436
421
|
|
|
437
422
|
/**
|
|
@@ -452,19 +437,14 @@ export const OverlayMixin = (superClass) =>
|
|
|
452
437
|
}
|
|
453
438
|
this._attachOverlay();
|
|
454
439
|
this._appendAttachedInstance();
|
|
455
|
-
this.bringToFront();
|
|
456
440
|
if (!this.modeless) {
|
|
457
441
|
this._enterModalState();
|
|
458
442
|
}
|
|
459
443
|
setOverlayStateAttribute(this, 'opening', true);
|
|
460
444
|
|
|
461
|
-
|
|
462
|
-
this._enqueueAnimation('opening', () => {
|
|
463
|
-
this._finishOpening();
|
|
464
|
-
});
|
|
465
|
-
} else {
|
|
445
|
+
this._enqueueAnimation('opening', () => {
|
|
466
446
|
this._finishOpening();
|
|
467
|
-
}
|
|
447
|
+
});
|
|
468
448
|
}
|
|
469
449
|
|
|
470
450
|
/** @private */
|
|
@@ -483,7 +463,7 @@ export const OverlayMixin = (superClass) =>
|
|
|
483
463
|
_finishClosing() {
|
|
484
464
|
this._detachOverlay();
|
|
485
465
|
this._removeAttachedInstance();
|
|
486
|
-
this
|
|
466
|
+
this.toggleAttribute('suppressed', false);
|
|
487
467
|
setOverlayStateAttribute(this, 'closing', false);
|
|
488
468
|
this.dispatchEvent(new CustomEvent('vaadin-overlay-closed'));
|
|
489
469
|
}
|
|
@@ -498,13 +478,9 @@ export const OverlayMixin = (superClass) =>
|
|
|
498
478
|
setOverlayStateAttribute(this, 'closing', true);
|
|
499
479
|
this.dispatchEvent(new CustomEvent('vaadin-overlay-closing'));
|
|
500
480
|
|
|
501
|
-
|
|
502
|
-
this._enqueueAnimation('closing', () => {
|
|
503
|
-
this._finishClosing();
|
|
504
|
-
});
|
|
505
|
-
} else {
|
|
481
|
+
this._enqueueAnimation('closing', () => {
|
|
506
482
|
this._finishClosing();
|
|
507
|
-
}
|
|
483
|
+
});
|
|
508
484
|
}
|
|
509
485
|
}
|
|
510
486
|
|
|
@@ -561,6 +537,11 @@ export const OverlayMixin = (superClass) =>
|
|
|
561
537
|
|
|
562
538
|
if (this.opened && !evt.defaultPrevented) {
|
|
563
539
|
this.close(event);
|
|
540
|
+
|
|
541
|
+
// Only for modal overlays which make underlying content non-interactive.
|
|
542
|
+
if (!this.opened && !this.modeless) {
|
|
543
|
+
event.preventDefault();
|
|
544
|
+
}
|
|
564
545
|
}
|
|
565
546
|
}
|
|
566
547
|
|
|
@@ -106,10 +106,10 @@ export const OverlayStackMixin = (superClass) =>
|
|
|
106
106
|
document.body.style.pointerEvents = 'none';
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
//
|
|
109
|
+
// Suppress pointer events in other attached overlays
|
|
110
110
|
getAttachedInstances().forEach((el) => {
|
|
111
111
|
if (el !== this) {
|
|
112
|
-
el
|
|
112
|
+
el.toggleAttribute('suppressed', true);
|
|
113
113
|
}
|
|
114
114
|
});
|
|
115
115
|
}
|
|
@@ -122,7 +122,7 @@ export const OverlayStackMixin = (superClass) =>
|
|
|
122
122
|
delete this._previousDocumentPointerEvents;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
//
|
|
125
|
+
// Stop suppressing pointer events in the previous overlay(s)
|
|
126
126
|
const instances = getAttachedInstances();
|
|
127
127
|
|
|
128
128
|
let el;
|
|
@@ -132,7 +132,7 @@ export const OverlayStackMixin = (superClass) =>
|
|
|
132
132
|
// Skip the current instance
|
|
133
133
|
continue;
|
|
134
134
|
}
|
|
135
|
-
el
|
|
135
|
+
el.toggleAttribute('suppressed', false);
|
|
136
136
|
if (!el.modeless) {
|
|
137
137
|
// Stop after the last modal
|
|
138
138
|
break;
|
|
@@ -12,6 +12,20 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export function observeMove(element: HTMLElement, callback: () => void): () => void;
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Collect the animations that report the state of the given element, so that their end can be
|
|
17
|
+
* awaited before the element is hidden or removed. The animations are read instead of the
|
|
18
|
+
* computed style, so that the decision to wait cannot disagree with what the browser actually
|
|
19
|
+
* created: a keyframes rule that does not exist and an element that is not rendered both give
|
|
20
|
+
* an empty list.
|
|
21
|
+
*
|
|
22
|
+
* Only CSS animations count, as the transitions and the script animations that `getAnimations()`
|
|
23
|
+
* also returns do not report the state. Animations of any content are already left out, as
|
|
24
|
+
* `getAnimations()` only descends into the subtree when asked to. Animations that take no time
|
|
25
|
+
* are dropped as well, so that a delay on its own does not hold the state.
|
|
26
|
+
*/
|
|
27
|
+
export function getStateAnimations(element: HTMLElement): Animation[];
|
|
28
|
+
|
|
15
29
|
/**
|
|
16
30
|
* Toggle the state attribute on the overlay element and also its owner element. This allows targeting state attributes
|
|
17
31
|
* in the light DOM in case the overlay is in the shadow DOM of its owner.
|
|
@@ -85,6 +85,29 @@ export function observeMove(element, callback) {
|
|
|
85
85
|
return cleanup;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Collect the animations that report the state of the given element, so that their end can be
|
|
90
|
+
* awaited before the element is hidden or removed. The animations are read instead of the
|
|
91
|
+
* computed style, so that the decision to wait cannot disagree with what the browser actually
|
|
92
|
+
* created: a keyframes rule that does not exist and an element that is not rendered both give
|
|
93
|
+
* an empty list.
|
|
94
|
+
*
|
|
95
|
+
* Only CSS animations count, as the transitions and the script animations that `getAnimations()`
|
|
96
|
+
* also returns do not report the state. Animations of any content are already left out, as
|
|
97
|
+
* `getAnimations()` only descends into the subtree when asked to. Animations that take no time
|
|
98
|
+
* are dropped as well, so that a delay on its own does not hold the state.
|
|
99
|
+
*
|
|
100
|
+
* @param {HTMLElement} element
|
|
101
|
+
* @return {!Array<!Animation>}
|
|
102
|
+
*/
|
|
103
|
+
export function getStateAnimations(element) {
|
|
104
|
+
return element
|
|
105
|
+
.getAnimations()
|
|
106
|
+
.filter(
|
|
107
|
+
(animation) => animation instanceof CSSAnimation && animation.effect.getComputedTiming().activeDuration > 0,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
88
111
|
/**
|
|
89
112
|
* Toggle the state attribute on the overlay element and also its owner element. This allows targeting state attributes
|
|
90
113
|
* in the light DOM in case the overlay is in the shadow DOM of its owner.
|
package/src/vaadin-overlay.d.ts
CHANGED
|
@@ -118,6 +118,8 @@ export type OverlayEventMap = HTMLElementEventMap & OverlayCustomEventMap;
|
|
|
118
118
|
* @fires {CustomEvent} vaadin-overlay-closed - Fired after the overlay is closed.
|
|
119
119
|
* @fires {CustomEvent} vaadin-overlay-outside-click - Fired before the overlay is closed on outside click. Calling `preventDefault()` on the event cancels the closing.
|
|
120
120
|
* @fires {CustomEvent} vaadin-overlay-escape-press - Fired before the overlay is closed on Escape key press. Calling `preventDefault()` on the event cancels the closing.
|
|
121
|
+
* @deprecated `<vaadin-overlay>` is deprecated and will be removed in Vaadin 26.
|
|
122
|
+
* Consider using `OverlayMixin` and `PositionMixin` instead.
|
|
121
123
|
*/
|
|
122
124
|
declare class Overlay extends OverlayMixin(ThemableMixin(DirMixin(HTMLElement))) {
|
|
123
125
|
addEventListener<K extends keyof OverlayEventMap>(
|
package/src/vaadin-overlay.js
CHANGED
|
@@ -7,6 +7,7 @@ import { html, LitElement } from 'lit';
|
|
|
7
7
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
8
8
|
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
9
9
|
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
10
|
+
import { issueWarning } from '@vaadin/component-base/src/warnings.js';
|
|
10
11
|
import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
|
|
11
12
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
12
13
|
import { overlayStyles } from './styles/vaadin-overlay-base-styles.js';
|
|
@@ -73,6 +74,8 @@ import { OverlayMixin } from './vaadin-overlay-mixin.js';
|
|
|
73
74
|
*
|
|
74
75
|
* @customElement vaadin-overlay
|
|
75
76
|
* @extends HTMLElement
|
|
77
|
+
* @deprecated `<vaadin-overlay>` is deprecated and will be removed in Vaadin 26.
|
|
78
|
+
* Consider using `OverlayMixin` and `PositionMixin` instead.
|
|
76
79
|
*/
|
|
77
80
|
class Overlay extends OverlayMixin(DirMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))))) {
|
|
78
81
|
static get is() {
|
|
@@ -94,6 +97,15 @@ class Overlay extends OverlayMixin(DirMixin(ThemableMixin(PolylitMixin(LumoInjec
|
|
|
94
97
|
</div>
|
|
95
98
|
`;
|
|
96
99
|
}
|
|
100
|
+
|
|
101
|
+
/** @protected */
|
|
102
|
+
firstUpdated() {
|
|
103
|
+
super.firstUpdated();
|
|
104
|
+
|
|
105
|
+
issueWarning(
|
|
106
|
+
'`<vaadin-overlay>` is deprecated and will be removed in Vaadin 26. Consider using `OverlayMixin` and `PositionMixin` instead.',
|
|
107
|
+
);
|
|
108
|
+
}
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
defineCustomElement(Overlay);
|