nuxt-unified-ui 0.5.12 → 0.5.14
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.
|
@@ -4,14 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
const props = defineProps({
|
|
6
6
|
columns: Array,
|
|
7
|
+
ui: Object,
|
|
7
8
|
loading: Boolean,
|
|
8
9
|
data: Array,
|
|
9
10
|
hidePagination: Boolean,
|
|
10
11
|
totalItems: Number,
|
|
12
|
+
itemsPerPageItems: Array,
|
|
13
|
+
rowTo: Function,
|
|
14
|
+
stickyActions: Boolean,
|
|
11
15
|
actions: Array,
|
|
12
16
|
extraActions: Array,
|
|
13
|
-
stickyActions: Boolean,
|
|
14
|
-
ui: Object,
|
|
15
17
|
meta: Object,
|
|
16
18
|
});
|
|
17
19
|
|
|
@@ -66,14 +68,32 @@ const columnPinning = computed(() => {
|
|
|
66
68
|
|
|
67
69
|
const tableUi = computed(() => {
|
|
68
70
|
return {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
...(props.ui ?? {}),
|
|
72
|
+
tr: [
|
|
73
|
+
'data-[expanded=true]:bg-elevated!',
|
|
74
|
+
props.rowTo ? 'cursor-pointer' : '',
|
|
75
|
+
(props.ui ?? {}).tr,
|
|
76
|
+
].filter(Boolean).join(' '),
|
|
71
77
|
};
|
|
72
78
|
});
|
|
73
79
|
|
|
74
80
|
|
|
75
81
|
/* actions */
|
|
76
82
|
|
|
83
|
+
const actionButtonOmitKeys = [
|
|
84
|
+
'actionType',
|
|
85
|
+
'vIf',
|
|
86
|
+
'tooltip',
|
|
87
|
+
'warning',
|
|
88
|
+
'to',
|
|
89
|
+
'href',
|
|
90
|
+
'disabled',
|
|
91
|
+
'onClick',
|
|
92
|
+
'label',
|
|
93
|
+
'items',
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
|
|
77
97
|
function resolveActionValue(value, row) {
|
|
78
98
|
if (typeof value === 'function') {
|
|
79
99
|
return value(row.original);
|
|
@@ -83,6 +103,23 @@ function resolveActionValue(value, row) {
|
|
|
83
103
|
}
|
|
84
104
|
}
|
|
85
105
|
|
|
106
|
+
function getActionWarning(action, row) {
|
|
107
|
+
return resolveActionValue(action.warning, row);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getSplitActionItems(action, row) {
|
|
111
|
+
return (resolveActionValue(action.items, row) || []).map(it => {
|
|
112
|
+
return {
|
|
113
|
+
...radOmit(it, [
|
|
114
|
+
'onClick',
|
|
115
|
+
'onSelect',
|
|
116
|
+
]),
|
|
117
|
+
label: resolveActionValue(it.label, row),
|
|
118
|
+
onSelect: () => it.onSelect?.(row.original),
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
86
123
|
function getExtraActionItems(row) {
|
|
87
124
|
return (props.extraActions || [])
|
|
88
125
|
.filter(it => !it.vIf || it.vIf(row.original))
|
|
@@ -92,6 +129,7 @@ function getExtraActionItems(row) {
|
|
|
92
129
|
'vIf',
|
|
93
130
|
'actionType',
|
|
94
131
|
'tooltip',
|
|
132
|
+
'warning',
|
|
95
133
|
'to',
|
|
96
134
|
'href',
|
|
97
135
|
'disabled',
|
|
@@ -108,13 +146,20 @@ function getExtraActionItems(row) {
|
|
|
108
146
|
|
|
109
147
|
/* pagination */
|
|
110
148
|
|
|
111
|
-
const pageSizeItems =
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
149
|
+
const pageSizeItems = computed(() => {
|
|
150
|
+
if (props.itemsPerPageItems?.length) {
|
|
151
|
+
return props.itemsPerPageItems;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
return [
|
|
155
|
+
5,
|
|
156
|
+
10,
|
|
157
|
+
25,
|
|
158
|
+
50,
|
|
159
|
+
100,
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
});
|
|
118
163
|
|
|
119
164
|
</script>
|
|
120
165
|
|
|
@@ -145,17 +190,61 @@ const pageSizeItems = [
|
|
|
145
190
|
<template v-if="!action.vIf || action.vIf(row.original)">
|
|
146
191
|
|
|
147
192
|
<template v-if="!action.actionType || action.actionType === 'button'">
|
|
148
|
-
<
|
|
149
|
-
<u-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
193
|
+
<div class="flex flex-col items-end gap-1.5">
|
|
194
|
+
<u-tooltip :text="resolveActionValue(action.tooltip, row)">
|
|
195
|
+
<span class="inline-flex">
|
|
196
|
+
<u-button
|
|
197
|
+
variant="subtle"
|
|
198
|
+
v-bind="radOmit(action, actionButtonOmitKeys)"
|
|
199
|
+
:label="resolveActionValue(action.label, row)"
|
|
200
|
+
:to="resolveActionValue(action.to, row)"
|
|
201
|
+
:href="resolveActionValue(action.href, row)"
|
|
202
|
+
:disabled="resolveActionValue(action.disabled, row)"
|
|
203
|
+
loading-auto
|
|
204
|
+
@click.stop="action.onClick?.(row.original)"
|
|
205
|
+
/>
|
|
206
|
+
</span>
|
|
207
|
+
</u-tooltip>
|
|
208
|
+
<template v-if="getActionWarning(action, row)">
|
|
209
|
+
<span class="flex items-center gap-1.5 text-sm text-warning max-w-sm">
|
|
210
|
+
<u-icon
|
|
211
|
+
name="lucide:triangle-alert"
|
|
212
|
+
class="shrink-0 size-4"
|
|
213
|
+
/>
|
|
214
|
+
<span>
|
|
215
|
+
{{ getActionWarning(action, row) }}
|
|
216
|
+
</span>
|
|
217
|
+
</span>
|
|
218
|
+
</template>
|
|
219
|
+
</div>
|
|
220
|
+
</template>
|
|
221
|
+
|
|
222
|
+
<template v-else-if="action.actionType === 'split'">
|
|
223
|
+
<div class="flex">
|
|
224
|
+
<u-tooltip :text="resolveActionValue(action.tooltip, row)">
|
|
225
|
+
<span class="inline-flex">
|
|
226
|
+
<u-button
|
|
227
|
+
variant="subtle"
|
|
228
|
+
v-bind="radOmit(action, actionButtonOmitKeys)"
|
|
229
|
+
class="rounded-e-none"
|
|
230
|
+
:label="resolveActionValue(action.label, row)"
|
|
231
|
+
:disabled="resolveActionValue(action.disabled, row)"
|
|
232
|
+
loading-auto
|
|
233
|
+
@click.stop="action.onClick?.(row.original)"
|
|
234
|
+
/>
|
|
235
|
+
</span>
|
|
236
|
+
</u-tooltip>
|
|
237
|
+
<u-dropdown-menu :items="getSplitActionItems(action, row)">
|
|
238
|
+
<u-button
|
|
239
|
+
variant="subtle"
|
|
240
|
+
:color="action.color"
|
|
241
|
+
icon="lucide:chevron-down"
|
|
242
|
+
class="rounded-s-none"
|
|
243
|
+
:disabled="resolveActionValue(action.disabled, row)"
|
|
244
|
+
@click.stop
|
|
245
|
+
/>
|
|
246
|
+
</u-dropdown-menu>
|
|
247
|
+
</div>
|
|
159
248
|
</template>
|
|
160
249
|
|
|
161
250
|
<template v-else-if="action.actionType === 'separator'">
|
|
@@ -181,6 +270,7 @@ const pageSizeItems = [
|
|
|
181
270
|
<u-button
|
|
182
271
|
variant="subtle"
|
|
183
272
|
icon="lucide:ellipsis-vertical"
|
|
273
|
+
@click.stop
|
|
184
274
|
/>
|
|
185
275
|
</u-dropdown-menu>
|
|
186
276
|
|
package/package.json
CHANGED
|
@@ -522,8 +522,8 @@ Practical shortcuts:
|
|
|
522
522
|
|
|
523
523
|
- `u-button`: `variant` → `color` → `size` → `icon` → label/value → `block` → `disabled` → `loading-auto` → events
|
|
524
524
|
- `u-input` / `u-select*`: user-facing props (`:placeholder`, `:label`) → transient state (`:loading`, `:disabled`) → data source (`:items`) → `class` → `v-model` → events
|
|
525
|
-
- Action objects (`:actions`, `:append-actions`, table row actions): `vIf` → `actionType` → `color` → `icon` → `label` → `tooltip` → `disabled` → `to` → `href` → `onClick`
|
|
526
|
-
- `un-table`: `:columns` → `class` / `:ui` → `:loading` → `:data` → `hide-pagination` → `:total-items` → `v-model:items-per-page` → `v-model:current-page` → `sticky-actions` → `:actions` → `:extra-actions` → `:meta` (omit unused). Full table rules → [tables.md](tables.md)
|
|
525
|
+
- Action objects (`:actions`, `:append-actions`, table row actions): `vIf` → `actionType` → `color` → `icon` → `label` → `tooltip` → `warning` → `disabled` → `to` → `href` → `onClick` → `items`
|
|
526
|
+
- `un-table`: `:columns` → `class` / `:ui` → `:loading` → `:data` → `hide-pagination` → `:total-items` → `:items-per-page-items` → `:row-to` → `v-model:items-per-page` → `v-model:current-page` → `sticky-actions` → `:actions` → `:extra-actions` → `:meta` (omit unused). Full table rules → [tables.md](tables.md)
|
|
527
527
|
- Tab / select item objects: `value` → `icon` → `label`
|
|
528
528
|
|
|
529
529
|
### Default attribute values (omit noise)
|
|
@@ -12,7 +12,7 @@ Reference components in **kebab-case** in templates.
|
|
|
12
12
|
| `un-card` | `un-card.vue` | Card + typography + action rows |
|
|
13
13
|
| `un-typography` | `un-typography.vue` | Icon / title / subtitle / text / append |
|
|
14
14
|
| `un-spinner` | `un-spinner.vue` | Spinning Lucide loader icon |
|
|
15
|
-
| `un-table` | `un-table.vue` | `u-table` + row actions + pagination footer → [tables.md](tables.md) |
|
|
15
|
+
| `un-table` | `un-table.vue` | `u-table` + row actions (button / split / warning) + pagination footer → [tables.md](tables.md) |
|
|
16
16
|
|
|
17
17
|
## Composables
|
|
18
18
|
|
|
@@ -27,14 +27,16 @@ Do **not** add sort, filter, or selection props to `un-table`. Resource dashboar
|
|
|
27
27
|
| `actions` | Visible row buttons; adds the trailing `actions` column |
|
|
28
28
|
| `extraActions` | Overflow `u-dropdown-menu` (ellipsis); also creates the column |
|
|
29
29
|
| `stickyActions` | Pin the `actions` column to the right |
|
|
30
|
-
| `
|
|
30
|
+
| `rowTo` | Adds `cursor-pointer` on rows; parent still owns navigation |
|
|
31
|
+
| `ui` | Merged into `u-table` `:ui`. Default `tr` classes (`data-[expanded=true]:bg-elevated!`, plus `cursor-pointer` when `rowTo` is set) are prepended to `ui.tr` |
|
|
31
32
|
| `meta` | Passed through to `u-table` |
|
|
32
33
|
| `v-model:itemsPerPage` | Page size (default `'25'`; choices 5 / 10 / 25 / 50 / 100) |
|
|
34
|
+
| `itemsPerPageItems` | Overrides the page-size select options |
|
|
33
35
|
| `v-model:currentPage` | Page number (default `'1'`) |
|
|
34
36
|
|
|
35
37
|
The actions column is added when **either** `actions` or `extraActions` has length. `#actions-cell` is then owned by the wrapper — do not override it.
|
|
36
38
|
|
|
37
|
-
`to` / `href` / `disabled` on an action may be a value or `(row) => …`. `vIf(row)` hides the item. `onClick(row)` receives the original row. Buttons use `loading-auto`.
|
|
39
|
+
`to` / `href` / `disabled` / `label` / `tooltip` / `warning` on an action may be a value or `(row) => …`. `vIf(row)` hides the item. `onClick(row)` receives the original row. Action clicks use `@click.stop` so they do not select the row. Buttons use `loading-auto`.
|
|
38
40
|
|
|
39
41
|
## Attribute order on `<un-table>`
|
|
40
42
|
|
|
@@ -46,12 +48,14 @@ Omit unused props. When present, write them in this order:
|
|
|
46
48
|
4. `:data`
|
|
47
49
|
5. `hide-pagination`
|
|
48
50
|
6. `:total-items`
|
|
49
|
-
7.
|
|
50
|
-
8.
|
|
51
|
-
9. `
|
|
52
|
-
10.
|
|
53
|
-
11.
|
|
54
|
-
12. `:
|
|
51
|
+
7. `:items-per-page-items`
|
|
52
|
+
8. `:row-to`
|
|
53
|
+
9. `v-model:items-per-page`
|
|
54
|
+
10. `v-model:current-page`
|
|
55
|
+
11. `sticky-actions`
|
|
56
|
+
12. `:actions`
|
|
57
|
+
13. `:extra-actions`
|
|
58
|
+
14. `:meta`
|
|
55
59
|
|
|
56
60
|
Models are always **page size, then page**. Closing `>` sits on the same line as the last attribute.
|
|
57
61
|
|
|
@@ -160,9 +164,19 @@ Two channels:
|
|
|
160
164
|
|
|
161
165
|
Use `actions` for the one or two primary row operations (view, edit, delete). Use `extraActions` for the rest (assign, revoke, copy, archive). A vertical separator is inserted between the two groups automatically.
|
|
162
166
|
|
|
167
|
+
`actionType` values on `actions`:
|
|
168
|
+
|
|
169
|
+
| `actionType` | UI |
|
|
170
|
+
|--------------|----|
|
|
171
|
+
| omitted / `'button'` | Inline `u-button` |
|
|
172
|
+
| `'split'` | Primary button plus a chevron `u-dropdown-menu` |
|
|
173
|
+
| `'separator'` | Vertical rule between button groups |
|
|
174
|
+
|
|
175
|
+
`warning` is a string or `(row) => string | undefined` rendered under the button (triangle + text). Split `items` may be an array or `(row) => array`. Each item uses `label` (value or `(row) => …`) and `onSelect(row)` — do not put `onClick` on split items.
|
|
176
|
+
|
|
163
177
|
Field order on **every** action object (omit unused):
|
|
164
178
|
|
|
165
|
-
`vIf` → `actionType` → `color` → `icon` → `label` → `tooltip` → `disabled` → `to` → `href` → `onClick`
|
|
179
|
+
`vIf` → `actionType` → `color` → `icon` → `label` → `tooltip` → `warning` → `disabled` → `to` → `href` → `onClick` → `items`
|
|
166
180
|
|
|
167
181
|
```js
|
|
168
182
|
const itemActions = computed(() => {
|
|
@@ -180,11 +194,30 @@ const itemActions = computed(() => {
|
|
|
180
194
|
{
|
|
181
195
|
actionType: 'separator',
|
|
182
196
|
},
|
|
197
|
+
{
|
|
198
|
+
actionType: 'split',
|
|
199
|
+
icon: 'lucide:download',
|
|
200
|
+
tooltip: 'Download',
|
|
201
|
+
onClick: handleItemDownloadFile,
|
|
202
|
+
items: [
|
|
203
|
+
{
|
|
204
|
+
icon: 'lucide:file-text',
|
|
205
|
+
label: 'Download file',
|
|
206
|
+
onSelect: handleItemDownloadFile,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
icon: 'lucide:file-archive',
|
|
210
|
+
label: 'Download archive',
|
|
211
|
+
onSelect: handleItemDownloadArchive,
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
},
|
|
183
215
|
{
|
|
184
216
|
vIf: it => it.status !== 'archived',
|
|
185
217
|
color: 'error',
|
|
186
218
|
icon: 'lucide:trash',
|
|
187
|
-
tooltip: 'Delete',
|
|
219
|
+
tooltip: it => it.role === 'admin' ? 'Admins cannot be deleted' : 'Delete',
|
|
220
|
+
warning: it => it.role === 'admin' ? 'Admins cannot be deleted' : undefined,
|
|
188
221
|
disabled: it => it.role === 'admin',
|
|
189
222
|
onClick: handleItemDelete,
|
|
190
223
|
},
|
|
@@ -216,8 +249,9 @@ Rules:
|
|
|
216
249
|
- Destructive: `color: 'error'` before `icon`.
|
|
217
250
|
- Emphasized extra action: `color: 'primary'` before `icon`.
|
|
218
251
|
- `{ actionType: 'separator' }` is a lone-key object between visual groups in `actions`.
|
|
252
|
+
- `{ actionType: 'split', items, onClick }` is a default click plus overflow choices; item handlers are `onSelect`.
|
|
219
253
|
- Omit `variant: 'subtle'` — that is the button default.
|
|
220
|
-
- `vIf` / `disabled` / `to` / `href` take `(row) => …` when they depend on the row.
|
|
254
|
+
- `vIf` / `disabled` / `to` / `href` / `label` / `tooltip` / `warning` take `(row) => …` when they depend on the row.
|
|
221
255
|
- Do not put toolbar Create / Refresh on the row. Those belong on the parent `un-card` (`:actions` / `:append-actions`).
|
|
222
256
|
|
|
223
257
|
Resource managers prepend custom row actions, then default Edit / Delete:
|
|
@@ -251,6 +285,7 @@ const currentPage = ref(1);
|
|
|
251
285
|
```
|
|
252
286
|
|
|
253
287
|
- Layer default page size is `25` if the parent does not bind the model. Host pages often use `10` or `20` — set the ref explicitly.
|
|
288
|
+
- Page-size choices default to `5 / 10 / 25 / 50 / 100`. Pass `:items-per-page-items` to replace that list.
|
|
254
289
|
- Server lists: `skip = (currentPage - 1) * itemsPerPage`, `limit = itemsPerPage`, `total-items` from the `/count` endpoint.
|
|
255
290
|
- Client lists: pass `data` already sliced; `total-items` is the uncut length.
|
|
256
291
|
- Reset `currentPage` to `1` when page size, filters, or the resource path change.
|