@vaadin/overlay 25.3.0-alpha6 → 25.3.0-alpha8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/overlay",
3
- "version": "25.3.0-alpha6",
3
+ "version": "25.3.0-alpha8",
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-alpha6",
38
- "@vaadin/component-base": "25.3.0-alpha6",
39
- "@vaadin/vaadin-themable-mixin": "25.3.0-alpha6",
37
+ "@vaadin/a11y-base": "25.3.0-alpha8",
38
+ "@vaadin/component-base": "25.3.0-alpha8",
39
+ "@vaadin/vaadin-themable-mixin": "25.3.0-alpha8",
40
40
  "lit": "^3.0.0"
41
41
  },
42
42
  "devDependencies": {
43
- "@vaadin/aura": "25.3.0-alpha6",
44
- "@vaadin/chai-plugins": "25.3.0-alpha6",
45
- "@vaadin/test-runner-commands": "25.3.0-alpha6",
43
+ "@vaadin/aura": "25.3.0-alpha8",
44
+ "@vaadin/chai-plugins": "25.3.0-alpha8",
45
+ "@vaadin/test-runner-commands": "25.3.0-alpha8",
46
46
  "@vaadin/testing-helpers": "^2.0.0",
47
- "@vaadin/vaadin-lumo-styles": "25.3.0-alpha6",
47
+ "@vaadin/vaadin-lumo-styles": "25.3.0-alpha8",
48
48
  "sinon": "^22.0.0"
49
49
  },
50
50
  "customElements": "custom-elements.json",
51
- "gitHead": "92c124fb9cff367bc07e734d8e65707facd0bd43"
51
+ "gitHead": "ccbb4aaffb63c745c6da0426b532d4d05e47af29"
52
52
  }
