@vaadin/popover 25.2.0-alpha1 → 25.2.0-alpha10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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-alpha1",
3
+ "version": "25.2.0-alpha10",
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-alpha1",
41
- "@vaadin/component-base": "25.2.0-alpha1",
42
- "@vaadin/lit-renderer": "25.2.0-alpha1",
43
- "@vaadin/overlay": "25.2.0-alpha1",
44
- "@vaadin/vaadin-themable-mixin": "25.2.0-alpha1",
40
+ "@vaadin/a11y-base": "25.2.0-alpha10",
41
+ "@vaadin/component-base": "25.2.0-alpha10",
42
+ "@vaadin/lit-renderer": "25.2.0-alpha10",
43
+ "@vaadin/overlay": "25.2.0-alpha10",
44
+ "@vaadin/vaadin-themable-mixin": "25.2.0-alpha10",
45
45
  "lit": "^3.0.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@vaadin/aura": "25.2.0-alpha1",
49
- "@vaadin/chai-plugins": "25.2.0-alpha1",
50
- "@vaadin/test-runner-commands": "25.2.0-alpha1",
48
+ "@vaadin/aura": "25.2.0-alpha10",
49
+ "@vaadin/chai-plugins": "25.2.0-alpha10",
50
+ "@vaadin/test-runner-commands": "25.2.0-alpha10",
51
51
  "@vaadin/testing-helpers": "^2.0.0",
52
- "@vaadin/vaadin-lumo-styles": "25.2.0-alpha1",
52
+ "@vaadin/vaadin-lumo-styles": "25.2.0-alpha10",
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": "866f813f89655a351cbd25328eba1fcb317e267d"
60
+ "gitHead": "1303b6a3eeecb44a9d26f2b53cb56d9e906febdf"
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
+ }
@@ -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']`.
@@ -6,13 +6,7 @@
6
6
  import './vaadin-popover-overlay.js';
7
7
  import { css, html, LitElement } from 'lit';
8
8
  import { ifDefined } from 'lit/directives/if-defined.js';
9
- import { getActiveTrappingNode } from '@vaadin/a11y-base/src/focus-trap-controller.js';
10
- import {
11
- getDeepActiveElement,
12
- getFocusableElements,
13
- isElementFocused,
14
- isKeyboardActive,
15
- } from '@vaadin/a11y-base/src/focus-utils.js';
9
+ import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
16
10
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
17
11
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
18
12
  import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
@@ -22,6 +16,7 @@ import {
22
16
  isLastOverlay as isLastOverlayBase,
23
17
  } from '@vaadin/overlay/src/vaadin-overlay-stack-mixin.js';
24
18
  import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
19
+ import { PopoverFocusController } from './vaadin-popover-focus-controller.js';
25
20
  import { PopoverPositionMixin } from './vaadin-popover-position-mixin.js';
26
21
  import { PopoverTargetMixin } from './vaadin-popover-target-mixin.js';
27
22
 
@@ -403,6 +398,22 @@ class Popover extends PopoverPositionMixin(
403
398
  value: false,
404
399
  },
405
400
 
401
+ /**
402
+ * When true, pressing Tab on the target or a sibling element does not move
403
+ * focus into the popover's content (including any nested popovers), and
404
+ * Shift+Tab does not move focus into the popover's last focusable. Focus
405
+ * still moves out of the popover on Tab / Shift+Tab if it was placed
406
+ * inside programmatically.
407
+ *
408
+ * Has no effect on modal popovers, which use their own focus trap.
409
+ *
410
+ * @attr {boolean} no-tab-focus
411
+ */
412
+ noTabFocus: {
413
+ type: Boolean,
414
+ value: false,
415
+ },
416
+
406
417
  /**
407
418
  * Popover trigger mode, used to configure how the popover is opened or closed.
408
419
  * Could be set to multiple by providing an array, e.g. `trigger = ['hover', 'focus']`.
@@ -486,7 +497,6 @@ class Popover extends PopoverPositionMixin(
486
497
 
487
498
  this.__generatedId = `vaadin-popover-${generateUniqueId()}`;
488
499
 
489
- this.__onGlobalKeyDown = this.__onGlobalKeyDown.bind(this);
490
500
  this.__onTargetClick = this.__onTargetClick.bind(this);
491
501
  this.__onTargetFocusIn = this.__onTargetFocusIn.bind(this);
492
502
  this.__onTargetFocusOut = this.__onTargetFocusOut.bind(this);
@@ -494,6 +504,7 @@ class Popover extends PopoverPositionMixin(
494
504
  this.__onTargetMouseLeave = this.__onTargetMouseLeave.bind(this);
495
505
 
496
506
  this._openedStateController = new PopoverOpenedStateController(this);
507
+ this._focusController = new PopoverFocusController(this);
497
508
  }
498
509
 
499
510
  /** @protected */
