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