@@ -40,9 +40,10 @@ export declare class OverlayFocusMixinClass {
40
40
  protected _saveFocus(): void;
41
41
 
42
42
  /**
43
- * Trap focus within the overlay after opening has completed.
43
+ * Sets up focus after the overlay opening has completed: traps focus
44
+ * within the overlay if `focusTrap` is enabled.
44
45
  */
45
- protected _trapFocus(): void;
46
+ protected _initFocus(): void;
46
47
 
47
48
  /**
48
49
  * Returns true if focus is still inside the overlay or on the body element,
@@ -66,11 +66,11 @@ export const OverlayFocusMixin = (superClass) =>
66
66
  }
67
67
 
68
68
  /**
69
- * Override to specify another element used as a focus trap root,
69
+ * Override to specify another element used as a focus root,
70
70
  * e.g. the overlay's owner element, rather than overlay part.
71
71
  * @protected
72
72
  */
73
- get _focusTrapRoot() {
73
+ get _focusRoot() {
74
74
  return this.$.overlay;
75
75
  }
76
76
 
@@ -103,13 +103,14 @@ export const OverlayFocusMixin = (superClass) =>
103
103
  }
104
104
 
105
105
  /**
106
- * Trap focus within the overlay after opening has completed.
106
+ * Sets up focus after the overlay opening has completed: traps focus
107
+ * within the overlay if `focusTrap` is enabled.
107
108
  *
108
109
  * @protected
109
110
  */
110
- _trapFocus() {
111
- if (this.focusTrap && !isElementHidden(this._focusTrapRoot)) {
112
- this.__focusTrapController.trapFocus(this._focusTrapRoot);
111
+ _initFocus() {
112
+ if (this.focusTrap && !isElementHidden(this._focusRoot)) {
113
+ this.__focusTrapController.trapFocus(this._focusRoot);
113
114
  }
114
115
  }
115
116
 
@@ -359,7 +359,7 @@ export const OverlayMixin = (superClass) =>
359
359
 
360
360
  this.__scheduledOpen = requestAnimationFrame(() => {
361
361
  setTimeout(() => {
362
- this._trapFocus();
362
+ this._initFocus();
363
363
 
364
364
  // Dispatch the event on the overlay. Not using composed, as propagating the event through shadow roots
365
365
  // could have side effects when nesting overlays
@@ -574,7 +574,7 @@ export const OverlayMixin = (superClass) =>
574
574
  }
575
575
 
576
576
  // Only close modeless overlay on Esc press when it contains focus
577
- if (!this._shouldAddGlobalListeners() && !event.composedPath().includes(this._focusTrapRoot)) {
577
+ if (!this._shouldAddGlobalListeners() && !event.composedPath().includes(this._focusRoot)) {
578
578
  return;
579
579
  }
580
580
 
@@ -116,6 +116,11 @@ export const PositionMixin = (superClass) =>
116
116
  constructor() {
117
117
  super();
118
118
 
119
+ /**
120
+ * Used for mixin detection because `instanceof` does not work with mixins.
121
+ */
122
+ this._hasOverlayPositionMixin = true;
123
+
119
124
  this.__onScroll = this.__onScroll.bind(this);
120
125
  this._updatePosition = this._updatePosition.bind(this);
121
126
  }
@@ -14,23 +14,27 @@ const attachedInstances = new Set();
14
14
  const getAttachedInstances = () => [...attachedInstances].filter((el) => !el.hasAttribute('closing'));
15
15
 
16
16
  /**
17
- * Returns true if all the instances on top of the overlay are nested overlays.
18
- * @private
17
+ * Returns overlays shown on top of the given overlay.
18
+ * @param {HTMLElement} overlay
19
+ * @return {HTMLElement[]}
20
+ * @protected
19
21
  */
20
- export const hasOnlyNestedOverlays = (overlay) => {
22
+ export const getOverlaysOnTop = (overlay) => {
21
23
  const instances = getAttachedInstances();
22
- const next = instances[instances.indexOf(overlay) + 1];
23
- if (!next) {
24
- return true;
25
- }
26
-
27
- if (!overlay._deepContains(next)) {
28
- return false;
29
- }
30
-
31
- return hasOnlyNestedOverlays(next);
24
+ const index = instances.indexOf(overlay);
25
+ // The overlay is not in the visible stack (closing), so nothing is "on top" of it.
26
+ return index === -1 ? [] : instances.slice(index + 1);
32
27
  };
33
28
 
29
+ /**
30
+ * Returns true if the given overlay is a child of a parent overlay.
31
+ * @param {HTMLElement} parent
32
+ * @param {HTMLElement} overlay
33
+ * @return {boolean}
34
+ * @protected
35
+ */
36
+ export const isNestedOverlay = (parent, overlay) => parent._deepContains(overlay);
37
+
34
38
  /**
35
39
  * Returns true if the overlay is the last one in the opened overlays stack.
36
40
  * @param {HTMLElement} overlay
@@ -69,22 +73,28 @@ export const OverlayStackMixin = (superClass) =>
69
73
  * Brings the overlay as visually the frontmost one.
70
74
  */
71
75
  bringToFront() {
72
- // If the overlay is the last one, or if all other overlays shown above
73
- // are nested overlays (e.g. date-picker inside a dialog), do not call
74
- // `showPopover()` unnecessarily to avoid scroll position being reset.
75
- if (isLastOverlay(this) || hasOnlyNestedOverlays(this)) {
76
+ if (isLastOverlay(this)) {
76
77
  return;
77
78
  }
78
79
 
79
- // Update stacking order of native popover-based overlays
80
- if (this.matches(':popover-open')) {
81
- this.hidePopover();
82
- this.showPopover();
80
+ const overlays = getOverlaysOnTop(this);
81
+ // Nested positioned overlays anchored must stay visually on top.
82
+ const nestedOverlays = overlays.filter((el) => el._hasOverlayPositionMixin && isNestedOverlay(this, el));
83
+
84
+ // If the only overlays on top are nested positioned overlays, this overlay is already
85
+ // effectively frontmost. Skip to avoid resetting scroll position via `showPopover()`.
86
+ if (nestedOverlays.length === overlays.length) {
87
+ return;
83
88
  }
84
89
 
85
- // Update order of attached instances
86
- this._removeAttachedInstance();
87
- this._appendAttachedInstance();
90
+ [this, ...nestedOverlays].forEach((overlay) => {
91
+ if (overlay.matches(':popover-open')) {
92
+ overlay.hidePopover();
93
+ overlay.showPopover();
94
+ }
95
+ overlay._removeAttachedInstance();
96
+ overlay._appendAttachedInstance();
97
+ });
88
98
  }
89
99
 
90
100
  /** @protected */