@vmz/ui 0.3.6 → 0.3.7
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.
- package/README.md +51 -51
- package/package.json +2 -2
- package/src/components/Accordion.vmz +94 -94
- package/src/components/AppShell.vmz +5 -2
- package/src/components/Autocomplete.vmz +154 -154
- package/src/components/Badge.vmz +70 -70
- package/src/components/Breadcrumb.vmz +72 -72
- package/src/components/BulkActions.vmz +51 -51
- package/src/components/Button.vmz +213 -211
- package/src/components/Callout.vmz +77 -77
- package/src/components/Card.vmz +120 -120
- package/src/components/Checkbox.vmz +55 -55
- package/src/components/CodeBlock.vmz +21 -6
- package/src/components/ConsoleShell.vmz +251 -251
- package/src/components/DataTable.vmz +194 -194
- package/src/components/DatePicker.vmz +674 -674
- package/src/components/Dialog.vmz +17 -1
- package/src/components/Drawer.vmz +427 -427
- package/src/components/Dropdown.vmz +51 -5
- package/src/components/Empty.vmz +54 -54
- package/src/components/Field.vmz +115 -115
- package/src/components/FormItem.vmz +79 -79
- package/src/components/Link.vmz +5 -1
- package/src/components/List.vmz +102 -102
- package/src/components/Menu.vmz +243 -243
- package/src/components/Pagination.vmz +71 -69
- package/src/components/Popover.vmz +223 -223
- package/src/components/Prose.vmz +81 -81
- package/src/components/RadioGroup.vmz +124 -124
- package/src/components/Segmented.vmz +51 -51
- package/src/components/Select.vmz +533 -533
- package/src/components/Steps.vmz +103 -103
- package/src/components/Switch.vmz +98 -98
- package/src/components/Table.vmz +165 -165
- package/src/components/Tabs.vmz +110 -110
- package/src/components/Tag.vmz +43 -43
- package/src/components/TextArea.vmz +118 -118
- package/src/components/Timeline.vmz +76 -76
- package/src/components/Toc.vmz +74 -74
- package/src/components/Tooltip.vmz +58 -58
- package/src/components/Tree.vmz +191 -191
- package/src/components/Upload.vmz +1299 -1289
package/src/components/Table.vmz
CHANGED
|
@@ -1,165 +1,165 @@
|
|
|
1
|
-
<template>
|
|
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>
|
|
29
|
-
</div>
|
|
30
|
-
</template>
|
|
31
|
-
<style>
|
|
32
|
-
.vmz-ui-table-wrap {
|
|
33
|
-
overflow-x: auto;
|
|
34
|
-
border: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
|
|
35
|
-
border-radius: var(--vmz-radius-lg, 8px);
|
|
36
|
-
background: var(--vmz-surface-paper);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.vmz-ui-table {
|
|
40
|
-
width: 100%;
|
|
41
|
-
border-collapse: collapse;
|
|
42
|
-
font: inherit;
|
|
43
|
-
font-size: 0.875rem;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.vmz-ui-table th,
|
|
47
|
-
.vmz-ui-table td {
|
|
48
|
-
padding: 0.75rem 1rem;
|
|
49
|
-
text-align: left;
|
|
50
|
-
border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
|
|
51
|
-
vertical-align: middle;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/* Header is structure, not brand: quiet ink on a tinted band. */
|
|
55
|
-
.vmz-ui-table th {
|
|
56
|
-
font-weight: 600;
|
|
57
|
-
font-size: 0.8125rem;
|
|
58
|
-
letter-spacing: 0.01em;
|
|
59
|
-
color: var(--vmz-text-steel);
|
|
60
|
-
background: var(--vmz-surface-mist);
|
|
61
|
-
white-space: nowrap;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.vmz-ui-table tbody tr {
|
|
65
|
-
transition: background-color var(--vmz-motion-control-duration, 150ms)
|
|
66
|
-
var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.vmz-ui-table tbody tr:hover {
|
|
70
|
-
background: var(--vmz-surface-mist);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.vmz-ui-table tbody tr:last-child td {
|
|
74
|
-
border-bottom: 0;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.vmz-ui-table__sort {
|
|
78
|
-
display: inline-flex;
|
|
79
|
-
align-items: center;
|
|
80
|
-
gap: 0.25rem;
|
|
81
|
-
font: inherit;
|
|
82
|
-
font-weight: 600;
|
|
83
|
-
padding: 0;
|
|
84
|
-
border: 0;
|
|
85
|
-
background: transparent;
|
|
86
|
-
color: inherit;
|
|
87
|
-
cursor: pointer;
|
|
88
|
-
outline: none;
|
|
89
|
-
border-radius: var(--vmz-radius-xs, 2px);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.vmz-ui-table__sort:focus-visible {
|
|
93
|
-
box-shadow: 0 0 0 3px var(--vmz-focus-ring);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.vmz-ui-table__row-action {
|
|
97
|
-
font: inherit;
|
|
98
|
-
font-weight: 500;
|
|
99
|
-
padding: 0.25rem 0.625rem;
|
|
100
|
-
border: 1px solid var(--vmz-line-default);
|
|
101
|
-
border-radius: var(--vmz-radius-md, 6px);
|
|
102
|
-
background: var(--vmz-surface-paper);
|
|
103
|
-
color: var(--vmz-action-primary-background);
|
|
104
|
-
cursor: pointer;
|
|
105
|
-
outline: none;
|
|
106
|
-
transition:
|
|
107
|
-
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
|
-
background-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
.vmz-ui-table__row-action:hover:not(:disabled) {
|
|
113
|
-
color: var(--vmz-action-primary-hover);
|
|
114
|
-
border-color: var(--vmz-action-primary-hover);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.vmz-ui-table__row-action:focus-visible {
|
|
118
|
-
box-shadow: 0 0 0 3px var(--vmz-focus-ring);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
.vmz-ui-table__row-action:disabled {
|
|
122
|
-
opacity: 0.55;
|
|
123
|
-
cursor: not-allowed;
|
|
124
|
-
}
|
|
125
|
-
</style>
|
|
126
|
-
<script client>
|
|
127
|
-
/**
|
|
128
|
-
* VMZ UI — Table (UI5 Console Surface).
|
|
129
|
-
* Column header click → onSort; row action → onRowAction; permission via rowActionDisabled.
|
|
130
|
-
*/
|
|
131
|
-
export default class Table {
|
|
132
|
-
/** @type {{ id: string, title: string }[]} */
|
|
133
|
-
public columns: {
|
|
134
|
-
id: string;
|
|
135
|
-
title: string;
|
|
136
|
-
}[] = [];
|
|
137
|
-
/** @type {{ id: string, cells: string[] }[]} */
|
|
138
|
-
public rows: {
|
|
139
|
-
id: string;
|
|
140
|
-
cells: string[];
|
|
141
|
-
}[] = [];
|
|
142
|
-
public hasRowAction: boolean = false;
|
|
143
|
-
public rowActionDisabled: boolean = false;
|
|
144
|
-
public actionTitle: string = "Action";
|
|
145
|
-
public actionLabel: string = "Open";
|
|
146
|
-
public sortKey: string = "";
|
|
147
|
-
public sortDir: string = "asc";
|
|
148
|
-
public onRowAction: ((rowId: string) => void) | null = null;
|
|
149
|
-
public onSort: ((columnId: string) => void) | null = null;
|
|
150
|
-
onBodyClick(ev) {
|
|
151
|
-
const t = ev && ev.target;
|
|
152
|
-
const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-row-action]") : null;
|
|
153
|
-
if (!el || el.disabled) return;
|
|
154
|
-
const id = el.getAttribute("data-vmz-row-action");
|
|
155
|
-
if (id && typeof this.onRowAction === "function") this.onRowAction(id);
|
|
156
|
-
}
|
|
157
|
-
onHeadClick(ev) {
|
|
158
|
-
const t = ev && ev.target;
|
|
159
|
-
const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-sort]") : null;
|
|
160
|
-
if (!el) return;
|
|
161
|
-
const id = el.getAttribute("data-vmz-sort");
|
|
162
|
-
if (id && typeof this.onSort === "function") this.onSort(id);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
</script>
|
|
1
|
+
<template>
|
|
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>
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
31
|
+
<style>
|
|
32
|
+
.vmz-ui-table-wrap {
|
|
33
|
+
overflow-x: auto;
|
|
34
|
+
border: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
|
|
35
|
+
border-radius: var(--vmz-radius-lg, 8px);
|
|
36
|
+
background: var(--vmz-surface-paper);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.vmz-ui-table {
|
|
40
|
+
width: 100%;
|
|
41
|
+
border-collapse: collapse;
|
|
42
|
+
font: inherit;
|
|
43
|
+
font-size: 0.875rem;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.vmz-ui-table th,
|
|
47
|
+
.vmz-ui-table td {
|
|
48
|
+
padding: 0.75rem 1rem;
|
|
49
|
+
text-align: left;
|
|
50
|
+
border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
|
|
51
|
+
vertical-align: middle;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Header is structure, not brand: quiet ink on a tinted band. */
|
|
55
|
+
.vmz-ui-table th {
|
|
56
|
+
font-weight: 600;
|
|
57
|
+
font-size: 0.8125rem;
|
|
58
|
+
letter-spacing: 0.01em;
|
|
59
|
+
color: var(--vmz-text-steel);
|
|
60
|
+
background: var(--vmz-surface-mist);
|
|
61
|
+
white-space: nowrap;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.vmz-ui-table tbody tr {
|
|
65
|
+
transition: background-color var(--vmz-motion-control-duration, 150ms)
|
|
66
|
+
var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.vmz-ui-table tbody tr:hover {
|
|
70
|
+
background: var(--vmz-surface-mist);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.vmz-ui-table tbody tr:last-child td {
|
|
74
|
+
border-bottom: 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.vmz-ui-table__sort {
|
|
78
|
+
display: inline-flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
gap: 0.25rem;
|
|
81
|
+
font: inherit;
|
|
82
|
+
font-weight: 600;
|
|
83
|
+
padding: 0;
|
|
84
|
+
border: 0;
|
|
85
|
+
background: transparent;
|
|
86
|
+
color: inherit;
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
outline: none;
|
|
89
|
+
border-radius: var(--vmz-radius-xs, 2px);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.vmz-ui-table__sort:focus-visible {
|
|
93
|
+
box-shadow: 0 0 0 3px var(--vmz-focus-ring);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.vmz-ui-table__row-action {
|
|
97
|
+
font: inherit;
|
|
98
|
+
font-weight: 500;
|
|
99
|
+
padding: 0.25rem 0.625rem;
|
|
100
|
+
border: 1px solid var(--vmz-line-default);
|
|
101
|
+
border-radius: var(--vmz-radius-md, 6px);
|
|
102
|
+
background: var(--vmz-surface-paper);
|
|
103
|
+
color: var(--vmz-action-primary-background);
|
|
104
|
+
cursor: pointer;
|
|
105
|
+
outline: none;
|
|
106
|
+
transition:
|
|
107
|
+
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
|
+
background-color var(--vmz-motion-control-duration, 150ms) var(--vmz-motion-control-easing, cubic-bezier(0.4, 0, 0.2, 1));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.vmz-ui-table__row-action:hover:not(:disabled) {
|
|
113
|
+
color: var(--vmz-action-primary-hover);
|
|
114
|
+
border-color: var(--vmz-action-primary-hover);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.vmz-ui-table__row-action:focus-visible {
|
|
118
|
+
box-shadow: 0 0 0 3px var(--vmz-focus-ring);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.vmz-ui-table__row-action:disabled {
|
|
122
|
+
opacity: 0.55;
|
|
123
|
+
cursor: not-allowed;
|
|
124
|
+
}
|
|
125
|
+
</style>
|
|
126
|
+
<script client>
|
|
127
|
+
/**
|
|
128
|
+
* VMZ UI — Table (UI5 Console Surface).
|
|
129
|
+
* Column header click → onSort; row action → onRowAction; permission via rowActionDisabled.
|
|
130
|
+
*/
|
|
131
|
+
export default class Table {
|
|
132
|
+
/** @type {{ id: string, title: string }[]} */
|
|
133
|
+
public columns: {
|
|
134
|
+
id: string;
|
|
135
|
+
title: string;
|
|
136
|
+
}[] = [];
|
|
137
|
+
/** @type {{ id: string, cells: string[] }[]} */
|
|
138
|
+
public rows: {
|
|
139
|
+
id: string;
|
|
140
|
+
cells: string[];
|
|
141
|
+
}[] = [];
|
|
142
|
+
public hasRowAction: boolean = false;
|
|
143
|
+
public rowActionDisabled: boolean = false;
|
|
144
|
+
public actionTitle: string = "Action";
|
|
145
|
+
public actionLabel: string = "Open";
|
|
146
|
+
public sortKey: string = "";
|
|
147
|
+
public sortDir: string = "asc";
|
|
148
|
+
public onRowAction: ((rowId: string) => void) | null = null;
|
|
149
|
+
public onSort: ((columnId: string) => void) | null = null;
|
|
150
|
+
onBodyClick(ev) {
|
|
151
|
+
const t = ev && ev.target;
|
|
152
|
+
const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-row-action]") : null;
|
|
153
|
+
if (!el || el.disabled) return;
|
|
154
|
+
const id = el.getAttribute("data-vmz-row-action");
|
|
155
|
+
if (id && typeof this.onRowAction === "function") this.onRowAction(id);
|
|
156
|
+
}
|
|
157
|
+
onHeadClick(ev) {
|
|
158
|
+
const t = ev && ev.target;
|
|
159
|
+
const el = t && typeof t.closest === "function" ? t.closest("[data-vmz-sort]") : null;
|
|
160
|
+
if (!el) return;
|
|
161
|
+
const id = el.getAttribute("data-vmz-sort");
|
|
162
|
+
if (id && typeof this.onSort === "function") this.onSort(id);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
</script>
|
package/src/components/Tabs.vmz
CHANGED
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
<template>
|
|
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>
|
|
11
|
-
</div>
|
|
12
|
-
</template>
|
|
13
|
-
<style>
|
|
14
|
-
.vmz-ui-tabs {
|
|
15
|
-
display: flex;
|
|
16
|
-
flex-direction: column;
|
|
17
|
-
gap: 0.625rem;
|
|
18
|
-
max-width: 36rem;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.vmz-ui-tabs__list {
|
|
22
|
-
display: flex;
|
|
23
|
-
flex-wrap: wrap;
|
|
24
|
-
gap: 0.25rem;
|
|
25
|
-
border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.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;
|
|
34
|
-
border: 0;
|
|
35
|
-
border-bottom: 2px solid transparent;
|
|
36
|
-
border-radius: 0;
|
|
37
|
-
background: transparent;
|
|
38
|
-
color: var(--vmz-text-ink);
|
|
39
|
-
cursor: pointer;
|
|
40
|
-
outline: none;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.vmz-ui-tabs__tab[aria-selected="true"] {
|
|
44
|
-
background: transparent;
|
|
45
|
-
color: var(--vmz-action-primary-background);
|
|
46
|
-
font-weight: 500;
|
|
47
|
-
border-bottom-color: var(--vmz-action-primary-background);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.vmz-ui-tabs__tab:focus-visible {
|
|
51
|
-
box-shadow: 0 0 0 3px var(--vmz-focus-ring);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.vmz-ui-tabs__panel {
|
|
55
|
-
min-height: 2.5rem;
|
|
56
|
-
outline: none;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.vmz-ui-tabs__panel:focus-visible {
|
|
60
|
-
box-shadow: 0 0 0 3px var(--vmz-focus-ring);
|
|
61
|
-
}
|
|
62
|
-
</style>
|
|
63
|
-
<script client>
|
|
64
|
-
/**
|
|
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
|
-
*/
|
|
69
|
-
export default class Tabs {
|
|
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
|
-
}
|
|
109
|
-
}
|
|
110
|
-
</script>
|
|
1
|
+
<template>
|
|
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>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
<style>
|
|
14
|
+
.vmz-ui-tabs {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
gap: 0.625rem;
|
|
18
|
+
max-width: 36rem;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.vmz-ui-tabs__list {
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-wrap: wrap;
|
|
24
|
+
gap: 0.25rem;
|
|
25
|
+
border-bottom: 1px solid var(--vmz-line-subtle, var(--vmz-line-default));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.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;
|
|
34
|
+
border: 0;
|
|
35
|
+
border-bottom: 2px solid transparent;
|
|
36
|
+
border-radius: 0;
|
|
37
|
+
background: transparent;
|
|
38
|
+
color: var(--vmz-text-ink);
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
outline: none;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.vmz-ui-tabs__tab[aria-selected="true"] {
|
|
44
|
+
background: transparent;
|
|
45
|
+
color: var(--vmz-action-primary-background);
|
|
46
|
+
font-weight: 500;
|
|
47
|
+
border-bottom-color: var(--vmz-action-primary-background);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.vmz-ui-tabs__tab:focus-visible {
|
|
51
|
+
box-shadow: 0 0 0 3px var(--vmz-focus-ring);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.vmz-ui-tabs__panel {
|
|
55
|
+
min-height: 2.5rem;
|
|
56
|
+
outline: none;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.vmz-ui-tabs__panel:focus-visible {
|
|
60
|
+
box-shadow: 0 0 0 3px var(--vmz-focus-ring);
|
|
61
|
+
}
|
|
62
|
+
</style>
|
|
63
|
+
<script client>
|
|
64
|
+
/**
|
|
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
|
+
*/
|
|
69
|
+
export default class Tabs {
|
|
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
|
+
}
|
|
109
|
+
}
|
|
110
|
+
</script>
|
package/src/components/Tag.vmz
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<span data-vmz-ui="tag" :data-status="status || undefined" :data-vmz-tag-status="status || undefined" class="vmz-ui-tag">
|
|
3
|
-
<slot />
|
|
4
|
-
</span>
|
|
5
|
-
</template>
|
|
6
|
-
<style>
|
|
7
|
-
.vmz-ui-tag {
|
|
8
|
-
display: inline-flex;
|
|
9
|
-
align-items: center;
|
|
10
|
-
gap: 0.25rem;
|
|
11
|
-
min-height: 1.5rem;
|
|
12
|
-
padding: 0 0.5rem;
|
|
13
|
-
border: 1px solid var(--vmz-line-default);
|
|
14
|
-
border-radius: var(--vmz-radius-sm, 4px);
|
|
15
|
-
background: var(--vmz-surface-mist);
|
|
16
|
-
color: var(--vmz-text-steel);
|
|
17
|
-
font-size: 0.75rem;
|
|
18
|
-
font-weight: 500;
|
|
19
|
-
line-height: 1;
|
|
20
|
-
white-space: nowrap;
|
|
21
|
-
}
|
|
22
|
-
.vmz-ui-tag[data-status="success"] {
|
|
23
|
-
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
|
-
}
|
|
27
|
-
.vmz-ui-tag[data-status="warning"] {
|
|
28
|
-
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));
|
|
31
|
-
}
|
|
32
|
-
.vmz-ui-tag[data-status="error"] {
|
|
33
|
-
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));
|
|
36
|
-
}
|
|
37
|
-
</style>
|
|
38
|
-
<script client>
|
|
39
|
-
export default class Tag {
|
|
40
|
-
static __vmzHostBox = "contents";
|
|
41
|
-
public status: string = "";
|
|
42
|
-
}
|
|
43
|
-
</script>
|
|
1
|
+
<template>
|
|
2
|
+
<span data-vmz-ui="tag" :data-status="status || undefined" :data-vmz-tag-status="status || undefined" class="vmz-ui-tag">
|
|
3
|
+
<slot />
|
|
4
|
+
</span>
|
|
5
|
+
</template>
|
|
6
|
+
<style>
|
|
7
|
+
.vmz-ui-tag {
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: 0.25rem;
|
|
11
|
+
min-height: 1.5rem;
|
|
12
|
+
padding: 0 0.5rem;
|
|
13
|
+
border: 1px solid var(--vmz-line-default);
|
|
14
|
+
border-radius: var(--vmz-radius-sm, 4px);
|
|
15
|
+
background: var(--vmz-surface-mist);
|
|
16
|
+
color: var(--vmz-text-steel);
|
|
17
|
+
font-size: 0.75rem;
|
|
18
|
+
font-weight: 500;
|
|
19
|
+
line-height: 1;
|
|
20
|
+
white-space: nowrap;
|
|
21
|
+
}
|
|
22
|
+
.vmz-ui-tag[data-status="success"] {
|
|
23
|
+
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
|
+
}
|
|
27
|
+
.vmz-ui-tag[data-status="warning"] {
|
|
28
|
+
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));
|
|
31
|
+
}
|
|
32
|
+
.vmz-ui-tag[data-status="error"] {
|
|
33
|
+
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));
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
38
|
+
<script client>
|
|
39
|
+
export default class Tag {
|
|
40
|
+
static __vmzHostBox = "contents";
|
|
41
|
+
public status: string = "";
|
|
42
|
+
}
|
|
43
|
+
</script>
|