@vmz/ui 0.3.6 → 0.3.7

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 (42) hide show
  1. package/README.md +51 -51
  2. package/package.json +2 -2
  3. package/src/components/Accordion.vmz +94 -94
  4. package/src/components/AppShell.vmz +5 -2
  5. package/src/components/Autocomplete.vmz +154 -154
  6. package/src/components/Badge.vmz +70 -70
  7. package/src/components/Breadcrumb.vmz +72 -72
  8. package/src/components/BulkActions.vmz +51 -51
  9. package/src/components/Button.vmz +213 -211
  10. package/src/components/Callout.vmz +77 -77
  11. package/src/components/Card.vmz +120 -120
  12. package/src/components/Checkbox.vmz +55 -55
  13. package/src/components/CodeBlock.vmz +21 -6
  14. package/src/components/ConsoleShell.vmz +251 -251
  15. package/src/components/DataTable.vmz +194 -194
  16. package/src/components/DatePicker.vmz +674 -674
  17. package/src/components/Dialog.vmz +17 -1
  18. package/src/components/Drawer.vmz +427 -427
  19. package/src/components/Dropdown.vmz +51 -5
  20. package/src/components/Empty.vmz +54 -54
  21. package/src/components/Field.vmz +115 -115
  22. package/src/components/FormItem.vmz +79 -79
  23. package/src/components/Link.vmz +5 -1
  24. package/src/components/List.vmz +102 -102
  25. package/src/components/Menu.vmz +243 -243
  26. package/src/components/Pagination.vmz +71 -69
  27. package/src/components/Popover.vmz +223 -223
  28. package/src/components/Prose.vmz +81 -81
  29. package/src/components/RadioGroup.vmz +124 -124
  30. package/src/components/Segmented.vmz +51 -51
  31. package/src/components/Select.vmz +533 -533
  32. package/src/components/Steps.vmz +103 -103
  33. package/src/components/Switch.vmz +98 -98
  34. package/src/components/Table.vmz +165 -165
  35. package/src/components/Tabs.vmz +110 -110
  36. package/src/components/Tag.vmz +43 -43
  37. package/src/components/TextArea.vmz +118 -118
  38. package/src/components/Timeline.vmz +76 -76
  39. package/src/components/Toc.vmz +74 -74
  40. package/src/components/Tooltip.vmz +58 -58
  41. package/src/components/Tree.vmz +191 -191
  42. package/src/components/Upload.vmz +1299 -1289
@@ -5,8 +5,13 @@
5
5
  </summary>
6
6
  <div data-vmz-dropdown-menu="1" role="menu" class="vmz-ui-dropdown__menu">
7
7
  <div v-for="item in items" :key="item.id" class="vmz-ui-dropdown__row">
8
- <div v-if="item.divider" role="separator" class="vmz-ui-dropdown__divider"></div>
9
- <a v-else :href="item.href" role="menuitem" :data-vmz-dropdown-item="item.id" :data-danger="item.danger ? 'true' : 'false'" :data-disabled="item.disabled ? 'true' : 'false'" :aria-disabled="item.disabled ? 'true' : null" :tabindex="item.disabled ? -1 : null" class="vmz-ui-dropdown__item">{{ item.title }}</a>
8
+ <div v-if="item.divider" role="separator" class="vmz-ui-dropdown__divider" />
9
+ <a v-else-if="hasHref(item)" :href="item.href" role="menuitem" :data-vmz-dropdown-item="item.id" :data-danger="item.danger ? 'true' : 'false'" :data-disabled="item.disabled ? 'true' : 'false'" :aria-disabled="item.disabled ? 'true' : null" :tabindex="item.disabled ? -1 : null" class="vmz-ui-dropdown__item">
10
+ {{ item.title }}
11
+ </a>
12
+ <button v-else type="button" role="menuitem" :data-vmz-dropdown-item="item.id" :data-danger="item.danger ? 'true' : 'false'" :data-disabled="item.disabled ? 'true' : 'false'" :aria-disabled="item.disabled ? 'true' : null" :tabindex="item.disabled ? -1 : null" @click="onItemClick" class="vmz-ui-dropdown__item">
13
+ {{ item.title }}
14
+ </button>
10
15
  </div>
11
16
  </div>
12
17
  </details>
@@ -114,11 +119,17 @@
114
119
 
