@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.
Files changed (41) hide show
  1. package/README.md +174 -2
  2. package/dist/module.json +1 -1
  3. package/dist/runtime/app/components/Cell/CellBadge.d.vue.ts +5 -0
  4. package/dist/runtime/app/components/Cell/CellBadge.vue +24 -0
  5. package/dist/runtime/app/components/Cell/CellBadge.vue.d.ts +5 -0
  6. package/dist/runtime/app/components/Cell/CellBoolean.d.vue.ts +5 -0
  7. package/dist/runtime/app/components/Cell/CellBoolean.vue +21 -0
  8. package/dist/runtime/app/components/Cell/CellBoolean.vue.d.ts +5 -0
  9. package/dist/runtime/app/components/Cell/CellCurrency.d.vue.ts +5 -0
  10. package/dist/runtime/app/components/Cell/CellCurrency.vue +14 -0
  11. package/dist/runtime/app/components/Cell/CellCurrency.vue.d.ts +5 -0
  12. package/dist/runtime/app/components/Cell/CellDate.d.vue.ts +5 -0
  13. package/dist/runtime/app/components/Cell/CellDate.vue +19 -0
  14. package/dist/runtime/app/components/Cell/CellDate.vue.d.ts +5 -0
  15. package/dist/runtime/app/components/Cell/CellImage.d.vue.ts +5 -0
  16. package/dist/runtime/app/components/Cell/CellImage.vue +22 -0
  17. package/dist/runtime/app/components/Cell/CellImage.vue.d.ts +5 -0
  18. package/dist/runtime/app/components/Cell/CellNumber.d.vue.ts +5 -0
  19. package/dist/runtime/app/components/Cell/CellNumber.vue +15 -0
  20. package/dist/runtime/app/components/Cell/CellNumber.vue.d.ts +5 -0
  21. package/dist/runtime/app/components/Cell/CellText.d.vue.ts +5 -0
  22. package/dist/runtime/app/components/Cell/CellText.vue +13 -0
  23. package/dist/runtime/app/components/Cell/CellText.vue.d.ts +5 -0
  24. package/dist/runtime/app/components/Field/FieldSwitch.d.vue.ts +5 -0
  25. package/dist/runtime/app/components/Field/FieldSwitch.vue +27 -0
  26. package/dist/runtime/app/components/Field/FieldSwitch.vue.d.ts +5 -0
  27. package/dist/runtime/app/components/Form/FormField.vue +18 -16
  28. package/dist/runtime/app/components/Form/FormModal.d.vue.ts +6 -0
  29. package/dist/runtime/app/components/Form/FormModal.vue +47 -46
  30. package/dist/runtime/app/components/Form/FormModal.vue.d.ts +6 -0
  31. package/dist/runtime/app/components/Table/Table.vue +273 -264
  32. package/dist/runtime/app/components/Table/TableCell.vue +24 -73
  33. package/dist/runtime/app/composables/useVunoCrud.js +9 -1
  34. package/dist/runtime/app/theme/index.js +18 -1
  35. package/dist/runtime/app/types/crud.d.ts +17 -1
  36. package/dist/runtime/app/types/field.d.ts +8 -1
  37. package/dist/runtime/app/types/table.d.ts +80 -13
  38. package/dist/runtime/app/types/theme.d.ts +6 -0
  39. package/dist/runtime/app/utils/column.d.ts +9 -0
  40. package/dist/runtime/app/utils/column.js +5 -0
  41. package/package.json +1 -1
@@ -1,84 +1,35 @@
1
1
  <script setup>
2
2
  import { computed } from "vue";
3
- import { useLocale } from "@nuxt/ui/composables";
4
- import { formatCurrency, formatDate, formatNumber } from "#vuno/utils/format";
5
- import { getPath } from "#vuno/utils/path";
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 locale = useLocale();
11
- const value = computed(() => getPath(props.row, props.column.key));
12
- const text = computed(() => {
13
- const raw = value.value;
14
- if (raw === null || raw === void 0) return "";
15
- return String(raw);
16
- });
17
- const badge = computed(() => {
18
- if (props.column.type !== "badge") return null;
19
- const entry = props.column.map?.[text.value];
20
- return {
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
- <span
34
- v-if="props.column.type === 'number'"
35
- class="tabular-nums"
36
- >
37
- {{ formatNumber(value, locale.code.value, props.column.decimals) }}
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(() => isError.value ? { retry: () => void refetch() } : void 0);
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
- table: "min-w-full",
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
- /** Convierte una fila en el estado del formulario al editar. */
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
- export type VunoTableRow = Record<string, unknown>;
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
- /** Ruta del valor en la fila. Admite anidado: `'proveedor.nombre'`. */
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, 'badge'> | WithType<VunoTableColumnBoolean, 'boolean'> | WithType<VunoTableColumnCurrency, 'currency'> | WithType<VunoTableColumnCustom, 'custom'> | WithType<VunoTableColumnDate, 'date'> | WithType<VunoTableColumnImage, 'image'> | WithType<VunoTableColumnNumber, 'number'> | WithType<VunoTableColumnText, 'text'>;
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;
@@ -0,0 +1,5 @@
1
+ import { getPath } from "./path.js";
2
+ export function resolveColumnValue(column, row) {
3
+ if (column.value) return column.value(row);
4
+ return getPath(row, column.key);
5
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groupteknology/vuno",
3
- "version": "0.3.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {