@vmz/ui 0.1.0 → 0.1.2
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 +8 -0
- package/contracts/token-requirements.v0.json +2 -6
- package/package.json +18 -2
- package/presets/web-surface.v0.json +24 -10
- package/src/components/Accordion.vmz +88 -89
- package/src/components/Alert.vmz +64 -63
- package/src/components/AppShell.vmz +64 -45
- package/src/components/Autocomplete.vmz +152 -151
- package/src/components/Avatar.vmz +16 -0
- package/src/components/Badge.vmz +46 -46
- package/src/components/Breadcrumb.vmz +52 -48
- package/src/components/BulkActions.vmz +37 -37
- package/src/components/Button.vmz +90 -75
- package/src/components/Callout.vmz +61 -61
- package/src/components/Card.vmz +58 -51
- package/src/components/Checkbox.vmz +56 -53
- package/src/components/CodeBlock.vmz +51 -51
- package/src/components/Collapse.vmz +20 -0
- package/src/components/ConsoleShell.vmz +134 -99
- package/src/components/Content.vmz +11 -0
- package/src/components/DataTable.vmz +197 -196
- package/src/components/DatePicker.vmz +613 -536
- package/src/components/Dialog.vmz +365 -384
- package/src/components/Divider.vmz +19 -0
- package/src/components/Drawer.vmz +361 -378
- package/src/components/Dropdown.vmz +22 -0
- package/src/components/Empty.vmz +36 -36
- package/src/components/Field.vmz +69 -40
- package/src/components/FilterBar.vmz +15 -15
- package/src/components/Flex.vmz +12 -0
- package/src/components/Footer.vmz +11 -0
- package/src/components/Form.vmz +56 -58
- package/src/components/FormItem.vmz +59 -59
- package/src/components/Grid.vmz +12 -0
- package/src/components/Header.vmz +11 -0
- package/src/components/Icon.vmz +86 -0
- package/src/components/Layout.vmz +11 -0
- package/src/components/Link.vmz +33 -33
- package/src/components/List.vmz +96 -96
- package/src/components/Menu.vmz +229 -240
- package/src/components/Notification.vmz +62 -62
- package/src/components/Pagination.vmz +63 -65
- package/src/components/Popover.vmz +202 -219
- package/src/components/Progress.vmz +18 -0
- package/src/components/Prose.vmz +61 -61
- package/src/components/QueryForm.vmz +26 -27
- package/src/components/RadioGroup.vmz +116 -113
- package/src/components/Result.vmz +63 -63
- package/src/components/Segmented.vmz +29 -0
- package/src/components/Select.vmz +462 -290
- package/src/components/Sider.vmz +6 -0
- package/src/components/Skeleton.vmz +58 -58
- package/src/components/Space.vmz +15 -0
- package/src/components/Spinner.vmz +29 -29
- package/src/components/Steps.vmz +86 -86
- package/src/components/Switch.vmz +86 -88
- package/src/components/Table.vmz +116 -114
- package/src/components/Tabs.vmz +113 -112
- package/src/components/Tag.vmz +8 -0
- package/src/components/TextArea.vmz +76 -49
- package/src/components/Timeline.vmz +55 -50
- package/src/components/Toc.vmz +64 -60
- package/src/components/Tooltip.vmz +51 -51
- package/src/components/Tree.vmz +184 -190
- package/src/components/Typography.vmz +9 -0
- package/src/components/Upload.vmz +1130 -1151
|
@@ -1,442 +1,423 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
>
|
|
7
|
-
<div
|
|
8
|
-
if={open}
|
|
9
|
-
class="vmz-ui-dialog"
|
|
10
|
-
data-vmz-overlay="dialog"
|
|
11
|
-
data-vmz-overlay-owner="dialog"
|
|
12
|
-
data-vmz-overlay-stack={stackLevel}
|
|
3
|
+
class="vmz-ui-dialog-root"
|
|
4
|
+
data-vmz-ui="dialog"
|
|
5
|
+
data-vmz-overlay-open={open}
|
|
13
6
|
>
|
|
14
|
-
<button
|
|
15
|
-
type="button"
|
|
16
|
-
class="vmz-ui-dialog__backdrop"
|
|
17
|
-
data-vmz-overlay-layer="backdrop"
|
|
18
|
-
aria-label="Dismiss dialog"
|
|
19
|
-
onClick={dismiss}
|
|
20
|
-
></button>
|
|
21
7
|
<div
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
data-vmz-overlay-layer="panel"
|
|
28
|
-
data-vmz-focus="enter"
|
|
29
|
-
data-vmz-motion="overlay-enter"
|
|
30
|
-
data-vmz-motion-token="motion.overlay"
|
|
31
|
-
onKeyDown={onPanelKeyDown}
|
|
8
|
+
if={open}
|
|
9
|
+
class="vmz-ui-dialog"
|
|
10
|
+
data-vmz-overlay="dialog"
|
|
11
|
+
data-vmz-overlay-owner="dialog"
|
|
12
|
+
data-vmz-overlay-stack={stackLevel}
|
|
32
13
|
>
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
14
|
+
<button
|
|
15
|
+
type="button"
|
|
16
|
+
class="vmz-ui-dialog__backdrop"
|
|
17
|
+
data-vmz-overlay-layer="backdrop"
|
|
18
|
+
aria-label="Dismiss dialog"
|
|
19
|
+
onClick={dismiss}
|
|
20
|
+
></button>
|
|
21
|
+
<div
|
|
22
|
+
class="vmz-ui-dialog__panel"
|
|
23
|
+
role="dialog"
|
|
24
|
+
aria-modal="true"
|
|
25
|
+
aria-labelledby={titleId}
|
|
26
|
+
tabindex="-1"
|
|
27
|
+
data-vmz-overlay-layer="panel"
|
|
28
|
+
data-vmz-focus="enter"
|
|
29
|
+
data-vmz-motion="overlay-enter"
|
|
30
|
+
data-vmz-motion-token="motion.overlay"
|
|
31
|
+
onKeyDown={onPanelKeyDown}
|
|
32
|
+
>
|
|
33
|
+
<h2 id={titleId} class="vmz-ui-dialog__title">{title}</h2>
|
|
34
|
+
<div class="vmz-ui-dialog__body">
|
|
35
|
+
<slot />
|
|
36
|
+
</div>
|
|
37
|
+
<div class="vmz-ui-dialog__actions">
|
|
38
|
+
<button type="button" class="vmz-ui-dialog__close" data-vmz-focus="close" onClick={dismiss}>
|
|
39
|
+
Close
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
41
42
|
</div>
|
|
42
43
|
</div>
|
|
43
44
|
</div>
|
|
44
|
-
</div>
|
|
45
45
|
</template>
|
|
46
46
|
|
|
47
47
|
<style>
|
|
48
|
-
.vmz-ui-dialog-root {
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.vmz-ui-dialog {
|
|
53
|
-
position: fixed;
|
|
54
|
-
inset: 0;
|
|
55
|
-
z-index: 40;
|
|
56
|
-
display: grid;
|
|
57
|
-
place-items: center;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.vmz-ui-dialog[data-vmz-overlay-stack='1'] {
|
|
61
|
-
z-index: 50;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.vmz-ui-dialog[data-vmz-overlay-stack='2'] {
|
|
65
|
-
z-index: 60;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.vmz-ui-dialog[data-vmz-overlay-stack='3'] {
|
|
69
|
-
z-index: 70;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.vmz-ui-dialog__backdrop {
|
|
73
|
-
position: absolute;
|
|
74
|
-
inset: 0;
|
|
75
|
-
border: 0;
|
|
76
|
-
padding: 0;
|
|
77
|
-
margin: 0;
|
|
78
|
-
cursor: pointer;
|
|
79
|
-
background: color-mix(in srgb, var(--vmz-action-primary-active) 35%, transparent);
|
|
80
|
-
animation: vmz-ui-overlay-fade-in var(--vmz-motion-overlay-duration) var(--vmz-motion-overlay-easing);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.vmz-ui-dialog__panel {
|
|
84
|
-
position: relative;
|
|
85
|
-
z-index: 1;
|
|
86
|
-
width: min(28rem, calc(100vw - 2rem));
|
|
87
|
-
padding: 1rem 1.1rem 1.1rem;
|
|
88
|
-
border: 1px solid var(--vmz-action-primary-background);
|
|
89
|
-
border-radius: 2px;
|
|
90
|
-
background: var(--vmz-action-primary-foreground);
|
|
91
|
-
color: var(--vmz-action-primary-active);
|
|
92
|
-
outline: none;
|
|
93
|
-
animation: vmz-ui-overlay-panel-in var(--vmz-motion-overlay-duration) var(--vmz-motion-overlay-easing);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.vmz-ui-dialog[data-vmz-motion-adopt='true'] .vmz-ui-dialog__backdrop,
|
|
97
|
-
.vmz-ui-dialog[data-vmz-motion-adopt='true'] .vmz-ui-dialog__panel {
|
|
98
|
-
animation: none;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.vmz-ui-dialog[data-vmz-motion-phase='exit'] .vmz-ui-dialog__backdrop {
|
|
102
|
-
animation: vmz-ui-overlay-fade-out var(--vmz-motion-overlay-duration) var(--vmz-motion-overlay-easing);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
.vmz-ui-dialog[data-vmz-motion-phase='exit'] .vmz-ui-dialog__panel {
|
|
106
|
-
animation: vmz-ui-overlay-panel-out var(--vmz-motion-overlay-duration) var(--vmz-motion-overlay-easing);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.vmz-ui-dialog__panel:focus-visible {
|
|
110
|
-
box-shadow: 0 0 0 2px var(--vmz-focus-ring);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
.vmz-ui-dialog__title {
|
|
114
|
-
margin: 0 0 0.65rem;
|
|
115
|
-
font-size: 1.1rem;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.vmz-ui-dialog__body {
|
|
119
|
-
margin-bottom: 0.9rem;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.vmz-ui-dialog__close {
|
|
123
|
-
font: inherit;
|
|
124
|
-
font-weight: 600;
|
|
125
|
-
padding: 0.45rem 0.8rem;
|
|
126
|
-
border: 1px solid transparent;
|
|
127
|
-
border-radius: 2px;
|
|
128
|
-
cursor: pointer;
|
|
129
|
-
background: var(--vmz-action-primary-background);
|
|
130
|
-
color: var(--vmz-action-primary-foreground);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.vmz-ui-dialog__close:focus-visible {
|
|
134
|
-
box-shadow: 0 0 0 2px var(--vmz-focus-ring);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
@keyframes vmz-ui-overlay-fade-in {
|
|
138
|
-
from {
|
|
139
|
-
opacity: 0;
|
|
140
|
-
}
|
|
141
|
-
to {
|
|
142
|
-
opacity: 1;
|
|
48
|
+
.vmz-ui-dialog-root {
|
|
49
|
+
display: block;
|
|
143
50
|
}
|
|
144
|
-
}
|
|
145
51
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
52
|
+
.vmz-ui-dialog {
|
|
53
|
+
position: fixed;
|
|
54
|
+
inset: 0;
|
|
55
|
+
z-index: 40;
|
|
56
|
+
display: grid;
|
|
57
|
+
place-items: center;
|
|
150
58
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
59
|
+
|
|
60
|
+
.vmz-ui-dialog[data-vmz-overlay-stack='1'] {
|
|
61
|
+
z-index: 50;
|
|
154
62
|
}
|
|
155
|
-
}
|
|
156
63
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
opacity: 1;
|
|
64
|
+
.vmz-ui-dialog[data-vmz-overlay-stack='2'] {
|
|
65
|
+
z-index: 60;
|
|
160
66
|
}
|
|
161
|
-
|
|
162
|
-
|
|
67
|
+
|
|
68
|
+
.vmz-ui-dialog[data-vmz-overlay-stack='3'] {
|
|
69
|
+
z-index: 70;
|
|
163
70
|
}
|
|
164
|
-
}
|
|
165
71
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
72
|
+
.vmz-ui-dialog__backdrop {
|
|
73
|
+
position: absolute;
|
|
74
|
+
inset: 0;
|
|
75
|
+
border: 0;
|
|
76
|
+
padding: 0;
|
|
77
|
+
margin: 0;
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
background: rgba(0, 0, 0, 0.45);
|
|
80
|
+
animation: vmz-ui-overlay-fade-in var(--vmz-motion-overlay-duration) var(--vmz-motion-overlay-easing);
|
|
170
81
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
82
|
+
|
|
83
|
+
.vmz-ui-dialog__panel {
|
|
84
|
+
position: relative;
|
|
85
|
+
z-index: 1;
|
|
86
|
+
width: min(32rem, calc(100vw - 2rem));
|
|
87
|
+
padding: 1.25rem 1.5rem 1.25rem;
|
|
88
|
+
border: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
|
|
89
|
+
border-radius: var(--vmz-radius-lg, 8px);
|
|
90
|
+
background: var(--vmz-surface-paper, #fff);
|
|
91
|
+
color: var(--vmz-text-ink);
|
|
92
|
+
box-shadow: var(--vmz-shadow-popup);
|
|
93
|
+
outline: none;
|
|
94
|
+
animation: vmz-ui-overlay-panel-in var(--vmz-motion-overlay-duration) var(--vmz-motion-overlay-easing);
|
|
174
95
|
}
|
|
175
|
-
}
|
|
176
96
|
|
|
177
|
-
|
|
178
|
-
.vmz-ui-
|
|
179
|
-
.vmz-ui-dialog__panel,
|
|
180
|
-
.vmz-ui-dialog[data-vmz-motion-phase='exit'] .vmz-ui-dialog__backdrop,
|
|
181
|
-
.vmz-ui-dialog[data-vmz-motion-phase='exit'] .vmz-ui-dialog__panel {
|
|
97
|
+
.vmz-ui-dialog[data-vmz-motion-adopt='true'] .vmz-ui-dialog__backdrop,
|
|
98
|
+
.vmz-ui-dialog[data-vmz-motion-adopt='true'] .vmz-ui-dialog__panel {
|
|
182
99
|
animation: none;
|
|
183
100
|
}
|
|
184
|
-
}
|
|
185
|
-
</style>
|
|
186
101
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
* Live `open` + overlay ownership + focus + Escape/outside dismiss + stackLevel.
|
|
191
|
-
* SSR/resume adopt skips enter replay; dismiss plays overlay-exit then calls onClose.
|
|
192
|
-
*/
|
|
193
|
-
export default class Dialog {
|
|
194
|
-
public open: boolean = false;
|
|
195
|
-
public title: string = 'Dialog';
|
|
196
|
-
public titleId: string = 'vmz-dialog-title';
|
|
197
|
-
public stackLevel: string = '0';
|
|
198
|
-
public onClose: (() => void) | null = null;
|
|
102
|
+
.vmz-ui-dialog[data-vmz-motion-phase='exit'] .vmz-ui-dialog__backdrop {
|
|
103
|
+
animation: vmz-ui-overlay-fade-out var(--vmz-motion-overlay-duration) var(--vmz-motion-overlay-easing);
|
|
104
|
+
}
|
|
199
105
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
_observer = null;
|
|
204
|
-
_adoptNextEnter = false;
|
|
205
|
-
_exiting = false;
|
|
206
|
-
_exitTimer = null;
|
|
207
|
-
/** Motion generation — exit timer and cancel edges key off this owner counter. */
|
|
208
|
-
_motionGen = 0;
|
|
106
|
+
.vmz-ui-dialog[data-vmz-motion-phase='exit'] .vmz-ui-dialog__panel {
|
|
107
|
+
animation: vmz-ui-overlay-panel-out var(--vmz-motion-overlay-duration) var(--vmz-motion-overlay-easing);
|
|
108
|
+
}
|
|
209
109
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const root = this.__vmzDomRoot;
|
|
213
|
-
if (!root || typeof root.querySelector !== 'function') return;
|
|
214
|
-
if (root.querySelector('[data-vmz-overlay="dialog"]')) this._adoptNextEnter = true;
|
|
215
|
-
this._observer = new MutationObserver(() => this._syncFromDom());
|
|
216
|
-
this._observer.observe(root, { childList: true, subtree: true });
|
|
217
|
-
this._syncFromDom();
|
|
110
|
+
.vmz-ui-dialog__panel:focus-visible {
|
|
111
|
+
box-shadow: 0 0 0 2px var(--vmz-focus-ring);
|
|
218
112
|
}
|
|
219
113
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if (this._observer) {
|
|
227
|
-
this._observer.disconnect();
|
|
228
|
-
this._observer = null;
|
|
229
|
-
}
|
|
230
|
-
this._teardownDocKey();
|
|
231
|
-
this._panel = null;
|
|
232
|
-
this._restoreFocus = null;
|
|
114
|
+
.vmz-ui-dialog__title {
|
|
115
|
+
margin: 0 0 0.75rem;
|
|
116
|
+
font-size: 1rem;
|
|
117
|
+
font-weight: 600;
|
|
118
|
+
line-height: 1.4;
|
|
119
|
+
color: var(--vmz-text-ink);
|
|
233
120
|
}
|
|
234
121
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (this._exiting) {
|
|
239
|
-
this._cancelExit('reverse');
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
const panel = this._panel;
|
|
243
|
-
const overlay =
|
|
244
|
-
panel && typeof panel.closest === 'function' ? panel.closest('[data-vmz-overlay="dialog"]') : null;
|
|
245
|
-
const reduce =
|
|
246
|
-
typeof window !== 'undefined' &&
|
|
247
|
-
typeof window.matchMedia === 'function' &&
|
|
248
|
-
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
249
|
-
if (panel && overlay && !reduce) {
|
|
250
|
-
this._exiting = true;
|
|
251
|
-
const exitGen = this._motionGen;
|
|
252
|
-
overlay.setAttribute('data-vmz-motion-phase', 'exit');
|
|
253
|
-
overlay.removeAttribute('data-vmz-motion-cancelled');
|
|
254
|
-
panel.setAttribute('data-vmz-motion', 'overlay-exit');
|
|
255
|
-
const ms = this._overlayDurationMs(panel);
|
|
256
|
-
this._exitTimer = setTimeout(() => {
|
|
257
|
-
this._exitTimer = null;
|
|
258
|
-
// Stale exit after interrupt/re-enter must not call onClose.
|
|
259
|
-
if (!this._exiting || exitGen !== this._motionGen) return;
|
|
260
|
-
this._exiting = false;
|
|
261
|
-
if (typeof this.onClose === 'function') this.onClose();
|
|
262
|
-
}, ms);
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
if (typeof this.onClose === 'function') this.onClose();
|
|
122
|
+
.vmz-ui-dialog__body {
|
|
123
|
+
margin-bottom: 1rem;
|
|
124
|
+
color: var(--vmz-text-ink);
|
|
266
125
|
}
|
|
267
126
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
overlay.removeAttribute('data-vmz-motion-phase');
|
|
280
|
-
overlay.setAttribute('data-vmz-motion-cancelled', reason || 'true');
|
|
281
|
-
overlay.setAttribute('data-vmz-motion-gen', String(this._motionGen));
|
|
282
|
-
}
|
|
283
|
-
if (panel) {
|
|
284
|
-
panel.setAttribute('data-vmz-motion', 'overlay-enter');
|
|
285
|
-
}
|
|
127
|
+
.vmz-ui-dialog__close {
|
|
128
|
+
font: inherit;
|
|
129
|
+
font-size: 0.875rem;
|
|
130
|
+
font-weight: 400;
|
|
131
|
+
min-height: var(--vmz-control-height, 2rem);
|
|
132
|
+
padding: 0.25rem 0.95rem;
|
|
133
|
+
border: 1px solid var(--vmz-line-default);
|
|
134
|
+
border-radius: var(--vmz-radius-md, 6px);
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
background: var(--vmz-surface-paper, #fff);
|
|
137
|
+
color: var(--vmz-text-ink);
|
|
286
138
|
}
|
|
287
139
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (raw.endsWith('ms')) return Math.max(0, Number.parseFloat(raw) || 180);
|
|
292
|
-
if (raw.endsWith('s')) return Math.max(0, (Number.parseFloat(raw) || 0.18) * 1000);
|
|
293
|
-
return 180;
|
|
294
|
-
} catch {
|
|
295
|
-
return 180;
|
|
296
|
-
}
|
|
140
|
+
.vmz-ui-dialog__close:hover {
|
|
141
|
+
color: var(--vmz-action-primary-background);
|
|
142
|
+
border-color: var(--vmz-action-primary-background);
|
|
297
143
|
}
|
|
298
144
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const root = this.__vmzDomRoot;
|
|
302
|
-
const mine =
|
|
303
|
-
root && typeof root.querySelector === 'function'
|
|
304
|
-
? root.querySelector('[data-vmz-overlay][data-vmz-overlay-stack]')
|
|
305
|
-
: null;
|
|
306
|
-
if (!mine) return true;
|
|
307
|
-
const myStack = Number(mine.getAttribute('data-vmz-overlay-stack') || this.stackLevel || '0');
|
|
308
|
-
const open = [...document.querySelectorAll('[data-vmz-overlay][data-vmz-overlay-stack]')];
|
|
309
|
-
let max = -Infinity;
|
|
310
|
-
for (const el of open) {
|
|
311
|
-
const n = Number(el.getAttribute('data-vmz-overlay-stack') || '0');
|
|
312
|
-
if (Number.isFinite(n) && n > max) max = n;
|
|
313
|
-
}
|
|
314
|
-
if (!Number.isFinite(max)) return true;
|
|
315
|
-
if ((Number.isFinite(myStack) ? myStack : 0) < max) return false;
|
|
316
|
-
if ((Number.isFinite(myStack) ? myStack : 0) > max) return true;
|
|
317
|
-
const same = open.filter((el) => Number(el.getAttribute('data-vmz-overlay-stack') || '0') === max);
|
|
318
|
-
return same[same.length - 1] === mine;
|
|
145
|
+
.vmz-ui-dialog__close:focus-visible {
|
|
146
|
+
box-shadow: 0 0 0 2px var(--vmz-focus-ring);
|
|
319
147
|
}
|
|
320
148
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (!panel) return;
|
|
325
|
-
const nodes = panel.querySelectorAll(
|
|
326
|
-
'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])',
|
|
327
|
-
);
|
|
328
|
-
const list = [];
|
|
329
|
-
for (const n of nodes) {
|
|
330
|
-
if (n instanceof HTMLElement && n.offsetParent !== null) list.push(n);
|
|
331
|
-
}
|
|
332
|
-
if (panel.tabIndex >= 0) list.unshift(panel);
|
|
333
|
-
if (!list.length) {
|
|
334
|
-
ev.preventDefault();
|
|
335
|
-
panel.focus();
|
|
336
|
-
return;
|
|
149
|
+
@keyframes vmz-ui-overlay-fade-in {
|
|
150
|
+
from {
|
|
151
|
+
opacity: 0;
|
|
337
152
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const active = document.activeElement;
|
|
341
|
-
if (ev.shiftKey) {
|
|
342
|
-
if (active === first || active === panel) {
|
|
343
|
-
ev.preventDefault();
|
|
344
|
-
last.focus();
|
|
345
|
-
}
|
|
346
|
-
} else if (active === last) {
|
|
347
|
-
ev.preventDefault();
|
|
348
|
-
first.focus();
|
|
153
|
+
to {
|
|
154
|
+
opacity: 1;
|
|
349
155
|
}
|
|
350
156
|
}
|
|
351
157
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
} else if (!panel && this._panel) {
|
|
361
|
-
this._exitFocus();
|
|
158
|
+
@keyframes vmz-ui-overlay-panel-in {
|
|
159
|
+
from {
|
|
160
|
+
opacity: 0;
|
|
161
|
+
transform: translateY(0.35rem);
|
|
162
|
+
}
|
|
163
|
+
to {
|
|
164
|
+
opacity: 1;
|
|
165
|
+
transform: none;
|
|
362
166
|
}
|
|
363
167
|
}
|
|
364
168
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
if (!this._restoreFocus) {
|
|
369
|
-
const root = this.__vmzDomRoot;
|
|
370
|
-
const outside =
|
|
371
|
-
active instanceof HTMLElement &&
|
|
372
|
-
active !== document.body &&
|
|
373
|
-
!(root && typeof root.contains === 'function' && root.contains(active));
|
|
374
|
-
if (outside) this._restoreFocus = active;
|
|
375
|
-
}
|
|
376
|
-
this._panel = panel instanceof HTMLElement ? panel : null;
|
|
377
|
-
const overlay =
|
|
378
|
-
this._panel && typeof this._panel.closest === 'function'
|
|
379
|
-
? this._panel.closest('[data-vmz-overlay="dialog"]')
|
|
380
|
-
: null;
|
|
381
|
-
// Owner remount / re-enter cancels any in-flight exit from a prior generation.
|
|
382
|
-
if (this._exiting) {
|
|
383
|
-
this._cancelExit('reenter');
|
|
384
|
-
} else {
|
|
385
|
-
this._motionGen += 1;
|
|
169
|
+
@keyframes vmz-ui-overlay-fade-out {
|
|
170
|
+
from {
|
|
171
|
+
opacity: 1;
|
|
386
172
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
if (this._adoptNextEnter && overlay) {
|
|
391
|
-
overlay.setAttribute('data-vmz-motion-adopt', 'true');
|
|
392
|
-
this._panel.setAttribute('data-vmz-motion', 'overlay-stable');
|
|
393
|
-
this._adoptNextEnter = false;
|
|
394
|
-
} else if (overlay) {
|
|
395
|
-
overlay.removeAttribute('data-vmz-motion-adopt');
|
|
396
|
-
overlay.removeAttribute('data-vmz-motion-phase');
|
|
397
|
-
overlay.removeAttribute('data-vmz-motion-cancelled');
|
|
398
|
-
this._panel.setAttribute('data-vmz-motion', 'overlay-enter');
|
|
399
|
-
}
|
|
400
|
-
if (this._panel && typeof this._panel.focus === 'function') {
|
|
401
|
-
this._panel.focus();
|
|
402
|
-
}
|
|
403
|
-
if (!this._docKeyHandler) {
|
|
404
|
-
this._docKeyHandler = (ev) => {
|
|
405
|
-
if (ev.key !== 'Escape') return;
|
|
406
|
-
if (ev.defaultPrevented) return;
|
|
407
|
-
if (!this._isTopmostStack()) return;
|
|
408
|
-
ev.preventDefault();
|
|
409
|
-
ev.stopPropagation();
|
|
410
|
-
this.dismiss();
|
|
411
|
-
};
|
|
412
|
-
document.addEventListener('keydown', this._docKeyHandler);
|
|
173
|
+
to {
|
|
174
|
+
opacity: 0;
|
|
413
175
|
}
|
|
414
176
|
}
|
|
415
177
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
178
|
+
@keyframes vmz-ui-overlay-panel-out {
|
|
179
|
+
from {
|
|
180
|
+
opacity: 1;
|
|
181
|
+
transform: none;
|
|
420
182
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const restore = this._restoreFocus;
|
|
425
|
-
this._restoreFocus = null;
|
|
426
|
-
const focusTarget =
|
|
427
|
-
restore && typeof restore.focus === 'function' && document.contains(restore)
|
|
428
|
-
? restore
|
|
429
|
-
: document.querySelector('button.vmz-ui-btn');
|
|
430
|
-
if (focusTarget && typeof focusTarget.focus === 'function') {
|
|
431
|
-
focusTarget.focus();
|
|
183
|
+
to {
|
|
184
|
+
opacity: 0;
|
|
185
|
+
transform: translateY(0.35rem);
|
|
432
186
|
}
|
|
433
187
|
}
|
|
434
188
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
189
|
+
@media (prefers-reduced-motion: reduce) {
|
|
190
|
+
.vmz-ui-dialog__backdrop,
|
|
191
|
+
.vmz-ui-dialog__panel,
|
|
192
|
+
.vmz-ui-dialog[data-vmz-motion-phase='exit'] .vmz-ui-dialog__backdrop,
|
|
193
|
+
.vmz-ui-dialog[data-vmz-motion-phase='exit'] .vmz-ui-dialog__panel {
|
|
194
|
+
animation: none;
|
|
438
195
|
}
|
|
439
|
-
this._docKeyHandler = null;
|
|
440
196
|
}
|
|
197
|
+
</style>
|
|
198
|
+
|
|
199
|
+
<script client>
|
|
200
|
+
/**
|
|
201
|
+
* VMZ UI — Dialog (UI1).
|
|
202
|
+
* Live `open` + overlay ownership + focus + Escape/outside dismiss + stackLevel.
|
|
203
|
+
* SSR/resume adopt skips enter replay; dismiss plays overlay-exit then calls onClose.
|
|
204
|
+
*/
|
|
205
|
+
export default class Dialog {
|
|
206
|
+
public open: boolean = false;
|
|
207
|
+
public title: string = "Dialog";
|
|
208
|
+
public titleId: string = "vmz-dialog-title";
|
|
209
|
+
public stackLevel: string = "0";
|
|
210
|
+
public onClose: (() => void) | null = null;
|
|
211
|
+
_restoreFocus = null;
|
|
212
|
+
_panel = null;
|
|
213
|
+
_docKeyHandler = null;
|
|
214
|
+
_observer = null;
|
|
215
|
+
_adoptNextEnter = false;
|
|
216
|
+
_exiting = false;
|
|
217
|
+
_exitTimer = null;
|
|
218
|
+
/** Motion generation — exit timer and cancel edges key off this owner counter. */
|
|
219
|
+
_motionGen = 0;
|
|
220
|
+
async onMount() {
|
|
221
|
+
if (typeof document === "undefined" || typeof MutationObserver === "undefined") return;
|
|
222
|
+
const root = this.__vmzDomRoot;
|
|
223
|
+
if (!root || typeof root.querySelector !== "function") return;
|
|
224
|
+
if (root.querySelector("[data-vmz-overlay=\"dialog\"]")) this._adoptNextEnter = true;
|
|
225
|
+
this._observer = new MutationObserver(() => this._syncFromDom());
|
|
226
|
+
this._observer.observe(root, {
|
|
227
|
+
childList: true,
|
|
228
|
+
subtree: true
|
|
229
|
+
});
|
|
230
|
+
this._syncFromDom();
|
|
231
|
+
}
|
|
232
|
+
onDestroy() {
|
|
233
|
+
if (this._exitTimer) {
|
|
234
|
+
clearTimeout(this._exitTimer);
|
|
235
|
+
this._exitTimer = null;
|
|
236
|
+
}
|
|
237
|
+
this._exiting = false;
|
|
238
|
+
if (this._observer) {
|
|
239
|
+
this._observer.disconnect();
|
|
240
|
+
this._observer = null;
|
|
241
|
+
}
|
|
242
|
+
this._teardownDocKey();
|
|
243
|
+
this._panel = null;
|
|
244
|
+
this._restoreFocus = null;
|
|
245
|
+
}
|
|
246
|
+
dismiss() {
|
|
247
|
+
if (!this._isTopmostStack()) return;
|
|
248
|
+
// Reverse / interrupt: second dismiss during exit cancels close (stay open).
|
|
249
|
+
if (this._exiting) {
|
|
250
|
+
this._cancelExit("reverse");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const panel = this._panel;
|
|
254
|
+
const overlay = panel && typeof panel.closest === "function" ? panel.closest("[data-vmz-overlay=\"dialog\"]") : null;
|
|
255
|
+
const reduce = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
256
|
+
if (panel && overlay && !reduce) {
|
|
257
|
+
this._exiting = true;
|
|
258
|
+
const exitGen = this._motionGen;
|
|
259
|
+
overlay.setAttribute("data-vmz-motion-phase", "exit");
|
|
260
|
+
overlay.removeAttribute("data-vmz-motion-cancelled");
|
|
261
|
+
panel.setAttribute("data-vmz-motion", "overlay-exit");
|
|
262
|
+
const ms = this._overlayDurationMs(panel);
|
|
263
|
+
this._exitTimer = setTimeout(() => {
|
|
264
|
+
this._exitTimer = null;
|
|
265
|
+
// Stale exit after interrupt/re-enter must not call onClose.
|
|
266
|
+
if (!this._exiting || exitGen !== this._motionGen) return;
|
|
267
|
+
this._exiting = false;
|
|
268
|
+
if (typeof this.onClose === "function") this.onClose();
|
|
269
|
+
}, ms);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (typeof this.onClose === "function") this.onClose();
|
|
273
|
+
}
|
|
274
|
+
_cancelExit(reason) {
|
|
275
|
+
if (this._exitTimer) {
|
|
276
|
+
clearTimeout(this._exitTimer);
|
|
277
|
+
this._exitTimer = null;
|
|
278
|
+
}
|
|
279
|
+
this._exiting = false;
|
|
280
|
+
this._motionGen += 1;
|
|
281
|
+
const panel = this._panel;
|
|
282
|
+
const overlay = panel && typeof panel.closest === "function" ? panel.closest("[data-vmz-overlay=\"dialog\"]") : null;
|
|
283
|
+
if (overlay) {
|
|
284
|
+
overlay.removeAttribute("data-vmz-motion-phase");
|
|
285
|
+
overlay.setAttribute("data-vmz-motion-cancelled", reason || "true");
|
|
286
|
+
overlay.setAttribute("data-vmz-motion-gen", String(this._motionGen));
|
|
287
|
+
}
|
|
288
|
+
if (panel) {
|
|
289
|
+
panel.setAttribute("data-vmz-motion", "overlay-enter");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
_overlayDurationMs(panel) {
|
|
293
|
+
try {
|
|
294
|
+
const raw = (getComputedStyle(panel).getPropertyValue("--vmz-motion-overlay-duration") || "").trim() || "180ms";
|
|
295
|
+
if (raw.endsWith("ms")) return Math.max(0, Number.parseFloat(raw) || 180);
|
|
296
|
+
if (raw.endsWith("s")) return Math.max(0, (Number.parseFloat(raw) || .18) * 1e3);
|
|
297
|
+
return 180;
|
|
298
|
+
} catch {
|
|
299
|
+
return 180;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
_isTopmostStack() {
|
|
303
|
+
if (typeof document === "undefined") return true;
|
|
304
|
+
const root = this.__vmzDomRoot;
|
|
305
|
+
const mine = root && typeof root.querySelector === "function" ? root.querySelector("[data-vmz-overlay][data-vmz-overlay-stack]") : null;
|
|
306
|
+
if (!mine) return true;
|
|
307
|
+
const myStack = Number(mine.getAttribute("data-vmz-overlay-stack") || this.stackLevel || "0");
|
|
308
|
+
const open = [...document.querySelectorAll("[data-vmz-overlay][data-vmz-overlay-stack]")];
|
|
309
|
+
let max = -Infinity;
|
|
310
|
+
for (const el of open) {
|
|
311
|
+
const n = Number(el.getAttribute("data-vmz-overlay-stack") || "0");
|
|
312
|
+
if (Number.isFinite(n) && n > max) max = n;
|
|
313
|
+
}
|
|
314
|
+
if (!Number.isFinite(max)) return true;
|
|
315
|
+
if ((Number.isFinite(myStack) ? myStack : 0) < max) return false;
|
|
316
|
+
if ((Number.isFinite(myStack) ? myStack : 0) > max) return true;
|
|
317
|
+
const same = open.filter((el) => Number(el.getAttribute("data-vmz-overlay-stack") || "0") === max);
|
|
318
|
+
return same[same.length - 1] === mine;
|
|
319
|
+
}
|
|
320
|
+
onPanelKeyDown(ev) {
|
|
321
|
+
if (!ev || ev.key !== "Tab") return;
|
|
322
|
+
const panel = this._panel;
|
|
323
|
+
if (!panel) return;
|
|
324
|
+
const nodes = panel.querySelectorAll("a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex=\"-1\"])");
|
|
325
|
+
const list = [];
|
|
326
|
+
for (const n of nodes) {
|
|
327
|
+
if (n instanceof HTMLElement && n.offsetParent !== null) list.push(n);
|
|
328
|
+
}
|
|
329
|
+
if (panel.tabIndex >= 0) list.unshift(panel);
|
|
330
|
+
if (!list.length) {
|
|
331
|
+
ev.preventDefault();
|
|
332
|
+
panel.focus();
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
const first = list[0];
|
|
336
|
+
const last = list[list.length - 1];
|
|
337
|
+
const active = document.activeElement;
|
|
338
|
+
if (ev.shiftKey) {
|
|
339
|
+
if (active === first || active === panel) {
|
|
340
|
+
ev.preventDefault();
|
|
341
|
+
last.focus();
|
|
342
|
+
}
|
|
343
|
+
} else if (active === last) {
|
|
344
|
+
ev.preventDefault();
|
|
345
|
+
first.focus();
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
_syncFromDom() {
|
|
349
|
+
const root = this.__vmzDomRoot;
|
|
350
|
+
const panel = root && typeof root.querySelector === "function" ? root.querySelector("[data-vmz-focus=\"enter\"]") : null;
|
|
351
|
+
if (panel && !this._panel) {
|
|
352
|
+
this._enterFocus(panel);
|
|
353
|
+
} else if (!panel && this._panel) {
|
|
354
|
+
this._exitFocus();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
_enterFocus(panel) {
|
|
358
|
+
if (typeof document === "undefined") return;
|
|
359
|
+
const active = document.activeElement;
|
|
360
|
+
if (!this._restoreFocus) {
|
|
361
|
+
const root = this.__vmzDomRoot;
|
|
362
|
+
const outside = active instanceof HTMLElement && active !== document.body && !(root && typeof root.contains === "function" && root.contains(active));
|
|
363
|
+
if (outside) this._restoreFocus = active;
|
|
364
|
+
}
|
|
365
|
+
this._panel = panel instanceof HTMLElement ? panel : null;
|
|
366
|
+
const overlay = this._panel && typeof this._panel.closest === "function" ? this._panel.closest("[data-vmz-overlay=\"dialog\"]") : null;
|
|
367
|
+
// Owner remount / re-enter cancels any in-flight exit from a prior generation.
|
|
368
|
+
if (this._exiting) {
|
|
369
|
+
this._cancelExit("reenter");
|
|
370
|
+
} else {
|
|
371
|
+
this._motionGen += 1;
|
|
372
|
+
}
|
|
373
|
+
if (overlay) {
|
|
374
|
+
overlay.setAttribute("data-vmz-motion-gen", String(this._motionGen));
|
|
375
|
+
}
|
|
376
|
+
if (this._adoptNextEnter && overlay) {
|
|
377
|
+
overlay.setAttribute("data-vmz-motion-adopt", "true");
|
|
378
|
+
this._panel.setAttribute("data-vmz-motion", "overlay-stable");
|
|
379
|
+
this._adoptNextEnter = false;
|
|
380
|
+
} else if (overlay) {
|
|
381
|
+
overlay.removeAttribute("data-vmz-motion-adopt");
|
|
382
|
+
overlay.removeAttribute("data-vmz-motion-phase");
|
|
383
|
+
overlay.removeAttribute("data-vmz-motion-cancelled");
|
|
384
|
+
this._panel.setAttribute("data-vmz-motion", "overlay-enter");
|
|
385
|
+
}
|
|
386
|
+
if (this._panel && typeof this._panel.focus === "function") {
|
|
387
|
+
this._panel.focus();
|
|
388
|
+
}
|
|
389
|
+
if (!this._docKeyHandler) {
|
|
390
|
+
this._docKeyHandler = (ev) => {
|
|
391
|
+
if (ev.key !== "Escape") return;
|
|
392
|
+
if (ev.defaultPrevented) return;
|
|
393
|
+
if (!this._isTopmostStack()) return;
|
|
394
|
+
ev.preventDefault();
|
|
395
|
+
ev.stopPropagation();
|
|
396
|
+
this.dismiss();
|
|
397
|
+
};
|
|
398
|
+
document.addEventListener("keydown", this._docKeyHandler);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
_exitFocus() {
|
|
402
|
+
if (this._exitTimer) {
|
|
403
|
+
clearTimeout(this._exitTimer);
|
|
404
|
+
this._exitTimer = null;
|
|
405
|
+
}
|
|
406
|
+
this._exiting = false;
|
|
407
|
+
this._teardownDocKey();
|
|
408
|
+
this._panel = null;
|
|
409
|
+
const restore = this._restoreFocus;
|
|
410
|
+
this._restoreFocus = null;
|
|
411
|
+
const focusTarget = restore && typeof restore.focus === "function" && document.contains(restore) ? restore : document.querySelector("button.vmz-ui-btn");
|
|
412
|
+
if (focusTarget && typeof focusTarget.focus === "function") {
|
|
413
|
+
focusTarget.focus();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
_teardownDocKey() {
|
|
417
|
+
if (typeof document !== "undefined" && this._docKeyHandler) {
|
|
418
|
+
document.removeEventListener("keydown", this._docKeyHandler);
|
|
419
|
+
}
|
|
420
|
+
this._docKeyHandler = null;
|
|
421
|
+
}
|
|
441
422
|
}
|
|
442
423
|
</script>
|