@vmz/ui 0.2.3 → 0.2.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 (63) hide show
  1. package/package.json +1 -1
  2. package/src/components/Accordion.vmz +46 -63
  3. package/src/components/Alert.vmz +35 -38
  4. package/src/components/AppShell.vmz +92 -75
  5. package/src/components/Autocomplete.vmz +91 -119
  6. package/src/components/Avatar.vmz +37 -8
  7. package/src/components/Badge.vmz +29 -23
  8. package/src/components/Breadcrumb.vmz +36 -34
  9. package/src/components/BulkActions.vmz +22 -28
  10. package/src/components/Button.vmz +139 -121
  11. package/src/components/Callout.vmz +33 -36
  12. package/src/components/Card.vmz +68 -51
  13. package/src/components/Checkbox.vmz +28 -47
  14. package/src/components/CodeBlock.vmz +31 -25
  15. package/src/components/Collapse.vmz +34 -16
  16. package/src/components/ConsoleShell.vmz +107 -110
  17. package/src/components/Content.vmz +8 -4
  18. package/src/components/DataTable.vmz +124 -150
  19. package/src/components/DatePicker.vmz +526 -521
  20. package/src/components/Dialog.vmz +408 -327
  21. package/src/components/Divider.vmz +38 -11
  22. package/src/components/Drawer.vmz +325 -318
  23. package/src/components/Dropdown.vmz +53 -19
  24. package/src/components/Empty.vmz +23 -21
  25. package/src/components/Field.vmz +77 -83
  26. package/src/components/FilterBar.vmz +8 -10
  27. package/src/components/Flex.vmz +21 -6
  28. package/src/components/Footer.vmz +9 -4
  29. package/src/components/Form.vmz +35 -46
  30. package/src/components/FormItem.vmz +44 -43
  31. package/src/components/Grid.vmz +22 -6
  32. package/src/components/Header.vmz +11 -4
  33. package/src/components/Icon.vmz +43 -44
  34. package/src/components/Layout.vmz +9 -4
  35. package/src/components/Link.vmz +17 -25
  36. package/src/components/List.vmz +62 -75
  37. package/src/components/Menu.vmz +182 -197
  38. package/src/components/Notification.vmz +35 -38
  39. package/src/components/Pagination.vmz +37 -47
  40. package/src/components/Popover.vmz +157 -171
  41. package/src/components/Progress.vmz +36 -10
  42. package/src/components/Prose.vmz +29 -31
  43. package/src/components/QueryForm.vmz +14 -22
  44. package/src/components/RadioGroup.vmz +70 -84
  45. package/src/components/Result.vmz +36 -38
  46. package/src/components/Segmented.vmz +43 -22
  47. package/src/components/Select.vmz +404 -411
  48. package/src/components/Sider.vmz +21 -4
  49. package/src/components/Skeleton.vmz +34 -42
  50. package/src/components/Space.vmz +24 -9
  51. package/src/components/Spinner.vmz +17 -23
  52. package/src/components/Steps.vmz +48 -51
  53. package/src/components/Switch.vmz +50 -60
  54. package/src/components/Table.vmz +81 -84
  55. package/src/components/Tabs.vmz +66 -88
  56. package/src/components/Tag.vmz +36 -11
  57. package/src/components/TextArea.vmz +73 -77
  58. package/src/components/Timeline.vmz +36 -32
  59. package/src/components/Toc.vmz +31 -37
  60. package/src/components/Tooltip.vmz +25 -35
  61. package/src/components/Tree.vmz +127 -144
  62. package/src/components/Typography.vmz +33 -7
  63. package/src/components/Upload.vmz +1135 -1057
@@ -1,15 +1,20 @@
1
1
  <template>
2
- <ol class="vmz-ui-timeline" data-vmz-ui="timeline" :aria-label="label">
3
- <li v-for="item in items" :key="item.id" class="vmz-ui-timeline__item" :data-vmz-audit="item.id">
4
- <p class="vmz-ui-timeline__time">{{ item.time }}</p>
5
- <p class="vmz-ui-timeline__title">{{ item.title }}</p>
6
- <p v-if="item.detail" class="vmz-ui-timeline__detail">{{ item.detail }}</p>
7
- </li>
2
+ <ol data-vmz-ui="timeline" :aria-label="label" class="vmz-ui-timeline">
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>
13
+ </li>
8
14
  </ol>
9
15
  </template>
10
-
11
16
  <style>
