@asteby/metacore-runtime-react 8.0.0 → 9.1.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/CHANGELOG.md +73 -0
  2. package/dist/column-visibility.d.ts +22 -0
  3. package/dist/column-visibility.d.ts.map +1 -0
  4. package/dist/column-visibility.js +40 -0
  5. package/dist/dynamic-columns.d.ts.map +1 -1
  6. package/dist/dynamic-columns.js +4 -1
  7. package/dist/dynamic-form-schema.d.ts +7 -0
  8. package/dist/dynamic-form-schema.d.ts.map +1 -0
  9. package/dist/dynamic-form-schema.js +68 -0
  10. package/dist/dynamic-form.d.ts +2 -0
  11. package/dist/dynamic-form.d.ts.map +1 -1
  12. package/dist/dynamic-form.js +28 -9
  13. package/dist/dynamic-relation-helpers.d.ts +77 -0
  14. package/dist/dynamic-relation-helpers.d.ts.map +1 -0
  15. package/dist/dynamic-relation-helpers.js +186 -0
  16. package/dist/dynamic-relation.d.ts +64 -0
  17. package/dist/dynamic-relation.d.ts.map +1 -0
  18. package/dist/dynamic-relation.js +226 -0
  19. package/dist/dynamic-table.d.ts.map +1 -1
  20. package/dist/dynamic-table.js +17 -3
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +2 -0
  24. package/dist/types.d.ts +33 -0
  25. package/dist/types.d.ts.map +1 -1
  26. package/docs/relations.md +290 -0
  27. package/package.json +9 -3
  28. package/src/__tests__/column-visibility.test.ts +116 -0
  29. package/src/__tests__/dynamic-form.test.ts +104 -0
  30. package/src/__tests__/dynamic-relation.test.ts +293 -0
  31. package/src/column-visibility.ts +43 -0
  32. package/src/dynamic-columns.tsx +4 -1
  33. package/src/dynamic-form-schema.ts +66 -0
  34. package/src/dynamic-form.tsx +34 -9
  35. package/src/dynamic-relation-helpers.ts +226 -0
  36. package/src/dynamic-relation.tsx +497 -0
  37. package/src/dynamic-table.tsx +20 -2
  38. package/src/index.ts +14 -0
  39. package/src/types.ts +49 -0
  40. package/tsconfig.json +2 -1
  41. package/vitest.config.ts +8 -0