115
120
  .vmz-ui-dropdown__item {
116
121
  display: block;
122
+ width: 100%;
117
123
  padding: 0.5rem 0.75rem;
124
+ border: 0;
118
125
  border-radius: var(--vmz-radius-sm, 4px);
126
+ background: transparent;
119
127
  color: var(--vmz-text-ink);
128
+ cursor: pointer;
129
+ font: inherit;
120
130
  font-size: 0.875rem;
121
131
  line-height: 1.4286;
132
+ text-align: left;
122
133
  text-decoration: none;
123
134
  outline: none;
124
135
  transition:
@@ -158,16 +169,18 @@
158
169
  <script client>
159
170
  /**
160
171
  * VMZ UI — Dropdown (Menu).
161
- * Native `<details>` disclosure: works without JS, menu items are plain links
162
- * unless a host attaches selection handlers.
172
+ * Native `<details>` disclosure: works without JS. An item with a non-empty
173
+ * `href` is a plain link; an item without `href` is an action button that
174
+ * notifies via `onSelect(id)`. Selection handlers never navigate.
163
175
  */
164
176
  export type DropdownItem = {
165
177
  id: string;
166
178
  title: string;
179
+ /** Non-empty → link item (native navigation). Empty/absent → action item. */
167
180
  href?: string;
168
181
  /** Destructive action styling. */
169
182
  danger?: boolean;
170
- /** Non-interactive state. */
183
+ /** Non-interactive state: neither navigates nor selects. */
171
184
  disabled?: boolean;
172
185
  /** Render a separator instead of a row (title/href are ignored). */
173
186
  divider?: boolean;
@@ -176,5 +189,38 @@ export type DropdownItem = {
176
189
  export default class Dropdown {
177
190
  public label: string = "Menu";
178
191
  public items: DropdownItem[] = [];
192
+ /** Action-item selection callback (`:on-select`). Link items bypass it. */
193
+ public onSelect: ((id: string) => void) | null = null;
194
+
195
+ hasHref(item) {
196
+ return typeof item?.href === "string" && item.href.length > 0;
197
+ }
198
+
199
+ onItemClick(ev) {
200
+ const el = ev && ev.currentTarget;
201
+ const id =
202
+ el && typeof el.getAttribute === "function"
203
+ ? el.getAttribute("data-vmz-dropdown-item")
204
+ : null;
205
+ if (!id) return;
206
+ const item = this.items.find((candidate) => candidate.id === id);
207
+ if (!item || item.disabled || this.hasHref(item)) return;
208
+ this.close();
209
+ if (typeof this.onSelect === "function") this.onSelect(id);
210
+ }
211
+
212
+ close() {
213
+ const root = this.__vmzDomRoot;
214
+ if (!root) return;
215
+ const details =
216
+ root.tagName === "DETAILS"
217
+ ? root
218
+ : typeof root.closest === "function"
219
+ ? root.closest("details")
220
+ : null;
221
+ if (details && typeof details.removeAttribute === "function") {
222
+ details.removeAttribute("open");
223
+ }
224
+ }
179
225
  }
180
226
  </script>
@@ -1,54 +1,54 @@
1
- <template>
2
- <div data-vmz-ui="empty" class="vmz-ui-empty">
3
- <h2 class="vmz-ui-empty__title">
4
- {{ title }}
5
- </h2>
6
- <p v-if="description" class="vmz-ui-empty__description">
7
- {{ description }}
8
- </p>
9
- <div class="vmz-ui-empty__actions">
10
- <slot />
11
- </div>
12
- </div>
13
- </template>
14
- <style>
15
- .vmz-ui-empty {
16
- display: flex;
17
- flex-direction: column;
18
- align-items: flex-start;
19
- gap: 0.625rem;
20
- padding: 1.5rem 1.15rem;
21
- border: 1px dashed var(--vmz-line-default);
22
- border-radius: var(--vmz-radius-md, 6px);
23
- background: color-mix(in srgb, var(--vmz-surface-mist) 70%, var(--vmz-surface-paper));
24
- color: var(--vmz-text-ink);
25
- }
26
-
27
- .vmz-ui-empty__title {
28
- margin: 0;
29
- font-family: var(--vmz-font-display);
30
- font-size: 1.1rem;
31
- font-weight: 700;
32
- }
33
-
34
- .vmz-ui-empty__description {
35
- margin: 0;
36
- color: var(--vmz-text-steel);
37
- max-width: 36rem;
38
- line-height: 1.45;
39
- }
40
-
41
- .vmz-ui-empty__actions:empty {
42
- display: none;
43
- }
44
- </style>
45
- <script client>
46
- /**
47
- * VMZ UI — Empty (Commercial composition).
48
- * Empty / zero-state surface with optional action slot.
49
- */
50
- export default class Empty {
51
- public title: string = "Nothing here yet";
52
- public description: string = "";
53
- }
54
- </script>
1
+ <template>
2
+ <div data-vmz-ui="empty" class="vmz-ui-empty">
3
+ <h2 class="vmz-ui-empty__title">
4
+ {{ title }}
5
+ </h2>
6
+ <p v-if="description" class="vmz-ui-empty__description">
7
+ {{ description }}
8
+ </p>
9
+ <div class="vmz-ui-empty__actions">
10
+ <slot />
11
+ </div>
12
+ </div>
13
+ </template>
14
+ <style>
15
+ .vmz-ui-empty {
16
+ display: flex;
17
+ flex-direction: column;
18
+ align-items: flex-start;
19
+ gap: 0.625rem;
20
+ padding: 1.5rem 1.15rem;
21
+ border: 1px dashed var(--vmz-line-default);
22
+ border-radius: var(--vmz-radius-md, 6px);
23
+ background: color-mix(in srgb, var(--vmz-surface-mist) 70%, var(--vmz-surface-paper));
24
+ color: var(--vmz-text-ink);
25
+ }
26
+
27
+ .vmz-ui-empty__title {
28
+ margin: 0;
29
+ font-family: var(--vmz-font-display);
30
+ font-size: 1.1rem;
31
+ font-weight: 700;
32
+ }
33
+
34
+ .vmz-ui-empty__description {
35
+ margin: 0;
36
+ color: var(--vmz-text-steel);
37
+ max-width: 36rem;
38
+ line-height: 1.45;
39
+ }
40
+
41
+ .vmz-ui-empty__actions:empty {
42
+ display: none;
43
+ }
44
+ </style>
45
+ <script client>
46
+ /**
47
+ * VMZ UI — Empty (Commercial composition).
48
+ * Empty / zero-state surface with optional action slot.
49
+ */
50
+ export default class Empty {
51
+ public title: string = "Nothing here yet";
52
+ public description: string = "";
53
+ }
54
+ </script>
@@ -1,115 +1,115 @@
1
- <template>
2
- <div data-vmz-ui="field" :data-invalid="invalidAttr" class="vmz-ui-field">
3
- <label v-if="label" :for="controlId" class="vmz-ui-field__label">
4
- {{ label }}
5
- </label>
6
- <input :id="controlId" :type="type" :value="value" :min="min" :max="max" :step="step" :disabled="disabled" :aria-invalid="error ? 'true' : 'false'" :aria-describedby="describedBy" @input="onDomInput" class="vmz-ui-field__control" />
7
- <p v-if="description" :id="descriptionId" class="vmz-ui-field__description">
8
- {{ description }}
9
- </p>
10
- <p v-if="error" :id="errorId" role="alert" class="vmz-ui-field__error">
11
- {{ error }}
12
- </p>
13
- </div>
14
- </template>
15
- <style>
16
- .vmz-ui-field {
17
- display: flex;
18
- flex-direction: column;
19
- gap: 0.375rem;
20
- max-width: 28rem;
21
- }
22
-
23
- .vmz-ui-field__label {
24
- font: inherit;
25
- font-weight: 500;
26
- font-size: 0.875rem;
27
- color: var(--vmz-text-ink, inherit);
28
- }
29
-
30
- .vmz-ui-field__control {
31
- box-sizing: border-box;
32
- width: 100%;
33
- min-height: var(--vmz-control-height, 2rem);
34
- font-family: var(--vmz-font-sans, inherit);
35
- font-size: 0.875rem;
36
- line-height: 1.5715;
37
- padding: var(--vmz-density-control-padding-y, 0.25rem)
38
- var(--vmz-density-control-padding-x, 0.75rem);
39
- border: 1px solid var(--vmz-line-default, #d9d9d9);
40
- border-radius: var(--vmz-radius-md, 6px);
41
- background: var(--vmz-surface-paper, #fff);
42
- color: var(--vmz-text-ink, inherit);
43
- outline: none;
44
- transition:
45
- border-color var(--vmz-motion-control-duration, 120ms)
46
- var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
47
- box-shadow var(--vmz-motion-control-duration, 120ms)
48
- var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
49
- }
50
-
51
- .vmz-ui-field__control::placeholder {
52
- color: var(--vmz-text-steel, #8c8c8c);
53
- opacity: 1;
54
- }
55
-
56
- .vmz-ui-field[data-invalid="true"] .vmz-ui-field__control {
57
- border-color: var(--vmz-status-danger-accent);
58
- }
59
-
60
- .vmz-ui-field__control:hover:not(:disabled):not(:focus-visible) {
61
- border-color: var(--vmz-action-primary-background);
62
- }
63
-
64
- .vmz-ui-field__control:focus-visible {
65
- border-color: var(--vmz-action-primary-background);
66
- box-shadow: 0 0 0 3px var(--vmz-focus-ring);
67
- }
68
-
69
- .vmz-ui-field__control:disabled {
70
- opacity: 0.55;
71
- cursor: not-allowed;
72
- background: var(--vmz-surface-mist, #fafafa);
73
- }
74
-
75
- .vmz-ui-field__description {
76
- margin: 0;
77
- color: var(--vmz-text-steel, #8c8c8c);
78
- font-size: 0.875rem;
79
- line-height: 1.4;
80
- }
81
-
82
- .vmz-ui-field__error {
83
- margin: 0;
84
- color: var(--vmz-status-danger-foreground);
85
- font-size: 0.875rem;
86
- }
87
- </style>
88
- <script client>
89
- /**
90
- * VMZ UI — Field (UI1).
91
- * Label / description / error association via for + aria-describedby.
92
- * Colors only from Style Theme semantic tokens.
93
- */
94
- export default class Field {
95
- public label: string = "";
96
- public description: string = "";
97
- public error: string = "";
98
- public value: string = "";
99
- public type: string = "text";
100
- public min: string = "";
101
- public max: string = "";
102
- public step: string = "";
103
- public disabled: boolean = false;
104
- public invalidAttr: string = "false";
105
- public controlId: string = "vmz-field";
106
- public descriptionId: string = "vmz-field-desc";
107
- public errorId: string = "vmz-field-err";
108
- public describedBy: string = "";
109
- public onInput: ((e: Event) => void) | null = null;
110
- onDomInput(e: Event) {
111
- this.emit("input", e);
112
- if (typeof this.onInput === "function") this.onInput(e);
113
- }
114
- }
115
- </script>
1
+ <template>
2
+ <div data-vmz-ui="field" :data-invalid="invalidAttr" class="vmz-ui-field">
3
+ <label v-if="label" :for="controlId" class="vmz-ui-field__label">
4
+ {{ label }}
5
+ </label>
6
+ <input :id="controlId" :type="type" :value="value" :min="min" :max="max" :step="step" :disabled="disabled" :aria-invalid="error ? 'true' : 'false'" :aria-describedby="describedBy" @input="onDomInput" class="vmz-ui-field__control" />
7
+ <p v-if="description" :id="descriptionId" class="vmz-ui-field__description">
8
+ {{ description }}
9
+ </p>
10
+ <p v-if="error" :id="errorId" role="alert" class="vmz-ui-field__error">
11
+ {{ error }}
12
+ </p>
13
+ </div>
14
+ </template>
15
+ <style>
16
+ .vmz-ui-field {
17
+ display: flex;
18
+ flex-direction: column;
19
+ gap: 0.375rem;
20
+ max-width: 28rem;
21
+ }
22
+
23
+ .vmz-ui-field__label {
24
+ font: inherit;
25
+ font-weight: 500;
26
+ font-size: 0.875rem;
27
+ color: var(--vmz-text-ink, inherit);
28
+ }
29
+
30
+ .vmz-ui-field__control {
31
+ box-sizing: border-box;
32
+ width: 100%;
33
+ min-height: var(--vmz-control-height, 2rem);
34
+ font-family: var(--vmz-font-sans, inherit);
35
+ font-size: 0.875rem;
36
+ line-height: 1.5715;
37
+ padding: var(--vmz-density-control-padding-y, 0.25rem)
38
+ var(--vmz-density-control-padding-x, 0.75rem);
39
+ border: 1px solid var(--vmz-line-default, #d9d9d9);
40
+ border-radius: var(--vmz-radius-md, 6px);
41
+ background: var(--vmz-surface-paper, #fff);
42
+ color: var(--vmz-text-ink, inherit);
43
+ outline: none;
44
+ transition:
45
+ border-color var(--vmz-motion-control-duration, 120ms)
46
+ var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
47
+ box-shadow var(--vmz-motion-control-duration, 120ms)
48
+ var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
49
+ }
50
+
51
+ .vmz-ui-field__control::placeholder {
52
+ color: var(--vmz-text-steel, #8c8c8c);
53
+ opacity: 1;
54
+ }
55
+
56
+ .vmz-ui-field[data-invalid="true"] .vmz-ui-field__control {
57
+ border-color: var(--vmz-status-danger-accent);
58
+ }
59
+
60
+ .vmz-ui-field__control:hover:not(:disabled):not(:focus-visible) {
61
+ border-color: var(--vmz-action-primary-background);
62
+ }
63
+
64
+ .vmz-ui-field__control:focus-visible {
65
+ border-color: var(--vmz-action-primary-background);
66
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring);
67
+ }
68
+
69
+ .vmz-ui-field__control:disabled {
70
+ opacity: 0.55;
71
+ cursor: not-allowed;
72
+ background: var(--vmz-surface-mist, #fafafa);
73
+ }
74
+
75
+ .vmz-ui-field__description {
76
+ margin: 0;
77
+ color: var(--vmz-text-steel, #8c8c8c);
78
+ font-size: 0.875rem;
79
+ line-height: 1.4;
80
+ }
81
+
82
+ .vmz-ui-field__error {
83
+ margin: 0;
84
+ color: var(--vmz-status-danger-foreground);
85
+ font-size: 0.875rem;
86
+ }
87
+ </style>
88
+ <script client>
89
+ /**
90
+ * VMZ UI — Field (UI1).
91
+ * Label / description / error association via for + aria-describedby.
92
+ * Colors only from Style Theme semantic tokens.
93
+ */
94
+ export default class Field {
95
+ public label: string = "";
96
+ public description: string = "";
97
+ public error: string = "";
98
+ public value: string = "";
99
+ public type: string = "text";
100
+ public min: string = "";
101
+ public max: string = "";
102
+ public step: string = "";
103
+ public disabled: boolean = false;
104
+ public invalidAttr: string = "false";
105
+ public controlId: string = "vmz-field";
106
+ public descriptionId: string = "vmz-field-desc";
107
+ public errorId: string = "vmz-field-err";
108
+ public describedBy: string = "";
109
+ public onInput: ((e: Event) => void) | null = null;
110
+ onDomInput(e: Event) {
111
+ this.emit("input", e);
112
+ if (typeof this.onInput === "function") this.onInput(e);
113
+ }
114
+ }
115
+ </script>
@@ -1,79 +1,79 @@
1
- <template>
2
- <div data-vmz-ui="form-item" :data-required="requiredAttr" :data-invalid="invalidAttr" class="vmz-ui-form-item">
3
- <label v-if="label" :for="controlId" class="vmz-ui-form-item__label">
4
- <span>
5
- {{ label }}
6
- </span>
7
- <span v-if="required" aria-hidden="true" class="vmz-ui-form-item__required">
8
- *
9
- </span>
10
- </label>
11
- <div class="vmz-ui-form-item__control">
12
- <slot />
13
- </div>
14
- <p v-if="description" :id="descriptionId" class="vmz-ui-form-item__description">
15
- {{ description }}
16
- </p>
17
- <p v-if="error" :id="errorId" role="alert" class="vmz-ui-form-item__error">
18
- {{ error }}
19
- </p>
20
- </div>
21
- </template>
22
- <style>
23
- .vmz-ui-form-item {
24
- display: flex;
25
- flex-direction: column;
26
- gap: 0.375rem;
27
- max-width: 28rem;
28
- }
29
-
30
- .vmz-ui-form-item__label {
31
- display: inline-flex;
32
- align-items: baseline;
33
- gap: 0.25rem;
34
- font: inherit;
35
- font-weight: 600;
36
- }
37
-
38
- .vmz-ui-form-item__required {
39
- color: var(--vmz-status-danger-accent);
40
- font-weight: 700;
41
- }
42
-
43
- .vmz-ui-form-item__control {
44
- min-width: 0;
45
- }
46
-
47
- .vmz-ui-form-item__description {
48
- margin: 0;
49
- opacity: 0.75;
50
- font-size: 0.9em;
51
- }
52
-
53
- .vmz-ui-form-item__error {
54
- margin: 0;
55
- color: var(--vmz-status-danger-foreground);
56
- font-size: 0.9em;
57
- }
58
-
59
- .vmz-ui-form-item[data-invalid="true"] .vmz-ui-form-item__control :where(input, select, textarea) {
60
- border-color: var(--vmz-status-danger-accent);
61
- }
62
- </style>
63
- <script client>
64
- /**
65
- * VMZ UI — FormItem.
66
- * Label / description / error shell for slotted controls; parent owns validation strings.
67
- */
68
- export default class FormItem {
69
- public label: string = "";
70
- public description: string = "";
71
- public error: string = "";
72
- public required: boolean = false;
73
- public requiredAttr: string = "false";
74
- public invalidAttr: string = "false";
75
- public controlId: string = "vmz-form-item";
76
- public descriptionId: string = "vmz-form-item-desc";
77
- public errorId: string = "vmz-form-item-err";
78
- }
79
- </script>
1
+ <template>
2
+ <div data-vmz-ui="form-item" :data-required="requiredAttr" :data-invalid="invalidAttr" class="vmz-ui-form-item">
3
+ <label v-if="label" :for="controlId" class="vmz-ui-form-item__label">
4
+ <span>
5
+ {{ label }}
6
+ </span>
7
+ <span v-if="required" aria-hidden="true" class="vmz-ui-form-item__required">
8
+ *
9
+ </span>
10
+ </label>
11
+ <div class="vmz-ui-form-item__control">
12
+ <slot />
13
+ </div>
14
+ <p v-if="description" :id="descriptionId" class="vmz-ui-form-item__description">
15
+ {{ description }}
16
+ </p>
17
+ <p v-if="error" :id="errorId" role="alert" class="vmz-ui-form-item__error">
18
+ {{ error }}
19
+ </p>
20
+ </div>
21
+ </template>
22
+ <style>
23
+ .vmz-ui-form-item {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: 0.375rem;
27
+ max-width: 28rem;
28
+ }
29
+
30
+ .vmz-ui-form-item__label {
31
+ display: inline-flex;
32
+ align-items: baseline;
33
+ gap: 0.25rem;
34
+ font: inherit;
35
+ font-weight: 600;
36
+ }
37
+
38
+ .vmz-ui-form-item__required {
39
+ color: var(--vmz-status-danger-accent);
40
+ font-weight: 700;
41
+ }
42
+
43
+ .vmz-ui-form-item__control {
44
+ min-width: 0;
45
+ }
46
+
47
+ .vmz-ui-form-item__description {
48
+ margin: 0;
49
+ opacity: 0.75;
50
+ font-size: 0.9em;
51
+ }
52
+
53
+ .vmz-ui-form-item__error {
54
+ margin: 0;
55
+ color: var(--vmz-status-danger-foreground);
56
+ font-size: 0.9em;
57
+ }
58
+
59
+ .vmz-ui-form-item[data-invalid="true"] .vmz-ui-form-item__control :where(input, select, textarea) {
60
+ border-color: var(--vmz-status-danger-accent);
61
+ }
62
+ </style>
63
+ <script client>
64
+ /**
65
+ * VMZ UI — FormItem.
66
+ * Label / description / error shell for slotted controls; parent owns validation strings.
67
+ */
68
+ export default class FormItem {
69
+ public label: string = "";
70
+ public description: string = "";
71
+ public error: string = "";
72
+ public required: boolean = false;
73
+ public requiredAttr: string = "false";
74
+ public invalidAttr: string = "false";
75
+ public controlId: string = "vmz-form-item";
76
+ public descriptionId: string = "vmz-form-item-desc";
77
+ public errorId: string = "vmz-form-item-err";
78
+ }
79
+ </script>
@@ -65,7 +65,11 @@ export default class Link {
65
65
  public href: string = "#";
66
66
  public target: string = "";
67
67
  public rel: string = "";
68
- /** `default` inline prose link · `muted` quiet inline link · `nav` chrome link. */
68
+ /**
69
+ * `default` inline prose link · `muted` quiet inline link · `nav` chrome link.
70
+ * Appearance/semantic hint only: navigation, `href`, `target` and `rel` are
71
+ * unaffected, and unknown values render as `default` without error.
72
+ */
69
73
  public variant: string = "default";
70
74
  }
71
75
  </script>