12
- .vmz-ui-timeline {
17
+ .vmz-ui-timeline {
13
18
  margin: 0;
14
19
  padding: 0;
15
20
  list-style: none;
@@ -17,15 +22,15 @@
17
22
  flex-direction: column;
18
23
  gap: var(--vmz-density-compact-gap);
19
24
  border-left: 2px solid var(--vmz-line-default);
20
- }
25
+ }
21
26
 
22
- .vmz-ui-timeline__item {
27
+ .vmz-ui-timeline__item {
23
28
  position: relative;
24
29
  padding: 0.15rem 0 0.15rem 0.85rem;
25
- }
30
+ }
26
31
 
27
- .vmz-ui-timeline__item::before {
28
- content: '';
32
+ .vmz-ui-timeline__item::before {
33
+ content: "";
29
34
  position: absolute;
30
35
  left: -0.35rem;
31
36
  top: 0.45rem;
@@ -33,40 +38,39 @@
33
38
  height: 0.55rem;
34
39
  border-radius: 999px;
35
40
  background: var(--vmz-action-primary-background);
36
- }
41
+ }
37
42
 
38
- .vmz-ui-timeline__time {
43
+ .vmz-ui-timeline__time {
39
44
  margin: 0;
40
45
  font-size: 0.85em;
41
46
  opacity: 0.75;
42
47
  color: var(--vmz-action-primary-active);
43
- }
48
+ }
44
49
 
45
- .vmz-ui-timeline__title {
50
+ .vmz-ui-timeline__title {
46
51
  margin: 0.15rem 0 0;
47
52
  font-weight: 700;
48
53
  color: var(--vmz-action-primary-active);
49
- }
54
+ }
50
55
 
51
- .vmz-ui-timeline__detail {
56
+ .vmz-ui-timeline__detail {
52
57
  margin: 0.2rem 0 0;
53
58
  opacity: 0.9;
54
- }
59
+ }
55
60
  </style>
56
-
57
61
  <script client>
58
62
  /**
59
- * VMZ UI — Timeline (UI5 Console audit trail).
60
- * Parent owns audit items; stable ids for list identity.
61
- */
63
+ * VMZ UI — Timeline (UI5 Console audit trail).
64
+ * Parent owns audit items; stable ids for list identity.
65
+ */
62
66
  export default class Timeline {
63
- public label: string = "Audit";
64
- /** @type {{ id: string, time: string, title: string, detail?: string }[]} */
65
- public items: {
66
- id: string;
67
- time: string;
68
- title: string;
69
- detail?: string;
70
- }[] = [];
67
+ public label: string = "Audit";
68
+ /** @type {{ id: string, time: string, title: string, detail?: string }[]} */
69
+ public items: {
70
+ id: string;
71
+ time: string;
72
+ title: string;
73
+ detail?: string;
74
+ }[] = [];
71
75
  }
72
76
  </script>
@@ -1,22 +1,17 @@
1
1
  <template>
2
- <aside class="vmz-ui-toc" data-vmz-ui="toc" :aria-label="label">
3
- <p class="vmz-ui-toc__title">{{ label }}</p>
4
- <nav class="vmz-ui-toc__nav">
5
- <a
6
- v-for="item in items"
7
- :key="item.id"
8
- class="vmz-ui-toc__link"
9
- :href="item.href"
10
- :data-vmz-toc="item.id"
11
- >
12
- {{ item.title }}
13
- </a>
14
- </nav>
2
+ <aside data-vmz-ui="toc" :aria-label="label" class="vmz-ui-toc">
3
+ <p class="vmz-ui-toc__title">
4
+ {{ label }}
5
+ </p>
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">
8
+ {{ item.title }}
9
+ </a>
10
+ </nav>
15
11
  </aside>
16
12
  </template>
17
-
18
13
  <style>
19
- .vmz-ui-toc {
14
+ .vmz-ui-toc {
20
15
  display: flex;
21
16
  flex-direction: column;
22
17
  gap: 0.55rem;
@@ -26,9 +21,9 @@
26
21
  border-radius: var(--vmz-radius-md, 6px);
27
22
  background: var(--vmz-surface-mist);
28
23
  color: var(--vmz-text-ink);
29
- }
24
+ }
30
25
 
31
- .vmz-ui-toc__title {
26
+ .vmz-ui-toc__title {
32
27
  margin: 0;
33
28
  font-family: var(--vmz-font-display);
34
29
  font-size: 0.8rem;
@@ -36,45 +31,44 @@
36
31
  letter-spacing: 0.04em;
37
32
  text-transform: uppercase;
38
33
  color: var(--vmz-text-steel);
39
- }
34
+ }
40
35
 
