@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
@@ -3,7 +3,7 @@
3
3
  <label v-if="label" :for="controlId" class="vmz-ui-textarea__label">
4
4
  {{ label }}
5
5
  </label>
6
- <textarea :id="controlId" :rows="rows" :value="value" :disabled="disabled" :readonly="readonly" :aria-invalid="error ? 'true' : 'false'" :aria-describedby="describedBy" @input="onDomInput" class="vmz-ui-textarea__control" />
6
+ <textarea :id="controlId" :rows="rows" :value="value" :placeholder="placeholder" :disabled="disabled" :readonly="readonly" :aria-invalid="error ? 'true' : 'false'" :aria-describedby="describedBy" @input="onDomInput" class="vmz-ui-textarea__control" />
7
7
  <p v-if="description" :id="descriptionId" class="vmz-ui-textarea__description">
8
8
  {{ description }}
9
9
  </p>
@@ -16,7 +16,7 @@
16
16
  .vmz-ui-textarea {
17
17
  display: flex;
18
18
  flex-direction: column;
19
- gap: 0.5rem;
19
+ gap: var(--vmz-space-2);
20
20
  max-width: none;
21
21
  width: 100%;
22
22
  }
@@ -25,32 +25,31 @@
25
25
  font: inherit;
26
26
  font-weight: 500;
27
27
  font-size: 0.875rem;
28
+ line-height: 1.5715;
29
+ color: var(--vmz-text-ink);
28
30
  }
29
31
 
30
32
  .vmz-ui-textarea__control {
31
33
  box-sizing: border-box;
32
34
  width: 100%;
33
- font-family: var(--vmz-font-sans, inherit);
35
+ font-family: inherit;
34
36
  font-size: 0.875rem;
35
37
  line-height: 1.5715;
36
- min-height: 5.5rem;
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);
38
+ padding: var(--vmz-density-control-padding-y) var(--vmz-density-control-padding-x);
39
+ border: 1px solid var(--vmz-line-default);
40
+ border-radius: var(--vmz-radius-lg);
41
+ background: var(--vmz-surface-paper);
42
+ color: var(--vmz-text-ink);
43
43
  outline: none;
44
44
  resize: vertical;
45
45
  transition:
46
- border-color var(--vmz-motion-control-duration, 120ms)
47
- var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
48
- box-shadow var(--vmz-motion-control-duration, 120ms)
49
- var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
46
+ border-color var(--vmz-motion-control-duration) var(--vmz-motion-control-easing),
47
+ box-shadow var(--vmz-motion-control-duration) var(--vmz-motion-control-easing),
48
+ background-color var(--vmz-motion-control-duration) var(--vmz-motion-control-easing);
50
49
  }
51
50
 
52
51
  .vmz-ui-textarea__control::placeholder {
53
- color: var(--vmz-text-steel, #8c8c8c);
52
+ color: var(--vmz-text-tertiary);
54
53
  opacity: 1;
55
54
  }
56
55
 
@@ -58,8 +57,13 @@
58
57
  border-color: var(--vmz-status-danger-accent);
59
58
  }
60
59
 
60
+ .vmz-ui-textarea[data-invalid="true"] .vmz-ui-textarea__control:hover:not(:disabled),
61
+ .vmz-ui-textarea[data-invalid="true"] .vmz-ui-textarea__control:focus-visible {
62
+ border-color: var(--vmz-status-danger-foreground);
63
+ }
64
+
61
65
  .vmz-ui-textarea__control:hover:not(:disabled):not(:focus-visible) {
62
- border-color: var(--vmz-action-primary-background);
66
+ border-color: var(--vmz-action-primary-hover);
63
67
  }
64
68
 
65
69
  .vmz-ui-textarea__control:focus-visible {
@@ -67,28 +71,30 @@
67
71
  box-shadow: 0 0 0 3px var(--vmz-focus-ring);
68
72
  }
69
73
 
