@uniform-ts/core 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1592 @@
1
+ import * as z from 'zod/v4/core';
2
+ import * as React3 from 'react';
3
+ import { useMemo, useRef, useEffect, useCallback, useState } from 'react';
4
+ import { useFormState, useWatch, useForm, useFieldArray, Controller } from 'react-hook-form';
5
+ import { zodResolver } from '@hookform/resolvers/zod';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+
8
+ // src/introspection/deriveLabel.ts
9
+ function deriveLabel(name) {
10
+ const segment = name.split(".").pop() ?? name;
11
+ if (!segment) return "";
12
+ return segment.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").split(" ").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
13
+ }
14
+ function extractMeta(schema) {
15
+ return z.globalRegistry.get(schema) ?? {};
16
+ }
17
+ var WRAPPER_KINDS = /* @__PURE__ */ new Set([
18
+ "optional",
19
+ "nullable",
20
+ "default",
21
+ "prefault",
22
+ "pipe"
23
+ // ZodPipe — .transform() returns ZodPipe<Source, ZodTransform>
24
+ ]);
25
+ function unwrap(schema) {
26
+ let inner = schema;
27
+ let required = true;
28
+ let meta = extractMeta(inner);
29
+ let kind = inner._zod.def.type;
30
+ while (WRAPPER_KINDS.has(kind)) {
31
+ if (kind === "optional" || kind === "nullable") {
32
+ required = false;
33
+ }
34
+ if (kind === "pipe") {
35
+ inner = inner._zod.def.in;
36
+ } else {
37
+ inner = inner._zod.def.innerType;
38
+ }
39
+ meta = { ...extractMeta(inner), ...meta };
40
+ kind = inner._zod.def.type;
41
+ }
42
+ return { schema: inner, required, meta };
43
+ }
44
+
45
+ // src/introspection/introspect.ts
46
+ function introspectSchema(schema, name = "", parentPath = "") {
47
+ const path = parentPath && name ? `${parentPath}.${name}` : name || parentPath;
48
+ const { schema: unwrappedSchema, required, meta } = unwrap(schema);
49
+ const inner = unwrappedSchema;
50
+ const def = inner._zod.def;
51
+ const kind = def.type;
52
+ const mergedMeta = { ...meta };
53
+ const label = typeof mergedMeta.label === "string" ? mergedMeta.label : deriveLabel(name);
54
+ let type = "unknown";
55
+ let options;
56
+ let children;
57
+ let itemConfig;
58
+ let unionVariants;
59
+ let discriminatorKey;
60
+ let minItems;
61
+ let maxItems;
62
+ try {
63
+ if (kind === "string") {
64
+ type = "string";
65
+ const defFormat = def.format;
66
+ if (defFormat === "email") {
67
+ mergedMeta["inputType"] = "email";
68
+ } else if (defFormat === "url") {
69
+ mergedMeta["inputType"] = "url";
70
+ } else if (defFormat === "uuid") {
71
+ mergedMeta["inputType"] = "uuid";
72
+ } else {
73
+ const checks = def.checks ?? [];
74
+ const hasFormat = (fmt) => checks.some(
75
+ (c) => c._zod.def.check === "string_format" && c._zod.def.format === fmt
76
+ );
77
+ if (hasFormat("email")) {
78
+ mergedMeta["inputType"] = "email";
79
+ } else if (hasFormat("url")) {
80
+ mergedMeta["inputType"] = "url";
81
+ } else if (hasFormat("uuid")) {
82
+ mergedMeta["inputType"] = "uuid";
83
+ }
84
+ }
85
+ } else if (kind === "number") {
86
+ type = "number";
87
+ mergedMeta["inputType"] = "number";
88
+ } else if (kind === "boolean") {
89
+ type = "boolean";
90
+ } else if (kind === "date") {
91
+ type = "date";
92
+ mergedMeta["inputType"] = "date";
93
+ } else if (kind === "enum") {
94
+ type = "select";
95
+ options = Object.entries(def.entries).filter(([key]) => isNaN(Number(key))).map(([key, value]) => ({
96
+ label: typeof value === "string" ? value.charAt(0).toUpperCase() + value.slice(1).toLowerCase() : key.charAt(0).toUpperCase() + key.slice(1).toLowerCase(),
97
+ value
98
+ }));
99
+ } else if (kind === "object") {
100
+ type = "object";
101
+ children = Object.entries(def.shape).map(
102
+ ([key, fieldSchema]) => introspectSchema(fieldSchema, key, path)
103
+ );
104
+ } else if (kind === "array") {
105
+ type = "array";
106
+ const elementSchema = def.element;
107
+ itemConfig = introspectSchema(elementSchema, "", "");
108
+ const checks = def.checks ?? [];
109
+ for (const check of checks) {
110
+ const checkDef = check._zod.def;
111
+ if (checkDef.check === "min_length" && typeof checkDef.minimum === "number") {
112
+ minItems = checkDef.minimum;
113
+ }
114
+ if (checkDef.check === "max_length" && typeof checkDef.maximum === "number") {
115
+ maxItems = checkDef.maximum;
116
+ }
117
+ }
118
+ } else if (def.type === "union") {
119
+ type = "union";
120
+ const unionDef = def;
121
+ if ("discriminator" in unionDef) {
122
+ discriminatorKey = unionDef.discriminator;
123
+ }
124
+ const variants = unionDef.options;
125
+ unionVariants = variants.map(
126
+ (variant, i) => introspectSchema(variant, String(i), path)
127
+ );
128
+ }
129
+ } catch {
130
+ type = "unknown";
131
+ }
132
+ return {
133
+ name: path,
134
+ type,
135
+ label,
136
+ required,
137
+ meta: mergedMeta,
138
+ ...options !== void 0 && { options },
139
+ ...children !== void 0 && { children },
140
+ ...itemConfig !== void 0 && { itemConfig },
141
+ ...unionVariants !== void 0 && { unionVariants },
142
+ ...discriminatorKey !== void 0 && { discriminatorKey },
143
+ ...minItems !== void 0 && { minItems },
144
+ ...maxItems !== void 0 && { maxItems }
145
+ };
146
+ }
147
+ function introspectObjectSchema(schema) {
148
+ return Object.entries(schema._zod.def.shape).map(
149
+ ([key, fieldSchema]) => introspectSchema(fieldSchema, key, "")
150
+ );
151
+ }
152
+
153
+ // src/introspection/discriminatedUnion.ts
154
+ function parseDiscriminatedUnionMeta(schema) {
155
+ const def = schema._zod.def;
156
+ const discriminatorKey = def.discriminator;
157
+ const variants = def.options;
158
+ const variantMap = /* @__PURE__ */ new Map();
159
+ const discriminatorOptions = [];
160
+ for (const variant of variants) {
161
+ const shape = variant._zod.def.shape;
162
+ const litDef = shape[discriminatorKey]._zod.def;
163
+ const value = String(litDef.values[0]);
164
+ variantMap.set(value, variant);
165
+ discriminatorOptions.push({
166
+ label: value.charAt(0).toUpperCase() + value.slice(1),
167
+ value
168
+ });
169
+ }
170
+ const discriminatorField = {
171
+ name: discriminatorKey,
172
+ type: "select",
173
+ label: deriveLabel(discriminatorKey),
174
+ required: true,
175
+ meta: {},
176
+ options: discriminatorOptions
177
+ };
178
+ return {
179
+ discriminatorKey,
180
+ variantMap,
181
+ discriminatorField,
182
+ firstVariant: variants[0]
183
+ };
184
+ }
185
+
186
+ // src/registry/mergeRegistries.ts
187
+ function mergeRegistries(base, overrides) {
188
+ if (!overrides) return base;
189
+ return { ...base, ...overrides };
190
+ }
191
+ function resolveInputType(props) {
192
+ const metaType = props.meta.inputType;
193
+ if (typeof metaType === "string") return metaType;
194
+ if (props.meta.component === "date") return "date";
195
+ return "text";
196
+ }
197
+ function formatValue(value) {
198
+ if (value instanceof Date) {
199
+ if (isNaN(value.getTime())) return "";
200
+ return value.toISOString().split("T")[0];
201
+ }
202
+ if (value === null || value === void 0) return "";
203
+ return String(value);
204
+ }
205
+ function DefaultInput(props) {
206
+ const { name, value, onChange, onBlur, ref, required, disabled, meta } = props;
207
+ const inputType = resolveInputType(props);
208
+ return /* @__PURE__ */ jsx(
209
+ "input",
210
+ {
211
+ id: name,
212
+ name,
213
+ type: inputType,
214
+ value: formatValue(value),
215
+ onChange: (e) => onChange(e.target.value),
216
+ onBlur,
217
+ ref,
218
+ required,
219
+ disabled,
220
+ "aria-required": required,
221
+ "aria-disabled": disabled,
222
+ placeholder: meta.placeholder,
223
+ "data-input-type": inputType,
224
+ "data-required": required || void 0,
225
+ "data-disabled": disabled || void 0
226
+ }
227
+ );
228
+ }
229
+ function DefaultCheckbox(props) {
230
+ const { name, value, onChange, onBlur, ref, required, disabled, label } = props;
231
+ const checked = Boolean(value);
232
+ return /* @__PURE__ */ jsxs("label", { htmlFor: name, "data-disabled": disabled || void 0, children: [
233
+ /* @__PURE__ */ jsx(
234
+ "input",
235
+ {
236
+ id: name,
237
+ name,
238
+ type: "checkbox",
239
+ checked,
240
+ onChange: (e) => onChange(e.target.checked),
241
+ onBlur,
242
+ ref,
243
+ required,
244
+ disabled,
245
+ "aria-required": required,
246
+ "aria-disabled": disabled,
247
+ "data-required": required || void 0,
248
+ "data-disabled": disabled || void 0,
249
+ "data-checked": checked || void 0
250
+ }
251
+ ),
252
+ label
253
+ ] });
254
+ }
255
+ function DefaultSelect(props) {
256
+ const {
257
+ name,
258
+ value,
259
+ onChange,
260
+ onBlur,
261
+ ref,
262
+ required,
263
+ disabled,
264
+ options = []
265
+ } = props;
266
+ return /* @__PURE__ */ jsx(
267
+ "select",
268
+ {
269
+ id: name,
270
+ name,
271
+ value: String(value ?? ""),
272
+ onChange: (e) => onChange(e.target.value),
273
+ onBlur,
274
+ ref,
275
+ required,
276
+ disabled,
277
+ "aria-required": required,
278
+ "aria-disabled": disabled,
279
+ "data-required": required || void 0,
280
+ "data-disabled": disabled || void 0,
281
+ children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: String(opt.value), children: opt.label }, String(opt.value)))
282
+ }
283
+ );
284
+ }
285
+
286
+ // src/registry/defaultRegistry.ts
287
+ var defaultRegistry = {
288
+ string: DefaultInput,
289
+ number: DefaultInput,
290
+ date: DefaultInput,
291
+ boolean: DefaultCheckbox,
292
+ select: DefaultSelect
293
+ };
294
+ var AutoFormContext = React3.createContext(null);
295
+ function useAutoFormContext() {
296
+ const ctx = React3.useContext(AutoFormContext);
297
+ if (!ctx) {
298
+ throw new Error(
299
+ "[UniForm] useAutoFormContext must be used inside an <AutoForm> component."
300
+ );
301
+ }
302
+ return ctx;
303
+ }
304
+ var AutoFormContextProvider = AutoFormContext.Provider;
305
+ function DefaultFieldWrapper({
306
+ children,
307
+ field,
308
+ error,
309
+ span,
310
+ index = 0,
311
+ depth = 0
312
+ }) {
313
+ const { classNames, disabled: contextDisabled } = useAutoFormContext();
314
+ const isDisabled = field.meta.disabled || contextDisabled;
315
+ const hasError = Boolean(error);
316
+ const hasDescription = Boolean(field.meta.description);
317
+ return /* @__PURE__ */ jsxs(
318
+ "div",
319
+ {
320
+ className: classNames.fieldWrapper,
321
+ style: {
322
+ "--field-span": span ?? field.meta.span ?? 1,
323
+ "--field-index": index,
324
+ "--field-depth": depth
325
+ },
326
+ "data-field-name": field.name,
327
+ "data-field-type": field.type,
328
+ "data-required": field.required || void 0,
329
+ "data-disabled": isDisabled || void 0,
330
+ "data-has-error": hasError || void 0,
331
+ "data-has-description": hasDescription || void 0,
332
+ children: [
333
+ /* @__PURE__ */ jsxs("label", { htmlFor: field.name, className: classNames.label, children: [
334
+ field.label,
335
+ field.required && " *"
336
+ ] }),
337
+ children,
338
+ hasDescription && /* @__PURE__ */ jsx("p", { className: classNames.description, children: String(field.meta.description) }),
339
+ error && /* @__PURE__ */ jsx("span", { role: "alert", className: classNames.error, children: error })
340
+ ]
341
+ }
342
+ );
343
+ }
344
+ function DefaultSubmitButton({
345
+ isSubmitting
346
+ }) {
347
+ const { labels } = useAutoFormContext();
348
+ return /* @__PURE__ */ jsx(
349
+ "button",
350
+ {
351
+ type: "submit",
352
+ disabled: isSubmitting,
353
+ "data-submitting": isSubmitting || void 0,
354
+ children: labels.submit ?? "Submit"
355
+ }
356
+ );
357
+ }
358
+ function DefaultFormWrapper({ children }) {
359
+ return /* @__PURE__ */ jsx(Fragment, { children });
360
+ }
361
+ function DefaultSectionWrapper({
362
+ children,
363
+ title
364
+ }) {
365
+ return /* @__PURE__ */ jsxs("fieldset", { children: [
366
+ /* @__PURE__ */ jsx("legend", { children: title }),
367
+ children
368
+ ] });
369
+ }
370
+ function DefaultArrayRowLayout({
371
+ children,
372
+ buttons
373
+ }) {
374
+ return /* @__PURE__ */ jsxs("div", { children: [
375
+ buttons.collapse,
376
+ children,
377
+ /* @__PURE__ */ jsxs("div", { children: [
378
+ buttons.moveUp,
379
+ buttons.moveDown,
380
+ buttons.duplicate,
381
+ buttons.remove
382
+ ] })
383
+ ] });
384
+ }
385
+
386
+ // src/utils/resolveErrorMessage.ts
387
+ function resolveErrorMessage(fieldName, error, messages) {
388
+ if (!error) return void 0;
389
+ const originalMessage = error.message;
390
+ if (!messages) return originalMessage;
391
+ const fieldMessage = messages[fieldName];
392
+ if (fieldMessage !== void 0) {
393
+ if (typeof fieldMessage === "string") return fieldMessage;
394
+ if (typeof fieldMessage === "object" && error.type) {
395
+ const coded = fieldMessage[error.type];
396
+ if (coded) return coded;
397
+ }
398
+ }
399
+ if (messages.required && isRequiredError(error)) {
400
+ return messages.required;
401
+ }
402
+ return originalMessage;
403
+ }
404
+ function isRequiredError(error) {
405
+ return error.type === "too_small" || error.type === "invalid_type";
406
+ }
407
+
408
+ // src/components/resolveComponent.ts
409
+ function resolveComponent(field, registry) {
410
+ if (field.meta.component && typeof field.meta.component === "function") {
411
+ return field.meta.component;
412
+ }
413
+ if (typeof field.meta.component === "string" && registry[field.meta.component]) {
414
+ return registry[field.meta.component];
415
+ }
416
+ if (registry[field.type]) {
417
+ return registry[field.type];
418
+ }
419
+ if (defaultRegistry[field.type]) {
420
+ return defaultRegistry[field.type];
421
+ }
422
+ console.warn(
423
+ `[UniForm] No component found for field type "${field.type}"${field.meta.component ? ` with meta.component "${String(field.meta.component)}"` : ""}. Rendering null.`
424
+ );
425
+ return null;
426
+ }
427
+
428
+ // src/coercion/coerce.ts
429
+ var defaultCoercionMap = {
430
+ number: (value) => {
431
+ if (value === "" || value === null || value === void 0) return void 0;
432
+ const num = Number(value);
433
+ return isNaN(num) ? value : num;
434
+ },
435
+ date: (value) => {
436
+ if (value === "" || value === null || value === void 0) return void 0;
437
+ const d = new Date(String(value));
438
+ return isNaN(d.getTime()) ? value : d;
439
+ },
440
+ boolean: (value) => Boolean(value),
441
+ string: (value) => value == null || value == void 0 ? "" : String(value)
442
+ };
443
+ function coerceValue(type, value, customCoercions) {
444
+ const coercionFn = customCoercions?.[type] ?? defaultCoercionMap[type];
445
+ if (!coercionFn) return value;
446
+ return coercionFn(value);
447
+ }
448
+ function ScalarField({
449
+ field,
450
+ control,
451
+ effectiveName,
452
+ shouldUnregister
453
+ }) {
454
+ const {
455
+ registry,
456
+ disabled: contextDisabled,
457
+ coercions,
458
+ formMethods
459
+ } = useAutoFormContext();
460
+ const Component = resolveComponent(field, registry);
461
+ if (!Component) return null;
462
+ return /* @__PURE__ */ jsx(
463
+ Controller,
464
+ {
465
+ name: effectiveName,
466
+ control,
467
+ shouldUnregister,
468
+ render: ({ field: rhfField, fieldState }) => /* @__PURE__ */ jsx(
469
+ Component,
470
+ {
471
+ name: effectiveName,
472
+ value: rhfField.value ?? "",
473
+ onChange: (value) => {
474
+ const coerced = coerceValue(field.type, value, coercions);
475
+ rhfField.onChange(coerced);
476
+ field.meta.onChange?.(coerced, formMethods);
477
+ },
478
+ onBlur: rhfField.onBlur,
479
+ ref: rhfField.ref,
480
+ label: field.label,
481
+ placeholder: field.meta.placeholder,
482
+ description: field.meta.description,
483
+ error: fieldState.error?.message,
484
+ required: field.required,
485
+ disabled: field.meta.disabled || contextDisabled,
486
+ meta: field.meta
487
+ }
488
+ )
489
+ }
490
+ );
491
+ }
492
+ function BooleanField({
493
+ field,
494
+ control,
495
+ effectiveName,
496
+ shouldUnregister
497
+ }) {
498
+ const {
499
+ registry,
500
+ disabled: contextDisabled,
501
+ formMethods
502
+ } = useAutoFormContext();
503
+ const Component = resolveComponent(field, registry);
504
+ if (!Component) return null;
505
+ return /* @__PURE__ */ jsx(
506
+ Controller,
507
+ {
508
+ name: effectiveName,
509
+ control,
510
+ shouldUnregister,
511
+ render: ({ field: rhfField, fieldState }) => /* @__PURE__ */ jsx(
512
+ Component,
513
+ {
514
+ name: effectiveName,
515
+ value: rhfField.value ?? false,
516
+ onChange: (value) => {
517
+ rhfField.onChange(value);
518
+ field.meta.onChange?.(value, formMethods);
519
+ },
520
+ onBlur: rhfField.onBlur,
521
+ ref: rhfField.ref,
522
+ label: field.label,
523
+ placeholder: field.meta.placeholder,
524
+ description: field.meta.description,
525
+ error: fieldState.error?.message,
526
+ required: field.required,
527
+ disabled: field.meta.disabled || rhfField.disabled || contextDisabled,
528
+ meta: field.meta
529
+ }
530
+ )
531
+ }
532
+ );
533
+ }
534
+ function SelectField({
535
+ field,
536
+ control,
537
+ effectiveName,
538
+ shouldUnregister
539
+ }) {
540
+ const {
541
+ registry,
542
+ disabled: contextDisabled,
543
+ formMethods
544
+ } = useAutoFormContext();
545
+ const Component = resolveComponent(field, registry);
546
+ if (!Component) return null;
547
+ return /* @__PURE__ */ jsx(
548
+ Controller,
549
+ {
550
+ name: effectiveName,
551
+ control,
552
+ shouldUnregister,
553
+ render: ({ field: rhfField, fieldState }) => /* @__PURE__ */ jsx(
554
+ Component,
555
+ {
556
+ name: effectiveName,
557
+ value: rhfField.value ?? "",
558
+ onChange: (value) => {
559
+ rhfField.onChange(value);
560
+ field.meta.onChange?.(value, formMethods);
561
+ },
562
+ onBlur: rhfField.onBlur,
563
+ ref: rhfField.ref,
564
+ label: field.label,
565
+ placeholder: field.meta.placeholder,
566
+ description: field.meta.description,
567
+ error: fieldState.error?.message,
568
+ required: field.required,
569
+ disabled: field.meta.disabled || contextDisabled,
570
+ options: field.options,
571
+ meta: field.meta
572
+ }
573
+ )
574
+ }
575
+ );
576
+ }
577
+ function ObjectField({
578
+ field,
579
+ control,
580
+ namePrefix,
581
+ depth = 0,
582
+ shouldUnregister
583
+ }) {
584
+ const children = field.children;
585
+ const content = children.map((child, idx) => /* @__PURE__ */ jsx(
586
+ FieldRenderer,
587
+ {
588
+ field: child,
589
+ control,
590
+ namePrefix,
591
+ index: idx,
592
+ depth: depth + 1,
593
+ shouldUnregister
594
+ },
595
+ child.name
596
+ ));
597
+ if (field.meta.section) {
598
+ return /* @__PURE__ */ jsx(Fragment, { children: content });
599
+ }
600
+ return /* @__PURE__ */ jsxs("fieldset", { children: [
601
+ field.label && /* @__PURE__ */ jsx("legend", { children: field.label }),
602
+ content
603
+ ] });
604
+ }
605
+
606
+ // src/components/fields/getDefaultValue.ts
607
+ function getDefaultValue(field) {
608
+ switch (field.type) {
609
+ case "string":
610
+ return "";
611
+ case "number":
612
+ return 0;
613
+ case "boolean":
614
+ return false;
615
+ case "date":
616
+ return /* @__PURE__ */ new Date();
617
+ case "select":
618
+ return field.options?.[0]?.value ?? "";
619
+ case "object": {
620
+ const result = {};
621
+ for (const child of field.children ?? []) {
622
+ const key = child.name.split(".").pop() ?? child.name;
623
+ result[key] = getDefaultValue(child);
624
+ }
625
+ return result;
626
+ }
627
+ case "array":
628
+ return [];
629
+ default:
630
+ return void 0;
631
+ }
632
+ }
633
+ function getRowSummary(row, itemConfig, index) {
634
+ if (itemConfig.type === "object") {
635
+ for (const child of itemConfig.children) {
636
+ const key = child.name.split(".").pop() ?? child.name;
637
+ const val = row[key];
638
+ if ((child.type === "string" || child.type === "number") && val != null && val !== "") {
639
+ return String(val);
640
+ }
641
+ }
642
+ }
643
+ return `Item ${index + 1}`;
644
+ }
645
+ function ArrayField({ field, control, effectiveName }) {
646
+ const { classNames, layout, labels } = useAutoFormContext();
647
+ const {
648
+ fields: rows,
649
+ append,
650
+ remove,
651
+ move,
652
+ insert
653
+ } = useFieldArray({
654
+ control,
655
+ name: effectiveName
656
+ });
657
+ const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
658
+ const itemConfig = field.itemConfig;
659
+ const isObjectItems = itemConfig.type === "object";
660
+ const minItems = field.minItems;
661
+ const maxItems = field.maxItems;
662
+ const atMin = minItems != null && rows.length <= minItems;
663
+ const atMax = maxItems != null && rows.length >= maxItems;
664
+ const showMove = field.meta.movable === true;
665
+ const showDuplicate = field.meta.duplicable === true;
666
+ const showCollapse = field.meta.collapsible === true && isObjectItems;
667
+ const toggleCollapse = (index) => {
668
+ setCollapsed((prev) => {
669
+ const next = new Set(prev);
670
+ if (next.has(index)) {
671
+ next.delete(index);
672
+ } else {
673
+ next.add(index);
674
+ }
675
+ return next;
676
+ });
677
+ };
678
+ const effectiveItemConfig = field.meta.section && !itemConfig.meta.section ? {
679
+ ...itemConfig,
680
+ meta: { ...itemConfig.meta, section: field.meta.section }
681
+ } : itemConfig;
682
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
683
+ rows.map((row, index) => {
684
+ const isCollapsed = showCollapse && collapsed.has(index);
685
+ const collapseButton = showCollapse ? /* @__PURE__ */ jsxs(
686
+ "button",
687
+ {
688
+ type: "button",
689
+ className: classNames.arrayCollapse,
690
+ onClick: () => toggleCollapse(index),
691
+ "aria-label": isCollapsed ? `Expand item ${index + 1}` : `Collapse item ${index + 1}`,
692
+ children: [
693
+ isCollapsed ? labels.arrayExpand ?? "\u25BC" : labels.arrayCollapse ?? "\u25B6",
694
+ " ",
695
+ /* @__PURE__ */ jsx(
696
+ CollapseSummary,
697
+ {
698
+ control,
699
+ effectiveName,
700
+ index,
701
+ itemConfig,
702
+ isCollapsed
703
+ }
704
+ )
705
+ ]
706
+ }
707
+ ) : null;
708
+ const moveUpButton = showMove && rows.length > 1 ? /* @__PURE__ */ jsx(
709
+ "button",
710
+ {
711
+ type: "button",
712
+ className: classNames.arrayMove,
713
+ onClick: () => move(index, index - 1),
714
+ disabled: index === 0,
715
+ "aria-label": `Move item ${index + 1} up`,
716
+ children: labels.arrayMoveUp ?? "\u2191"
717
+ }
718
+ ) : null;
719
+ const moveDownButton = showMove && rows.length > 1 ? /* @__PURE__ */ jsx(
720
+ "button",
721
+ {
722
+ type: "button",
723
+ className: classNames.arrayMove,
724
+ onClick: () => move(index, index + 1),
725
+ disabled: index === rows.length - 1,
726
+ "aria-label": `Move item ${index + 1} down`,
727
+ children: labels.arrayMoveDown ?? "\u2193"
728
+ }
729
+ ) : null;
730
+ const duplicateButton = showDuplicate && !atMax ? /* @__PURE__ */ jsx(
731
+ "button",
732
+ {
733
+ type: "button",
734
+ className: classNames.arrayDuplicate,
735
+ onClick: () => {
736
+ const values = Object.fromEntries(
737
+ Object.entries(row).filter(([k]) => k !== "id")
738
+ );
739
+ insert(index + 1, values);
740
+ },
741
+ "aria-label": `Duplicate item ${index + 1}`,
742
+ children: labels.arrayDuplicate ?? "Duplicate"
743
+ }
744
+ ) : null;
745
+ const removeButton = /* @__PURE__ */ jsx(
746
+ "button",
747
+ {
748
+ type: "button",
749
+ className: classNames.arrayRemove,
750
+ onClick: () => remove(index),
751
+ disabled: atMin,
752
+ "aria-label": `Remove item ${index + 1}`,
753
+ children: labels.arrayRemove ?? "Remove"
754
+ }
755
+ );
756
+ const fieldContent = !isCollapsed ? /* @__PURE__ */ jsx(
757
+ FieldRenderer,
758
+ {
759
+ field: effectiveItemConfig,
760
+ control,
761
+ namePrefix: `${effectiveName}.${index}`
762
+ }
763
+ ) : null;
764
+ const RowLayout = layout.arrayRowLayout;
765
+ return /* @__PURE__ */ jsx(
766
+ RowLayout,
767
+ {
768
+ buttons: {
769
+ moveUp: moveUpButton,
770
+ moveDown: moveDownButton,
771
+ duplicate: duplicateButton,
772
+ remove: removeButton,
773
+ collapse: collapseButton
774
+ },
775
+ index,
776
+ rowCount: rows.length,
777
+ children: fieldContent
778
+ },
779
+ row.id
780
+ );
781
+ }),
782
+ /* @__PURE__ */ jsx(
783
+ "button",
784
+ {
785
+ type: "button",
786
+ className: classNames.arrayAdd,
787
+ disabled: atMax,
788
+ onClick: () => append(getDefaultValue(itemConfig)),
789
+ children: labels.arrayAdd ?? "Add"
790
+ }
791
+ )
792
+ ] });
793
+ if (field.meta.section) {
794
+ return content;
795
+ }
796
+ return /* @__PURE__ */ jsxs("fieldset", { children: [
797
+ field.label && /* @__PURE__ */ jsx("legend", { children: field.label }),
798
+ content
799
+ ] });
800
+ }
801
+ function CollapseSummary({
802
+ control,
803
+ effectiveName,
804
+ index,
805
+ itemConfig,
806
+ isCollapsed
807
+ }) {
808
+ const rowValues = useWatch({ control, name: `${effectiveName}.${index}` });
809
+ const summary = useMemo(() => {
810
+ if (!isCollapsed) return `Item ${index + 1}`;
811
+ if (!rowValues) return `Item ${index + 1}`;
812
+ return getRowSummary(rowValues, itemConfig, index);
813
+ }, [isCollapsed, rowValues, itemConfig, index]);
814
+ return /* @__PURE__ */ jsx(Fragment, { children: isCollapsed ? summary : `Item ${index + 1}` });
815
+ }
816
+ function getEffectiveName(field, namePrefix) {
817
+ if (!namePrefix) return field.name;
818
+ if (!field.name) return namePrefix;
819
+ return `${namePrefix}.${field.name}`;
820
+ }
821
+ function getFieldError(errors, name) {
822
+ const parts = name.split(".");
823
+ let current = errors;
824
+ for (const part of parts) {
825
+ if (current == null || typeof current !== "object") return void 0;
826
+ current = current[part];
827
+ }
828
+ if (current && typeof current === "object" && "message" in current) {
829
+ return current;
830
+ }
831
+ return void 0;
832
+ }
833
+ function FieldRenderer({
834
+ field,
835
+ control,
836
+ namePrefix,
837
+ index = 0,
838
+ depth = 0,
839
+ shouldUnregister
840
+ }) {
841
+ const effectiveShouldUnregister = shouldUnregister ?? typeof field.meta.condition === "function";
842
+ const { fieldWrapper: FieldWrapper, messages } = useAutoFormContext();
843
+ const { errors } = useFormState({ control });
844
+ const effectiveName = getEffectiveName(field, namePrefix);
845
+ const hasDirectComponent = typeof field.meta.component === "function";
846
+ if (field.type === "object" && !hasDirectComponent) {
847
+ const objectField = effectiveName !== field.name ? { ...field, name: effectiveName } : field;
848
+ return /* @__PURE__ */ jsx(
849
+ ObjectField,
850
+ {
851
+ field: objectField,
852
+ control,
853
+ namePrefix,
854
+ depth,
855
+ shouldUnregister: effectiveShouldUnregister
856
+ }
857
+ );
858
+ }
859
+ if (field.type === "array" && !hasDirectComponent) {
860
+ const arrayField = effectiveName !== field.name ? { ...field, name: effectiveName } : field;
861
+ return /* @__PURE__ */ jsx(
862
+ ArrayField,
863
+ {
864
+ field: arrayField,
865
+ control,
866
+ effectiveName
867
+ }
868
+ );
869
+ }
870
+ const effectiveField = effectiveName !== field.name ? { ...field, name: effectiveName } : field;
871
+ const rawError = getFieldError(
872
+ errors,
873
+ effectiveName
874
+ );
875
+ const error = resolveErrorMessage(effectiveName, rawError, messages);
876
+ const renderField = () => {
877
+ if (field.type === "boolean") {
878
+ return /* @__PURE__ */ jsx(
879
+ BooleanField,
880
+ {
881
+ field: effectiveField,
882
+ control,
883
+ effectiveName,
884
+ shouldUnregister: effectiveShouldUnregister
885
+ }
886
+ );
887
+ }
888
+ if (field.type === "select") {
889
+ return /* @__PURE__ */ jsx(
890
+ SelectField,
891
+ {
892
+ field: effectiveField,
893
+ control,
894
+ effectiveName,
895
+ shouldUnregister: effectiveShouldUnregister
896
+ }
897
+ );
898
+ }
899
+ if (field.type === "string" || field.type === "number" || field.type === "date" || // array/object with a direct meta.component — let ScalarField render it
900
+ hasDirectComponent) {
901
+ return /* @__PURE__ */ jsx(
902
+ ScalarField,
903
+ {
904
+ field: effectiveField,
905
+ control,
906
+ effectiveName,
907
+ shouldUnregister: effectiveShouldUnregister
908
+ }
909
+ );
910
+ }
911
+ console.warn(
912
+ `[UniForm] Unsupported field type: "${field.type}". Rendering null.`
913
+ );
914
+ return null;
915
+ };
916
+ const rendered = renderField();
917
+ if (rendered === null) return null;
918
+ return /* @__PURE__ */ jsx(
919
+ FieldWrapper,
920
+ {
921
+ field: effectiveField,
922
+ error,
923
+ span: field.meta.span,
924
+ index,
925
+ depth,
926
+ children: rendered
927
+ }
928
+ );
929
+ }
930
+ function useConditionalFields(fields, control) {
931
+ const values = useWatch({ control });
932
+ return useMemo(() => {
933
+ return fields.filter((field) => {
934
+ if (field.meta.hidden) return false;
935
+ if (typeof field.meta.condition === "function") {
936
+ return field.meta.condition(values);
937
+ }
938
+ return true;
939
+ }).sort((a, b) => {
940
+ const orderA = typeof a.meta.order === "number" ? a.meta.order : Infinity;
941
+ const orderB = typeof b.meta.order === "number" ? b.meta.order : Infinity;
942
+ return orderA - orderB;
943
+ });
944
+ }, [fields, values]);
945
+ }
946
+ function useSectionGrouping(fields) {
947
+ return useMemo(() => {
948
+ const ungrouped = [];
949
+ const sectionMap = /* @__PURE__ */ new Map();
950
+ const sectionOrder = [];
951
+ for (const field of fields) {
952
+ const section = field.meta.section;
953
+ if (typeof section !== "string") {
954
+ ungrouped.push(field);
955
+ } else {
956
+ if (!sectionMap.has(section)) {
957
+ sectionMap.set(section, []);
958
+ sectionOrder.push(section);
959
+ }
960
+ sectionMap.get(section).push(field);
961
+ }
962
+ }
963
+ const groups = [];
964
+ if (ungrouped.length > 0) {
965
+ groups.push({ title: null, fields: ungrouped });
966
+ }
967
+ for (const title of sectionOrder) {
968
+ groups.push({ title, fields: sectionMap.get(title) });
969
+ }
970
+ return groups;
971
+ }, [fields]);
972
+ }
973
+ var defaultStorage = typeof window !== "undefined" ? {
974
+ getItem: (key) => sessionStorage.getItem(key),
975
+ setItem: (key, value) => sessionStorage.setItem(key, value),
976
+ removeItem: (key) => sessionStorage.removeItem(key)
977
+ } : void 0;
978
+ function useFormPersistence(options) {
979
+ const {
980
+ control,
981
+ key,
982
+ debounceMs,
983
+ storage: customStorage,
984
+ reset,
985
+ defaultValues
986
+ } = options;
987
+ const storage = customStorage ?? defaultStorage;
988
+ const timerRef = useRef(null);
989
+ const restoredRef = useRef(false);
990
+ useEffect(() => {
991
+ if (!key || !storage || restoredRef.current) return;
992
+ restoredRef.current = true;
993
+ try {
994
+ const raw = storage.getItem(key);
995
+ if (raw) {
996
+ const parsed = JSON.parse(raw);
997
+ reset({ ...defaultValues, ...parsed });
998
+ }
999
+ } catch {
1000
+ }
1001
+ }, [key, storage, reset, defaultValues]);
1002
+ const values = useWatch({ control });
1003
+ useEffect(() => {
1004
+ if (!key || !storage) return;
1005
+ if (!restoredRef.current) return;
1006
+ if (timerRef.current) clearTimeout(timerRef.current);
1007
+ timerRef.current = setTimeout(() => {
1008
+ try {
1009
+ storage.setItem(key, JSON.stringify(values));
1010
+ } catch {
1011
+ }
1012
+ }, debounceMs);
1013
+ return () => {
1014
+ if (timerRef.current) clearTimeout(timerRef.current);
1015
+ };
1016
+ }, [key, storage, values, debounceMs]);
1017
+ const clearPersistedData = useCallback(() => {
1018
+ if (!key || !storage) return;
1019
+ try {
1020
+ storage.removeItem(key);
1021
+ } catch {
1022
+ }
1023
+ }, [key, storage]);
1024
+ return { clearPersistedData };
1025
+ }
1026
+ function useLatestRef(value) {
1027
+ const ref = React3.useRef(value);
1028
+ React3.useLayoutEffect(() => {
1029
+ ref.current = value;
1030
+ }, [value]);
1031
+ return ref;
1032
+ }
1033
+
1034
+ // src/utils/fieldPipeline.ts
1035
+ function applyFieldOverrides(fields, overrides) {
1036
+ return fields.map((field) => {
1037
+ const override = overrides[field.name];
1038
+ const updated = override ? { ...field, meta: { ...field.meta, ...override } } : field;
1039
+ if (updated.type === "object") {
1040
+ const newChildren = applyFieldOverrides(updated.children, overrides);
1041
+ if (newChildren !== updated.children)
1042
+ return { ...updated, children: newChildren };
1043
+ }
1044
+ if (updated.type === "array" && updated.itemConfig.type === "object") {
1045
+ const prefix = `${updated.name}.`;
1046
+ const strippedOverrides = {};
1047
+ for (const [key, value] of Object.entries(overrides)) {
1048
+ if (key.startsWith(prefix))
1049
+ strippedOverrides[key.slice(prefix.length)] = value;
1050
+ }
1051
+ const newItemChildren = applyFieldOverrides(
1052
+ updated.itemConfig.children,
1053
+ strippedOverrides
1054
+ );
1055
+ if (newItemChildren !== updated.itemConfig.children) {
1056
+ return {
1057
+ ...updated,
1058
+ itemConfig: { ...updated.itemConfig, children: newItemChildren }
1059
+ };
1060
+ }
1061
+ }
1062
+ return updated;
1063
+ });
1064
+ }
1065
+ function injectOnChangeHandlers(fields, uniForm, ctx, handlerKeys = new Set(uniForm._getWatchedFields())) {
1066
+ if (!handlerKeys.size) return fields;
1067
+ return fields.map((field) => {
1068
+ let updated = field;
1069
+ if (handlerKeys.has(field.name)) {
1070
+ const existingOnChange = field.meta.onChange;
1071
+ updated = {
1072
+ ...field,
1073
+ meta: {
1074
+ ...field.meta,
1075
+ onChange: (value, formMethods) => {
1076
+ void existingOnChange?.(value, formMethods);
1077
+ void uniForm._fireHandler(field.name, value, ctx);
1078
+ }
1079
+ }
1080
+ };
1081
+ }
1082
+ if (updated.type === "object") {
1083
+ const newChildren = injectOnChangeHandlers(
1084
+ updated.children,
1085
+ uniForm,
1086
+ ctx,
1087
+ handlerKeys
1088
+ );
1089
+ if (newChildren !== updated.children)
1090
+ updated = { ...updated, children: newChildren };
1091
+ } else if (updated.type === "array") {
1092
+ const prefix = field.name + ".";
1093
+ const itemKeys = /* @__PURE__ */ new Set();
1094
+ for (const key of handlerKeys) {
1095
+ if (key.startsWith(prefix)) itemKeys.add(key.slice(prefix.length));
1096
+ }
1097
+ if (itemKeys.size) {
1098
+ const remappedUniForm = {
1099
+ _getWatchedFields: () => Array.from(itemKeys),
1100
+ _fireHandlers: (name, value, c) => uniForm._fireHandler(`${field.name}.${name}`, value, c)
1101
+ };
1102
+ const newItemConfig = injectOnChangeHandlers(
1103
+ [updated.itemConfig],
1104
+ remappedUniForm,
1105
+ ctx,
1106
+ itemKeys
1107
+ )[0];
1108
+ if (newItemConfig !== updated.itemConfig)
1109
+ updated = { ...updated, itemConfig: newItemConfig };
1110
+ }
1111
+ }
1112
+ return updated;
1113
+ });
1114
+ }
1115
+ function injectConditions(fields, conditions) {
1116
+ if (!conditions.size) return fields;
1117
+ return fields.map((field) => {
1118
+ const condition = conditions.get(field.name);
1119
+ let updated = condition ? { ...field, meta: { ...field.meta, condition } } : field;
1120
+ if (updated.type === "object") {
1121
+ const newChildren = injectConditions(updated.children, conditions);
1122
+ if (newChildren !== updated.children)
1123
+ updated = { ...updated, children: newChildren };
1124
+ } else if (updated.type === "array") {
1125
+ const prefix = field.name + ".";
1126
+ const itemConditions = /* @__PURE__ */ new Map();
1127
+ for (const [key, cond] of conditions) {
1128
+ if (key.startsWith(prefix))
1129
+ itemConditions.set(key.slice(prefix.length), cond);
1130
+ }
1131
+ if (itemConditions.size) {
1132
+ const newItemConfig = injectConditions(
1133
+ [updated.itemConfig],
1134
+ itemConditions
1135
+ )[0];
1136
+ if (newItemConfig !== updated.itemConfig)
1137
+ updated = { ...updated, itemConfig: newItemConfig };
1138
+ }
1139
+ }
1140
+ return updated;
1141
+ });
1142
+ }
1143
+ function applyDynamicMeta(fields, overrides) {
1144
+ if (!Object.keys(overrides).length) return fields;
1145
+ return fields.map((field) => {
1146
+ const override = overrides[field.name];
1147
+ if (!override) return field;
1148
+ const { options, label, ...metaOverrides } = override;
1149
+ return {
1150
+ ...field,
1151
+ ...label !== void 0 ? { label } : {},
1152
+ ...options !== void 0 ? { options } : {},
1153
+ meta: { ...field.meta, ...metaOverrides }
1154
+ };
1155
+ });
1156
+ }
1157
+ function buildDefaults(fields) {
1158
+ const result = {};
1159
+ for (const field of fields) {
1160
+ const key = field.name;
1161
+ switch (field.type) {
1162
+ case "string":
1163
+ result[key] = "";
1164
+ break;
1165
+ case "number":
1166
+ result[key] = "";
1167
+ break;
1168
+ case "boolean":
1169
+ result[key] = false;
1170
+ break;
1171
+ case "select":
1172
+ result[key] = field.options?.[0]?.value ?? "";
1173
+ break;
1174
+ case "array":
1175
+ result[key] = [];
1176
+ break;
1177
+ case "object":
1178
+ result[key] = {};
1179
+ break;
1180
+ }
1181
+ }
1182
+ return result;
1183
+ }
1184
+ function AutoForm(props) {
1185
+ const {
1186
+ form: uniForm,
1187
+ onSubmit,
1188
+ defaultValues,
1189
+ components,
1190
+ fields: fieldOverridesProp = {},
1191
+ fieldWrapper,
1192
+ layout,
1193
+ classNames = {},
1194
+ disabled = false,
1195
+ coercions,
1196
+ messages,
1197
+ persistKey,
1198
+ persistDebounce = 300,
1199
+ persistStorage,
1200
+ onValuesChange,
1201
+ labels = {},
1202
+ ref
1203
+ } = props;
1204
+ const schema = uniForm.schema;
1205
+ const unionInfo = React3.useMemo(() => {
1206
+ const def = schema._zod.def;
1207
+ if (def.type !== "union") return null;
1208
+ return parseDiscriminatedUnionMeta(
1209
+ schema
1210
+ );
1211
+ }, [schema]);
1212
+ const rawFields = React3.useMemo(() => {
1213
+ if (!unionInfo) return introspectObjectSchema(schema);
1214
+ const firstVariantFields = introspectObjectSchema(
1215
+ unionInfo.firstVariant
1216
+ ).filter((f) => f.name !== unionInfo.discriminatorKey);
1217
+ return [unionInfo.discriminatorField, ...firstVariantFields];
1218
+ }, [schema, unionInfo]);
1219
+ const registry = React3.useMemo(
1220
+ () => mergeRegistries(defaultRegistry, components),
1221
+ [components]
1222
+ );
1223
+ const generatedDefaults = React3.useMemo(
1224
+ () => buildDefaults(rawFields),
1225
+ [rawFields]
1226
+ );
1227
+ const computedDefaults = React3.useMemo(() => {
1228
+ const base = {
1229
+ ...generatedDefaults,
1230
+ ...typeof defaultValues === "function" ? {} : defaultValues
1231
+ };
1232
+ const conditions = new Map(
1233
+ uniForm._getConditions()
1234
+ );
1235
+ for (const [name, override] of Object.entries(
1236
+ fieldOverridesProp
1237
+ )) {
1238
+ if (typeof override.condition === "function" && !conditions.has(name)) {
1239
+ conditions.set(name, override.condition);
1240
+ }
1241
+ }
1242
+ for (const [name, condition] of conditions) {
1243
+ if (!condition(base)) {
1244
+ delete base[name];
1245
+ }
1246
+ }
1247
+ return base;
1248
+ }, [generatedDefaults, defaultValues, uniForm, fieldOverridesProp]);
1249
+ const isAsyncDefaults = typeof defaultValues === "function";
1250
+ const [isLoadingDefaults, setIsLoadingDefaults] = React3.useState(isAsyncDefaults);
1251
+ const rhf = useForm({
1252
+ resolver: zodResolver(schema),
1253
+ defaultValues: computedDefaults
1254
+ });
1255
+ const {
1256
+ control,
1257
+ formState,
1258
+ clearErrors,
1259
+ getValues,
1260
+ handleSubmit,
1261
+ reset,
1262
+ resetField,
1263
+ setValue,
1264
+ setError,
1265
+ setFocus,
1266
+ watch
1267
+ } = rhf;
1268
+ const discriminatorValue = useWatch({
1269
+ control,
1270
+ name: unionInfo?.discriminatorKey ?? "",
1271
+ disabled: !unionInfo?.discriminatorKey
1272
+ });
1273
+ const activeFields = React3.useMemo(() => {
1274
+ if (!unionInfo) return rawFields;
1275
+ const variant = unionInfo.variantMap.get(
1276
+ discriminatorValue
1277
+ );
1278
+ if (!variant) return [unionInfo.discriminatorField];
1279
+ const variantFields = introspectObjectSchema(variant).filter(
1280
+ (f) => f.name !== unionInfo.discriminatorKey
1281
+ );
1282
+ return [unionInfo.discriminatorField, ...variantFields];
1283
+ }, [unionInfo, discriminatorValue, rawFields]);
1284
+ const mergedFields = React3.useMemo(
1285
+ () => applyFieldOverrides(
1286
+ activeFields,
1287
+ fieldOverridesProp
1288
+ ),
1289
+ [activeFields, fieldOverridesProp]
1290
+ );
1291
+ const { clearPersistedData } = useFormPersistence({
1292
+ control,
1293
+ key: persistKey,
1294
+ debounceMs: persistDebounce,
1295
+ storage: persistStorage,
1296
+ reset: rhf.reset,
1297
+ defaultValues: computedDefaults
1298
+ });
1299
+ const [dynamicMeta, setDynamicMeta] = React3.useState({});
1300
+ const onSubmitRef = useLatestRef(onSubmit);
1301
+ const onValuesChangeRef = useLatestRef(onValuesChange);
1302
+ const generatedDefaultsRef = useLatestRef(generatedDefaults);
1303
+ React3.useEffect(() => {
1304
+ if (!isAsyncDefaults) return;
1305
+ let cancelled = false;
1306
+ void defaultValues().then(
1307
+ (vals) => {
1308
+ if (cancelled) return;
1309
+ rhf.reset({ ...generatedDefaultsRef.current, ...vals });
1310
+ setIsLoadingDefaults(false);
1311
+ }
1312
+ );
1313
+ return () => {
1314
+ cancelled = true;
1315
+ };
1316
+ }, []);
1317
+ const formMethods = React3.useMemo(
1318
+ () => ({
1319
+ setValue: (name, value) => setValue(name, value, {
1320
+ shouldValidate: true,
1321
+ shouldDirty: true
1322
+ }),
1323
+ setValues: (values) => {
1324
+ for (const [key, val] of Object.entries(values)) {
1325
+ setValue(key, val, { shouldValidate: true, shouldDirty: true });
1326
+ }
1327
+ },
1328
+ getValues: () => getValues(),
1329
+ resetField: (name) => resetField(name),
1330
+ reset: (values) => {
1331
+ if (values) {
1332
+ reset({ ...getValues(), ...values });
1333
+ } else {
1334
+ reset();
1335
+ }
1336
+ setDynamicMeta({});
1337
+ },
1338
+ setError: (name, message) => setError(name, { type: "manual", message }),
1339
+ setErrors: (errors) => {
1340
+ for (const [key, message] of Object.entries(errors)) {
1341
+ setError(key, { type: "manual", message });
1342
+ }
1343
+ },
1344
+ clearErrors: (names) => clearErrors(names),
1345
+ submit: () => {
1346
+ void handleSubmit(
1347
+ (values) => onSubmitRef.current(values)
1348
+ )();
1349
+ },
1350
+ focus: (fieldName) => setFocus(fieldName),
1351
+ watch
1352
+ }),
1353
+ [
1354
+ clearErrors,
1355
+ getValues,
1356
+ handleSubmit,
1357
+ reset,
1358
+ resetField,
1359
+ setValue,
1360
+ setError,
1361
+ setFocus,
1362
+ watch,
1363
+ onSubmitRef
1364
+ ]
1365
+ );
1366
+ React3.useImperativeHandle(
1367
+ ref,
1368
+ () => ({ ...formMethods, isSubmitting: formState.isSubmitting }),
1369
+ [formMethods, formState.isSubmitting]
1370
+ );
1371
+ const setFieldMeta = React3.useCallback(
1372
+ (field, meta) => {
1373
+ if (Object.keys(meta).length) {
1374
+ setDynamicMeta((prev) => ({
1375
+ ...prev,
1376
+ [field]: { ...prev[field], ...meta }
1377
+ }));
1378
+ }
1379
+ },
1380
+ []
1381
+ );
1382
+ const uniFormCtx = React3.useMemo(
1383
+ () => ({ ...formMethods, setFieldMeta }),
1384
+ [formMethods, setFieldMeta]
1385
+ );
1386
+ const fieldsWithHandlers = React3.useMemo(
1387
+ () => injectOnChangeHandlers(
1388
+ mergedFields,
1389
+ uniForm,
1390
+ uniFormCtx
1391
+ ),
1392
+ [mergedFields, uniForm, uniFormCtx]
1393
+ );
1394
+ const fieldsWithConditions = React3.useMemo(
1395
+ () => injectConditions(
1396
+ fieldsWithHandlers,
1397
+ uniForm._getConditions()
1398
+ ),
1399
+ [fieldsWithHandlers, uniForm]
1400
+ );
1401
+ const fieldsWithDynamic = React3.useMemo(
1402
+ () => applyDynamicMeta(fieldsWithConditions, dynamicMeta),
1403
+ [fieldsWithConditions, dynamicMeta]
1404
+ );
1405
+ const allValues = useWatch({ control });
1406
+ React3.useEffect(() => {
1407
+ onValuesChangeRef.current?.(allValues);
1408
+ }, [onValuesChangeRef, allValues]);
1409
+ const visibleFields = useConditionalFields(fieldsWithDynamic, control);
1410
+ const sections = useSectionGrouping(visibleFields);
1411
+ const resolvedLayout = React3.useMemo(
1412
+ () => ({
1413
+ formWrapper: layout?.formWrapper ?? DefaultFormWrapper,
1414
+ sectionWrapper: layout?.sectionWrapper ?? DefaultSectionWrapper,
1415
+ submitButton: layout?.submitButton ?? DefaultSubmitButton,
1416
+ arrayRowLayout: layout?.arrayRowLayout ?? DefaultArrayRowLayout,
1417
+ loadingFallback: layout?.loadingFallback ?? /* @__PURE__ */ jsx("p", { children: "Loading\u2026" })
1418
+ }),
1419
+ [
1420
+ layout?.formWrapper,
1421
+ layout?.sectionWrapper,
1422
+ layout?.submitButton,
1423
+ layout?.arrayRowLayout,
1424
+ layout?.loadingFallback
1425
+ ]
1426
+ );
1427
+ const resolvedFieldWrapper = fieldWrapper ?? DefaultFieldWrapper;
1428
+ const FormWrapper = resolvedLayout.formWrapper;
1429
+ const SectionWrapper = resolvedLayout.sectionWrapper;
1430
+ const SubmitButton = resolvedLayout.submitButton;
1431
+ const contextValue = React3.useMemo(
1432
+ () => ({
1433
+ registry,
1434
+ fieldOverrides: fieldOverridesProp,
1435
+ fieldWrapper: resolvedFieldWrapper,
1436
+ layout: resolvedLayout,
1437
+ classNames,
1438
+ disabled,
1439
+ coercions,
1440
+ messages,
1441
+ labels,
1442
+ formMethods
1443
+ }),
1444
+ [
1445
+ registry,
1446
+ fieldOverridesProp,
1447
+ resolvedFieldWrapper,
1448
+ resolvedLayout,
1449
+ classNames,
1450
+ disabled,
1451
+ coercions,
1452
+ messages,
1453
+ labels,
1454
+ formMethods
1455
+ ]
1456
+ );
1457
+ if (isLoadingDefaults) {
1458
+ return /* @__PURE__ */ jsx(Fragment, { children: resolvedLayout.loadingFallback });
1459
+ }
1460
+ return /* @__PURE__ */ jsx(AutoFormContextProvider, { value: contextValue, children: /* @__PURE__ */ jsx(
1461
+ "form",
1462
+ {
1463
+ noValidate: true,
1464
+ className: classNames.form,
1465
+ onSubmit: (e) => {
1466
+ void handleSubmit(async (values) => {
1467
+ await onSubmit(values);
1468
+ clearPersistedData();
1469
+ })(e);
1470
+ },
1471
+ children: /* @__PURE__ */ jsxs(FormWrapper, { children: [
1472
+ sections.map((section) => {
1473
+ const renderedFields = section.fields.map((field, idx) => /* @__PURE__ */ jsx(
1474
+ FieldRenderer,
1475
+ {
1476
+ field,
1477
+ control,
1478
+ index: idx,
1479
+ depth: 0
1480
+ },
1481
+ field.name
1482
+ ));
1483
+ if (section.title === null) {
1484
+ return /* @__PURE__ */ jsx(React3.Fragment, { children: renderedFields }, "__ungrouped");
1485
+ }
1486
+ return /* @__PURE__ */ jsx(SectionWrapper, { title: section.title, children: renderedFields }, section.title);
1487
+ }),
1488
+ /* @__PURE__ */ jsx(
1489
+ SubmitButton,
1490
+ {
1491
+ isSubmitting: formState.isSubmitting,
1492
+ label: labels.submit ?? "Submit"
1493
+ }
1494
+ )
1495
+ ] })
1496
+ }
1497
+ ) });
1498
+ }
1499
+ function createAutoForm(config) {
1500
+ function ConfiguredAutoForm(props) {
1501
+ const mergedComponents = React3.useMemo(
1502
+ () => mergeRegistries(config.components ?? {}, props.components),
1503
+ [props.components]
1504
+ );
1505
+ const mergedLayout = React3.useMemo(
1506
+ () => ({ ...config.layout, ...props.layout }),
1507
+ [props.layout]
1508
+ );
1509
+ const mergedClassNames = React3.useMemo(
1510
+ () => ({ ...config.classNames, ...props.classNames }),
1511
+ [props.classNames]
1512
+ );
1513
+ const mergedCoercions = React3.useMemo(
1514
+ () => props.coercions || config.coercions ? { ...config.coercions, ...props.coercions } : void 0,
1515
+ [props.coercions]
1516
+ );
1517
+ const mergedMessages = React3.useMemo(
1518
+ () => props.messages || config.messages ? { ...config.messages, ...props.messages } : void 0,
1519
+ [props.messages]
1520
+ );
1521
+ const mergedLabels = React3.useMemo(
1522
+ () => props.labels || config.labels ? { ...config.labels, ...props.labels } : void 0,
1523
+ [props.labels]
1524
+ );
1525
+ return /* @__PURE__ */ jsx(
1526
+ AutoForm,
1527
+ {
1528
+ ...props,
1529
+ ref: props.ref,
1530
+ components: mergedComponents,
1531
+ fieldWrapper: props.fieldWrapper ?? config.fieldWrapper,
1532
+ layout: mergedLayout,
1533
+ classNames: mergedClassNames,
1534
+ disabled: props.disabled || config.disabled || false,
1535
+ coercions: mergedCoercions,
1536
+ messages: mergedMessages,
1537
+ labels: mergedLabels ?? {}
1538
+ }
1539
+ );
1540
+ }
1541
+ ConfiguredAutoForm.displayName = "AutoForm(configured)";
1542
+ return ConfiguredAutoForm;
1543
+ }
1544
+
1545
+ // src/UniForm.ts
1546
+ var UniForm = class {
1547
+ constructor(schema) {
1548
+ this.schema = schema;
1549
+ this._handlers = /* @__PURE__ */ new Map();
1550
+ this._conditions = /* @__PURE__ */ new Map();
1551
+ }
1552
+ /**
1553
+ * Set the typed onChange handler for a specific field.
1554
+ * Replaces any previously registered handler for that field — only one
1555
+ * handler per field is kept. This prevents accidental handler accumulation
1556
+ * when called inside a React render cycle.
1557
+ * Returns `this` for fluent chaining.
1558
+ */
1559
+ setOnChange(field, handler) {
1560
+ this._handlers.set(field, handler);
1561
+ return this;
1562
+ }
1563
+ /**
1564
+ * Attach a typed condition for a specific field.
1565
+ * The field is shown when the predicate returns `true`, hidden when `false`.
1566
+ * Composes with any `condition` set via the `fields` prop (UniForm takes precedence).
1567
+ * Returns `this` for fluent chaining.
1568
+ */
1569
+ setCondition(field, predicate) {
1570
+ this._conditions.set(field, predicate);
1571
+ return this;
1572
+ }
1573
+ /** @internal Called by AutoForm to fire the handler registered for a field. */
1574
+ _fireHandler(field, value, ctx) {
1575
+ return this._handlers.get(field)?.(value, ctx);
1576
+ }
1577
+ /** @internal Returns all field names that have registered onChange handlers. */
1578
+ _getWatchedFields() {
1579
+ return Array.from(this._handlers.keys());
1580
+ }
1581
+ /** @internal Returns a copy of the conditions map for AutoForm to inject into field meta. */
1582
+ _getConditions() {
1583
+ return new Map(this._conditions);
1584
+ }
1585
+ };
1586
+ function createForm(schema) {
1587
+ return new UniForm(schema);
1588
+ }
1589
+
1590
+ export { AutoForm, DefaultCheckbox, DefaultFieldWrapper, DefaultInput, DefaultSelect, DefaultSubmitButton, FieldRenderer, UniForm, coerceValue, createAutoForm, createForm, defaultCoercionMap, defaultRegistry, introspectObjectSchema, introspectSchema, mergeRegistries, useAutoFormContext, useConditionalFields, useFormPersistence, useSectionGrouping };
1591
+ //# sourceMappingURL=index.mjs.map
1592
+ //# sourceMappingURL=index.mjs.map