41
- .vmz-ui-toc__nav {
36
+ .vmz-ui-toc__nav {
42
37
  display: flex;
43
38
  flex-direction: column;
44
39
  gap: 0.35rem;
45
- }
40
+ }
46
41
 
47
- .vmz-ui-toc__link {
42
+ .vmz-ui-toc__link {
48
43
  font: inherit;
49
44
  font-weight: 600;
50
45
  font-size: 0.92rem;
51
46
  text-decoration: none;
52
47
  color: var(--vmz-action-primary-active);
53
48
  outline: none;
54
- }
49
+ }
55
50
 
56
- .vmz-ui-toc__link:hover {
51
+ .vmz-ui-toc__link:hover {
57
52
  color: var(--vmz-action-primary-background);
58
- }
53
+ }
59
54
 
60
- .vmz-ui-toc__link:focus-visible {
55
+ .vmz-ui-toc__link:focus-visible {
61
56
  box-shadow: 0 0 0 2px var(--vmz-focus-ring);
62
57
  border-radius: var(--vmz-radius-md, 6px);
63
- }
58
+ }
64
59
  </style>
65
-
66
60
  <script client>
67
61
  /**
68
- * VMZ UI — Toc (Document/Product composition).
69
- * In-page outline; parent supplies items. Readable without JS.
70
- */
62
+ * VMZ UI — Toc (Document/Product composition).
63
+ * In-page outline; parent supplies items. Readable without JS.
64
+ */
71
65
  export default class Toc {
72
- public label: string = "On this page";
73
- /** @type {{ id: string, title: string, href: string }[]} */
74
- public items: {
75
- id: string;
76
- title: string;
77
- href: string;
78
- }[] = [];
66
+ public label: string = "On this page";
67
+ /** @type {{ id: string, title: string, href: string }[]} */
68
+ public items: {
69
+ id: string;
70
+ title: string;
71
+ href: string;
72
+ }[] = [];
79
73
  }
80
74
  </script>
@@ -1,41 +1,30 @@
1
1
  <template>
2
- <div
3
- class="vmz-ui-tooltip"
4
- data-vmz-ui="tooltip"
5
- :data-open="openAttr"
6
- >
7
- <div class="vmz-ui-tooltip__trigger" data-vmz-tooltip="trigger">
8
- <slot />
9
- </div>
10
- <div
11
- v-if="open"
12
- class="vmz-ui-tooltip__bubble"
13
- role="tooltip"
14
- :id="tooltipId"
15
- data-vmz-tooltip="bubble"
16
- >
17
- {{ content }}
18
- </div>
2
+ <div data-vmz-ui="tooltip" :data-open="openAttr" class="vmz-ui-tooltip">
3
+ <div data-vmz-tooltip="trigger" class="vmz-ui-tooltip__trigger">
4
+ <slot />
5
+ </div>
6
+ <div v-if="open" role="tooltip" :id="tooltipId" data-vmz-tooltip="bubble" class="vmz-ui-tooltip__bubble">
7
+ {{ content }}
8
+ </div>
19
9
  </div>
20
10
  </template>
21
-
22
11
  <style>
23
- .vmz-ui-tooltip {
12
+ .vmz-ui-tooltip {
24
13
  position: relative;
25
14
  display: inline-flex;
26
15
  flex-direction: column;
27
16
  align-items: flex-start;
28
17
  gap: 0.35rem;
29
18
  max-width: 28rem;
30
- }
19
+ }
31
20
 
32
- .vmz-ui-tooltip__trigger {
21
+ .vmz-ui-tooltip__trigger {
33
22
  display: inline-flex;
34
23
  align-items: center;
35
24
  gap: 0.35rem;
36
- }
25
+ }
37
26
 
38
- .vmz-ui-tooltip__bubble {
27
+ .vmz-ui-tooltip__bubble {
39
28
  box-sizing: border-box;
40
29
  max-width: 18rem;
41
30
  padding: 0.4rem 0.65rem;
@@ -46,23 +35,24 @@
46
35
  font-size: 0.88rem;
47
36
  line-height: 1.4;
48
37
  box-shadow: 0 1px 0 color-mix(in srgb, var(--vmz-line-default) 70%, transparent);
49
- }
38
+ }
50
39
 