@@ -667,9 +678,9 @@ class Popover extends PopoverPositionMixin(
667
678
  /** @private */
668
679
  __openedChanged(opened, oldOpened) {
669
680
  if (opened) {
670
- document.addEventListener('keydown', this.__onGlobalKeyDown, true);
681
+ this._focusController.activate();
671
682
  } else if (oldOpened) {
672
- document.removeEventListener('keydown', this.__onGlobalKeyDown, true);
683
+ this._focusController.deactivate();
673
684
  }
674
685
  }
675
686
 
@@ -714,230 +725,6 @@ class Popover extends PopoverPositionMixin(
714
725
  }
715
726
  }
716
727
 
717
- /**
718
- * Overlay's global Escape press listener doesn't work when
719
- * the overlay is modeless, so we use a separate listener.
720
- * @private
721
- */
722
- __onGlobalKeyDown(event) {
723
- // Modal popover uses overlay logic focus trap.
724
- if (this.modal) {
725
- return;
726
- }
727
-
728
- // Include popover content in the Tab order after the target.
729
- if (event.key === 'Tab') {
730
- if (event.shiftKey) {
731
- this.__onGlobalShiftTab(event);
732
- } else {
733
- this.__onGlobalTab(event);
734
- }
735
- }
736
- }
737
-
738
- /** @private */
739
- __onGlobalTab(event) {
740
- // Move focus to the popover on target element Tab
741
- if (this.target && isElementFocused(this.__getTargetFocusable())) {
742
- event.preventDefault();
743
- this.focus();
744
- return;
745
- }
746
-
747
- // Cache filtered focusable list for this keystroke to avoid redundant DOM traversals
748
- const focusables = this.__getScopeFocusables();
749
-
750
- // Move focus to the next element after target on last content Tab,
751
- // or when popover itself is focused and has no focusable content
752
- const lastFocusable = this.__getLastFocusable();
753
- const isFocusOut = lastFocusable ? isElementFocused(lastFocusable) : isElementFocused(this);
754
- if (isFocusOut) {
755
- let focusable = this.__getNextScopeFocusable(this.__getTargetFocusable(), focusables);
756
- // If the next element after the target is the popover itself (DOM position
757
- // differs from logical position), skip past it to the actual next element.
758
- if (focusable === this) {
759
- focusable = this.__getNextScopeFocusable(this, focusables);
760
- }
761
- if (focusable) {
762
- event.preventDefault();
763
- focusable.focus();
764
- return;
765
- }
766
- // No next element after the target in the scope. When inside a focus trap,
767
- // wrap explicitly to the first focusable. Don't fall through - the
768
- // FocusTrapController uses DOM order which may differ from the popover's
769
- // logical tab position.
770
- if (getActiveTrappingNode(this) && focusables[0]) {
771
- event.preventDefault();
772
- focusables[0].focus();
773
- return;
774
- }
775
- }
776
-
777
- // Handle cases where Tab from the current element would land on the popover
778
- const activeElement = getDeepActiveElement();
779
- const nextFocusable = this.__getNextScopeFocusable(activeElement, focusables);
780
- if (nextFocusable === this) {
781
- // The popover should only be Tab-reachable from its target (handled above).
782
- // Skip the popover when Tab from any other element would land on it
783
- // due to its DOM position.
784
- const focusableAfterPopover = this.__getNextScopeFocusable(this, focusables);
785
- if (focusableAfterPopover) {
786
- event.preventDefault();
787
- focusableAfterPopover.focus();
788
- } else if (getActiveTrappingNode(this) && focusables[0]) {
789
- // Popover is last in DOM scope but shouldn't be Tab-reachable from
790
- // non-target elements. Wrap to first focusable in focus trap.
791
- event.preventDefault();
792
- focusables[0].focus();
793
- }
794
- }
795
- }
796
-
797
- /** @private */
798
- __onGlobalShiftTab(event) {
799
- // Prevent restoring focus after target blur on Shift + Tab
800
- if (this.target && isElementFocused(this.__getTargetFocusable()) && this.__shouldRestoreFocus) {
801
- this.__shouldRestoreFocus = false;
802
- return;
803
- }
804
-
805
- // Move focus back to the target on popover Shift + Tab
806
- if (this.target && isElementFocused(this)) {
807
- event.preventDefault();
808
- this.__getTargetFocusable().focus();
809
- return;
810
- }
811
-
812
- // Don't intercept if focus is inside the popover content.
813
- // The browser's native Shift+Tab handles navigation within
814
- // the overlay (e.g. between focusable content and the popover element itself).
815
- const activeElement = getDeepActiveElement();
816
- if (this.contains(activeElement)) {
817
- return;
818
- }
819
-
820
- // Cache filtered focusable list for this keystroke to avoid redundant DOM traversals
821
- const focusables = this.__getScopeFocusables();
822
-
823
- // Get previous focusable element excluding the popover
824
- const prevFocusable = this.__getPrevScopeFocusable(activeElement, focusables);
825
- const targetFocusable = this.__getTargetFocusable();
826
-
827
- // Intercept Shift+Tab when the previous focusable (excluding the popover)
828
- // is the target. Instead of moving to the target, redirect focus into
829
- // the popover's last focusable content (or the popover itself).
830
- if (prevFocusable === targetFocusable) {
831
- event.preventDefault();
832
- this.__focusLastOrSelf();
833
- return;
834
- }
835
-
836
- // Move focus into the popover when:
837
- // 1. There is no previous focusable element in the focus trap (at the
838
- // beginning, would wrap around), and
839
- // 2. The target is the last focusable in the focus trap (making the
840
- // popover logically last).
841
- // Don't fall through - the FocusTrapController uses DOM order which
842
- // may differ from the popover's logical tab position.
843
- if (!prevFocusable && getActiveTrappingNode(this)) {
844
- const list = focusables.filter((el) => el !== this);
845
- if (list.at(-1) === targetFocusable) {
846
- event.preventDefault();
847
- this.__focusLastOrSelf();
848
- return;
849
- }
850
- // Popover is last in DOM but target is not the last focusable.
851
- // Wrap to last non-popover focusable to prevent FocusTrapController
852
- // from landing on the popover.
853
- const last = list.at(-1);
854
- if (last) {
855
- event.preventDefault();
856
- last.focus();
857
- return;
858
- }
859
- }
860
-
861
- // Get previous focusable element including the popover (simulates native Tab order)
862
- const prevFocusableNative = this.__getPrevScopeFocusable(activeElement, focusables, true);
863
- // Skip the popover when native Shift+Tab would land on it
864
- // and redirect to the actual previous element
865
- if (prevFocusableNative === this) {
866
- if (prevFocusable) {
867
- event.preventDefault();
868
- prevFocusable.focus();
869
- } else if (getActiveTrappingNode(this)) {
870
- // Popover is first in DOM scope but shouldn't be Shift+Tab-reachable
871
- // from non-target elements. Wrap to last non-popover focusable.
872
- const list = focusables.filter((el) => el !== this);
873
- const last = list.at(-1);
874
- if (last) {
875
- event.preventDefault();
876
- last.focus();
877
- }
878
- }
879
- }
880
- }
881
-
882
- /**
883
- * Returns whether the element is a light DOM child of this popover
884
- * (i.e. slotted popover content, excluding the popover element itself).
885
- * @param {Element} el
886
- * @return {boolean}
887
- * @private
888
- */
889
- __isPopoverContent(el) {
890
- return el !== this && this.contains(el);
891
- }
892
-
893
- /**
894
- * Returns focusable elements within the current scope (active focus trap or
895
- * document body) with popover light DOM children filtered out.
896
- * @return {Element[]}
897
- * @private
898
- */
899
- __getScopeFocusables() {
900
- const scope = getActiveTrappingNode(this) || document.body;
901
- return getFocusableElements(scope).filter((el) => !this.__isPopoverContent(el));
902
- }
903
-
904
- /** @private */
905
- __getNextScopeFocusable(target, focusables = this.__getScopeFocusables()) {
906
- const idx = focusables.findIndex((el) => el === target);
907
- return idx >= 0 ? focusables[idx + 1] : undefined;
908
- }
909
-
910
- /** @private */
911
- __getPrevScopeFocusable(target, focusables = this.__getScopeFocusables(), includePopover = false) {
912
- const list = includePopover ? focusables : focusables.filter((el) => el !== this);
913
- const idx = list.findIndex((el) => el === target);
914
- // Returns null both when target is the first element (idx === 0)
915
- // and when target is not found in the list (idx === -1)
916
- return idx > 0 ? list[idx - 1] : null;
917
- }
918
-
919
- /** @private */
920
- __getLastFocusable() {
921
- // Search within the overlay's content area to avoid returning the popover element itself
922
- const focusables = getFocusableElements(this._overlayElement.$.content);
923
- return focusables.pop();
924
- }
925
-
926
- /** @private */
927
- __focusLastOrSelf() {
928
- (this.__getLastFocusable() || this).focus();
929
- }
930
-
931
- /** @private */
932
- __getTargetFocusable() {
933
- if (!this.target) {
934
- return null;
935
- }
936
-
937
- // If target has `focusElement`, check if that one is focused.
938
- return this.target.focusElement || this.target;
939
- }
940
-
941
728
  /** @private */
