@vmz/ui 0.3.7 → 0.3.9

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 +2 -0
  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 +49 -16
  6. package/src/components/Alert.vmz +125 -28
  7. package/src/components/AppShell.vmz +121 -56
  8. package/src/components/Autocomplete.vmz +136 -29
  9. package/src/components/Avatar.vmz +20 -12
  10. package/src/components/Badge.vmz +18 -26
  11. package/src/components/Breadcrumb.vmz +28 -14
  12. package/src/components/BulkActions.vmz +13 -8
  13. package/src/components/Button.vmz +68 -44
  14. package/src/components/Callout.vmz +128 -27
  15. package/src/components/Card.vmz +67 -46
  16. package/src/components/Checkbox.vmz +81 -5
  17. package/src/components/CodeBlock.vmz +59 -27
  18. package/src/components/Collapse.vmz +64 -11
  19. package/src/components/ConsoleShell.vmz +249 -69
  20. package/src/components/Content.vmz +15 -1
  21. package/src/components/DataTable.vmz +123 -23
  22. package/src/components/DatePicker.vmz +171 -79
  23. package/src/components/Descriptions.vmz +90 -0
  24. package/src/components/Dialog.vmz +133 -50
  25. package/src/components/Divider.vmz +8 -1
  26. package/src/components/Drawer.vmz +131 -40
  27. package/src/components/Dropdown.vmz +147 -49
  28. package/src/components/Empty.vmz +56 -14
  29. package/src/components/Field.vmz +78 -23
  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 +2 -3
  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 +20 -9
  39. package/src/components/List.vmz +35 -13
  40. package/src/components/Menu.vmz +198 -25
  41. package/src/components/Message.vmz +167 -0
  42. package/src/components/Notification.vmz +132 -37
  43. package/src/components/Pagination.vmz +186 -20
  44. package/src/components/Popconfirm.vmz +287 -0
  45. package/src/components/Popover.vmz +152 -34
  46. package/src/components/Progress.vmz +16 -5
  47. package/src/components/Prose.vmz +131 -32
  48. package/src/components/QueryForm.vmz +31 -9
  49. package/src/components/RadioGroup.vmz +91 -11
  50. package/src/components/Result.vmz +60 -27
  51. package/src/components/Segmented.vmz +46 -11
  52. package/src/components/Select.vmz +174 -87
  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 +40 -25
  58. package/src/components/Switch.vmz +48 -15
  59. package/src/components/Table.vmz +130 -25
  60. package/src/components/Tabs.vmz +28 -13
  61. package/src/components/Tag.vmz +29 -12
  62. package/src/components/TextArea.vmz +32 -25
  63. package/src/components/Timeline.vmz +65 -30
  64. package/src/components/Toc.vmz +45 -29
  65. package/src/components/Tooltip.vmz +70 -21
  66. package/src/components/Tree.vmz +48 -25
  67. package/src/components/Typography.vmz +23 -8
  68. package/src/components/Upload.vmz +191 -96
@@ -2,7 +2,10 @@
2
2
  <ol data-vmz-ui="steps" :aria-label="label" class="vmz-ui-steps">
3
3
  <li v-for="item in items" :key="item.id" :data-vmz-step="item.id" :data-status="item.status" :aria-current="current === item.id ? 'step' : 'false'" class="vmz-ui-steps__item">
4
4
  <span :data-vmz-step-index="item.id" class="vmz-ui-steps__index">
5
- {{ item.index }}
5
+ <Icon v-if="item.status === 'done'" name="action.check" size="md" decorative />
6
+ <span v-else class="vmz-ui-steps__number">
7
+ {{ item.index }}
8
+ </span>
6
9
  </span>
7
10
  <span class="vmz-ui-steps__body">
8
11
  <span class="vmz-ui-steps__title">
@@ -22,37 +25,31 @@
22
25
  list-style: none;
23
26
  display: flex;
24
27
  flex-direction: column;
25
- gap: var(--vmz-density-control-gap);
28
+ gap: var(--vmz-space-3, 0.75rem);
26
29
  max-width: 36rem;
27
30
  }
28
31
 