51
- .vmz-ui-tooltip[data-open='true'] .vmz-ui-tooltip__trigger :where(button, a, [tabindex]):focus-visible {
40
+ .vmz-ui-tooltip[data-open="true"]
41
+ .vmz-ui-tooltip__trigger
42
+ :where(button, a, [tabindex]):focus-visible {
52
43
  box-shadow: 0 0 0 2px var(--vmz-focus-ring);
53
- }
44
+ }
54
45
  </style>
55
-
56
46
  <script client>
57
47
  /**
58
- * VMZ UI — Tooltip.
59
- * Parent owns live `open` + content string. Not a second overlay runtime;
60
- * Escape/outside policies stay with richer Popover/Dialog when needed.
61
- */
48
+ * VMZ UI — Tooltip.
49
+ * Parent owns live `open` + content string. Not a second overlay runtime;
50
+ * Escape/outside policies stay with richer Popover/Dialog when needed.
51
+ */
62
52
  export default class Tooltip {
63
- public open: boolean = false;
64
- public openAttr: string = "false";
65
- public content: string = "";
66
- public tooltipId: string = "vmz-tooltip";
53
+ public open: boolean = false;
54
+ public openAttr: string = "false";
55
+ public content: string = "";
56
+ public tooltipId: string = "vmz-tooltip";
67
57
  }
68
58
  </script>
@@ -1,59 +1,36 @@
1
1
  <template>
2
- <div class="vmz-ui-tree" data-vmz-ui="tree">
3
- <p v-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
- @click="onTreeClick"
10
- :onKeyDown="onTreeKeyDown"
11
- >
12
- <li
13
- v-for="item in items"
14
- :key="item.id"
15
- class="vmz-ui-tree__item"
16
- role="treeitem"
17
- :id="item.id"
18
- :data-vmz-tree-item="item.id"
19
- :data-depth="item.depth"
20
- :aria-selected="selected === item.id ? 'true' : 'false'"
21
- :aria-expanded="item.expandedAttr"
22
- :tabindex="selected === item.id ? 0 : -1"
23
- :style="item.padStyle"
24
- >
25
- <button
26
- v-if="item.expandable"
27
- type="button"
28
- class="vmz-ui-tree__twist"
29
- :data-vmz-tree-twist="item.id"
30
- :aria-label="item.twistLabel"
31
- tabindex="-1"
32
- >
33
- {{ item.expanded ? '−' : '+' }}
34
- </button>
35
- <span v-if="!item.expandable" class="vmz-ui-tree__twist-spacer" aria-hidden="true"></span>
36
- <span class="vmz-ui-tree__title" :data-vmz-tree-title="item.id">{{ item.title }}</span>
37
- </li>
38
- </ul>
2
+ <div data-vmz-ui="tree" class="vmz-ui-tree">
3
+ <p v-if="label" :id="labelId" class="vmz-ui-tree__label">
4
+ {{ label }}
5
+ </p>
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">
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 ? "−" : "+" }}
10
+ </button>
11
+ <span v-if="!item.expandable" aria-hidden="true" class="vmz-ui-tree__twist-spacer" />
12
+ <span :data-vmz-tree-title="item.id" class="vmz-ui-tree__title">
13
+ {{ item.title }}
14
+ </span>
15
+ </li>
16
+ </ul>
39
17
  </div>
40
18
  </template>
41
-
42
19
  <style>
43
- .vmz-ui-tree {
20
+ .vmz-ui-tree {
44
21
  display: flex;
45
22
  flex-direction: column;
46
23
  gap: 0.35rem;
47
24
  max-width: 28rem;
48
- }
25
+ }
49
26
 
50
- .vmz-ui-tree__label {
27
+ .vmz-ui-tree__label {
51
28
  margin: 0;
52
29
  font-weight: 600;
53
30
  color: var(--vmz-text-ink);
54
- }
31
+ }
55
32
 
56
- .vmz-ui-tree__items {
33
+ .vmz-ui-tree__items {
57
34
  margin: 0;
58
35
  padding: 0.25rem;
59
36
  list-style: none;
@@ -61,9 +38,9 @@
61
38
  border-radius: var(--vmz-radius-md, 6px);
62
39
  background: var(--vmz-surface-paper);
63
40
  color: var(--vmz-text-ink);
64
- }
41
+ }
65
42
 
66
- .vmz-ui-tree__item {
43
+ .vmz-ui-tree__item {
67
44
  display: flex;
68
45
  align-items: center;
69
46
  gap: 0.35rem;
@@ -71,26 +48,26 @@
71
48
  border-radius: var(--vmz-radius-md, 6px);
72
49
  cursor: pointer;
73
50
  outline: none;
74
- }
51
+ }
75
52
 
