@vaadin/popover 25.2.0-alpha1 → 25.2.0-alpha11
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 +54 -0
- package/package.json +11 -11
- package/src/vaadin-popover-focus-controller.d.ts +35 -0
- package/src/vaadin-popover-focus-controller.js +242 -0
- package/src/vaadin-popover-overlay-mixin.js +0 -4
- package/src/vaadin-popover-overlay.js +0 -3
- package/src/vaadin-popover-position-mixin.js +0 -2
- package/src/vaadin-popover-target-mixin.js +0 -2
- package/src/vaadin-popover.d.ts +13 -0
- package/src/vaadin-popover.js +21 -244
- package/web-types.json +56 -112
- package/web-types.lit.json +34 -27
package/custom-elements.json
CHANGED
|
@@ -17,6 +17,42 @@
|
|
|
17
17
|
}
|
|
18
18
|
]
|
|
19
19
|
},
|
|
20
|
+
{
|
|
21
|
+
"kind": "javascript-module",
|
|
22
|
+
"path": "src/vaadin-popover-focus-controller.js",
|
|
23
|
+
"declarations": [
|
|
24
|
+
{
|
|
25
|
+
"kind": "class",
|
|
26
|
+
"description": "Controller that routes Tab and Shift+Tab when a non-modal popover is opened.\nThe controller's host element is the popover itself.\n\nThe popover is reachable via Tab only from its target, and its content comes\nlogically right after the target — regardless of the popover's DOM position.\nWhen the popover lives inside a focus trap (e.g. a dialog), the controller\ncooperates with the active `FocusTrapController` so the trap never lands\nfocus on the popover itself.\n\nModal popovers rely on the overlay's own focus trap; this controller bails\nout early in that case.",
|
|
27
|
+
"name": "PopoverFocusController",
|
|
28
|
+
"members": [
|
|
29
|
+
{
|
|
30
|
+
"kind": "method",
|
|
31
|
+
"name": "activate"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"kind": "method",
|
|
35
|
+
"name": "deactivate"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"kind": "field",
|
|
39
|
+
"name": "host",
|
|
40
|
+
"default": "host"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"exports": [
|
|
46
|
+
{
|
|
47
|
+
"kind": "js",
|
|
48
|
+
"name": "PopoverFocusController",
|
|
49
|
+
"declaration": {
|
|
50
|
+
"name": "PopoverFocusController",
|
|
51
|
+
"module": "src/vaadin-popover-focus-controller.js"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
20
56
|
{
|
|
21
57
|
"kind": "javascript-module",
|
|
22
58
|
"path": "src/vaadin-popover-overlay-mixin.js",
|
|
@@ -776,6 +812,16 @@
|
|
|
776
812
|
"description": "Set to true to disable closing popover on outside click.",
|
|
777
813
|
"attribute": "no-close-on-outside-click"
|
|
778
814
|
},
|
|
815
|
+
{
|
|
816
|
+
"kind": "field",
|
|
817
|
+
"name": "noTabFocus",
|
|
818
|
+
"privacy": "public",
|
|
819
|
+
"type": {
|
|
820
|
+
"text": "boolean"
|
|
821
|
+
},
|
|
822
|
+
"description": "When true, pressing Tab on the target or a sibling element does not move\nfocus into the popover's content (including any nested popovers), and\nShift+Tab does not move focus into the popover's last focusable. Focus\nstill moves out of the popover on Tab / Shift+Tab if it was placed\ninside programmatically.\n\nHas no effect on modal popovers, which use their own focus trap.",
|
|
823
|
+
"attribute": "no-tab-focus"
|
|
824
|
+
},
|
|
779
825
|
{
|
|
780
826
|
"kind": "field",
|
|
781
827
|
"name": "opened",
|
|
@@ -1036,6 +1082,14 @@
|
|
|
1036
1082
|
"description": "Set to true to disable closing popover on outside click.",
|
|
1037
1083
|
"fieldName": "noCloseOnOutsideClick"
|
|
1038
1084
|
},
|
|
1085
|
+
{
|
|
1086
|
+
"name": "no-tab-focus",
|
|
1087
|
+
"type": {
|
|
1088
|
+
"text": "boolean"
|
|
1089
|
+
},
|
|
1090
|
+
"description": "When true, pressing Tab on the target or a sibling element does not move\nfocus into the popover's content (including any nested popovers), and\nShift+Tab does not move focus into the popover's last focusable. Focus\nstill moves out of the popover on Tab / Shift+Tab if it was placed\ninside programmatically.\n\nHas no effect on modal popovers, which use their own focus trap.",
|
|
1091
|
+
"fieldName": "noTabFocus"
|
|
1092
|
+
},
|
|
1039
1093
|
{
|
|
1040
1094
|
"name": "opened",
|
|
1041
1095
|
"type": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/popover",
|
|
3
|
-
"version": "25.2.0-
|
|
3
|
+
"version": "25.2.0-alpha11",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -37,19 +37,19 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@open-wc/dedupe-mixin": "^1.3.0",
|
|
40
|
-
"@vaadin/a11y-base": "25.2.0-
|
|
41
|
-
"@vaadin/component-base": "25.2.0-
|
|
42
|
-
"@vaadin/lit-renderer": "25.2.0-
|
|
43
|
-
"@vaadin/overlay": "25.2.0-
|
|
44
|
-
"@vaadin/vaadin-themable-mixin": "25.2.0-
|
|
40
|
+
"@vaadin/a11y-base": "25.2.0-alpha11",
|
|
41
|
+
"@vaadin/component-base": "25.2.0-alpha11",
|
|
42
|
+
"@vaadin/lit-renderer": "25.2.0-alpha11",
|
|
43
|
+
"@vaadin/overlay": "25.2.0-alpha11",
|
|
44
|
+
"@vaadin/vaadin-themable-mixin": "25.2.0-alpha11",
|
|
45
45
|
"lit": "^3.0.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@vaadin/aura": "25.2.0-
|
|
49
|
-
"@vaadin/chai-plugins": "25.2.0-
|
|
50
|
-
"@vaadin/test-runner-commands": "25.2.0-
|
|
48
|
+
"@vaadin/aura": "25.2.0-alpha11",
|
|
49
|
+
"@vaadin/chai-plugins": "25.2.0-alpha11",
|
|
50
|
+
"@vaadin/test-runner-commands": "25.2.0-alpha11",
|
|
51
51
|
"@vaadin/testing-helpers": "^2.0.0",
|
|
52
|
-
"@vaadin/vaadin-lumo-styles": "25.2.0-
|
|
52
|
+
"@vaadin/vaadin-lumo-styles": "25.2.0-alpha11",
|
|
53
53
|
"sinon": "^21.0.2"
|
|
54
54
|
},
|
|
55
55
|
"customElements": "custom-elements.json",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"web-types.json",
|
|
58
58
|
"web-types.lit.json"
|
|
59
59
|
],
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "fdc37e932709f95491a027aeb2090911cb7528c6"
|
|
61
61
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2024 - 2026 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import type { Popover } from './vaadin-popover.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Controller that routes Tab and Shift+Tab when a non-modal popover is opened.
|
|
10
|
+
* The controller's host element is the popover itself.
|
|
11
|
+
*
|
|
12
|
+
* The popover is reachable via Tab only from its target, and its content comes
|
|
13
|
+
* logically right after the target — regardless of the popover's DOM position.
|
|
14
|
+
* When the popover lives inside a focus trap (e.g. a dialog), the controller
|
|
15
|
+
* cooperates with the active `FocusTrapController` so the trap never lands
|
|
16
|
+
* focus on the popover itself.
|
|
17
|
+
*
|
|
18
|
+
* Modal popovers rely on the overlay's own focus trap; this controller bails
|
|
19
|
+
* out early in that case.
|
|
20
|
+
*/
|
|
21
|
+
export class PopoverFocusController {
|
|
22
|
+
host: Popover;
|
|
23
|
+
|
|
24
|
+
constructor(host: Popover);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Starts listening for Tab keystrokes. Called when the popover opens.
|
|
28
|
+
*/
|
|
29
|
+
activate(): void;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Stops listening for Tab keystrokes. Called when the popover closes.
|
|
33
|
+
*/
|
|
34
|
+
deactivate(): void;
|
|
35
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2024 - 2026 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { getActiveTrappingNode } from '@vaadin/a11y-base/src/focus-trap-controller.js';
|
|
7
|
+
import { getDeepActiveElement, getFocusableElements, isElementFocused } from '@vaadin/a11y-base/src/focus-utils.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Controller that routes Tab and Shift+Tab when a non-modal popover is opened.
|
|
11
|
+
* The controller's host element is the popover itself.
|
|
12
|
+
*
|
|
13
|
+
* The popover is reachable via Tab only from its target, and its content comes
|
|
14
|
+
* logically right after the target — regardless of the popover's DOM position.
|
|
15
|
+
* When the popover lives inside a focus trap (e.g. a dialog), the controller
|
|
16
|
+
* cooperates with the active `FocusTrapController` so the trap never lands
|
|
17
|
+
* focus on the popover itself.
|
|
18
|
+
*
|
|
19
|
+
* Modal popovers rely on the overlay's own focus trap; this controller bails
|
|
20
|
+
* out early in that case.
|
|
21
|
+
*/
|
|
22
|
+
export class PopoverFocusController {
|
|
23
|
+
constructor(host) {
|
|
24
|
+
this.host = host;
|
|
25
|
+
this.__onKeyDown = this.__onKeyDown.bind(this);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
activate() {
|
|
29
|
+
document.addEventListener('keydown', this.__onKeyDown, true);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
deactivate() {
|
|
33
|
+
document.removeEventListener('keydown', this.__onKeyDown, true);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** @private */
|
|
37
|
+
__handleTab(event) {
|
|
38
|
+
const host = this.host;
|
|
39
|
+
const targetFocusable = this.__getTargetFocusable();
|
|
40
|
+
|
|
41
|
+
if (targetFocusable && isElementFocused(targetFocusable)) {
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
if (host.noTabFocus) {
|
|
44
|
+
this.__moveLogicalNext(event, targetFocusable);
|
|
45
|
+
} else {
|
|
46
|
+
host.focus();
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const lastPopoverFocusable = this.__getLastPopoverFocusable();
|
|
52
|
+
if (isElementFocused(lastPopoverFocusable)) {
|
|
53
|
+
// With noTabFocus the popover isn't in the logical list, so the target
|
|
54
|
+
// becomes the reference for "next after the popover".
|
|
55
|
+
this.__moveLogicalNext(event, host.noTabFocus ? targetFocusable : host);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Native Tab would land on the popover when DOM order places it right
|
|
60
|
+
// after the current element. Skip past it via the logical list.
|
|
61
|
+
const activeElement = getDeepActiveElement();
|
|
62
|
+
const scopeFocusables = this.__getScopeFocusables();
|
|
63
|
+
const activeIdx = scopeFocusables.indexOf(activeElement);
|
|
64
|
+
if (activeIdx >= 0 && scopeFocusables[activeIdx + 1] === host) {
|
|
65
|
+
this.__moveLogicalNext(event, activeElement, scopeFocusables);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** @private */
|
|
70
|
+
__handleShiftTab(event) {
|
|
71
|
+
const host = this.host;
|
|
72
|
+
const targetFocusable = this.__getTargetFocusable();
|
|
73
|
+
|
|
74
|
+
// Clear the flag so native Shift+Tab from the target doesn't reopen. Fall
|
|
75
|
+
// through to the redirect logic: when the popover is DOM-prev of the target,
|
|
76
|
+
// case 5 steers focus away from it instead of letting the browser land on
|
|
77
|
+
// the popover host or its content.
|
|
78
|
+
if (targetFocusable && isElementFocused(targetFocusable) && host.__shouldRestoreFocus) {
|
|
79
|
+
host.__shouldRestoreFocus = false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (isElementFocused(host)) {
|
|
83
|
+
event.preventDefault();
|
|
84
|
+
targetFocusable.focus();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Browser handles Shift+Tab inside popover content.
|
|
89
|
+
const activeElement = getDeepActiveElement();
|
|
90
|
+
if (host.contains(activeElement)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const scopeFocusables = this.__getScopeFocusables();
|
|
95
|
+
const logicalFocusables = this.__buildLogicalList(scopeFocusables);
|
|
96
|
+
const activeLogicalIdx = logicalFocusables.indexOf(activeElement);
|
|
97
|
+
const prevFocusable = activeLogicalIdx > 0 ? logicalFocusables[activeLogicalIdx - 1] : null;
|
|
98
|
+
|
|
99
|
+
// When the logical previous is the popover, move focus into the popover tail.
|
|
100
|
+
if (prevFocusable === host) {
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
this.__getLastPopoverFocusable().focus();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Native Shift+Tab would land on the popover: skip it and redirect to the
|
|
107
|
+
// true logical previous, or wrap when at the logical start inside a trap.
|
|
108
|
+
const activeScopeIdx = scopeFocusables.indexOf(activeElement);
|
|
109
|
+
if (activeScopeIdx > 0 && scopeFocusables[activeScopeIdx - 1] === host) {
|
|
110
|
+
if (prevFocusable) {
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
prevFocusable.focus();
|
|
113
|
+
} else if (getActiveTrappingNode(host)) {
|
|
114
|
+
this.__wrapToLogicalLast(event, logicalFocusables);
|
|
115
|
+
} else if (host.noTabFocus) {
|
|
116
|
+
// No redirect target and no trap: still block native Shift+Tab so the
|
|
117
|
+
// popover host can never receive Shift+Tab focus.
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// At the logical start of a trap: wrap to the logical last. When the
|
|
124
|
+
// popover is the logical last (target is last), this lands on the popover tail.
|
|
125
|
+
if (!prevFocusable && getActiveTrappingNode(host)) {
|
|
126
|
+
this.__wrapToLogicalLast(event, logicalFocusables);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** @private */
|
|
131
|
+
__onKeyDown(event) {
|
|
132
|
+
// Modal popovers rely on the overlay's focus trap.
|
|
133
|
+
if (this.host.modal) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (event.key !== 'Tab') {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (event.shiftKey) {
|
|
140
|
+
this.__handleShiftTab(event);
|
|
141
|
+
} else {
|
|
142
|
+
this.__handleTab(event);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** @private */
|
|
147
|
+
__getTargetFocusable() {
|
|
148
|
+
const target = this.host.target;
|
|
149
|
+
if (!target) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
return target.focusElement || target;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The popover's tail element: the last focusable inside the popover's content
|
|
157
|
+
* area, or the popover itself when it has no focusable content.
|
|
158
|
+
* @private
|
|
159
|
+
*/
|
|
160
|
+
__getLastPopoverFocusable() {
|
|
161
|
+
const lastContent = getFocusableElements(this.host._overlayElement.$.content).pop();
|
|
162
|
+
return lastContent || this.host;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* DOM-ordered focusables in the current scope (active focus trap, or document
|
|
167
|
+
* body), with popover light-DOM descendants excluded but the popover itself
|
|
168
|
+
* retained. Used to detect DOM adjacency to the popover.
|
|
169
|
+
* @private
|
|
170
|
+
*/
|
|
171
|
+
__getScopeFocusables() {
|
|
172
|
+
const host = this.host;
|
|
173
|
+
const scope = getActiveTrappingNode(host) || document.body;
|
|
174
|
+
return getFocusableElements(scope).filter((el) => el === host || !host.contains(el));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Scope focusables in *logical* tab order: the popover is moved from its DOM
|
|
179
|
+
* position to right after the target focusable. The popover is left out of
|
|
180
|
+
* the list entirely when there is no target, or when `noTabFocus` is set.
|
|
181
|
+
* @private
|
|
182
|
+
*/
|
|
183
|
+
__buildLogicalList(scopeFocusables = this.__getScopeFocusables()) {
|
|
184
|
+
const host = this.host;
|
|
185
|
+
const targetFocusable = this.__getTargetFocusable();
|
|
186
|
+
const logicalFocusables = scopeFocusables.filter((el) => el !== host);
|
|
187
|
+
|
|
188
|
+
if (!host.noTabFocus && targetFocusable && targetFocusable !== host) {
|
|
189
|
+
const targetIdx = logicalFocusables.indexOf(targetFocusable);
|
|
190
|
+
if (targetIdx >= 0) {
|
|
191
|
+
logicalFocusables.splice(targetIdx + 1, 0, host);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return logicalFocusables;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** @private */
|
|
198
|
+
__moveLogicalNext(event, from, scopeFocusables) {
|
|
199
|
+
const host = this.host;
|
|
200
|
+
const logicalFocusables = this.__buildLogicalList(scopeFocusables);
|
|
201
|
+
const fromIdx = logicalFocusables.indexOf(from);
|
|
202
|
+
|
|
203
|
+
let focusable;
|
|
204
|
+
if (fromIdx >= 0) {
|
|
205
|
+
// The popover sits right after the target in the logical list, so it can
|
|
206
|
+
// be the logical next only when `from` is the target. Skip it: the popover
|
|
207
|
+
// is Tab-reachable only from its target (handled in __handleTab case 1).
|
|
208
|
+
let nextIdx = fromIdx + 1;
|
|
209
|
+
if (logicalFocusables[nextIdx] === host) {
|
|
210
|
+
nextIdx += 1;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Past the end inside a trap: wrap to the first element. The popover
|
|
214
|
+
// never sits at position 0 (it only follows a target), so list[0] is real.
|
|
215
|
+
focusable = logicalFocusables[nextIdx];
|
|
216
|
+
if (!focusable && getActiveTrappingNode(host)) {
|
|
217
|
+
focusable = logicalFocusables[0];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (focusable) {
|
|
222
|
+
event.preventDefault();
|
|
223
|
+
focusable.focus();
|
|
224
|
+
} else if (host.noTabFocus) {
|
|
225
|
+
// No redirect target found: still block native Tab so the popover host
|
|
226
|
+
// can never receive Tab focus when it is DOM-adjacent.
|
|
227
|
+
event.preventDefault();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** @private */
|
|
232
|
+
__wrapToLogicalLast(event, logicalFocusables) {
|
|
233
|
+
const logicalLast = logicalFocusables.at(-1);
|
|
234
|
+
if (!logicalLast) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
// When the popover is the logical last, land on the popover tail instead.
|
|
238
|
+
const focusable = logicalLast === this.host ? this.__getLastPopoverFocusable() : logicalLast;
|
|
239
|
+
event.preventDefault();
|
|
240
|
+
focusable.focus();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -8,10 +8,6 @@ import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* A mixin providing common popover overlay functionality.
|
|
11
|
-
*
|
|
12
|
-
* @polymerMixin
|
|
13
|
-
* @mixes PositionMixin
|
|
14
|
-
* @mixes OverlayMixin
|
|
15
11
|
*/
|
|
16
12
|
export const PopoverOverlayMixin = (superClass) =>
|
|
17
13
|
class PopoverOverlayMixinClass extends PositionMixin(OverlayMixin(superClass)) {
|
|
@@ -18,9 +18,6 @@ import { PopoverOverlayMixin } from './vaadin-popover-overlay-mixin.js';
|
|
|
18
18
|
*
|
|
19
19
|
* @customElement vaadin-popover-overlay
|
|
20
20
|
* @extends HTMLElement
|
|
21
|
-
* @mixes DirMixin
|
|
22
|
-
* @mixes PopoverOverlayMixin
|
|
23
|
-
* @mixes ThemableMixin
|
|
24
21
|
* @private
|
|
25
22
|
*/
|
|
26
23
|
class PopoverOverlay extends PopoverOverlayMixin(
|
|
@@ -8,8 +8,6 @@ import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* A mixin providing popover target functionality.
|
|
11
|
-
*
|
|
12
|
-
* @polymerMixin
|
|
13
11
|
*/
|
|
14
12
|
export const PopoverTargetMixin = (superClass) =>
|
|
15
13
|
class PopoverTargetMixinClass extends superClass {
|
package/src/vaadin-popover.d.ts
CHANGED
|
@@ -205,6 +205,19 @@ declare class Popover extends PopoverPositionMixin(PopoverTargetMixin(ThemePrope
|
|
|
205
205
|
*/
|
|
206
206
|
noCloseOnEsc: boolean;
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* When true, pressing Tab on the target or a sibling element does not move
|
|
210
|
+
* focus into the popover's content (including any nested popovers), and
|
|
211
|
+
* Shift+Tab does not move focus into the popover's last focusable. Focus
|
|
212
|
+
* still moves out of the popover on Tab / Shift+Tab if it was placed
|
|
213
|
+
* inside programmatically.
|
|
214
|
+
*
|
|
215
|
+
* Has no effect on modal popovers, which use their own focus trap.
|
|
216
|
+
*
|
|
217
|
+
* @attr {boolean} no-tab-focus
|
|
218
|
+
*/
|
|
219
|
+
noTabFocus: boolean;
|
|
220
|
+
|
|
208
221
|
/**
|
|
209
222
|
* Popover trigger mode, used to configure how the popover is opened or closed.
|
|
210
223
|
* Could be set to multiple by providing an array, e.g. `trigger = ['hover', 'focus']`.
|