942
729
  __onTargetFocusIn() {
943
730
  this.__focusInside = true;
@@ -1165,12 +952,6 @@ class Popover extends PopoverPositionMixin(
1165
952
  __hasTrigger(trigger) {
1166
953
  return Array.isArray(this.trigger) && this.trigger.includes(trigger);
1167
954
  }
1168
-
1169
- /**
1170
- * Fired when the popover is closed.
1171
- *
1172
- * @event closed
1173
- */
1174
955
  }
1175
956
 
1176
957
  defineCustomElement(Popover);
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/popover",
4
- "version": "25.2.0-alpha1",
4
+ "version": "25.2.0-alpha10",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
@@ -15,9 +15,7 @@
15
15
  "description": "String used to label the popover to screen reader users.",
16
16
  "value": {
17
17
  "type": [
18
- "string",
19
- "null",
20
- "undefined"
18
+ "string"
21
19
  ]
22
20
  }
23
21
  },
@@ -26,9 +24,7 @@
26
24
  "description": "Id of the element used as label of the popover to screen reader users.",
27
25
  "value": {
28
26
  "type": [
29
- "string",
30
- "null",
31
- "undefined"
27
+ "string"
32
28
  ]
33
29
  }
34
30
  },
@@ -37,9 +33,7 @@
37
33
  "description": "When true, the popover content automatically receives focus after\nit is opened. Modal popovers use this behavior by default.",
