@vmz/ui 0.3.6 → 0.3.8

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.
Files changed (68) hide show
  1. package/README.md +53 -51
  2. package/contracts/token-requirements.v0.json +58 -1
  3. package/package.json +5 -2
  4. package/presets/web-surface.v0.json +14 -12
  5. package/src/components/Accordion.vmz +127 -94
  6. package/src/components/Alert.vmz +125 -28
  7. package/src/components/AppShell.vmz +125 -57
  8. package/src/components/Autocomplete.vmz +261 -154
  9. package/src/components/Avatar.vmz +20 -12
  10. package/src/components/Badge.vmz +62 -70
  11. package/src/components/Breadcrumb.vmz +86 -72
  12. package/src/components/BulkActions.vmz +56 -51
  13. package/src/components/Button.vmz +237 -211
  14. package/src/components/Callout.vmz +178 -77
  15. package/src/components/Card.vmz +141 -120
  16. package/src/components/Checkbox.vmz +131 -55
  17. package/src/components/CodeBlock.vmz +77 -30
  18. package/src/components/Collapse.vmz +64 -11
  19. package/src/components/ConsoleShell.vmz +405 -251
  20. package/src/components/Content.vmz +15 -1
  21. package/src/components/DataTable.vmz +294 -194
  22. package/src/components/DatePicker.vmz +766 -674
  23. package/src/components/Descriptions.vmz +90 -0
  24. package/src/components/Dialog.vmz +137 -38
  25. package/src/components/Divider.vmz +8 -1
  26. package/src/components/Drawer.vmz +518 -427
  27. package/src/components/Dropdown.vmz +195 -51
  28. package/src/components/Empty.vmz +96 -54
  29. package/src/components/Field.vmz +170 -115
  30. package/src/components/FilterBar.vmz +34 -4
  31. package/src/components/Flex.vmz +3 -1
  32. package/src/components/Footer.vmz +6 -3
  33. package/src/components/Form.vmz +41 -0
  34. package/src/components/FormItem.vmz +78 -79
  35. package/src/components/Grid.vmz +6 -3
  36. package/src/components/Header.vmz +193 -6
  37. package/src/components/Layout.vmz +3 -1
  38. package/src/components/Link.vmz +25 -10
  39. package/src/components/List.vmz +124 -102
  40. package/src/components/Menu.vmz +416 -243
  41. package/src/components/Message.vmz +167 -0
  42. package/src/components/Notification.vmz +132 -37
  43. package/src/components/Pagination.vmz +237 -69
  44. package/src/components/Popconfirm.vmz +287 -0
  45. package/src/components/Popover.vmz +341 -223
  46. package/src/components/Progress.vmz +16 -5
  47. package/src/components/Prose.vmz +180 -81
  48. package/src/components/QueryForm.vmz +31 -9
  49. package/src/components/RadioGroup.vmz +204 -124
  50. package/src/components/Result.vmz +60 -27
  51. package/src/components/Segmented.vmz +86 -51
  52. package/src/components/Select.vmz +620 -533
  53. package/src/components/Sider.vmz +120 -7
  54. package/src/components/Skeleton.vmz +9 -7
  55. package/src/components/Space.vmz +9 -3
  56. package/src/components/Spinner.vmz +5 -4
  57. package/src/components/Steps.vmz +118 -103
  58. package/src/components/Switch.vmz +131 -98
  59. package/src/components/Table.vmz +270 -165
  60. package/src/components/Tabs.vmz +125 -110
  61. package/src/components/Tag.vmz +60 -43
  62. package/src/components/TextArea.vmz +125 -118
  63. package/src/components/Timeline.vmz +111 -76
  64. package/src/components/Toc.vmz +90 -74
  65. package/src/components/Tooltip.vmz +107 -58
  66. package/src/components/Tree.vmz +214 -191
  67. package/src/components/Typography.vmz +23 -8
  68. package/src/components/Upload.vmz +1394 -1289
