@vmz/ui 0.2.2 → 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 +183 -94
  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,63 +1,61 @@
1
1
  <template>
2
- <div class="vmz-ui-table-wrap" data-vmz-ui="table" :data-sort-key="sortKey" :data-sort-dir="sortDir">
3
- <table class="vmz-ui-table">
4
- <thead @click="onHeadClick">
5
- <tr>
6
- <th v-for="col in columns" :key="col.id" scope="col" :data-vmz-col="col.id">
7
- <button type="button" class="vmz-ui-table__sort" :data-vmz-sort="col.id">
8
- {{ col.title }}
9
- </button>
10
- </th>
11
- <th v-if="hasRowAction" scope="col">{{ actionTitle }}</th>
12
- </tr>
13
- </thead>
14
- <tbody @click="onBodyClick">
15
- <tr v-for="row in rows" :key="row.id" :data-vmz-row="row.id">
16
- <td v-for="cell in row.cells">{{ cell }}</td>
17
- <td v-if="hasRowAction">
18
- <button
19
- type="button"
20
- class="vmz-ui-table__row-action"
21
- :data-vmz-row-action="row.id"
22
- :disabled="rowActionDisabled"
23
- >
24
- {{ actionLabel }}
25
- </button>
26
- </td>
27
- </tr>
28
- </tbody>
29
- </table>
2
+ <div data-vmz-ui="table" :data-sort-key="sortKey" :data-sort-dir="sortDir" class="vmz-ui-table-wrap">
3
+ <table class="vmz-ui-table">
4
+ <thead @click="onHeadClick">
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 }}
9
+ </button>
10
+ </th>
11
+ <th v-if="hasRowAction" scope="col">
12
+ {{ actionTitle }}
13
+ </th>
14
+ </tr>
15
+ </thead>
16
+ <tbody @click="onBodyClick">
17
+ <tr v-for="row in rows" :key="row.id" :data-vmz-row="row.id">
18
+ <td v-for="cell in row.cells">
19
+ {{ cell }}
20
+ </td>
21
+ <td v-if="hasRowAction">
22
+ <button type="button" :data-vmz-row-action="row.id" :disabled="rowActionDisabled" class="vmz-ui-table__row-action">
23
+ {{ actionLabel }}
24
+ </button>
25
+ </td>
26
+ </tr>
27
+ </tbody>
28
+ </table>
30
29
  </div>
31
30
  </template>
32
-
33
31
  <style>
34
- .vmz-ui-table-wrap {
32
+ .vmz-ui-table-wrap {
35
33
  overflow-x: auto;
36
34
  border: 1px solid var(--vmz-line-default);
37
35
  border-radius: var(--vmz-radius-md, 6px);
38
- }
36
+ }
39
37
 
40
- .vmz-ui-table {
38
+ .vmz-ui-table {
41
39
  width: 100%;
42
40
  border-collapse: collapse;
43
41
  font: inherit;
44
- }
42
+ }
45
43
 
46
- .vmz-ui-table th,
47
- .vmz-ui-table td {
44
+ .vmz-ui-table th,
45
+ .vmz-ui-table td {
48
46
  padding: var(--vmz-density-compact-padding-y) var(--vmz-density-compact-padding-x);
49
47
  text-align: left;
50
48
  border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
51
49
  vertical-align: middle;
52
- }
50
+ }
53
51
 
54
- .vmz-ui-table th {
52
+ .vmz-ui-table th {
55
53
  font-weight: 700;
56
54
  color: var(--vmz-action-primary-active);
57
55
  background: transparent;
58
- }
56
+ }
59
57
 
60
- .vmz-ui-table__sort {
58
+ .vmz-ui-table__sort {
61
59
  font: inherit;
62
60
  font-weight: 700;
63
61
  padding: 0;
@@ -66,13 +64,13 @@
66
64
  color: inherit;
67
65
  cursor: pointer;
68
66
  outline: none;
69
- }
67
+ }
70
68
 