76
- .vmz-ui-tree__item[aria-selected='true'] {
53
+ .vmz-ui-tree__item[aria-selected="true"] {
77
54
  background: var(--vmz-action-primary-background);
78
55
  color: var(--vmz-action-primary-foreground);
79
- }
56
+ }
80
57
 
81
- .vmz-ui-tree__item:hover {
58
+ .vmz-ui-tree__item:hover {
82
59
  background: color-mix(in srgb, var(--vmz-action-primary-background) 14%, transparent);
83
- }
60
+ }
84
61
 
85
- .vmz-ui-tree__item[aria-selected='true']:hover {
62
+ .vmz-ui-tree__item[aria-selected="true"]:hover {
86
63
  background: var(--vmz-action-primary-hover);
87
- }
64
+ }
88
65
 
89
- .vmz-ui-tree__item:focus-visible {
66
+ .vmz-ui-tree__item:focus-visible {
90
67
  box-shadow: 0 0 0 2px var(--vmz-focus-ring);
91
- }
68
+ }
92
69
 
93
- .vmz-ui-tree__twist {
70
+ .vmz-ui-tree__twist {
94
71
  flex: 0 0 auto;
95
72
  width: 1.35rem;
96
73
  height: 1.35rem;
@@ -104,105 +81,111 @@
104
81
  line-height: 1;
105
82
  cursor: pointer;
106
83
  outline: none;
107
- }
84
+ }
108
85
 
109
- .vmz-ui-tree__twist:focus-visible {
86
+ .vmz-ui-tree__twist:focus-visible {
110
87
  box-shadow: 0 0 0 2px var(--vmz-focus-ring);
111
- }
88
+ }
112
89
 
113
- .vmz-ui-tree__twist-spacer {
90
+ .vmz-ui-tree__twist-spacer {
114
91
  flex: 0 0 auto;
115
92
  width: 1.35rem;
116
- }
93
+ }
117
94
 
118
- .vmz-ui-tree__title {
95
+ .vmz-ui-tree__title {
119
96
  flex: 1 1 auto;
120
- }
97
+ }
121
98
  </style>
122
-
123
99
  <script client>