@@ -0,0 +1,64 @@
1
+ export type { DynamicRelationKind } from './dynamic-relation-helpers';
2
+ export { buildCreatePayload, buildPivotAttachPayload, buildPivotRowIndex, buildRelationFilterParams, deriveRelationFormFields, diffSelection, extractSelectedTargetIds, pickOptionLabel, relationRowKey, } from './dynamic-relation-helpers';
3
+ export interface DynamicRelationStrings {
4
+ title: string;
5
+ emptyState: string;
6
+ addLabel: string;
7
+ editLabel: string;
8
+ removeLabel: string;
9
+ confirmRemoveTitle: string;
10
+ confirmRemoveDescription: string;
11
+ cancelLabel: string;
12
+ saveLabel: string;
13
+ selectPlaceholder: string;
14
+ selectSearchPlaceholder: string;
15
+ selectEmpty: string;
16
+ }
17
+ interface CommonProps {
18
+ /** id del registro padre. */
19
+ parentId: string | number;
20
+ /** Hidden columns; el FK siempre se oculta automáticamente. */
21
+ hiddenColumns?: string[];
22
+ /** Permisos visibles. Default true. */
23
+ canCreate?: boolean;
24
+ canDelete?: boolean;
25
+ canEdit?: boolean;
26
+ /** Strings traducibles. */
27
+ strings?: Partial<DynamicRelationStrings>;
28
+ /** Wrapper className. */
29
+ className?: string;
30
+ /** Callback opcional cuando la selección o la lista cambia. */
31
+ onChange?: () => void;
32
+ }
33
+ export interface DynamicRelationOneToManyProps extends CommonProps {
34
+ kind: 'one_to_many';
35
+ /** Modelo hijo (lado N) cuyas filas se listan filtradas por `foreignKey == parentId`. */
36
+ model: string;
37
+ /** Foreign key del lado N que apunta al padre. */
38
+ foreignKey: string;
39
+ /** Endpoint override; default `/data/${model}`. */
40
+ endpoint?: string;
41
+ }
42
+ export interface DynamicRelationManyToManyProps extends CommonProps {
43
+ kind: 'many_to_many';
44
+ /** Tabla pivote (`through`). FK al padre vive acá como `foreignKey`. */
45
+ through: string;
46
+ /** Tabla destino (`references`) sobre la que se hace multi-select. */
47
+ references: string;
48
+ /** FK del pivot al padre. */
49
+ foreignKey: string;
50
+ /** FK del pivot a la tabla destino (default `${references}_id`). */
51
+ referencesKey?: string;
52
+ /** Override del endpoint del pivot; default `/data/${through}`. */
53
+ pivotEndpoint?: string;
54
+ /** Override del endpoint del target; default `/data/${references}`. */
55
+ referencesEndpoint?: string;
56
+ /**
57
+ * Columna del target que se usa como label en el multi-select. Si no se
58
+ * pasa, se infiere de la metadata (primer columna no-id, no-hidden).
59
+ */
60
+ displayKey?: string;
61
+ }
62
+ export type DynamicRelationProps = DynamicRelationOneToManyProps | DynamicRelationManyToManyProps;
63
+ export declare function DynamicRelation(props: DynamicRelationProps): import("react/jsx-runtime").JSX.Element;
64
+ //# sourceMappingURL=dynamic-relation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-relation.d.ts","sourceRoot":"","sources":["../src/dynamic-relation.tsx"],"names":[],"mappings":"AAyCA,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EACH,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,aAAa,EACb,wBAAwB,EACxB,eAAe,EACf,cAAc,GACjB,MAAM,4BAA4B,CAAA;AAEnC,MAAM,WAAW,sBAAsB;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,wBAAwB,EAAE,MAAM,CAAA;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,WAAW,EAAE,MAAM,CAAA;CACtB;AAiBD,UAAU,WAAW;IACjB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,uCAAuC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;IACzC,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,6BAA8B,SAAQ,WAAW;IAC9D,IAAI,EAAE,aAAa,CAAA;IACnB,yFAAyF;IACzF,KAAK,EAAE,MAAM,CAAA;IACb,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAA;IAClB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,8BAA+B,SAAQ,WAAW;IAC/D,IAAI,EAAE,cAAc,CAAA;IACpB,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAA;IAClB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,MAAM,oBAAoB,GAC1B,6BAA6B,GAC7B,8BAA8B,CAAA;AAEpC,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,2CAK1D"}
@@ -0,0 +1,226 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // DynamicRelation — primitivo metadata-driven que renderiza el lado N de una
3
+ // relación 1:N o N:N entre modelos. Cubre dos kinds:
4
+ // - "one_to_many": lista inline editable que cuelga del registro padre.
5
+ // - "many_to_many": multi-select sobre la tabla destino con sync a la pivot.
6
+ // La RFC completa vive en `packages/runtime-react/docs/relations.md`.
7
+ import { useCallback, useEffect, useMemo, useState } from 'react';
8
+ import { Button, Skeleton, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Dialog, DialogContent, DialogHeader, DialogTitle, MultiSelect, } from '@asteby/metacore-ui/primitives';
9
+ import { Plus, Trash2, Pencil } from 'lucide-react';
10
+ import { useApi } from './api-context';
11
+ import { useMetadataCache } from './metadata-cache';
12
+ import { DynamicForm } from './dynamic-form';
13
+ import { buildCreatePayload, buildPivotAttachPayload, buildPivotRowIndex, buildRelationFilterParams, deriveRelationFormFields, diffSelection, extractSelectedTargetIds, pickOptionLabel, relationRowKey, } from './dynamic-relation-helpers';
14
+ export { buildCreatePayload, buildPivotAttachPayload, buildPivotRowIndex, buildRelationFilterParams, deriveRelationFormFields, diffSelection, extractSelectedTargetIds, pickOptionLabel, relationRowKey, } from './dynamic-relation-helpers';
15
+ const DEFAULT_STRINGS = {
16
+ title: '',
17
+ emptyState: 'No hay registros relacionados.',
18
+ addLabel: 'Agregar',
19
+ editLabel: 'Editar',
20
+ removeLabel: 'Quitar',
21
+ confirmRemoveTitle: '¿Quitar el registro?',
22
+ confirmRemoveDescription: 'Esta acción no se puede deshacer.',
23
+ cancelLabel: 'Cancelar',
24
+ saveLabel: 'Guardar',
25
+ selectPlaceholder: 'Seleccionar…',
26
+ selectSearchPlaceholder: 'Buscar…',
27
+ selectEmpty: 'Sin resultados.',
28
+ };
29
+ export function DynamicRelation(props) {
30
+ if (props.kind === 'many_to_many') {
31
+ return _jsx(ManyToManyRelation, { ...props });
32
+ }
33
+ return _jsx(OneToManyRelation, { ...props });
34
+ }
35
+ function OneToManyRelation({ kind, model, foreignKey, parentId, endpoint, hiddenColumns = [], canCreate = true, canDelete = true, canEdit = true, strings, className, onChange, }) {
36
+ const api = useApi();
37
+ const { getMetadata, setMetadata: cacheMetadata } = useMetadataCache();
38
+ const cachedMeta = getMetadata(model);
39
+ const labels = { ...DEFAULT_STRINGS, ...(strings || {}) };
40
+ const [metadata, setMetadata] = useState(cachedMeta || null);
41
+ const [rows, setRows] = useState([]);
42
+ const [loading, setLoading] = useState(true);
43
+ const [formOpen, setFormOpen] = useState(false);
44
+ const [editingRow, setEditingRow] = useState(null);
45
+ const [rowToDelete, setRowToDelete] = useState(null);
46
+ const [submitting, setSubmitting] = useState(false);
47
+ const dataEndpoint = endpoint || `/data/${model}`;
48
+ const fetchAll = useCallback(async () => {
49
+ setLoading(true);
50
+ try {
51
+ const params = buildRelationFilterParams(foreignKey, parentId);
52
+ const [metaRes, dataRes] = await Promise.all([
53
+ metadata ? Promise.resolve(null) : api.get(`/metadata/table/${model}`),
54
+ api.get(dataEndpoint, { params }),
55
+ ]);
56
+ if (metaRes && metaRes.data?.success) {
57
+ const fresh = metaRes.data.data;
58
+ setMetadata(fresh);
59
+ cacheMetadata(model, fresh);
60
+ }
61
+ const list = dataRes.data;
62
+ if (list.success)
63
+ setRows(list.data || []);
64
+ }
65
+ catch (err) {
66
+ console.error('DynamicRelation fetch error', err);
67
+ }
68
+ finally {
69
+ setLoading(false);
70
+ }
71
+ }, [api, dataEndpoint, foreignKey, parentId, metadata, model, cacheMetadata]);
72
+ useEffect(() => { fetchAll(); }, [fetchAll]);
73
+ const formFields = useMemo(() => deriveRelationFormFields(metadata, foreignKey), [metadata, foreignKey]);
74
+ const visibleColumns = useMemo(() => {
75
+ if (!metadata?.columns)
76
+ return [];
77
+ const hidden = new Set([foreignKey, ...hiddenColumns]);
78
+ return metadata.columns.filter(c => !hidden.has(c.key) && !c.hidden);
79
+ }, [metadata, foreignKey, hiddenColumns]);
80
+ const handleSubmit = useCallback(async (values) => {
81
+ setSubmitting(true);
82
+ try {
83
+ if (editingRow) {
84
+ const res = await api.put(`${dataEndpoint}/${editingRow.id}`, values);
85
+ if (!res.data?.success)
86
+ throw new Error('update failed');
87
+ }
88
+ else {
89
+ const payload = buildCreatePayload(foreignKey, parentId, values);
90
+ const res = await api.post(dataEndpoint, payload);
91
+ if (!res.data?.success)
92
+ throw new Error('create failed');
93
+ }
94
+ setFormOpen(false);
95
+ setEditingRow(null);
96
+ await fetchAll();
97
+ onChange?.();
98
+ }
99
+ catch (err) {
100
+ console.error('DynamicRelation submit error', err);
101
+ }
102
+ finally {
103
+ setSubmitting(false);
104
+ }
105
+ }, [api, dataEndpoint, editingRow, fetchAll, foreignKey, onChange, parentId]);
106
+ const handleDelete = useCallback(async () => {
107
+ if (!rowToDelete)
108
+ return;
109
+ setSubmitting(true);
110
+ try {
111
+ const res = await api.delete(`${dataEndpoint}/${rowToDelete.id}`);
112
+ if (!res.data?.success)
113
+ throw new Error('delete failed');
114
+ setRowToDelete(null);
115
+ await fetchAll();
116
+ onChange?.();
117
+ }
118
+ catch (err) {
119
+ console.error('DynamicRelation delete error', err);
120
+ }
121
+ finally {
122
+ setSubmitting(false);
123
+ }
124
+ }, [api, dataEndpoint, fetchAll, onChange, rowToDelete]);
125
+ return (_jsxs("div", { className: className, "data-relation-kind": kind, "data-relation-model": model, children: [(labels.title || canCreate) && (_jsxs("div", { className: "flex items-center justify-between pb-3", children: [labels.title ? _jsx("h3", { className: "text-sm font-medium", children: labels.title }) : _jsx("span", {}), canCreate && (_jsxs(Button, { size: "sm", variant: "outline", onClick: () => { setEditingRow(null); setFormOpen(true); }, children: [_jsx(Plus, { className: "h-4 w-4 mr-1" }), labels.addLabel] }))] })), loading ? (_jsx("div", { className: "space-y-2", children: Array.from({ length: 3 }).map((_, i) => (_jsx(Skeleton, { className: "h-10 w-full" }, `rel-skeleton-${i}`))) })) : rows.length === 0 ? (_jsx("div", { className: "text-center text-sm text-muted-foreground py-8 border rounded-md bg-muted/30", children: labels.emptyState })) : (_jsx("div", { className: "border rounded-md divide-y bg-card", children: rows.map((row, idx) => (_jsxs("div", { className: "flex items-center justify-between gap-3 px-3 py-2", children: [_jsx("div", { className: "flex-1 grid grid-cols-[repeat(auto-fit,minmax(0,1fr))] gap-2 text-sm", children: visibleColumns.map(col => (_jsx("span", { className: "truncate", title: String(row[col.key] ?? ''), children: formatCell(row[col.key]) }, col.key))) }), _jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [canEdit && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => { setEditingRow(row); setFormOpen(true); }, "aria-label": labels.editLabel, children: _jsx(Pencil, { className: "h-4 w-4" }) })), canDelete && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => setRowToDelete(row), "aria-label": labels.removeLabel, children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })] }, relationRowKey(row, idx, foreignKey)))) })), _jsx(Dialog, { open: formOpen, onOpenChange: (open) => { setFormOpen(open); if (!open)
126
+ setEditingRow(null); }, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: editingRow ? labels.editLabel : labels.addLabel }) }), _jsx(DynamicForm, { fields: formFields, initialValues: editingRow || undefined, onSubmit: handleSubmit, onCancel: () => { setFormOpen(false); setEditingRow(null); }, submitLabel: labels.saveLabel, cancelLabel: labels.cancelLabel, disabled: submitting })] }) }), _jsx(AlertDialog, { open: !!rowToDelete, onOpenChange: (open) => !open && setRowToDelete(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: labels.confirmRemoveTitle }), _jsx(AlertDialogDescription, { children: labels.confirmRemoveDescription })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: submitting, children: labels.cancelLabel }), _jsx(AlertDialogAction, { onClick: (e) => { e.preventDefault(); handleDelete(); }, className: "bg-red-600 hover:bg-red-700", disabled: submitting, children: labels.removeLabel })] })] }) })] }));
127
+ }
128
+ function formatCell(value) {
129
+ if (value === null || value === undefined)
130
+ return '—';
131
+ if (typeof value === 'boolean')
132
+ return value ? '✓' : '—';
133
+ if (typeof value === 'object')
134
+ return JSON.stringify(value);
135
+ return String(value);
136
+ }
137
+ function ManyToManyRelation({ kind, through, references, foreignKey, referencesKey, parentId, pivotEndpoint, referencesEndpoint, displayKey, canCreate = true, canDelete = true, strings, className, onChange, }) {
138
+ const api = useApi();
139
+ const { getMetadata, setMetadata: cacheMetadata } = useMetadataCache();
140
+ const labels = { ...DEFAULT_STRINGS, ...(strings || {}) };
141
+ const refKey = referencesKey || `${references}_id`;
142
+ const pivotPath = pivotEndpoint || `/data/${through}`;
143
+ const targetPath = referencesEndpoint || `/data/${references}`;
144
+ const cachedTargetMeta = getMetadata(references);
145
+ const [targetMeta, setTargetMeta] = useState(cachedTargetMeta || null);
146
+ const [targetRows, setTargetRows] = useState([]);
147
+ const [pivotRows, setPivotRows] = useState([]);
148
+ const [loading, setLoading] = useState(true);
149
+ const [syncing, setSyncing] = useState(false);
150
+ const fetchAll = useCallback(async () => {
151
+ setLoading(true);
152
+ try {
153
+ const params = buildRelationFilterParams(foreignKey, parentId);
154
+ const [metaRes, pivotRes, targetRes] = await Promise.all([
155
+ targetMeta ? Promise.resolve(null) : api.get(`/metadata/table/${references}`),
156
+ api.get(pivotPath, { params }),
157
+ api.get(targetPath),
158
+ ]);
159
+ if (metaRes && metaRes.data?.success) {
160
+ const fresh = metaRes.data.data;
161
+ setTargetMeta(fresh);
162
+ cacheMetadata(references, fresh);
163
+ }
164
+ const pivotList = pivotRes.data;
165
+ if (pivotList.success)
166
+ setPivotRows(pivotList.data || []);
167
+ const targetList = targetRes.data;
168
+ if (targetList.success)
169
+ setTargetRows(targetList.data || []);
170
+ }
171
+ catch (err) {
172
+ console.error('DynamicRelation m2m fetch error', err);
173
+ }
174
+ finally {
175
+ setLoading(false);
176
+ }
177
+ }, [api, pivotPath, targetPath, foreignKey, parentId, references, targetMeta, cacheMetadata]);
178
+ useEffect(() => { fetchAll(); }, [fetchAll]);
179
+ const options = useMemo(() => {
180
+ return targetRows
181
+ .filter(r => r && r.id !== undefined && r.id !== null && r.id !== '')
182
+ .map(r => ({
183
+ value: String(r.id),
184
+ label: pickOptionLabel(r, displayKey, targetMeta?.columns),
185
+ }));
186
+ }, [targetRows, displayKey, targetMeta]);
187
+ const selectedIds = useMemo(() => extractSelectedTargetIds(pivotRows, refKey), [pivotRows, refKey]);
188
+ const pivotIndex = useMemo(() => buildPivotRowIndex(pivotRows, refKey), [pivotRows, refKey]);
189
+ const handleChange = useCallback(async (next) => {
190
+ if (syncing)
191
+ return;
192
+ const { toAdd, toRemove } = diffSelection(selectedIds, next);
193
+ if (toAdd.length === 0 && toRemove.length === 0)
194
+ return;
195
+ if (toAdd.length > 0 && !canCreate)
196
+ return;
197
+ if (toRemove.length > 0 && !canDelete)
198
+ return;
199
+ setSyncing(true);
200
+ try {
201
+ for (const targetId of toAdd) {
202
+ const payload = buildPivotAttachPayload(foreignKey, parentId, refKey, targetId);
203
+ const res = await api.post(pivotPath, payload);
204
+ if (!res.data?.success)
205
+ throw new Error('attach failed');
206
+ }
207
+ for (const targetId of toRemove) {
208
+ const pivotId = pivotIndex.get(targetId);
209
+ if (pivotId === undefined)
210
+ continue;
211
+ const res = await api.delete(`${pivotPath}/${pivotId}`);
212
+ if (!res.data?.success)
213
+ throw new Error('detach failed');
214
+ }
215
+ await fetchAll();
216
+ onChange?.();
217
+ }
218
+ catch (err) {
219
+ console.error('DynamicRelation m2m sync error', err);
220
+ }
221
+ finally {
222
+ setSyncing(false);
223
+ }
224
+ }, [api, canCreate, canDelete, fetchAll, foreignKey, onChange, parentId, pivotIndex, pivotPath, refKey, selectedIds, syncing]);
225
+ return (_jsxs("div", { className: className, "data-relation-kind": kind, "data-relation-through": through, "data-relation-references": references, children: [labels.title && (_jsx("div", { className: "pb-3", children: _jsx("h3", { className: "text-sm font-medium", children: labels.title }) })), loading ? (_jsx(Skeleton, { className: "h-10 w-full" })) : options.length === 0 ? (_jsx("div", { className: "text-center text-sm text-muted-foreground py-8 border rounded-md bg-muted/30", children: labels.emptyState })) : (_jsx(MultiSelect, { options: options, selected: selectedIds, onChange: handleChange, placeholder: labels.selectPlaceholder, searchPlaceholder: labels.selectSearchPlaceholder, emptyMessage: labels.selectEmpty }))] }));
226
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AA+B9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AASnF,UAAU,iBAAiB;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACxC;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,GAC/C,EAAE,iBAAiB,2CA8rBnB"}
1
+ {"version":3,"file":"dynamic-table.d.ts","sourceRoot":"","sources":["../src/dynamic-table.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAKH,KAAK,SAAS,EAajB,MAAM,uBAAuB,CAAA;AA+B9B,OAAO,KAAK,EAAsB,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAUnF,UAAU,iBAAiB;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;IAC/B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACxC;AAED,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAoB,EACpB,aAAkB,EAClB,QAAQ,EACR,cAAc,EACd,cAAc,EACd,YAAiB,EACjB,iBAA4C,GAC/C,EAAE,iBAAiB,2CA+sBnB"}
@@ -27,6 +27,7 @@ import { useApi, useCurrentBranch } from './api-context';
27
27
  import { defaultGetDynamicColumns } from './dynamic-columns';
28
28
  import { OptionsContext } from './options-context';
29
29
  import { ActionModalDispatcher } from './action-modal-dispatcher';
30
+ import { getSearchableColumnKeys } from './column-visibility';
30
31
  import { DynamicRecordDialog } from './dialogs/dynamic-record';
31
32
  import { ExportDialog } from './dialogs/export';
32
33
  import { ImportDialog } from './dialogs/import';
@@ -253,14 +254,27 @@ export function DynamicTable({ model, endpoint, enableUrlSync = true, hiddenColu
253
254
  };
254
255
  initMetadataAndOptions();
255
256
  }, [model]); // eslint-disable-line react-hooks/exhaustive-deps
257
+ // Derived from `metadata.columns[].searchable`. `null` means the kernel
258
+ // didn't emit the flag for any column → preserve legacy "search every
259
+ // column" behaviour by not narrowing the request. An empty array means
260
+ // every column was explicitly opted out → skip sending `search` at all.
261
+ const searchableKeys = useMemo(() => (metadata ? getSearchableColumnKeys(metadata) : null), [metadata]);
256
262
  const buildFilterParams = useCallback(() => {
257
263
  const params = {};
258
264
  if (sorting.length > 0) {
259
265
  params.sortBy = sorting[0].id;
260
266
  params.order = sorting[0].desc ? 'desc' : 'asc';
261
267
  }
262
- if (globalFilter)
263
- params.search = globalFilter;
268
+ if (globalFilter) {
269
+ if (searchableKeys === null) {
270
+ params.search = globalFilter;
271
+ }
272
+ else if (searchableKeys.length > 0) {
273
+ params.search = globalFilter;
274
+ params.search_columns = searchableKeys.join(',');
275
+ }
276
+ // searchableKeys === [] → drop the search request entirely
277
+ }
264
278
  columnFilters.forEach((filter) => { params[`f_${filter.id}`] = filter.value; });
265
279
  if (defaultFilters)
266
280
  Object.entries(defaultFilters).forEach(([key, value]) => { params[`f_${key}`] = value; });
@@ -286,7 +300,7 @@ export function DynamicTable({ model, endpoint, enableUrlSync = true, hiddenColu
286
300
  params['f_created_at'] = `${startDate}_${endDate}`;
287
301
  }
288
302
  return params;
289
- }, [sorting, globalFilter, columnFilters, defaultFilters, dynamicFilters, dateRange]);
303
+ }, [sorting, globalFilter, columnFilters, defaultFilters, dynamicFilters, dateRange, searchableKeys]);
290
304
  const hasActiveFilters = useMemo(() => {
291
305
  if (globalFilter)
292
306
  return true;
package/dist/index.d.ts CHANGED
@@ -17,5 +17,7 @@ export { DynamicRecordDialog } from './dialogs/dynamic-record';
17
17
  export { ExportDialog } from './dialogs/export';
18
18
  export { ImportDialog } from './dialogs/import';
19
19
  export { DynamicCRUDPage, type DynamicCRUDPageProps, type DynamicCRUDPageStrings, type DynamicCRUDPageClasses, } from './dynamic-crud-page';
20
+ export { DynamicRelation, type DynamicRelationProps, type DynamicRelationStrings, type DynamicRelationKind, buildRelationFilterParams, buildCreatePayload, deriveRelationFormFields, relationRowKey, } from './dynamic-relation';
20
21
  export { registerModelExtension, getModelExtension, clearModelExtensions, type ModelExtension, type ModelExtensionProps, } from './model-extension-registry';
22
+ export { isColumnVisibleInTable, getSearchableColumnKeys, } from './column-visibility';
21
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,SAAS,CAAA;AACvB,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EACH,qBAAqB,EACrB,KAAK,gBAAgB,GACxB,MAAM,2BAA2B,CAAA;AAClC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,QAAQ,CAAA;AACtB,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,YAAY,EACR,kBAAkB,EAClB,YAAY,IAAI,yBAAyB,EACzC,iBAAiB,EACjB,oBAAoB,GACvB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACH,wBAAwB,EACxB,4BAA4B,EAC5B,KAAK,qBAAqB,GAC7B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EACH,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,GAC9B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GAC3B,MAAM,4BAA4B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,SAAS,CAAA;AACvB,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EACH,qBAAqB,EACrB,KAAK,gBAAgB,GACxB,MAAM,2BAA2B,CAAA;AAClC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,QAAQ,CAAA;AACtB,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,YAAY,EACR,kBAAkB,EAClB,YAAY,IAAI,yBAAyB,EACzC,iBAAiB,EACjB,oBAAoB,GACvB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACH,wBAAwB,EACxB,4BAA4B,EAC5B,KAAK,qBAAqB,GAC7B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EACH,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,GAC9B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACH,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,kBAAkB,EAClB,wBAAwB,EACxB,cAAc,GACjB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACH,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,mBAAmB,GAC3B,MAAM,4BAA4B,CAAA;AACnC,OAAO,EACH,sBAAsB,EACtB,uBAAuB,GAC1B,MAAM,qBAAqB,CAAA"}
package/dist/index.js CHANGED
@@ -21,4 +21,6 @@ export { DynamicRecordDialog } from './dialogs/dynamic-record';
21
21
  export { ExportDialog } from './dialogs/export';
22
22
  export { ImportDialog } from './dialogs/import';
23
23
  export { DynamicCRUDPage, } from './dynamic-crud-page';
24
+ export { DynamicRelation, buildRelationFilterParams, buildCreatePayload, deriveRelationFormFields, relationRowKey, } from './dynamic-relation';
24
25
  export { registerModelExtension, getModelExtension, clearModelExtensions, } from './model-extension-registry';
26
+ export { isColumnVisibleInTable, getSearchableColumnKeys, } from './column-visibility';
package/dist/types.d.ts CHANGED
@@ -26,6 +26,17 @@ export interface FilterDefinition {
26
26
  }[];
27
27
  searchEndpoint?: string;
28
28
  }
29
+ /**
30
+ * Where a column is rendered. Mirrors `manifest.ColumnDef.Visibility` in the
31
+ * kernel:
32
+ * - `''` / `'all'` — visible everywhere (default).
33
+ * - `'table'` — only the list/index page.
34
+ * - `'modal'` — only the create/edit modal.
35
+ * - `'list'` — only API list payloads (omitted from UI).
36
+ * Hosts may extend the union with their own scopes; the SDK only acts on the
37
+ * canonical values above.
38
+ */
39
+ export type ColumnVisibility = 'all' | 'table' | 'modal' | 'list' | (string & {});
29
40
  export interface ColumnDefinition {
30
41
  key: string;
31
42
  label: string;
@@ -33,6 +44,19 @@ export interface ColumnDefinition {
33
44
  sortable: boolean;
34
45
  filterable: boolean;
35
46
  hidden?: boolean;
47
+ /**
48
+ * Scopes where this column is rendered. When `'modal'` (or `'list'`) the
49
+ * column is hidden from the table even if `hidden` is unset. Empty/`'all'`/
50
+ * `'table'` keep the column visible. See `column-visibility.ts`.
51
+ */
52
+ visibility?: ColumnVisibility;
53
+ /**
54
+ * Opts the column into the model's full-text/contains search. Independent
55
+ * of `filterable` (which drives column-level filter chips). When at least
56
+ * one column declares `searchable`, the SDK narrows the global search to
57
+ * those columns; otherwise legacy "search every column" behaviour applies.
58
+ */
59
+ searchable?: boolean;
36
60
  styleConfig?: Record<string, any>;
37
61
  tooltip?: string;
38
62
  description?: string;
@@ -56,6 +80,13 @@ export interface ActionCondition {
56
80
  operator: 'eq' | 'neq' | 'in' | 'not_in';
57
81
  value: string | string[];
58
82
  }
83
+ export interface FieldValidation {
84
+ regex?: string;
85
+ min?: number;
86
+ max?: number;
87
+ custom?: string;
88
+ }
89
+ export type FieldWidget = 'text' | 'textarea' | 'richtext' | 'color' | 'number' | 'date' | 'select' | 'switch';
59
90
  export interface ActionFieldDef {
60
91
  key: string;
61
92
  label: string;
@@ -68,6 +99,8 @@ export interface ActionFieldDef {
68
99
  defaultValue?: any;
69
100
  placeholder?: string;
70
101
  searchEndpoint?: string;
102
+ validation?: FieldValidation;
103
+ widget?: FieldWidget | string;
71
104
  }
72
105
  export interface ActionDefinition {
73
106
  key: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}