38
34
  "value": {
39
35
  "type": [
40
- "boolean",
41
- "null",
42
- "undefined"
36
+ "boolean"
43
37
  ]
44
38
  }
45
39
  },
@@ -48,9 +42,7 @@
48
42
  "description": "The delay in milliseconds before the popover is opened\non focus when the corresponding trigger is used.\n\nWhen not specified, the global default (500ms) is used.",
49
43
  "value": {
50
44
  "type": [
51
- "number",
52
- "null",
53
- "undefined"
45
+ "number"
54
46
  ]
55
47
  }
56
48
  },
@@ -59,9 +51,7 @@
59
51
  "description": "The id of the element to be used as `target` value.\nThe element should be in the DOM by the time when\nthe attribute is set, otherwise a warning is shown.",
60
52
  "value": {
61
53
  "type": [
62
- "string",
63
- "null",
64
- "undefined"
54
+ "string"
65
55
  ]
66
56
  }
67
57
  },
@@ -70,9 +60,7 @@
70
60
  "description": "Set the height of the popover.\nIf a unitless number is provided, pixels are assumed.",
71
61
  "value": {
72
62
  "type": [
73
- "string",
74
- "null",
75
- "undefined"
63
+ "string"
76
64
  ]
77
65
  }
78
66
  },
@@ -81,9 +69,7 @@
81
69
  "description": "The delay in milliseconds before the popover is closed\non losing hover, when the corresponding trigger is used.\nOn blur, the popover is closed immediately.\n\nWhen not specified, the global default (500ms) is used.",
82
70
  "value": {
83
71
  "type": [
84
- "number",
85
- "null",
86
- "undefined"
72
+ "number"
87
73
  ]
88
74
  }
89
75
  },
@@ -92,9 +78,7 @@
92
78
  "description": "The delay in milliseconds before the popover is opened\non hover when the corresponding trigger is used.\n\nWhen not specified, the global default (500ms) is used.",
93
79
  "value": {
94
80
  "type": [
95
- "number",
96
- "null",
97
- "undefined"
81
+ "number"
98
82
  ]
99
83
  }
100
84
  },