71
- .vmz-ui-table__sort:focus-visible {
69
+ .vmz-ui-table__sort:focus-visible {
72
70
  box-shadow: 0 0 0 2px var(--vmz-focus-ring);
73
- }
71
+ }
74
72
 
75
- .vmz-ui-table__row-action {
73
+ .vmz-ui-table__row-action {
76
74
  font: inherit;
77
75
  font-weight: 600;
78
76
  padding: 0.3rem 0.55rem;
@@ -82,55 +80,54 @@
82
80
  color: var(--vmz-action-primary-active);
83
81
  cursor: pointer;
84
82
  outline: none;
85
- }
83
+ }
86
84
 
87
- .vmz-ui-table__row-action:focus-visible {
85
+ .vmz-ui-table__row-action:focus-visible {
88
86
  box-shadow: 0 0 0 2px var(--vmz-focus-ring);
89
- }
87
+ }
90
88
 
91
- .vmz-ui-table__row-action:disabled {
89
+ .vmz-ui-table__row-action:disabled {
92
90
  opacity: 0.55;
93
91
  cursor: not-allowed;
94
- }
92
+ }
95
93
  </style>
96
-
97
94
  <script client>
98
95
  /**
99
- * VMZ UI — Table (UI5 Console Surface).
100
- * Column header click → onSort; row action → onRowAction; permission via rowActionDisabled.
101
- */
96
+ * VMZ UI — Table (UI5 Console Surface).
97
+ * Column header click → onSort; row action → onRowAction; permission via rowActionDisabled.
98
+ */
102
99
  export default class Table {
103
- /** @type {{ id: string, title: string }[]} */
104
- public columns: {
105
- id: string;
106
- title: string;
107
- }[] = [];
108
- /** @type {{ id: string, cells: string[] }[]} */
109
- public rows: {
110
- id: string;
111
- cells: string[];
112
- }[] = [];
113
- public hasRowAction: boolean = false;
114
- public rowActionDisabled: boolean = false;
115
- public actionTitle: string = "Action";
116
- public actionLabel: string = "Open";
117
- public sortKey: string = "";
118
- public sortDir: string = "asc";
119
- public onRowAction: ((rowId: string) => void) | null = null;
120
- public onSort: ((columnId: string) => void) | null = null;
121
- onBodyClick(ev) {
122
- const t = ev && ev.target;
123
- const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-row-action]") : null;
124
- if (!el || el.disabled) return;
125
- const id = el.getAttribute("data-vmz-row-action");
126
- if (id && typeof this.onRowAction === "function") this.onRowAction(id);
127
- }
128
- onHeadClick(ev) {
129
- const t = ev && ev.target;
130
- const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-sort]") : null;
131
- if (!el) return;
132
- const id = el.getAttribute("data-vmz-sort");
133
- if (id && typeof this.onSort === "function") this.onSort(id);
134
- }
100
+ /** @type {{ id: string, title: string }[]} */
101
+ public columns: {
102
+ id: string;
103
+ title: string;
104
+ }[] = [];
105
+ /** @type {{ id: string, cells: string[] }[]} */
106
+ public rows: {
107
+ id: string;
108
+ cells: string[];
109
+ }[] = [];
110
+ public hasRowAction: boolean = false;
111
+ public rowActionDisabled: boolean = false;
112
+ public actionTitle: string = "Action";
113
+ public actionLabel: string = "Open";
114
+ public sortKey: string = "";
115
+ public sortDir: string = "asc";
116
+ public onRowAction: ((rowId: string) => void) | null = null;
117
+ public onSort: ((columnId: string) => void) | null = null;
118
+ onBodyClick(ev) {
119
+ const t = ev && ev.target;
120
+ const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-row-action]") : null;
121
+ if (!el || el.disabled) return;
122
+ const id = el.getAttribute("data-vmz-row-action");
123
+ if (id && typeof this.onRowAction === "function") this.onRowAction(id);
124
+ }
125
+ onHeadClick(ev) {
126
+ const t = ev && ev.target;
127
+ const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-sort]") : null;
128
+ if (!el) return;
129
+ const id = el.getAttribute("data-vmz-sort");
130
+ if (id && typeof this.onSort === "function") this.onSort(id);
131
+ }
135
132
  }