29
32
  .vmz-ui-steps__item {
30
33
  display: flex;
31
34
  align-items: flex-start;
32
- gap: 0.75rem;
33
- color: var(--vmz-text-steel);
34
- }
35
-
36
- .vmz-ui-steps__item[data-status="done"] {
37
- color: var(--vmz-status-success-foreground);
38
- }
39
-
40
- .vmz-ui-steps__item[data-status="current"] {
41
- color: var(--vmz-action-primary-active);
35
+ gap: var(--vmz-space-3, 0.75rem);
36
+ color: var(--vmz-text-ink);
42
37
  }
43
38
 
44
39
  .vmz-ui-steps__index {
45
40
  flex: 0 0 auto;
46
- min-width: 1.75rem;
47
- height: 1.75rem;
48
41
  display: inline-flex;
49
42
  align-items: center;
50
43
  justify-content: center;
51
- border: 1px solid var(--vmz-line-default);
52
- border-radius: 999px;
53
- font-weight: 700;
54
- font-size: 0.85rem;
55
- color: inherit;
44
+ width: 1.75rem;
45
+ height: 1.75rem;
46
+ border: 1px solid var(--vmz-line-default, var(--vmz-line-strong));
47
+ border-radius: var(--vmz-radius-pill, 999px);
48
+ background: var(--vmz-surface-paper);
49
+ font-size: 0.8125rem;
50
+ font-weight: 600;
51
+ line-height: 1;
52
+ color: var(--vmz-text-tertiary);
56
53
  }
57
54
 