70
- .vmz-ui-textarea__control:disabled,
71
- .vmz-ui-textarea__control:read-only {
72
- cursor: default;
73
- background: var(--vmz-surface-mist, #fafafa);
74
- }
75
-
76
74
  .vmz-ui-textarea__control:disabled {
75
+ opacity: 0.6;
76
+ color: var(--vmz-text-muted);
77
77
  cursor: not-allowed;
78
- opacity: 0.55;
78
+ background: var(--vmz-surface-mist);
79
+ }
80
+
81
+ .vmz-ui-textarea__control:read-only {
82
+ cursor: default;
83
+ background: var(--vmz-surface-mist);
79
84
  }
80
85
 
81
86
  .vmz-ui-textarea__description {
82
87
  margin: 0;
83
- color: var(--vmz-text-steel, #8c8c8c);
88
+ color: var(--vmz-text-steel);
84
89
  font-size: 0.875rem;
85
- line-height: 1.4;
90
+ line-height: 1.5715;
86
91
  }
87
92
 
88
93
  .vmz-ui-textarea__error {
89
94
  margin: 0;
90
95
  color: var(--vmz-status-danger-foreground);
91
96
  font-size: 0.875rem;
97
+ line-height: 1.5715;
92
98
  }
93
99
  </style>
94
100
  <script client>
@@ -101,6 +107,7 @@ export default class TextArea {
101
107
  public description: string = "";
102
108
  public error: string = "";
103
109
  public value: string = "";
110
+ public placeholder: string = "";
104
111
  public rows: number = 4;
105
112
  public disabled: boolean = false;
106
113
  public readonly: boolean = false;
@@ -1,15 +1,18 @@
1
1
  <template>
2
2
  <ol data-vmz-ui="timeline" :aria-label="label" class="vmz-ui-timeline">
3
3
  <li v-for="item in items" :key="item.id" :data-vmz-audit="item.id" class="vmz-ui-timeline__item">
4
- <p class="vmz-ui-timeline__time">
5
- {{ item.time }}
6
- </p>
7
- <p class="vmz-ui-timeline__title">
8
- {{ item.title }}
9
- </p>
10
- <p v-if="item.detail" class="vmz-ui-timeline__detail">
11
- {{ item.detail }}
12
- </p>
4
+ <span aria-hidden="true" class="vmz-ui-timeline__marker" />
5
+ <div class="vmz-ui-timeline__content">
6
+ <p class="vmz-ui-timeline__time">
7
+ {{ item.time }}
8
+ </p>
9
+ <p class="vmz-ui-timeline__title">
10
+ {{ item.title }}
11
+ </p>
12
+ <p v-if="item.detail" class="vmz-ui-timeline__detail">
13
+ {{ item.detail }}
14
+ </p>
15
+ </div>
13
16
  </li>
14
17
  </ol>
15
18
  </template>
@@ -20,42 +23,74 @@
20
23
  list-style: none;
21
24
  display: flex;
22
25
  flex-direction: column;
23
- gap: var(--vmz-density-compact-gap);
24
- border-left: 2px solid var(--vmz-line-default);
26
+ color: var(--vmz-text-ink);
25
27
  }
26
28
 
27
29
  .vmz-ui-timeline__item {
28
- position: relative;
29
- padding: 0.125rem 0 0.125rem 0.85rem;
30
+ display: flex;
31
+ align-items: flex-start;
32
+ gap: var(--vmz-space-3, 0.75rem);
33
+ padding: var(--vmz-space-3, 0.75rem) 0;
34
+ border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
30
35
  }
31
36
 
32
- .vmz-ui-timeline__item::before {
33
- content: "";
34
- position: absolute;
35
- left: -0.35rem;
36
- top: 0.45rem;
37
- width: 0.55rem;
38
- height: 0.55rem;
39
- border-radius: 999px;
37
+ .vmz-ui-timeline__item:first-child {
38
+ padding-top: 0;
39
+ }
40
+
41
+ .vmz-ui-timeline__item:last-child {
42
+ padding-bottom: 0;
43
+ border-bottom: none;
44
+ }
45
+
46
+ .vmz-ui-timeline__marker {
47
+ flex: 0 0 auto;
48
+ width: 0.5rem;
49
+ height: 0.5rem;
50
+ margin-top: 0.45rem;
51
+ border-radius: var(--vmz-radius-pill, 999px);
40
52
  background: var(--vmz-action-primary-background);
53
+ box-shadow: 0 0 0 3px var(--vmz-action-primary-soft, var(--vmz-surface-mist));
54
+ }
55
+
56
+ .vmz-ui-timeline__content {
57
+ flex: 1 1 auto;
58
+ display: flex;
59
+ flex-direction: column;
60
+ gap: var(--vmz-space-1, 0.25rem);
61
+ min-width: 0;
41
62
  }
42
63
 
43
64
  .vmz-ui-timeline__time {
44
65
  margin: 0;
45
- font-size: 0.85em;
46
- opacity: 0.75;
47
- color: var(--vmz-action-primary-active);
66
+ font-size: 0.8125rem;
67
+ line-height: 1.4;
68
+ color: var(--vmz-text-tertiary);
48
69
  }
49
70
 
50
71
  .vmz-ui-timeline__title {
51
- margin: 0.125rem 0 0;
52
- font-weight: 700;
53
- color: var(--vmz-action-primary-active);
72
+ margin: 0;
73
+ font-size: 0.875rem;
74
+ font-weight: 600;
75
+ line-height: 1.5;
76
+ color: var(--vmz-text-ink);
54
77
  }
55
78
 
56
79
  .vmz-ui-timeline__detail {
57
- margin: 0.2rem 0 0;
58
- opacity: 0.9;
80
+ margin: 0;
81
+ font-size: 0.875rem;
82
+ line-height: 1.55;
83
+ color: var(--vmz-text-steel);
84
+ }
85
+
86
+ :where([data-density="compact"]) .vmz-ui-timeline__item {
87
+ gap: var(--vmz-density-compact-gap);
88
+ padding-block: var(--vmz-density-compact-gap);
89
+ }
90
+
91
+ :where([data-density="dense"]) .vmz-ui-timeline__item {
92
+ gap: var(--vmz-density-dense-gap);
93
+ padding-block: var(--vmz-density-dense-gap);
59
94
  }
60
95
  </style>
61
96
  <script client>
@@ -64,7 +99,7 @@
64
99
  * Parent owns audit items; stable ids for list identity.
65
100
  */
66
101
  export default class Timeline {
67
- public label: string = "Audit";
102
+ public label: string = "";
68
103
  /** @type {{ id: string, time: string, title: string, detail?: string }[]} */
69
104
  public items: {
70
105
  id: string;
@@ -1,10 +1,10 @@
1
1
  <template>
2
- <aside data-vmz-ui="toc" :aria-label="label" class="vmz-ui-toc">
3
- <p class="vmz-ui-toc__title">
2
+ <aside data-vmz-ui="toc" :aria-label="label ? label : null" class="vmz-ui-toc">
3
+ <p v-if="label" class="vmz-ui-toc__title">
4
4
  {{ label }}
5
5
  </p>
6
6
  <nav class="vmz-ui-toc__nav">
7
- <a v-for="item in items" :key="item.id" :href="item.href" :data-vmz-toc="item.id" class="vmz-ui-toc__link">
7
+ <a v-for="item in items" :key="item.id" :href="item.href" :data-vmz-toc="item.id" :data-level="item.level ? item.level : 1" :data-active="item.id === activeId ? 'true' : null" :aria-current="item.id === activeId ? 'location' : null" class="vmz-ui-toc__link">
8
8
  {{ item.title }}
9
9
  </a>
10
10
  </nav>
@@ -12,63 +12,79 @@
12
12
  </template>
13
13
  <style>
14
14
  .vmz-ui-toc {
15
+ position: sticky;
16
+ top: 5rem;
15
17
  display: flex;
16
18
  flex-direction: column;
17
- gap: 0.5rem;
18
- min-width: 11rem;
19
- padding: 0.85rem 0.75rem;
20
- border: 1px solid var(--vmz-line-default);
21
- border-radius: var(--vmz-radius-md, 6px);
22
- background: var(--vmz-surface-mist);
19
+ gap: var(--vmz-space-2, 0.5rem);
20
+ min-width: 0;
21
+ max-height: calc(100dvh - 6rem);
22
+ overflow-y: auto;
23
23
  color: var(--vmz-text-ink);
24
24
  }
25
-
26
25
  .vmz-ui-toc__title {
27
26
  margin: 0;
28
- font-family: var(--vmz-font-display);
29
- font-size: 0.8rem;
30
- font-weight: 700;
31
- letter-spacing: 0.04em;
32
- text-transform: uppercase;
33
- color: var(--vmz-text-steel);
27
+ padding: 0 var(--vmz-space-3, 0.75rem);
28
+ color: var(--vmz-text-tertiary);
29
+ font-size: 0.75rem;
30
+ font-weight: 600;
31
+ line-height: 1.25rem;
34
32
  }
35
-
36
33
  .vmz-ui-toc__nav {
37
34
  display: flex;
38
35
  flex-direction: column;
39
- gap: 0.375rem;
40
36
  }
41
-
42
37
  .vmz-ui-toc__link {
43
- font: inherit;
44
- font-weight: 600;
45
- font-size: 0.92rem;
38
+ display: block;
39
+ padding: 0.375rem var(--vmz-space-3, 0.75rem);
40
+ border-left: 2px solid transparent;
41
+ color: var(--vmz-text-steel);
42
+ font-size: 0.875rem;
43
+ font-weight: 400;
44
+ line-height: 1.5;
46
45
  text-decoration: none;
47
- color: var(--vmz-action-primary-active);
48
46
  outline: none;
47
+ transition:
48
+ color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1)),
49
+ border-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
49
50
  }
50
-
51
51
  .vmz-ui-toc__link:hover {
52
52
  color: var(--vmz-action-primary-background);
53
53
  }
54
-
54
+ .vmz-ui-toc__link[data-active="true"] {
55
+ color: var(--vmz-action-primary-background);
56
+ font-weight: 500;
57
+ border-left-color: var(--vmz-action-primary-background);
58
+ }
59
+ .vmz-ui-toc__link[data-level="2"] {
60
+ padding-left: var(--vmz-space-5, 1.5rem);
61
+ }
62
+ .vmz-ui-toc__link[data-level="3"] {
63
+ padding-left: 2.25rem;
64
+ }
65
+ .vmz-ui-toc__link[data-level="4"] {
66
+ padding-left: 3rem;
67
+ }
55
68
  .vmz-ui-toc__link:focus-visible {
69
+ border-radius: var(--vmz-radius-sm, 4px);
56
70
  box-shadow: 0 0 0 3px var(--vmz-focus-ring);
57
- border-radius: var(--vmz-radius-md, 6px);
58
71
  }
59
72
  </style>
60
73
  <script client>
61
74
  /**
62
75
  * VMZ UI — Toc (Document/Product composition).
63
- * In-page outline; parent supplies items. Readable without JS.
76
+ * Sticky in-page outline; parent supplies items and the current anchor.
77
+ * Readable without JS.
64
78
  */
65
79
  export default class Toc {
66
- public label: string = "On this page";
67
- /** @type {{ id: string, title: string, href: string }[]} */
80
+ public label: string = "";
81
+ public activeId: string = "";
82
+ /** @type {{ id: string, title: string, href: string, level?: number }[]} */
68
83
  public items: {
69
84
  id: string;
70
85
  title: string;
71
86
  href: string;
87
+ level?: number;
72
88
  }[] = [];
73
89
  }
74
90
  </script>
@@ -4,50 +4,99 @@
4
4
  <slot />
5
5
  </div>
6
6
  <div v-if="open" role="tooltip" :id="tooltipId" data-vmz-tooltip="bubble" class="vmz-ui-tooltip__bubble">
7
+ <span aria-hidden="true" data-vmz-tooltip="arrow" class="vmz-ui-tooltip__arrow" />
7
8
  {{ content }}
8
9
  </div>
9
10
  </div>
10
11
  </template>
11
12
  <style>
12
13
  .vmz-ui-tooltip {
13
- position: relative;
14
14
  display: inline-flex;
15
15
  flex-direction: column;
16
16
  align-items: flex-start;
17
- gap: 0.375rem;
18
- max-width: 28rem;
17
+ gap: 6px;
19
18
  }
20
19
 
21
20
  .vmz-ui-tooltip__trigger {
22
21
  display: inline-flex;
23
22
  align-items: center;
24
- gap: 0.375rem;
23
+ min-height: var(--vmz-control-height, 32px);
24
+ border-radius: var(--vmz-radius-sm);
25
+ }
26
+
27
+ .vmz-ui-tooltip__trigger :where(button, a, [tabindex]):focus-visible {
28
+ box-shadow: 0 0 0 3px var(--vmz-focus-ring);
25
29
  }
26
30
 
27
31
  .vmz-ui-tooltip__bubble {
28
- box-sizing: border-box;
32
+ position: relative;
33
+ display: inline-flex;
34
+ align-items: center;
29
35
  max-width: 18rem;
30
- padding: 0.375rem 0.625rem;
31
- border: 1px solid var(--vmz-line-default);
32
- border-radius: var(--vmz-radius-md, 6px);
33
- background: var(--vmz-surface-paper);
34
- color: var(--vmz-text-ink);
35
- font-size: 0.88rem;
36
- line-height: 1.4;
37
- box-shadow: 0 1px 0 color-mix(in srgb, var(--vmz-line-default) 70%, transparent);
38
- }
39
-
40
- .vmz-ui-tooltip[data-open="true"]
41
- .vmz-ui-tooltip__trigger
42
- :where(button, a, [tabindex]):focus-visible {
43
- box-shadow: 0 0 0 3px var(--vmz-focus-ring);
36
+ padding: 6px 10px;
37
+ border-radius: var(--vmz-radius-lg);
38
+ background: var(--vmz-surface-ink);
39
+ color: var(--vmz-text-on-ink);
40
+ font-size: 14px;
41
+ line-height: 22px;
42
+ box-shadow: var(--vmz-shadow-popup);
43
+ animation: vmz-ui-tooltip-in var(--vmz-motion-overlay-duration)
44
+ var(--vmz-motion-overlay-easing);
45
+ }
46
+
47
+ .vmz-ui-tooltip__arrow {
48
+ position: absolute;
49
+ top: -4px;
50
+ left: 14px;
51
+ width: 8px;
52
+ height: 8px;
53
+ border-radius: 1px 0 0 0;
54
+ background: var(--vmz-surface-ink);
55
+ transform: rotate(45deg);
56
+ }
57
+
58
+ .vmz-ui-tooltip__bubble :where(a) {
59
+ color: inherit;
60
+ text-decoration: underline;
61
+ }
62
+
63
+ @keyframes vmz-ui-tooltip-in {
64
+ from {
65
+ opacity: 0;
66
+ transform: translateY(-4px);
67
+ }
68
+ to {
69
+ opacity: 1;
70
+ transform: none;
71
+ }
72
+ }
73
+
74
+ @media (prefers-contrast: more) {
75
+ .vmz-ui-tooltip__bubble {
76
+ border: 1px solid var(--vmz-line-default);
77
+ background: var(--vmz-surface-paper);
78
+ color: var(--vmz-text-ink);
79
+ }
80
+
81
+ .vmz-ui-tooltip__arrow {
82
+ border-top: 1px solid var(--vmz-line-default);
83
+ border-left: 1px solid var(--vmz-line-default);
84
+ background: var(--vmz-surface-paper);
85
+ }
86
+ }
87
+
88
+ @media (prefers-reduced-motion: reduce) {
89
+ .vmz-ui-tooltip__bubble {
90
+ animation: none;
91
+ }
44
92
  }
45
93
  </style>
46
94
  <script client>
47
95
  /**
48
- * VMZ UI — Tooltip.
49
- * Parent owns live `open` + content string. Not a second overlay runtime;
96
+ * VMZ UI — Tooltip (UI1).
97
+ * Hover/focus-visible adoption + tooltipId/aria-describedby on the trigger.
50
98
  * Escape/outside policies stay with richer Popover/Dialog when needed.
99
+ * The parent owns live `open`; `openAttr` mirrors it for CSS and automation.
51
100
  */
52
101
  export default class Tooltip {
53
102
  public open: boolean = false;
@@ -4,9 +4,9 @@
4
4
  {{ label }}
5
5
  </p>
6
6
  <ul role="tree" :aria-labelledby="labelId" :aria-label="label" @click="onTreeClick" :onKeyDown="onTreeKeyDown" class="vmz-ui-tree__items">
7
- <li v-for="item in items" :key="item.id" role="treeitem" :id="item.id" :data-vmz-tree-item="item.id" :data-depth="item.depth" :aria-selected="selected === item.id ? 'true' : 'false'" :aria-expanded="item.expandedAttr" :tabindex="selected === item.id ? 0 : -1" class="vmz-ui-tree__item" :style="item.padStyle">
7
+ <li v-for="item in items" :key="item.id" role="treeitem" :id="item.id" :data-vmz-tree-item="item.id" :data-depth="item.depth" :aria-selected="selected === item.id ? 'true' : 'false'" :aria-expanded="item.expandedAttr" :tabindex="selected === item.id ? 0 : -1" class="vmz-ui-tree__item" :style="rowStyle(item)">
8
8
  <button v-if="item.expandable" type="button" :data-vmz-tree-twist="item.id" :aria-label="item.twistLabel" tabindex="-1" class="vmz-ui-tree__twist">
9
- {{ item.expanded ? '−' : '+' }}
9
+ <Icon :name="item.expanded ? 'action.chevron-down' : 'action.chevron-right'" size="sm" decorative />
10
10
  </button>
11
11
  <span v-if="!item.expandable" aria-hidden="true" class="vmz-ui-tree__twist-spacer" />
12
12
  <span :data-vmz-tree-title="item.id" class="vmz-ui-tree__title">
@@ -20,22 +20,24 @@
20
20
  .vmz-ui-tree {
21
21
  display: flex;
22
22
  flex-direction: column;
23
- gap: 0.375rem;
23
+ gap: var(--vmz-space-2, 0.5rem);
24
24
  max-width: 28rem;
25
25
  }
26
26
 
27
27
  .vmz-ui-tree__label {
28
28
  margin: 0;
29
+ font-size: 0.875rem;
29
30
  font-weight: 600;
30
31
  color: var(--vmz-text-ink);
31
32
  }
32
33
 
33
34
  .vmz-ui-tree__items {
34
35
  margin: 0;
35
- padding: 0.25rem;
36
+ padding: var(--vmz-space-1, 0.25rem);
36
37
  list-style: none;
37
- border: 1px solid var(--vmz-line-default);
38
- border-radius: var(--vmz-radius-md, 6px);
38
+ overflow: hidden;
39
+ border: 1px solid var(--vmz-line-default, var(--vmz-line-strong));
40
+ border-radius: var(--vmz-radius-lg, 8px);
39
41
  background: var(--vmz-surface-paper);
40
42
  color: var(--vmz-text-ink);
41
43
  }
@@ -43,57 +45,72 @@
43
45
  .vmz-ui-tree__item {
44
46
  display: flex;
45
47
  align-items: center;
46
- gap: 0.375rem;
47
- padding: var(--vmz-density-compact-padding-y) var(--vmz-density-compact-padding-x);
48
+ gap: var(--vmz-space-1, 0.25rem);
49
+ min-height: var(--vmz-control-height, 2rem);
50
+ padding-inline-end: var(--vmz-space-3, 0.75rem);
48
51
  border-radius: var(--vmz-radius-md, 6px);
52
+ font-size: 0.875rem;
53
+ line-height: 1.5;
49
54
  cursor: pointer;
50
55
  outline: none;
56
+ transition:
57
+ background-color var(--vmz-motion-control-duration, 120ms) var(--vmz-motion-control-easing, cubic-bezier(0.2, 0, 0, 1)),
58
+ color var(--vmz-motion-control-duration, 120ms) var(--vmz-motion-control-easing, cubic-bezier(0.2, 0, 0, 1));
51
59
  }
52
60
 
53
- .vmz-ui-tree__item[aria-selected="true"] {
54
- background: var(--vmz-action-primary-background);
55
- color: var(--vmz-action-primary-foreground);
61
+ .vmz-ui-tree__item:hover {
62
+ background: var(--vmz-surface-mist);
56
63
  }
57
64
 
58
- .vmz-ui-tree__item:hover {
59
- background: color-mix(in srgb, var(--vmz-action-primary-background) 14%, transparent);
65
+ .vmz-ui-tree__item[aria-selected="true"] {
66
+ background: var(--vmz-action-primary-soft, var(--vmz-surface-mist));
67
+ color: var(--vmz-action-primary-active);
68
+ font-weight: 500;
60
69
  }
61
70
 
62
71
  .vmz-ui-tree__item[aria-selected="true"]:hover {
63
- background: var(--vmz-action-primary-hover);
72
+ background: var(--vmz-action-primary-soft, var(--vmz-surface-mist));
64
73
  }
65
74
 
66
75
  .vmz-ui-tree__item:focus-visible {
67
- box-shadow: 0 0 0 3px var(--vmz-focus-ring);
76
+ box-shadow: inset 0 0 0 2px var(--vmz-focus-ring);
68
77
  }
69
78
 
70
79
  .vmz-ui-tree__twist {
71
80
  flex: 0 0 auto;
72
- width: 1.35rem;
73
- height: 1.35rem;
81
+ display: inline-flex;
82
+ align-items: center;
83
+ justify-content: center;
84
+ width: 1.25rem;
85
+ height: 1.25rem;
74
86
  padding: 0;
75
- border: 1px solid var(--vmz-line-default);
76
- border-radius: var(--vmz-radius-md, 6px);
77
- background: var(--vmz-surface-mist);
78
- color: inherit;
87
+ border: none;
88
+ border-radius: var(--vmz-radius-sm, 4px);
89
+ background: transparent;
90
+ color: var(--vmz-text-tertiary);
79
91
  font: inherit;
80
- font-weight: 700;
81
92
  line-height: 1;
82
93
  cursor: pointer;
83
94
  outline: none;
84
95
  }
85
96
 
97
+ .vmz-ui-tree__twist:hover {
98
+ color: var(--vmz-text-ink);
99
+ background: var(--vmz-surface-mist);
100
+ }
101
+
86
102
  .vmz-ui-tree__twist:focus-visible {
87
- box-shadow: 0 0 0 3px var(--vmz-focus-ring);
103
+ box-shadow: inset 0 0 0 2px var(--vmz-focus-ring);
88
104
  }
89
105
 
90
106
  .vmz-ui-tree__twist-spacer {
91
107
  flex: 0 0 auto;
92
- width: 1.35rem;
108
+ width: 1.25rem;
93
109
  }
94
110
 
95
111
  .vmz-ui-tree__title {
96
112
  flex: 1 1 auto;
113
+ min-width: 0;
97
114
  }
98
115
  </style>
99
116
  <script client>
@@ -103,7 +120,7 @@
103
120
  * Not virtualized / not DataGrid tree mode.
104
121
  */
105
122
  export default class Tree {
106
- public label: string = "Tree";
123
+ public label: string = "";
107
124
  public labelId: string = "vmz-tree-label";
108
125
  public selected: string = "";
109
126
  /**
@@ -136,6 +153,12 @@ export default class Tree {
136
153
  toggle(id) {
137
154
  if (typeof this.onToggle === "function") this.onToggle(id);
138
155
  }
156
+ rowStyle(item) {
157
+ const provided = String(item?.padStyle || "").trim();
158
+ if (provided) return provided;
159
+ const depth = Math.max(0, Number(item?.depth) || 0);
160
+ return `padding-inline-start: ${0.75 + depth}rem`;
161
+ }
139
162
  onTreeClick(ev) {
140
163
  const t = ev && ev.target;
141
164
  const twist =