136
133
  </script>
@@ -1,56 +1,31 @@
1
1
  <template>
2
- <div class="vmz-ui-tabs" data-vmz-ui="tabs">
3
- <div
4
- class="vmz-ui-tabs__list"
5
- role="tablist"
6
- :aria-label="label"
7
- :onKeyDown="onListKeyDown"
8
- @click="onListClick"
9
- >
10
- <button
11
- v-for="item in items"
12
- :key="item.id"
13
- type="button"
14
- class="vmz-ui-tabs__tab"
15
- role="tab"
16
- :id="item.id"
17
- :data-vmz-tab="item.id"
18
- :aria-selected="selected === item.id ? 'true' : 'false'"
19
- :aria-controls="item.controls"
20
- :tabindex="selected === item.id ? 0 : -1"
21
- >
22
- {{ item.title }}
23
- </button>
24
- </div>
25
- <div
26
- class="vmz-ui-tabs__panel"
27
- role="tabpanel"
28
- :id="panelId"
29
- :data-vmz-tab-panel="selected"
30
- :aria-labelledby="selected"
31
- tabindex="0"
32
- >
33
- <slot />
34
- </div>
2
+ <div data-vmz-ui="tabs" class="vmz-ui-tabs">
3
+ <div role="tablist" :aria-label="label" :onKeyDown="onListKeyDown" @click="onListClick" class="vmz-ui-tabs__list">
4
+ <button v-for="item in items" :key="item.id" type="button" role="tab" :id="item.id" :data-vmz-tab="item.id" :aria-selected='selected === item.id ? "true" : "false"' :aria-controls="item.controls" :tabindex="selected === item.id ? 0 : -1" class="vmz-ui-tabs__tab">
5
+ {{ item.title }}
6
+ </button>
7
+ </div>
8
+ <div role="tabpanel" :id="panelId" :data-vmz-tab-panel="selected" :aria-labelledby="selected" tabindex="0" class="vmz-ui-tabs__panel">
9
+ <slot />
10
+ </div>
35
11
  </div>
36
12
  </template>
37
-
38
13
  <style>
39
- .vmz-ui-tabs {
14
+ .vmz-ui-tabs {
40
15
  display: flex;
41
16
  flex-direction: column;
42
17
  gap: 0.65rem;
43
18
  max-width: 36rem;
44
- }
19
+ }
45
20
 
46
- .vmz-ui-tabs__list {
21
+ .vmz-ui-tabs__list {
47
22
  display: flex;
48
23
  flex-wrap: wrap;
49
24
  gap: 0.25rem;
50
25
  border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
51
- }
26
+ }
52
27
 
53
- .vmz-ui-tabs__tab {
28
+ .vmz-ui-tabs__tab {
54
29
  font: inherit;
55
30
  font-size: 0.875rem;
56
31
  font-weight: 400;
@@ -63,70 +38,73 @@
63
38
  color: var(--vmz-text-ink);
64
39
  cursor: pointer;
65
40
  outline: none;
66
- }
41
+ }
67
42
 
68
- .vmz-ui-tabs__tab[aria-selected='true'] {
43
+ .vmz-ui-tabs__tab[aria-selected="true"] {
69
44
  background: transparent;
70
45
  color: var(--vmz-action-primary-background);
71
46
  font-weight: 500;
72
47
  border-bottom-color: var(--vmz-action-primary-background);
73
- }
48
+ }
74
49
 
75
- .vmz-ui-tabs__tab:focus-visible {
50
+ .vmz-ui-tabs__tab:focus-visible {
76
51
  box-shadow: 0 0 0 2px var(--vmz-focus-ring);
77
- }
52
+ }
78
53
 
79
- .vmz-ui-tabs__panel {
54
+ .vmz-ui-tabs__panel {
80
55
  min-height: 2.5rem;
81
56
  outline: none;
82
- }
57
+ }
83
58
 
