@uniform-ts/core 0.0.6 → 0.0.8

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.
@@ -0,0 +1,748 @@
1
+ import * as React$1 from 'react';
2
+ import { FieldValues, FieldPath, FieldPathValue, RefCallBack } from 'react-hook-form';
3
+ import * as z from 'zod/v4/core';
4
+
5
+ /**
6
+ * Extracts the element type of an array type.
7
+ * e.g. `ArrayItem<{ name: string }[]>` → `{ name: string }`
8
+ */
9
+ type ArrayItem<T> = T extends (infer U)[] ? U : never;
10
+ /**
11
+ * Recursively produces all valid `fields` prop keys for a given schema shape:
12
+ *
13
+ * - Scalar fields → just their key (e.g. `"name"`)
14
+ * - Object fields → their key + all dot-notated child paths
15
+ * (e.g. `"address"` | `"address.street"`)
16
+ * - Array fields → their key + the unprefixed keys of the item object, so
17
+ * you can target every row's sub-field uniformly
18
+ * (e.g. `"items"` | `"items.name"` | `"items.qty"`)
19
+ * Index-based paths like `"items.0.name"` are intentionally
20
+ * excluded — row count is dynamic at runtime.
21
+ *
22
+ * @example
23
+ * // Given { name: string; address: { street: string }; items: { qty: number }[] }
24
+ * // DeepKeys produces:
25
+ * // "name" | "address" | "address.street" | "items" | "items.qty"
26
+ */
27
+ type DeepKeys<T> = T extends object ? {
28
+ [K in keyof T & string]: T[K] extends unknown[] ? ArrayItem<T[K]> extends object ? K | `${K}.${DeepKeys<ArrayItem<T[K]>>}` : K : T[K] extends object ? K | `${K}.${DeepKeys<T[K]>}` : K;
29
+ }[keyof T & string] : never;
30
+ /**
31
+ * Resolves the value type at a dot-notated path within a type `T`.
32
+ * Array fields use the unprefixed child path (matching `DeepKeys` convention).
33
+ *
34
+ * @example
35
+ * // DeepFieldValue<{ name: string; items: { qty: number }[] }, 'items.qty'> → number
36
+ */
37
+ type DeepFieldValue<T, K extends string> = K extends keyof T ? T[K] : K extends `${infer Head}.${infer Tail}` ? Head extends keyof T ? T[Head] extends (infer Item)[] ? DeepFieldValue<NonNullable<Item>, Tail> : DeepFieldValue<NonNullable<T[Head]>, Tail> : unknown : unknown;
38
+ /**
39
+ * Resolves the values type that a `setCondition` predicate receives for a
40
+ * given field key `K` within form values type `TValues`.
41
+ *
42
+ * - **Array item fields** (e.g. `"tasks.note"`): the predicate receives the
43
+ * array item type (`{ title, priority, note }`), enabling row-local sibling
44
+ * conditions like `(row) => row.priority === 'high'`.
45
+ * - **All other fields**: the predicate receives the full form values type.
46
+ *
47
+ * @example
48
+ * // ConditionValues<{ name: string; tasks: { priority: string; note: string }[] }, 'tasks.note'>
49
+ * // → { priority: string; note: string }
50
+ *
51
+ * // ConditionValues<{ name: string; address: { street: string } }, 'address.street'>
52
+ * // → { name: string; address: { street: string } } (full form — not an array)
53
+ */
54
+ type ConditionValues<TValues, K extends string> = K extends `${infer Head}.${string}` ? Head extends keyof TValues ? TValues[Head] extends readonly (infer Item)[] ? Item : TValues : TValues : TValues;
55
+
56
+ /**
57
+ * A single option entry used in `select` / enum fields.
58
+ */
59
+ type SelectOption = {
60
+ /** Human-readable text displayed in the dropdown. */
61
+ label: string;
62
+ /** The underlying value submitted with the form. */
63
+ value: string | number;
64
+ };
65
+
66
+ /**
67
+ * A map of field type keys to React components used to render them.
68
+ * Built-in keys (`string`, `number`, `boolean`, `date`, `select`, `textarea`)
69
+ * are pre-typed. Additional custom keys can be added via the index signature
70
+ * and registered through `createAutoForm` or the `components` prop.
71
+ */
72
+ type ComponentRegistry = {
73
+ string?: React$1.ComponentType<FieldProps>;
74
+ number?: React$1.ComponentType<FieldProps>;
75
+ boolean?: React$1.ComponentType<FieldProps>;
76
+ date?: React$1.ComponentType<FieldProps>;
77
+ select?: React$1.ComponentType<FieldProps>;
78
+ textarea?: React$1.ComponentType<FieldProps>;
79
+ [key: string]: React$1.ComponentType<FieldProps> | undefined;
80
+ };
81
+ /**
82
+ * Props passed to the field wrapper component that surrounds every rendered
83
+ * field. Used to render the label, description, error message, and grid span.
84
+ */
85
+ interface FieldWrapperProps {
86
+ /** The field input component to wrap. */
87
+ children: React$1.ReactNode;
88
+ /** The fully resolved field configuration. */
89
+ field: FieldConfig;
90
+ /** Validation error message for the field. */
91
+ error?: string;
92
+ /** Grid column span override (takes precedence over `field.meta.span`). */
93
+ span?: number;
94
+ /**
95
+ * Zero-based render index of this field within its parent container
96
+ * (form root or section). Exposed as the `--field-index` CSS custom property.
97
+ */
98
+ index?: number;
99
+ /**
100
+ * Nesting depth of this field (0 = top-level, 1 = inside object, etc.).
101
+ * Exposed as the `--field-depth` CSS custom property.
102
+ */
103
+ depth?: number;
104
+ }
105
+
106
+ /**
107
+ * Props for a generic array action button. Compatible with standard design
108
+ * system button components — pass your own via the `arrayButton` layout slot
109
+ * and it will be used for all array buttons (add, remove, move, duplicate).
110
+ */
111
+ interface ArrayButtonProps {
112
+ onClick?: React$1.MouseEventHandler<HTMLButtonElement>;
113
+ disabled?: boolean;
114
+ /** Always `"button"` — prevents accidental form submission. */
115
+ type?: 'button' | 'submit' | 'reset';
116
+ /** Accessible label describing the specific action and target row. */
117
+ 'aria-label'?: string;
118
+ /** CSS class name forwarded from `classNames.arrayAdd` / `arrayRemove` / etc. */
119
+ className?: string;
120
+ children?: React$1.ReactNode;
121
+ }
122
+ /**
123
+ * Props for the collapse/expand toggle button on an array row.
124
+ * Extends `ArrayButtonProps` with collapse state so custom components
125
+ * can apply directional styling (e.g. rotating a chevron icon).
126
+ */
127
+ interface ArrayCollapseButtonProps extends ArrayButtonProps {
128
+ /** Whether the row is currently collapsed. */
129
+ isCollapsed: boolean;
130
+ }
131
+ /**
132
+ * Props for the component that wraps the entire array field body,
133
+ * controlling the relative position of the rows and the add button.
134
+ */
135
+ interface ArrayFieldLayoutProps {
136
+ /** The rendered list of array rows. */
137
+ rows: React$1.ReactNode;
138
+ /** The rendered "add row" button. */
139
+ addButton: React$1.ReactNode;
140
+ /** Total number of rows currently in the array. */
141
+ rowCount: number;
142
+ /** Whether adding another row is permitted (respects `maxItems`). */
143
+ canAdd: boolean;
144
+ }
145
+ /**
146
+ * Props passed to the component that renders a single row inside an array field,
147
+ * including the row's content and action buttons (move, duplicate, remove, collapse).
148
+ */
149
+ interface ArrayRowLayoutProps {
150
+ /** The rendered fields for this array item. */
151
+ children: React$1.ReactNode;
152
+ /** Action button nodes for the row. */
153
+ buttons: {
154
+ /** Button to move the row up, or `null` if already first. */
155
+ moveUp: React$1.ReactNode | null;
156
+ /** Button to move the row down, or `null` if already last. */
157
+ moveDown: React$1.ReactNode | null;
158
+ /** Button to duplicate the row, or `null` if at max items. */
159
+ duplicate: React$1.ReactNode | null;
160
+ /** Button to remove the row, or `null` when omitted via layout slot. */
161
+ remove: React$1.ReactNode | null;
162
+ /** Button to collapse/expand the row, or `null` if collapsing is disabled. */
163
+ collapse: React$1.ReactNode | null;
164
+ };
165
+ /** Zero-based index of this row within the array. */
166
+ index: number;
167
+ /** Total number of rows currently in the array. */
168
+ rowCount: number;
169
+ }
170
+ /** Props received by the `layout.formWrapper` component. */
171
+ interface FormWrapperProps {
172
+ children: React$1.ReactNode;
173
+ }
174
+ /** Props received by the `layout.sectionWrapper` component (and per-section `SectionConfig.component`). */
175
+ interface SectionWrapperProps {
176
+ children: React$1.ReactNode;
177
+ title: string;
178
+ className?: string;
179
+ }
180
+ /**
181
+ * Props received by the `layout.objectWrapper` component.
182
+ * Replaces the default `<fieldset>` / `<legend>` wrapper rendered around
183
+ * nested object fields.
184
+ */
185
+ interface ObjectWrapperProps {
186
+ /** The rendered child fields. */
187
+ children: React$1.ReactNode;
188
+ /** The field label (used as the legend / heading). May be undefined. */
189
+ label: string | undefined;
190
+ /** CSS class name forwarded from `classNames.objectFieldset`. */
191
+ className?: string;
192
+ /** CSS class name forwarded from `classNames.objectLegend` for the label element. */
193
+ labelClassName?: string;
194
+ }
195
+ /**
196
+ * Props received by the `layout.arrayWrapper` component.
197
+ * Replaces the default `<fieldset>` / `<legend>` wrapper rendered around
198
+ * array fields.
199
+ */
200
+ interface ArrayWrapperProps {
201
+ /** The rendered array field body (rows + add button). */
202
+ children: React$1.ReactNode;
203
+ /** The field label (used as the legend / heading). May be undefined. */
204
+ label: string | undefined;
205
+ /** CSS class name forwarded from `classNames.arrayFieldset`. */
206
+ className?: string;
207
+ /** CSS class name forwarded from `classNames.arrayLegend` for the label element. */
208
+ labelClassName?: string;
209
+ }
210
+ /** Props received by the `layout.submitButton` component. */
211
+ interface SubmitButtonProps {
212
+ isSubmitting: boolean;
213
+ label: string;
214
+ }
215
+ type OptionalSlotComponent<TProps> = React$1.ComponentType<TProps> | null;
216
+ /**
217
+ * Per-section styling overrides forwarded to the `sectionWrapper` component.
218
+ * Keys are section titles; values control how that section wrapper is styled.
219
+ */
220
+ type SectionConfig = {
221
+ /** CSS class name(s) applied to the section wrapper. */
222
+ className?: string;
223
+ /** Replaces the section wrapper component entirely for this section. */
224
+ component?: React$1.ComponentType<SectionWrapperProps>;
225
+ };
226
+ /**
227
+ * Button component overrides for array fields. Set `base` to swap in your
228
+ * design system's button everywhere; use the specific keys to override
229
+ * individual actions on top of that. All keys fall back to `base`, which
230
+ * itself falls back to a plain `<button>`.
231
+ */
232
+ type ArrayButtonSlots = {
233
+ /** Used for every array button that has no specific override. Set to `null` to omit all unspecified buttons. */
234
+ base?: OptionalSlotComponent<ArrayButtonProps>;
235
+ /** Override for the add-row button only. Set to `null` to omit. */
236
+ add?: OptionalSlotComponent<ArrayButtonProps>;
237
+ /** Override for the remove-row button only. Set to `null` to omit. */
238
+ remove?: OptionalSlotComponent<ArrayButtonProps>;
239
+ /** Override for the move-up button only. Set to `null` to omit. */
240
+ moveUp?: OptionalSlotComponent<ArrayButtonProps>;
241
+ /** Override for the move-down button only. Set to `null` to omit. */
242
+ moveDown?: OptionalSlotComponent<ArrayButtonProps>;
243
+ /** Override for the duplicate-row button only. Set to `null` to omit. */
244
+ duplicate?: OptionalSlotComponent<ArrayButtonProps>;
245
+ /**
246
+ * Override for the collapse/expand toggle button only.
247
+ * Receives `isCollapsed` in addition to standard `ArrayButtonProps`.
248
+ */
249
+ collapse?: OptionalSlotComponent<ArrayCollapseButtonProps>;
250
+ };
251
+ /**
252
+ * Resolved button slots where every entry is guaranteed to be defined.
253
+ */
254
+ type ResolvedArrayButtonSlots = {
255
+ base: OptionalSlotComponent<ArrayButtonProps>;
256
+ add: OptionalSlotComponent<ArrayButtonProps>;
257
+ remove: OptionalSlotComponent<ArrayButtonProps>;
258
+ moveUp: OptionalSlotComponent<ArrayButtonProps>;
259
+ moveDown: OptionalSlotComponent<ArrayButtonProps>;
260
+ duplicate: OptionalSlotComponent<ArrayButtonProps>;
261
+ collapse: OptionalSlotComponent<ArrayCollapseButtonProps>;
262
+ };
263
+ /**
264
+ * Optional layout slot overrides for top-level structural components of the
265
+ * form. Provide only the slots you want to replace; omitted slots fall back
266
+ * to the built-in defaults.
267
+ */
268
+ type LayoutSlots = {
269
+ /** Wrapper rendered around the entire form. */
270
+ formWrapper?: React$1.ComponentType<FormWrapperProps>;
271
+ /** Wrapper rendered around each named field section. */
272
+ sectionWrapper?: React$1.ComponentType<SectionWrapperProps>;
273
+ /** Custom submit button component. Set to `null` to omit rendering it. */
274
+ submitButton?: OptionalSlotComponent<SubmitButtonProps>;
275
+ /** Custom layout component for individual rows in array fields. */
276
+ arrayRowLayout?: React$1.ComponentType<ArrayRowLayoutProps>;
277
+ /**
278
+ * Controls the layout of the entire array field body — position the rows
279
+ * and add button relative to each other (e.g. add button above rows).
280
+ */
281
+ arrayFieldLayout?: React$1.ComponentType<ArrayFieldLayoutProps>;
282
+ /**
283
+ * Replaces the `<fieldset>` / `<legend>` wrapper rendered around nested
284
+ * object fields. Receives `label`, `className`, and `labelClassName` props.
285
+ */
286
+ objectWrapper?: React$1.ComponentType<ObjectWrapperProps>;
287
+ /**
288
+ * Replaces the `<fieldset>` / `<legend>` wrapper rendered around array
289
+ * fields. Receives `label`, `className`, and `labelClassName` props.
290
+ */
291
+ arrayWrapper?: React$1.ComponentType<ArrayWrapperProps>;
292
+ /**
293
+ * Button component overrides for array fields. Set `base` to use your
294
+ * design system's button everywhere; use specific keys to override
295
+ * individual actions.
296
+ */
297
+ arrayButtons?: ArrayButtonSlots;
298
+ /**
299
+ * Content rendered while async `defaultValues` are loading.
300
+ * Defaults to a simple `<p>Loading…</p>` when not provided.
301
+ */
302
+ loadingFallback?: React$1.ReactNode;
303
+ /**
304
+ * Per-section config keyed by section title.
305
+ * Forwarded to the `sectionWrapper` component as a `className` prop.
306
+ */
307
+ sections?: Record<string, SectionConfig>;
308
+ };
309
+ /**
310
+ * The resolved version of `LayoutSlots` used internally, where all slots are
311
+ * guaranteed to be defined (falling back to built-in defaults).
312
+ */
313
+ type ResolvedLayoutSlots = {
314
+ formWrapper: React$1.ComponentType<FormWrapperProps>;
315
+ sectionWrapper: React$1.ComponentType<SectionWrapperProps>;
316
+ submitButton: OptionalSlotComponent<SubmitButtonProps>;
317
+ arrayRowLayout: React$1.ComponentType<ArrayRowLayoutProps>;
318
+ arrayFieldLayout: React$1.ComponentType<ArrayFieldLayoutProps>;
319
+ objectWrapper: React$1.ComponentType<ObjectWrapperProps>;
320
+ arrayWrapper: React$1.ComponentType<ArrayWrapperProps>;
321
+ arrayButtons: ResolvedArrayButtonSlots;
322
+ loadingFallback: React$1.ReactNode;
323
+ };
324
+ /**
325
+ * CSS class name overrides for the various structural elements of the form.
326
+ * Only the keys you provide will be applied; omitted keys use the built-in
327
+ * default class names (or none, if the default components don't apply any).
328
+ */
329
+ type FormClassNames = {
330
+ /** Class applied to the `<form>` element. */
331
+ form?: string;
332
+ /** Class applied to each field wrapper. */
333
+ fieldWrapper?: string;
334
+ /** Class applied to each field label. */
335
+ label?: string;
336
+ /** Class applied to each field description. */
337
+ description?: string;
338
+ /** Class applied to each field error message. */
339
+ error?: string;
340
+ /** Class applied to the "add item" button in array fields. */
341
+ arrayAdd?: string;
342
+ /** Class applied to the "remove item" button in array fields. */
343
+ arrayRemove?: string;
344
+ /** Class applied to the "move item" buttons in array fields. */
345
+ arrayMove?: string;
346
+ /** Class applied to the "duplicate item" button in array fields. */
347
+ arrayDuplicate?: string;
348
+ /** Class applied to the "collapse item" button in array fields. */
349
+ arrayCollapse?: string;
350
+ /** Class applied to the `<fieldset>` wrapper around nested object fields. */
351
+ objectFieldset?: string;
352
+ /** Class applied to the `<legend>` label inside nested object fields. */
353
+ objectLegend?: string;
354
+ /** Class applied to the `<fieldset>` wrapper around array fields. */
355
+ arrayFieldset?: string;
356
+ /** Class applied to the `<legend>` label inside array fields. */
357
+ arrayLegend?: string;
358
+ };
359
+
360
+ /**
361
+ * All programmatic form control methods, shared by both the field `onChange`
362
+ * callback and the imperative ref handle.
363
+ *
364
+ * @template TValues - The inferred shape of the form values.
365
+ */
366
+ type FormMethods<TValues extends FieldValues = FieldValues> = {
367
+ /** Set a single field value programmatically */
368
+ setValue: <K extends FieldPath<TValues>>(name: K, value: FieldPathValue<TValues, K>) => void;
369
+ /** Set multiple field values at once */
370
+ setValues: (values: Partial<TValues>) => void;
371
+ /** Get the current form values */
372
+ getValues: () => TValues;
373
+ /** Reset a single field to its default value */
374
+ resetField: (name: FieldPath<TValues>) => void;
375
+ /** Reset the entire form, optionally to new values */
376
+ reset: (values?: Partial<TValues>) => void;
377
+ /** Set a validation error on a specific field */
378
+ setError: (name: FieldPath<TValues>, message: string) => void;
379
+ /** Set validation errors on multiple fields at once */
380
+ setErrors: (errors: Partial<Record<FieldPath<TValues>, string>>) => void;
381
+ /** Clear validation errors (all fields, or specific ones) */
382
+ clearErrors: (names?: FieldPath<TValues> | FieldPath<TValues>[]) => void;
383
+ /** Programmatically trigger form submission */
384
+ submit: () => void;
385
+ /** Focus a specific field by name (dot-notated for nested fields) */
386
+ focus: (fieldName: FieldPath<TValues>) => void;
387
+ /** Watch field values reactively */
388
+ watch: {
389
+ (): TValues;
390
+ <K extends FieldPath<TValues>>(name: K): FieldPathValue<TValues, K>;
391
+ };
392
+ };
393
+ type FormLabels = {
394
+ /** Submit button text — default: "Submit" */
395
+ submit?: string;
396
+ /** Array "Add item" button — default: "Add" */
397
+ arrayAdd?: string;
398
+ /** Array "Remove row" button — default: "Remove" */
399
+ arrayRemove?: string;
400
+ /** Array "Move row up" button — default: "↑" */
401
+ arrayMoveUp?: string;
402
+ /** Array "Move row down" button — default: "↓" */
403
+ arrayMoveDown?: string;
404
+ /** Array "Duplicate row" button — default: "Duplicate" */
405
+ arrayDuplicate?: string;
406
+ /** Array row toggle shown when the row is expanded (clicking collapses it) — default: "▼" */
407
+ arrayCollapse?: string;
408
+ /** Array row toggle shown when the row is collapsed (clicking expands it) — default: "▶" */
409
+ arrayExpand?: string;
410
+ /** Collapsed row summary fallback — default: (i) => `Item ${i + 1}` */
411
+ arrayItemSummary?: (index: number) => string;
412
+ /** Aria label for the expand toggle — default: (i) => `Expand item ${i + 1}` */
413
+ arrayAriaExpand?: (index: number) => string;
414
+ /** Aria label for the collapse toggle — default: (i) => `Collapse item ${i + 1}` */
415
+ arrayAriaCollapse?: (index: number) => string;
416
+ /** Aria label for the move-up button — default: (i) => `Move item ${i + 1} up` */
417
+ arrayAriaMoveUp?: (index: number) => string;
418
+ /** Aria label for the move-down button — default: (i) => `Move item ${i + 1} down` */
419
+ arrayAriaMoveDown?: (index: number) => string;
420
+ /** Aria label for the duplicate button — default: (i) => `Duplicate item ${i + 1}` */
421
+ arrayAriaDuplicate?: (index: number) => string;
422
+ /** Aria label for the remove button — default: (i) => `Remove item ${i + 1}` */
423
+ arrayAriaRemove?: (index: number) => string;
424
+ };
425
+ /**
426
+ * A map of field names to coercion functions. Each function receives the raw
427
+ * field value and returns the coerced value before Zod validation is applied.
428
+ * Useful for transforming string inputs (e.g. from native `<input>`) into the
429
+ * types expected by the schema (e.g. numbers, dates).
430
+ */
431
+ type CoercionMap = Record<string, (value: unknown) => unknown>;
432
+ /**
433
+ * Custom validation error message overrides. Use `required` to override the
434
+ * global "required field" message, or provide a field name key to override
435
+ * messages for a specific field (supports nested dot-notated paths).
436
+ */
437
+ type ValidationMessages = {
438
+ required?: string;
439
+ [fieldName: string]: string | Record<string, string> | undefined;
440
+ };
441
+ /**
442
+ * A minimal storage adapter interface compatible with `localStorage` and
443
+ * `sessionStorage`. Provide a custom implementation to persist form values
444
+ * to any backing store (e.g. IndexedDB, AsyncStorage).
445
+ */
446
+ type PersistStorage = {
447
+ getItem: (key: string) => string | null;
448
+ setItem: (key: string, value: string) => void;
449
+ removeItem: (key: string) => void;
450
+ };
451
+ /**
452
+ * The imperative handle exposed via `ref` on `<AutoForm>`. Provides methods
453
+ * to programmatically control the form from a parent component.
454
+ *
455
+ * @template TSchema - The Zod object schema that defines the form shape.
456
+ */
457
+ type AutoFormHandle<TSchema extends z.$ZodObject = z.$ZodObject> = FormMethods<z.infer<TSchema>>;
458
+ /**
459
+ * Static configuration provided to `createAutoForm`. These options become the
460
+ * default for every form instance created by the factory, and can be
461
+ * overridden per-instance via the corresponding `<AutoForm>` props.
462
+ */
463
+ type AutoFormConfig = {
464
+ /** Default component registry for all form instances. */
465
+ components?: ComponentRegistry;
466
+ /** Default field wrapper component for all form instances. */
467
+ fieldWrapper?: React.ComponentType<FieldWrapperProps>;
468
+ /** Default layout slot overrides for all form instances. */
469
+ layout?: LayoutSlots;
470
+ /** Default CSS class name overrides for all form instances. */
471
+ classNames?: FormClassNames;
472
+ /** When `true`, all fields in every form instance are disabled by default. */
473
+ disabled?: boolean;
474
+ /** Default coercion map applied to all form instances. */
475
+ coercions?: CoercionMap;
476
+ /** Default validation message overrides for all form instances. */
477
+ messages?: ValidationMessages;
478
+ /** Default label strings; overridden per-instance by the `labels` prop */
479
+ labels?: FormLabels;
480
+ };
481
+ /**
482
+ * Props for the `<AutoForm>` component. Drives schema introspection, field
483
+ * rendering, validation, and submission.
484
+ *
485
+ * @template TSchema - A `ZodObject` schema that defines the form shape.
486
+ */
487
+ type AutoFormProps<TSchema extends z.$ZodObject> = {
488
+ /** A UniForm instance carrying the schema and typed onChange handlers. */
489
+ form: {
490
+ readonly schema: TSchema;
491
+ };
492
+ /** Called with the validated form values when the form is submitted successfully. */
493
+ onSubmit: (values: z.infer<TSchema>) => void | Promise<void>;
494
+ /**
495
+ * Initial values to pre-populate the form with.
496
+ * When an async function is provided, the form shows `loadingFallback` until the
497
+ * promise resolves, then resets the form with the loaded values.
498
+ */
499
+ defaultValues?: Partial<z.infer<TSchema>> | (() => Promise<Partial<z.infer<TSchema>>>);
500
+ /** Component registry overrides for this form instance. */
501
+ components?: ComponentRegistry;
502
+ /** Per-field UI metadata overrides (label, placeholder, options, etc.). */
503
+ fields?: {
504
+ [K in DeepKeys<z.infer<TSchema>>]?: FieldOverride<TSchema, DeepFieldValue<z.infer<TSchema>, K>>;
505
+ };
506
+ /** Field wrapper component override for this form instance. */
507
+ fieldWrapper?: React.ComponentType<FieldWrapperProps>;
508
+ /** Layout slot overrides for this form instance. */
509
+ layout?: LayoutSlots;
510
+ /** CSS class name overrides for this form instance. */
511
+ classNames?: FormClassNames;
512
+ /** When `true`, all fields are rendered in a disabled (non-interactive) state. */
513
+ disabled?: boolean;
514
+ /** Coercion map applied before Zod validation for this form instance. */
515
+ coercions?: CoercionMap;
516
+ /** Validation message overrides for this form instance. */
517
+ messages?: ValidationMessages;
518
+ /** When set, form values are auto-saved to storage under this key */
519
+ persistKey?: string;
520
+ /** Debounce interval in ms for persistence writes (default: 300) */
521
+ persistDebounce?: number;
522
+ /** Custom storage adapter (default: localStorage) */
523
+ persistStorage?: PersistStorage;
524
+ /** Called on every value change with the current form values */
525
+ onValuesChange?: (values: z.infer<TSchema>) => void;
526
+ /** Customize hard-coded UI text (submit button, array buttons, etc.) */
527
+ labels?: FormLabels;
528
+ };
529
+
530
+ /**
531
+ * The resolved primitive or structural type of a schema field, as determined
532
+ * by introspecting the Zod schema. Used internally to decide which field
533
+ * component to render.
534
+ */
535
+ type FieldType = 'string' | 'number' | 'boolean' | 'date' | 'select' | 'object' | 'array' | 'union' | 'unknown';
536
+ /**
537
+ * A predicate function that receives the current form values and returns
538
+ * `true` when the field should be visible, `false` when it should be hidden.
539
+ *
540
+ * @template TValues - The shape of the form values object.
541
+ */
542
+ type FieldCondition<TValues = Record<string, unknown>> = (values: TValues) => boolean;
543
+ /**
544
+ * Dynamic field property overrides passed to `ctx.setFieldMeta()` inside a
545
+ * UniForm onChange handler. Each key is optional — only the properties you
546
+ * provide will be applied; omitted keys leave the current field state unchanged.
547
+ */
548
+ type FieldDependencyResult = {
549
+ /** Override the available options for select fields */
550
+ options?: SelectOption[];
551
+ /** Dynamically show or hide the field */
552
+ hidden?: boolean;
553
+ /** Dynamically enable or disable the field */
554
+ disabled?: boolean;
555
+ /** Override the field label */
556
+ label?: string;
557
+ /** Override the placeholder text */
558
+ placeholder?: string;
559
+ /** Override the description text */
560
+ description?: string;
561
+ };
562
+ /**
563
+ * The base set of per-field UI metadata that can be provided via the `fields`
564
+ * prop or through Zod schema extensions (`.meta()`).
565
+ *
566
+ * `FieldMeta` extends this type with an index signature to allow arbitrary
567
+ * extra keys for custom component use-cases.
568
+ */
569
+ type FieldMetaBase = {
570
+ /** Human-readable label rendered above the field. Falls back to a derived label from the field name. */
571
+ label?: string;
572
+ /** Placeholder text rendered inside the input when it has no value. */
573
+ placeholder?: string;
574
+ /** Helper text rendered below the field to provide additional context. */
575
+ description?: string;
576
+ /** Static list of options for `select` / enum fields. */
577
+ options?: SelectOption[];
578
+ /** Group the field under a named section in the form layout. */
579
+ section?: string;
580
+ /** Explicit render order within the form or section (lower numbers render first). */
581
+ order?: number;
582
+ /** Grid column span for multi-column layouts (e.g. `1`–`12`). */
583
+ span?: number;
584
+ /** When `true`, the field is not rendered. */
585
+ hidden?: boolean;
586
+ /** When `true`, the field is rendered but not interactive. */
587
+ disabled?: boolean;
588
+ /** Conditionally show or hide the field based on the current form values. */
589
+ condition?: FieldCondition;
590
+ /**
591
+ * Override the component used to render this field.
592
+ *
593
+ * - **string** — a key registered in the `ComponentRegistry` (e.g. `'autocomplete'`
594
+ * registered via `createAutoForm({ components: { autocomplete: MyComp } })` or the
595
+ * `components` prop).
596
+ * - **React component** — a `FieldProps`-compatible component passed inline,
597
+ * bypassing the registry entirely (e.g. `component: MyCustomInput`).
598
+ *
599
+ * Uses `React.ComponentType<never>` as the type parameter rather than
600
+ * `React.ComponentType<FieldProps>` to avoid both a circular inference error
601
+ * (FieldProps → FieldMeta → component → FieldProps) and a contravariance
602
+ * error when components typed as `(props: FieldProps) => JSX.Element` are
603
+ * assigned via Zod's `.meta()`, whose return type widens `meta` to `any`.
604
+ * `ComponentType<never>` is the widest possible assignable supertype.
605
+ */
606
+ component?: string | React$1.ComponentType<any>;
607
+ /** Called when this field's value changes. Receives the new value and form control methods. May be async. */
608
+ onChange?: (value: unknown, form: FormMethods) => void | Promise<void>;
609
+ /** When `true`, rows in an array field can be reordered via move-up/move-down buttons. */
610
+ movable?: boolean;
611
+ /** When `true`, rows in an array field can be duplicated. */
612
+ duplicable?: boolean;
613
+ /** When `true`, rows in an array field can be individually collapsed. */
614
+ collapsible?: boolean;
615
+ /**
616
+ * Override the wrapper component rendered around this specific object or array field.
617
+ * Takes precedence over the global `layout.objectWrapper` / `layout.arrayWrapper` slots.
618
+ *
619
+ * The component receives the same `ObjectWrapperProps` / `ArrayWrapperProps` as the
620
+ * global slot — `children`, `label`, `className`, and `labelClassName`.
621
+ */
622
+ wrapper?: React$1.ComponentType<ObjectWrapperProps | ArrayWrapperProps>;
623
+ };
624
+ /**
625
+ * Per-field UI metadata with an open index signature, allowing arbitrary
626
+ * extra keys for custom component use-cases. Extends `FieldMetaBase` with
627
+ * all the standard metadata properties.
628
+ */
629
+ type FieldMeta = FieldMetaBase & {
630
+ [key: string]: unknown;
631
+ };
632
+ /**
633
+ * Common properties shared by every field variant.
634
+ */
635
+ type FieldConfigBase = {
636
+ /** Dot-notated field path (e.g. `"address.street"`). */
637
+ name: string;
638
+ /** Display label for the field. */
639
+ label: string;
640
+ /** Whether the field is required by the schema. */
641
+ required: boolean;
642
+ /** Merged UI metadata for the field. */
643
+ meta: FieldMeta;
644
+ /**
645
+ * The original Zod schema for this field, after transparent wrappers
646
+ * (`optional`, `nullable`, `default`, `pipe`) have been stripped.
647
+ *
648
+ * This is a general escape hatch for custom components that need to inspect
649
+ * the raw schema — for example, to read union variants, access custom Zod
650
+ * metadata not captured by introspection, or build schema-aware validation UI.
651
+ */
652
+ schema: z.$ZodType;
653
+ };
654
+ /**
655
+ * The fully resolved configuration for a single form field, produced by
656
+ * introspecting the Zod schema and merging any `fields` prop overrides.
657
+ * Consumed internally by field renderer components.
658
+ *
659
+ * This is a discriminated union on the `type` field — narrow on `type` to
660
+ * access the fields that are only present for specific field kinds (e.g.
661
+ * `children` for `"object"`, `itemConfig` for `"array"`, etc.).
662
+ */
663
+ type FieldConfig = FieldConfigBase & ({
664
+ type: 'string';
665
+ } | {
666
+ type: 'number';
667
+ } | {
668
+ type: 'boolean';
669
+ } | {
670
+ type: 'date';
671
+ } | {
672
+ type: 'select';
673
+ /** Resolved options for `select` / enum fields. */
674
+ options: SelectOption[];
675
+ } | {
676
+ type: 'object';
677
+ /** Child field configs for nested object fields. */
678
+ children: FieldConfig[];
679
+ } | {
680
+ type: 'array';
681
+ /** Item field config describing a single row's shape. */
682
+ itemConfig: FieldConfig;
683
+ /** Minimum number of items (from `z.array().min(...)`). */
684
+ minItems?: number;
685
+ /** Maximum number of items (from `z.array().max(...)`). */
686
+ maxItems?: number;
687
+ } | {
688
+ type: 'union';
689
+ /** Variant configs for each union member. */
690
+ unionVariants: FieldConfig[];
691
+ /** Discriminator key for discriminated unions. */
692
+ discriminatorKey?: string;
693
+ } | {
694
+ type: 'unknown';
695
+ });
696
+ /**
697
+ * The props passed to every field renderer component. Provides the current
698
+ * value, change/blur handlers, and all resolved UI metadata needed to render
699
+ * a single field.
700
+ */
701
+ interface FieldProps<Value = unknown> {
702
+ /** Dot-notated field path (e.g. `"address.street"`). */
703
+ name: string;
704
+ /** The current field value. */
705
+ value: Value;
706
+ /** Callback to update the field value. */
707
+ onChange: (value: Value) => void;
708
+ /** Callback fired when the field loses focus. */
709
+ onBlur: () => void;
710
+ /** Ref callback for registering the DOM element with `react-hook-form`. */
711
+ ref: RefCallBack;
712
+ /** Resolved display label for the field. */
713
+ label: string;
714
+ /** Placeholder text for the input. */
715
+ placeholder?: string;
716
+ /** Helper text rendered below the field. */
717
+ description?: string;
718
+ /** Validation error message for the field. */
719
+ error?: string;
720
+ /** Whether the field is required by the schema. */
721
+ required: boolean;
722
+ /** When `true`, the field is rendered but not interactive. */
723
+ disabled?: boolean;
724
+ /** Resolved options for `select` / enum fields. */
725
+ options?: SelectOption[];
726
+ /** Full field metadata, including any custom keys. */
727
+ meta: FieldMeta;
728
+ /**
729
+ * The original Zod schema for this field (after transparent wrappers are stripped).
730
+ * Use this as an escape hatch when you need capabilities beyond what `FieldConfig`
731
+ * exposes — e.g. inspecting union variants, accessing custom Zod refinements, etc.
732
+ */
733
+ schema: z.$ZodType;
734
+ }
735
+ /**
736
+ * A per-field override entry used in the AutoFormProps `fields` prop.
737
+ * The `onChange` callback is typed to the specific schema's inferred value
738
+ * type, providing full IDE autocomplete.
739
+ */
740
+ type FieldOverride<TSchema extends z.$ZodObject = z.$ZodObject, TValue = unknown> = Partial<FieldMetaBase> & {
741
+ /** Conditionally show or hide the field based on the current form values. */
742
+ condition?: FieldCondition<z.infer<TSchema>>;
743
+ /** Called when this field's value changes. Receives the new value and form control methods. May be async. */
744
+ onChange?: (value: TValue, form: FormMethods<z.infer<TSchema>>) => void | Promise<void>;
745
+ [key: string]: unknown;
746
+ };
747
+
748
+ export type { AutoFormProps as A, SubmitButtonProps as B, ComponentRegistry as C, DeepKeys as D, FieldMetaBase as F, LayoutSlots as L, ObjectWrapperProps as O, PersistStorage as P, ResolvedLayoutSlots as R, SectionConfig as S, ValidationMessages as V, FieldConfig as a, AutoFormHandle as b, FieldProps as c, FieldWrapperProps as d, ArrayButtonProps as e, ArrayCollapseButtonProps as f, ArrayFieldLayoutProps as g, ArrayRowLayoutProps as h, ArrayWrapperProps as i, AutoFormConfig as j, FormMethods as k, FieldDependencyResult as l, DeepFieldValue as m, ConditionValues as n, FormClassNames as o, CoercionMap as p, FormLabels as q, ArrayButtonSlots as r, FieldCondition as s, FieldMeta as t, FieldOverride as u, FieldType as v, FormWrapperProps as w, ResolvedArrayButtonSlots as x, SectionWrapperProps as y, SelectOption as z };