zod-collection-ui 0.0.1
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/LICENSE +21 -0
- package/README.md +383 -0
- package/dist/collection.d.ts +77 -0
- package/dist/collection.js +273 -0
- package/dist/collection.js.map +1 -0
- package/dist/data-provider.d.ts +65 -0
- package/dist/data-provider.js +185 -0
- package/dist/data-provider.js.map +1 -0
- package/dist/generators.d.ts +118 -0
- package/dist/generators.js +239 -0
- package/dist/generators.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/inference.d.ts +39 -0
- package/dist/inference.js +395 -0
- package/dist/inference.js.map +1 -0
- package/dist/prompt.d.ts +29 -0
- package/dist/prompt.js +247 -0
- package/dist/prompt.js.map +1 -0
- package/dist/store.d.ts +105 -0
- package/dist/store.js +171 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +205 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection Definition: The main entry point.
|
|
3
|
+
*
|
|
4
|
+
* `defineCollection` takes a Zod object schema + optional configuration and produces
|
|
5
|
+
* a CollectionDefinition with resolved affordances, generators, and utilities.
|
|
6
|
+
*/
|
|
7
|
+
import { inferFieldAffordances, getZodBaseType, humanizeFieldName, } from './inference.js';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Default Collection Affordances
|
|
10
|
+
// ============================================================================
|
|
11
|
+
const DEFAULT_COLLECTION_AFFORDANCES = {
|
|
12
|
+
create: true,
|
|
13
|
+
read: true,
|
|
14
|
+
update: true,
|
|
15
|
+
delete: true,
|
|
16
|
+
bulkDelete: false,
|
|
17
|
+
bulkEdit: false,
|
|
18
|
+
search: true,
|
|
19
|
+
pagination: {
|
|
20
|
+
defaultPageSize: 25,
|
|
21
|
+
pageSizeOptions: [10, 25, 50, 100],
|
|
22
|
+
style: 'pages',
|
|
23
|
+
serverSide: false,
|
|
24
|
+
},
|
|
25
|
+
multiSort: true,
|
|
26
|
+
filterPanel: true,
|
|
27
|
+
selectable: 'multi',
|
|
28
|
+
columnVisibility: true,
|
|
29
|
+
columnOrder: true,
|
|
30
|
+
columnResize: true,
|
|
31
|
+
refresh: true,
|
|
32
|
+
defaultView: 'table',
|
|
33
|
+
views: ['table'],
|
|
34
|
+
};
|
|
35
|
+
const DEFAULT_PAGINATION = {
|
|
36
|
+
defaultPageSize: 25,
|
|
37
|
+
pageSizeOptions: [10, 25, 50, 100],
|
|
38
|
+
style: 'pages',
|
|
39
|
+
serverSide: false,
|
|
40
|
+
};
|
|
41
|
+
const DEFAULT_SEARCH = {
|
|
42
|
+
debounce: 300,
|
|
43
|
+
minChars: 1,
|
|
44
|
+
highlight: false,
|
|
45
|
+
placeholder: 'Search...',
|
|
46
|
+
};
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// defineCollection
|
|
49
|
+
// ============================================================================
|
|
50
|
+
/**
|
|
51
|
+
* Define a collection from a Zod object schema and optional configuration.
|
|
52
|
+
*
|
|
53
|
+
* This is the main entry point for the collection affordance library.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const projectCollection = defineCollection(
|
|
58
|
+
* z.object({
|
|
59
|
+
* id: z.string().uuid(),
|
|
60
|
+
* name: z.string().min(1),
|
|
61
|
+
* status: z.enum(['draft', 'active', 'archived']),
|
|
62
|
+
* priority: z.number().int().min(1).max(5),
|
|
63
|
+
* }),
|
|
64
|
+
* {
|
|
65
|
+
* affordances: { bulkDelete: true, export: ['csv', 'json'] },
|
|
66
|
+
* fields: { name: { inlineEditable: true } },
|
|
67
|
+
* operations: [{ name: 'archive', label: 'Archive', scope: 'item' }],
|
|
68
|
+
* }
|
|
69
|
+
* );
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export function defineCollection(schema, config) {
|
|
73
|
+
// 1. Resolve collection-level affordances
|
|
74
|
+
const affordances = resolveCollectionAffordances(config?.affordances);
|
|
75
|
+
// 2. Resolve per-field affordances
|
|
76
|
+
const fieldAffordances = resolveAllFieldAffordances(schema, config?.fields);
|
|
77
|
+
// 3. Resolve identity fields
|
|
78
|
+
const idField = config?.idField ?? detectIdField(schema);
|
|
79
|
+
const labelField = config?.labelField ?? detectLabelField(schema, fieldAffordances);
|
|
80
|
+
// 4. Operations
|
|
81
|
+
const operations = config?.operations ?? [];
|
|
82
|
+
// 5. Build the collection definition
|
|
83
|
+
const definition = {
|
|
84
|
+
schema,
|
|
85
|
+
affordances,
|
|
86
|
+
fieldAffordances,
|
|
87
|
+
operations,
|
|
88
|
+
idField,
|
|
89
|
+
labelField,
|
|
90
|
+
getVisibleFields() {
|
|
91
|
+
return Object.entries(fieldAffordances)
|
|
92
|
+
.filter(([_, fa]) => fa.visible !== false && !fa.hidden && !fa.detailOnly)
|
|
93
|
+
.sort(([, a], [, b]) => (a.order ?? 999) - (b.order ?? 999))
|
|
94
|
+
.map(([key]) => key);
|
|
95
|
+
},
|
|
96
|
+
getSearchableFields() {
|
|
97
|
+
return Object.entries(fieldAffordances)
|
|
98
|
+
.filter(([_, fa]) => fa.searchable === true)
|
|
99
|
+
.map(([key]) => key);
|
|
100
|
+
},
|
|
101
|
+
getFilterableFields() {
|
|
102
|
+
return Object.entries(fieldAffordances)
|
|
103
|
+
.filter(([_, fa]) => fa.filterable !== false)
|
|
104
|
+
.map(([key, fa]) => ({ key, affordance: fa }));
|
|
105
|
+
},
|
|
106
|
+
getSortableFields() {
|
|
107
|
+
return Object.entries(fieldAffordances)
|
|
108
|
+
.filter(([_, fa]) => fa.sortable !== false && fa.sortable !== 'none')
|
|
109
|
+
.map(([key, fa]) => ({ key, affordance: fa }));
|
|
110
|
+
},
|
|
111
|
+
getGroupableFields() {
|
|
112
|
+
return Object.entries(fieldAffordances)
|
|
113
|
+
.filter(([_, fa]) => fa.groupable === true)
|
|
114
|
+
.map(([key, fa]) => ({ key, affordance: fa }));
|
|
115
|
+
},
|
|
116
|
+
getOperations(scope) {
|
|
117
|
+
return operations.filter((op) => op.scope === scope);
|
|
118
|
+
},
|
|
119
|
+
describe() {
|
|
120
|
+
return generateDescription(definition);
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
return definition;
|
|
124
|
+
}
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// Internals
|
|
127
|
+
// ============================================================================
|
|
128
|
+
function resolveCollectionAffordances(explicit) {
|
|
129
|
+
const merged = { ...DEFAULT_COLLECTION_AFFORDANCES, ...explicit };
|
|
130
|
+
// Normalize pagination
|
|
131
|
+
if (merged.pagination === true) {
|
|
132
|
+
merged.pagination = { ...DEFAULT_PAGINATION };
|
|
133
|
+
}
|
|
134
|
+
else if (merged.pagination && typeof merged.pagination === 'object') {
|
|
135
|
+
merged.pagination = { ...DEFAULT_PAGINATION, ...merged.pagination };
|
|
136
|
+
}
|
|
137
|
+
// Normalize search
|
|
138
|
+
if (merged.search === true) {
|
|
139
|
+
merged.search = { ...DEFAULT_SEARCH };
|
|
140
|
+
}
|
|
141
|
+
else if (merged.search && typeof merged.search === 'object') {
|
|
142
|
+
merged.search = { ...DEFAULT_SEARCH, ...merged.search };
|
|
143
|
+
}
|
|
144
|
+
return merged;
|
|
145
|
+
}
|
|
146
|
+
function resolveAllFieldAffordances(schema, explicit) {
|
|
147
|
+
const shape = schema.shape;
|
|
148
|
+
const result = {};
|
|
149
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
150
|
+
// Infer defaults from type + name + meta
|
|
151
|
+
const inferred = inferFieldAffordances(key, fieldSchema);
|
|
152
|
+
// Merge with explicit overrides
|
|
153
|
+
const explicitOverrides = explicit?.[key] ?? {};
|
|
154
|
+
const merged = { ...inferred, ...explicitOverrides };
|
|
155
|
+
// Ensure title and zodType are set
|
|
156
|
+
result[key] = {
|
|
157
|
+
...merged,
|
|
158
|
+
title: merged.title ?? humanizeFieldName(key),
|
|
159
|
+
zodType: getZodBaseType(fieldSchema),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
/** Detect the ID field from schema. */
|
|
165
|
+
function detectIdField(schema) {
|
|
166
|
+
const shape = schema.shape;
|
|
167
|
+
// Direct 'id' field
|
|
168
|
+
if ('id' in shape)
|
|
169
|
+
return 'id';
|
|
170
|
+
if ('_id' in shape)
|
|
171
|
+
return '_id';
|
|
172
|
+
if ('uuid' in shape)
|
|
173
|
+
return 'uuid';
|
|
174
|
+
if ('key' in shape)
|
|
175
|
+
return 'key';
|
|
176
|
+
// First field ending with Id/id/_id
|
|
177
|
+
for (const key of Object.keys(shape)) {
|
|
178
|
+
if (/[iI]d$/.test(key) || key.endsWith('_id'))
|
|
179
|
+
return key;
|
|
180
|
+
}
|
|
181
|
+
// Fallback to first field
|
|
182
|
+
const keys = Object.keys(shape);
|
|
183
|
+
return keys[0] ?? 'id';
|
|
184
|
+
}
|
|
185
|
+
/** Detect the label field from schema + affordances. */
|
|
186
|
+
function detectLabelField(schema, fieldAffordances) {
|
|
187
|
+
// Check for summaryField flag
|
|
188
|
+
for (const [key, fa] of Object.entries(fieldAffordances)) {
|
|
189
|
+
if (fa.summaryField)
|
|
190
|
+
return key;
|
|
191
|
+
}
|
|
192
|
+
// Common label field names
|
|
193
|
+
const labelNames = ['name', 'title', 'label', 'displayName', 'display_name', 'username'];
|
|
194
|
+
for (const name of labelNames) {
|
|
195
|
+
if (name in fieldAffordances)
|
|
196
|
+
return name;
|
|
197
|
+
}
|
|
198
|
+
// First string field that's not an ID
|
|
199
|
+
for (const [key, fa] of Object.entries(fieldAffordances)) {
|
|
200
|
+
if (fa.zodType === 'string' && fa.editable !== false)
|
|
201
|
+
return key;
|
|
202
|
+
}
|
|
203
|
+
return Object.keys(fieldAffordances)[0] ?? '';
|
|
204
|
+
}
|
|
205
|
+
/** Generate a human-readable description of the collection's affordances. */
|
|
206
|
+
function generateDescription(def) {
|
|
207
|
+
const lines = [];
|
|
208
|
+
// Collection info
|
|
209
|
+
const fieldCount = Object.keys(def.fieldAffordances).length;
|
|
210
|
+
lines.push(`Collection with ${fieldCount} fields (ID: ${def.idField}, Label: ${def.labelField})`);
|
|
211
|
+
lines.push('');
|
|
212
|
+
// CRUD
|
|
213
|
+
const crud = [];
|
|
214
|
+
if (def.affordances.create)
|
|
215
|
+
crud.push('Create');
|
|
216
|
+
if (def.affordances.read)
|
|
217
|
+
crud.push('Read');
|
|
218
|
+
if (def.affordances.update)
|
|
219
|
+
crud.push('Update');
|
|
220
|
+
if (def.affordances.delete)
|
|
221
|
+
crud.push('Delete');
|
|
222
|
+
lines.push(`CRUD: ${crud.join(', ')}`);
|
|
223
|
+
// Bulk operations
|
|
224
|
+
const bulk = [];
|
|
225
|
+
if (def.affordances.bulkDelete)
|
|
226
|
+
bulk.push('Bulk Delete');
|
|
227
|
+
if (def.affordances.bulkEdit)
|
|
228
|
+
bulk.push('Bulk Edit');
|
|
229
|
+
if (bulk.length)
|
|
230
|
+
lines.push(`Bulk: ${bulk.join(', ')}`);
|
|
231
|
+
// Search
|
|
232
|
+
if (def.affordances.search) {
|
|
233
|
+
const searchFields = def.getSearchableFields();
|
|
234
|
+
lines.push(`Search: Yes (fields: ${searchFields.join(', ')})`);
|
|
235
|
+
}
|
|
236
|
+
// Pagination
|
|
237
|
+
if (def.affordances.pagination && typeof def.affordances.pagination === 'object') {
|
|
238
|
+
lines.push(`Pagination: ${def.affordances.pagination.style} (default: ${def.affordances.pagination.defaultPageSize})`);
|
|
239
|
+
}
|
|
240
|
+
lines.push('');
|
|
241
|
+
lines.push('Fields:');
|
|
242
|
+
// Per-field summary
|
|
243
|
+
for (const [key, fa] of Object.entries(def.fieldAffordances)) {
|
|
244
|
+
const caps = [];
|
|
245
|
+
if (fa.sortable && fa.sortable !== 'none')
|
|
246
|
+
caps.push(`sort:${fa.sortable}`);
|
|
247
|
+
if (fa.filterable)
|
|
248
|
+
caps.push(`filter:${fa.filterable}`);
|
|
249
|
+
if (fa.searchable)
|
|
250
|
+
caps.push('search');
|
|
251
|
+
if (fa.groupable)
|
|
252
|
+
caps.push('group');
|
|
253
|
+
if (fa.editable)
|
|
254
|
+
caps.push('edit');
|
|
255
|
+
if (fa.inlineEditable)
|
|
256
|
+
caps.push('inline-edit');
|
|
257
|
+
if (!fa.visible || fa.hidden)
|
|
258
|
+
caps.push('HIDDEN');
|
|
259
|
+
if (fa.detailOnly)
|
|
260
|
+
caps.push('detail-only');
|
|
261
|
+
lines.push(` ${key} (${fa.zodType}): ${caps.join(', ') || 'display-only'}`);
|
|
262
|
+
}
|
|
263
|
+
// Operations
|
|
264
|
+
if (def.operations.length > 0) {
|
|
265
|
+
lines.push('');
|
|
266
|
+
lines.push('Custom Operations:');
|
|
267
|
+
for (const op of def.operations) {
|
|
268
|
+
lines.push(` ${op.name} [${op.scope}]: ${op.label}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return lines.join('\n');
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=collection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Provider: A normalized interface for CRUD operations on collections.
|
|
3
|
+
*
|
|
4
|
+
* Inspired by React Admin's data provider (9 methods) but simplified.
|
|
5
|
+
* The interface is framework-agnostic — implementations can wrap REST, GraphQL,
|
|
6
|
+
* Supabase, Firebase, in-memory arrays, or any data source.
|
|
7
|
+
*
|
|
8
|
+
* Also includes an in-memory adapter for prototyping and testing.
|
|
9
|
+
*/
|
|
10
|
+
import type { SortingState, ColumnFilter } from './store.js';
|
|
11
|
+
export interface GetListParams {
|
|
12
|
+
sort?: SortingState[];
|
|
13
|
+
filter?: ColumnFilter[];
|
|
14
|
+
search?: string;
|
|
15
|
+
pagination?: {
|
|
16
|
+
page: number;
|
|
17
|
+
pageSize: number;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export interface GetListResult<T> {
|
|
21
|
+
data: T[];
|
|
22
|
+
total: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Normalized data access interface for collection CRUD operations.
|
|
26
|
+
*
|
|
27
|
+
* All methods return Promises to support both sync and async data sources.
|
|
28
|
+
*/
|
|
29
|
+
export interface DataProvider<T> {
|
|
30
|
+
getList(params: GetListParams): Promise<GetListResult<T>>;
|
|
31
|
+
getOne(id: string): Promise<T>;
|
|
32
|
+
create(data: Partial<T>): Promise<T>;
|
|
33
|
+
update(id: string, data: Partial<T>): Promise<T>;
|
|
34
|
+
updateMany(ids: string[], data: Partial<T>): Promise<T[]>;
|
|
35
|
+
delete(id: string): Promise<void>;
|
|
36
|
+
deleteMany(ids: string[]): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export interface InMemoryProviderOptions {
|
|
39
|
+
/** Field name used as the unique identifier. Default: 'id'. */
|
|
40
|
+
idField?: string;
|
|
41
|
+
/** Delay in ms to simulate network latency. Default: 0. */
|
|
42
|
+
simulateDelay?: number;
|
|
43
|
+
/** Searchable fields for the `search` parameter. Default: all string fields. */
|
|
44
|
+
searchFields?: string[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create an in-memory DataProvider from an array of items.
|
|
48
|
+
*
|
|
49
|
+
* Useful for prototyping, testing, and small datasets.
|
|
50
|
+
* Supports sorting, filtering, search, and pagination.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const provider = createInMemoryProvider([
|
|
55
|
+
* { id: '1', name: 'Alpha', priority: 3 },
|
|
56
|
+
* { id: '2', name: 'Beta', priority: 1 },
|
|
57
|
+
* ], { idField: 'id' });
|
|
58
|
+
*
|
|
59
|
+
* const { data, total } = await provider.getList({
|
|
60
|
+
* sort: [{ id: 'priority', desc: false }],
|
|
61
|
+
* pagination: { page: 1, pageSize: 10 },
|
|
62
|
+
* });
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function createInMemoryProvider<T extends Record<string, any>>(initialData: T[], options?: InMemoryProviderOptions): DataProvider<T>;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Provider: A normalized interface for CRUD operations on collections.
|
|
3
|
+
*
|
|
4
|
+
* Inspired by React Admin's data provider (9 methods) but simplified.
|
|
5
|
+
* The interface is framework-agnostic — implementations can wrap REST, GraphQL,
|
|
6
|
+
* Supabase, Firebase, in-memory arrays, or any data source.
|
|
7
|
+
*
|
|
8
|
+
* Also includes an in-memory adapter for prototyping and testing.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Create an in-memory DataProvider from an array of items.
|
|
12
|
+
*
|
|
13
|
+
* Useful for prototyping, testing, and small datasets.
|
|
14
|
+
* Supports sorting, filtering, search, and pagination.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const provider = createInMemoryProvider([
|
|
19
|
+
* { id: '1', name: 'Alpha', priority: 3 },
|
|
20
|
+
* { id: '2', name: 'Beta', priority: 1 },
|
|
21
|
+
* ], { idField: 'id' });
|
|
22
|
+
*
|
|
23
|
+
* const { data, total } = await provider.getList({
|
|
24
|
+
* sort: [{ id: 'priority', desc: false }],
|
|
25
|
+
* pagination: { page: 1, pageSize: 10 },
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export function createInMemoryProvider(initialData, options = {}) {
|
|
30
|
+
const idField = options.idField ?? 'id';
|
|
31
|
+
const delay = options.simulateDelay ?? 0;
|
|
32
|
+
const searchFields = options.searchFields;
|
|
33
|
+
// Internal mutable store
|
|
34
|
+
let items = [...initialData];
|
|
35
|
+
let nextId = items.length + 1;
|
|
36
|
+
const maybeDelay = () => delay > 0 ? new Promise(r => setTimeout(r, delay)) : Promise.resolve();
|
|
37
|
+
function getItemId(item) {
|
|
38
|
+
return String(item[idField]);
|
|
39
|
+
}
|
|
40
|
+
function matchesFilter(item, filter) {
|
|
41
|
+
const value = item[filter.id];
|
|
42
|
+
if (filter.value === undefined || filter.value === null || filter.value === '') {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
// Array filter value → check if item value is in the array
|
|
46
|
+
if (Array.isArray(filter.value)) {
|
|
47
|
+
if (filter.value.length === 0)
|
|
48
|
+
return true;
|
|
49
|
+
if (Array.isArray(value)) {
|
|
50
|
+
// Array contains any of the filter values
|
|
51
|
+
return filter.value.some(fv => value.includes(fv));
|
|
52
|
+
}
|
|
53
|
+
return filter.value.includes(value);
|
|
54
|
+
}
|
|
55
|
+
// Range filter: { min, max }
|
|
56
|
+
if (typeof filter.value === 'object' && ('min' in filter.value || 'max' in filter.value)) {
|
|
57
|
+
const range = filter.value;
|
|
58
|
+
const num = Number(value);
|
|
59
|
+
if (range.min !== undefined && num < range.min)
|
|
60
|
+
return false;
|
|
61
|
+
if (range.max !== undefined && num > range.max)
|
|
62
|
+
return false;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
// Boolean exact match
|
|
66
|
+
if (typeof filter.value === 'boolean') {
|
|
67
|
+
return value === filter.value;
|
|
68
|
+
}
|
|
69
|
+
// String contains (case-insensitive)
|
|
70
|
+
if (typeof filter.value === 'string') {
|
|
71
|
+
return String(value).toLowerCase().includes(filter.value.toLowerCase());
|
|
72
|
+
}
|
|
73
|
+
// Exact match fallback
|
|
74
|
+
return value === filter.value;
|
|
75
|
+
}
|
|
76
|
+
function matchesSearch(item, search) {
|
|
77
|
+
if (!search)
|
|
78
|
+
return true;
|
|
79
|
+
const lowerSearch = search.toLowerCase();
|
|
80
|
+
const fields = searchFields ?? Object.keys(item).filter(k => typeof item[k] === 'string');
|
|
81
|
+
return fields.some(field => {
|
|
82
|
+
const val = item[field];
|
|
83
|
+
return typeof val === 'string' && val.toLowerCase().includes(lowerSearch);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function compareValues(a, b) {
|
|
87
|
+
if (a === b)
|
|
88
|
+
return 0;
|
|
89
|
+
if (a == null)
|
|
90
|
+
return -1;
|
|
91
|
+
if (b == null)
|
|
92
|
+
return 1;
|
|
93
|
+
if (typeof a === 'string' && typeof b === 'string') {
|
|
94
|
+
return a.localeCompare(b);
|
|
95
|
+
}
|
|
96
|
+
if (a instanceof Date && b instanceof Date) {
|
|
97
|
+
return a.getTime() - b.getTime();
|
|
98
|
+
}
|
|
99
|
+
return a < b ? -1 : 1;
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
async getList(params) {
|
|
103
|
+
await maybeDelay();
|
|
104
|
+
let result = [...items];
|
|
105
|
+
// Apply filters
|
|
106
|
+
if (params.filter) {
|
|
107
|
+
for (const filter of params.filter) {
|
|
108
|
+
result = result.filter(item => matchesFilter(item, filter));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Apply search
|
|
112
|
+
if (params.search) {
|
|
113
|
+
result = result.filter(item => matchesSearch(item, params.search));
|
|
114
|
+
}
|
|
115
|
+
const total = result.length;
|
|
116
|
+
// Apply sorting
|
|
117
|
+
if (params.sort && params.sort.length > 0) {
|
|
118
|
+
result.sort((a, b) => {
|
|
119
|
+
for (const sortCol of params.sort) {
|
|
120
|
+
const cmp = compareValues(a[sortCol.id], b[sortCol.id]);
|
|
121
|
+
if (cmp !== 0)
|
|
122
|
+
return sortCol.desc ? -cmp : cmp;
|
|
123
|
+
}
|
|
124
|
+
return 0;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
// Apply pagination
|
|
128
|
+
if (params.pagination) {
|
|
129
|
+
const { page, pageSize } = params.pagination;
|
|
130
|
+
const start = (page - 1) * pageSize;
|
|
131
|
+
result = result.slice(start, start + pageSize);
|
|
132
|
+
}
|
|
133
|
+
return { data: result, total };
|
|
134
|
+
},
|
|
135
|
+
async getOne(id) {
|
|
136
|
+
await maybeDelay();
|
|
137
|
+
const item = items.find(i => getItemId(i) === id);
|
|
138
|
+
if (!item)
|
|
139
|
+
throw new Error(`Item not found: ${id}`);
|
|
140
|
+
return { ...item };
|
|
141
|
+
},
|
|
142
|
+
async create(data) {
|
|
143
|
+
await maybeDelay();
|
|
144
|
+
const newItem = {
|
|
145
|
+
...data,
|
|
146
|
+
[idField]: data[idField] ?? String(nextId++),
|
|
147
|
+
};
|
|
148
|
+
items.push(newItem);
|
|
149
|
+
return { ...newItem };
|
|
150
|
+
},
|
|
151
|
+
async update(id, data) {
|
|
152
|
+
await maybeDelay();
|
|
153
|
+
const index = items.findIndex(i => getItemId(i) === id);
|
|
154
|
+
if (index === -1)
|
|
155
|
+
throw new Error(`Item not found: ${id}`);
|
|
156
|
+
items[index] = { ...items[index], ...data };
|
|
157
|
+
return { ...items[index] };
|
|
158
|
+
},
|
|
159
|
+
async updateMany(ids, data) {
|
|
160
|
+
await maybeDelay();
|
|
161
|
+
const updated = [];
|
|
162
|
+
for (const id of ids) {
|
|
163
|
+
const index = items.findIndex(i => getItemId(i) === id);
|
|
164
|
+
if (index !== -1) {
|
|
165
|
+
items[index] = { ...items[index], ...data };
|
|
166
|
+
updated.push({ ...items[index] });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return updated;
|
|
170
|
+
},
|
|
171
|
+
async delete(id) {
|
|
172
|
+
await maybeDelay();
|
|
173
|
+
const index = items.findIndex(i => getItemId(i) === id);
|
|
174
|
+
if (index === -1)
|
|
175
|
+
throw new Error(`Item not found: ${id}`);
|
|
176
|
+
items.splice(index, 1);
|
|
177
|
+
},
|
|
178
|
+
async deleteMany(ids) {
|
|
179
|
+
await maybeDelay();
|
|
180
|
+
const idSet = new Set(ids);
|
|
181
|
+
items = items.filter(i => !idSet.has(getItemId(i)));
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=data-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-provider.js","sourceRoot":"","sources":["../src/data-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgDH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,sBAAsB,CACpC,WAAgB,EAChB,UAAmC,EAAE;IAErC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC;IACxC,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC;IACzC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAE1C,yBAAyB;IACzB,IAAI,KAAK,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC;IAC7B,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAE9B,MAAM,UAAU,GAAG,GAAG,EAAE,CACtB,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAE/E,SAAS,SAAS,CAAC,IAAO;QACxB,OAAO,MAAM,CAAE,IAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,SAAS,aAAa,CAAC,IAAO,EAAE,MAAoB;QAClD,MAAM,KAAK,GAAI,IAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEvC,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,KAAK,KAAK,EAAE,EAAE,CAAC;YAC/E,OAAO,IAAI,CAAC;QACd,CAAC;QAED,2DAA2D;QAC3D,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAChC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC3C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,0CAA0C;gBAC1C,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;YACrD,CAAC;YACD,OAAO,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACtC,CAAC;QAED,6BAA6B;QAC7B,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,IAAI,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACzF,MAAM,KAAK,GAAG,MAAM,CAAC,KAAuC,CAAC;YAC7D,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC1B,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,GAAG,GAAG,KAAK,CAAC,GAAG;gBAAE,OAAO,KAAK,CAAC;YAC7D,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,GAAG,GAAG,KAAK,CAAC,GAAG;gBAAE,OAAO,KAAK,CAAC;YAC7D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,sBAAsB;QACtB,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YACtC,OAAO,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC;QAChC,CAAC;QAED,qCAAqC;QACrC,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACrC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1E,CAAC;QAED,uBAAuB;QACvB,OAAO,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC;IAChC,CAAC;IAED,SAAS,aAAa,CAAC,IAAO,EAAE,MAAc;QAC5C,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,YAAY,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAQ,IAAY,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC;QACnG,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YACzB,MAAM,GAAG,GAAI,IAAY,CAAC,KAAK,CAAC,CAAC;YACjC,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;IACL,CAAC;IAED,SAAS,aAAa,CAAC,CAAM,EAAE,CAAM;QACnC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,IAAI;YAAE,OAAO,CAAC,CAAC,CAAC;QACzB,IAAI,CAAC,IAAI,IAAI;YAAE,OAAO,CAAC,CAAC;QACxB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YACnD,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC;YAC3C,OAAO,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;QACnC,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC;IAED,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,MAAqB;YACjC,MAAM,UAAU,EAAE,CAAC;YAEnB,IAAI,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;YAExB,gBAAgB;YAChB,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;oBACnC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;gBAC9D,CAAC;YACH,CAAC;YAED,eAAe;YACf,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,MAAO,CAAC,CAAC,CAAC;YACtE,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;YAE5B,gBAAgB;YAChB,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1C,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;oBACnB,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAK,EAAE,CAAC;wBACnC,MAAM,GAAG,GAAG,aAAa,CAAE,CAAS,CAAC,OAAO,CAAC,EAAE,CAAC,EAAG,CAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;wBAC1E,IAAI,GAAG,KAAK,CAAC;4BAAE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;oBAClD,CAAC;oBACD,OAAO,CAAC,CAAC;gBACX,CAAC,CAAC,CAAC;YACL,CAAC;YAED,mBAAmB;YACnB,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBACtB,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC;gBAC7C,MAAM,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;gBACpC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,CAAC,CAAC;YACjD,CAAC;YAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACjC,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,EAAU;YACrB,MAAM,UAAU,EAAE,CAAC;YACnB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YAClD,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;YACpD,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;QACrB,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,IAAgB;YAC3B,MAAM,UAAU,EAAE,CAAC;YACnB,MAAM,OAAO,GAAG;gBACd,GAAG,IAAI;gBACP,CAAC,OAAO,CAAC,EAAG,IAAY,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;aACjD,CAAC;YACP,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpB,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;QACxB,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,IAAgB;YACvC,MAAM,UAAU,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACxD,IAAI,KAAK,KAAK,CAAC,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;YAC3D,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC;YAC5C,OAAO,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,GAAa,EAAE,IAAgB;YAC9C,MAAM,UAAU,EAAE,CAAC;YACnB,MAAM,OAAO,GAAQ,EAAE,CAAC;YACxB,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;gBACrB,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;gBACxD,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;oBACjB,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC;oBAC5C,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACpC,CAAC;YACH,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,EAAU;YACrB,MAAM,UAAU,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YACxD,IAAI,KAAK,KAAK,CAAC,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;YAC3D,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QACzB,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,GAAa;YAC5B,MAAM,UAAU,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAC3B,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generators: Transform CollectionDefinition into framework-specific configurations.
|
|
3
|
+
*
|
|
4
|
+
* This module produces configuration objects that existing renderers consume:
|
|
5
|
+
* - toColumnDefs() → TanStack Table ColumnDef[] shape
|
|
6
|
+
* - toFormConfig() → Form field configuration for create/edit forms
|
|
7
|
+
* - toFilterConfig() → Filter field configuration for filter panels
|
|
8
|
+
*
|
|
9
|
+
* These are "headless" — they produce data, not React components.
|
|
10
|
+
* The actual rendering is done by a renderer package (e.g. shadcn/ui) or equivalent.
|
|
11
|
+
*/
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import type { CollectionDefinition } from './collection.js';
|
|
14
|
+
import type { FilterType } from './types.js';
|
|
15
|
+
/**
|
|
16
|
+
* A framework-agnostic column definition.
|
|
17
|
+
* Structurally compatible with TanStack Table's ColumnDef but framework-independent.
|
|
18
|
+
*/
|
|
19
|
+
export interface ColumnConfig {
|
|
20
|
+
/** Column identifier (matches field key). */
|
|
21
|
+
id: string;
|
|
22
|
+
/** Display header text. */
|
|
23
|
+
header: string;
|
|
24
|
+
/** Field accessor key. */
|
|
25
|
+
accessorKey?: string;
|
|
26
|
+
enableSorting: boolean;
|
|
27
|
+
enableColumnFilter: boolean;
|
|
28
|
+
enableGlobalFilter: boolean;
|
|
29
|
+
enableGrouping: boolean;
|
|
30
|
+
enableHiding: boolean;
|
|
31
|
+
enableResizing: boolean;
|
|
32
|
+
size?: number;
|
|
33
|
+
minSize?: number;
|
|
34
|
+
maxSize?: number;
|
|
35
|
+
sortingFn?: string;
|
|
36
|
+
sortDescFirst?: boolean;
|
|
37
|
+
filterFn?: string;
|
|
38
|
+
meta: {
|
|
39
|
+
zodType: string;
|
|
40
|
+
filterType: FilterType | boolean;
|
|
41
|
+
editable: boolean;
|
|
42
|
+
inlineEditable: boolean;
|
|
43
|
+
displayFormat?: string;
|
|
44
|
+
badge?: Record<string, string>;
|
|
45
|
+
copyable?: boolean;
|
|
46
|
+
truncate?: number;
|
|
47
|
+
tooltip?: boolean;
|
|
48
|
+
enumValues?: string[];
|
|
49
|
+
numericBounds?: {
|
|
50
|
+
min?: number;
|
|
51
|
+
max?: number;
|
|
52
|
+
};
|
|
53
|
+
pinned?: 'left' | 'right' | false;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Generate column definitions from a CollectionDefinition.
|
|
58
|
+
*
|
|
59
|
+
* @returns An array of ColumnConfig objects compatible with TanStack Table.
|
|
60
|
+
*/
|
|
61
|
+
export declare function toColumnDefs<T extends z.ZodObject<any>>(collection: CollectionDefinition<T>): ColumnConfig[];
|
|
62
|
+
export interface FormFieldConfig {
|
|
63
|
+
/** Field key. */
|
|
64
|
+
name: string;
|
|
65
|
+
/** Display label. */
|
|
66
|
+
label: string;
|
|
67
|
+
/** Input widget type. */
|
|
68
|
+
type: string;
|
|
69
|
+
/** Whether this field is required. */
|
|
70
|
+
required: boolean;
|
|
71
|
+
/** Whether this field is disabled (read-only). */
|
|
72
|
+
disabled: boolean;
|
|
73
|
+
/** Whether this field is hidden. */
|
|
74
|
+
hidden: boolean;
|
|
75
|
+
/** Placeholder text. */
|
|
76
|
+
placeholder?: string;
|
|
77
|
+
/** Help text. */
|
|
78
|
+
helpText?: string;
|
|
79
|
+
/** Default value. */
|
|
80
|
+
defaultValue?: unknown;
|
|
81
|
+
/** Options for select/multiselect fields. */
|
|
82
|
+
options?: {
|
|
83
|
+
label: string;
|
|
84
|
+
value: string;
|
|
85
|
+
}[];
|
|
86
|
+
/** Display order. */
|
|
87
|
+
order: number;
|
|
88
|
+
/** Zod type for the underlying schema. */
|
|
89
|
+
zodType: string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Generate form field configurations for create or edit forms.
|
|
93
|
+
*/
|
|
94
|
+
export declare function toFormConfig<T extends z.ZodObject<any>>(collection: CollectionDefinition<T>, mode?: 'create' | 'edit'): FormFieldConfig[];
|
|
95
|
+
export interface FilterFieldConfig {
|
|
96
|
+
/** Field key. */
|
|
97
|
+
name: string;
|
|
98
|
+
/** Display label. */
|
|
99
|
+
label: string;
|
|
100
|
+
/** Filter UI type. */
|
|
101
|
+
filterType: FilterType;
|
|
102
|
+
/** Options for select/multiselect filters. */
|
|
103
|
+
options?: {
|
|
104
|
+
label: string;
|
|
105
|
+
value: string;
|
|
106
|
+
}[];
|
|
107
|
+
/** Numeric bounds for range filters. */
|
|
108
|
+
bounds?: {
|
|
109
|
+
min?: number;
|
|
110
|
+
max?: number;
|
|
111
|
+
};
|
|
112
|
+
/** Zod type. */
|
|
113
|
+
zodType: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Generate filter field configurations for the filter panel.
|
|
117
|
+
*/
|
|
118
|
+
export declare function toFilterConfig<T extends z.ZodObject<any>>(collection: CollectionDefinition<T>): FilterFieldConfig[];
|