84
- .vmz-ui-tabs__panel:focus-visible {
59
+ .vmz-ui-tabs__panel:focus-visible {
85
60
  box-shadow: 0 0 0 2px var(--vmz-focus-ring);
86
- }
61
+ }
87
62
  </style>
88
-
89
63
  <script client>
90
64
  /**
91
- * VMZ UI — Tabs (UI2).
92
- * Parent owns `selected` id; keyboard Left/Right/Home/End on the tablist.
93
- * Panel content is the default slot (page chooses which body to show).
94
- */
65
+ * VMZ UI — Tabs (UI2).
66
+ * Parent owns `selected` id; keyboard Left/Right/Home/End on the tablist.
67
+ * Panel content is the default slot (page chooses which body to show).
68
+ */
95
69
  export default class Tabs {
96
- public label: string = "Tabs";
97
- public selected: string = "";
98
- public panelId: string = "";
99
- /** @type {{ id: string, title: string, controls?: string }[]} */
100
- public items: {
101
- id: string;
102
- title: string;
103
- controls?: string;
104
- }[] = [];
105
- public onSelect: ((id: string) => void) | null = null;
106
- select(id) {
107
- if (typeof this.onSelect === "function") this.onSelect(id);
108
- }
109
- onListClick(ev) {
110
- const t = ev && ev.target;
111
- const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-tab]") : null;
112
- const id = el && typeof el.getAttribute === "function" ? el.getAttribute("data-vmz-tab") : null;
113
- if (id) this.select(id);
114
- }
115
- onListKeyDown(ev) {
116
- if (!ev || !this.items?.length) return;
117
- const ids = this.items.map((it) => it.id);
118
- const idx = Math.max(0, ids.indexOf(this.selected));
119
- let next = idx;
120
- if (ev.key === "ArrowRight") next = (idx + 1) % ids.length;
121
- else if (ev.key === "ArrowLeft") next = (idx - 1 + ids.length) % ids.length;
122
- else if (ev.key === "Home") next = 0;
123
- else if (ev.key === "End") next = ids.length - 1;
124
- else return;
125
- ev.preventDefault();
126
- this.select(ids[next]);
127
- const root = this.__vmzDomRoot;
128
- const btn = root && typeof root.querySelector === "function" ? root.querySelector(`[data-vmz-tab="${ids[next]}"]`) : null;
129
- if (btn && typeof btn.focus === "function") btn.focus();
130
- }
70
+ public label: string = "Tabs";
71
+ public selected: string = "";
72
+ public panelId: string = "";
73
+ /** @type {{ id: string, title: string, controls?: string }[]} */
74
+ public items: {
75
+ id: string;
76
+ title: string;
77
+ controls?: string;
78
+ }[] = [];
79
+ public onSelect: ((id: string) => void) | null = null;
80
+ select(id) {
81
+ if (typeof this.onSelect === "function") this.onSelect(id);
82
+ }
83
+ onListClick(ev) {
84
+ const t = ev && ev.target;
85
+ const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-tab]") : null;
86
+ const id =
87
+ el && typeof el.getAttribute === "function" ? el.getAttribute("data-vmz-tab") : null;
88
+ if (id) this.select(id);
89
+ }
90
+ onListKeyDown(ev) {
91
+ if (!ev || !this.items?.length) return;
92
+ const ids = this.items.map((it) => it.id);
93
+ const idx = Math.max(0, ids.indexOf(this.selected));
94
+ let next = idx;
95
+ if (ev.key === "ArrowRight") next = (idx + 1) % ids.length;
96
+ else if (ev.key === "ArrowLeft") next = (idx - 1 + ids.length) % ids.length;
97
+ else if (ev.key === "Home") next = 0;
98
+ else if (ev.key === "End") next = ids.length - 1;
99
+ else return;
100
+ ev.preventDefault();
101
+ this.select(ids[next]);
102
+ const root = this.__vmzDomRoot;
103
+ const btn =
104
+ root && typeof root.querySelector === "function"
105
+ ? root.querySelector(`[data-vmz-tab="${ids[next]}"]`)
106
+ : null;
107
+ if (btn && typeof btn.focus === "function") btn.focus();
108
+ }
131
109
  }
132
110
  </script>
@@ -1,15 +1,40 @@
1
1
  <template>
2
- <span
3
- class="vmz-ui-tag"
4
- data-vmz-ui="tag"
5
- :data-status="status || undefined"
6
- :data-vmz-tag-status="status || undefined"
7
- ><slot /></span>
2
+ <span data-vmz-ui="tag" :data-status="status || undefined" :data-vmz-tag-status="status || undefined" class="vmz-ui-tag">
3
+ <slot />
4
+ </span>
8
5
  </template>
9
6
  <style>
10
- .vmz-ui-tag{display:inline-flex;align-items:center;min-height:1.5rem;padding:0 .45rem;border:1px solid var(--vmz-line-default);border-radius:var(--vmz-radius-sm,2px);background:var(--vmz-surface-mist);color:var(--vmz-text-steel);font-size:.75rem;line-height:1}
11
- .vmz-ui-tag[data-status='success']{color:var(--vmz-status-success-foreground);background:color-mix(in srgb,var(--vmz-status-success-accent) 12%,var(--vmz-surface-paper));border-color:color-mix(in srgb,var(--vmz-status-success-accent) 45%,var(--vmz-line-default))}
12
- .vmz-ui-tag[data-status='warning']{color:var(--vmz-status-warning-foreground);background:color-mix(in srgb,var(--vmz-status-warning-accent) 12%,var(--vmz-surface-paper));border-color:color-mix(in srgb,var(--vmz-status-warning-accent) 45%,var(--vmz-line-default))}
13
- .vmz-ui-tag[data-status='error']{color:var(--vmz-status-danger-foreground);background:color-mix(in srgb,var(--vmz-status-danger-accent) 12%,var(--vmz-surface-paper));border-color:color-mix(in srgb,var(--vmz-status-danger-accent) 45%,var(--vmz-line-default))}
7
+ .vmz-ui-tag {
8
+ display: inline-flex;
9
+ align-items: center;
10
+ min-height: 1.5rem;
11
+ padding: 0 0.45rem;
12
+ border: 1px solid var(--vmz-line-default);
13
+ border-radius: var(--vmz-radius-sm, 2px);
14
+ background: var(--vmz-surface-mist);
15
+ color: var(--vmz-text-steel);
16
+ font-size: 0.75rem;
17
+ line-height: 1;
18
+ }
19
+ .vmz-ui-tag[data-status="success"] {
20
+ color: var(--vmz-status-success-foreground);
21
+ background: color-mix(in srgb, var(--vmz-status-success-accent) 12%, var(--vmz-surface-paper));
22
+ border-color: color-mix(in srgb, var(--vmz-status-success-accent) 45%, var(--vmz-line-default));
23
+ }
24
+ .vmz-ui-tag[data-status="warning"] {
25
+ color: var(--vmz-status-warning-foreground);
26
+ background: color-mix(in srgb, var(--vmz-status-warning-accent) 12%, var(--vmz-surface-paper));
27
+ border-color: color-mix(in srgb, var(--vmz-status-warning-accent) 45%, var(--vmz-line-default));
28
+ }
29
+ .vmz-ui-tag[data-status="error"] {
30
+ color: var(--vmz-status-danger-foreground);
31
+ background: color-mix(in srgb, var(--vmz-status-danger-accent) 12%, var(--vmz-surface-paper));
32
+ border-color: color-mix(in srgb, var(--vmz-status-danger-accent) 45%, var(--vmz-line-default));
33
+ }
14
34
  </style>
15
- <script client>export default class Tag { static __vmzHostBox = 'contents'; public status: string = ''; }</script>
35
+ <script client>
36
+ export default class Tag {
37
+ static __vmzHostBox = "contents";
38
+ public status: string = "";
39
+ }
40
+ </script>
@@ -1,122 +1,118 @@
1
1
  <template>
