@vmz/ui 0.3.4 → 0.3.6

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 (43) hide show
  1. package/README.md +51 -51
  2. package/package.json +2 -2
  3. package/presets/web-surface.v0.json +19 -7
  4. package/src/components/Accordion.vmz +94 -94
  5. package/src/components/AppShell.vmz +77 -33
  6. package/src/components/Autocomplete.vmz +154 -154
  7. package/src/components/Badge.vmz +70 -70
  8. package/src/components/Breadcrumb.vmz +72 -72
  9. package/src/components/BulkActions.vmz +51 -51
  10. package/src/components/Button.vmz +211 -192
  11. package/src/components/Callout.vmz +77 -77
  12. package/src/components/Card.vmz +120 -106
  13. package/src/components/Checkbox.vmz +55 -55
  14. package/src/components/CodeBlock.vmz +77 -18
  15. package/src/components/ConsoleShell.vmz +251 -251
  16. package/src/components/DataTable.vmz +194 -194
  17. package/src/components/DatePicker.vmz +674 -674
  18. package/src/components/Dialog.vmz +10 -7
  19. package/src/components/Drawer.vmz +427 -427
  20. package/src/components/Dropdown.vmz +135 -16
  21. package/src/components/Empty.vmz +54 -54
  22. package/src/components/Field.vmz +115 -115
  23. package/src/components/FormItem.vmz +79 -79
  24. package/src/components/Link.vmz +34 -4
  25. package/src/components/List.vmz +102 -102
  26. package/src/components/Menu.vmz +243 -243
  27. package/src/components/Pagination.vmz +69 -69
  28. package/src/components/Popover.vmz +223 -223
  29. package/src/components/Prose.vmz +81 -81
  30. package/src/components/RadioGroup.vmz +124 -124
  31. package/src/components/Segmented.vmz +51 -51
  32. package/src/components/Select.vmz +533 -533
  33. package/src/components/Steps.vmz +103 -103
  34. package/src/components/Switch.vmz +98 -98
  35. package/src/components/Table.vmz +165 -133
  36. package/src/components/Tabs.vmz +110 -110
  37. package/src/components/Tag.vmz +43 -40
  38. package/src/components/TextArea.vmz +118 -118
  39. package/src/components/Timeline.vmz +76 -76
  40. package/src/components/Toc.vmz +74 -74
  41. package/src/components/Tooltip.vmz +58 -58
  42. package/src/components/Tree.vmz +191 -191
  43. package/src/components/Upload.vmz +1289 -1289
@@ -1,12 +1,13 @@
1
1
  <template>
2
- <details data-vmz-ui="dropdown" class="vmz-ui-dropdown">
3
- <summary data-vmz-dropdown-trigger="1">
2
+ <details data-vmz-ui="dropdown" data-vmz-motion="overlay" data-vmz-motion-token="motion.overlay" class="vmz-ui-dropdown">
3
+ <summary data-vmz-dropdown-trigger="1" class="vmz-ui-dropdown__trigger">
4
4
  {{ label }}
5
5
  </summary>
6
- <div data-vmz-dropdown-menu="1" class="vmz-ui-dropdown__menu">
7
- <a v-for="item in items" :key="item.id" :href="item.href" :data-vmz-dropdown-item="item.id">
8
- {{ item.title }}
9
- </a>
6
+ <div data-vmz-dropdown-menu="1" role="menu" class="vmz-ui-dropdown__menu">
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>
10
+ </div>
10
11
  </div>
11
12
  </details>
12
13
  </template>
@@ -15,47 +16,165 @@
15
16
  position: relative;
16
17
  display: inline-block;
17
18
  }
