@rsc-kit/press 0.30.51 → 0.30.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/admin/panel.js +4 -1
- package/dist/admin/panel.js.map +1 -1
- package/dist/conditions.d.ts +32 -0
- package/dist/conditions.js +25 -0
- package/dist/conditions.js.map +1 -0
- package/dist/forms/actions.d.ts +6 -5
- package/dist/forms/actions.js +13 -8
- package/dist/forms/actions.js.map +1 -1
- package/dist/forms/blueprint.d.ts +99 -0
- package/dist/forms/blueprint.js +141 -0
- package/dist/forms/blueprint.js.map +1 -0
- package/dist/forms/brand.d.ts +26 -0
- package/dist/forms/brand.js +9 -0
- package/dist/forms/brand.js.map +1 -0
- package/dist/forms/host.d.ts +1 -1
- package/dist/forms/host.js.map +1 -1
- package/dist/forms/index.d.ts +3 -3
- package/dist/forms/index.js +1 -1
- package/dist/forms/index.js.map +1 -1
- package/dist/forms/registry.d.ts +6 -1
- package/dist/forms/registry.js +1 -1
- package/dist/forms/registry.js.map +1 -1
- package/dist/forms/theme.d.ts +10 -7
- package/dist/forms/theme.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/resource.d.ts +23 -1
- package/dist/resource.js +80 -16
- package/dist/resource.js.map +1 -1
- package/dist/shadcn/blueprint-form.d.ts +8 -0
- package/dist/shadcn/{public-form.js → blueprint-form.js} +7 -6
- package/dist/shadcn/blueprint-form.js.map +1 -0
- package/dist/shadcn/fields.d.ts +11 -1
- package/dist/shadcn/fields.js +72 -10
- package/dist/shadcn/fields.js.map +1 -1
- package/dist/shadcn/index.d.ts +1 -1
- package/dist/shadcn/index.js +3 -3
- package/dist/shadcn/index.js.map +1 -1
- package/dist/shadcn/resource-form.js +3 -2
- package/dist/shadcn/resource-form.js.map +1 -1
- package/package.json +2 -2
- package/dist/forms/form.d.ts +0 -61
- package/dist/forms/form.js +0 -66
- package/dist/forms/form.js.map +0 -1
- package/dist/shadcn/public-form.d.ts +0 -8
- package/dist/shadcn/public-form.js.map +0 -1
package/dist/resource.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { issuesToErrors } from '@rsc-kit/core/standardSchema';
|
|
2
2
|
import { accepts, IMAGE_TYPES, sizeOf } from './uploads.js';
|
|
3
3
|
import { hostSchema, requireDrawable } from './forms/host.js';
|
|
4
|
+
import { isBlueprint } from './forms/brand.js';
|
|
5
|
+
import { shown, slugify } from './conditions.js';
|
|
4
6
|
/** A column, chained: text('name').searchable().sortable(). */
|
|
5
7
|
export class ColumnBuilder {
|
|
6
8
|
#column;
|
|
@@ -54,7 +56,11 @@ export function defineResource() {
|
|
|
54
56
|
throw new Error(`[admin] a resource's name is its url segment, lowercase letters, digits and dashes: ${JSON.stringify(definition.name)}`);
|
|
55
57
|
}
|
|
56
58
|
const { actions } = definition;
|
|
57
|
-
|
|
59
|
+
const blueprint = isBlueprint(definition.form) ? definition.form : null;
|
|
60
|
+
if (blueprint && (actions !== undefined || definition.fields !== undefined)) {
|
|
61
|
+
throw new Error(`[admin] ${definition.name}'s form is the blueprint ${blueprint.name}, so its fields and actions are said there, not here`);
|
|
62
|
+
}
|
|
63
|
+
if (!blueprint && (actions === undefined) === (definition.form === undefined)) {
|
|
58
64
|
throw new Error(`[admin] ${definition.name} needs a form schema, or the backend's actions, and not both`);
|
|
59
65
|
}
|
|
60
66
|
if (actions !== undefined && (typeof actions.create !== 'function' || typeof actions.update !== 'function')) {
|
|
@@ -66,18 +72,22 @@ export function defineResource() {
|
|
|
66
72
|
}
|
|
67
73
|
const overrides = (definition.fields ?? {});
|
|
68
74
|
const host = actions;
|
|
69
|
-
const schema = host ? hostSchema(host.create) : definition.form;
|
|
70
|
-
const editSchema = host ? hostSchema(host.update) : schema;
|
|
75
|
+
const schema = blueprint ? blueprint.schema : host ? hostSchema(host.create) : definition.form;
|
|
76
|
+
const editSchema = blueprint ? blueprint.editSchema : host ? hostSchema(host.update) : schema;
|
|
71
77
|
// A backend's types arrive with the server entry, which runs after this
|
|
72
78
|
// module: its fields wait for the first page that needs them. A schema's
|
|
73
79
|
// fail here, at startup.
|
|
74
80
|
const drawn = {};
|
|
75
81
|
const createFields = () => {
|
|
82
|
+
if (blueprint)
|
|
83
|
+
return blueprint.fields;
|
|
76
84
|
if (host)
|
|
77
85
|
requireDrawable({ name: `[admin] the ${definition.name} resource's create`, key: false }, host.create, overrides);
|
|
78
86
|
return (drawn.create ??= fieldsOf(schema, overrides));
|
|
79
87
|
};
|
|
80
88
|
const editFields = () => {
|
|
89
|
+
if (blueprint)
|
|
90
|
+
return blueprint.editFields;
|
|
81
91
|
if (!host)
|
|
82
92
|
return createFields();
|
|
83
93
|
requireDrawable({ name: `[admin] the ${definition.name} resource's update`, key: true }, host.update, overrides);
|
|
@@ -89,18 +99,33 @@ export function defineResource() {
|
|
|
89
99
|
throw new Error(`[admin] ${definition.name}: the filter ${key} needs an enum field in the form`);
|
|
90
100
|
return { key, label: field.label, options: field.options };
|
|
91
101
|
}));
|
|
92
|
-
|
|
102
|
+
// A blueprint's are checked when the panel first draws, as a backend's are.
|
|
103
|
+
if (!host && !blueprint)
|
|
93
104
|
filters();
|
|
94
|
-
const relations =
|
|
95
|
-
?
|
|
96
|
-
:
|
|
97
|
-
|
|
105
|
+
const relations = blueprint
|
|
106
|
+
? blueprint.relations
|
|
107
|
+
: host
|
|
108
|
+
? Object.values(overrides).flatMap((override) => (override?.relation ? [override.relation] : []))
|
|
109
|
+
: createFields().flatMap((field) => (field.relation ? [field.relation] : []));
|
|
110
|
+
// The saves: the backend's actions, the blueprint's own, or the source's.
|
|
111
|
+
const saves = blueprint
|
|
112
|
+
? blueprint.create && blueprint.update
|
|
113
|
+
? { create: blueprint.create, update: blueprint.update }
|
|
114
|
+
: null
|
|
115
|
+
: host
|
|
116
|
+
? { create: (values) => host.create(values), update: (values, id) => host.update(values, id) }
|
|
117
|
+
: null;
|
|
118
|
+
if (blueprint && !saves && !definition.readOnly && (typeof definition.source.create !== 'function' || typeof definition.source.update !== 'function')) {
|
|
119
|
+
throw new Error(`[admin] ${definition.name}'s blueprint ${blueprint.name} does not both create and update, and its source does not either. ` +
|
|
120
|
+
'Give the blueprint both, or the source create and update, or make the resource readOnly.');
|
|
121
|
+
}
|
|
122
|
+
const source = saves
|
|
98
123
|
? {
|
|
99
124
|
list: (query, context) => definition.source.list(query, context),
|
|
100
125
|
find: (id, context) => definition.source.find(id, context),
|
|
101
126
|
delete: (id, context) => definition.source.delete(id, context),
|
|
102
|
-
create: async (values) => ((await
|
|
103
|
-
update: async (id, values) => ((await
|
|
127
|
+
create: async (values) => ((await saves.create(values)) ?? {}),
|
|
128
|
+
update: async (id, values) => ((await saves.update(values, id)) ?? {}),
|
|
104
129
|
}
|
|
105
130
|
: definition.source;
|
|
106
131
|
const bulkActions = definition.bulkActions ?? [];
|
|
@@ -264,10 +289,10 @@ export function fieldsOf(schema, overrides = {}) {
|
|
|
264
289
|
if (Object.keys(overrides).length === 0) {
|
|
265
290
|
throw new Error(`[admin] this ${schema['~standard'].vendor} schema does not describe its fields as JSON Schema; list them in fields: { name: { type: 'text' }, ... }`);
|
|
266
291
|
}
|
|
267
|
-
return Object.entries(overrides).map(([name, override]) => withSchemas({ name, label: humanize(name), type: 'text', required: true, ...applied(override) }, override));
|
|
292
|
+
return connected(Object.entries(overrides).map(([name, override]) => withSchemas({ name, label: humanize(name), type: 'text', required: true, ...applied(override) }, override)));
|
|
268
293
|
}
|
|
269
294
|
const required = new Set(json.required ?? []);
|
|
270
|
-
return Object.entries(json.properties).map(([name, property]) => {
|
|
295
|
+
return connected(Object.entries(json.properties).map(([name, property]) => {
|
|
271
296
|
const inner = typeOf(property);
|
|
272
297
|
const types = [].concat(inner.type ?? []);
|
|
273
298
|
const options = inner.enum?.filter((option) => option !== null).map((option) => optionOf(String(option)));
|
|
@@ -281,13 +306,16 @@ export function fieldsOf(schema, overrides = {}) {
|
|
|
281
306
|
: 'text';
|
|
282
307
|
const override = overrides[name];
|
|
283
308
|
const numeric = types.includes('number') || types.includes('integer');
|
|
309
|
+
// required_if:type,business: shown for a business, and required then.
|
|
310
|
+
const conditional = property['x-visible'];
|
|
284
311
|
const final = override?.relation ? 'select' : (override?.type ?? type);
|
|
285
312
|
return withSchemas({
|
|
286
313
|
name,
|
|
287
314
|
// categoryId is a Category to whoever picks it.
|
|
288
315
|
label: humanize(override?.relation ? name.replace(/_?[iI]d$/, '') : name),
|
|
289
316
|
type,
|
|
290
|
-
required: required.has(name) && !nullable,
|
|
317
|
+
required: (required.has(name) || conditional?.required === true) && !nullable,
|
|
318
|
+
...(conditional ? { visible: { when: conditional.when, ...(conditional.is !== undefined ? { is: conditional.is } : {}), ...(conditional.not !== undefined ? { not: conditional.not } : {}) } } : {}),
|
|
291
319
|
...(nullable ? { nullable: true } : {}),
|
|
292
320
|
...(options ? { options } : {}),
|
|
293
321
|
...(numeric && final === 'select' ? { numeric: true } : {}),
|
|
@@ -295,7 +323,20 @@ export function fieldsOf(schema, overrides = {}) {
|
|
|
295
323
|
...constraintsOf(final, inner, types.includes('integer')),
|
|
296
324
|
...applied(override),
|
|
297
325
|
}, override);
|
|
298
|
-
});
|
|
326
|
+
}));
|
|
327
|
+
}
|
|
328
|
+
/** A field that depends on another names one the form has, or the form fails at startup rather than never showing it. */
|
|
329
|
+
function connected(fields) {
|
|
330
|
+
const names = new Set(fields.map((field) => field.name));
|
|
331
|
+
for (const field of fields) {
|
|
332
|
+
if (field.visible && (field.visible.when === field.name || !names.has(field.visible.when))) {
|
|
333
|
+
throw new Error(`[press] the field ${field.name} is shown when ${field.visible.when}, and the form has no other field of that name`);
|
|
334
|
+
}
|
|
335
|
+
if (field.from && (field.from.field === field.name || !names.has(field.from.field))) {
|
|
336
|
+
throw new Error(`[press] the field ${field.name} is made from ${field.from.field}, and the form has no other field of that name`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return fields;
|
|
299
340
|
}
|
|
300
341
|
/**
|
|
301
342
|
* A submitted form's values as the schema expects them, by its fields: a
|
|
@@ -304,14 +345,30 @@ export function fieldsOf(schema, overrides = {}) {
|
|
|
304
345
|
* field is left out rather than sent as "".
|
|
305
346
|
*/
|
|
306
347
|
export function valuesFrom(target, form) {
|
|
348
|
+
const types = new Map(target.fields.map((field) => [field.name, field.type]));
|
|
349
|
+
const read = (name) => (types.get(name) === 'toggle' ? form.has(name) : String(form.get(name) ?? ''));
|
|
350
|
+
// A slug left empty is made here as the browser would have: with
|
|
351
|
+
// JavaScript off, or posted by something that is not a browser.
|
|
352
|
+
const made = new Map();
|
|
353
|
+
for (const field of target.fields) {
|
|
354
|
+
if (field.from && String(form.get(field.name) ?? '') === '') {
|
|
355
|
+
const slug = slugify(String(read(field.from.field)));
|
|
356
|
+
if (slug !== '')
|
|
357
|
+
made.set(field.name, slug);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
307
360
|
return Object.fromEntries(target.fields.flatMap((field) => {
|
|
361
|
+
// Hidden by its condition: what a post sent for it anyway is dropped,
|
|
362
|
+
// as the browser would not have sent it.
|
|
363
|
+
if (field.visible && !shown(field.visible, read))
|
|
364
|
+
return [];
|
|
308
365
|
if (field.type === 'toggle')
|
|
309
366
|
return [[field.name, form.has(field.name)]];
|
|
310
367
|
if (field.type === 'blocks')
|
|
311
368
|
return [[field.name, blocksFrom(field, form)]];
|
|
312
369
|
if (field.type === 'file' || field.type === 'image')
|
|
313
370
|
return fileFrom(field, form);
|
|
314
|
-
const value = form.get(field.name);
|
|
371
|
+
const value = made.get(field.name) ?? form.get(field.name);
|
|
315
372
|
if (value === null || (value === '' && !field.required))
|
|
316
373
|
return [];
|
|
317
374
|
// A number field arrives as text. Converted here, so the schema says
|
|
@@ -350,7 +407,14 @@ function blocksFrom(field, form) {
|
|
|
350
407
|
out.push({ type, data: {} });
|
|
351
408
|
continue;
|
|
352
409
|
}
|
|
353
|
-
const data = valuesFrom({
|
|
410
|
+
const data = valuesFrom({
|
|
411
|
+
fields: spec.fields.map((inner) => ({
|
|
412
|
+
...inner,
|
|
413
|
+
name: prefix + inner.name,
|
|
414
|
+
...(inner.visible ? { visible: { ...inner.visible, when: prefix + inner.visible.when } } : {}),
|
|
415
|
+
...(inner.from ? { from: { ...inner.from, field: prefix + inner.from.field } } : {}),
|
|
416
|
+
})),
|
|
417
|
+
}, form);
|
|
354
418
|
out.push({ type, data: Object.fromEntries(Object.entries(data).map(([name, value]) => [name.slice(prefix.length), value])) });
|
|
355
419
|
}
|
|
356
420
|
return out;
|
package/dist/resource.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A resource, described once: what its table shows, what its form takes, and\n * where its rows come from. The panel renders every page from this - nothing\n * per resource is written as a page.\n *\n * The form is a schema, not a list of widgets: the same object validates the\n * save on the server and is read for the fields it has, so a field cannot be\n * on the form and missing from the check, or the other way round. Any\n * Standard Schema validates; one that also describes itself as JSON Schema\n * (Standard JSON Schema - zod 4.2+, and others as they add it) gives its\n * fields too, and any other says them in `fields`.\n */\nimport type { ComponentType, ReactNode } from 'react'\nimport { issuesToErrors } from '@rsc-kit/core/standardSchema'\nimport type { StandardSchemaV1 } from '@rsc-kit/core/standardSchema'\nimport { accepts, IMAGE_TYPES, sizeOf } from './uploads.js'\nimport { hostSchema, requireDrawable } from './forms/host.js'\nimport type { HostAction, HostFields } from './forms/host.js'\nimport type { Storage } from './uploads.js'\n\n/** What the form validates with: any Standard Schema - zod, valibot, arktype, effect. */\nexport type FormSchema = StandardSchemaV1<any, Record<string, unknown>>\n\n/** What a schema's save hands the data source. */\nexport type ValuesOf<Schema extends FormSchema> = NonNullable<Schema['~standard']['types']>['output']\n\nexport type Format = 'text' | 'money' | 'badge' | 'boolean' | 'date' | 'relation'\n\nexport interface Column {\n key: string\n label: string\n format: Format\n sortable: boolean\n searchable: boolean\n /** Shown until the visitor hides it from the Columns menu. */\n visible: boolean\n /** Draws the cell instead of the format. Runs on the server, so it may be async and read anything. */\n cell?: (value: any, row: any) => ReactNode | Promise<ReactNode>\n /** For a relation column: the resource whose row the value names, shown by its title. */\n relation?: string\n}\n\n/** A column, chained: text('name').searchable().sortable(). */\nexport class ColumnBuilder {\n readonly #column: Column\n\n constructor(column: Column) {\n this.#column = column\n }\n\n sortable(): this {\n this.#column.sortable = true\n return this\n }\n\n searchable(): this {\n this.#column.searchable = true\n return this\n }\n\n label(label: string): this {\n this.#column.label = label\n return this\n }\n\n /** In the Columns menu, unticked until the visitor shows it. */\n hidden(): this {\n this.#column.visible = false\n return this\n }\n\n /** Draw the cell yourself: .cell((value, row) => <Avatar src={row.photo} />). */\n cell(render: NonNullable<Column['cell']>): this {\n this.#column.cell = render\n return this\n }\n\n build(): Column {\n return { ...this.#column }\n }\n}\n\n/** The column helpers, typed to the row so a misspelt key fails the typecheck. */\nexport interface Columns<Row> {\n text(key: keyof Row & string): ColumnBuilder\n money(key: keyof Row & string): ColumnBuilder\n badge(key: keyof Row & string): ColumnBuilder\n boolean(key: keyof Row & string): ColumnBuilder\n date(key: keyof Row & string): ColumnBuilder\n /**\n * A belongsTo: the value is another resource's key, shown as that row's\n * title - col.relation('categoryId', 'categories') shows \"Lighting\", not 3.\n */\n relation(key: keyof Row & string, resource: string): ColumnBuilder\n}\n\nfunction columns<Row>(): Columns<Row> {\n const make = (format: Format) => (key: string) =>\n new ColumnBuilder({ key, label: humanize(key), format, sortable: false, searchable: false, visible: true })\n\n return {\n text: make('text'),\n money: make('money'),\n badge: make('badge'),\n boolean: make('boolean'),\n date: make('date'),\n relation: (key, resource) =>\n new ColumnBuilder({ key, label: humanize(key.replace(/_?[iI]d$/, '')), format: 'relation', relation: resource, sortable: false, searchable: false, visible: true }),\n }\n}\n\nexport type FieldType = 'text' | 'textarea' | 'number' | 'select' | 'toggle' | 'email' | 'date' | 'blocks' | 'file' | 'image'\n\n/** A kind of block a blocks field offers, as the form needs it: plain data. */\nexport interface BlockSpec {\n name: string\n label: string\n fields: Field[]\n}\n\n/** One choice of a select: what is saved, and what the visitor reads. */\nexport interface Option {\n value: string\n label: string\n}\n\n/** What the form renders. Plain data: it crosses to the client component. */\nexport interface Field {\n name: string\n label: string\n type: FieldType\n required: boolean\n options?: Option[]\n /**\n * A belongsTo: the options are another resource's rows, its key saved and\n * its title shown, read when the form is drawn and scoped to the tenant.\n */\n relation?: string\n /** A select whose schema wants a number: its value is converted, as a number field's is. */\n numeric?: boolean\n /** Help under the control. */\n description?: string\n /**\n * What the browser checks before a submit, from the schema: the native\n * constraints, so a mistake is caught without a round trip and without the\n * schema in the browser. A courtesy only - the server validates every save.\n */\n min?: number\n max?: number\n minLength?: number\n maxLength?: number\n /** Whole-value, as the pattern attribute matches. */\n pattern?: string\n step?: number | 'any'\n /**\n * A blocks field's kinds of block. Its value is a list of { type, data },\n * each block's data checked by its own schema; in a form, block i's field\n * is named `name.i.field`, which is also where its errors are.\n */\n blocks?: BlockSpec[]\n /**\n * A file or image field's types, as the accept attribute says them:\n * `image/*`, `application/pdf`, `.csv`. Checked by the browser when the\n * file is chosen and by the server when it arrives.\n */\n accept?: string\n /** A file or image field's largest file, in bytes. */\n maxSize?: number\n /** The schema allows null: a file field that is emptied saves null, so the source clears it. */\n nullable?: boolean\n}\n\nexport interface Filter {\n key: string\n label: string\n options: Option[]\n}\n\nexport interface ListQuery {\n q?: string\n sort?: string\n dir: 'asc' | 'desc'\n page: number\n /**\n * The cursor a source that pages by cursor answered as next or prev, from\n * the url's ?cursor=; absent for the first page. Such a source reads this\n * and not page.\n */\n cursor?: string\n /**\n * Null asks for every row: a resource whose table works in the browser\n * (mode: 'client') is loaded whole, and searched, sorted and paged there.\n */\n perPage: number | null\n /** Only the resource's declared filters, each with a value. */\n filters: Record<string, string>\n /** The columns marked searchable: what q is matched against. */\n searchable: string[]\n}\n\n/** Whoever's records these are, in a panel with tenants. */\nexport interface Tenant {\n id: string\n name: string\n /** A second line under the name in the switcher: a plan, a role. */\n description?: string\n /** An image url for the switcher. Without one the first letter is shown. */\n avatar?: string\n}\n\n/** What every call to a data source is told besides its arguments. */\nexport interface SourceContext {\n /** The tenant the visitor is working in, or null in a panel without tenants. */\n tenant: Tenant | null\n}\n\n/**\n * Where a resource's rows live: five functions, and nothing about how. A\n * database (Drizzle, Prisma, SQL), a REST or GraphQL API, a Laravel app through\n * rpc(), a file, memory - the panel calls these and never anything else.\n *\n * In a panel with tenants, a source answers only for context.tenant. find()\n * returning null for another tenant's record is what makes its url a 404.\n */\n/**\n * One page of a list. Counted, { rows, total }, and the table numbers its\n * pages. By cursor, { rows, next, prev } - Laravel's cursorPaginate, a keyset\n * query, an API that hands back a cursor - and the table pages with Previous\n * and Next, asking for the cursor it was given. null is no page that way.\n */\nexport type ListPage<Row> = { rows: Row[]; total: number } | { rows: Row[]; next: string | null; prev?: string | null; total?: number }\n\nexport interface DataSource<Row, Values> {\n list(query: ListQuery, context: SourceContext): Promise<ListPage<Row>>\n find(id: string, context: SourceContext): Promise<Row | null>\n create(values: Values, context: SourceContext): Promise<Row>\n update(id: string, values: Values, context: SourceContext): Promise<Row>\n delete(id: string, context: SourceContext): Promise<void>\n}\n\n/** A source for a resource whose saves are a backend's actions: it lists, finds and deletes. */\nexport type ReadSource<Row> = Pick<DataSource<Row, never>, 'list' | 'find' | 'delete'>\n\n/**\n * Where a table's search, filters, sort and paging happen.\n *\n * 'server' each change is a request; the source is asked for one page.\n * For a table that grows: orders, users, an API with millions.\n * 'client' the source is asked once for every row (perPage: null) and the\n * browser does the rest, with no request per keystroke. For a\n * table that stays small: categories, settings, team members.\n */\nexport type TableMode = 'server' | 'client'\n\n/**\n * Something done to the rows ticked in the table: publish, archive, export.\n * `run` is given the rows themselves, each found by the source as the\n * visitor - an id the browser sent that is not theirs never reaches it.\n */\nexport interface BulkAction<Row = any> {\n /** Identifies it in the call; 'delete' is the panel's own. */\n name: string\n label: string\n /** Asked about first, as a delete is. */\n destructive?: boolean\n /** What happened to the rows, for the toast: 'published' says \"3 products published.\", as Delete says \"deleted\". */\n done?: string\n /** Returns its own words for the toast, or leaves them to `done`. */\n run: (rows: Row[], context: SourceContext) => void | string | Promise<void | string>\n}\n\nexport interface Resource<Row = any, Schema extends FormSchema = FormSchema> {\n name: string\n label: string\n singular: string\n key: keyof Row & string\n title: keyof Row & string\n source: DataSource<Row, ValuesOf<Schema>>\n columns: Column[]\n filters: Filter[]\n schema: Schema\n /** The create form's fields. */\n fields: Field[]\n /** The edit form's: the update action's, for a resource saved by a backend. */\n editFields: Field[]\n editSchema: FormSchema\n /** The resources its fields name, known before the fields are: a backend's arrive with the server entry. */\n relations: string[]\n perPage: number\n mode: TableMode\n /** Listed and searched, never created, edited or deleted from the panel. */\n readOnly: boolean\n bulkActions: BulkAction<Row>[]\n /** The sidebar's icon: a lucide icon, or any component. */\n icon?: ComponentType<{ className?: string }>\n description?: string\n}\n\nexport interface ResourceDefinition<Row, Schema extends FormSchema> {\n /** The url segment: /admin/products. */\n name: string\n label?: string\n singular?: string\n /** The field that identifies a row in the url. */\n key: keyof Row & string\n /** The field an edit page's heading shows. */\n title: keyof Row & string\n source: DataSource<Row, ValuesOf<Schema>>\n table: (col: Columns<Row>) => ColumnBuilder[]\n /** Enum fields of the form, offered as a select above the table. */\n filters?: (keyof Row & string)[]\n /** Any Standard Schema of an object: z.object(), v.object(), type({...}). */\n form: Schema\n /**\n * What the schema cannot say: a long string is a textarea, a label, help.\n * The whole list, for a schema that does not describe itself as JSON\n * Schema - a field missing here is then missing from the form.\n */\n fields?: Partial<Record<keyof ValuesOf<Schema> & string, FieldOverride>>\n perPage?: number\n /** Where search, filters, sort and paging happen. 'server' unless said. */\n mode?: TableMode\n /** Listed and searched, never created, edited or deleted from the panel: a log, a feed, a plugin's own records. */\n readOnly?: boolean\n /** Offered when rows are ticked, beside the panel's own Delete. */\n bulkActions?: BulkAction<Row>[]\n /** The sidebar's icon: import { Package } from 'lucide-react'. */\n icon?: ComponentType<{ className?: string }>\n /** Under the page's heading. */\n description?: string\n}\n\n/**\n * A resource saved by the backend's own actions, with no schema in\n * TypeScript: the generated stubs for a Laravel FormRequest or a Go struct.\n * The create form is drawn from what `create` takes, the edit form from what\n * `update` takes, and every check is the backend's - its 422 lands under the\n * fields. `update` is called with the record's key after the values:\n * `update(UpdateProductRequest $request, string $product)`.\n */\nexport interface HostResourceDefinition<Row, Create extends HostAction, Update extends HostAction>\n extends Omit<ResourceDefinition<Row, FormSchema>, 'source' | 'form' | 'fields' | 'readOnly'> {\n /** Lists, finds and deletes; the saves are the actions. */\n source: ReadSource<Row>\n /** `import { productsStore, productsUpdate } from '../server-actions.generated'`. */\n actions: { create: Create; update: Update }\n /** Labels, controls and relations the backend's types cannot say. */\n fields?: Partial<Record<HostFields<Create> | HostFields<Update>, FieldOverride>>\n}\n\n/**\n * Two calls so the row is named and the schema is inferred:\n * defineResource<Product>()({ ... }).\n */\nexport function defineResource<Row>() {\n function define<Schema extends FormSchema>(definition: ResourceDefinition<Row, Schema>): Resource<Row, Schema>\n function define<Create extends HostAction, Update extends HostAction>(definition: HostResourceDefinition<Row, Create, Update>): Resource<Row>\n function define(\n definition: (ResourceDefinition<Row, FormSchema> | HostResourceDefinition<Row, HostAction, HostAction>) & {\n form?: FormSchema\n actions?: { create?: unknown; update?: unknown }\n readOnly?: boolean\n },\n ): Resource<Row> {\n if (!/^[a-z0-9][a-z0-9-]*$/.test(definition.name)) {\n throw new Error(`[admin] a resource's name is its url segment, lowercase letters, digits and dashes: ${JSON.stringify(definition.name)}`)\n }\n\n const { actions } = definition\n\n if ((actions === undefined) === (definition.form === undefined)) {\n throw new Error(`[admin] ${definition.name} needs a form schema, or the backend's actions, and not both`)\n }\n\n if (actions !== undefined && (typeof actions.create !== 'function' || typeof actions.update !== 'function')) {\n throw new Error(\n `[admin] ${definition.name}'s actions are { create, update }, each the action the build generated: ` +\n \"import { productsStore, productsUpdate } from '../server-actions.generated'\",\n )\n }\n\n if (actions !== undefined && definition.readOnly) {\n throw new Error(`[admin] ${definition.name} is read-only, so it has no actions to save with; list it with a source alone and a form schema`)\n }\n\n const overrides = (definition.fields ?? {}) as Overrides\n const host = actions as { create: HostAction; update: HostAction } | undefined\n const schema = host ? hostSchema(host.create) : definition.form!\n const editSchema = host ? hostSchema(host.update) : schema\n\n // A backend's types arrive with the server entry, which runs after this\n // module: its fields wait for the first page that needs them. A schema's\n // fail here, at startup.\n const drawn: { create?: Field[]; edit?: Field[]; filters?: Filter[] } = {}\n const createFields = (): Field[] => {\n if (host) requireDrawable({ name: `[admin] the ${definition.name} resource's create`, key: false }, host.create, overrides)\n\n return (drawn.create ??= fieldsOf(schema, overrides))\n }\n const editFields = (): Field[] => {\n if (!host) return createFields()\n\n requireDrawable({ name: `[admin] the ${definition.name} resource's update`, key: true }, host.update, overrides)\n\n return (drawn.edit ??= fieldsOf(editSchema, overrides))\n }\n const filters = (): Filter[] =>\n (drawn.filters ??= (definition.filters ?? []).map((key) => {\n const field = createFields().find((candidate) => candidate.name === key)\n\n if (!field?.options) throw new Error(`[admin] ${definition.name}: the filter ${key} needs an enum field in the form`)\n\n return { key, label: field.label, options: field.options }\n }))\n\n if (!host) filters()\n\n const relations = host\n ? Object.values(overrides).flatMap((override) => (override?.relation ? [override.relation] : []))\n : createFields().flatMap((field) => (field.relation ? [field.relation] : []))\n\n const source: DataSource<Row, Record<string, unknown>> = host\n ? {\n list: (query, context) => definition.source.list(query, context),\n find: (id, context) => definition.source.find(id, context),\n delete: (id, context) => definition.source.delete(id, context),\n create: async (values) => ((await host.create(values as never)) ?? {}) as Row,\n update: async (id, values) => ((await (host.update as (values: unknown, id: string) => Promise<unknown>)(values, id)) ?? {}) as Row,\n }\n : (definition.source as DataSource<Row, Record<string, unknown>>)\n const bulkActions = definition.bulkActions ?? []\n const bulkNames = bulkActions.map((action) => action.name)\n\n if (bulkActions.length > 0 && definition.readOnly) {\n throw new Error(`[admin] ${definition.name} is read-only, so it has no bulk actions`)\n }\n\n for (const [index, name] of bulkNames.entries()) {\n if (name === 'delete') throw new Error(`[admin] ${definition.name}: 'delete' is the panel's own bulk action`)\n if (bulkNames.indexOf(name) !== index) throw new Error(`[admin] ${definition.name}: two bulk actions are named ${name}`)\n }\n\n return {\n name: definition.name,\n label: definition.label ?? humanize(definition.name),\n singular: definition.singular ?? humanize(definition.name).replace(/s$/, ''),\n key: definition.key,\n title: definition.title,\n source: source as Resource<Row>['source'],\n columns: definition.table(columns<Row>()).map((column) => column.build()),\n get filters() {\n return filters()\n },\n schema,\n get fields() {\n return createFields()\n },\n get editFields() {\n return editFields()\n },\n editSchema,\n relations,\n perPage: definition.perPage ?? 10,\n mode: definition.mode ?? 'server',\n readOnly: definition.readOnly ?? false,\n bulkActions,\n ...(definition.icon ? { icon: definition.icon } : {}),\n ...(definition.description ? { description: definition.description } : {}),\n }\n }\n\n return define\n}\n\n/** A field as the app says it, over what the schema said. */\nexport type FieldOverride = Partial<Pick<Field, 'type' | 'label' | 'description' | 'required' | 'relation'>> & {\n /** A bare value is labelled by humanizing it: 'in_stock' reads \"In stock\". */\n options?: (string | Option)[]\n /** The kinds a blocks field offers: { type: 'blocks', blocks: [hero, features] }. */\n blocks?: BlockLike[]\n /** Where a file or image field keeps what is uploaded. Required for one. */\n storage?: Storage\n /** A file or image field's types. An image field takes PNG, JPEG, WebP, GIF and AVIF unless it says. */\n accept?: string\n /** A file or image field's largest file, in bytes. */\n maxSize?: number\n}\n\n/** What a blocks field needs of a block: its name, label, fields and schema. */\ninterface BlockLike {\n name: string\n label: string\n fields: Field[]\n schema: FormSchema\n}\n\n/**\n * A blocks field's schemas, by field. Kept beside the field rather than on\n * it, because a field crosses to the browser and a schema cannot.\n */\nconst schemasOf = new WeakMap<Field, Map<string, FormSchema>>()\n\n/** A file field's storage, kept beside it for the same reason. */\nconst storageOf = new WeakMap<Field, Storage>()\n\n/** A bare option labelled for reading, or one the app labelled itself. */\nfunction optionOf(option: string | Option): Option {\n return typeof option === 'string' ? { value: option, label: humanize(option) } : option\n}\n\n/** An override as a field: options made whole, and a relation drawn as a select. */\nfunction applied(override: FieldOverride | undefined): Partial<Field> {\n if (!override) return {}\n\n // storage never goes on the field: a field crosses to the browser.\n const { options, blocks, storage: _, ...rest } = override\n\n return {\n ...(override.relation ? { type: 'select' as FieldType } : {}),\n ...rest,\n ...(options ? { options: options.map(optionOf) } : {}),\n ...(blocks ? { type: 'blocks' as FieldType, blocks: blocks.map(({ name, label, fields }) => ({ name, label, fields })) } : {}),\n }\n}\n\n/** Keeps a blocks field's schemas, and a file field's storage, where parse() finds them. */\nfunction withSchemas(field: Field, override: FieldOverride | undefined): Field {\n if (field.type === 'file' || field.type === 'image') {\n if (!override?.storage) throw new Error(`[press] the ${field.type} field ${field.name} needs storage: where an upload is kept`)\n\n storageOf.set(field, override.storage)\n if (field.type === 'image' && field.accept === undefined) field.accept = IMAGE_TYPES\n\n return field\n }\n\n if (field.type !== 'blocks') return field\n\n if (!override?.blocks?.length) throw new Error(`[press] the blocks field ${field.name} needs blocks: [...] - the kinds it offers`)\n\n schemasOf.set(field, new Map(override.blocks.map((block) => [block.name, block.schema])))\n\n return field\n}\n\ntype Overrides = Record<string, FieldOverride | undefined>\n\ntype JsonSchema = {\n type?: string | string[]\n format?: string\n enum?: unknown[]\n const?: unknown\n anyOf?: JsonSchema[]\n oneOf?: JsonSchema[]\n properties?: Record<string, JsonSchema>\n required?: string[]\n minimum?: number\n maximum?: number\n minLength?: number\n maxLength?: number\n pattern?: string\n}\n\n/**\n * A JSON Schema pattern as the pattern attribute, or nothing. The attribute\n * matches the whole value and compiles with the v flag; a JSON Schema pattern\n * matches anywhere. So only an anchored one carries over, and only one the v\n * flag accepts - an attribute the browser cannot compile is ignored, and one\n * that meant \"contains\" would refuse what the server accepts.\n */\nfunction patternOf(pattern: string | undefined): string | undefined {\n if (!pattern?.startsWith('^') || !pattern.endsWith('$') || pattern.endsWith('\\\\$')) return undefined\n\n try {\n new RegExp(pattern, 'v')\n\n return pattern\n } catch {\n return undefined\n }\n}\n\n/** The constraints the browser can check for a field of this type. */\nfunction constraintsOf(type: FieldType, json: JsonSchema, integer: boolean): Partial<Field> {\n const out: Partial<Field> = {}\n\n if (type === 'number') {\n // zod's int() writes the safe-integer range as bounds: not a constraint anyone wrote.\n if (json.minimum !== undefined && json.minimum > Number.MIN_SAFE_INTEGER) out.min = json.minimum\n if (json.maximum !== undefined && json.maximum < Number.MAX_SAFE_INTEGER) out.max = json.maximum\n // Without step a number input refuses 12.5: 'any' unless the schema says integer.\n out.step = integer ? 1 : 'any'\n }\n\n if (type === 'text' || type === 'textarea' || type === 'email') {\n if (json.minLength !== undefined) out.minLength = json.minLength\n if (json.maxLength !== undefined) out.maxLength = json.maxLength\n }\n\n // Only plain text: an email's or a date's pattern is the library's own\n // spelling of the format, which type=\"email\" and type=\"date\" already check.\n if (type === 'text') {\n const pattern = patternOf(json.pattern)\n\n if (pattern) out.pattern = pattern\n }\n\n return out\n}\n\n/**\n * The schema as JSON Schema, if it can say: the Standard JSON Schema\n * interface, which zod (4.2+) implements and other libraries are adding. Of\n * the input - what the form is filled with - not the output it parses to.\n */\nfunction describe(schema: FormSchema): JsonSchema | null {\n const converter = (schema['~standard'] as { jsonSchema?: { input?: (options: { target: string }) => unknown } }).jsonSchema\n\n if (!converter?.input) return null\n\n try {\n return converter.input({ target: 'draft-2020-12' }) as JsonSchema\n } catch {\n return null\n }\n}\n\n/** The one type a property is, looking through a nullable union. */\nfunction typeOf(property: JsonSchema): JsonSchema {\n const options = property.anyOf ?? property.oneOf\n\n if (options) return options.find((option) => option.type !== 'null') ?? property\n\n return property\n}\n\n/**\n * The fields a schema describes: the type of each property, whether it is\n * required, an enum's options. From its JSON Schema when it has one; from\n * `overrides` alone when it does not, which must then list every field.\n */\nexport function fieldsOf(schema: FormSchema, overrides: Overrides = {}): Field[] {\n const json = describe(schema)\n\n if (!json?.properties) {\n if (Object.keys(overrides).length === 0) {\n throw new Error(\n `[admin] this ${schema['~standard'].vendor} schema does not describe its fields as JSON Schema; list them in fields: { name: { type: 'text' }, ... }`,\n )\n }\n\n return Object.entries(overrides).map(([name, override]) =>\n withSchemas({ name, label: humanize(name), type: 'text' as FieldType, required: true, ...applied(override) }, override),\n )\n }\n\n const required = new Set(json.required ?? [])\n\n return Object.entries(json.properties).map(([name, property]) => {\n const inner = typeOf(property)\n const types = ([] as string[]).concat(inner.type ?? [])\n const options = inner.enum?.filter((option) => option !== null).map((option) => optionOf(String(option)))\n // zod writes a nullable string as type: ['string', 'null'], and a nullable url as anyOf: [{ format: 'uri' }, { type: 'null' }].\n const nullable = ([] as string[]).concat(property.type ?? []).includes('null') || ((property.anyOf ?? property.oneOf)?.some((option) => option.type === 'null') ?? false)\n const type: FieldType =\n options ? 'select'\n : types.includes('boolean') ? 'toggle'\n : types.includes('number') || types.includes('integer') ? 'number'\n : inner.format === 'email' ? 'email'\n : inner.format === 'date' || inner.format === 'date-time' ? 'date'\n : 'text'\n\n const override = overrides[name]\n const numeric = types.includes('number') || types.includes('integer')\n const final = override?.relation ? 'select' : (override?.type ?? type)\n\n return withSchemas({\n name,\n // categoryId is a Category to whoever picks it.\n label: humanize(override?.relation ? name.replace(/_?[iI]d$/, '') : name),\n type,\n required: required.has(name) && !nullable,\n ...(nullable ? { nullable: true } : {}),\n ...(options ? { options } : {}),\n ...(numeric && final === 'select' ? { numeric: true } : {}),\n // Checked for the type the field ends up as: a string made a textarea keeps its lengths, loses its pattern.\n ...constraintsOf(final, inner, types.includes('integer')),\n ...applied(override),\n }, override)\n })\n}\n\n/**\n * A submitted form's values as the schema expects them, by its fields: a\n * resource's, a public form's, a block's. A checkbox that is off is not in\n * FormData at all, so a toggle is whether its name arrived; an empty optional\n * field is left out rather than sent as \"\".\n */\nexport function valuesFrom(target: { fields: Field[] }, form: FormData): Record<string, unknown> {\n return Object.fromEntries(\n target.fields.flatMap((field): [string, unknown][] => {\n if (field.type === 'toggle') return [[field.name, form.has(field.name)]]\n if (field.type === 'blocks') return [[field.name, blocksFrom(field, form)]]\n if (field.type === 'file' || field.type === 'image') return fileFrom(field, form)\n\n const value = form.get(field.name)\n\n if (value === null || (value === '' && !field.required)) return []\n\n // A number field arrives as text. Converted here, so the schema says\n // z.number() and not z.coerce.number() - the same schema an API would\n // use - and a field that is not a number stays text for it to refuse.\n if ((field.type === 'number' || field.numeric) && typeof value === 'string' && value.trim() !== '' && Number.isFinite(Number(value))) {\n return [[field.name, Number(value)]]\n }\n\n return [[field.name, value]]\n }),\n )\n}\n\n/**\n * A file field's value: the file chosen, else the value it already had\n * (sent back by the form beside the file input), else nothing - or null\n * when it was emptied and may be. A kept value is the browser's word, so\n * parse() takes it only if the record already had it.\n */\nfunction fileFrom(field: Field, form: FormData): [string, unknown][] {\n const sent = form.getAll(field.name)\n const chosen = sent.find((entry): entry is File => typeof entry !== 'string' && entry.size > 0)\n\n if (chosen) return [[field.name, chosen]]\n\n const kept = sent.find((entry) => typeof entry === 'string' && entry !== '')\n\n if (kept !== undefined) return [[field.name, kept]]\n\n return field.nullable && sent.length > 0 ? [[field.name, null]] : []\n}\n\n/** A blocks field's list, read from `name.0.type`, `name.0.heading`, ... until an index is missing. */\nfunction blocksFrom(field: Field, form: FormData): { type: string; data: Record<string, unknown> }[] {\n const out: { type: string; data: Record<string, unknown> }[] = []\n\n for (let index = 0; form.has(`${field.name}.${index}.type`); index++) {\n const prefix = `${field.name}.${index}.`\n const type = String(form.get(`${prefix}type`))\n const spec = field.blocks?.find((candidate) => candidate.name === type)\n\n if (!spec) {\n out.push({ type, data: {} })\n continue\n }\n\n const data = valuesFrom({ fields: spec.fields.map((inner) => ({ ...inner, name: prefix + inner.name })) }, form)\n\n out.push({ type, data: Object.fromEntries(Object.entries(data).map(([name, value]) => [name.slice(prefix.length), value])) })\n }\n\n return out\n}\n\n/**\n * Each block of each blocks field checked by its own schema, its data\n * replaced by what the schema parsed it to. A refusal is keyed\n * `name.index.field`, where the form shows it.\n */\nasync function checkBlocks(fields: Field[], values: Record<string, unknown>, apply: boolean): Promise<Record<string, string[]>> {\n const errors: Record<string, string[]> = {}\n\n for (const field of fields) {\n const schemas = schemasOf.get(field)\n const list = values[field.name]\n\n if (!schemas || !Array.isArray(list)) continue\n\n for (const [index, item] of (list as { type: string; data: Record<string, unknown> }[]).entries()) {\n const schema = schemas.get(item.type)\n\n if (!schema) {\n errors[`${field.name}.${index}`] = [`There is no ${item.type} block.`]\n continue\n }\n\n const result = await schema['~standard'].validate(item.data)\n\n if (!result.issues) {\n if (apply) item.data = result.value as Record<string, unknown>\n continue\n }\n\n for (const [path, messages] of Object.entries(issuesToErrors(result.issues))) {\n errors[`${field.name}.${index}${path ? `.${path}` : ''}`] = messages\n }\n }\n }\n\n return errors\n}\n\nexport type Parsed<Values> = { ok: true; values: Values } | { ok: false; errors: Record<string, string[]> }\n\n/** An upload waiting for the rest of the form to pass. */\ninterface Upload {\n path: string\n file: File\n storage: Storage\n keep: (value: string) => void\n}\n\n/**\n * Finds each file field's upload, here and inside blocks. A file the field\n * does not accept, or too large, is refused under it. A kept value the\n * record did not have is dropped: anyone can post a string, and it would\n * otherwise save a url of their choosing as though it had been uploaded.\n */\nfunction uploadsIn(fields: Field[], values: Record<string, unknown>, had: Record<string, unknown>[], at: string, uploads: Upload[], refused: Record<string, string[]>) {\n for (const field of fields) {\n const path = at + field.name\n const value = values[field.name]\n\n if (field.type === 'blocks') {\n const before = Array.isArray(had[0]?.[field.name]) ? (had[0]![field.name] as { type?: string; data?: Record<string, unknown> }[]) : []\n\n for (const [index, item] of (Array.isArray(value) ? (value as { type: string; data: Record<string, unknown> }[]) : []).entries()) {\n const spec = field.blocks?.find((candidate) => candidate.name === item.type)\n const same = before.filter((block) => block?.type === item.type && block.data).map((block) => block.data!)\n\n if (spec) uploadsIn(spec.fields, item.data, same, `${path}.${index}.`, uploads, refused)\n }\n\n continue\n }\n\n if (field.type !== 'file' && field.type !== 'image') continue\n\n if (typeof value === 'string' && !had.some((record) => record[field.name] === value)) {\n if (field.nullable) values[field.name] = null\n else delete values[field.name]\n }\n\n if (value instanceof Blob) {\n const file = value as File\n\n if (!accepts(field.accept, file)) refused[path] = [field.type === 'image' ? 'Choose a PNG, JPEG, WebP, GIF or AVIF image.' : 'That type of file is not accepted here.']\n else if (field.maxSize !== undefined && file.size > field.maxSize) refused[path] = [`Choose a file of ${sizeOf(field.maxSize)} or less.`]\n else uploads.push({ path, file, storage: storageOf.get(field)!, keep: (kept) => (values[field.name] = kept) })\n\n continue\n }\n\n if (field.required && (values[field.name] === undefined || values[field.name] === null)) refused[path] = ['Choose a file.']\n }\n}\n\n/** Blocks, then the whole form, each against its schema. */\nasync function check<Schema extends FormSchema>(target: { schema: Schema; fields: Field[] }, values: Record<string, unknown>, apply: boolean): Promise<Parsed<ValuesOf<Schema>>> {\n const blocks = await checkBlocks(target.fields, values, apply)\n const result = await target.schema['~standard'].validate(values)\n\n if (!result.issues && Object.keys(blocks).length === 0) return { ok: true, values: result.value as ValuesOf<Schema> }\n\n return { ok: false, errors: { ...(result.issues ? issuesToErrors(result.issues) : {}), ...blocks } }\n}\n\nexport interface ParseOptions {\n /** The record being edited, as it was: the values a file field may keep without uploading again. */\n current?: Record<string, unknown> | null\n /** Handed to storage with each upload. */\n tenant?: Tenant | null\n}\n\n/**\n * A submitted form checked against its schema, with each refusal under its\n * field. Uploads are kept last: the form is checked with them still files,\n * and only when nothing else is wrong is each one stored and the form\n * checked again with what storage answered - so a refused save leaves no\n * file behind.\n */\nexport async function parse<Schema extends FormSchema>(\n target: { schema: Schema; fields: Field[] },\n form: FormData,\n options: ParseOptions = {},\n): Promise<Parsed<ValuesOf<Schema>>> {\n const values = valuesFrom(target, form)\n const uploads: Upload[] = []\n const refused: Record<string, string[]> = {}\n\n uploadsIn(target.fields, values, options.current ? [options.current] : [], '', uploads, refused)\n\n if (uploads.length === 0 && Object.keys(refused).length === 0) return check(target, values, true)\n\n const waiting = new Set(uploads.map((upload) => upload.path))\n const first = await check(target, values, false)\n const others = first.ok ? {} : Object.fromEntries(Object.entries(first.errors).filter(([path]) => !waiting.has(path)))\n\n if (Object.keys(others).length > 0 || Object.keys(refused).length > 0) return { ok: false, errors: { ...others, ...refused } }\n\n for (const upload of uploads) upload.keep(await upload.storage.put(upload.file, { field: upload.path, tenant: options.tenant ?? null }))\n\n return check(target, values, true)\n}\n\nexport function humanize(key: string): string {\n const words = key.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ')\n\n return words.charAt(0).toUpperCase() + words.slice(1).toLowerCase()\n}\n\n/** What a bulk action's toast says: \"3 products published.\" with `done`, else \"Publish: 3 products.\". */\nexport function bulkToast(bulk: Pick<BulkAction, 'label' | 'done'>, count: number, noun: string): string {\n return bulk.done ? `${count} ${noun} ${bulk.done}.` : `${bulk.label}: ${count} ${noun}.`\n}\n"]}
|
|
1
|
+
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A resource, described once: what its table shows, what its form takes, and\n * where its rows come from. The panel renders every page from this - nothing\n * per resource is written as a page.\n *\n * The form is a schema, not a list of widgets: the same object validates the\n * save on the server and is read for the fields it has, so a field cannot be\n * on the form and missing from the check, or the other way round. Any\n * Standard Schema validates; one that also describes itself as JSON Schema\n * (Standard JSON Schema - zod 4.2+, and others as they add it) gives its\n * fields too, and any other says them in `fields`.\n */\nimport type { ComponentType, ReactNode } from 'react'\nimport { issuesToErrors } from '@rsc-kit/core/standardSchema'\nimport type { StandardSchemaV1 } from '@rsc-kit/core/standardSchema'\nimport { accepts, IMAGE_TYPES, sizeOf } from './uploads.js'\nimport { hostSchema, requireDrawable } from './forms/host.js'\nimport type { HostAction, HostFields } from './forms/host.js'\nimport { isBlueprint } from './forms/brand.js'\nimport { shown, slugify } from './conditions.js'\nimport type { Derivation, Read, Visibility } from './conditions.js'\nimport type { BlueprintParts } from './forms/brand.js'\nimport type { Storage } from './uploads.js'\n\n/** What the form validates with: any Standard Schema - zod, valibot, arktype, effect. */\nexport type FormSchema = StandardSchemaV1<any, Record<string, unknown>>\n\n/** What a schema's save hands the data source. */\nexport type ValuesOf<Schema extends FormSchema> = NonNullable<Schema['~standard']['types']>['output']\n\nexport type Format = 'text' | 'money' | 'badge' | 'boolean' | 'date' | 'relation'\n\nexport interface Column {\n key: string\n label: string\n format: Format\n sortable: boolean\n searchable: boolean\n /** Shown until the visitor hides it from the Columns menu. */\n visible: boolean\n /** Draws the cell instead of the format. Runs on the server, so it may be async and read anything. */\n cell?: (value: any, row: any) => ReactNode | Promise<ReactNode>\n /** For a relation column: the resource whose row the value names, shown by its title. */\n relation?: string\n}\n\n/** A column, chained: text('name').searchable().sortable(). */\nexport class ColumnBuilder {\n readonly #column: Column\n\n constructor(column: Column) {\n this.#column = column\n }\n\n sortable(): this {\n this.#column.sortable = true\n return this\n }\n\n searchable(): this {\n this.#column.searchable = true\n return this\n }\n\n label(label: string): this {\n this.#column.label = label\n return this\n }\n\n /** In the Columns menu, unticked until the visitor shows it. */\n hidden(): this {\n this.#column.visible = false\n return this\n }\n\n /** Draw the cell yourself: .cell((value, row) => <Avatar src={row.photo} />). */\n cell(render: NonNullable<Column['cell']>): this {\n this.#column.cell = render\n return this\n }\n\n build(): Column {\n return { ...this.#column }\n }\n}\n\n/** The column helpers, typed to the row so a misspelt key fails the typecheck. */\nexport interface Columns<Row> {\n text(key: keyof Row & string): ColumnBuilder\n money(key: keyof Row & string): ColumnBuilder\n badge(key: keyof Row & string): ColumnBuilder\n boolean(key: keyof Row & string): ColumnBuilder\n date(key: keyof Row & string): ColumnBuilder\n /**\n * A belongsTo: the value is another resource's key, shown as that row's\n * title - col.relation('categoryId', 'categories') shows \"Lighting\", not 3.\n */\n relation(key: keyof Row & string, resource: string): ColumnBuilder\n}\n\nfunction columns<Row>(): Columns<Row> {\n const make = (format: Format) => (key: string) =>\n new ColumnBuilder({ key, label: humanize(key), format, sortable: false, searchable: false, visible: true })\n\n return {\n text: make('text'),\n money: make('money'),\n badge: make('badge'),\n boolean: make('boolean'),\n date: make('date'),\n relation: (key, resource) =>\n new ColumnBuilder({ key, label: humanize(key.replace(/_?[iI]d$/, '')), format: 'relation', relation: resource, sortable: false, searchable: false, visible: true }),\n }\n}\n\nexport type FieldType = 'text' | 'textarea' | 'number' | 'select' | 'toggle' | 'email' | 'date' | 'blocks' | 'file' | 'image'\n\n/** A kind of block a blocks field offers, as the form needs it: plain data. */\nexport interface BlockSpec {\n name: string\n label: string\n fields: Field[]\n}\n\n/** One choice of a select: what is saved, and what the visitor reads. */\nexport interface Option {\n value: string\n label: string\n}\n\n/** What the form renders. Plain data: it crosses to the client component. */\nexport interface Field {\n name: string\n label: string\n type: FieldType\n required: boolean\n options?: Option[]\n /**\n * A belongsTo: the options are another resource's rows, its key saved and\n * its title shown, read when the form is drawn and scoped to the tenant.\n */\n relation?: string\n /** A select whose schema wants a number: its value is converted, as a number field's is. */\n numeric?: boolean\n /** Help under the control. */\n description?: string\n /**\n * What the browser checks before a submit, from the schema: the native\n * constraints, so a mistake is caught without a round trip and without the\n * schema in the browser. A courtesy only - the server validates every save.\n */\n min?: number\n max?: number\n minLength?: number\n maxLength?: number\n /** Whole-value, as the pattern attribute matches. */\n pattern?: string\n step?: number | 'any'\n /**\n * A blocks field's kinds of block. Its value is a list of { type, data },\n * each block's data checked by its own schema; in a form, block i's field\n * is named `name.i.field`, which is also where its errors are.\n */\n blocks?: BlockSpec[]\n /**\n * A file or image field's types, as the accept attribute says them:\n * `image/*`, `application/pdf`, `.csv`. Checked by the browser when the\n * file is chosen and by the server when it arrives.\n */\n accept?: string\n /** A file or image field's largest file, in bytes. */\n maxSize?: number\n /** The schema allows null: a file field that is emptied saves null, so the source clears it. */\n nullable?: boolean\n /**\n * Shown only while another field says so: { when: 'type', is: 'business' }.\n * Hidden, it is not sent, not checked by the browser, and dropped by the\n * server if a post sends it anyway. Required means required when shown.\n */\n visible?: Visibility\n /** Filled from another field while that is typed, until edited by hand: { field: 'title' } for a slug. */\n from?: Derivation\n}\n\nexport interface Filter {\n key: string\n label: string\n options: Option[]\n}\n\nexport interface ListQuery {\n q?: string\n sort?: string\n dir: 'asc' | 'desc'\n page: number\n /**\n * The cursor a source that pages by cursor answered as next or prev, from\n * the url's ?cursor=; absent for the first page. Such a source reads this\n * and not page.\n */\n cursor?: string\n /**\n * Null asks for every row: a resource whose table works in the browser\n * (mode: 'client') is loaded whole, and searched, sorted and paged there.\n */\n perPage: number | null\n /** Only the resource's declared filters, each with a value. */\n filters: Record<string, string>\n /** The columns marked searchable: what q is matched against. */\n searchable: string[]\n}\n\n/** Whoever's records these are, in a panel with tenants. */\nexport interface Tenant {\n id: string\n name: string\n /** A second line under the name in the switcher: a plan, a role. */\n description?: string\n /** An image url for the switcher. Without one the first letter is shown. */\n avatar?: string\n}\n\n/** What every call to a data source is told besides its arguments. */\nexport interface SourceContext {\n /** The tenant the visitor is working in, or null in a panel without tenants. */\n tenant: Tenant | null\n}\n\n/**\n * Where a resource's rows live: five functions, and nothing about how. A\n * database (Drizzle, Prisma, SQL), a REST or GraphQL API, a Laravel app through\n * rpc(), a file, memory - the panel calls these and never anything else.\n *\n * In a panel with tenants, a source answers only for context.tenant. find()\n * returning null for another tenant's record is what makes its url a 404.\n */\n/**\n * One page of a list. Counted, { rows, total }, and the table numbers its\n * pages. By cursor, { rows, next, prev } - Laravel's cursorPaginate, a keyset\n * query, an API that hands back a cursor - and the table pages with Previous\n * and Next, asking for the cursor it was given. null is no page that way.\n */\nexport type ListPage<Row> = { rows: Row[]; total: number } | { rows: Row[]; next: string | null; prev?: string | null; total?: number }\n\nexport interface DataSource<Row, Values> {\n list(query: ListQuery, context: SourceContext): Promise<ListPage<Row>>\n find(id: string, context: SourceContext): Promise<Row | null>\n create(values: Values, context: SourceContext): Promise<Row>\n update(id: string, values: Values, context: SourceContext): Promise<Row>\n delete(id: string, context: SourceContext): Promise<void>\n}\n\n/** A source for a resource whose saves are a backend's actions: it lists, finds and deletes. */\nexport type ReadSource<Row> = Pick<DataSource<Row, never>, 'list' | 'find' | 'delete'>\n\n/**\n * Where a table's search, filters, sort and paging happen.\n *\n * 'server' each change is a request; the source is asked for one page.\n * For a table that grows: orders, users, an API with millions.\n * 'client' the source is asked once for every row (perPage: null) and the\n * browser does the rest, with no request per keystroke. For a\n * table that stays small: categories, settings, team members.\n */\nexport type TableMode = 'server' | 'client'\n\n/**\n * Something done to the rows ticked in the table: publish, archive, export.\n * `run` is given the rows themselves, each found by the source as the\n * visitor - an id the browser sent that is not theirs never reaches it.\n */\nexport interface BulkAction<Row = any> {\n /** Identifies it in the call; 'delete' is the panel's own. */\n name: string\n label: string\n /** Asked about first, as a delete is. */\n destructive?: boolean\n /** What happened to the rows, for the toast: 'published' says \"3 products published.\", as Delete says \"deleted\". */\n done?: string\n /** Returns its own words for the toast, or leaves them to `done`. */\n run: (rows: Row[], context: SourceContext) => void | string | Promise<void | string>\n}\n\nexport interface Resource<Row = any, Schema extends FormSchema = FormSchema> {\n name: string\n label: string\n singular: string\n key: keyof Row & string\n title: keyof Row & string\n source: DataSource<Row, ValuesOf<Schema>>\n columns: Column[]\n filters: Filter[]\n schema: Schema\n /** The create form's fields. */\n fields: Field[]\n /** The edit form's: the update action's, for a resource saved by a backend. */\n editFields: Field[]\n editSchema: FormSchema\n /** The resources its fields name, known before the fields are: a backend's arrive with the server entry. */\n relations: string[]\n perPage: number\n mode: TableMode\n /** Listed and searched, never created, edited or deleted from the panel. */\n readOnly: boolean\n bulkActions: BulkAction<Row>[]\n /** The sidebar's icon: a lucide icon, or any component. */\n icon?: ComponentType<{ className?: string }>\n description?: string\n}\n\nexport interface ResourceDefinition<Row, Schema extends FormSchema> {\n /** The url segment: /admin/products. */\n name: string\n label?: string\n singular?: string\n /** The field that identifies a row in the url. */\n key: keyof Row & string\n /** The field an edit page's heading shows. */\n title: keyof Row & string\n source: DataSource<Row, ValuesOf<Schema>>\n table: (col: Columns<Row>) => ColumnBuilder[]\n /** Enum fields of the form, offered as a select above the table. */\n filters?: (keyof Row & string)[]\n /** Any Standard Schema of an object: z.object(), v.object(), type({...}). */\n form: Schema\n /**\n * What the schema cannot say: a long string is a textarea, a label, help.\n * The whole list, for a schema that does not describe itself as JSON\n * Schema - a field missing here is then missing from the form.\n */\n fields?: Partial<Record<keyof ValuesOf<Schema> & string, FieldOverride>>\n perPage?: number\n /** Where search, filters, sort and paging happen. 'server' unless said. */\n mode?: TableMode\n /** Listed and searched, never created, edited or deleted from the panel: a log, a feed, a plugin's own records. */\n readOnly?: boolean\n /** Offered when rows are ticked, beside the panel's own Delete. */\n bulkActions?: BulkAction<Row>[]\n /** The sidebar's icon: import { Package } from 'lucide-react'. */\n icon?: ComponentType<{ className?: string }>\n /** Under the page's heading. */\n description?: string\n}\n\n/**\n * A resource saved by the backend's own actions, with no schema in\n * TypeScript: the generated stubs for a Laravel FormRequest or a Go struct.\n * The create form is drawn from what `create` takes, the edit form from what\n * `update` takes, and every check is the backend's - its 422 lands under the\n * fields. `update` is called with the record's key after the values:\n * `update(UpdateProductRequest $request, string $product)`.\n */\nexport interface HostResourceDefinition<Row, Create extends HostAction, Update extends HostAction>\n extends Omit<ResourceDefinition<Row, FormSchema>, 'source' | 'form' | 'fields' | 'readOnly'> {\n /** Lists, finds and deletes; the saves are the actions. */\n source: ReadSource<Row>\n /** `import { productsStore, productsUpdate } from '../server-actions.generated'`. */\n actions: { create: Create; update: Update }\n /** Labels, controls and relations the backend's types cannot say. */\n fields?: Partial<Record<HostFields<Create> | HostFields<Update>, FieldOverride>>\n}\n\n/**\n * A resource whose form is a blueprint: the same fields, checks and saves a\n * page draws with `<product.Form />`. A blueprint that saves - a backend's\n * actions, or a schema's handle and update - needs only a source that lists,\n * finds and deletes; one that leaves saving to the resource needs a whole one.\n */\nexport interface BlueprintResourceDefinition<Row> extends Omit<ResourceDefinition<Row, FormSchema>, 'source' | 'form' | 'fields'> {\n source: ReadSource<Row> | DataSource<Row, Record<string, unknown>>\n /** import { product } from './blueprints/product', defined with defineBlueprint. */\n form: BlueprintParts\n}\n\n/**\n * Two calls so the row is named and the schema is inferred:\n * defineResource<Product>()({ ... }).\n */\nexport function defineResource<Row>() {\n function define<Schema extends FormSchema>(definition: ResourceDefinition<Row, Schema>): Resource<Row, Schema>\n function define<Create extends HostAction, Update extends HostAction>(definition: HostResourceDefinition<Row, Create, Update>): Resource<Row>\n function define(definition: BlueprintResourceDefinition<Row>): Resource<Row>\n function define(\n definition: (ResourceDefinition<Row, FormSchema> | HostResourceDefinition<Row, HostAction, HostAction> | BlueprintResourceDefinition<Row>) & {\n form?: FormSchema | BlueprintParts\n fields?: unknown\n actions?: { create?: unknown; update?: unknown }\n readOnly?: boolean\n },\n ): Resource<Row> {\n if (!/^[a-z0-9][a-z0-9-]*$/.test(definition.name)) {\n throw new Error(`[admin] a resource's name is its url segment, lowercase letters, digits and dashes: ${JSON.stringify(definition.name)}`)\n }\n\n const { actions } = definition\n const blueprint = isBlueprint(definition.form) ? definition.form : null\n\n if (blueprint && (actions !== undefined || definition.fields !== undefined)) {\n throw new Error(`[admin] ${definition.name}'s form is the blueprint ${blueprint.name}, so its fields and actions are said there, not here`)\n }\n\n if (!blueprint && (actions === undefined) === (definition.form === undefined)) {\n throw new Error(`[admin] ${definition.name} needs a form schema, or the backend's actions, and not both`)\n }\n\n if (actions !== undefined && (typeof actions.create !== 'function' || typeof actions.update !== 'function')) {\n throw new Error(\n `[admin] ${definition.name}'s actions are { create, update }, each the action the build generated: ` +\n \"import { productsStore, productsUpdate } from '../server-actions.generated'\",\n )\n }\n\n if (actions !== undefined && definition.readOnly) {\n throw new Error(`[admin] ${definition.name} is read-only, so it has no actions to save with; list it with a source alone and a form schema`)\n }\n\n const overrides = (definition.fields ?? {}) as Overrides\n const host = actions as { create: HostAction; update: HostAction } | undefined\n const schema = blueprint ? blueprint.schema : host ? hostSchema(host.create) : (definition.form as FormSchema)\n const editSchema = blueprint ? blueprint.editSchema : host ? hostSchema(host.update) : schema\n\n // A backend's types arrive with the server entry, which runs after this\n // module: its fields wait for the first page that needs them. A schema's\n // fail here, at startup.\n const drawn: { create?: Field[]; edit?: Field[]; filters?: Filter[] } = {}\n const createFields = (): Field[] => {\n if (blueprint) return blueprint.fields\n if (host) requireDrawable({ name: `[admin] the ${definition.name} resource's create`, key: false }, host.create, overrides)\n\n return (drawn.create ??= fieldsOf(schema, overrides))\n }\n const editFields = (): Field[] => {\n if (blueprint) return blueprint.editFields\n if (!host) return createFields()\n\n requireDrawable({ name: `[admin] the ${definition.name} resource's update`, key: true }, host.update, overrides)\n\n return (drawn.edit ??= fieldsOf(editSchema, overrides))\n }\n const filters = (): Filter[] =>\n (drawn.filters ??= (definition.filters ?? []).map((key) => {\n const field = createFields().find((candidate) => candidate.name === key)\n\n if (!field?.options) throw new Error(`[admin] ${definition.name}: the filter ${key} needs an enum field in the form`)\n\n return { key, label: field.label, options: field.options }\n }))\n\n // A blueprint's are checked when the panel first draws, as a backend's are.\n if (!host && !blueprint) filters()\n\n const relations = blueprint\n ? blueprint.relations\n : host\n ? Object.values(overrides).flatMap((override) => (override?.relation ? [override.relation] : []))\n : createFields().flatMap((field) => (field.relation ? [field.relation] : []))\n\n // The saves: the backend's actions, the blueprint's own, or the source's.\n const saves = blueprint\n ? blueprint.create && blueprint.update\n ? { create: blueprint.create, update: blueprint.update }\n : null\n : host\n ? { create: (values: Record<string, unknown>) => host.create(values as never), update: (values: Record<string, unknown>, id: string) => (host.update as (values: unknown, id: string) => Promise<unknown>)(values, id) }\n : null\n\n if (blueprint && !saves && !definition.readOnly && (typeof (definition.source as Partial<DataSource<Row, unknown>>).create !== 'function' || typeof (definition.source as Partial<DataSource<Row, unknown>>).update !== 'function')) {\n throw new Error(\n `[admin] ${definition.name}'s blueprint ${blueprint.name} does not both create and update, and its source does not either. ` +\n 'Give the blueprint both, or the source create and update, or make the resource readOnly.',\n )\n }\n\n const source: DataSource<Row, Record<string, unknown>> = saves\n ? {\n list: (query, context) => definition.source.list(query, context),\n find: (id, context) => definition.source.find(id, context),\n delete: (id, context) => definition.source.delete(id, context),\n create: async (values) => ((await saves.create(values)) ?? {}) as Row,\n update: async (id, values) => ((await saves.update(values, id)) ?? {}) as Row,\n }\n : (definition.source as DataSource<Row, Record<string, unknown>>)\n const bulkActions = definition.bulkActions ?? []\n const bulkNames = bulkActions.map((action) => action.name)\n\n if (bulkActions.length > 0 && definition.readOnly) {\n throw new Error(`[admin] ${definition.name} is read-only, so it has no bulk actions`)\n }\n\n for (const [index, name] of bulkNames.entries()) {\n if (name === 'delete') throw new Error(`[admin] ${definition.name}: 'delete' is the panel's own bulk action`)\n if (bulkNames.indexOf(name) !== index) throw new Error(`[admin] ${definition.name}: two bulk actions are named ${name}`)\n }\n\n return {\n name: definition.name,\n label: definition.label ?? humanize(definition.name),\n singular: definition.singular ?? humanize(definition.name).replace(/s$/, ''),\n key: definition.key,\n title: definition.title,\n source: source as Resource<Row>['source'],\n columns: definition.table(columns<Row>()).map((column) => column.build()),\n get filters() {\n return filters()\n },\n schema,\n get fields() {\n return createFields()\n },\n get editFields() {\n return editFields()\n },\n editSchema,\n relations,\n perPage: definition.perPage ?? 10,\n mode: definition.mode ?? 'server',\n readOnly: definition.readOnly ?? false,\n bulkActions,\n ...(definition.icon ? { icon: definition.icon } : {}),\n ...(definition.description ? { description: definition.description } : {}),\n }\n }\n\n return define\n}\n\n/** A field as the app says it, over what the schema said. */\nexport type FieldOverride = Partial<Pick<Field, 'type' | 'label' | 'description' | 'required' | 'relation' | 'visible' | 'from'>> & {\n /** A bare value is labelled by humanizing it: 'in_stock' reads \"In stock\". */\n options?: (string | Option)[]\n /** The kinds a blocks field offers: { type: 'blocks', blocks: [hero, features] }. */\n blocks?: BlockLike[]\n /** Where a file or image field keeps what is uploaded. Required for one. */\n storage?: Storage\n /** A file or image field's types. An image field takes PNG, JPEG, WebP, GIF and AVIF unless it says. */\n accept?: string\n /** A file or image field's largest file, in bytes. */\n maxSize?: number\n}\n\n/** What a blocks field needs of a block: its name, label, fields and schema. */\ninterface BlockLike {\n name: string\n label: string\n fields: Field[]\n schema: FormSchema\n}\n\n/**\n * A blocks field's schemas, by field. Kept beside the field rather than on\n * it, because a field crosses to the browser and a schema cannot.\n */\nconst schemasOf = new WeakMap<Field, Map<string, FormSchema>>()\n\n/** A file field's storage, kept beside it for the same reason. */\nconst storageOf = new WeakMap<Field, Storage>()\n\n/** A bare option labelled for reading, or one the app labelled itself. */\nfunction optionOf(option: string | Option): Option {\n return typeof option === 'string' ? { value: option, label: humanize(option) } : option\n}\n\n/** An override as a field: options made whole, and a relation drawn as a select. */\nfunction applied(override: FieldOverride | undefined): Partial<Field> {\n if (!override) return {}\n\n // storage never goes on the field: a field crosses to the browser.\n const { options, blocks, storage: _, ...rest } = override\n\n return {\n ...(override.relation ? { type: 'select' as FieldType } : {}),\n ...rest,\n ...(options ? { options: options.map(optionOf) } : {}),\n ...(blocks ? { type: 'blocks' as FieldType, blocks: blocks.map(({ name, label, fields }) => ({ name, label, fields })) } : {}),\n }\n}\n\n/** Keeps a blocks field's schemas, and a file field's storage, where parse() finds them. */\nfunction withSchemas(field: Field, override: FieldOverride | undefined): Field {\n if (field.type === 'file' || field.type === 'image') {\n if (!override?.storage) throw new Error(`[press] the ${field.type} field ${field.name} needs storage: where an upload is kept`)\n\n storageOf.set(field, override.storage)\n if (field.type === 'image' && field.accept === undefined) field.accept = IMAGE_TYPES\n\n return field\n }\n\n if (field.type !== 'blocks') return field\n\n if (!override?.blocks?.length) throw new Error(`[press] the blocks field ${field.name} needs blocks: [...] - the kinds it offers`)\n\n schemasOf.set(field, new Map(override.blocks.map((block) => [block.name, block.schema])))\n\n return field\n}\n\ntype Overrides = Record<string, FieldOverride | undefined>\n\ntype JsonSchema = {\n type?: string | string[]\n format?: string\n enum?: unknown[]\n const?: unknown\n anyOf?: JsonSchema[]\n oneOf?: JsonSchema[]\n properties?: Record<string, JsonSchema>\n required?: string[]\n minimum?: number\n maximum?: number\n minLength?: number\n maxLength?: number\n pattern?: string\n /** A backend's conditional rule, as its host writes it: Laravel's required_if, Go's required_if tag. */\n 'x-visible'?: Visibility & { required?: boolean }\n}\n\n/**\n * A JSON Schema pattern as the pattern attribute, or nothing. The attribute\n * matches the whole value and compiles with the v flag; a JSON Schema pattern\n * matches anywhere. So only an anchored one carries over, and only one the v\n * flag accepts - an attribute the browser cannot compile is ignored, and one\n * that meant \"contains\" would refuse what the server accepts.\n */\nfunction patternOf(pattern: string | undefined): string | undefined {\n if (!pattern?.startsWith('^') || !pattern.endsWith('$') || pattern.endsWith('\\\\$')) return undefined\n\n try {\n new RegExp(pattern, 'v')\n\n return pattern\n } catch {\n return undefined\n }\n}\n\n/** The constraints the browser can check for a field of this type. */\nfunction constraintsOf(type: FieldType, json: JsonSchema, integer: boolean): Partial<Field> {\n const out: Partial<Field> = {}\n\n if (type === 'number') {\n // zod's int() writes the safe-integer range as bounds: not a constraint anyone wrote.\n if (json.minimum !== undefined && json.minimum > Number.MIN_SAFE_INTEGER) out.min = json.minimum\n if (json.maximum !== undefined && json.maximum < Number.MAX_SAFE_INTEGER) out.max = json.maximum\n // Without step a number input refuses 12.5: 'any' unless the schema says integer.\n out.step = integer ? 1 : 'any'\n }\n\n if (type === 'text' || type === 'textarea' || type === 'email') {\n if (json.minLength !== undefined) out.minLength = json.minLength\n if (json.maxLength !== undefined) out.maxLength = json.maxLength\n }\n\n // Only plain text: an email's or a date's pattern is the library's own\n // spelling of the format, which type=\"email\" and type=\"date\" already check.\n if (type === 'text') {\n const pattern = patternOf(json.pattern)\n\n if (pattern) out.pattern = pattern\n }\n\n return out\n}\n\n/**\n * The schema as JSON Schema, if it can say: the Standard JSON Schema\n * interface, which zod (4.2+) implements and other libraries are adding. Of\n * the input - what the form is filled with - not the output it parses to.\n */\nfunction describe(schema: FormSchema): JsonSchema | null {\n const converter = (schema['~standard'] as { jsonSchema?: { input?: (options: { target: string }) => unknown } }).jsonSchema\n\n if (!converter?.input) return null\n\n try {\n return converter.input({ target: 'draft-2020-12' }) as JsonSchema\n } catch {\n return null\n }\n}\n\n/** The one type a property is, looking through a nullable union. */\nfunction typeOf(property: JsonSchema): JsonSchema {\n const options = property.anyOf ?? property.oneOf\n\n if (options) return options.find((option) => option.type !== 'null') ?? property\n\n return property\n}\n\n/**\n * The fields a schema describes: the type of each property, whether it is\n * required, an enum's options. From its JSON Schema when it has one; from\n * `overrides` alone when it does not, which must then list every field.\n */\nexport function fieldsOf(schema: FormSchema, overrides: Overrides = {}): Field[] {\n const json = describe(schema)\n\n if (!json?.properties) {\n if (Object.keys(overrides).length === 0) {\n throw new Error(\n `[admin] this ${schema['~standard'].vendor} schema does not describe its fields as JSON Schema; list them in fields: { name: { type: 'text' }, ... }`,\n )\n }\n\n return connected(Object.entries(overrides).map(([name, override]) =>\n withSchemas({ name, label: humanize(name), type: 'text' as FieldType, required: true, ...applied(override) }, override),\n ))\n }\n\n const required = new Set(json.required ?? [])\n\n return connected(Object.entries(json.properties).map(([name, property]) => {\n const inner = typeOf(property)\n const types = ([] as string[]).concat(inner.type ?? [])\n const options = inner.enum?.filter((option) => option !== null).map((option) => optionOf(String(option)))\n // zod writes a nullable string as type: ['string', 'null'], and a nullable url as anyOf: [{ format: 'uri' }, { type: 'null' }].\n const nullable = ([] as string[]).concat(property.type ?? []).includes('null') || ((property.anyOf ?? property.oneOf)?.some((option) => option.type === 'null') ?? false)\n const type: FieldType =\n options ? 'select'\n : types.includes('boolean') ? 'toggle'\n : types.includes('number') || types.includes('integer') ? 'number'\n : inner.format === 'email' ? 'email'\n : inner.format === 'date' || inner.format === 'date-time' ? 'date'\n : 'text'\n\n const override = overrides[name]\n const numeric = types.includes('number') || types.includes('integer')\n // required_if:type,business: shown for a business, and required then.\n const conditional = property['x-visible']\n const final = override?.relation ? 'select' : (override?.type ?? type)\n\n return withSchemas({\n name,\n // categoryId is a Category to whoever picks it.\n label: humanize(override?.relation ? name.replace(/_?[iI]d$/, '') : name),\n type,\n required: (required.has(name) || conditional?.required === true) && !nullable,\n ...(conditional ? { visible: { when: conditional.when, ...(conditional.is !== undefined ? { is: conditional.is } : {}), ...(conditional.not !== undefined ? { not: conditional.not } : {}) } } : {}),\n ...(nullable ? { nullable: true } : {}),\n ...(options ? { options } : {}),\n ...(numeric && final === 'select' ? { numeric: true } : {}),\n // Checked for the type the field ends up as: a string made a textarea keeps its lengths, loses its pattern.\n ...constraintsOf(final, inner, types.includes('integer')),\n ...applied(override),\n }, override)\n }))\n}\n\n/** A field that depends on another names one the form has, or the form fails at startup rather than never showing it. */\nfunction connected(fields: Field[]): Field[] {\n const names = new Set(fields.map((field) => field.name))\n\n for (const field of fields) {\n if (field.visible && (field.visible.when === field.name || !names.has(field.visible.when))) {\n throw new Error(`[press] the field ${field.name} is shown when ${field.visible.when}, and the form has no other field of that name`)\n }\n\n if (field.from && (field.from.field === field.name || !names.has(field.from.field))) {\n throw new Error(`[press] the field ${field.name} is made from ${field.from.field}, and the form has no other field of that name`)\n }\n }\n\n return fields\n}\n\n/**\n * A submitted form's values as the schema expects them, by its fields: a\n * resource's, a public form's, a block's. A checkbox that is off is not in\n * FormData at all, so a toggle is whether its name arrived; an empty optional\n * field is left out rather than sent as \"\".\n */\nexport function valuesFrom(target: { fields: Field[] }, form: FormData): Record<string, unknown> {\n const types = new Map(target.fields.map((field) => [field.name, field.type]))\n const read: Read = (name) => (types.get(name) === 'toggle' ? form.has(name) : String(form.get(name) ?? ''))\n\n // A slug left empty is made here as the browser would have: with\n // JavaScript off, or posted by something that is not a browser.\n const made = new Map<string, string>()\n\n for (const field of target.fields) {\n if (field.from && String(form.get(field.name) ?? '') === '') {\n const slug = slugify(String(read(field.from.field)))\n\n if (slug !== '') made.set(field.name, slug)\n }\n }\n\n return Object.fromEntries(\n target.fields.flatMap((field): [string, unknown][] => {\n // Hidden by its condition: what a post sent for it anyway is dropped,\n // as the browser would not have sent it.\n if (field.visible && !shown(field.visible, read)) return []\n\n if (field.type === 'toggle') return [[field.name, form.has(field.name)]]\n if (field.type === 'blocks') return [[field.name, blocksFrom(field, form)]]\n if (field.type === 'file' || field.type === 'image') return fileFrom(field, form)\n\n const value = made.get(field.name) ?? form.get(field.name)\n\n if (value === null || (value === '' && !field.required)) return []\n\n // A number field arrives as text. Converted here, so the schema says\n // z.number() and not z.coerce.number() - the same schema an API would\n // use - and a field that is not a number stays text for it to refuse.\n if ((field.type === 'number' || field.numeric) && typeof value === 'string' && value.trim() !== '' && Number.isFinite(Number(value))) {\n return [[field.name, Number(value)]]\n }\n\n return [[field.name, value]]\n }),\n )\n}\n\n/**\n * A file field's value: the file chosen, else the value it already had\n * (sent back by the form beside the file input), else nothing - or null\n * when it was emptied and may be. A kept value is the browser's word, so\n * parse() takes it only if the record already had it.\n */\nfunction fileFrom(field: Field, form: FormData): [string, unknown][] {\n const sent = form.getAll(field.name)\n const chosen = sent.find((entry): entry is File => typeof entry !== 'string' && entry.size > 0)\n\n if (chosen) return [[field.name, chosen]]\n\n const kept = sent.find((entry) => typeof entry === 'string' && entry !== '')\n\n if (kept !== undefined) return [[field.name, kept]]\n\n return field.nullable && sent.length > 0 ? [[field.name, null]] : []\n}\n\n/** A blocks field's list, read from `name.0.type`, `name.0.heading`, ... until an index is missing. */\nfunction blocksFrom(field: Field, form: FormData): { type: string; data: Record<string, unknown> }[] {\n const out: { type: string; data: Record<string, unknown> }[] = []\n\n for (let index = 0; form.has(`${field.name}.${index}.type`); index++) {\n const prefix = `${field.name}.${index}.`\n const type = String(form.get(`${prefix}type`))\n const spec = field.blocks?.find((candidate) => candidate.name === type)\n\n if (!spec) {\n out.push({ type, data: {} })\n continue\n }\n\n const data = valuesFrom(\n {\n fields: spec.fields.map((inner) => ({\n ...inner,\n name: prefix + inner.name,\n ...(inner.visible ? { visible: { ...inner.visible, when: prefix + inner.visible.when } } : {}),\n ...(inner.from ? { from: { ...inner.from, field: prefix + inner.from.field } } : {}),\n })),\n },\n form,\n )\n\n out.push({ type, data: Object.fromEntries(Object.entries(data).map(([name, value]) => [name.slice(prefix.length), value])) })\n }\n\n return out\n}\n\n/**\n * Each block of each blocks field checked by its own schema, its data\n * replaced by what the schema parsed it to. A refusal is keyed\n * `name.index.field`, where the form shows it.\n */\nasync function checkBlocks(fields: Field[], values: Record<string, unknown>, apply: boolean): Promise<Record<string, string[]>> {\n const errors: Record<string, string[]> = {}\n\n for (const field of fields) {\n const schemas = schemasOf.get(field)\n const list = values[field.name]\n\n if (!schemas || !Array.isArray(list)) continue\n\n for (const [index, item] of (list as { type: string; data: Record<string, unknown> }[]).entries()) {\n const schema = schemas.get(item.type)\n\n if (!schema) {\n errors[`${field.name}.${index}`] = [`There is no ${item.type} block.`]\n continue\n }\n\n const result = await schema['~standard'].validate(item.data)\n\n if (!result.issues) {\n if (apply) item.data = result.value as Record<string, unknown>\n continue\n }\n\n for (const [path, messages] of Object.entries(issuesToErrors(result.issues))) {\n errors[`${field.name}.${index}${path ? `.${path}` : ''}`] = messages\n }\n }\n }\n\n return errors\n}\n\nexport type Parsed<Values> = { ok: true; values: Values } | { ok: false; errors: Record<string, string[]> }\n\n/** An upload waiting for the rest of the form to pass. */\ninterface Upload {\n path: string\n file: File\n storage: Storage\n keep: (value: string) => void\n}\n\n/**\n * Finds each file field's upload, here and inside blocks. A file the field\n * does not accept, or too large, is refused under it. A kept value the\n * record did not have is dropped: anyone can post a string, and it would\n * otherwise save a url of their choosing as though it had been uploaded.\n */\nfunction uploadsIn(fields: Field[], values: Record<string, unknown>, had: Record<string, unknown>[], at: string, uploads: Upload[], refused: Record<string, string[]>) {\n for (const field of fields) {\n const path = at + field.name\n const value = values[field.name]\n\n if (field.type === 'blocks') {\n const before = Array.isArray(had[0]?.[field.name]) ? (had[0]![field.name] as { type?: string; data?: Record<string, unknown> }[]) : []\n\n for (const [index, item] of (Array.isArray(value) ? (value as { type: string; data: Record<string, unknown> }[]) : []).entries()) {\n const spec = field.blocks?.find((candidate) => candidate.name === item.type)\n const same = before.filter((block) => block?.type === item.type && block.data).map((block) => block.data!)\n\n if (spec) uploadsIn(spec.fields, item.data, same, `${path}.${index}.`, uploads, refused)\n }\n\n continue\n }\n\n if (field.type !== 'file' && field.type !== 'image') continue\n\n if (typeof value === 'string' && !had.some((record) => record[field.name] === value)) {\n if (field.nullable) values[field.name] = null\n else delete values[field.name]\n }\n\n if (value instanceof Blob) {\n const file = value as File\n\n if (!accepts(field.accept, file)) refused[path] = [field.type === 'image' ? 'Choose a PNG, JPEG, WebP, GIF or AVIF image.' : 'That type of file is not accepted here.']\n else if (field.maxSize !== undefined && file.size > field.maxSize) refused[path] = [`Choose a file of ${sizeOf(field.maxSize)} or less.`]\n else uploads.push({ path, file, storage: storageOf.get(field)!, keep: (kept) => (values[field.name] = kept) })\n\n continue\n }\n\n if (field.required && (values[field.name] === undefined || values[field.name] === null)) refused[path] = ['Choose a file.']\n }\n}\n\n/** Blocks, then the whole form, each against its schema. */\nasync function check<Schema extends FormSchema>(target: { schema: Schema; fields: Field[] }, values: Record<string, unknown>, apply: boolean): Promise<Parsed<ValuesOf<Schema>>> {\n const blocks = await checkBlocks(target.fields, values, apply)\n const result = await target.schema['~standard'].validate(values)\n\n if (!result.issues && Object.keys(blocks).length === 0) return { ok: true, values: result.value as ValuesOf<Schema> }\n\n return { ok: false, errors: { ...(result.issues ? issuesToErrors(result.issues) : {}), ...blocks } }\n}\n\nexport interface ParseOptions {\n /** The record being edited, as it was: the values a file field may keep without uploading again. */\n current?: Record<string, unknown> | null\n /** Handed to storage with each upload. */\n tenant?: Tenant | null\n}\n\n/**\n * A submitted form checked against its schema, with each refusal under its\n * field. Uploads are kept last: the form is checked with them still files,\n * and only when nothing else is wrong is each one stored and the form\n * checked again with what storage answered - so a refused save leaves no\n * file behind.\n */\nexport async function parse<Schema extends FormSchema>(\n target: { schema: Schema; fields: Field[] },\n form: FormData,\n options: ParseOptions = {},\n): Promise<Parsed<ValuesOf<Schema>>> {\n const values = valuesFrom(target, form)\n const uploads: Upload[] = []\n const refused: Record<string, string[]> = {}\n\n uploadsIn(target.fields, values, options.current ? [options.current] : [], '', uploads, refused)\n\n if (uploads.length === 0 && Object.keys(refused).length === 0) return check(target, values, true)\n\n const waiting = new Set(uploads.map((upload) => upload.path))\n const first = await check(target, values, false)\n const others = first.ok ? {} : Object.fromEntries(Object.entries(first.errors).filter(([path]) => !waiting.has(path)))\n\n if (Object.keys(others).length > 0 || Object.keys(refused).length > 0) return { ok: false, errors: { ...others, ...refused } }\n\n for (const upload of uploads) upload.keep(await upload.storage.put(upload.file, { field: upload.path, tenant: options.tenant ?? null }))\n\n return check(target, values, true)\n}\n\nexport function humanize(key: string): string {\n const words = key.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ')\n\n return words.charAt(0).toUpperCase() + words.slice(1).toLowerCase()\n}\n\n/** What a bulk action's toast says: \"3 products published.\" with `done`, else \"Publish: 3 products.\". */\nexport function bulkToast(bulk: Pick<BulkAction, 'label' | 'done'>, count: number, noun: string): string {\n return bulk.done ? `${count} ${noun} ${bulk.done}.` : `${bulk.label}: ${count} ${noun}.`\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FormProps } from '../forms/theme.js';
|
|
2
|
+
/**
|
|
3
|
+
* A blueprint's form in a page, drawn with the same fields as the panel's:
|
|
4
|
+
* the browser checks each one first, and the server's refusals land under the
|
|
5
|
+
* same fields. Once sent it says so in its place - or the action redirects.
|
|
6
|
+
* No card around it: it sits in the app's own page, which decides that.
|
|
7
|
+
*/
|
|
8
|
+
export declare function BlueprintForm({ action, fields, values, submit, done, honeypot }: FormProps): import("react").JSX.Element;
|
|
@@ -5,22 +5,23 @@ import Form from '@rsc-kit/core/form';
|
|
|
5
5
|
import { CircleCheck } from 'lucide-react';
|
|
6
6
|
import { Button } from './components/ui/button.js';
|
|
7
7
|
import { FieldGroup } from './components/ui/field.js';
|
|
8
|
-
import { FieldList, useBrowserErrors } from './fields.js';
|
|
8
|
+
import { FieldList, useBrowserErrors, useConditions } from './fields.js';
|
|
9
9
|
/**
|
|
10
|
-
* A form
|
|
11
|
-
* browser checks each one first, and the server's refusals land under the
|
|
10
|
+
* A blueprint's form in a page, drawn with the same fields as the panel's:
|
|
11
|
+
* the browser checks each one first, and the server's refusals land under the
|
|
12
12
|
* same fields. Once sent it says so in its place - or the action redirects.
|
|
13
13
|
* No card around it: it sits in the app's own page, which decides that.
|
|
14
14
|
*/
|
|
15
|
-
export function
|
|
15
|
+
export function BlueprintForm({ action, fields, values, submit, done, honeypot }) {
|
|
16
16
|
const form = useRef(null);
|
|
17
17
|
const browser = useBrowserErrors(form);
|
|
18
|
+
const hidden = useConditions(form, fields, values);
|
|
18
19
|
return (_jsx(Form, { action: action, ref: form, children: ({ pending, error: server, formError, succeeded }) => {
|
|
19
20
|
if (succeeded && done !== undefined) {
|
|
20
21
|
return (_jsxs("p", { role: "status", className: "flex items-center gap-2 text-sm", children: [_jsx(CircleCheck, { className: "size-4 text-muted-foreground" }), done] }));
|
|
21
22
|
}
|
|
22
23
|
const error = (name) => browser[name] ?? server(name);
|
|
23
|
-
return (_jsxs(FieldGroup, { children: [_jsx(FieldList, { fields: fields, values:
|
|
24
|
+
return (_jsxs(FieldGroup, { children: [_jsx(FieldList, { fields: fields, values: values, error: error, hidden: hidden }), honeypot !== undefined && (_jsx("div", { "aria-hidden": "true", className: "absolute -left-[9999px] size-px overflow-hidden", children: _jsx("input", { type: "text", name: honeypot, tabIndex: -1, autoComplete: "off", defaultValue: "" }) })), formError && (_jsx("p", { role: "alert", className: "text-sm text-destructive", children: formError })), _jsx("div", { children: _jsx(Button, { type: "submit", disabled: pending, children: pending ? 'Sending…' : submit }) })] }));
|
|
24
25
|
} }));
|
|
25
26
|
}
|
|
26
|
-
//# sourceMappingURL=
|
|
27
|
+
//# sourceMappingURL=blueprint-form.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blueprint-form.js","sourceRoot":"","sources":["../../src/shadcn/blueprint-form.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAC9B,OAAO,IAAI,MAAM,oBAAoB,CAAA;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAExE;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAa;IACzF,MAAM,IAAI,GAAG,MAAM,CAAkB,IAAI,CAAC,CAAA;IAC1C,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAA;IACtC,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAElD,OAAO,CACL,KAAC,IAAI,IAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,YAC5B,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE;YACpD,IAAI,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACpC,OAAO,CACL,aAAG,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAC,iCAAiC,aAC1D,KAAC,WAAW,IAAC,SAAS,EAAC,8BAA8B,GAAG,EACvD,IAAI,IACH,CACL,CAAA;YACH,CAAC;YAED,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAA;YAE7D,OAAO,CACL,MAAC,UAAU,eACT,KAAC,SAAS,IAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAI,EAE1E,QAAQ,KAAK,SAAS,IAAI,CACzB,6BAAiB,MAAM,EAAC,SAAS,EAAC,iDAAiD,YACjF,gBAAO,IAAI,EAAC,MAAM,EAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,YAAY,EAAC,KAAK,EAAC,YAAY,EAAC,EAAE,GAAG,GAClF,CACP,EACA,SAAS,IAAI,CACZ,YAAG,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,0BAA0B,YACjD,SAAS,GACR,CACL,EACD,wBACE,KAAC,MAAM,IAAC,IAAI,EAAC,QAAQ,EAAC,QAAQ,EAAE,OAAO,YACpC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,GACvB,GACL,IACK,CACd,CAAA;QACH,CAAC,GACI,CACR,CAAA;AACH,CAAC","sourcesContent":["'use client'\n\nimport { useRef } from 'react'\nimport Form from '@rsc-kit/core/form'\nimport { CircleCheck } from 'lucide-react'\nimport type { FormProps } from '../forms/theme.js'\nimport { Button } from './components/ui/button.js'\nimport { FieldGroup } from './components/ui/field.js'\nimport { FieldList, useBrowserErrors, useConditions } from './fields.js'\n\n/**\n * A blueprint's form in a page, drawn with the same fields as the panel's:\n * the browser checks each one first, and the server's refusals land under the\n * same fields. Once sent it says so in its place - or the action redirects.\n * No card around it: it sits in the app's own page, which decides that.\n */\nexport function BlueprintForm({ action, fields, values, submit, done, honeypot }: FormProps) {\n const form = useRef<HTMLFormElement>(null)\n const browser = useBrowserErrors(form)\n const hidden = useConditions(form, fields, values)\n\n return (\n <Form action={action} ref={form}>\n {({ pending, error: server, formError, succeeded }) => {\n if (succeeded && done !== undefined) {\n return (\n <p role=\"status\" className=\"flex items-center gap-2 text-sm\">\n <CircleCheck className=\"size-4 text-muted-foreground\" />\n {done}\n </p>\n )\n }\n\n const error = (name: string) => browser[name] ?? server(name)\n\n return (\n <FieldGroup>\n <FieldList fields={fields} values={values} error={error} hidden={hidden} />\n {/* For bots: out of sight, out of the tab order, and not read out. */}\n {honeypot !== undefined && (\n <div aria-hidden=\"true\" className=\"absolute -left-[9999px] size-px overflow-hidden\">\n <input type=\"text\" name={honeypot} tabIndex={-1} autoComplete=\"off\" defaultValue=\"\" />\n </div>\n )}\n {formError && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n {formError}\n </p>\n )}\n <div>\n <Button type=\"submit\" disabled={pending}>\n {pending ? 'Sending…' : submit}\n </Button>\n </div>\n </FieldGroup>\n )\n }}\n </Form>\n )\n}\n"]}
|
package/dist/shadcn/fields.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
1
2
|
import type { Field as FieldSpec } from '../resource.js';
|
|
2
3
|
/**
|
|
3
4
|
* Each field as its shadcn control, labelled, with its message under it: the
|
|
@@ -5,11 +6,20 @@ import type { Field as FieldSpec } from '../resource.js';
|
|
|
5
6
|
* panel's forms and the public ones, so a field looks and checks the same in
|
|
6
7
|
* both.
|
|
7
8
|
*/
|
|
8
|
-
export declare function FieldList({ fields, values, error }: {
|
|
9
|
+
export declare function FieldList({ fields, values, error, hidden, }: {
|
|
9
10
|
fields: FieldSpec[];
|
|
10
11
|
values: Record<string, unknown>;
|
|
11
12
|
error: (name: string) => string | undefined;
|
|
13
|
+
/** The fields whose condition hides them now, from useConditions. */
|
|
14
|
+
hidden?: ReadonlySet<string>;
|
|
12
15
|
}): import("react").JSX.Element[];
|
|
16
|
+
/**
|
|
17
|
+
* The fields' conditions and slugs, applied while the visitor types: which
|
|
18
|
+
* fields are hidden now, read from the form's own controls - the form stays
|
|
19
|
+
* uncontrolled, so nothing typed is lost to a render. A slug follows its
|
|
20
|
+
* source until it is edited by hand. The server applies both again.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useConditions(form: RefObject<HTMLFormElement | null>, fields: FieldSpec[], values: Record<string, unknown>): ReadonlySet<string>;
|
|
13
23
|
/**
|
|
14
24
|
* The browser's own validation, shown the shadcn way. A submit fires
|
|
15
25
|
* 'invalid' at each control that fails its constraints; that is cancelled -
|