2
- <div class="vmz-ui-textarea" data-vmz-ui="textarea" :data-invalid="invalidAttr">
3
- <label v-if="label" class="vmz-ui-textarea__label" :for="controlId">{{ label }}</label>
4
- <textarea
5
- :id="controlId"
6
- class="vmz-ui-textarea__control"
7
- :rows="rows"
8
- :value="value"
9
- :disabled="disabled"
10
- :readonly="readonly"
11
- :aria-invalid="error ? 'true' : 'false'"
12
- :aria-describedby="describedBy"
13
- @input="onDomInput"
14
- ></textarea>
15
- <p v-if="description" :id="descriptionId" class="vmz-ui-textarea__description">{{ description }}</p>
16
- <p v-if="error" :id="errorId" class="vmz-ui-textarea__error" role="alert">{{ error }}</p>
2
+ <div data-vmz-ui="textarea" :data-invalid="invalidAttr" class="vmz-ui-textarea">
3
+ <label v-if="label" :for="controlId" class="vmz-ui-textarea__label">
4
+ {{ label }}
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" />
7
+ <p v-if="description" :id="descriptionId" class="vmz-ui-textarea__description">
8
+ {{ description }}
9
+ </p>
10
+ <p v-if="error" :id="errorId" role="alert" class="vmz-ui-textarea__error">
11
+ {{ error }}
12
+ </p>
17
13
  </div>
18
14
  </template>
19
-
20
15
  <style>
21
- .vmz-ui-textarea {
16
+ .vmz-ui-textarea {
22
17
  display: flex;
23
18
  flex-direction: column;
24
19
  gap: 0.45rem;
25
20
  max-width: none;
26
21
  width: 100%;
27
- }
22
+ }
28
23
 
29
24
  .vmz-ui-textarea__label {
30
- font: inherit;
31
- font-weight: 500;
32
- font-size: 0.875rem;
25
+ font: inherit;
26
+ font-weight: 500;
27
+ font-size: 0.875rem;
33
28
  }
34
29
 