@@ -103,9 +87,7 @@
103
87
  "description": "When true, the popover prevents interacting with background elements\nby setting `pointer-events` style on the document body to `none`.\nThis also enables trapping focus inside the popover.",
104
88
  "value": {
105
89
  "type": [
106
- "boolean",
107
- "null",
108
- "undefined"
90
+ "boolean"
109
91
  ]
110
92
  }
111
93
  },
@@ -114,9 +96,7 @@
114
96
  "description": "Set to true to disable closing popover on Escape press.",
115
97
  "value": {
116
98
  "type": [
117
- "boolean",
118
- "null",
119
- "undefined"
99
+ "boolean"
120
100
  ]
121
101
  }
122
102
  },
@@ -125,9 +105,16 @@
125
105
  "description": "Set to true to disable closing popover on outside click.",
126
106
  "value": {
127
107
  "type": [
128
- "boolean",
129
- "null",
130
- "undefined"
108
+ "boolean"
109
+ ]
110
+ }
111
+ },
112
+ {
113
+ "name": "no-tab-focus",
114
+ "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.",
115
+ "value": {
116
+ "type": [
117
+ "boolean"
131
118
  ]
132
119
  }
133
120
  },
@@ -136,9 +123,7 @@
136
123
  "description": "True if the popover is visible and available for interaction.",
137
124
  "value": {
138
125
  "type": [
139
- "boolean",
140
- "null",
141
- "undefined"
126
+ "boolean"
142
127
  ]
143
128
  }
144
129
  },
@@ -147,9 +132,7 @@
147
132
  "description": "The `role` attribute value to be set on the popover.",
148
133
  "value": {
149
134
  "type": [
150
- "string",
151
- "null",
152
- "undefined"
135
+ "string"
153
136
  ]
154
137
  }
155
138
  },
@@ -158,9 +141,7 @@
158
141
  "description": "Position of the overlay with respect to the target.\nSupported values: `top-start`, `top`, `top-end`,\n`bottom-start`, `bottom`, `bottom-end`, `start-top`,\n`start`, `start-bottom`, `end-top`, `end`, `end-bottom`.",
159
142
  "value": {
160
143
  "type": [
161
- "string",
162
- "null",
163
- "undefined"
144
+ "string"
164
145
  ]
165
146
  }
166
147
  },
@@ -169,9 +150,7 @@
169
150
  "description": "The `role` attribute value to be set on the popover.\nWhen not specified, defaults to 'dialog'.",
170
151
  "value": {
171
152
  "type": [
172
- "string",
173
- "null",
174
- "undefined"
153
+ "string"
175
154
  ]
176
155
  }
177
156
  },
@@ -191,9 +170,7 @@
191
170
  "description": "Set the width of the popover.\nIf a unitless number is provided, pixels are assumed.",
192
171
  "value": {
193
172
  "type": [
194
- "string",
195
- "null",
196
- "undefined"
173
+ "string"
197
174
  ]
198
175
  }
199
176
  },
@@ -202,9 +179,7 @@
202
179
  "description": "When true, the popover has a backdrop (modality curtain) on top of the\nunderlying page content, covering the whole viewport.",
203
180
  "value": {
204
181
  "type": [
205
- "boolean",
206
- "null",
207
- "undefined"
182
+ "boolean"
208
183
  ]
209
184
  }
210
185
  }
@@ -216,9 +191,7 @@
216
191
  "description": "String used to label the popover to screen reader users.",
217
192
  "value": {
218
193
  "type": [
219
- "string",
220
- "null",
221
- "undefined"
194
+ "string"
222
195
  ]
223
196
  }
224
197
  },
@@ -227,9 +200,7 @@
227
200
  "description": "Id of the element used as label of the popover to screen reader users.",
228
201
  "value": {
229
202
  "type": [
230
- "string",
231
- "null",
232
- "undefined"
203
+ "string"
233
204
  ]
234
205
  }
235
206
  },
@@ -238,9 +209,7 @@
238
209
  "description": "When true, the popover content automatically receives focus after\nit is opened. Modal popovers use this behavior by default.",
239
210
  "value": {
240
211
  "type": [
241
- "boolean",
242
- "null",
243
- "undefined"
212
+ "boolean"
244
213
  ]
245
214
  }
246
215
  },
@@ -249,9 +218,7 @@
249
218
  "description": "The delay in milliseconds before the popover is opened\non focus when the corresponding trigger is used.\n\nWhen not specified, the global default (500ms) is used.",
