@vmz/ui 0.0.3 → 0.0.4

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 (53) hide show
  1. package/README.md +23 -1
  2. package/conformance/fixtures/button-control-motion.json +22 -0
  3. package/conformance/fixtures/dialog-overlay-motion.json +27 -0
  4. package/conformance/fixtures/drawer-overlay-motion.json +27 -0
  5. package/conformance/pack.v0.json +7 -0
  6. package/contracts/token-requirements.v0.json +604 -2
  7. package/package.json +50 -2
  8. package/presets/web-surface.v0.json +34 -0
  9. package/src/components/Accordion.vmz +112 -0
  10. package/src/components/Alert.vmz +84 -0
  11. package/src/components/AppShell.vmz +97 -0
  12. package/src/components/Autocomplete.vmz +176 -0
  13. package/src/components/Badge.vmz +63 -0
  14. package/src/components/Breadcrumb.vmz +66 -0
  15. package/src/components/BulkActions.vmz +52 -0
  16. package/src/components/Button.vmz +71 -4
  17. package/src/components/Callout.vmz +80 -0
  18. package/src/components/Card.vmz +81 -0
  19. package/src/components/Checkbox.vmz +69 -0
  20. package/src/components/CodeBlock.vmz +67 -0
  21. package/src/components/ConsoleShell.vmz +127 -0
  22. package/src/components/DataTable.vmz +220 -0
  23. package/src/components/DatePicker.vmz +93 -0
  24. package/src/components/Dialog.vmz +442 -0
  25. package/src/components/Drawer.vmz +435 -0
  26. package/src/components/Empty.vmz +52 -0
  27. package/src/components/Field.vmz +88 -0
  28. package/src/components/FilterBar.vmz +27 -0
  29. package/src/components/Form.vmz +72 -0
  30. package/src/components/FormItem.vmz +78 -0
  31. package/src/components/Link.vmz +48 -0
  32. package/src/components/List.vmz +116 -0
  33. package/src/components/Menu.vmz +270 -0
  34. package/src/components/Notification.vmz +83 -0
  35. package/src/components/Pagination.vmz +81 -0
  36. package/src/components/Popover.vmz +253 -0
  37. package/src/components/Prose.vmz +83 -0
  38. package/src/components/QueryForm.vmz +39 -0
  39. package/src/components/RadioGroup.vmz +135 -0
  40. package/src/components/Result.vmz +84 -0
  41. package/src/components/Select.vmz +351 -0
  42. package/src/components/Skeleton.vmz +80 -0
  43. package/src/components/Spinner.vmz +45 -0
  44. package/src/components/Steps.vmz +107 -0
  45. package/src/components/Switch.vmz +109 -0
  46. package/src/components/Table.vmz +134 -0
  47. package/src/components/Tabs.vmz +132 -0
  48. package/src/components/TextArea.vmz +91 -0
  49. package/src/components/Timeline.vmz +67 -0
  50. package/src/components/Toc.vmz +77 -0
  51. package/src/components/Tooltip.vmz +68 -0
  52. package/src/components/Tree.vmz +215 -0
  53. package/src/components/Upload.vmz +102 -0