@@ -1,251 +1,405 @@
1
- <template>
2
- <div data-vmz-ui="console-shell" class="vmz-ui-console-shell">
3
- <aside data-vmz-shell="sidebar" :aria-label="navLabel" class="vmz-ui-console-shell__sidebar">
4
- <a :href="homeHref" class="vmz-ui-console-shell__brand">
5
- {{ brand }}
6
- </a>
7
- <nav class="vmz-ui-console-shell__nav">
8
- <a v-for="item in nav" :key="item.id" :href="item.href" :data-vmz-nav="item.id" :data-active="item.id === activeNavId ? 'true' : null" :aria-current="item.id === activeNavId ? 'page' : null">
9
- {{ item.title }}
10
- </a>
11
- </nav>
12
- </aside>
13
- <div class="vmz-ui-console-shell__body">
14
- <header data-vmz-shell="header" class="vmz-ui-console-shell__header">
15
- <div class="vmz-ui-console-shell__header-copy">
16
- <p v-if="breadcrumbLabel" data-vmz-shell="breadcrumb" class="vmz-ui-console-shell__breadcrumb">
17
- {{ breadcrumbLabel }}
18
- </p>
19
- <h1 class="vmz-ui-console-shell__title">
20
- {{ title }}
21
- </h1>
22
- <p v-if="subtitle" class="vmz-ui-console-shell__subtitle">
23
- {{ subtitle }}
24
- </p>
25
- </div>
26
- <a v-if="primaryActionLabel && primaryActionHref" :href="primaryActionHref" class="vmz-ui-console-shell__action">
27
- {{ primaryActionLabel }}
28
- </a>
29
- </header>
30
- <main data-vmz-shell="main" class="vmz-ui-console-shell__main">
31
- <slot />
32
- </main>
33
- <footer v-if="footer" data-vmz-shell="footer" class="vmz-ui-console-shell__footer">
34
- {{ footer }}
35
- </footer>
36
- </div>
37
- </div>
38
- </template>
39
- <style>
40
- .vmz-ui-console-shell {
41
- display: grid;
42
- grid-template-columns: 13rem minmax(0, 1fr);
43
- grid-template-rows: minmax(100%, 100%);
44
- min-height: 100vh;
45
- min-height: 100dvh;
46
- height: 100dvh;
47
- color: inherit;
48
- background: var(--vmz-surface-page, #f5f6f8);
49
- }
50
-
51
- .vmz-ui-console-shell__sidebar {
52
- display: flex;
53
- flex-direction: column;
54
- gap: 1rem;
55
- padding: 1rem 0.75rem 1.5rem;
56
- border-right: 1px solid var(--vmz-line-subtle, #e5e6eb);
57
- min-height: 100%;
58
- align-self: stretch;
59
- background: #fff;
60
- }
61
-
62
- .vmz-ui-console-shell__brand {
63
- font: inherit;
64
- font-weight: 600;
65
- font-size: 1rem;
66
- letter-spacing: 0;
67
- text-decoration: none;
68
- color: #1f2329;
69
- }
70
-
71
- .vmz-ui-console-shell__brand:focus-visible {
72
- outline: none;
73
- box-shadow: 0 0 0 3px var(--vmz-focus-ring);
74
- }
75
-
76
- .vmz-ui-console-shell__nav {
77
- display: flex;
78
- flex-direction: column;
79
- gap: 0.25rem;
80
- flex: 1;
81
- }
82
-
83
- .vmz-ui-console-shell__nav a {
84
- font: inherit;
85
- font-weight: 400;
86
- font-size: 0.875rem;
87
- line-height: 1.375rem;
88
- text-decoration: none;
89
- color: #51565d;
90
- padding: 0.5rem 0.5rem;
91
- border-radius: 6px;
92
- }
93
-
94
- .vmz-ui-console-shell__nav a[data-active="true"] {
95
- color: #0958d9;
96
- font-weight: 500;
97
- background: #e6f4ff;
98
- }
99
-
100
- .vmz-ui-console-shell__nav a:focus-visible {
101
- outline: none;
102
- box-shadow: 0 0 0 3px var(--vmz-focus-ring);
103
- }
104
-
105
- .vmz-ui-console-shell__body {
106
- display: flex;
107
- flex-direction: column;
108
- min-width: 0;
109
- min-height: 0;
110
- height: 100%;
111
- overflow: auto;
112
- }
113
-
114
- .vmz-ui-console-shell__header {
115
- position: sticky;
116
- top: 0;
117
- z-index: 20;
118
- display: flex;
119
- align-items: center;
120
- justify-content: space-between;
121
- gap: 1rem;
122
- min-height: 3.5rem;
123
- padding: 0.75rem 1.5rem;
124
- border-bottom: 1px solid var(--vmz-line-subtle, #e5e6eb);
125
- background: #fff;
126
- }
127
-
128
- .vmz-ui-console-shell__header-copy {
129
- display: grid;
130
- gap: 0.125rem;
131
- min-width: 0;
132
- }
133
-
134
- .vmz-ui-console-shell__breadcrumb {
135
- margin: 0;
136
- color: #646a73;
137
- font-size: 0.8125rem;
138
- line-height: 1.25rem;
139
- }
140
-
141
- .vmz-ui-console-shell__title {
142
- margin: 0;
143
- font-size: 1.5rem;
144
- line-height: 2rem;
145
- font-weight: 600;
146
- color: #1f2329;
147
- }
148
-
149
- .vmz-ui-console-shell__subtitle {
150
- margin: 0;
151
- color: #51565d;
152
- font-size: 0.875rem;
153
- line-height: 1.375rem;
154
- }
155
-
156
- .vmz-ui-console-shell__action {
157
- display: inline-flex;
158
- align-items: center;
159
- justify-content: center;
160
- min-height: 2rem;
161
- padding: 0.25rem 0.75rem;
162
- border-radius: 6px;
163
- background: #1677ff;
164
- color: #fff;
165
- font-size: 0.875rem;
166
- font-weight: 500;
167
- text-decoration: none;
168
- white-space: nowrap;
169
- }
170
-
171
- .vmz-ui-console-shell__action:hover {
172
- color: #fff;
173
- background: #0958d9;
174
- }
175
-
176
- .vmz-ui-console-shell__action:focus-visible {
177
- outline: none;
178
- box-shadow: 0 0 0 3px var(--vmz-focus-ring);
179
- }
180
-
181
- .vmz-ui-console-shell__main {
182
- flex: 1 1 auto;
183
- padding: 1.5rem;
184
- display: flex;
185
- flex-direction: column;
186
- gap: 1rem;
187
- }
188
-
189
- .vmz-ui-console-shell__footer {
190
- margin-top: auto;
191
- padding: 0.85rem 1.5rem 1.1rem;
192
- border-top: 1px solid var(--vmz-line-subtle, #e5e6eb);
193
- color: var(--vmz-text-steel, #646a73);
194
- font-size: 0.82rem;
195
- background: #fff;
196
- }
197
-
198
- @media (max-width: 40rem) {
199
- .vmz-ui-console-shell {
200
- grid-template-columns: 1fr;
201
- grid-template-rows: auto minmax(0, 1fr);
202
- height: auto;
203
- min-height: 100dvh;
204
- }
205
-
206
- .vmz-ui-console-shell__sidebar {
207
- border-right: 0;
208
- border-bottom: 1px solid var(--vmz-line-subtle, #e5e6eb);
209
- }
210
-
211
- .vmz-ui-console-shell__nav {
212
- flex-direction: row;
213
- flex-wrap: wrap;
214
- flex: 0;
215
- }
216
-
217
- .vmz-ui-console-shell__header {
218
- flex-direction: column;
219
- align-items: flex-start;
220
- padding-inline: 1rem;
221
- }
222
-
223
- .vmz-ui-console-shell__main {
224
- padding: 1rem;
225
- }
226
- }
227
- </style>
228
- <script client>
229
- /**
230
- * VMZ UI — ConsoleShell (Console composition).
231
- * Sidebar + sticky header + main slot + optional footer. Parent supplies nav.
232
- */
233
- export default class ConsoleShell {
234
- public brand: string = "VMZ";
235
- public homeHref: string = "/";
236
- public title: string = "Console";
237
- public subtitle: string = "";
238
- public breadcrumbLabel: string = "";
239
- public primaryActionLabel: string = "";
240
- public primaryActionHref: string = "";
241
- public activeNavId: string = "";
242
- public navLabel: string = "Console";
243
- public footer: string = "";
244
- /** @type {{ id: string, title: string, href: string }[]} */
245
- public nav: {
246
- id: string;
247
- title: string;
248
- href: string;
249
- }[] = [];
250
- }
251
- </script>
1
+ <template>
2
+ <div data-vmz-ui="console-shell" :onKeyDown="onShellKeyDown" class="vmz-ui-console-shell">
3
+ <aside data-vmz-shell="sidebar" :aria-label="navLabel" class="vmz-ui-console-shell__sidebar">
4
+ <div class="vmz-ui-console-shell__sidebar-top">
5
+ <a v-if="brand" :href="homeHref" class="vmz-ui-console-shell__brand">
6
+ {{ brand }}
7
+ </a>
8
+ <button v-if="nav.length" type="button" :aria-label="menuLabel || navLabel" :aria-expanded="menuOpen ? 'true' : 'false'" aria-controls="vmz-console-shell-nav" @click="toggleMenu" class="vmz-ui-console-shell__menu">
9
+ <Icon :name="menuOpen ? 'action.close' : 'action.menu'" size="md" decorative />
10
+ </button>
11
+ </div>
12
+ <nav id="vmz-console-shell-nav" :data-open="menuOpen ? 'true' : null" class="vmz-ui-console-shell__nav">
13
+ <a v-for="item in nav" :key="item.id" :href="item.href" :data-vmz-nav="item.id" :data-active="item.id === activeNavId ? 'true' : null" :aria-current="item.id === activeNavId ? 'page' : null">
14
+ {{ item.title }}
15
+ </a>
16
+ </nav>
17
+ </aside>
18
+ <div class="vmz-ui-console-shell__body">
19
+ <header data-vmz-shell="header" class="vmz-ui-console-shell__header">
20
+ <a v-if="backLabel && backHref" :href="backHref" class="vmz-ui-console-shell__back">
21
+ {{ backLabel }}
22
+ </a>
23
+ <div class="vmz-ui-console-shell__header-copy">
24
+ <p v-if="breadcrumbLabel" data-vmz-shell="breadcrumb" class="vmz-ui-console-shell__breadcrumb">
25
+ {{ breadcrumbLabel }}
26
+ </p>
27
+ <h1 v-if="title" class="vmz-ui-console-shell__title">
28
+ {{ title }}
29
+ </h1>
30
+ <p v-if="subtitle" class="vmz-ui-console-shell__subtitle">
31
+ {{ subtitle }}
32
+ </p>
33
+ </div>
34
+ <a v-if="primaryActionLabel && primaryActionHref" :href="primaryActionHref" class="vmz-ui-console-shell__action">
35
+ {{ primaryActionLabel }}
36
+ </a>
37
+ </header>
38
+ <main data-vmz-shell="main" class="vmz-ui-console-shell__main">
39
+ <div class="vmz-ui-console-shell__panel">
40
+ <slot />
41
+ </div>
42
+ </main>
43
+ <footer v-if="footer" data-vmz-shell="footer" class="vmz-ui-console-shell__footer">
44
+ {{ footer }}
45
+ </footer>
46
+ </div>
47
+ </div>
48
+ </template>
49
+ <style>
50
+ .vmz-ui-console-shell {
51
+ display: grid;
52
+ grid-template-columns: 13rem minmax(0, 1fr);
53
+ grid-template-rows: minmax(100%, 100%);
54
+ min-height: 100vh;
55
+ min-height: 100dvh;
56
+ height: 100dvh;
57
+ color: var(--vmz-text-ink);
58
+ background: var(--vmz-surface-page);
59
+ font-family: var(--vmz-font-sans);
60
+ }
61
+
62
+ .vmz-ui-console-shell__sidebar {
63
+ display: flex;
64
+ flex-direction: column;
65
+ gap: var(--vmz-space-4, 1rem);
66
+ padding: var(--vmz-space-3, 0.75rem);
67
+ border-right: 1px solid var(--vmz-line-subtle);
68
+ min-height: 100%;
69
+ align-self: stretch;
70
+ background: var(--vmz-surface-paper);
71
+ }
72
+
73
+ .vmz-ui-console-shell__sidebar-top {
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: space-between;
77
+ gap: var(--vmz-space-2, 0.5rem);
78
+ }
79
+
80
+ .vmz-ui-console-shell__menu {
81
+ display: none;
82
+ align-items: center;
83
+ justify-content: center;
84
+ flex: none;
85
+ width: var(--vmz-control-height, 2rem);
86
+ min-height: var(--vmz-control-height, 2rem);
87
+ padding: 0;
88
+ border: 1px solid var(--vmz-line-default);
89
+ border-radius: var(--vmz-radius-md, 6px);
90
+ background: var(--vmz-surface-paper);
91
+ color: var(--vmz-text-steel);
92
+ cursor: pointer;
93
+ outline: none;
94
+ }
95
+
96
+ .vmz-ui-console-shell__menu:focus-visible {
97
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring);
98
+ }
99
+
100
+ .vmz-ui-console-shell__brand {
101
+ padding: var(--vmz-space-1, 0.25rem) var(--vmz-space-3, 0.75rem);
102
+ color: var(--vmz-text-ink);
103
+ font-size: 1rem;
104
+ font-weight: 600;
105
+ line-height: 1.5rem;
106
+ letter-spacing: 0;
107
+ text-decoration: none;
108
+ outline: none;
109
+ border-radius: var(--vmz-radius-md, 6px);
110
+ }
111
+
112
+ .vmz-ui-console-shell__brand:hover {
113
+ color: var(--vmz-action-primary-background);
114
+ }
115
+
116
+ .vmz-ui-console-shell__brand:focus-visible {
117
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring);
118
+ }
119
+
120
+ .vmz-ui-console-shell__nav {
121
+ display: flex;
122
+ flex-direction: column;
123
+ gap: var(--vmz-space-1, 0.25rem);
124
+ flex: 1;
125
+ }
126
+
127
+ .vmz-ui-console-shell__nav a {
128
+ display: flex;
129
+ align-items: center;
130
+ min-height: 2.25rem;
131
+ padding: 0 var(--vmz-space-3, 0.75rem);
132
+ border-radius: var(--vmz-radius-md, 6px);
133
+ color: var(--vmz-text-steel);
134
+ font-size: 0.875rem;
135
+ font-weight: 500;
136
+ line-height: 1.25rem;
137
+ text-decoration: none;
138
+ outline: none;
139
+ transition:
140
+ color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
141
+ background-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
142
+ }
143
+
144
+ .vmz-ui-console-shell__nav a:hover {
145
+ color: var(--vmz-text-ink);
146
+ background: var(--vmz-surface-mist);
147
+ }
148
+
149
+ .vmz-ui-console-shell__nav a[data-active="true"] {
150
+ color: var(--vmz-action-primary-background);
151
+ background: var(--vmz-action-primary-soft);
152
+ font-weight: 600;
153
+ box-shadow: inset 2px 0 0 var(--vmz-action-primary-background);
154
+ }
155
+
156
+ .vmz-ui-console-shell__nav a:focus-visible {
157
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring);
158
+ }
159
+
160
+ .vmz-ui-console-shell__body {
161
+ display: flex;
162
+ flex-direction: column;
163
+ min-width: 0;
164
+ min-height: 0;
165
+ height: 100%;
166
+ overflow: auto;
167
+ background: var(--vmz-surface-page);
168
+ }
169
+
170
+ .vmz-ui-console-shell__header {
171
+ position: sticky;
172
+ top: 0;
173
+ z-index: 20;
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: space-between;
177
+ gap: var(--vmz-space-4, 1rem);
178
+ min-height: var(--vmz-layout-header-height-compact, 3.5rem);
179
+ padding: 0 var(--vmz-space-5, 1.5rem);
180
+ border-bottom: 1px solid var(--vmz-line-subtle);
181
+ background: var(--vmz-surface-paper);
182
+ }
183
+
184
+ .vmz-ui-console-shell__back {
185
+ display: none;
186
+ align-items: center;
187
+ flex: none;
188
+ min-height: var(--vmz-control-height, 2rem);
189
+ color: var(--vmz-action-link-foreground);
190
+ font-size: var(--vmz-type-caption);
191
+ line-height: var(--vmz-type-line-height-caption);
192
+ text-decoration: none;
193
+ white-space: nowrap;
194
+ outline: none;
195
+ }
196
+
197
+ .vmz-ui-console-shell__back:hover {
198
+ color: var(--vmz-action-link-hover);
199
+ }
200
+
201
+ .vmz-ui-console-shell__back:focus-visible {
202
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring);
203
+ }
204
+
205
+ .vmz-ui-console-shell__header-copy {
206
+ display: grid;
207
+ gap: 0.125rem;
208
+ min-width: 0;
209
+ }
210
+
211
+ .vmz-ui-console-shell__breadcrumb {
212
+ margin: 0;
213
+ color: var(--vmz-text-tertiary);
214
+ font-size: 0.75rem;
215
+ line-height: 1.125rem;
216
+ }
217
+
218
+ .vmz-ui-console-shell__title {
219
+ margin: 0;
220
+ color: var(--vmz-text-ink);
221
+ font-size: 1.25rem;
222
+ line-height: 1.75rem;
223
+ font-weight: 600;
224
+ }
225
+
226
+ .vmz-ui-console-shell__subtitle {
227
+ margin: 0;
228
+ color: var(--vmz-text-steel);
229
+ font-size: 0.8125rem;
230
+ line-height: 1.25rem;
231
+ }
232
+
233
+ .vmz-ui-console-shell__action {
234
+ display: inline-flex;
235
+ align-items: center;
236
+ justify-content: center;
237
+ flex: none;
238
+ min-height: 2rem;
239
+ padding: 0 var(--vmz-space-3, 0.75rem);
240
+ border-radius: var(--vmz-radius-md, 6px);
241
+ background: var(--vmz-action-primary-background);
242
+ color: var(--vmz-action-primary-foreground);
243
+ font-size: 0.875rem;
244
+ font-weight: 500;
245
+ text-decoration: none;
246
+ white-space: nowrap;
247
+ outline: none;
248
+ transition: background-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
249
+ }
250
+
251
+ .vmz-ui-console-shell__action:hover {
252
+ background: var(--vmz-action-primary-hover);
253
+ }
254
+
255
+ .vmz-ui-console-shell__action:focus-visible {
256
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring);
257
+ }
258
+
259
+ .vmz-ui-console-shell__main {
260
+ flex: 1 1 auto;
261
+ padding: 0;
262
+ display: flex;
263
+ flex-direction: column;
264
+ }
265
+
266
+ /* The shell owns the page axis, not a card boundary. Pages place their own
267
+ workspace panel so header and content share one left edge. */
268
+ .vmz-ui-console-shell__panel {
269
+ flex: 0 0 auto;
270
+ display: flex;
271
+ flex-direction: column;
272
+ gap: var(--vmz-space-4, 1rem);
273
+ padding: var(--vmz-space-5, 1.5rem);
274
+ }
275
+
276
+ .vmz-ui-console-shell__footer {
277
+ margin-top: auto;
278
+ padding: var(--vmz-space-4, 1rem) var(--vmz-space-5, 1.5rem);
279
+ border-top: 1px solid var(--vmz-line-subtle);
280
+ color: var(--vmz-text-tertiary);
281
+ font-size: 0.8125rem;
282
+ background: var(--vmz-surface-paper);
283
+ }
284
+
285
+ @media (max-width: 720px) {
286
+ .vmz-ui-console-shell {
287
+ grid-template-columns: 1fr;
288
+ grid-template-rows: auto minmax(0, 1fr);
289
+ height: auto;
290
+ min-height: 100dvh;
291
+ }
292
+
293
+ .vmz-ui-console-shell__sidebar {
294
+ border-right: 0;
295
+ border-bottom: 1px solid var(--vmz-line-subtle);
296
+ gap: var(--vmz-space-2, 0.5rem);
297
+ padding: var(--vmz-space-2, 0.5rem) var(--vmz-space-4, 1rem);
298
+ }
299
+
300
+ .vmz-ui-console-shell__menu {
301
+ display: inline-flex;
302
+ width: calc(var(--vmz-control-height, 2rem) * 1.5);
303
+ min-height: calc(var(--vmz-control-height, 2rem) * 1.5);
304
+ }
305
+
306
+ .vmz-ui-console-shell__nav {
307
+ display: none;
308
+ flex-direction: column;
309
+ gap: var(--vmz-space-1, 0.25rem);
310
+ flex: none;
311
+ }
312
+
313
+ .vmz-ui-console-shell__nav[data-open="true"] {
314
+ display: flex;
315
+ }
316
+
317
+ .vmz-ui-console-shell__nav a {
318
+ min-height: calc(var(--vmz-control-height, 2rem) * 1.5);
319
+ white-space: normal;
320
+ }
321
+
322
+ .vmz-ui-console-shell__header {
323
+ display: grid;
324
+ grid-template-columns: auto minmax(0, 1fr) auto;
325
+ align-items: center;
326
+ gap: var(--vmz-space-2, 0.5rem);
327
+ min-height: calc(var(--vmz-control-height, 2rem) * 1.5);
328
+ padding: var(--vmz-space-3, 0.75rem) var(--vmz-space-4, 1rem);
329
+ }
330
+
331
+ .vmz-ui-console-shell__back {
332
+ display: inline-flex;
333
+ grid-column: 1;
334
+ min-height: calc(var(--vmz-control-height, 2rem) * 1.5);
335
+ }
336
+
337
+ .vmz-ui-console-shell__header-copy {
338
+ grid-column: 2;
339
+ gap: 0;
340
+ }
341
+
342
+ .vmz-ui-console-shell__breadcrumb,
343
+ .vmz-ui-console-shell__subtitle {
344
+ display: none;
345
+ }
346
+
347
+ .vmz-ui-console-shell__title {
348
+ font-size: var(--vmz-type-subtitle);
349
+ line-height: var(--vmz-type-line-height-title);
350
+ white-space: nowrap;
351
+ overflow: hidden;
352
+ text-overflow: ellipsis;
353
+ }
354
+
355
+ .vmz-ui-console-shell__action {
356
+ grid-column: 3;
357
+ justify-self: end;
358
+ min-height: calc(var(--vmz-control-height, 2rem) * 1.5);
359
+ }
360
+
361
+ .vmz-ui-console-shell__main {
362
+ padding: 0;
363
+ }
364
+
365
+ .vmz-ui-console-shell__panel {
366
+ padding: var(--vmz-space-4, 1rem);
367
+ }
368
+ }
369
+ </style>
370
+ <script client>
371
+ /**
372
+ * VMZ UI — ConsoleShell (Console composition).
373
+ * Sidebar + 56px sticky header + white panel + optional footer. Parent owns nav/copy.
374
+ */
375
+ export default class ConsoleShell {
376
+ public brand: string = "";
377
+ public homeHref: string = "/";
378
+ public title: string = "";
379
+ public subtitle: string = "";
380
+ public breadcrumbLabel: string = "";
381
+ public primaryActionLabel: string = "";
382
+ public primaryActionHref: string = "";
383
+ public backLabel: string = "";
384
+ public backHref: string = "";
385
+ public activeNavId: string = "";
386
+ public navLabel: string = "";
387
+ public menuLabel: string = "";
388
+ public footer: string = "";
389
+ public menuOpen: boolean = false;
390
+ /** @type {{ id: string, title: string, href: string }[]} */
391
+ public nav: {
392
+ id: string;
393
+ title: string;
394
+ href: string;
395
+ }[] = [];
396
+ toggleMenu() {
397
+ this.menuOpen = !this.menuOpen;
398
+ }
399
+ onShellKeyDown(ev) {
400
+ if (ev && ev.key === "Escape" && this.menuOpen) {
401
+ this.menuOpen = false;
402
+ }
403
+ }
404
+ }
405
+ </script>