250
219
  "value": {
251
220
  "type": [
252
- "number",
253
- "null",
254
- "undefined"
221
+ "number"
255
222
  ]
256
223
  }
257
224
  },
@@ -260,9 +227,7 @@
260
227
  "description": "The id of the element to be used as `target` value.\nThe element should be in the DOM by the time when\nthe attribute is set, otherwise a warning is shown.",
261
228
  "value": {
262
229
  "type": [
263
- "string",
264
- "null",
265
- "undefined"
230
+ "string"
266
231
  ]
267
232
  }
268
233
  },
@@ -271,9 +236,7 @@
271
236
  "description": "Set the height of the popover.\nIf a unitless number is provided, pixels are assumed.",
272
237
  "value": {
273
238
  "type": [
274
- "string",
275
- "null",
276
- "undefined"
239
+ "string"
277
240
  ]
278
241
  }
279
242
  },
@@ -282,9 +245,7 @@
282
245
  "description": "The delay in milliseconds before the popover is closed\non losing hover, when the corresponding trigger is used.\nOn blur, the popover is closed immediately.\n\nWhen not specified, the global default (500ms) is used.",
283
246
  "value": {
284
247
  "type": [
285
- "number",
286
- "null",
287
- "undefined"
248
+ "number"
288
249
  ]
289
250
  }
290
251
  },
@@ -293,9 +254,7 @@
293
254
  "description": "The delay in milliseconds before the popover is opened\non hover when the corresponding trigger is used.\n\nWhen not specified, the global default (500ms) is used.",
294
255
  "value": {
295
256
  "type": [
296
- "number",
297
- "null",
298
- "undefined"
257
+ "number"
299
258
  ]
300
259
  }
301
260
  },
@@ -304,9 +263,7 @@
304
263
  "description": "When true, the popover prevents interacting with background elements\nby setting `pointer-events` style on the document body to `none`.\nThis also enables trapping focus inside the popover.",
305
264
  "value": {
306
265
  "type": [
307
- "boolean",
308
- "null",
309
- "undefined"
266
+ "boolean"
310
267
  ]
311
268
  }
312
269
  },
@@ -315,9 +272,7 @@
315
272
  "description": "Set to true to disable closing popover on Escape press.",
316
273
  "value": {
317
274
  "type": [
318
- "boolean",
319
- "null",
320
- "undefined"
275
+ "boolean"
321
276
  ]
322
277
  }
323
278
  },
@@ -326,9 +281,16 @@
326
281
  "description": "Set to true to disable closing popover on outside click.",
327
282
  "value": {
328
283
  "type": [
329
- "boolean",
330
- "null",
331
- "undefined"
284
+ "boolean"
285
+ ]
286
+ }
287
+ },
288
+ {
289
+ "name": "noTabFocus",
290
+ "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.",
291
+ "value": {
292
+ "type": [
293
+ "boolean"
332
294
  ]
333
295
  }
334
296
  },
@@ -337,9 +299,7 @@
337
299
  "description": "True if the popover is visible and available for interaction.",
338
300
  "value": {
339
301
  "type": [
340
- "boolean",
341
- "null",
342
- "undefined"
302
+ "boolean"
343
303
  ]
344
304
  }
345
305
  },
@@ -348,9 +308,7 @@
348
308
  "description": "The `role` attribute value to be set on the popover.",
349
309
  "value": {
350
310
  "type": [
351
- "string",
352
- "null",
353
- "undefined"
311
+ "string"
354
312
  ]
355
313
  }
356
314
  },
@@ -359,9 +317,7 @@
359
317
  "description": "Position of the overlay with respect to the target.\nSupported values: `top-start`, `top`, `top-end`,\n`bottom-start`, `bottom`, `bottom-end`, `start-top`,\n`start`, `start-bottom`, `end-top`, `end`, `end-bottom`.",
360
318
  "value": {
361
319
  "type": [
362
- "string",
363
- "null",
364
- "undefined"
320
+ "string"
365
321
  ]
366
322
  }
367
323
  },
@@ -370,9 +326,7 @@
370
326
  "description": "Custom function for rendering the content of the popover.\nReceives two arguments:\n\n- `root` The root container DOM element. Append your content to it.\n- `popover` The reference to the `vaadin-popover` element.",
371
327
  "value": {
372
328
  "type": [
373
- "Object",
374
- "null",
375
- "undefined"
329
+ "object"
376
330
  ]
377
331
  }