18
- .vmz-ui-dropdown summary {
19
+
20
+ .vmz-ui-dropdown__trigger {
19
21
  list-style: none;
20
22
  min-height: 2.5rem;
21
23
  display: inline-flex;
22
24
  align-items: center;
23
- padding: 0 0.85rem;
25
+ gap: 0.375rem;
26
+ padding: 0 0.9375rem;
24
27
  border: 1px solid var(--vmz-line-default);
25
28
  border-radius: var(--vmz-radius-md, 6px);
26
29
  background: var(--vmz-surface-paper);
30
+ color: var(--vmz-text-ink);
27
31
  cursor: pointer;
28
32
  font-weight: 550;
33
+ outline: none;
34
+ transition:
35
+ border-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
36
+ background-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
37
+ box-shadow var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
38
+ }
39
+
40
+ /* Chevron: affordance that the trigger opens a surface, not a page. */
41
+ .vmz-ui-dropdown__trigger::after {
42
+ content: "";
43
+ width: 0.4375rem;
44
+ height: 0.4375rem;
45
+ margin-top: -0.1875rem;
46
+ border-right: 1.5px solid currentColor;
47
+ border-bottom: 1.5px solid currentColor;
48
+ opacity: 0.55;
49
+ transform: rotate(45deg);
50
+ transition: transform var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
29
51
  }
52
+
30
53
  .vmz-ui-dropdown summary::-webkit-details-marker {
31
54
  display: none;
32
55
  }
56
+
57
+ .vmz-ui-dropdown__trigger:hover {
58
+ border-color: var(--vmz-action-primary-hover);
59
+ background: var(--vmz-surface-mist);
60
+ }
61
+
62
+ .vmz-ui-dropdown__trigger:focus-visible {
63
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring);
64
+ }
65
+
66
+ .vmz-ui-dropdown[open] > .vmz-ui-dropdown__trigger {
67
+ border-color: var(--vmz-action-primary-background);
68
+ }
69
+
70
+ .vmz-ui-dropdown[open] > .vmz-ui-dropdown__trigger::after {
71
+ margin-top: 0.125rem;
72
+ transform: rotate(-135deg);
73
+ }
74
+
33
75
  .vmz-ui-dropdown__menu {
34
76
  position: absolute;
35
- top: calc(100% + 0.35rem);
77
+ top: calc(100% + 0.375rem);
36
78
  right: 0;
37
79
  z-index: 50;
38
80
  min-width: 11rem;
39
- padding: 0.3rem;
40
- border: 1px solid var(--vmz-line-default);
81
+ padding: 0.25rem;
82
+ border: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
41
83
  border-radius: var(--vmz-radius-lg, 8px);
42
84
  background: var(--vmz-surface-paper);
43
85
  box-shadow: var(--vmz-shadow-popup);
86
+ transform-origin: top right;
87
+ animation: vmz-dropdown-in var(--vmz-motion-overlay-duration, 180ms) var(--vmz-motion-overlay-easing, cubic-bezier(0.2, 0, 0, 1));
88
+ }
89
+
90
+ @keyframes vmz-dropdown-in {
91
+ from {
92
+ opacity: 0;
93
+ transform: translateY(-0.25rem) scale(0.98);
94
+ }
95
+ to {
96
+ opacity: 1;
97
+ transform: translateY(0) scale(1);
98
+ }
44
99
  }
45
- .vmz-ui-dropdown__menu a {
100
+
101
+ @media (prefers-reduced-motion: reduce) {
102
+ .vmz-ui-dropdown__menu {
103
+ animation: none;
104
+ }
105
+ .vmz-ui-dropdown__trigger,
106
+ .vmz-ui-dropdown__trigger::after {
107
+ transition: none;
108
+ }
109
+ }
110
+
111
+ .vmz-ui-dropdown__row {
112
+ display: contents;
113
+ }
114
+
115
+ .vmz-ui-dropdown__item {
46
116
  display: block;
47
- padding: 0.55rem 0.7rem;
48
- border-radius: var(--vmz-radius-sm, 2px);
117
+ padding: 0.5rem 0.75rem;
118
+ border-radius: var(--vmz-radius-sm, 4px);
49
119
  color: var(--vmz-text-ink);
120
+ font-size: 0.875rem;
121
+ line-height: 1.4286;
50
122
  text-decoration: none;
123
+ outline: none;
124
+ transition:
125
+ background-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
126
+ color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
127
+ }
128
+
129
+ .vmz-ui-dropdown__item:hover {
130
+ background: var(--vmz-surface-mist);
51
131
  }
52
- .vmz-ui-dropdown__menu a:hover {
132
+
133
+ .vmz-ui-dropdown__item:focus-visible {
53
134
  background: var(--vmz-surface-mist);
135
+ box-shadow: inset 0 0 0 3px var(--vmz-focus-ring);
136
+ }
137
+
138
+ .vmz-ui-dropdown__item[data-danger="true"] {
139
+ color: var(--vmz-status-danger-foreground);
140
+ }
141
+
142
+ .vmz-ui-dropdown__item[data-danger="true"]:hover {
143
+ background: color-mix(in srgb, var(--vmz-status-danger-accent) 12%, transparent);
144
+ }
145
+
146
+ .vmz-ui-dropdown__item[data-disabled="true"] {
147
+ color: var(--vmz-text-steel);
148
+ cursor: not-allowed;
149
+ pointer-events: none;
150
+ }
151
+
152
+ .vmz-ui-dropdown__divider {
153
+ height: 1px;
154
+ margin: 0.25rem 0.5rem;
155
+ background: var(--vmz-line-subtle, var(--vmz-line-default));
54
156
  }
55
157
  </style>
56
158
  <script client>
159
+ /**
160
+ * VMZ UI — Dropdown (Menu).
161
+ * Native `<details>` disclosure: works without JS, menu items are plain links
162
+ * unless a host attaches selection handlers.
163
+ */
164
+ export type DropdownItem = {
165
+ id: string;
166
+ title: string;
167
+ href?: string;
168
+ /** Destructive action styling. */
169
+ danger?: boolean;
170
+ /** Non-interactive state. */
171
+ disabled?: boolean;
172
+ /** Render a separator instead of a row (title/href are ignored). */
173
+ divider?: boolean;
174
+ };
175
+
57
176
  export default class Dropdown {
58
177
  public label: string = "Menu";
59
- public items: Array<{ id: string; title: string; href: string }> = [];
178
+ public items: DropdownItem[] = [];
60
179
  }
61
180
  </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.65rem;
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.35rem;
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, linear),
47
- box-shadow var(--vmz-motion-control-duration, 120ms)
48
- var(--vmz-motion-control-easing, linear);
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 2px 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>