35
30
  .vmz-ui-textarea__control {
36
- box-sizing: border-box;
37
- width: 100%;
38
- font-family: var(--vmz-font-sans, inherit);
39
- font-size: 0.875rem;
40
- line-height: 1.5715;
41
- min-height: 5.5rem;
42
- padding: var(--vmz-density-control-padding-y, 0.25rem)
43
- var(--vmz-density-control-padding-x, 0.75rem);
44
- border: 1px solid var(--vmz-line-default, #d9d9d9);
45
- border-radius: var(--vmz-radius-md, 6px);
46
- background: var(--vmz-surface-paper, #fff);
47
- color: var(--vmz-text-ink, inherit);
48
- outline: none;
49
- resize: vertical;
50
- transition:
51
- border-color var(--vmz-motion-control-duration, 120ms) var(--vmz-motion-control-easing, linear),
52
- box-shadow var(--vmz-motion-control-duration, 120ms) var(--vmz-motion-control-easing, linear);
31
+ box-sizing: border-box;
32
+ width: 100%;
33
+ font-family: var(--vmz-font-sans, inherit);
34
+ font-size: 0.875rem;
35
+ 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);
43
+ outline: none;
44
+ resize: vertical;
45
+ transition:
46
+ border-color var(--vmz-motion-control-duration, 120ms)
47
+ var(--vmz-motion-control-easing, linear),
48
+ box-shadow var(--vmz-motion-control-duration, 120ms)
49
+ var(--vmz-motion-control-easing, linear);
53
50
  }
54
51
 
55
52
  .vmz-ui-textarea__control::placeholder {
56
- color: var(--vmz-text-steel, #8c8c8c);
57
- opacity: 1;
53
+ color: var(--vmz-text-steel, #8c8c8c);
54
+ opacity: 1;
58
55
  }
59
56
 
60
- .vmz-ui-textarea[data-invalid='true'] .vmz-ui-textarea__control {
61
- border-color: var(--vmz-status-danger-accent);
57
+ .vmz-ui-textarea[data-invalid="true"] .vmz-ui-textarea__control {
58
+ border-color: var(--vmz-status-danger-accent);
62
59
  }
63
60
 
64
61
  .vmz-ui-textarea__control:hover:not(:disabled):not(:focus-visible) {
65
- border-color: var(--vmz-action-primary-background);
62
+ border-color: var(--vmz-action-primary-background);
66
63
  }
67
64
 
68
65
  .vmz-ui-textarea__control:focus-visible {
69
- border-color: var(--vmz-action-primary-background);
70
- box-shadow: 0 0 0 2px var(--vmz-focus-ring);
66
+ border-color: var(--vmz-action-primary-background);
67
+ box-shadow: 0 0 0 2px var(--vmz-focus-ring);
71
68
  }
72
69
 
73
70
  .vmz-ui-textarea__control:disabled,
74
71
  .vmz-ui-textarea__control:read-only {
75
- cursor: default;
76
- background: var(--vmz-surface-mist, #fafafa);
72
+ cursor: default;
73
+ background: var(--vmz-surface-mist, #fafafa);
77
74
  }
78
75
 
79
76
  .vmz-ui-textarea__control:disabled {
80
- cursor: not-allowed;
81
- opacity: 0.55;
77
+ cursor: not-allowed;
78
+ opacity: 0.55;
82
79
  }
83
80
 
84
81
  .vmz-ui-textarea__description {
85
- margin: 0;
86
- color: var(--vmz-text-steel, #8c8c8c);
87
- font-size: 0.875rem;
88
- line-height: 1.4;
82
+ margin: 0;
83
+ color: var(--vmz-text-steel, #8c8c8c);
84
+ font-size: 0.875rem;
85
+ line-height: 1.4;
89
86
  }
90
87
 
91
88
  .vmz-ui-textarea__error {
92
- margin: 0;
93
- color: var(--vmz-status-danger-foreground);
94
- font-size: 0.875rem;
89
+ margin: 0;
90
+ color: var(--vmz-status-danger-foreground);
91
+ font-size: 0.875rem;
95
92
  }
96
93
  </style>
97
-
98
94
  <script client>
99
95
  /**
100
- * VMZ UI — TextArea.
101
- * Same label/description/error ownership contract as Field; parent owns value.
102
- */
96
+ * VMZ UI — TextArea.
97
+ * Same label/description/error ownership contract as Field; parent owns value.
98
+ */
103
99
  export default class TextArea {
104
- public label: string = "";
105
- public description: string = "";
106
- public error: string = "";
107
- public value: string = "";
108
- public rows: number = 4;
109
- public disabled: boolean = false;
110
- public readonly: boolean = false;
111
- public invalidAttr: string = "false";
112
- public controlId: string = "vmz-textarea";
113
- public descriptionId: string = "vmz-textarea-desc";
114
- public errorId: string = "vmz-textarea-err";
115
- public describedBy: string = "";
116
- public onInput: ((e: Event) => void) | null = null;
117
- onDomInput(e: Event) {
118
- this.emit("input", e);
119
- if (typeof this.onInput === "function") this.onInput(e);
120
- }
100
+ public label: string = "";
101
+ public description: string = "";
102
+ public error: string = "";
103
+ public value: string = "";
104
+ public rows: number = 4;
105
+ public disabled: boolean = false;
106
+ public readonly: boolean = false;
107
+ public invalidAttr: string = "false";
108
+ public controlId: string = "vmz-textarea";
109
+ public descriptionId: string = "vmz-textarea-desc";
110
+ public errorId: string = "vmz-textarea-err";
111
+ public describedBy: string = "";
112
+ public onInput: ((e: Event) => void) | null = null;
113
+ onDomInput(e: Event) {
114
+ this.emit("input", e);
115
+ if (typeof this.onInput === "function") this.onInput(e);
116
+ }
121
117
  }
122
118
  </script>