378
332
  },
@@ -381,9 +335,7 @@
381
335
  "description": "The `role` attribute value to be set on the popover.\nWhen not specified, defaults to 'dialog'.",
382
336
  "value": {
383
337
  "type": [
384
- "string",
385
- "null",
386
- "undefined"
338
+ "string"
387
339
  ]
388
340
  }
389
341
  },
@@ -392,9 +344,7 @@
392
344
  "description": "Reference to the DOM element used both to trigger the overlay\nby user interaction and to visually position it on the screen.\n\nDefaults to an element referenced with `for` attribute, in\nwhich case it must be located in the same shadow scope.",
393
345
  "value": {
394
346
  "type": [
395
- "Object",
396
- "null",
397
- "undefined"
347
+ "object"
398
348
  ]
399
349
  }
400
350
  },
@@ -403,9 +353,7 @@
403
353
  "description": "Popover trigger mode, used to configure how the popover is opened or closed.\nCould be set to multiple by providing an array, e.g. `trigger = ['hover', 'focus']`.\n\nSupported values:\n- `click` (default) - opens and closes on target click.\n- `hover` - opens on target mouseenter, closes on target mouseleave. Moving mouse\nto the popover content keeps the popover opened.\n- `focus` - opens on target focus, closes on target blur. Moving focus to the\npopover content keeps the popover opened.\n\nIn addition to the behavior specified by `trigger`, the popover can be closed by:\n- pressing Escape key (unless `noCloseOnEsc` property is true)\n- outside click (unless `noCloseOnOutsideClick` property is true)\n\nWhen setting `trigger` property to `null`, `undefined` or empty array, the popover\ncan be only opened programmatically by changing `opened` property. Note, closing\non Escape press or outside click is still allowed unless explicitly disabled.",
404
354
  "value": {
405
355
  "type": [
406
- "Array",
407
- "null",
408
- "undefined"
356
+ "array"
409
357
  ]
410
358
  }
411
359
  },
@@ -414,9 +362,7 @@
414
362
  "description": "Set the width of the popover.\nIf a unitless number is provided, pixels are assumed.",
415
363
  "value": {
416
364
  "type": [
417
- "string",
418
- "null",
419
- "undefined"
365
+ "string"
420
366
  ]
421
367
  }
422
368
  },
@@ -425,9 +371,7 @@
425
371
  "description": "When true, the popover has a backdrop (modality curtain) on top of the\nunderlying page content, covering the whole viewport.",
426
372
  "value": {
427
373
  "type": [
428
- "boolean",
429
- "null",
430
- "undefined"
374
+ "boolean"
431
375
  ]
432
376
  }
433
377
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/popover",
4
- "version": "25.2.0-alpha1",
4
+ "version": "25.2.0-alpha10",
5
5
  "description-markup": "markdown",
6
6
  "framework": "lit",