58
55
  .vmz-ui-steps__item[data-status="current"] .vmz-ui-steps__index {
@@ -63,23 +60,41 @@
63
60
 
64
61
  .vmz-ui-steps__item[data-status="done"] .vmz-ui-steps__index {
65
62
  border-color: var(--vmz-status-success-accent);
66
- background: color-mix(in srgb, var(--vmz-status-success-accent) 18%, transparent);
63
+ background: var(--vmz-status-success-background);
64
+ color: var(--vmz-status-success-foreground);
67
65
  }
68
66
 
69
67
  .vmz-ui-steps__body {
70
68
  display: flex;
71
69
  flex-direction: column;
72
- gap: 0.2rem;
70
+ gap: var(--vmz-space-1, 0.25rem);
73
71
  padding-top: 0.125rem;
72
+ min-width: 0;
74
73
  }
75
74
 
76
75
  .vmz-ui-steps__title {
77
- font-weight: 700;
76
+ font-size: 0.875rem;
77
+ font-weight: 600;
78
+ line-height: 1.5;
79
+ color: var(--vmz-text-ink);
80
+ }
81
+
82
+ .vmz-ui-steps__item[data-status="current"] .vmz-ui-steps__title {
83
+ color: var(--vmz-action-primary-active);
78
84
  }
79
85
 
80
86
  .vmz-ui-steps__description {
81
- font-size: 0.9em;
82
- opacity: 0.9;
87
+ font-size: 0.8125rem;
88
+ line-height: 1.5;
89
+ color: var(--vmz-text-tertiary);
90
+ }
91
+
92
+ :where([data-density="compact"]) .vmz-ui-steps {
93
+ gap: var(--vmz-density-compact-gap);
94
+ }
95
+
96
+ :where([data-density="dense"]) .vmz-ui-steps {
97
+ gap: var(--vmz-density-dense-gap);
83
98
  }
84
99
  </style>
85
100
  <script client>
@@ -89,7 +104,7 @@
89
104
  * Navigation actions stay on the page (no private wizard store).
90
105
  */
91
106
  export default class Steps {
92
- public label: string = "Steps";
107
+ public label: string = "";
93
108
  public current: string = "";
94
109
  /** @type {{ id: string, index: string, title: string, description?: string, status: string }[]} */
95
110
  public items: {
@@ -1,6 +1,6 @@
1
1
  <template>
2
- <label data-vmz-ui="switch" :for="controlId" class="vmz-ui-switch">
3
- <button :id="controlId" type="button" role="switch" :aria-checked="checked" :aria-describedby="describedBy" :disabled="disabled" @click="toggle" :onKeyDown="onKeyDown" class="vmz-ui-switch__control">
2
+ <label data-vmz-ui="switch" :for="controlId" :data-disabled="disabled ? 'true' : 'false'" class="vmz-ui-switch">
3
+ <button :id="controlId" type="button" role="switch" :aria-checked="checked ? 'true' : 'false'" :data-checked="checked ? 'true' : 'false'" :aria-describedby="describedBy" :disabled="disabled" @click="toggle" :onKeyDown="onKeyDown" class="vmz-ui-switch__control">
4
4
  <span aria-hidden="true" class="vmz-ui-switch__thumb" />
5
5
  </button>
6
6
  <span class="vmz-ui-switch__label">
@@ -12,28 +12,50 @@
12
12
  .vmz-ui-switch {
13
13
  display: inline-flex;
14
14
  align-items: center;
15
- gap: 0.5rem;
15
+ gap: var(--vmz-space-2);
16
16
  font: inherit;
17
+ font-size: 0.875rem;
18
+ line-height: 1.5715;
19
+ color: var(--vmz-text-ink);
17
20
  cursor: pointer;
18
21
  }
19
22
 
20
23
  .vmz-ui-switch__control {
21
24
  position: relative;
22
- width: 2.4rem;
23
- height: 1.35rem;
25
+ flex: 0 0 auto;
26
+ box-sizing: border-box;
27
+ width: 2.5rem;
28
+ height: 1.5rem;
24
29
  padding: 0;
25
- border: 1px solid var(--vmz-line-default);
26
- border-radius: 999px;
27
- background: transparent;
30
+ border: 0;
31
+ border-radius: var(--vmz-radius-pill);
32
+ background: var(--vmz-line-strong);
28
33
  cursor: pointer;
29
34
  outline: none;
35
+ transition:
36
+ background-color var(--vmz-motion-control-duration) var(--vmz-motion-control-easing),
37
+ box-shadow var(--vmz-motion-control-duration) var(--vmz-motion-control-easing);
38
+ }
39
+
40
+ .vmz-ui-switch__control:hover:not(:disabled):not([aria-checked="true"]) {
41
+ background: var(--vmz-text-tertiary);
30
42
  }
31
43
 
32
44
  .vmz-ui-switch__control[aria-checked="true"],
33
- .vmz-ui-switch__control[aria-checked=""] {
45
+ .vmz-ui-switch__control[data-checked="true"] {
34
46
  background: var(--vmz-action-primary-background);
35
47
  }
36
48
 
49
+ .vmz-ui-switch__control[aria-checked="true"]:hover:not(:disabled),
50
+ .vmz-ui-switch__control[data-checked="true"]:hover:not(:disabled) {
51
+ background: var(--vmz-action-primary-hover);
52
+ }
53
+
54
+ .vmz-ui-switch__control:active:not(:disabled)[aria-checked="true"],
55
+ .vmz-ui-switch__control:active:not(:disabled)[data-checked="true"] {
56
+ background: var(--vmz-action-primary-active);
57
+ }
58
+
37
59
  .vmz-ui-switch__control:focus-visible {
38
60
  box-shadow: 0 0 0 3px var(--vmz-focus-ring);
39
61
  }
@@ -45,25 +67,36 @@
45
67
 
46
68
  .vmz-ui-switch__thumb {
47
69
  position: absolute;
48
- top: 0.12rem;
49
- left: 0.12rem;
70
+ top: var(--vmz-space-1);
71
+ left: var(--vmz-space-1);
50
72
  width: 1rem;
51
73
  height: 1rem;
52
- border-radius: 999px;
74
+ border-radius: var(--vmz-radius-pill);
53
75
  background: var(--vmz-action-primary-foreground);
54
76
  transition: transform var(--vmz-motion-control-duration) var(--vmz-motion-control-easing);
55
77
  }
56
78
 
57
79
  .vmz-ui-switch__control[aria-checked="true"] .vmz-ui-switch__thumb,
58
- .vmz-ui-switch__control[aria-checked=""] .vmz-ui-switch__thumb {
59
- transform: translateX(1.05rem);
80
+ .vmz-ui-switch__control[data-checked="true"] .vmz-ui-switch__thumb {
81
+ transform: translateX(1rem);
60
82
  }
61
83
 
62
84
  .vmz-ui-switch__label {
63
- font-weight: 600;
85
+ font-weight: 400;
86
+ }
87
+
88
+ .vmz-ui-switch[data-disabled="true"] {
89
+ opacity: 0.55;
90
+ cursor: not-allowed;
91
+ }
92
+
93
+ .vmz-ui-switch[data-disabled="true"] .vmz-ui-switch__control,
94
+ .vmz-ui-switch[data-disabled="true"] .vmz-ui-switch__label {
95
+ cursor: not-allowed;
64
96
  }
65
97
 
66
98
  @media (prefers-reduced-motion: reduce) {
99
+ .vmz-ui-switch__control,
67
100
  .vmz-ui-switch__thumb {
68
101
  transition: none;
69
102
  }
@@ -3,9 +3,16 @@
3
3
  <table class="vmz-ui-table">
4
4
  <thead @click="onHeadClick">
5
5
  <tr>
6
- <th v-for="col in columns" :key="col.id" scope="col" :data-vmz-col="col.id">
7
- <button type="button" :data-vmz-sort="col.id" class="vmz-ui-table__sort">
8
- {{ col.title }}
6
+ <th v-for="col in columns" :key="col.id" scope="col" :data-vmz-col="col.id" :data-align="columnAlign(col)" :aria-sort="sortAria(col.id)">
7
+ <button type="button" :data-vmz-sort="col.id" :data-active="col.id === sortKey ? 'true' : 'false'" class="vmz-ui-table__sort">
8
+ <span class="vmz-ui-table__title">
9
+ {{ col.title }}
10
+ </span>
11
+ <span class="vmz-ui-table__sort-icon">
12
+ <Icon v-if="col.id === sortKey && sortDir === 'asc'" name="action.sort-asc" size="sm" decorative />
13
+ <Icon v-else-if="col.id === sortKey && sortDir === 'desc'" name="action.sort-desc" size="sm" decorative />
14
+ <Icon v-else name="action.sort" size="sm" decorative />
15
+ </span>
9
16
  </button>
10
17
  </th>
11
18
  <th v-if="hasRowAction" scope="col">
@@ -15,7 +22,7 @@
15
22
  </thead>
16
23
  <tbody @click="onBodyClick">
17
24
  <tr v-for="row in rows" :key="row.id" :data-vmz-row="row.id">
18
- <td v-for="cell in row.cells">
25
+ <td v-for="(cell, index) in row.cells" :key="index" :data-align="cellAlign(index, cell)">
19
26
  {{ cell }}
20
27
  </td>
21
28
  <td v-if="hasRowAction">
@@ -24,14 +31,30 @@
24
31
  </button>
25
32
  </td>
26
33
  </tr>
34
+ <tr v-if="loading" class="vmz-ui-table__state-row">
35
+ <td :colspan="columnSpan()" class="vmz-ui-table__state">
36
+ <Spinner :label="loadingLabel" />
37
+ <span v-if="loadingText" class="vmz-ui-table__state-text">
38
+ {{ loadingText }}
39
+ </span>
40
+ </td>
41
+ </tr>
42
+ <tr v-else-if="rows.length === 0" class="vmz-ui-table__state-row">
43
+ <td :colspan="columnSpan()" class="vmz-ui-table__state">
44
+ <span class="vmz-ui-table__state-text">
45
+ {{ emptyText }}
46
+ </span>
47
+ </td>
48
+ </tr>
27
49
  </tbody>
28
50
  </table>
29
51
  </div>
30
52
  </template>
31
53
  <style>
32
54
  .vmz-ui-table-wrap {
33
- overflow-x: auto;
34
- border: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
55
+ width: 100%;
56
+ overflow: auto;
57
+ border: 1px solid var(--vmz-line-default);
35
58
  border-radius: var(--vmz-radius-lg, 8px);
36
59
  background: var(--vmz-surface-paper);
37
60
  }
@@ -41,24 +64,37 @@
41
64
  border-collapse: collapse;
42
65
  font: inherit;
43
66
  font-size: 0.875rem;
67
+ color: var(--vmz-text-ink);
44
68
  }
45
69
 
46
70
  .vmz-ui-table th,
47
71
  .vmz-ui-table td {
48
- padding: 0.75rem 1rem;
72
+ height: 3rem;
73
+ padding: 0 1rem;
49
74
  text-align: left;
50
- border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
51
75
  vertical-align: middle;
76
+ border: 0;
77
+ border-bottom: 1px solid var(--vmz-line-subtle);
78
+ white-space: nowrap;
52
79
  }
53
80
 
54
- /* Header is structure, not brand: quiet ink on a tinted band. */
55
81
  .vmz-ui-table th {
82
+ height: 2.75rem;
83
+ font-size: 0.75rem;
56
84
  font-weight: 600;
57
- font-size: 0.8125rem;
58
- letter-spacing: 0.01em;
59
- color: var(--vmz-text-steel);
85
+ color: var(--vmz-text-tertiary);
60
86
  background: var(--vmz-surface-mist);
61
- white-space: nowrap;
87
+ }
88
+
89
+ .vmz-ui-table td[data-align="right"],
90
+ .vmz-ui-table th[data-align="right"] {
91
+ text-align: right;
92
+ font-variant-numeric: tabular-nums;
93
+ }
94
+
95
+ .vmz-ui-table td[data-align="center"],
96
+ .vmz-ui-table th[data-align="center"] {
97
+ text-align: center;
62
98
  }
63
99
 
64
100
  .vmz-ui-table tbody tr {
@@ -67,7 +103,7 @@
67
103
  }
68
104
 
69
105
  .vmz-ui-table tbody tr:hover {
70
- background: var(--vmz-surface-mist);
106
+ background: var(--vmz-surface-fill);
71
107
  }
72
108
 
73
109
  .vmz-ui-table tbody tr:last-child td {
@@ -77,41 +113,69 @@
77
113
  .vmz-ui-table__sort {
78
114
  display: inline-flex;
79
115
  align-items: center;
116
+ justify-content: flex-start;
80
117
  gap: 0.25rem;
81
- font: inherit;
82
- font-weight: 600;
118
+ width: 100%;
83
119
  padding: 0;
84
120
  border: 0;
85
121
  background: transparent;
86
122
  color: inherit;
123
+ font: inherit;
124
+ font-size: 0.75rem;
125
+ font-weight: 600;
87
126
  cursor: pointer;
88
127
  outline: none;
89
128
  border-radius: var(--vmz-radius-xs, 2px);
90
129
  }
91
130
 
131
+ .vmz-ui-table th[data-align="right"] .vmz-ui-table__sort {
132
+ justify-content: flex-end;
133
+ }
134
+
135
+ .vmz-ui-table th[data-align="center"] .vmz-ui-table__sort {
136
+ justify-content: center;
137
+ }
138
+
139
+ .vmz-ui-table__sort:hover {
140
+ color: var(--vmz-action-primary-background);
141
+ }
142
+
92
143
  .vmz-ui-table__sort:focus-visible {
93
144
  box-shadow: 0 0 0 3px var(--vmz-focus-ring);
94
145
  }
95
146
 
147
+ .vmz-ui-table__sort-icon {
148
+ display: inline-flex;
149
+ align-items: center;
150
+ color: var(--vmz-text-tertiary);
151
+ font-size: 0.875rem;
152
+ }
153
+
154
+ .vmz-ui-table__sort:hover .vmz-ui-table__sort-icon,
155
+ .vmz-ui-table__sort[data-active="true"] .vmz-ui-table__sort-icon {
156
+ color: var(--vmz-action-primary-background);
157
+ }
158
+
96
159
  .vmz-ui-table__row-action {
160
+ height: 2rem;
161
+ padding: 0 0.5rem;
97
162
  font: inherit;
163
+ font-size: 0.875rem;
98
164
  font-weight: 500;
99
- padding: 0.25rem 0.625rem;
100
- border: 1px solid var(--vmz-line-default);
165
+ border: 0;
101
166
  border-radius: var(--vmz-radius-md, 6px);
102
- background: var(--vmz-surface-paper);
167
+ background: transparent;
103
168
  color: var(--vmz-action-primary-background);
104
169
  cursor: pointer;
105
170
  outline: none;
106
171
  transition:
107
172
  color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
108
- border-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
109
173
  background-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
110
174
  }
111
175
 
112
176
  .vmz-ui-table__row-action:hover:not(:disabled) {
177
+ background: var(--vmz-action-primary-soft);
113
178
  color: var(--vmz-action-primary-hover);
114
- border-color: var(--vmz-action-primary-hover);
115
179
  }
116
180
 
117
181
  .vmz-ui-table__row-action:focus-visible {
@@ -121,18 +185,37 @@
121
185
  .vmz-ui-table__row-action:disabled {
122
186
  opacity: 0.55;
123
187
  cursor: not-allowed;
188
+ color: var(--vmz-text-tertiary);
189
+ }
190
+
191
+ .vmz-ui-table tbody tr.vmz-ui-table__state-row:hover {
192
+ background: transparent;
193
+ }
194
+
195
+ .vmz-ui-table__state {
196
+ height: 3rem;
197
+ text-align: center;
198
+ color: var(--vmz-text-tertiary);
199
+ white-space: normal;
200
+ }
201
+
202
+ .vmz-ui-table__state-text {
203
+ margin-left: 0.5rem;
204
+ font-size: 0.875rem;
124
205
  }
125
206
  </style>
126
207
  <script client>
127
208
  /**
128
- * VMZ UI — Table (UI5 Console Surface).
209
+ * VMZ UI — Table (commercial data surface).
129
210
  * Column header click → onSort; row action → onRowAction; permission via rowActionDisabled.
211
+ * Numeric cells right-align automatically; column `align` overrides.
130
212
  */
131
213
  export default class Table {
132
- /** @type {{ id: string, title: string }[]} */
214
+ /** @type {{ id: string, title: string, align?: string }[]} */
133
215
  public columns: {
134
216
  id: string;
135
217
  title: string;
218
+ align?: string;
136
219
  }[] = [];
137
220
  /** @type {{ id: string, cells: string[] }[]} */
138
221
  public rows: {
@@ -141,12 +224,34 @@ export default class Table {
141
224
  }[] = [];
142
225
  public hasRowAction: boolean = false;
143
226
  public rowActionDisabled: boolean = false;
144
- public actionTitle: string = "Action";
145
- public actionLabel: string = "Open";
227
+ public actionTitle: string = "";
228
+ public actionLabel: string = "";
146
229
  public sortKey: string = "";
147
230
  public sortDir: string = "asc";
231
+ public loading: boolean = false;
232
+ public loadingLabel: string = "";
233
+ public loadingText: string = "";
234
+ public emptyText: string = "";
148
235
  public onRowAction: ((rowId: string) => void) | null = null;
149
236
  public onSort: ((columnId: string) => void) | null = null;
237
+ columnAlign(col) {
238
+ return col && col.align ? col.align : "left";
239
+ }
240
+ cellAlign(index, cell) {
241
+ const col = (this.columns || [])[index];
242
+ if (col && col.align) return col.align;
243
+ const text = String(cell == null ? "" : cell).trim();
244
+ if (!text) return "left";
245
+ return /^[-+]?[\d.,%]+$/.test(text) ? "right" : "left";
246
+ }
247
+ columnSpan() {
248
+ const count = (this.columns || []).length + (this.hasRowAction ? 1 : 0);
249
+ return Math.max(1, count);
250
+ }
251
+ sortAria(id) {
252
+ if (id !== this.sortKey) return null;
253
+ return this.sortDir === "desc" ? "descending" : "ascending";
254
+ }
150
255
  onBodyClick(ev) {
151
256
  const t = ev && ev.target;
152
257
  const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-row-action]") : null;
@@ -14,30 +14,43 @@
14
14
  .vmz-ui-tabs {
15
15
  display: flex;
16
16
  flex-direction: column;
17
- gap: 0.625rem;
18
- max-width: 36rem;
17
+ gap: 1rem;
18
+ width: 100%;
19
+ min-width: 0;
19
20
  }
20
21
 
21
22
  .vmz-ui-tabs__list {
22
23
  display: flex;
23
- flex-wrap: wrap;
24
- gap: 0.25rem;
25
- border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
24
+ flex-wrap: nowrap;
25
+ gap: 1.5rem;
26
+ overflow-x: auto;
27
+ border-bottom: 1px solid var(--vmz-line-subtle);
28
+ scrollbar-width: thin;
26
29
  }
27
30
 
28
31
  .vmz-ui-tabs__tab {
29
- font: inherit;
30
- font-size: 0.875rem;
31
- font-weight: 400;
32
- padding: 0.5rem 0;
33
- margin: 0 1rem 0 0;
32
+ display: inline-flex;
33
+ align-items: center;
34
+ height: 2.5rem;
35
+ padding: 0;
36
+ margin: 0;
34
37
  border: 0;
35
38
  border-bottom: 2px solid transparent;
36
39
  border-radius: 0;
37
40
  background: transparent;
38
41
  color: var(--vmz-text-ink);
42
+ font: inherit;
43
+ font-size: 0.875rem;
44
+ font-weight: 400;
45
+ white-space: nowrap;
39
46
  cursor: pointer;
40
47
  outline: none;
48
+ transition: color var(--vmz-motion-control-duration, 150ms)
49
+ var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
50
+ }
51
+
52
+ .vmz-ui-tabs__tab:hover {
53
+ color: var(--vmz-action-primary-background);
41
54
  }
42
55
 
43
56
  .vmz-ui-tabs__tab[aria-selected="true"] {
@@ -48,7 +61,9 @@
48
61
  }
49
62
 
50
63
  .vmz-ui-tabs__tab:focus-visible {
51
- box-shadow: 0 0 0 3px var(--vmz-focus-ring);
64
+ outline: 2px solid var(--vmz-action-primary-background);
65
+ outline-offset: -2px;
66
+ border-radius: var(--vmz-radius-sm, 4px);
52
67
  }
53
68
 
54
69
  .vmz-ui-tabs__panel {
@@ -62,12 +77,12 @@
62
77
  </style>
63
78
  <script client>
64
79
  /**
65
- * VMZ UI — Tabs (UI2).
80
+ * VMZ UI — Tabs.
66
81
  * Parent owns `selected` id; keyboard Left/Right/Home/End on the tablist.
67
82
  * Panel content is the default slot (page chooses which body to show).
68
83
  */
69
84
  export default class Tabs {
70
- public label: string = "Tabs";
85
+ public label: string = "";
71
86
  public selected: string = "";
72
87
  public panelId: string = "";
73
88
  /** @type {{ id: string, title: string, controls?: string }[]} */
@@ -7,32 +7,49 @@
7
7
  .vmz-ui-tag {
8
8
  display: inline-flex;
9
9
  align-items: center;
10
- gap: 0.25rem;
11
- min-height: 1.5rem;
12
- padding: 0 0.5rem;
10
+ gap: var(--vmz-space-1);
11
+ min-height: var(--vmz-space-5);
12
+ max-width: 100%;
13
+ padding: 0 var(--vmz-space-2);
13
14
  border: 1px solid var(--vmz-line-default);
14
- border-radius: var(--vmz-radius-sm, 4px);
15
+ border-radius: var(--vmz-radius-sm);
15
16
  background: var(--vmz-surface-mist);
16
17
  color: var(--vmz-text-steel);
17
18
  font-size: 0.75rem;
18
19
  font-weight: 500;
19
- line-height: 1;
20
+ line-height: 1.6667;
20
21
  white-space: nowrap;
21
22
  }
23
+
22
24
  .vmz-ui-tag[data-status="success"] {
23
25
  color: var(--vmz-status-success-foreground);
24
- background: color-mix(in srgb, var(--vmz-status-success-accent) 12%, var(--vmz-surface-paper));
25
- border-color: color-mix(in srgb, var(--vmz-status-success-accent) 45%, var(--vmz-line-default));
26
+ background: var(--vmz-status-success-background);
27
+ border-color: var(--vmz-status-success-border);
26
28
  }
29
+
27
30
  .vmz-ui-tag[data-status="warning"] {
28
31
  color: var(--vmz-status-warning-foreground);
29
- background: color-mix(in srgb, var(--vmz-status-warning-accent) 12%, var(--vmz-surface-paper));
30
- border-color: color-mix(in srgb, var(--vmz-status-warning-accent) 45%, var(--vmz-line-default));
32
+ background: var(--vmz-status-warning-background);
33
+ border-color: var(--vmz-status-warning-border);
31
34
  }
32
- .vmz-ui-tag[data-status="error"] {
35
+
36
+ .vmz-ui-tag[data-status="error"],
37
+ .vmz-ui-tag[data-status="danger"] {
33
38
  color: var(--vmz-status-danger-foreground);
34
- background: color-mix(in srgb, var(--vmz-status-danger-accent) 12%, var(--vmz-surface-paper));
35
- border-color: color-mix(in srgb, var(--vmz-status-danger-accent) 45%, var(--vmz-line-default));
39
+ background: var(--vmz-status-danger-background);
40
+ border-color: var(--vmz-status-danger-border);
41
+ }
42
+
43
+ .vmz-ui-tag[data-status="info"] {
44
+ color: var(--vmz-status-info-foreground);
45
+ background: var(--vmz-status-info-background);
46
+ border-color: var(--vmz-status-info-border);
47
+ }
48
+
49
+ .vmz-ui-tag[data-status="brand"] {
50
+ color: var(--vmz-action-primary-active);
51
+ background: var(--vmz-action-primary-soft);
52
+ border-color: var(--vmz-action-primary-hover);
36
53
  }
37
54
  </style>
38
55
  <script client>