@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/README.md +1122 -0
- package/dist/index.d.mts +857 -0
- package/dist/index.d.ts +857 -0
- package/dist/index.js +1633 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1592 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +66 -0
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
|