@groupteknology/vuno 0.3.0 → 0.6.0
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 +174 -2
- package/dist/module.json +1 -1
- package/dist/runtime/app/components/Cell/CellBadge.d.vue.ts +5 -0
- package/dist/runtime/app/components/Cell/CellBadge.vue +24 -0
- package/dist/runtime/app/components/Cell/CellBadge.vue.d.ts +5 -0
- package/dist/runtime/app/components/Cell/CellBoolean.d.vue.ts +5 -0
- package/dist/runtime/app/components/Cell/CellBoolean.vue +21 -0
- package/dist/runtime/app/components/Cell/CellBoolean.vue.d.ts +5 -0
- package/dist/runtime/app/components/Cell/CellCurrency.d.vue.ts +5 -0
- package/dist/runtime/app/components/Cell/CellCurrency.vue +14 -0
- package/dist/runtime/app/components/Cell/CellCurrency.vue.d.ts +5 -0
- package/dist/runtime/app/components/Cell/CellDate.d.vue.ts +5 -0
- package/dist/runtime/app/components/Cell/CellDate.vue +19 -0
- package/dist/runtime/app/components/Cell/CellDate.vue.d.ts +5 -0
- package/dist/runtime/app/components/Cell/CellImage.d.vue.ts +5 -0
- package/dist/runtime/app/components/Cell/CellImage.vue +22 -0
- package/dist/runtime/app/components/Cell/CellImage.vue.d.ts +5 -0
- package/dist/runtime/app/components/Cell/CellNumber.d.vue.ts +5 -0
- package/dist/runtime/app/components/Cell/CellNumber.vue +15 -0
- package/dist/runtime/app/components/Cell/CellNumber.vue.d.ts +5 -0
- package/dist/runtime/app/components/Cell/CellText.d.vue.ts +5 -0
- package/dist/runtime/app/components/Cell/CellText.vue +13 -0
- package/dist/runtime/app/components/Cell/CellText.vue.d.ts +5 -0
- package/dist/runtime/app/components/Field/FieldSwitch.d.vue.ts +5 -0
- package/dist/runtime/app/components/Field/FieldSwitch.vue +27 -0
- package/dist/runtime/app/components/Field/FieldSwitch.vue.d.ts +5 -0
- package/dist/runtime/app/components/Form/FormField.vue +18 -16
- package/dist/runtime/app/components/Form/FormModal.d.vue.ts +6 -0
- package/dist/runtime/app/components/Form/FormModal.vue +47 -46
- package/dist/runtime/app/components/Form/FormModal.vue.d.ts +6 -0
- package/dist/runtime/app/components/Table/Table.vue +273 -264
- package/dist/runtime/app/components/Table/TableCell.vue +24 -73
- package/dist/runtime/app/composables/useVunoCrud.js +9 -1
- package/dist/runtime/app/theme/index.js +18 -1
- package/dist/runtime/app/types/crud.d.ts +17 -1
- package/dist/runtime/app/types/field.d.ts +8 -1
- package/dist/runtime/app/types/table.d.ts +80 -13
- package/dist/runtime/app/types/theme.d.ts +6 -0
- package/dist/runtime/app/utils/column.d.ts +9 -0
- package/dist/runtime/app/utils/column.js +5 -0
- package/package.json +1 -1
|
@@ -1,84 +1,35 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed } from "vue";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
3
|
+
import CellBadge from "#vuno/components/Cell/CellBadge.vue";
|
|
4
|
+
import CellBoolean from "#vuno/components/Cell/CellBoolean.vue";
|
|
5
|
+
import CellCurrency from "#vuno/components/Cell/CellCurrency.vue";
|
|
6
|
+
import CellDate from "#vuno/components/Cell/CellDate.vue";
|
|
7
|
+
import CellImage from "#vuno/components/Cell/CellImage.vue";
|
|
8
|
+
import CellNumber from "#vuno/components/Cell/CellNumber.vue";
|
|
9
|
+
import CellText from "#vuno/components/Cell/CellText.vue";
|
|
10
|
+
import { resolveColumnValue } from "#vuno/utils/column";
|
|
6
11
|
const props = defineProps({
|
|
7
12
|
column: { type: null, required: true },
|
|
8
13
|
row: { type: Object, required: true }
|
|
9
14
|
});
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
color: entry?.color ?? props.column.color ?? "neutral",
|
|
22
|
-
label: entry?.label ?? text.value
|
|
23
|
-
};
|
|
24
|
-
});
|
|
25
|
-
const alt = computed(() => {
|
|
26
|
-
if (props.column.type !== "image") return "";
|
|
27
|
-
if (props.column.altKey) return String(getPath(props.row, props.column.altKey) ?? "");
|
|
28
|
-
return props.column.alt ?? "";
|
|
29
|
-
});
|
|
15
|
+
const components = {
|
|
16
|
+
badge: CellBadge,
|
|
17
|
+
boolean: CellBoolean,
|
|
18
|
+
currency: CellCurrency,
|
|
19
|
+
date: CellDate,
|
|
20
|
+
image: CellImage,
|
|
21
|
+
number: CellNumber,
|
|
22
|
+
text: CellText
|
|
23
|
+
};
|
|
24
|
+
const value = computed(() => resolveColumnValue(props.column, props.row));
|
|
25
|
+
const component = computed(() => components[props.column.type] ?? CellText);
|
|
30
26
|
</script>
|
|
31
27
|
|
|
32
28
|
<template>
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
</span>
|
|
39
|
-
|
|
40
|
-
<span
|
|
41
|
-
v-else-if="props.column.type === 'currency'"
|
|
42
|
-
class="tabular-nums"
|
|
43
|
-
>
|
|
44
|
-
{{ formatCurrency(value, locale.code.value, props.column.currency, props.column.decimals) }}
|
|
45
|
-
</span>
|
|
46
|
-
|
|
47
|
-
<span
|
|
48
|
-
v-else-if="props.column.type === 'date'"
|
|
49
|
-
class="tabular-nums"
|
|
50
|
-
>
|
|
51
|
-
{{ formatDate(value, locale.code.value, { dateStyle: props.column.dateStyle ?? "medium", ...props.column.timeStyle ? { timeStyle: props.column.timeStyle } : {} }) }}
|
|
52
|
-
</span>
|
|
53
|
-
|
|
54
|
-
<UBadge
|
|
55
|
-
v-else-if="props.column.type === 'badge' && badge"
|
|
56
|
-
:color="badge.color"
|
|
57
|
-
:label="badge.label"
|
|
58
|
-
variant="subtle"
|
|
29
|
+
<component
|
|
30
|
+
:is="component"
|
|
31
|
+
:column="props.column"
|
|
32
|
+
:row="props.row"
|
|
33
|
+
:value="value"
|
|
59
34
|
/>
|
|
60
|
-
|
|
61
|
-
<UIcon
|
|
62
|
-
v-else-if="props.column.type === 'boolean' && !props.column.trueLabel && !props.column.falseLabel"
|
|
63
|
-
:class="value ? 'text-success' : 'text-muted'"
|
|
64
|
-
:name="value ? 'lucide:check' : 'lucide:minus'"
|
|
65
|
-
/>
|
|
66
|
-
|
|
67
|
-
<span v-else-if="props.column.type === 'boolean'">
|
|
68
|
-
{{ value ? props.column.trueLabel ?? "" : props.column.falseLabel ?? "" }}
|
|
69
|
-
</span>
|
|
70
|
-
|
|
71
|
-
<UAvatar
|
|
72
|
-
v-else-if="props.column.type === 'image'"
|
|
73
|
-
:alt="alt"
|
|
74
|
-
:size="props.column.size ?? 'md'"
|
|
75
|
-
:src="text"
|
|
76
|
-
/>
|
|
77
|
-
|
|
78
|
-
<span
|
|
79
|
-
v-else
|
|
80
|
-
:class="props.column.type === 'text' && props.column.truncate ? 'block max-w-[32ch] truncate' : void 0"
|
|
81
|
-
>
|
|
82
|
-
{{ text }}
|
|
83
|
-
</span>
|
|
84
35
|
</template>
|
|
@@ -43,7 +43,15 @@ export function useVunoCrud(options) {
|
|
|
43
43
|
});
|
|
44
44
|
const items = computed(() => data.value?.data ?? []);
|
|
45
45
|
const total = computed(() => data.value?.total ?? 0);
|
|
46
|
-
const failure = computed(
|
|
46
|
+
const failure = computed(
|
|
47
|
+
() => isError.value ? {
|
|
48
|
+
// Sin valor propio los deja en `undefined` y el estado de
|
|
49
|
+
// fallo cae a los textos globales de `vuno.messages`.
|
|
50
|
+
hint: options.messages?.listFailedHint,
|
|
51
|
+
title: options.messages?.listFailed,
|
|
52
|
+
retry: () => void refetch()
|
|
53
|
+
} : void 0
|
|
54
|
+
);
|
|
47
55
|
function invalidate() {
|
|
48
56
|
if (!resolvedKey.value.ready) return;
|
|
49
57
|
void queryClient.invalidateQueries({ queryKey: baseKey.value });
|
|
@@ -50,6 +50,9 @@ export const defaultTheme = {
|
|
|
50
50
|
fieldSlug: {
|
|
51
51
|
slots: { root: "w-full", trailing: "pe-1" }
|
|
52
52
|
},
|
|
53
|
+
fieldSwitch: {
|
|
54
|
+
slots: { root: "w-full" }
|
|
55
|
+
},
|
|
53
56
|
fieldText: {
|
|
54
57
|
slots: { root: "w-full" }
|
|
55
58
|
},
|
|
@@ -135,7 +138,18 @@ export const defaultTheme = {
|
|
|
135
138
|
root: "flex w-full flex-col gap-4",
|
|
136
139
|
search: "w-full sm:max-w-xs",
|
|
137
140
|
selection: "bg-elevated/50 flex items-center justify-between gap-2 rounded-lg px-3 py-2 text-sm",
|
|
138
|
-
|
|
141
|
+
/**
|
|
142
|
+
* `border-separate` es obligatorio, no una preferencia: con
|
|
143
|
+
* `border-collapse: collapse` el navegador ignora el `border-radius`
|
|
144
|
+
* de una tabla y el marco saldría cuadrado. El `border-spacing-0`
|
|
145
|
+
* devuelve el aspecto de colapsada.
|
|
146
|
+
*
|
|
147
|
+
* Se sostiene sobre dos cosas de Nuxt UI: que la cabecera no lleva
|
|
148
|
+
* fondo propio —si lo llevara, taparía las esquinas de arriba— y que
|
|
149
|
+
* la última fila no tiene borde inferior, que si no doblaría la
|
|
150
|
+
* línea con la del marco.
|
|
151
|
+
*/
|
|
152
|
+
table: "border-default min-w-full rounded-lg border border-separate border-spacing-0",
|
|
139
153
|
/**
|
|
140
154
|
* Nuxt UI solo aplica hover a las filas con `data-selectable`, que
|
|
141
155
|
* son las que reciben un manejador de selección. Aquí se aplica
|
|
@@ -153,6 +167,8 @@ export const defaultTheme = {
|
|
|
153
167
|
export const defaultIcons = {
|
|
154
168
|
actions: "lucide:ellipsis-vertical",
|
|
155
169
|
addItem: "lucide:plus",
|
|
170
|
+
booleanFalse: "lucide:minus",
|
|
171
|
+
booleanTrue: "lucide:check",
|
|
156
172
|
clearSearch: "lucide:x",
|
|
157
173
|
confirm: "lucide:triangle-alert",
|
|
158
174
|
empty: "lucide:inbox",
|
|
@@ -196,6 +212,7 @@ export const defaultMessages = {
|
|
|
196
212
|
filtersTitle: "Filtrar resultados",
|
|
197
213
|
of: "de",
|
|
198
214
|
removeItem: "Eliminar elemento",
|
|
215
|
+
results: "resultados",
|
|
199
216
|
retry: "Reintentar",
|
|
200
217
|
rowActions: "Acciones",
|
|
201
218
|
rowSelected: "fila seleccionada",
|
|
@@ -10,6 +10,14 @@ export type VunoCrudMessages = {
|
|
|
10
10
|
createFailed: string;
|
|
11
11
|
deleted: string;
|
|
12
12
|
deleteFailed: string;
|
|
13
|
+
/** Título cuando falla el listado. Por defecto, el de `vuno.messages`. */
|
|
14
|
+
listFailed: string;
|
|
15
|
+
/**
|
|
16
|
+
* Pista bajo ese título. Es el sitio para lo que evita un error a quien
|
|
17
|
+
* mira una tabla que no cargó: «no des una de alta sin verlas, puede
|
|
18
|
+
* existir ya».
|
|
19
|
+
*/
|
|
20
|
+
listFailedHint: string;
|
|
13
21
|
updated: string;
|
|
14
22
|
updateFailed: string;
|
|
15
23
|
};
|
|
@@ -55,7 +63,15 @@ export type VunoCrudOptions<TRow extends VunoTableRow, TForm> = {
|
|
|
55
63
|
emptyForm: () => TForm;
|
|
56
64
|
list: (query: VunoTableQuery) => Promise<VunoCrudPage<TRow>>;
|
|
57
65
|
remove?: (row: TRow) => Promise<unknown>;
|
|
58
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Convierte una fila en el estado del formulario al editar.
|
|
68
|
+
*
|
|
69
|
+
* Es opcional solo mientras la fila y el formulario tengan la misma forma.
|
|
70
|
+
* En cuanto la fila traiga campos que no son del formulario —un contador,
|
|
71
|
+
* una fecha de alta— hace falta: sin él se copia la fila entera al estado
|
|
72
|
+
* y esos campos acaban viajando en el guardado, que es donde se nota, lejos
|
|
73
|
+
* de aquí.
|
|
74
|
+
*/
|
|
59
75
|
toForm?: (row: TRow) => TForm;
|
|
60
76
|
update?: (id: number | string, form: TForm) => Promise<unknown>;
|
|
61
77
|
};
|
|
@@ -52,6 +52,13 @@ export type VunoFieldArray = VunoFieldBase & {
|
|
|
52
52
|
export type VunoFieldCheckbox = VunoFieldBase & {
|
|
53
53
|
indeterminate?: boolean;
|
|
54
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* Interruptor para un booleano que es un estado encendido o apagado, no una
|
|
57
|
+
* opción que se marca. La diferencia con `checkbox` es de lectura, no de dato:
|
|
58
|
+
* en una pantalla de ajustes el interruptor dice «esto está activo» y la
|
|
59
|
+
* casilla dice «esto queda seleccionado».
|
|
60
|
+
*/
|
|
61
|
+
export type VunoFieldSwitch = VunoFieldBase;
|
|
55
62
|
export type VunoFieldColor = VunoFieldBase & {
|
|
56
63
|
placeholder?: string;
|
|
57
64
|
};
|
|
@@ -112,7 +119,7 @@ export type VunoFieldTextarea = VunoFieldBase & {
|
|
|
112
119
|
* Unión discriminada por `type`. Es lo que consume `VunoFormDefinition`, de modo
|
|
113
120
|
* que cada campo de la definición queda tipado con sus propios props.
|
|
114
121
|
*/
|
|
115
|
-
export type VunoField = WithType<VunoFieldArray, 'array'> | WithType<VunoFieldCheckbox, 'checkbox'> | WithType<VunoFieldColor, 'color'> | WithType<VunoFieldEmail, 'email'> | WithType<VunoFieldFile, 'file'> | WithType<VunoFieldNumber, 'number'> | WithType<VunoFieldPassword, 'password'> | WithType<VunoFieldRadio, 'radio'> | WithType<VunoFieldSelect, 'select'> | WithType<VunoFieldSlug, 'slug'> | WithType<VunoFieldText, 'text'> | WithType<VunoFieldTextarea, 'textarea'>;
|
|
122
|
+
export type VunoField = WithType<VunoFieldArray, 'array'> | WithType<VunoFieldCheckbox, 'checkbox'> | WithType<VunoFieldColor, 'color'> | WithType<VunoFieldEmail, 'email'> | WithType<VunoFieldFile, 'file'> | WithType<VunoFieldNumber, 'number'> | WithType<VunoFieldPassword, 'password'> | WithType<VunoFieldRadio, 'radio'> | WithType<VunoFieldSelect, 'select'> | WithType<VunoFieldSlug, 'slug'> | WithType<VunoFieldSwitch, 'switch'> | WithType<VunoFieldText, 'text'> | WithType<VunoFieldTextarea, 'textarea'>;
|
|
116
123
|
export type VunoFieldType = VunoField['type'];
|
|
117
124
|
/** Extrae los props de un tipo concreto de campo: `VunoFieldOf<'slug'>`. */
|
|
118
125
|
export type VunoFieldOf<TType extends VunoFieldType> = Extract<VunoField, {
|
|
@@ -41,7 +41,18 @@ export type VunoTableEmpty = {
|
|
|
41
41
|
title?: string;
|
|
42
42
|
};
|
|
43
43
|
export type VunoTableAlign = 'center' | 'end' | 'start';
|
|
44
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Una fila. `object` y no `Record<string, unknown>` a propósito: un `interface`
|
|
46
|
+
* de TypeScript no tiene firma de índice implícita, así que no satisface un
|
|
47
|
+
* `Record`, y los tipos de respuesta de una API se declaran como `interface`
|
|
48
|
+
* casi siempre. Con el `Record`, cada consumidor tenía que escribir
|
|
49
|
+
* `type Fila = SuInterface & Record<string, unknown>` para poder usar la tabla,
|
|
50
|
+
* y el error que salía —«no assignable to VunoTableRow»— no señalaba la causa.
|
|
51
|
+
*
|
|
52
|
+
* La firma de índice no hacía falta para nada: los valores se leen con
|
|
53
|
+
* `getPath`, que recorre el objeto en tiempo de ejecución y acepta `unknown`.
|
|
54
|
+
*/
|
|
55
|
+
export type VunoTableRow = object;
|
|
45
56
|
/** Color semántico de Nuxt UI; el proyecto consumidor decide qué tono es cada uno. */
|
|
46
57
|
export type VunoColor = 'error' | 'info' | 'neutral' | 'primary' | 'secondary' | 'success' | 'warning';
|
|
47
58
|
/**
|
|
@@ -49,31 +60,52 @@ export type VunoColor = 'error' | 'info' | 'neutral' | 'primary' | 'secondary' |
|
|
|
49
60
|
* que es quien sabe sobre qué campos puede buscar; declararlo aquí sugeriría
|
|
50
61
|
* que la tabla filtra, y no lo hace.
|
|
51
62
|
*/
|
|
52
|
-
export type VunoTableColumnBase = {
|
|
63
|
+
export type VunoTableColumnBase<TRow extends VunoTableRow = VunoTableRow> = {
|
|
53
64
|
align?: VunoTableAlign;
|
|
54
65
|
class?: string;
|
|
55
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Ruta del valor en la fila. Admite anidado: `'proveedor.nombre'`.
|
|
68
|
+
*
|
|
69
|
+
* Sigue haciendo falta con `value`: nombra la columna para el orden y para
|
|
70
|
+
* el slot `cell-<key>`.
|
|
71
|
+
*/
|
|
56
72
|
key: string;
|
|
57
73
|
label?: string;
|
|
58
74
|
sortable?: boolean;
|
|
59
75
|
ui?: VunoFieldSlots;
|
|
60
76
|
width?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Calcula el valor a partir de la fila entera, en lugar de leerlo de `key`.
|
|
79
|
+
*
|
|
80
|
+
* ```ts
|
|
81
|
+
* { key: 'role', type: 'badge', value: (row) => traducirRol(row.role) }
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* Sustituye de dónde sale el valor, no cómo se pinta: lo que devuelve sigue
|
|
85
|
+
* pasando por el formato de su tipo de columna —moneda, fecha, el `map` de
|
|
86
|
+
* un badge—, así que se combinan.
|
|
87
|
+
*
|
|
88
|
+
* Cuidado al ordenar: `sortable` ordena en servidor por `key`, y el servidor
|
|
89
|
+
* no conoce esta función. Si el valor mostrado no se deriva de `key` de
|
|
90
|
+
* forma monótona, el orden no coincidirá con lo que se ve.
|
|
91
|
+
*/
|
|
92
|
+
value?: (row: TRow) => unknown;
|
|
61
93
|
};
|
|
62
|
-
export type VunoTableColumnText = VunoTableColumnBase & {
|
|
94
|
+
export type VunoTableColumnText<TRow extends VunoTableRow = VunoTableRow> = VunoTableColumnBase<TRow> & {
|
|
63
95
|
truncate?: boolean;
|
|
64
96
|
};
|
|
65
|
-
export type VunoTableColumnNumber = VunoTableColumnBase & {
|
|
97
|
+
export type VunoTableColumnNumber<TRow extends VunoTableRow = VunoTableRow> = VunoTableColumnBase<TRow> & {
|
|
66
98
|
decimals?: number;
|
|
67
99
|
};
|
|
68
|
-
export type VunoTableColumnCurrency = VunoTableColumnBase & {
|
|
100
|
+
export type VunoTableColumnCurrency<TRow extends VunoTableRow = VunoTableRow> = VunoTableColumnBase<TRow> & {
|
|
69
101
|
currency?: string;
|
|
70
102
|
decimals?: number;
|
|
71
103
|
};
|
|
72
|
-
export type VunoTableColumnDate = VunoTableColumnBase & {
|
|
104
|
+
export type VunoTableColumnDate<TRow extends VunoTableRow = VunoTableRow> = VunoTableColumnBase<TRow> & {
|
|
73
105
|
dateStyle?: 'full' | 'long' | 'medium' | 'short';
|
|
74
106
|
timeStyle?: 'full' | 'long' | 'medium' | 'short';
|
|
75
107
|
};
|
|
76
|
-
export type VunoTableColumnBoolean = VunoTableColumnBase & {
|
|
108
|
+
export type VunoTableColumnBoolean<TRow extends VunoTableRow = VunoTableRow> = VunoTableColumnBase<TRow> & {
|
|
77
109
|
falseLabel?: string;
|
|
78
110
|
trueLabel?: string;
|
|
79
111
|
};
|
|
@@ -82,18 +114,18 @@ export type VunoTableBadgeMap = Record<string, {
|
|
|
82
114
|
color?: VunoColor;
|
|
83
115
|
label?: string;
|
|
84
116
|
}>;
|
|
85
|
-
export type VunoTableColumnBadge = VunoTableColumnBase & {
|
|
117
|
+
export type VunoTableColumnBadge<TRow extends VunoTableRow = VunoTableRow> = VunoTableColumnBase<TRow> & {
|
|
86
118
|
color?: VunoColor;
|
|
87
119
|
map?: VunoTableBadgeMap;
|
|
88
120
|
};
|
|
89
|
-
export type VunoTableColumnImage = VunoTableColumnBase & {
|
|
121
|
+
export type VunoTableColumnImage<TRow extends VunoTableRow = VunoTableRow> = VunoTableColumnBase<TRow> & {
|
|
90
122
|
alt?: string;
|
|
91
123
|
/** Clave de la fila de la que sacar el texto alternativo. */
|
|
92
124
|
altKey?: string;
|
|
93
125
|
size?: 'lg' | 'md' | 'sm' | 'xs';
|
|
94
126
|
};
|
|
95
127
|
/** Delega el contenido en el slot `cell-<key>`. */
|
|
96
|
-
export type VunoTableColumnCustom = VunoTableColumnBase
|
|
128
|
+
export type VunoTableColumnCustom<TRow extends VunoTableRow = VunoTableRow> = VunoTableColumnBase<TRow>;
|
|
97
129
|
export type VunoTableAction<TRow extends VunoTableRow = VunoTableRow> = {
|
|
98
130
|
onSelect: (row: TRow) => void;
|
|
99
131
|
color?: VunoColor;
|
|
@@ -105,12 +137,29 @@ export type VunoTableAction<TRow extends VunoTableRow = VunoTableRow> = {
|
|
|
105
137
|
/** Oculta la acción para esa fila concreta. */
|
|
106
138
|
hidden?: (row: TRow) => boolean;
|
|
107
139
|
};
|
|
108
|
-
export type VunoTableColumnActions<TRow extends VunoTableRow = VunoTableRow> = VunoTableColumnBase & {
|
|
140
|
+
export type VunoTableColumnActions<TRow extends VunoTableRow = VunoTableRow> = VunoTableColumnBase<TRow> & {
|
|
109
141
|
actions: VunoTableAction<TRow>[];
|
|
110
142
|
};
|
|
111
143
|
/** Unión discriminada por `type`, igual que `VunoField` en los formularios. */
|
|
112
|
-
export type VunoTableColumn<TRow extends VunoTableRow = VunoTableRow> = WithType<VunoTableColumnActions<TRow>, 'actions'> | WithType<VunoTableColumnBadge
|
|
144
|
+
export type VunoTableColumn<TRow extends VunoTableRow = VunoTableRow> = WithType<VunoTableColumnActions<TRow>, 'actions'> | WithType<VunoTableColumnBadge<TRow>, 'badge'> | WithType<VunoTableColumnBoolean<TRow>, 'boolean'> | WithType<VunoTableColumnCurrency<TRow>, 'currency'> | WithType<VunoTableColumnCustom<TRow>, 'custom'> | WithType<VunoTableColumnDate<TRow>, 'date'> | WithType<VunoTableColumnImage<TRow>, 'image'> | WithType<VunoTableColumnNumber<TRow>, 'number'> | WithType<VunoTableColumnText<TRow>, 'text'>;
|
|
113
145
|
export type VunoTableColumnType = VunoTableColumn['type'];
|
|
146
|
+
/** Extrae los props de un tipo concreto de columna: `VunoTableColumnOf<'badge'>`. */
|
|
147
|
+
export type VunoTableColumnOf<TType extends VunoTableColumnType> = Extract<VunoTableColumn, {
|
|
148
|
+
type: TType;
|
|
149
|
+
}>;
|
|
150
|
+
/**
|
|
151
|
+
* Tipos que pinta una celda.
|
|
152
|
+
*
|
|
153
|
+
* `actions` y `custom` quedan fuera porque no los resuelve la celda: el menú de
|
|
154
|
+
* acciones y el slot propio los pinta la tabla, que es quien tiene los eventos.
|
|
155
|
+
*/
|
|
156
|
+
export type VunoTableCellType = Exclude<VunoTableColumnType, 'actions' | 'custom'>;
|
|
157
|
+
/** Props comunes a toda celda. El valor llega resuelto para no repetir `getPath`. */
|
|
158
|
+
export type VunoTableCellProps<TType extends VunoTableCellType = VunoTableCellType> = {
|
|
159
|
+
column: VunoTableColumnOf<TType>;
|
|
160
|
+
row: VunoTableRow;
|
|
161
|
+
value: unknown;
|
|
162
|
+
};
|
|
114
163
|
export type VunoTableDefinition<TRow extends VunoTableRow = VunoTableRow> = {
|
|
115
164
|
columns: VunoTableColumn<TRow>[];
|
|
116
165
|
/** Texto e icono cuando no hay ninguna fila. */
|
|
@@ -122,8 +171,26 @@ export type VunoTableDefinition<TRow extends VunoTableRow = VunoTableRow> = {
|
|
|
122
171
|
filters?: VunoField[];
|
|
123
172
|
/** Tamaños ofrecidos en el selector de filas por página. */
|
|
124
173
|
pageSizes?: number[];
|
|
174
|
+
/**
|
|
175
|
+
* Si el endpoint pagina. Por defecto `true`.
|
|
176
|
+
*
|
|
177
|
+
* En `false` desaparecen el paginador y el selector de tamaño, y el pie
|
|
178
|
+
* pasa a decir cuántos resultados hay y nada más.
|
|
179
|
+
*/
|
|
180
|
+
paginated?: boolean;
|
|
125
181
|
/** Campo que identifica la fila; se usa como clave de selección. */
|
|
126
182
|
rowKey?: string;
|
|
183
|
+
/**
|
|
184
|
+
* Si el endpoint busca. Por defecto `true`.
|
|
185
|
+
*
|
|
186
|
+
* En `false` no se pinta la barra de búsqueda. No es cosmética: una barra
|
|
187
|
+
* que no filtra es una promesa que la pantalla no puede cumplir, y como
|
|
188
|
+
* funciona visualmente nadie la reporta como defecto.
|
|
189
|
+
*
|
|
190
|
+
* Es del endpoint y no de cada columna: quien decide sobre qué campos se
|
|
191
|
+
* busca es el servidor, no la tabla.
|
|
192
|
+
*/
|
|
193
|
+
searchable?: boolean;
|
|
127
194
|
searchPlaceholder?: string;
|
|
128
195
|
selectable?: boolean;
|
|
129
196
|
};
|
|
@@ -14,6 +14,10 @@ export type VunoThemeConfig = {
|
|
|
14
14
|
export type VunoIcons = {
|
|
15
15
|
actions: string;
|
|
16
16
|
addItem: string;
|
|
17
|
+
/** Celda booleana sin etiquetas: el «no». */
|
|
18
|
+
booleanFalse: string;
|
|
19
|
+
/** Celda booleana sin etiquetas: el «sí». */
|
|
20
|
+
booleanTrue: string;
|
|
17
21
|
clearSearch: string;
|
|
18
22
|
confirm: string;
|
|
19
23
|
empty: string;
|
|
@@ -57,6 +61,8 @@ export type VunoMessages = {
|
|
|
57
61
|
filtersTitle: string;
|
|
58
62
|
of: string;
|
|
59
63
|
removeItem: string;
|
|
64
|
+
/** Pie de una tabla que no pagina: «30 resultados». */
|
|
65
|
+
results: string;
|
|
60
66
|
retry: string;
|
|
61
67
|
rowActions: string;
|
|
62
68
|
rowSelected: string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { VunoTableColumn, VunoTableRow } from '#vuno/types/table';
|
|
2
|
+
/**
|
|
3
|
+
* El valor que muestra una columna.
|
|
4
|
+
*
|
|
5
|
+
* Vive aquí y no dentro de la celda porque lo necesitan dos sitios —la celda y
|
|
6
|
+
* el slot `cell-<key>`—, y si cada uno lo resolviera por su cuenta, una columna
|
|
7
|
+
* con `value` enseñaría una cosa en la celda y le pasaría otra al slot.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveColumnValue<TRow extends VunoTableRow>(column: VunoTableColumn<TRow>, row: TRow): unknown;
|