7
7
  "framework-config": {
@@ -20,92 +20,92 @@
20
20
  "extension": true,
21
21
  "attributes": [
22
22
  {
23
- "name": "?autofocus",
24
- "description": "When true, the popover content automatically receives focus after\nit is opened. Modal popovers use this behavior by default.",
23
+ "name": ".accessibleName",
24
+ "description": "String used to label the popover to screen reader users.",
25
25
  "value": {
26
26
  "kind": "expression"
27
27
  }
28
28
  },
29
29
  {
30
- "name": "?modal",
31
- "description": "When true, the popover prevents interacting with background elements\nby setting `pointer-events` style on the document body to `none`.\nThis also enables trapping focus inside the popover.",
30
+ "name": ".accessibleNameRef",
31
+ "description": "Id of the element used as label of the popover to screen reader users.",
32
32
  "value": {
33
33
  "kind": "expression"
34
34
  }
35
35
  },
36
36
  {
37
- "name": "?noCloseOnEsc",
38
- "description": "Set to true to disable closing popover on Escape press.",
37
+ "name": "?autofocus",
38
+ "description": "When true, the popover content automatically receives focus after\nit is opened. Modal popovers use this behavior by default.",
39
39
  "value": {
40
40
  "kind": "expression"
41
41
  }
42
42
  },
43
43
  {
44
- "name": "?noCloseOnOutsideClick",
45
- "description": "Set to true to disable closing popover on outside click.",
44
+ "name": ".focusDelay",
45
+ "description": "The delay in milliseconds before the popover is opened\non focus when the corresponding trigger is used.\n\nWhen not specified, the global default (500ms) is used.",
46
46
  "value": {
47
47
  "kind": "expression"
48
48
  }
49
49
  },
50
50
  {
51
- "name": "?opened",
52
- "description": "True if the popover is visible and available for interaction.",
51
+ "name": ".for",
52
+ "description": "The id of the element to be used as `target` value.\nThe element should be in the DOM by the time when\nthe attribute is set, otherwise a warning is shown.",
53
53
  "value": {
54
54
  "kind": "expression"
55
55
  }
56
56
  },
57
57
  {
58
- "name": "?withBackdrop",
59
- "description": "When true, the popover has a backdrop (modality curtain) on top of the\nunderlying page content, covering the whole viewport.",
58
+ "name": ".height",
59
+ "description": "Set the height of the popover.\nIf a unitless number is provided, pixels are assumed.",
60
60
  "value": {
61
61
  "kind": "expression"
62
62
  }
63
63
  },
64
64
  {
65
- "name": ".accessibleName",
66
- "description": "String used to label the popover to screen reader users.",
65
+ "name": ".hideDelay",
66
+ "description": "The delay in milliseconds before the popover is closed\non losing hover, when the corresponding trigger is used.\nOn blur, the popover is closed immediately.\n\nWhen not specified, the global default (500ms) is used.",
67
67
  "value": {
68
68
  "kind": "expression"
69
69
  }
70
70
  },
71
71
  {
72
- "name": ".accessibleNameRef",
73
- "description": "Id of the element used as label of the popover to screen reader users.",
72
+ "name": ".hoverDelay",
73
+ "description": "The delay in milliseconds before the popover is opened\non hover when the corresponding trigger is used.\n\nWhen not specified, the global default (500ms) is used.",
74
74
  "value": {
75
75
  "kind": "expression"
76
76
  }
77
77
  },
78
78
  {
79
- "name": ".focusDelay",
80
- "description": "The delay in milliseconds before the popover is opened\non focus when the corresponding trigger is used.\n\nWhen not specified, the global default (500ms) is used.",
79
+ "name": "?modal",
80
+ "description": "When true, the popover prevents interacting with background elements\nby setting `pointer-events` style on the document body to `none`.\nThis also enables trapping focus inside the popover.",
81
81
  "value": {
82
82
  "kind": "expression"
83
83
  }
84
84
  },
85
85
  {
86
- "name": ".for",
87
- "description": "The id of the element to be used as `target` value.\nThe element should be in the DOM by the time when\nthe attribute is set, otherwise a warning is shown.",
86
+ "name": "?noCloseOnEsc",
87
+ "description": "Set to true to disable closing popover on Escape press.",
88
88
  "value": {
89
89
  "kind": "expression"
90
90
  }
91
91
  },
92
92
  {
93
- "name": ".height",
94
- "description": "Set the height of the popover.\nIf a unitless number is provided, pixels are assumed.",
93
+ "name": "?noCloseOnOutsideClick",
94
+ "description": "Set to true to disable closing popover on outside click.",
95
95
  "value": {
96
96
  "kind": "expression"
97
97
  }
98
98
  },
99
99
  {
100
- "name": ".hideDelay",
101
- "description": "The delay in milliseconds before the popover is closed\non losing hover, when the corresponding trigger is used.\nOn blur, the popover is closed immediately.\n\nWhen not specified, the global default (500ms) is used.",
100
+ "name": "?noTabFocus",
101
+ "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.",
102
102
  "value": {
103
103
  "kind": "expression"
104
104
  }
105
105
  },
106
106
  {
107
- "name": ".hoverDelay",
108
- "description": "The delay in milliseconds before the popover is opened\non hover when the corresponding trigger is used.\n\nWhen not specified, the global default (500ms) is used.",
107
+ "name": "?opened",
108
+ "description": "True if the popover is visible and available for interaction.",
109
109
  "value": {
110
110
  "kind": "expression"
111
111
  }
@@ -159,6 +159,13 @@
159
159
  "kind": "expression"
160
160
  }
161
161
  },
162
+ {
163
+ "name": "?withBackdrop",
164
+ "description": "When true, the popover has a backdrop (modality curtain) on top of the\nunderlying page content, covering the whole viewport.",
165
+ "value": {
166
+ "kind": "expression"
167
+ }
168
+ },
162
169
  {
163
170
  "name": "@closed",
164
171
  "description": "Fired when the popover is closed.",