@vialiq/web-components 0.17.0 → 0.19.0
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/README.md +392 -118
- package/base/draggable-mixin.d.ts +2 -0
- package/base/draggable-mixin.d.ts.map +1 -1
- package/base/focus-trap-mixin.d.ts.map +1 -1
- package/base/overlay-manager.d.ts +16 -6
- package/base/overlay-manager.d.ts.map +1 -1
- package/base/resizable-mixin.d.ts +28 -0
- package/base/resizable-mixin.d.ts.map +1 -0
- package/index.d.ts +4 -0
- package/index.js +7 -1299
- package/overlay-manager-Ch_6fr_D.js +168 -0
- package/package.json +2 -2
- package/tooltip/vi-tooltip.js +1 -1
- package/overlay-manager-CZSDXl6Z.js +0 -108
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OverlayManagerService
|
|
3
|
+
*
|
|
4
|
+
* A singleton service responsible for managing the z-index stacking context
|
|
5
|
+
* of all floating elements (Modals, Dropdowns, Tooltips, Toasts) across the application.
|
|
6
|
+
*
|
|
7
|
+
* It ensures that newly opened overlays always appear on top of existing ones by
|
|
8
|
+
* maintaining a registry and dynamically calculating the next highest z-index.
|
|
9
|
+
* It also manages global state side-effects, such as locking `document.body` scroll
|
|
10
|
+
* when a modal is active.
|
|
11
|
+
*/ class OverlayManagerService {
|
|
12
|
+
getBaseZIndex(element) {
|
|
13
|
+
if (typeof document !== 'undefined' && typeof getComputedStyle !== 'undefined') {
|
|
14
|
+
// Derive base stacking context from the modal z-index token (minus 10 to start slightly below it)
|
|
15
|
+
// Read directly from the element if provided to support CSS variable scoping/theming.
|
|
16
|
+
const target = element || document.documentElement;
|
|
17
|
+
const cssVar = getComputedStyle(target).getPropertyValue('--vi-modal-z-index').trim();
|
|
18
|
+
const parsed = parseInt(cssVar, 10);
|
|
19
|
+
return !isNaN(parsed) ? parsed - 10 : 1040;
|
|
20
|
+
}
|
|
21
|
+
return 1040;
|
|
22
|
+
}
|
|
23
|
+
overlays = [];
|
|
24
|
+
_previousOverflow = null;
|
|
25
|
+
_previousPaddingRight = null;
|
|
26
|
+
_inertedElements = [];
|
|
27
|
+
/**
|
|
28
|
+
* Registers an element as an active overlay.
|
|
29
|
+
* Calculates and returns the appropriate z-index for this overlay.
|
|
30
|
+
*
|
|
31
|
+
* @param element The DOM element being registered (e.g., the modal dialog or dropdown listbox)
|
|
32
|
+
* @param type The type of overlay, used to determine behaviors like scroll-locking.
|
|
33
|
+
* @param scrollStrategy How this overlay interacts with background scrolling.
|
|
34
|
+
* @returns The calculated z-index to be applied to the element.
|
|
35
|
+
*/ register(element, type = 'dropdown', scrollStrategy, options) {
|
|
36
|
+
this.unregister(element); // Ensure no duplicates
|
|
37
|
+
let highestZIndex = this.getBaseZIndex(element);
|
|
38
|
+
if (this.overlays.length > 0) {
|
|
39
|
+
highestZIndex = Math.max(...this.overlays.map((o)=>o.zIndex), highestZIndex);
|
|
40
|
+
}
|
|
41
|
+
// Increment by 10 to allow room for backdrops (which typically sit at z-index - 1)
|
|
42
|
+
const newZIndex = highestZIndex + 10;
|
|
43
|
+
const finalStrategy = scrollStrategy ?? (type === 'modal' ? 'block' : 'noop');
|
|
44
|
+
this.overlays.push({
|
|
45
|
+
element,
|
|
46
|
+
type,
|
|
47
|
+
zIndex: newZIndex,
|
|
48
|
+
scrollStrategy: finalStrategy,
|
|
49
|
+
noBackdrop: options?.noBackdrop
|
|
50
|
+
});
|
|
51
|
+
this._updateBodyScroll();
|
|
52
|
+
this._syncInertState();
|
|
53
|
+
return newZIndex;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Unregisters an element, removing it from the overlay stack.
|
|
57
|
+
* Should be called when the overlay is closed or disconnected from the DOM.
|
|
58
|
+
*
|
|
59
|
+
* @param element The DOM element to unregister.
|
|
60
|
+
*/ unregister(element) {
|
|
61
|
+
this.overlays = this.overlays.filter((o)=>o.element !== element);
|
|
62
|
+
this._updateBodyScroll();
|
|
63
|
+
this._syncInertState();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Gets the assigned z-index for an element if it is currently registered.
|
|
67
|
+
*
|
|
68
|
+
* @param element The DOM element to query.
|
|
69
|
+
* @returns The z-index number, or null if the element is not registered.
|
|
70
|
+
*/ getZIndex(element) {
|
|
71
|
+
const item = this.overlays.find((o)=>o.element === element);
|
|
72
|
+
return item ? item.zIndex : null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Evaluates whether the provided element is currently the top-most active overlay.
|
|
76
|
+
* Useful for trapping focus or handling global Escape key presses.
|
|
77
|
+
*
|
|
78
|
+
* @param element The DOM element to check.
|
|
79
|
+
* @returns True if the element has the highest z-index in the registry.
|
|
80
|
+
*/ isTopOverlay(element) {
|
|
81
|
+
if (this.overlays.length === 0) return false;
|
|
82
|
+
const topOverlay = this.overlays.reduce((prev, current)=>prev.zIndex > current.zIndex ? prev : current);
|
|
83
|
+
return topOverlay.element === element;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Syncs the `inert` attribute on `document.body` children based on the active overlay stack.
|
|
87
|
+
* Modals with a backdrop trap focus globally, so everything beneath them must be `inert`.
|
|
88
|
+
*/ _syncInertState() {
|
|
89
|
+
if (typeof document === 'undefined') return;
|
|
90
|
+
// Find the topmost modal that requires a backdrop
|
|
91
|
+
const blockingOverlays = this.overlays.filter((o)=>o.type === 'modal' && !o.noBackdrop);
|
|
92
|
+
const topBlocking = blockingOverlays.length > 0 ? blockingOverlays[blockingOverlays.length - 1] : null;
|
|
93
|
+
if (!topBlocking) {
|
|
94
|
+
// If no blocking overlays, clear all inert state
|
|
95
|
+
this._inertedElements.forEach((el)=>{
|
|
96
|
+
el.inert = false;
|
|
97
|
+
});
|
|
98
|
+
this._inertedElements = [];
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Determine which overlays should NOT be inert (the top blocking one and any above it)
|
|
102
|
+
const activeOverlayElements = new Set();
|
|
103
|
+
const topBlockingIndex = this.overlays.indexOf(topBlocking);
|
|
104
|
+
for(let i = topBlockingIndex; i < this.overlays.length; i++){
|
|
105
|
+
activeOverlayElements.add(this.overlays[i].element);
|
|
106
|
+
}
|
|
107
|
+
// Mark children of body
|
|
108
|
+
Array.from(document.body.children).forEach((child)=>{
|
|
109
|
+
const el = child;
|
|
110
|
+
// Never make the active overlays inert
|
|
111
|
+
if (activeOverlayElements.has(el)) {
|
|
112
|
+
if (this._inertedElements.includes(el)) {
|
|
113
|
+
el.inert = false;
|
|
114
|
+
this._inertedElements = this._inertedElements.filter((e)=>e !== el);
|
|
115
|
+
}
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
// If it's not an active overlay, and not already inert, make it inert
|
|
119
|
+
if (!el.inert) {
|
|
120
|
+
el.inert = true;
|
|
121
|
+
this._inertedElements.push(el);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Locks or unlocks the document.body scroll based on the active overlays.
|
|
127
|
+
* Modals (and other overlays with scrollStrategy='block') require the body
|
|
128
|
+
* to be unscrollable. It applies a utility class `vi-scroll-locked` to the body.
|
|
129
|
+
*/ _updateBodyScroll() {
|
|
130
|
+
const hasBlock = this.overlays.some((o)=>o.scrollStrategy === 'block');
|
|
131
|
+
if (hasBlock) {
|
|
132
|
+
// Prevent double-setting if already locked
|
|
133
|
+
if (!document.body.classList.contains('vi-scroll-locked')) {
|
|
134
|
+
// Calculate scrollbar width before removing overflow
|
|
135
|
+
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
136
|
+
document.body.classList.add('vi-scroll-locked');
|
|
137
|
+
this._previousOverflow = document.body.style.getPropertyValue('overflow') || null;
|
|
138
|
+
this._previousPaddingRight = document.body.style.getPropertyValue('padding-right') || null;
|
|
139
|
+
document.body.style.setProperty('overflow', 'hidden', 'important');
|
|
140
|
+
// Apply compensation padding to prevent layout shift
|
|
141
|
+
if (scrollbarWidth > 0) {
|
|
142
|
+
const currentPadding = parseFloat(window.getComputedStyle(document.body).paddingRight || '0');
|
|
143
|
+
document.body.style.setProperty('padding-right', `${currentPadding + scrollbarWidth}px`, 'important');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
if (document.body.classList.contains('vi-scroll-locked')) {
|
|
148
|
+
document.body.classList.remove('vi-scroll-locked');
|
|
149
|
+
if (this._previousOverflow !== null) {
|
|
150
|
+
document.body.style.setProperty('overflow', this._previousOverflow);
|
|
151
|
+
} else {
|
|
152
|
+
document.body.style.removeProperty('overflow');
|
|
153
|
+
}
|
|
154
|
+
if (this._previousPaddingRight !== null) {
|
|
155
|
+
document.body.style.setProperty('padding-right', this._previousPaddingRight);
|
|
156
|
+
} else {
|
|
157
|
+
document.body.style.removeProperty('padding-right');
|
|
158
|
+
}
|
|
159
|
+
this._previousOverflow = null;
|
|
160
|
+
this._previousPaddingRight = null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Export as a singleton
|
|
166
|
+
const OverlayManager = new OverlayManagerService();
|
|
167
|
+
|
|
168
|
+
export { OverlayManager as O };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vialiq/web-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
},
|
|
104
104
|
"peerDependencies": {
|
|
105
105
|
"@floating-ui/dom": ">=1.0.0",
|
|
106
|
-
"@vialiq/flux-ui": "^0.
|
|
106
|
+
"@vialiq/flux-ui": "^0.15.0",
|
|
107
107
|
"@vialiq/icons": ">=0.0.3 <0.1.0",
|
|
108
108
|
"lit": "^3.0.0"
|
|
109
109
|
},
|
package/tooltip/vi-tooltip.js
CHANGED
|
@@ -2,7 +2,7 @@ import { unsafeCSS, css, nothing, html } from 'lit';
|
|
|
2
2
|
import { customElement, property, state, query } from 'lit/decorators.js';
|
|
3
3
|
import { V as ViElement } from '../vi-element-C6GfDPs3.js';
|
|
4
4
|
import { autoUpdate, offset, flip, shift, arrow, computePosition } from '@floating-ui/dom';
|
|
5
|
-
import { O as OverlayManager } from '../overlay-manager-
|
|
5
|
+
import { O as OverlayManager } from '../overlay-manager-Ch_6fr_D.js';
|
|
6
6
|
|
|
7
7
|
const tooltipStyles = "@charset \"UTF-8\";@layer reset,components,utilities;.tooltip-panel{position:fixed;z-index:var(--vi-tooltip-z-index, var(--vi-tooltip-z-index, 1070));pointer-events:none;opacity:0;transform:scale(.95);transition:opacity .15s ease-out,transform .15s ease-out;margin:0;border:none;background:transparent;padding:0;overflow:visible;font-family:var(--vi-font-family-base, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif)}.tooltip-panel[popover]:popover-open{display:block;pointer-events:auto;opacity:1;transform:scale(1)}.tooltip-panel{transition-property:opacity,transform,display,overlay;transition-behavior:allow-discrete}@media(prefers-reduced-motion:reduce){.tooltip-panel{transition:none;transform:none}}.tooltip-content{background-color:var(--vi-tooltip-background, var(--vi-layer-inverse, #111827));color:var(--vi-tooltip-color, var(--vi-text-primary-inverse, #ffffff));font-size:var(--vi-tooltip-font-size, 12px);line-height:1.4;padding:var(--vi-tooltip-padding, 6px 10px);border-radius:var(--vi-tooltip-border-radius, 4px);box-shadow:var(--vi-tooltip-shadow, var(--vi-shadow-md, 0 4px 6px -1px rgba(0, 0, 0, .1)));max-width:var(--vi-tooltip-max-width, 240px);word-wrap:break-word;white-space:normal;position:relative}.tooltip-arrow{position:absolute;width:0;height:0;border-style:solid;border-color:transparent;pointer-events:none}.tooltip-panel[placement^=top] .tooltip-arrow,.tooltip-panel[data-placement^=top] .tooltip-arrow{bottom:calc(-1 * var(--vi-tooltip-arrow-size, 6px));left:50%;transform:translate(-50%);border-width:var(--vi-tooltip-arrow-size, 6px) var(--vi-tooltip-arrow-size, 6px) 0;border-top-color:var(--vi-tooltip-background, var(--vi-layer-inverse, #111827))}.tooltip-panel[placement^=bottom] .tooltip-arrow,.tooltip-panel[data-placement^=bottom] .tooltip-arrow{top:calc(-1 * var(--vi-tooltip-arrow-size, 6px));left:50%;transform:translate(-50%);border-width:0 var(--vi-tooltip-arrow-size, 6px) var(--vi-tooltip-arrow-size, 6px);border-bottom-color:var(--vi-tooltip-background, var(--vi-layer-inverse, #111827))}.tooltip-panel[placement=left] .tooltip-arrow,.tooltip-panel[data-placement=left] .tooltip-arrow{right:calc(-1 * var(--vi-tooltip-arrow-size, 6px));top:50%;transform:translateY(-50%);border-width:var(--vi-tooltip-arrow-size, 6px) 0 var(--vi-tooltip-arrow-size, 6px) var(--vi-tooltip-arrow-size, 6px);border-left-color:var(--vi-tooltip-background, var(--vi-layer-inverse, #111827))}.tooltip-panel[placement=right] .tooltip-arrow,.tooltip-panel[data-placement=right] .tooltip-arrow{left:calc(-1 * var(--vi-tooltip-arrow-size, 6px));top:50%;transform:translateY(-50%);border-width:var(--vi-tooltip-arrow-size, 6px) var(--vi-tooltip-arrow-size, 6px) var(--vi-tooltip-arrow-size, 6px) 0;border-right-color:var(--vi-tooltip-background, var(--vi-layer-inverse, #111827))}.tooltip-panel[placement$=-start] .tooltip-arrow,.tooltip-panel[data-placement$=-start] .tooltip-arrow{left:12px;transform:none}.tooltip-panel[placement$=-end] .tooltip-arrow,.tooltip-panel[data-placement$=-end] .tooltip-arrow{left:auto;right:12px;transform:none}:host{display:inline-block}";
|
|
8
8
|
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OverlayManagerService
|
|
3
|
-
*
|
|
4
|
-
* A singleton service responsible for managing the z-index stacking context
|
|
5
|
-
* of all floating elements (Modals, Dropdowns, Tooltips, Toasts) across the application.
|
|
6
|
-
*
|
|
7
|
-
* It ensures that newly opened overlays always appear on top of existing ones by
|
|
8
|
-
* maintaining a registry and dynamically calculating the next highest z-index.
|
|
9
|
-
* It also manages global state side-effects, such as locking `document.body` scroll
|
|
10
|
-
* when a modal is active.
|
|
11
|
-
*/ class OverlayManagerService {
|
|
12
|
-
_baseZIndexCache;
|
|
13
|
-
get baseZIndex() {
|
|
14
|
-
if (this._baseZIndexCache !== undefined) return this._baseZIndexCache;
|
|
15
|
-
if (typeof document !== 'undefined' && typeof getComputedStyle !== 'undefined') {
|
|
16
|
-
// Derive base stacking context from the modal z-index token (minus 10 to start slightly below it)
|
|
17
|
-
const cssVar = getComputedStyle(document.documentElement).getPropertyValue('--vi-modal-z-index').trim();
|
|
18
|
-
const parsed = parseInt(cssVar, 10);
|
|
19
|
-
this._baseZIndexCache = !isNaN(parsed) ? parsed - 10 : 1040;
|
|
20
|
-
} else {
|
|
21
|
-
this._baseZIndexCache = 1040;
|
|
22
|
-
}
|
|
23
|
-
return this._baseZIndexCache;
|
|
24
|
-
}
|
|
25
|
-
overlays = [];
|
|
26
|
-
_previousOverflow = null;
|
|
27
|
-
/**
|
|
28
|
-
* Registers an element as an active overlay.
|
|
29
|
-
* Calculates and returns the appropriate z-index for this overlay.
|
|
30
|
-
*
|
|
31
|
-
* @param element The DOM element being registered (e.g., the modal dialog or dropdown listbox)
|
|
32
|
-
* @param type The type of overlay, used to determine behaviors like scroll-locking.
|
|
33
|
-
* @returns The calculated z-index to be applied to the element.
|
|
34
|
-
*/ register(element, type = 'dropdown') {
|
|
35
|
-
this.unregister(element); // Ensure no duplicates
|
|
36
|
-
let highestZIndex = this.baseZIndex;
|
|
37
|
-
if (this.overlays.length > 0) {
|
|
38
|
-
highestZIndex = Math.max(...this.overlays.map((o)=>o.zIndex));
|
|
39
|
-
}
|
|
40
|
-
// Increment by 10 to allow room for backdrops (which typically sit at z-index - 1)
|
|
41
|
-
const newZIndex = highestZIndex + 10;
|
|
42
|
-
this.overlays.push({
|
|
43
|
-
element,
|
|
44
|
-
type,
|
|
45
|
-
zIndex: newZIndex
|
|
46
|
-
});
|
|
47
|
-
this._updateBodyScroll();
|
|
48
|
-
return newZIndex;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Unregisters an element, removing it from the overlay stack.
|
|
52
|
-
* Should be called when the overlay is closed or disconnected from the DOM.
|
|
53
|
-
*
|
|
54
|
-
* @param element The DOM element to unregister.
|
|
55
|
-
*/ unregister(element) {
|
|
56
|
-
this.overlays = this.overlays.filter((o)=>o.element !== element);
|
|
57
|
-
this._updateBodyScroll();
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Gets the assigned z-index for an element if it is currently registered.
|
|
61
|
-
*
|
|
62
|
-
* @param element The DOM element to query.
|
|
63
|
-
* @returns The z-index number, or null if the element is not registered.
|
|
64
|
-
*/ getZIndex(element) {
|
|
65
|
-
const item = this.overlays.find((o)=>o.element === element);
|
|
66
|
-
return item ? item.zIndex : null;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Evaluates whether the provided element is currently the top-most active overlay.
|
|
70
|
-
* Useful for trapping focus or handling global Escape key presses.
|
|
71
|
-
*
|
|
72
|
-
* @param element The DOM element to check.
|
|
73
|
-
* @returns True if the element has the highest z-index in the registry.
|
|
74
|
-
*/ isTopOverlay(element) {
|
|
75
|
-
if (this.overlays.length === 0) return false;
|
|
76
|
-
const topOverlay = this.overlays.reduce((prev, current)=>prev.zIndex > current.zIndex ? prev : current);
|
|
77
|
-
return topOverlay.element === element;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Locks or unlocks the document.body scroll based on the presence of modals.
|
|
81
|
-
* Modals require the body to be unscrollable to trap scroll inside the modal.
|
|
82
|
-
* It applies a utility class `vi-scroll-locked` to the body.
|
|
83
|
-
*/ _updateBodyScroll() {
|
|
84
|
-
const hasModal = this.overlays.some((o)=>o.type === 'modal');
|
|
85
|
-
if (hasModal) {
|
|
86
|
-
// Prevent double-setting if already locked
|
|
87
|
-
if (!document.body.classList.contains('vi-scroll-locked')) {
|
|
88
|
-
document.body.classList.add('vi-scroll-locked');
|
|
89
|
-
this._previousOverflow = document.body.style.getPropertyValue('overflow') || null;
|
|
90
|
-
document.body.style.setProperty('overflow', 'hidden', 'important');
|
|
91
|
-
}
|
|
92
|
-
} else {
|
|
93
|
-
if (document.body.classList.contains('vi-scroll-locked')) {
|
|
94
|
-
document.body.classList.remove('vi-scroll-locked');
|
|
95
|
-
if (this._previousOverflow !== null) {
|
|
96
|
-
document.body.style.setProperty('overflow', this._previousOverflow);
|
|
97
|
-
} else {
|
|
98
|
-
document.body.style.removeProperty('overflow');
|
|
99
|
-
}
|
|
100
|
-
this._previousOverflow = null;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
// Export as a singleton
|
|
106
|
-
const OverlayManager = new OverlayManagerService();
|
|
107
|
-
|
|
108
|
-
export { OverlayManager as O };
|