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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thor Whalen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
# zod-collection-ui
|
|
2
|
+
|
|
3
|
+
Declare once, render anywhere. Define a Zod schema and get auto-generated table columns, form fields, filters, state management, and data provider — with zero configuration.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { defineCollection, toColumnDefs, toFormConfig, toFilterConfig } from 'zod-collection-ui';
|
|
8
|
+
|
|
9
|
+
const products = defineCollection(z.object({
|
|
10
|
+
id: z.string().uuid(),
|
|
11
|
+
name: z.string().min(1),
|
|
12
|
+
status: z.enum(['draft', 'active', 'archived']),
|
|
13
|
+
price: z.number().min(0),
|
|
14
|
+
tags: z.array(z.string()),
|
|
15
|
+
createdAt: z.date(),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
// Auto-generated from the schema:
|
|
19
|
+
const columns = toColumnDefs(products); // TanStack Table column defs
|
|
20
|
+
const form = toFormConfig(products); // Form field configs (create/edit)
|
|
21
|
+
const filters = toFilterConfig(products); // Filter panel configs
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
That's it. No annotations needed. The library infers that `id` is hidden, `name` is searchable, `status` is a select filter, `price` is a range filter, and `createdAt` is not editable — all from the Zod types and field names.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install zod-collection-ui zod
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Requires Zod v4+.
|
|
33
|
+
|
|
34
|
+
## Why
|
|
35
|
+
|
|
36
|
+
Every schema-driven UI tool solves half the problem:
|
|
37
|
+
|
|
38
|
+
- **Form generators** (RJSF, AutoForm) handle data shape → form widgets, but not collections
|
|
39
|
+
- **Table libraries** (TanStack Table, AG Grid) handle column config → table features, but not forms
|
|
40
|
+
- **CRUD frameworks** (React Admin, Refine) handle both, but imperatively — not from a schema
|
|
41
|
+
|
|
42
|
+
`zod-collection-ui` bridges the gap: one Zod schema produces table columns, form fields, filter configs, state management, and a data provider interface. Headless — bring your own renderer.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **Zero config**: A plain Zod schema produces a working collection with sensible defaults
|
|
47
|
+
- **4-layer inference**: Zod type → validation checks → field name heuristics → `.meta()` annotations
|
|
48
|
+
- **Headless**: Produces data structures, not React components — works with any framework
|
|
49
|
+
- **TanStack Table compatible**: `toColumnDefs()` outputs column definitions with sort/filter/group config
|
|
50
|
+
- **Form generation**: `toFormConfig()` outputs field configs for create and edit forms
|
|
51
|
+
- **Filter panels**: `toFilterConfig()` outputs filter configs with enum options and numeric bounds
|
|
52
|
+
- **State management**: `createCollectionStore()` produces framework-agnostic state + actions + selectors
|
|
53
|
+
- **Data provider**: `DataProvider<T>` interface with in-memory adapter for prototyping
|
|
54
|
+
- **AI-ready**: `toPrompt()` generates structured descriptions for LLM consumption
|
|
55
|
+
- **Custom operations**: Declare item/selection/collection actions with confirmation dialogs
|
|
56
|
+
- **TypeScript-first**: Full type inference from your Zod schema
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
### Zero Config
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { z } from 'zod';
|
|
64
|
+
import { defineCollection, toColumnDefs } from 'zod-collection-ui';
|
|
65
|
+
|
|
66
|
+
// Just a Zod schema — no annotations needed
|
|
67
|
+
const contacts = defineCollection(z.object({
|
|
68
|
+
id: z.string(),
|
|
69
|
+
name: z.string(),
|
|
70
|
+
email: z.string(),
|
|
71
|
+
role: z.enum(['customer', 'partner', 'lead']),
|
|
72
|
+
isActive: z.boolean().default(true),
|
|
73
|
+
createdAt: z.date(),
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
// The library auto-detects:
|
|
77
|
+
contacts.idField; // 'id'
|
|
78
|
+
contacts.labelField; // 'name' (detected from name pattern)
|
|
79
|
+
contacts.getSearchableFields(); // ['name', 'email']
|
|
80
|
+
contacts.getGroupableFields(); // [{ key: 'role', ... }, { key: 'isActive', ... }]
|
|
81
|
+
|
|
82
|
+
// Generate table columns
|
|
83
|
+
const columns = toColumnDefs(contacts);
|
|
84
|
+
// → name: sortable, searchable
|
|
85
|
+
// → email: sortable, searchable, email widget
|
|
86
|
+
// → role: sortable, select filter, groupable
|
|
87
|
+
// → isActive: boolean filter, groupable
|
|
88
|
+
// → id, createdAt: auto-hidden/non-editable
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### With Overrides
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const products = defineCollection(ProductSchema, {
|
|
95
|
+
affordances: {
|
|
96
|
+
bulkDelete: true,
|
|
97
|
+
export: ['csv', 'json'],
|
|
98
|
+
pagination: { defaultPageSize: 50 },
|
|
99
|
+
defaultSort: { field: 'createdAt', direction: 'desc' },
|
|
100
|
+
},
|
|
101
|
+
fields: {
|
|
102
|
+
name: { inlineEditable: true, columnWidth: 250 },
|
|
103
|
+
status: { badge: { draft: 'secondary', active: 'default', archived: 'outline' } },
|
|
104
|
+
price: { displayFormat: 'currency' },
|
|
105
|
+
description: { detailOnly: true },
|
|
106
|
+
},
|
|
107
|
+
operations: [
|
|
108
|
+
{ name: 'archive', label: 'Archive', scope: 'item', confirm: true },
|
|
109
|
+
{ name: 'bulkArchive', label: 'Archive Selected', scope: 'selection' },
|
|
110
|
+
{ name: 'exportReport', label: 'Export', scope: 'collection' },
|
|
111
|
+
],
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### With Zod `.meta()`
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const TaskSchema = z.object({
|
|
119
|
+
id: z.string().uuid(),
|
|
120
|
+
title: z.string().meta({
|
|
121
|
+
title: 'Task Title',
|
|
122
|
+
inlineEditable: true,
|
|
123
|
+
summaryField: true,
|
|
124
|
+
}),
|
|
125
|
+
status: z.enum(['todo', 'in_progress', 'done']).meta({
|
|
126
|
+
badge: { todo: 'secondary', in_progress: 'default', done: 'success' },
|
|
127
|
+
}),
|
|
128
|
+
priority: z.enum(['low', 'medium', 'high']).meta({
|
|
129
|
+
badge: { low: 'ghost', medium: 'secondary', high: 'destructive' },
|
|
130
|
+
}),
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## API
|
|
135
|
+
|
|
136
|
+
### `defineCollection(schema, config?)`
|
|
137
|
+
|
|
138
|
+
Main entry point. Takes a Zod object schema and optional config, returns a `CollectionDefinition`.
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
const collection = defineCollection(MySchema, {
|
|
142
|
+
affordances?: CollectionAffordances, // Collection-level capabilities
|
|
143
|
+
fields?: Record<string, FieldAffordance>, // Per-field overrides
|
|
144
|
+
operations?: OperationDefinition[], // Custom actions
|
|
145
|
+
idField?: string, // Default: auto-detected
|
|
146
|
+
labelField?: string, // Default: auto-detected
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Returns** a `CollectionDefinition` with:
|
|
151
|
+
- `schema` — The source Zod schema
|
|
152
|
+
- `affordances` — Resolved collection-level affordances
|
|
153
|
+
- `fieldAffordances` — Per-field affordances (inferred + merged)
|
|
154
|
+
- `operations` — Custom operations
|
|
155
|
+
- `idField` / `labelField` — Identity fields
|
|
156
|
+
- `getVisibleFields()` — Fields for table view
|
|
157
|
+
- `getSearchableFields()` — Fields for global search
|
|
158
|
+
- `getFilterableFields()` — Fields with filter config
|
|
159
|
+
- `getSortableFields()` — Sortable fields
|
|
160
|
+
- `getGroupableFields()` — Groupable fields
|
|
161
|
+
- `getOperations(scope)` — Operations by scope
|
|
162
|
+
- `describe()` — Human-readable description
|
|
163
|
+
|
|
164
|
+
### `toColumnDefs(collection)`
|
|
165
|
+
|
|
166
|
+
Generates TanStack Table-compatible column definitions.
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
const columns = toColumnDefs(collection);
|
|
170
|
+
// Each column has: id, header, accessorKey, enableSorting, enableColumnFilter,
|
|
171
|
+
// enableGlobalFilter, enableGrouping, sortingFn, filterFn, size, meta
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### `toFormConfig(collection, mode)`
|
|
175
|
+
|
|
176
|
+
Generates form field configurations for create or edit forms.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const createFields = toFormConfig(collection, 'create');
|
|
180
|
+
const editFields = toFormConfig(collection, 'edit');
|
|
181
|
+
// Each field has: name, label, type, required, disabled, options, placeholder, helpText
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### `toFilterConfig(collection)`
|
|
185
|
+
|
|
186
|
+
Generates filter panel configurations.
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const filters = toFilterConfig(collection);
|
|
190
|
+
// Each filter has: name, label, filterType, options (for enums), bounds (for ranges)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### `createCollectionStore(collection)`
|
|
194
|
+
|
|
195
|
+
Creates a framework-agnostic state factory with pure functions.
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
const store = createCollectionStore<Product>(collection);
|
|
199
|
+
|
|
200
|
+
// Initial state (derived from collection affordances)
|
|
201
|
+
let state = store.initialState;
|
|
202
|
+
|
|
203
|
+
// Pure actions: (state, args) → newState
|
|
204
|
+
state = store.actions.setItems(state, products, totalCount);
|
|
205
|
+
state = store.actions.setSorting(state, [{ id: 'price', desc: true }]);
|
|
206
|
+
state = store.actions.setColumnFilters(state, [{ id: 'status', value: 'active' }]);
|
|
207
|
+
state = store.actions.setGlobalFilter(state, 'search term');
|
|
208
|
+
state = store.actions.selectAll(state);
|
|
209
|
+
state = store.actions.reset(state);
|
|
210
|
+
|
|
211
|
+
// Selectors
|
|
212
|
+
store.selectors.getSelectedItems(state); // T[]
|
|
213
|
+
store.selectors.getSelectedCount(state); // number
|
|
214
|
+
store.selectors.getPageCount(state); // number
|
|
215
|
+
store.selectors.isAllSelected(state); // boolean
|
|
216
|
+
store.selectors.getVisibleItems(state); // T[] (current page)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Works with any state library:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
// With Zustand
|
|
223
|
+
const useStore = create(() => store.initialState);
|
|
224
|
+
|
|
225
|
+
// With React useReducer
|
|
226
|
+
const [state, dispatch] = useReducer(reducer, store.initialState);
|
|
227
|
+
|
|
228
|
+
// With plain variables
|
|
229
|
+
let state = store.initialState;
|
|
230
|
+
state = store.actions.setItems(state, data);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### `createInMemoryProvider(data, options?)`
|
|
234
|
+
|
|
235
|
+
Creates a `DataProvider<T>` backed by an in-memory array. Supports sorting, filtering, search, and pagination.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
const provider = createInMemoryProvider(products, {
|
|
239
|
+
idField: 'id', // default
|
|
240
|
+
simulateDelay: 100, // ms, for testing loading states
|
|
241
|
+
searchFields: ['name'], // default: all string fields
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const { data, total } = await provider.getList({
|
|
245
|
+
sort: [{ id: 'price', desc: true }],
|
|
246
|
+
filter: [{ id: 'status', value: ['active'] }],
|
|
247
|
+
search: 'widget',
|
|
248
|
+
pagination: { page: 1, pageSize: 25 },
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
await provider.create({ name: 'New Product', ... });
|
|
252
|
+
await provider.update('id-123', { price: 29.99 });
|
|
253
|
+
await provider.delete('id-123');
|
|
254
|
+
await provider.deleteMany(['id-1', 'id-2']);
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### `toPrompt(collection)`
|
|
258
|
+
|
|
259
|
+
Generates a structured markdown description for LLM/AI consumption.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
const prompt = toPrompt(collection);
|
|
263
|
+
// Returns markdown with: data shape table, capabilities, filter config,
|
|
264
|
+
// custom operations, and UI generation hints
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Inference Rules
|
|
268
|
+
|
|
269
|
+
The library infers affordances from four layers (later overrides earlier):
|
|
270
|
+
|
|
271
|
+
| Layer | Source | Example |
|
|
272
|
+
|-------|--------|---------|
|
|
273
|
+
| 1. Type | Zod type | `z.string()` → sortable, searchable, text filter |
|
|
274
|
+
| 2. Validation | Zod checks | `z.string().email()` → email widget |
|
|
275
|
+
| 3. Name | Field name | `createdAt` → not editable, range filter |
|
|
276
|
+
| 4. Meta | `.meta()` | `.meta({ sortable: false })` → override |
|
|
277
|
+
|
|
278
|
+
**Type defaults:**
|
|
279
|
+
|
|
280
|
+
| Zod Type | Sortable | Filterable | Searchable | Editable |
|
|
281
|
+
|----------|----------|------------|------------|----------|
|
|
282
|
+
| `string` | yes | search | yes | yes |
|
|
283
|
+
| `number` | yes | range | no | yes |
|
|
284
|
+
| `boolean` | yes | boolean | no | yes |
|
|
285
|
+
| `enum` | yes | select | no | yes |
|
|
286
|
+
| `date` | yes | range | no | yes |
|
|
287
|
+
| `array` | no | contains | no | yes |
|
|
288
|
+
| `object` | no | no | no | yes |
|
|
289
|
+
|
|
290
|
+
**Name heuristics:**
|
|
291
|
+
|
|
292
|
+
| Pattern | Inference |
|
|
293
|
+
|---------|-----------|
|
|
294
|
+
| `id`, `_id`, `uuid` | hidden, not editable, exact filter |
|
|
295
|
+
| `createdAt`, `created_at` | not editable, range filter |
|
|
296
|
+
| `updatedAt` | hidden, not editable |
|
|
297
|
+
| `password`, `secret`, `token` | hidden, not readable, not searchable |
|
|
298
|
+
| `email` | searchable, email widget |
|
|
299
|
+
| `name`, `title` | searchable, summary field |
|
|
300
|
+
| `description`, `notes` | textarea, truncated, not sortable |
|
|
301
|
+
| `status`, `state` | groupable, select filter |
|
|
302
|
+
| `imageUrl`, `avatar` | not sortable, not filterable |
|
|
303
|
+
|
|
304
|
+
## Field Affordances
|
|
305
|
+
|
|
306
|
+
Every field can declare these capabilities:
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
interface FieldAffordance {
|
|
310
|
+
// Query
|
|
311
|
+
sortable?: boolean | 'asc' | 'desc' | 'both' | 'none';
|
|
312
|
+
filterable?: boolean | 'exact' | 'search' | 'select' | 'multiSelect' | 'range' | 'contains' | 'boolean';
|
|
313
|
+
searchable?: boolean;
|
|
314
|
+
groupable?: boolean;
|
|
315
|
+
aggregatable?: boolean | ('sum' | 'avg' | 'min' | 'max' | 'count')[];
|
|
316
|
+
|
|
317
|
+
// CRUD
|
|
318
|
+
editable?: boolean;
|
|
319
|
+
inlineEditable?: boolean;
|
|
320
|
+
immutableAfterCreate?: boolean;
|
|
321
|
+
|
|
322
|
+
// Display
|
|
323
|
+
visible?: boolean;
|
|
324
|
+
hidden?: boolean;
|
|
325
|
+
detailOnly?: boolean;
|
|
326
|
+
summaryField?: boolean;
|
|
327
|
+
columnWidth?: number;
|
|
328
|
+
badge?: Record<string, string>;
|
|
329
|
+
copyable?: boolean;
|
|
330
|
+
truncate?: number;
|
|
331
|
+
editWidget?: string;
|
|
332
|
+
// ... and more
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Collection Affordances
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
interface CollectionAffordances {
|
|
340
|
+
create?: boolean;
|
|
341
|
+
delete?: boolean;
|
|
342
|
+
bulkDelete?: boolean;
|
|
343
|
+
bulkEdit?: boolean | string[];
|
|
344
|
+
search?: boolean | { debounce?: number; placeholder?: string };
|
|
345
|
+
pagination?: boolean | { defaultPageSize?: number; style?: 'pages' | 'infinite' };
|
|
346
|
+
defaultSort?: { field: string; direction: 'asc' | 'desc' };
|
|
347
|
+
selectable?: boolean | 'single' | 'multi';
|
|
348
|
+
export?: boolean | string[];
|
|
349
|
+
views?: ('table' | 'grid' | 'list' | 'kanban')[];
|
|
350
|
+
// ... and more
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Examples
|
|
355
|
+
|
|
356
|
+
Run the included examples:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
npx tsx examples/01-basic-usage.ts # Simplest usage
|
|
360
|
+
npx tsx examples/02-ecommerce-catalog.ts # Full pipeline with overrides
|
|
361
|
+
npx tsx examples/03-task-tracker.ts # .meta() annotations, state management
|
|
362
|
+
npx tsx examples/04-zero-config.ts # Zero config demo
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Design Philosophy
|
|
366
|
+
|
|
367
|
+
1. **Thin glue, not a framework.** ~1500 lines that reads Zod schemas and produces config objects for existing renderers.
|
|
368
|
+
2. **Convention over configuration.** A plain Zod schema produces a working collection — zero annotations needed.
|
|
369
|
+
3. **Escape hatches everywhere.** Any auto-generated config can be overridden per-field, per-collection, or per-view.
|
|
370
|
+
4. **Zod-native.** The schema IS the source of truth. Affordances are metadata ON the schema.
|
|
371
|
+
5. **Headless first.** Produces data structures, not React components. Renderers are separate.
|
|
372
|
+
|
|
373
|
+
## Background
|
|
374
|
+
|
|
375
|
+
This library fills a gap in the JS/TS ecosystem: no single library lets you declare both the **data shape** and the **available operations** (sort, filter, edit, bulk delete, search, create) in a unified schema that a renderer consumes.
|
|
376
|
+
|
|
377
|
+
The concept draws from [affordance theory](https://en.wikipedia.org/wiki/Affordance) (Gibson/Norman), [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS), OData's [Capabilities Vocabulary](https://github.com/oasis-tcs/odata-vocabularies), and 30 years of [model-based UI development](https://www.w3.org/2007/uwa/editors-drafts/mbui/latest/Model-Based-UI-XG-FinalReport.html) research.
|
|
378
|
+
|
|
379
|
+
See the full [landscape analysis](https://github.com/thorwhalen/zod-collection-ui/blob/main/schema_affordance_ui_report.md) for details.
|
|
380
|
+
|
|
381
|
+
## License
|
|
382
|
+
|
|
383
|
+
MIT
|
|
@@ -0,0 +1,77 @@
|
|
|
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 { z } from 'zod';
|
|
8
|
+
import type { CollectionConfig, CollectionAffordances, FieldAffordance, OperationDefinition } from './types.js';
|
|
9
|
+
export interface CollectionDefinition<TSchema extends z.ZodObject<any>> {
|
|
10
|
+
/** The source Zod schema. */
|
|
11
|
+
schema: TSchema;
|
|
12
|
+
/** Resolved collection-level affordances. */
|
|
13
|
+
affordances: CollectionAffordances;
|
|
14
|
+
/** Resolved per-field affordances (after inference + merge). */
|
|
15
|
+
fieldAffordances: Record<string, FieldAffordance & {
|
|
16
|
+
title: string;
|
|
17
|
+
zodType: string;
|
|
18
|
+
}>;
|
|
19
|
+
/** Custom operations. */
|
|
20
|
+
operations: OperationDefinition[];
|
|
21
|
+
/** Which field is the unique identifier. */
|
|
22
|
+
idField: string;
|
|
23
|
+
/** Which field is the human-readable label. */
|
|
24
|
+
labelField: string;
|
|
25
|
+
/** Get ordered list of visible fields for the collection view. */
|
|
26
|
+
getVisibleFields(): string[];
|
|
27
|
+
/** Get fields that are searchable (for global search). */
|
|
28
|
+
getSearchableFields(): string[];
|
|
29
|
+
/** Get fields that are filterable (for filter panel). */
|
|
30
|
+
getFilterableFields(): {
|
|
31
|
+
key: string;
|
|
32
|
+
affordance: FieldAffordance & {
|
|
33
|
+
title: string;
|
|
34
|
+
};
|
|
35
|
+
}[];
|
|
36
|
+
/** Get fields that are sortable (for sort controls). */
|
|
37
|
+
getSortableFields(): {
|
|
38
|
+
key: string;
|
|
39
|
+
affordance: FieldAffordance & {
|
|
40
|
+
title: string;
|
|
41
|
+
};
|
|
42
|
+
}[];
|
|
43
|
+
/** Get fields that are groupable. */
|
|
44
|
+
getGroupableFields(): {
|
|
45
|
+
key: string;
|
|
46
|
+
affordance: FieldAffordance & {
|
|
47
|
+
title: string;
|
|
48
|
+
};
|
|
49
|
+
}[];
|
|
50
|
+
/** Get operations by scope. */
|
|
51
|
+
getOperations(scope: 'item' | 'selection' | 'collection'): OperationDefinition[];
|
|
52
|
+
/** Generate a human-readable description of affordances. */
|
|
53
|
+
describe(): string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Define a collection from a Zod object schema and optional configuration.
|
|
57
|
+
*
|
|
58
|
+
* This is the main entry point for the collection affordance library.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const projectCollection = defineCollection(
|
|
63
|
+
* z.object({
|
|
64
|
+
* id: z.string().uuid(),
|
|
65
|
+
* name: z.string().min(1),
|
|
66
|
+
* status: z.enum(['draft', 'active', 'archived']),
|
|
67
|
+
* priority: z.number().int().min(1).max(5),
|
|
68
|
+
* }),
|
|
69
|
+
* {
|
|
70
|
+
* affordances: { bulkDelete: true, export: ['csv', 'json'] },
|
|
71
|
+
* fields: { name: { inlineEditable: true } },
|
|
72
|
+
* operations: [{ name: 'archive', label: 'Archive', scope: 'item' }],
|
|
73
|
+
* }
|
|
74
|
+
* );
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function defineCollection<TSchema extends z.ZodObject<any>>(schema: TSchema, config?: CollectionConfig<z.infer<TSchema>>): CollectionDefinition<TSchema>;
|