@@ -0,0 +1,215 @@
1
+ <template>
2
+ <div class="vmz-ui-tree" data-vmz-ui="tree">
3
+ <p if={label} class="vmz-ui-tree__label" id={labelId}>{label}</p>
4
+ <ul
5
+ class="vmz-ui-tree__items"
6
+ role="tree"
7
+ aria-labelledby={labelId}
8
+ aria-label={label}
9
+ onClick={onTreeClick}
10
+ onKeyDown={onTreeKeyDown}
11
+ >
12
+ <li
13
+ each={items}
14
+ as="item"
15
+ key={item.id}
16
+ class="vmz-ui-tree__item"
17
+ role="treeitem"
18
+ id={item.id}
19
+ data-vmz-tree-item={item.id}
20
+ data-depth={item.depth}
21
+ aria-selected={selected === item.id ? 'true' : 'false'}
22
+ aria-expanded={item.expandedAttr}
23
+ tabindex={selected === item.id ? 0 : -1}
24
+ style={item.padStyle}
25
+ >
26
+ <button
27
+ if={item.expandable}
28
+ type="button"
29
+ class="vmz-ui-tree__twist"
30
+ data-vmz-tree-twist={item.id}
31
+ aria-label={item.twistLabel}
32
+ tabindex="-1"
33
+ >
34
+ {item.expanded ? '−' : '+'}
35
+ </button>
36
+ <span if={!item.expandable} class="vmz-ui-tree__twist-spacer" aria-hidden="true"></span>
37
+ <span class="vmz-ui-tree__title" data-vmz-tree-title={item.id}>{item.title}</span>
38
+ </li>
39
+ </ul>
40
+ </div>
41
+ </template>
42
+
43
+ <style>
44
+ .vmz-ui-tree {
45
+ display: flex;
46
+ flex-direction: column;
47
+ gap: 0.35rem;
48
+ max-width: 28rem;
49
+ }
50
+
51
+ .vmz-ui-tree__label {
52
+ margin: 0;
53
+ font-weight: 600;
54
+ color: var(--vmz-text-ink);
55
+ }
56
+
57
+ .vmz-ui-tree__items {
58
+ margin: 0;
59
+ padding: 0.25rem;
60
+ list-style: none;
61
+ border: 1px solid var(--vmz-line-default);
62
+ border-radius: 2px;
63
+ background: var(--vmz-surface-paper);
64
+ color: var(--vmz-text-ink);
65
+ }
66
+
67
+ .vmz-ui-tree__item {
68
+ display: flex;
69
+ align-items: center;
70
+ gap: 0.35rem;
71
+ padding: var(--vmz-density-compact-padding-y) var(--vmz-density-compact-padding-x);
72
+ border-radius: 2px;
73
+ cursor: pointer;
74
+ outline: none;
75
+ }
76
+
77
+ .vmz-ui-tree__item[aria-selected='true'] {
78
+ background: var(--vmz-action-primary-background);
79
+ color: var(--vmz-action-primary-foreground);
80
+ }
81
+
82
+ .vmz-ui-tree__item:hover {
83
+ background: color-mix(in srgb, var(--vmz-action-primary-background) 14%, transparent);
84
+ }
85
+
86
+ .vmz-ui-tree__item[aria-selected='true']:hover {
87
+ background: var(--vmz-action-primary-hover);
88
+ }
89
+
90
+ .vmz-ui-tree__item:focus-visible {
91
+ box-shadow: 0 0 0 2px var(--vmz-focus-ring);
92
+ }
93
+
94
+ .vmz-ui-tree__twist {
95
+ flex: 0 0 auto;
96
+ width: 1.35rem;
97
+ height: 1.35rem;
98
+ padding: 0;
99
+ border: 1px solid var(--vmz-line-default);
100
+ border-radius: 2px;
101
+ background: var(--vmz-surface-mist);
102
+ color: inherit;
103
+ font: inherit;
104
+ font-weight: 700;
105
+ line-height: 1;
106
+ cursor: pointer;
107
+ outline: none;
108
+ }
109
+
110
+ .vmz-ui-tree__twist:focus-visible {
111
+ box-shadow: 0 0 0 2px var(--vmz-focus-ring);
112
+ }
113
+
114
+ .vmz-ui-tree__twist-spacer {
115
+ flex: 0 0 auto;
116
+ width: 1.35rem;
117
+ }
118
+
119
+ .vmz-ui-tree__title {
120
+ flex: 1 1 auto;
121
+ }
122
+ </style>
123
+
124
+ <script client>
125
+ /**
126
+ * VMZ UI — Tree (ordinary hierarchy).
127
+ * Parent owns visible flat rows + selected + expand state.
128
+ * Not virtualized / not DataGrid tree mode.
129
+ */
130
+ export default class Tree {
131
+ public label: string = 'Tree';
132
+ public labelId: string = 'vmz-tree-label';
133
+ public selected: string = '';
134
+ /**
135
+ * @type {{
136
+ * id: string,
137
+ * title: string,
138
+ * depth: string,
139
+ * expandable: boolean,
140
+ * expanded: boolean,
141
+ * expandedAttr: string,
142
+ * twistLabel: string,
143
+ * padStyle: string
144
+ * }[]}
145
+ */
146
+ public items: {
147
+ id: string;
148
+ title: string;
149
+ depth: string;
150
+ expandable: boolean;
151
+ expanded: boolean;
152
+ expandedAttr: string;
153
+ twistLabel: string;
154
+ padStyle: string;
155
+ }[] = [];
156
+ public onSelect: ((id: string) => void) | null = null;
157
+ public onToggle: ((id: string) => void) | null = null;
158
+
159
+ select(id) {
160
+ if (typeof this.onSelect === 'function') this.onSelect(id);
161
+ }
162
+
163
+ toggle(id) {
164
+ if (typeof this.onToggle === 'function') this.onToggle(id);
165
+ }
166
+
167
+ onTreeClick(ev) {
168
+ const t = ev && ev.target;
169
+ const twist =
170
+ t && typeof t.closest === 'function' ? t.closest('[data-vmz-tree-twist]') : null;
171
+ if (twist) {
172
+ const id =
173
+ typeof twist.getAttribute === 'function' ? twist.getAttribute('data-vmz-tree-twist') : null;
174
+ if (id) {
175
+ ev.preventDefault();
176
+ ev.stopPropagation();
177
+ this.toggle(id);
178
+ }
179
+ return;
180
+ }
181
+ const el = t && typeof t.closest === 'function' ? t.closest('[data-vmz-tree-item]') : null;
182
+ const id = el && typeof el.getAttribute === 'function' ? el.getAttribute('data-vmz-tree-item') : null;
183
+ if (id) this.select(id);
184
+ }
185
+
186
+ onTreeKeyDown(ev) {
187
+ if (!ev || !this.items?.length) return;
188
+ const ids = this.items.map((it) => it.id);
189
+ const idx = Math.max(0, ids.indexOf(this.selected));
190
+ const cur = this.items[idx];
191
+ if (ev.key === 'ArrowRight' && cur?.expandable && !cur.expanded) {
192
+ ev.preventDefault();
193
+ this.toggle(cur.id);
194
+ return;
195
+ }
196
+ if (ev.key === 'ArrowLeft' && cur?.expandable && cur.expanded) {
197
+ ev.preventDefault();
198
+ this.toggle(cur.id);
199
+ return;
200
+ }
201
+ let next = idx;
202
+ if (ev.key === 'ArrowDown') next = (idx + 1) % ids.length;
203
+ else if (ev.key === 'ArrowUp') next = (idx - 1 + ids.length) % ids.length;
204
+ else if (ev.key === 'Home') next = 0;
205
+ else if (ev.key === 'End') next = ids.length - 1;
206
+ else if (ev.key === 'Enter' || ev.key === ' ') {
207
+ ev.preventDefault();
208
+ if (cur) this.select(cur.id);
209
+ return;
210
+ } else return;
211
+ ev.preventDefault();
212
+ this.select(ids[next]);
213
+ }
214
+ }
215
+ </script>
@@ -0,0 +1,102 @@
1
+ <template>
2
+ <div class="vmz-ui-upload" data-vmz-ui="upload" data-invalid={invalidAttr}>
3
+ <label if={label} class="vmz-ui-upload__label" for={controlId}>{label}</label>
4
+ <input
5
+ id={controlId}
6
+ class="vmz-ui-upload__control"
7
+ type="file"
8
+ accept={accept}
9
+ disabled={disabled}
10
+ aria-invalid={error ? 'true' : 'false'}
11
+ aria-describedby={describedBy}
12
+ onChange={onChange}
13
+ />
14
+ <p if={fileName} class="vmz-ui-upload__file" data-vmz-upload="file">Selected: {fileName}</p>
15
+ <p if={emptyHint} class="vmz-ui-upload__empty" data-vmz-upload="empty">{emptyText}</p>
16
+ <p if={description} id={descriptionId} class="vmz-ui-upload__description">{description}</p>
17
+ <p if={error} id={errorId} class="vmz-ui-upload__error" role="alert">{error}</p>
18
+ </div>
19
+ </template>
20
+
21
+ <style>
22
+ .vmz-ui-upload {
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: 0.35rem;
26
+ max-width: 28rem;
27
+ }
28
+
29
+ .vmz-ui-upload__label {
30
+ font: inherit;
31
+ font-weight: 600;
32
+ }
33
+
34
+ .vmz-ui-upload__control {
35
+ font: inherit;
36
+ padding: var(--vmz-density-control-padding-y) 0;
37
+ color: inherit;
38
+ outline: none;
39
+ }
40
+
41
+ .vmz-ui-upload__control:focus-visible {
42
+ box-shadow: 0 0 0 2px var(--vmz-focus-ring);
43
+ }
44
+
45
+ .vmz-ui-upload__control:disabled {
46
+ opacity: 0.55;
47
+ cursor: not-allowed;
48
+ }
49
+
50
+ .vmz-ui-upload[data-invalid='true'] .vmz-ui-upload__label {
51
+ color: var(--vmz-status-danger-foreground);
52
+ }
53
+
54
+ .vmz-ui-upload__file {
55
+ margin: 0;
56
+ font-family: var(--vmz-font-mono);
57
+ font-size: 0.88rem;
58
+ color: var(--vmz-text-ink);
59
+ }
60
+
61
+ .vmz-ui-upload__empty {
62
+ margin: 0;
63
+ color: var(--vmz-text-steel);
64
+ font-size: 0.9em;
65
+ }
66
+
67
+ .vmz-ui-upload__description {
68
+ margin: 0;
69
+ opacity: 0.75;
70
+ font-size: 0.9em;
71
+ }
72
+
73
+ .vmz-ui-upload__error {
74
+ margin: 0;
75
+ color: var(--vmz-status-danger-foreground);
76
+ font-size: 0.9em;
77
+ }
78
+ </style>
79
+
80
+ <script client>
81
+ /**
82
+ * VMZ UI — Upload.
83
+ * Native file input; parent owns selected fileName presentation.
84
+ * No network upload runtime / second form store — pick + validate only.
85
+ */
86
+ export default class Upload {
87
+ public label: string = '';
88
+ public description: string = '';
89
+ public error: string = '';
90
+ public fileName: string = '';
91
+ public emptyText: string = 'No file selected';
92
+ public emptyHint: boolean = true;
93
+ public accept: string = '';
94
+ public disabled: boolean = false;
95
+ public invalidAttr: string = 'false';
96
+ public controlId: string = 'vmz-upload';
97
+ public descriptionId: string = 'vmz-upload-desc';
98
+ public errorId: string = 'vmz-upload-err';
99
+ public describedBy: string = '';
100
+ public onChange: ((e: Event) => void) | null = null;
101
+ }
102
+ </script>