124
100
  /**
125
- * VMZ UI — Tree (ordinary hierarchy).
126
- * Parent owns visible flat rows + selected + expand state.
127
- * Not virtualized / not DataGrid tree mode.
128
- */
101
+ * VMZ UI — Tree (ordinary hierarchy).
102
+ * Parent owns visible flat rows + selected + expand state.
103
+ * Not virtualized / not DataGrid tree mode.
104
+ */
129
105
  export default class Tree {
130
- public label: string = "Tree";
131
- public labelId: string = "vmz-tree-label";
132
- public selected: string = "";
133
- /**
134
- * @type {{
135
- * id: string,
136
- * title: string,
137
- * depth: string,
138
- * expandable: boolean,
139
- * expanded: boolean,
140
- * expandedAttr: string,
141
- * twistLabel: string,
142
- * padStyle: string
143
- * }[]}
144
- */
145
- public items: {
146
- id: string;
147
- title: string;
148
- depth: string;
149
- expandable: boolean;
150
- expanded: boolean;
151
- expandedAttr: string;
152
- twistLabel: string;
153
- padStyle: string;
154
- }[] = [];
155
- public onSelect: ((id: string) => void) | null = null;
156
- public onToggle: ((id: string) => void) | null = null;
157
- select(id) {
158
- if (typeof this.onSelect === "function") this.onSelect(id);
159
- }
160
- toggle(id) {
161
- if (typeof this.onToggle === "function") this.onToggle(id);
162
- }
163
- onTreeClick(ev) {
164
- const t = ev && ev.target;
165
- const twist = t && typeof t.closest === "function" ? t.closest("[data-vmz-tree-twist]") : null;
166
- if (twist) {
167
- const id = typeof twist.getAttribute === "function" ? twist.getAttribute("data-vmz-tree-twist") : null;
168
- if (id) {
169
- ev.preventDefault();
170
- ev.stopPropagation();
171
- this.toggle(id);
172
- }
173
- return;
174
- }
175
- const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-tree-item]") : null;
176
- const id = el && typeof el.getAttribute === "function" ? el.getAttribute("data-vmz-tree-item") : null;
177
- if (id) this.select(id);
178
- }
179
- onTreeKeyDown(ev) {
180
- if (!ev || !this.items?.length) return;
181
- const ids = this.items.map((it) => it.id);
182
- const idx = Math.max(0, ids.indexOf(this.selected));
183
- const cur = this.items[idx];
184
- if (ev.key === "ArrowRight" && cur?.expandable && !cur.expanded) {
185
- ev.preventDefault();
186
- this.toggle(cur.id);
187
- return;
188
- }
189
- if (ev.key === "ArrowLeft" && cur?.expandable && cur.expanded) {
190
- ev.preventDefault();
191
- this.toggle(cur.id);
192
- return;
193
- }
194
- let next = idx;
195
- if (ev.key === "ArrowDown") next = (idx + 1) % ids.length;
196
- else if (ev.key === "ArrowUp") next = (idx - 1 + ids.length) % ids.length;
197
- else if (ev.key === "Home") next = 0;
198
- else if (ev.key === "End") next = ids.length - 1;
199
- else if (ev.key === "Enter" || ev.key === " ") {
200
- ev.preventDefault();
201
- if (cur) this.select(cur.id);
202
- return;
203
- } else return;
204
- ev.preventDefault();
205
- this.select(ids[next]);
206
- }
106
+ public label: string = "Tree";
107
+ public labelId: string = "vmz-tree-label";
108
+ public selected: string = "";
109
+ /**
110
+ * @type {{
111
+ * id: string,
112
+ * title: string,
113
+ * depth: string,
114
+ * expandable: boolean,
115
+ * expanded: boolean,
116
+ * expandedAttr: string,
117
+ * twistLabel: string,
118
+ * padStyle: string
119
+ * }[]}
120
+ */
121
+ public items: {
122
+ id: string;
123
+ title: string;
124
+ depth: string;
125
+ expandable: boolean;
126
+ expanded: boolean;
127
+ expandedAttr: string;
128
+ twistLabel: string;
129
+ padStyle: string;
130
+ }[] = [];
131
+ public onSelect: ((id: string) => void) | null = null;
132
+ public onToggle: ((id: string) => void) | null = null;
133
+ select(id) {
134
+ if (typeof this.onSelect === "function") this.onSelect(id);
135
+ }
136
+ toggle(id) {
137
+ if (typeof this.onToggle === "function") this.onToggle(id);
138
+ }
139
+ onTreeClick(ev) {
140
+ const t = ev && ev.target;
141
+ const twist =
142
+ t && typeof t.closest === "function" ? t.closest("[data-vmz-tree-twist]") : null;
143
+ if (twist) {
144
+ const id =
145
+ typeof twist.getAttribute === "function"
146
+ ? twist.getAttribute("data-vmz-tree-twist")
147
+ : null;
148
+ if (id) {
149
+ ev.preventDefault();
150
+ ev.stopPropagation();
151
+ this.toggle(id);
152
+ }
153
+ return;
154
+ }
155
+ const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-tree-item]") : null;
156
+ const id =
157
+ el && typeof el.getAttribute === "function"
158
+ ? el.getAttribute("data-vmz-tree-item")
159
+ : null;
160
+ if (id) this.select(id);
161
+ }
162
+ onTreeKeyDown(ev) {
163
+ if (!ev || !this.items?.length) return;
164
+ const ids = this.items.map((it) => it.id);
165
+ const idx = Math.max(0, ids.indexOf(this.selected));
166
+ const cur = this.items[idx];
167
+ if (ev.key === "ArrowRight" && cur?.expandable && !cur.expanded) {
168
+ ev.preventDefault();
169
+ this.toggle(cur.id);
170
+ return;
171
+ }
172
+ if (ev.key === "ArrowLeft" && cur?.expandable && cur.expanded) {
173
+ ev.preventDefault();
174
+ this.toggle(cur.id);
175
+ return;
176
+ }
177
+ let next = idx;
178
+ if (ev.key === "ArrowDown") next = (idx + 1) % ids.length;
179
+ else if (ev.key === "ArrowUp") next = (idx - 1 + ids.length) % ids.length;
180
+ else if (ev.key === "Home") next = 0;
181
+ else if (ev.key === "End") next = ids.length - 1;
182
+ else if (ev.key === "Enter" || ev.key === " ") {
183
+ ev.preventDefault();
184
+ if (cur) this.select(cur.id);
185
+ return;
186
+ } else return;
187
+ ev.preventDefault();
188
+ this.select(ids[next]);
189
+ }
207
190
  }
208
191
  </script>