@rachelallyson/hero-hook-form 1.2.0 → 2.1.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/CHANGELOG.md +96 -0
- package/README.md +115 -351
- package/dist/cypress/index.d.ts +141 -0
- package/dist/cypress/index.js +897 -0
- package/dist/index.d.ts +789 -15
- package/dist/index.js +2414 -422
- package/dist/react/index.d.ts +789 -15
- package/dist/react/index.js +2414 -422
- package/package.json +65 -33
package/dist/react/index.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
1
8
|
// src/components/Form.tsx
|
|
2
|
-
import
|
|
3
|
-
import { Button as
|
|
9
|
+
import React16 from "react";
|
|
10
|
+
import { Button as Button3 } from "@heroui/react";
|
|
4
11
|
|
|
5
12
|
// src/hooks/useFormHelper.ts
|
|
6
13
|
import { useState } from "react";
|
|
@@ -68,8 +75,8 @@ function useFormHelper({
|
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
// src/components/FormField.tsx
|
|
71
|
-
import
|
|
72
|
-
import { useWatch } from "react-hook-form";
|
|
78
|
+
import React15 from "react";
|
|
79
|
+
import { useWatch as useWatch3 } from "react-hook-form";
|
|
73
80
|
|
|
74
81
|
// src/fields/CheckboxField.tsx
|
|
75
82
|
import React2 from "react";
|
|
@@ -196,16 +203,16 @@ function CheckboxField(props) {
|
|
|
196
203
|
{
|
|
197
204
|
control,
|
|
198
205
|
name,
|
|
199
|
-
render: ({ field, fieldState }) => /* @__PURE__ */ React2.createElement("div", { className }, /* @__PURE__ */ React2.createElement(
|
|
206
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React2.createElement("div", { className }, /* @__PURE__ */ React2.createElement(
|
|
200
207
|
Checkbox,
|
|
201
208
|
{
|
|
202
209
|
...defaults.checkbox,
|
|
203
210
|
...checkboxProps,
|
|
204
211
|
isDisabled,
|
|
205
212
|
isInvalid: Boolean(fieldState.error),
|
|
206
|
-
isSelected: Boolean(
|
|
207
|
-
onBlur:
|
|
208
|
-
onValueChange: (val) =>
|
|
213
|
+
isSelected: Boolean(field2.value),
|
|
214
|
+
onBlur: field2.onBlur,
|
|
215
|
+
onValueChange: (val) => field2.onChange(val)
|
|
209
216
|
},
|
|
210
217
|
label
|
|
211
218
|
), description ? /* @__PURE__ */ React2.createElement("p", { className: "text-small text-default-400" }, description) : null, fieldState.error?.message ? /* @__PURE__ */ React2.createElement("p", { className: "text-tiny text-danger mt-1" }, fieldState.error.message) : null),
|
|
@@ -214,13 +221,43 @@ function CheckboxField(props) {
|
|
|
214
221
|
);
|
|
215
222
|
}
|
|
216
223
|
|
|
217
|
-
// src/fields/
|
|
224
|
+
// src/fields/ConditionalField.tsx
|
|
218
225
|
import React3 from "react";
|
|
226
|
+
import { useWatch, useFormContext } from "react-hook-form";
|
|
227
|
+
function ConditionalField({
|
|
228
|
+
className,
|
|
229
|
+
config,
|
|
230
|
+
control
|
|
231
|
+
}) {
|
|
232
|
+
const { condition, field: field2 } = config;
|
|
233
|
+
const form = useFormContext();
|
|
234
|
+
const formValues = useWatch({ control });
|
|
235
|
+
const shouldShow = condition(formValues);
|
|
236
|
+
if (!shouldShow) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
return /* @__PURE__ */ React3.createElement("div", { className }, /* @__PURE__ */ React3.createElement(
|
|
240
|
+
FormField,
|
|
241
|
+
{
|
|
242
|
+
config: field2,
|
|
243
|
+
form,
|
|
244
|
+
submissionState: {
|
|
245
|
+
error: void 0,
|
|
246
|
+
isSubmitted: false,
|
|
247
|
+
isSubmitting: false,
|
|
248
|
+
isSuccess: false
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// src/fields/DateField.tsx
|
|
255
|
+
import React4 from "react";
|
|
219
256
|
import { Controller as Controller2 } from "react-hook-form";
|
|
220
257
|
function CoercedDateInput(props) {
|
|
221
|
-
const { dateProps, description, disabled, errorMessage, field, label } = props;
|
|
258
|
+
const { dateProps, description, disabled, errorMessage, field: field2, label } = props;
|
|
222
259
|
const defaults = useHeroHookFormDefaults();
|
|
223
|
-
return /* @__PURE__ */
|
|
260
|
+
return /* @__PURE__ */ React4.createElement(
|
|
224
261
|
DateInput,
|
|
225
262
|
{
|
|
226
263
|
...defaults.dateInput,
|
|
@@ -230,9 +267,9 @@ function CoercedDateInput(props) {
|
|
|
230
267
|
isDisabled: disabled,
|
|
231
268
|
isInvalid: Boolean(errorMessage),
|
|
232
269
|
label,
|
|
233
|
-
value:
|
|
234
|
-
onBlur:
|
|
235
|
-
onChange:
|
|
270
|
+
value: field2.value ?? null,
|
|
271
|
+
onBlur: field2.onBlur,
|
|
272
|
+
onChange: field2.onChange
|
|
236
273
|
}
|
|
237
274
|
);
|
|
238
275
|
}
|
|
@@ -248,12 +285,12 @@ function DateField(props) {
|
|
|
248
285
|
rules,
|
|
249
286
|
transform
|
|
250
287
|
} = props;
|
|
251
|
-
return /* @__PURE__ */
|
|
288
|
+
return /* @__PURE__ */ React4.createElement(
|
|
252
289
|
Controller2,
|
|
253
290
|
{
|
|
254
291
|
control,
|
|
255
292
|
name,
|
|
256
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
293
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React4.createElement("div", { className }, /* @__PURE__ */ React4.createElement(
|
|
257
294
|
CoercedDateInput,
|
|
258
295
|
{
|
|
259
296
|
dateProps,
|
|
@@ -261,8 +298,8 @@ function DateField(props) {
|
|
|
261
298
|
disabled: isDisabled,
|
|
262
299
|
errorMessage: fieldState.error?.message,
|
|
263
300
|
field: {
|
|
264
|
-
...
|
|
265
|
-
onChange: (value) =>
|
|
301
|
+
...field2,
|
|
302
|
+
onChange: (value) => field2.onChange(transform ? transform(value) : value)
|
|
266
303
|
},
|
|
267
304
|
label
|
|
268
305
|
}
|
|
@@ -272,8 +309,144 @@ function DateField(props) {
|
|
|
272
309
|
);
|
|
273
310
|
}
|
|
274
311
|
|
|
312
|
+
// src/fields/DynamicSectionField.tsx
|
|
313
|
+
import React5 from "react";
|
|
314
|
+
import { useWatch as useWatch2, useFormContext as useFormContext2 } from "react-hook-form";
|
|
315
|
+
function DynamicSectionField({
|
|
316
|
+
className,
|
|
317
|
+
config,
|
|
318
|
+
control
|
|
319
|
+
}) {
|
|
320
|
+
const { condition, description, fields, title } = config;
|
|
321
|
+
const form = useFormContext2();
|
|
322
|
+
const formValues = useWatch2({ control });
|
|
323
|
+
const shouldShow = condition(formValues);
|
|
324
|
+
if (!shouldShow) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
return /* @__PURE__ */ React5.createElement("div", { className }, (title || description) && /* @__PURE__ */ React5.createElement("div", { className: "mb-6" }, title && /* @__PURE__ */ React5.createElement("h3", { className: "text-lg font-semibold text-gray-900 mb-2" }, title), description && /* @__PURE__ */ React5.createElement("p", { className: "text-sm text-gray-600" }, description)), /* @__PURE__ */ React5.createElement("div", { className: "space-y-4" }, fields.map((fieldConfig, index) => /* @__PURE__ */ React5.createElement(
|
|
328
|
+
FormField,
|
|
329
|
+
{
|
|
330
|
+
key: `${fieldConfig.name}-${index}`,
|
|
331
|
+
config: fieldConfig,
|
|
332
|
+
form,
|
|
333
|
+
submissionState: {
|
|
334
|
+
error: void 0,
|
|
335
|
+
isSubmitted: false,
|
|
336
|
+
isSubmitting: false,
|
|
337
|
+
isSuccess: false
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
))));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/fields/FieldArrayField.tsx
|
|
344
|
+
import React6 from "react";
|
|
345
|
+
import { useFieldArray, useFormContext as useFormContext3 } from "react-hook-form";
|
|
346
|
+
import { Button as Button2 } from "@heroui/react";
|
|
347
|
+
function FieldArrayField({
|
|
348
|
+
className,
|
|
349
|
+
config
|
|
350
|
+
}) {
|
|
351
|
+
const {
|
|
352
|
+
addButtonText = "Add Item",
|
|
353
|
+
fields: fieldConfigs,
|
|
354
|
+
max = 10,
|
|
355
|
+
min = 0,
|
|
356
|
+
name,
|
|
357
|
+
removeButtonText = "Remove"
|
|
358
|
+
} = config;
|
|
359
|
+
const form = useFormContext3();
|
|
360
|
+
if (!form || !form.control) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
const { control } = form;
|
|
364
|
+
const { append, fields, remove } = useFieldArray({
|
|
365
|
+
control,
|
|
366
|
+
name
|
|
367
|
+
// FieldArray name
|
|
368
|
+
});
|
|
369
|
+
const canAdd = fields.length < max;
|
|
370
|
+
const canRemove = fields.length > min;
|
|
371
|
+
const handleAdd = () => {
|
|
372
|
+
if (canAdd) {
|
|
373
|
+
const defaultValues = fieldConfigs.reduce((acc, fieldConfig) => {
|
|
374
|
+
const fieldName = fieldConfig.name;
|
|
375
|
+
if (fieldConfig.type === "checkbox" || fieldConfig.type === "switch") {
|
|
376
|
+
acc[fieldName] = false;
|
|
377
|
+
} else if (fieldConfig.type === "slider") {
|
|
378
|
+
acc[fieldName] = 0;
|
|
379
|
+
} else {
|
|
380
|
+
acc[fieldName] = "";
|
|
381
|
+
}
|
|
382
|
+
return acc;
|
|
383
|
+
}, {});
|
|
384
|
+
append(defaultValues);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
const handleRemove = (index) => {
|
|
388
|
+
if (canRemove) {
|
|
389
|
+
remove(index);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
return /* @__PURE__ */ React6.createElement("div", { className }, /* @__PURE__ */ React6.createElement("div", { className: "space-y-4" }, fields.map((field2, index) => /* @__PURE__ */ React6.createElement(
|
|
393
|
+
"div",
|
|
394
|
+
{
|
|
395
|
+
key: field2.id,
|
|
396
|
+
className: "border border-gray-200 rounded-lg p-4 space-y-4"
|
|
397
|
+
},
|
|
398
|
+
/* @__PURE__ */ React6.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React6.createElement("h4", { className: "text-sm font-medium text-gray-700" }, config.label, " #", index + 1), canRemove && /* @__PURE__ */ React6.createElement(
|
|
399
|
+
Button2,
|
|
400
|
+
{
|
|
401
|
+
size: "sm",
|
|
402
|
+
variant: "light",
|
|
403
|
+
color: "danger",
|
|
404
|
+
startContent: "\u{1F5D1}\uFE0F",
|
|
405
|
+
onPress: () => handleRemove(index),
|
|
406
|
+
"aria-label": `${removeButtonText} ${config.label} ${index + 1}`
|
|
407
|
+
},
|
|
408
|
+
removeButtonText
|
|
409
|
+
)),
|
|
410
|
+
/* @__PURE__ */ React6.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, fieldConfigs.map((fieldConfig) => /* @__PURE__ */ React6.createElement(
|
|
411
|
+
FormField,
|
|
412
|
+
{
|
|
413
|
+
key: `${fieldConfig.name}-${index}`,
|
|
414
|
+
config: {
|
|
415
|
+
...fieldConfig,
|
|
416
|
+
name: `${name}.${index}.${fieldConfig.name}`
|
|
417
|
+
},
|
|
418
|
+
form,
|
|
419
|
+
submissionState: {
|
|
420
|
+
error: void 0,
|
|
421
|
+
isSubmitted: false,
|
|
422
|
+
isSubmitting: false,
|
|
423
|
+
isSuccess: false
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
)))
|
|
427
|
+
)), canAdd && /* @__PURE__ */ React6.createElement(
|
|
428
|
+
Button2,
|
|
429
|
+
{
|
|
430
|
+
variant: "bordered",
|
|
431
|
+
startContent: "\u2795",
|
|
432
|
+
onPress: handleAdd,
|
|
433
|
+
className: "w-full"
|
|
434
|
+
},
|
|
435
|
+
addButtonText
|
|
436
|
+
), fields.length === 0 && /* @__PURE__ */ React6.createElement("div", { className: "text-center py-8 text-gray-500" }, /* @__PURE__ */ React6.createElement("p", null, "No ", config.label?.toLowerCase(), " added yet."), /* @__PURE__ */ React6.createElement(
|
|
437
|
+
Button2,
|
|
438
|
+
{
|
|
439
|
+
variant: "bordered",
|
|
440
|
+
startContent: "\u2795",
|
|
441
|
+
onPress: handleAdd,
|
|
442
|
+
className: "mt-2"
|
|
443
|
+
},
|
|
444
|
+
addButtonText
|
|
445
|
+
))));
|
|
446
|
+
}
|
|
447
|
+
|
|
275
448
|
// src/fields/FileField.tsx
|
|
276
|
-
import
|
|
449
|
+
import React7 from "react";
|
|
277
450
|
import { Controller as Controller3 } from "react-hook-form";
|
|
278
451
|
function CoercedFileInput(props) {
|
|
279
452
|
const {
|
|
@@ -281,13 +454,13 @@ function CoercedFileInput(props) {
|
|
|
281
454
|
description,
|
|
282
455
|
disabled,
|
|
283
456
|
errorMessage,
|
|
284
|
-
field,
|
|
457
|
+
field: field2,
|
|
285
458
|
fileProps,
|
|
286
459
|
label,
|
|
287
460
|
multiple
|
|
288
461
|
} = props;
|
|
289
462
|
const defaults = useHeroHookFormDefaults();
|
|
290
|
-
return /* @__PURE__ */
|
|
463
|
+
return /* @__PURE__ */ React7.createElement(
|
|
291
464
|
Input,
|
|
292
465
|
{
|
|
293
466
|
...defaults.input,
|
|
@@ -300,11 +473,11 @@ function CoercedFileInput(props) {
|
|
|
300
473
|
label,
|
|
301
474
|
multiple,
|
|
302
475
|
type: "file",
|
|
303
|
-
value:
|
|
304
|
-
onBlur:
|
|
476
|
+
value: field2.value ? "" : "",
|
|
477
|
+
onBlur: field2.onBlur,
|
|
305
478
|
onChange: (e) => {
|
|
306
479
|
const target = e.target;
|
|
307
|
-
|
|
480
|
+
field2.onChange(target.files);
|
|
308
481
|
}
|
|
309
482
|
}
|
|
310
483
|
);
|
|
@@ -323,12 +496,12 @@ function FileField(props) {
|
|
|
323
496
|
rules,
|
|
324
497
|
transform
|
|
325
498
|
} = props;
|
|
326
|
-
return /* @__PURE__ */
|
|
499
|
+
return /* @__PURE__ */ React7.createElement(
|
|
327
500
|
Controller3,
|
|
328
501
|
{
|
|
329
502
|
control,
|
|
330
503
|
name,
|
|
331
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
504
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React7.createElement("div", { className }, /* @__PURE__ */ React7.createElement(
|
|
332
505
|
CoercedFileInput,
|
|
333
506
|
{
|
|
334
507
|
accept,
|
|
@@ -336,8 +509,8 @@ function FileField(props) {
|
|
|
336
509
|
disabled: isDisabled,
|
|
337
510
|
errorMessage: fieldState.error?.message,
|
|
338
511
|
field: {
|
|
339
|
-
...
|
|
340
|
-
onChange: (value) =>
|
|
512
|
+
...field2,
|
|
513
|
+
onChange: (value) => field2.onChange(transform ? transform(value) : value)
|
|
341
514
|
},
|
|
342
515
|
fileProps,
|
|
343
516
|
label,
|
|
@@ -350,7 +523,7 @@ function FileField(props) {
|
|
|
350
523
|
}
|
|
351
524
|
|
|
352
525
|
// src/fields/FontPickerField.tsx
|
|
353
|
-
import
|
|
526
|
+
import React8 from "react";
|
|
354
527
|
import { Controller as Controller4 } from "react-hook-form";
|
|
355
528
|
var FontPickerComponent = null;
|
|
356
529
|
var fontPickerLoaded = false;
|
|
@@ -367,17 +540,17 @@ function FontPickerField(props) {
|
|
|
367
540
|
name,
|
|
368
541
|
rules
|
|
369
542
|
} = props;
|
|
370
|
-
const [fontPickerState, setFontPickerState] =
|
|
543
|
+
const [fontPickerState, setFontPickerState] = React8.useState({
|
|
371
544
|
component: FontPickerComponent,
|
|
372
|
-
|
|
373
|
-
|
|
545
|
+
error: null,
|
|
546
|
+
loading: false
|
|
374
547
|
});
|
|
375
|
-
|
|
548
|
+
React8.useEffect(() => {
|
|
376
549
|
if (fontPickerLoaded && FontPickerComponent) {
|
|
377
550
|
setFontPickerState({
|
|
378
551
|
component: FontPickerComponent,
|
|
379
|
-
|
|
380
|
-
|
|
552
|
+
error: null,
|
|
553
|
+
loading: false
|
|
381
554
|
});
|
|
382
555
|
return;
|
|
383
556
|
}
|
|
@@ -387,14 +560,14 @@ function FontPickerField(props) {
|
|
|
387
560
|
if (fontPickerLoaded && FontPickerComponent) {
|
|
388
561
|
setFontPickerState({
|
|
389
562
|
component: FontPickerComponent,
|
|
390
|
-
|
|
391
|
-
|
|
563
|
+
error: null,
|
|
564
|
+
loading: false
|
|
392
565
|
});
|
|
393
566
|
} else {
|
|
394
567
|
setFontPickerState({
|
|
395
568
|
component: null,
|
|
396
|
-
|
|
397
|
-
|
|
569
|
+
error: "Font picker package not found",
|
|
570
|
+
loading: false
|
|
398
571
|
});
|
|
399
572
|
}
|
|
400
573
|
};
|
|
@@ -411,17 +584,17 @@ function FontPickerField(props) {
|
|
|
411
584
|
fontPickerLoading = false;
|
|
412
585
|
setFontPickerState({
|
|
413
586
|
component: FontPickerComponent,
|
|
414
|
-
|
|
415
|
-
|
|
587
|
+
error: null,
|
|
588
|
+
loading: false
|
|
416
589
|
});
|
|
417
590
|
loadingCallbacks.forEach((callback) => callback());
|
|
418
591
|
loadingCallbacks.length = 0;
|
|
419
|
-
} catch
|
|
592
|
+
} catch {
|
|
420
593
|
fontPickerLoading = false;
|
|
421
594
|
setFontPickerState({
|
|
422
595
|
component: null,
|
|
423
|
-
|
|
424
|
-
|
|
596
|
+
error: "Font picker package not found",
|
|
597
|
+
loading: false
|
|
425
598
|
});
|
|
426
599
|
loadingCallbacks.forEach((callback) => callback());
|
|
427
600
|
loadingCallbacks.length = 0;
|
|
@@ -430,23 +603,23 @@ function FontPickerField(props) {
|
|
|
430
603
|
void loadFontPicker();
|
|
431
604
|
}, []);
|
|
432
605
|
if (fontPickerState.loading) {
|
|
433
|
-
return /* @__PURE__ */
|
|
606
|
+
return /* @__PURE__ */ React8.createElement("div", { className }, /* @__PURE__ */ React8.createElement("div", { className: "space-y-2" }, label && /* @__PURE__ */ React8.createElement("label", { className: "block text-sm font-medium text-foreground" }, label), description && /* @__PURE__ */ React8.createElement("p", { className: "text-sm text-muted-foreground" }, description), /* @__PURE__ */ React8.createElement("div", { className: "p-4 border border-default-200 bg-default-50 rounded-medium" }, /* @__PURE__ */ React8.createElement("p", { className: "text-default-600 text-sm" }, "Loading font picker..."))));
|
|
434
607
|
}
|
|
435
608
|
if (!fontPickerState.component) {
|
|
436
|
-
return /* @__PURE__ */
|
|
609
|
+
return /* @__PURE__ */ React8.createElement("div", { className }, /* @__PURE__ */ React8.createElement("div", { className: "space-y-2" }, label && /* @__PURE__ */ React8.createElement("label", { className: "block text-sm font-medium text-foreground" }, label), description && /* @__PURE__ */ React8.createElement("p", { className: "text-sm text-muted-foreground" }, description), /* @__PURE__ */ React8.createElement("div", { className: "p-4 border border-warning-200 bg-warning-50 rounded-medium" }, /* @__PURE__ */ React8.createElement("p", { className: "text-warning-800 text-sm" }, "Font picker requires the @rachelallyson/heroui-font-picker package. Please install it as a peer dependency for advanced font selection features."))));
|
|
437
610
|
}
|
|
438
|
-
return /* @__PURE__ */
|
|
611
|
+
return /* @__PURE__ */ React8.createElement(
|
|
439
612
|
Controller4,
|
|
440
613
|
{
|
|
441
614
|
control,
|
|
442
615
|
name,
|
|
443
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
616
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React8.createElement(
|
|
444
617
|
fontPickerState.component,
|
|
445
618
|
{
|
|
446
619
|
label,
|
|
447
620
|
description,
|
|
448
|
-
value:
|
|
449
|
-
onSelectionChange: (value) =>
|
|
621
|
+
value: field2.value ?? "",
|
|
622
|
+
onSelectionChange: (value) => field2.onChange(value),
|
|
450
623
|
errorMessage: fieldState.error?.message,
|
|
451
624
|
isDisabled,
|
|
452
625
|
className,
|
|
@@ -459,12 +632,12 @@ function FontPickerField(props) {
|
|
|
459
632
|
}
|
|
460
633
|
|
|
461
634
|
// src/fields/InputField.tsx
|
|
462
|
-
import
|
|
635
|
+
import React9 from "react";
|
|
463
636
|
import { Controller as Controller5 } from "react-hook-form";
|
|
464
637
|
function CoercedInput(props) {
|
|
465
|
-
const { description, disabled, errorMessage, field, inputProps, label } = props;
|
|
638
|
+
const { description, disabled, errorMessage, field: field2, inputProps, label } = props;
|
|
466
639
|
const defaults = useHeroHookFormDefaults();
|
|
467
|
-
return /* @__PURE__ */
|
|
640
|
+
return /* @__PURE__ */ React9.createElement(
|
|
468
641
|
Input,
|
|
469
642
|
{
|
|
470
643
|
...defaults.input,
|
|
@@ -474,59 +647,61 @@ function CoercedInput(props) {
|
|
|
474
647
|
isDisabled: disabled,
|
|
475
648
|
isInvalid: Boolean(errorMessage),
|
|
476
649
|
label,
|
|
477
|
-
value:
|
|
478
|
-
onBlur:
|
|
479
|
-
onValueChange:
|
|
650
|
+
value: field2.value ?? "",
|
|
651
|
+
onBlur: field2.onBlur,
|
|
652
|
+
onValueChange: field2.onChange
|
|
480
653
|
}
|
|
481
654
|
);
|
|
482
655
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
description,
|
|
488
|
-
inputProps,
|
|
489
|
-
isDisabled,
|
|
490
|
-
label,
|
|
491
|
-
name,
|
|
492
|
-
rules,
|
|
493
|
-
transform
|
|
494
|
-
} = props;
|
|
495
|
-
return /* @__PURE__ */ React6.createElement(
|
|
496
|
-
Controller5,
|
|
497
|
-
{
|
|
656
|
+
var InputField = React9.memo(
|
|
657
|
+
(props) => {
|
|
658
|
+
const {
|
|
659
|
+
className,
|
|
498
660
|
control,
|
|
661
|
+
description,
|
|
662
|
+
inputProps,
|
|
663
|
+
isDisabled,
|
|
664
|
+
label,
|
|
499
665
|
name,
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
666
|
+
rules,
|
|
667
|
+
transform
|
|
668
|
+
} = props;
|
|
669
|
+
return /* @__PURE__ */ React9.createElement(
|
|
670
|
+
Controller5,
|
|
671
|
+
{
|
|
672
|
+
control,
|
|
673
|
+
name,
|
|
674
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React9.createElement("div", { className }, /* @__PURE__ */ React9.createElement(
|
|
675
|
+
CoercedInput,
|
|
676
|
+
{
|
|
677
|
+
description,
|
|
678
|
+
disabled: isDisabled,
|
|
679
|
+
errorMessage: fieldState.error?.message,
|
|
680
|
+
field: {
|
|
681
|
+
...field2,
|
|
682
|
+
onChange: (value) => {
|
|
683
|
+
if (inputProps?.type === "number") {
|
|
684
|
+
const numValue = value === "" ? void 0 : Number(value);
|
|
685
|
+
field2.onChange(
|
|
686
|
+
transform ? transform(String(numValue)) : numValue
|
|
687
|
+
);
|
|
688
|
+
} else {
|
|
689
|
+
field2.onChange(transform ? transform(value) : value);
|
|
690
|
+
}
|
|
516
691
|
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
692
|
+
},
|
|
693
|
+
inputProps,
|
|
694
|
+
label
|
|
695
|
+
}
|
|
696
|
+
)),
|
|
697
|
+
rules
|
|
698
|
+
}
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
);
|
|
527
702
|
|
|
528
703
|
// src/fields/RadioGroupField.tsx
|
|
529
|
-
import
|
|
704
|
+
import React10 from "react";
|
|
530
705
|
import { Controller as Controller6 } from "react-hook-form";
|
|
531
706
|
function RadioGroupField(props) {
|
|
532
707
|
const {
|
|
@@ -541,12 +716,12 @@ function RadioGroupField(props) {
|
|
|
541
716
|
rules
|
|
542
717
|
} = props;
|
|
543
718
|
const defaults = useHeroHookFormDefaults();
|
|
544
|
-
return /* @__PURE__ */
|
|
719
|
+
return /* @__PURE__ */ React10.createElement(
|
|
545
720
|
Controller6,
|
|
546
721
|
{
|
|
547
722
|
control,
|
|
548
723
|
name,
|
|
549
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
724
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React10.createElement("div", { className }, /* @__PURE__ */ React10.createElement(
|
|
550
725
|
RadioGroup,
|
|
551
726
|
{
|
|
552
727
|
...defaults.radioGroup,
|
|
@@ -555,11 +730,11 @@ function RadioGroupField(props) {
|
|
|
555
730
|
isDisabled,
|
|
556
731
|
isInvalid: Boolean(fieldState.error),
|
|
557
732
|
label,
|
|
558
|
-
value: String(
|
|
559
|
-
onBlur:
|
|
560
|
-
onValueChange: (val) =>
|
|
733
|
+
value: String(field2.value ?? ""),
|
|
734
|
+
onBlur: field2.onBlur,
|
|
735
|
+
onValueChange: (val) => field2.onChange(val)
|
|
561
736
|
},
|
|
562
|
-
options.map((opt) => /* @__PURE__ */
|
|
737
|
+
options.map((opt) => /* @__PURE__ */ React10.createElement(
|
|
563
738
|
Radio,
|
|
564
739
|
{
|
|
565
740
|
key: String(opt.value),
|
|
@@ -568,14 +743,14 @@ function RadioGroupField(props) {
|
|
|
568
743
|
},
|
|
569
744
|
opt.label
|
|
570
745
|
))
|
|
571
|
-
), fieldState.error?.message ? /* @__PURE__ */
|
|
746
|
+
), fieldState.error?.message ? /* @__PURE__ */ React10.createElement("p", { className: "text-tiny text-danger mt-1" }, fieldState.error.message) : null),
|
|
572
747
|
rules
|
|
573
748
|
}
|
|
574
749
|
);
|
|
575
750
|
}
|
|
576
751
|
|
|
577
752
|
// src/fields/SelectField.tsx
|
|
578
|
-
import
|
|
753
|
+
import React11 from "react";
|
|
579
754
|
import { Controller as Controller7 } from "react-hook-form";
|
|
580
755
|
function SelectField(props) {
|
|
581
756
|
const {
|
|
@@ -591,14 +766,14 @@ function SelectField(props) {
|
|
|
591
766
|
selectProps
|
|
592
767
|
} = props;
|
|
593
768
|
const defaults = useHeroHookFormDefaults();
|
|
594
|
-
return /* @__PURE__ */
|
|
769
|
+
return /* @__PURE__ */ React11.createElement(
|
|
595
770
|
Controller7,
|
|
596
771
|
{
|
|
597
772
|
control,
|
|
598
773
|
name,
|
|
599
|
-
render: ({ field, fieldState }) => {
|
|
600
|
-
const selectedKey =
|
|
601
|
-
return /* @__PURE__ */
|
|
774
|
+
render: ({ field: field2, fieldState }) => {
|
|
775
|
+
const selectedKey = field2.value;
|
|
776
|
+
return /* @__PURE__ */ React11.createElement("div", { className }, /* @__PURE__ */ React11.createElement(
|
|
602
777
|
Select,
|
|
603
778
|
{
|
|
604
779
|
...defaults.select,
|
|
@@ -613,10 +788,10 @@ function SelectField(props) {
|
|
|
613
788
|
onSelectionChange: (keys) => {
|
|
614
789
|
const keyArray = Array.from(keys);
|
|
615
790
|
const next = keyArray[0] ?? "";
|
|
616
|
-
|
|
791
|
+
field2.onChange(next);
|
|
617
792
|
}
|
|
618
793
|
},
|
|
619
|
-
options.map((opt) => /* @__PURE__ */
|
|
794
|
+
options.map((opt) => /* @__PURE__ */ React11.createElement(
|
|
620
795
|
SelectItem,
|
|
621
796
|
{
|
|
622
797
|
key: String(opt.value),
|
|
@@ -633,12 +808,12 @@ function SelectField(props) {
|
|
|
633
808
|
}
|
|
634
809
|
|
|
635
810
|
// src/fields/SliderField.tsx
|
|
636
|
-
import
|
|
811
|
+
import React12 from "react";
|
|
637
812
|
import { Controller as Controller8 } from "react-hook-form";
|
|
638
813
|
function CoercedSlider(props) {
|
|
639
|
-
const { description, disabled, errorMessage, field, label, sliderProps } = props;
|
|
814
|
+
const { description, disabled, errorMessage, field: field2, label, sliderProps } = props;
|
|
640
815
|
const defaults = useHeroHookFormDefaults();
|
|
641
|
-
return /* @__PURE__ */
|
|
816
|
+
return /* @__PURE__ */ React12.createElement(
|
|
642
817
|
Slider,
|
|
643
818
|
{
|
|
644
819
|
...defaults.slider,
|
|
@@ -648,9 +823,9 @@ function CoercedSlider(props) {
|
|
|
648
823
|
isDisabled: disabled,
|
|
649
824
|
isInvalid: Boolean(errorMessage),
|
|
650
825
|
label,
|
|
651
|
-
value:
|
|
652
|
-
onBlur:
|
|
653
|
-
onValueChange:
|
|
826
|
+
value: field2.value ?? 0,
|
|
827
|
+
onBlur: field2.onBlur,
|
|
828
|
+
onValueChange: field2.onChange
|
|
654
829
|
}
|
|
655
830
|
);
|
|
656
831
|
}
|
|
@@ -666,20 +841,20 @@ function SliderField(props) {
|
|
|
666
841
|
sliderProps,
|
|
667
842
|
transform
|
|
668
843
|
} = props;
|
|
669
|
-
return /* @__PURE__ */
|
|
844
|
+
return /* @__PURE__ */ React12.createElement(
|
|
670
845
|
Controller8,
|
|
671
846
|
{
|
|
672
847
|
control,
|
|
673
848
|
name,
|
|
674
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
849
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React12.createElement("div", { className }, /* @__PURE__ */ React12.createElement(
|
|
675
850
|
CoercedSlider,
|
|
676
851
|
{
|
|
677
852
|
description,
|
|
678
853
|
disabled: isDisabled,
|
|
679
854
|
errorMessage: fieldState.error?.message,
|
|
680
855
|
field: {
|
|
681
|
-
...
|
|
682
|
-
onChange: (value) =>
|
|
856
|
+
...field2,
|
|
857
|
+
onChange: (value) => field2.onChange(transform ? transform(value) : value)
|
|
683
858
|
},
|
|
684
859
|
label,
|
|
685
860
|
sliderProps
|
|
@@ -691,7 +866,7 @@ function SliderField(props) {
|
|
|
691
866
|
}
|
|
692
867
|
|
|
693
868
|
// src/fields/SwitchField.tsx
|
|
694
|
-
import
|
|
869
|
+
import React13 from "react";
|
|
695
870
|
import { Controller as Controller9 } from "react-hook-form";
|
|
696
871
|
function SwitchField(props) {
|
|
697
872
|
const {
|
|
@@ -705,30 +880,30 @@ function SwitchField(props) {
|
|
|
705
880
|
switchProps
|
|
706
881
|
} = props;
|
|
707
882
|
const defaults = useHeroHookFormDefaults();
|
|
708
|
-
return /* @__PURE__ */
|
|
883
|
+
return /* @__PURE__ */ React13.createElement(
|
|
709
884
|
Controller9,
|
|
710
885
|
{
|
|
711
886
|
control,
|
|
712
887
|
name,
|
|
713
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
888
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React13.createElement("div", { className }, /* @__PURE__ */ React13.createElement(
|
|
714
889
|
Switch,
|
|
715
890
|
{
|
|
716
891
|
...defaults.switch,
|
|
717
892
|
...switchProps,
|
|
718
893
|
isDisabled,
|
|
719
|
-
isSelected: Boolean(
|
|
720
|
-
onBlur:
|
|
721
|
-
onValueChange: (val) =>
|
|
894
|
+
isSelected: Boolean(field2.value),
|
|
895
|
+
onBlur: field2.onBlur,
|
|
896
|
+
onValueChange: (val) => field2.onChange(val)
|
|
722
897
|
},
|
|
723
898
|
label
|
|
724
|
-
), description ? /* @__PURE__ */
|
|
899
|
+
), description ? /* @__PURE__ */ React13.createElement("p", { className: "text-small text-default-400" }, description) : null, fieldState.error?.message ? /* @__PURE__ */ React13.createElement("p", { className: "text-tiny text-danger mt-1" }, fieldState.error.message) : null),
|
|
725
900
|
rules
|
|
726
901
|
}
|
|
727
902
|
);
|
|
728
903
|
}
|
|
729
904
|
|
|
730
905
|
// src/fields/TextareaField.tsx
|
|
731
|
-
import
|
|
906
|
+
import React14 from "react";
|
|
732
907
|
import { Controller as Controller10 } from "react-hook-form";
|
|
733
908
|
function TextareaField(props) {
|
|
734
909
|
const {
|
|
@@ -742,12 +917,12 @@ function TextareaField(props) {
|
|
|
742
917
|
textareaProps
|
|
743
918
|
} = props;
|
|
744
919
|
const defaults = useHeroHookFormDefaults();
|
|
745
|
-
return /* @__PURE__ */
|
|
920
|
+
return /* @__PURE__ */ React14.createElement(
|
|
746
921
|
Controller10,
|
|
747
922
|
{
|
|
748
923
|
control,
|
|
749
924
|
name,
|
|
750
|
-
render: ({ field, fieldState }) => /* @__PURE__ */
|
|
925
|
+
render: ({ field: field2, fieldState }) => /* @__PURE__ */ React14.createElement("div", { className }, /* @__PURE__ */ React14.createElement(
|
|
751
926
|
Textarea,
|
|
752
927
|
{
|
|
753
928
|
...defaults.textarea,
|
|
@@ -757,9 +932,9 @@ function TextareaField(props) {
|
|
|
757
932
|
isDisabled,
|
|
758
933
|
isInvalid: Boolean(fieldState.error),
|
|
759
934
|
label,
|
|
760
|
-
value:
|
|
761
|
-
onBlur:
|
|
762
|
-
onValueChange:
|
|
935
|
+
value: field2.value ?? "",
|
|
936
|
+
onBlur: field2.onBlur,
|
|
937
|
+
onValueChange: field2.onChange
|
|
763
938
|
}
|
|
764
939
|
)),
|
|
765
940
|
rules
|
|
@@ -768,158 +943,189 @@ function TextareaField(props) {
|
|
|
768
943
|
}
|
|
769
944
|
|
|
770
945
|
// src/components/FormField.tsx
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
if (config.condition && !config.condition(watchedValues)) {
|
|
779
|
-
return null;
|
|
780
|
-
}
|
|
781
|
-
if (config.dependsOn) {
|
|
782
|
-
const dependentValue = watchedValues[config.dependsOn];
|
|
783
|
-
if (config.dependsOnValue !== void 0 && dependentValue !== config.dependsOnValue) {
|
|
946
|
+
var FormField = React15.memo(
|
|
947
|
+
({
|
|
948
|
+
config,
|
|
949
|
+
form,
|
|
950
|
+
submissionState
|
|
951
|
+
}) => {
|
|
952
|
+
if (!form || !form.control) {
|
|
784
953
|
return null;
|
|
785
954
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
ariaLabel: config.ariaLabel,
|
|
790
|
-
className: config.className,
|
|
791
|
-
description: config.description,
|
|
792
|
-
isDisabled: config.isDisabled ?? submissionState.isSubmitting,
|
|
793
|
-
label: config.label,
|
|
794
|
-
name: config.name,
|
|
795
|
-
rules: config.rules
|
|
796
|
-
};
|
|
797
|
-
switch (config.type) {
|
|
798
|
-
case "input":
|
|
799
|
-
return /* @__PURE__ */ React12.createElement(
|
|
800
|
-
InputField,
|
|
801
|
-
{
|
|
802
|
-
...baseProps,
|
|
803
|
-
control,
|
|
804
|
-
defaultValue: config.defaultValue,
|
|
805
|
-
inputProps: config.inputProps
|
|
806
|
-
}
|
|
807
|
-
);
|
|
808
|
-
case "textarea":
|
|
809
|
-
return /* @__PURE__ */ React12.createElement(
|
|
810
|
-
TextareaField,
|
|
811
|
-
{
|
|
812
|
-
...baseProps,
|
|
813
|
-
control,
|
|
814
|
-
defaultValue: config.defaultValue,
|
|
815
|
-
textareaProps: config.textareaProps
|
|
816
|
-
}
|
|
817
|
-
);
|
|
818
|
-
case "select":
|
|
819
|
-
return /* @__PURE__ */ React12.createElement(
|
|
820
|
-
SelectField,
|
|
821
|
-
{
|
|
822
|
-
...baseProps,
|
|
823
|
-
control,
|
|
824
|
-
defaultValue: config.defaultValue,
|
|
825
|
-
options: (config.options ?? []).map((opt) => ({
|
|
826
|
-
label: opt.label,
|
|
827
|
-
value: String(opt.value)
|
|
828
|
-
})),
|
|
829
|
-
selectProps: config.selectProps
|
|
830
|
-
}
|
|
831
|
-
);
|
|
832
|
-
case "checkbox":
|
|
833
|
-
return /* @__PURE__ */ React12.createElement(
|
|
834
|
-
CheckboxField,
|
|
835
|
-
{
|
|
836
|
-
...baseProps,
|
|
837
|
-
checkboxProps: config.checkboxProps,
|
|
838
|
-
control,
|
|
839
|
-
defaultValue: config.defaultValue
|
|
840
|
-
}
|
|
841
|
-
);
|
|
842
|
-
case "radio":
|
|
843
|
-
return /* @__PURE__ */ React12.createElement(
|
|
844
|
-
RadioGroupField,
|
|
845
|
-
{
|
|
846
|
-
...baseProps,
|
|
847
|
-
control,
|
|
848
|
-
defaultValue: config.defaultValue,
|
|
849
|
-
options: (config.radioOptions ?? []).map((opt) => ({
|
|
850
|
-
label: opt.label,
|
|
851
|
-
value: String(opt.value)
|
|
852
|
-
})),
|
|
853
|
-
radioGroupProps: config.radioProps
|
|
854
|
-
}
|
|
855
|
-
);
|
|
856
|
-
case "switch":
|
|
857
|
-
return /* @__PURE__ */ React12.createElement(
|
|
858
|
-
SwitchField,
|
|
859
|
-
{
|
|
860
|
-
...baseProps,
|
|
861
|
-
control,
|
|
862
|
-
defaultValue: config.defaultValue,
|
|
863
|
-
switchProps: config.switchProps
|
|
864
|
-
}
|
|
865
|
-
);
|
|
866
|
-
case "slider":
|
|
867
|
-
return /* @__PURE__ */ React12.createElement(
|
|
868
|
-
SliderField,
|
|
869
|
-
{
|
|
870
|
-
...baseProps,
|
|
871
|
-
control,
|
|
872
|
-
defaultValue: config.defaultValue,
|
|
873
|
-
sliderProps: config.sliderProps
|
|
874
|
-
}
|
|
875
|
-
);
|
|
876
|
-
case "date":
|
|
877
|
-
return /* @__PURE__ */ React12.createElement(
|
|
878
|
-
DateField,
|
|
879
|
-
{
|
|
880
|
-
...baseProps,
|
|
881
|
-
control,
|
|
882
|
-
dateProps: config.dateProps,
|
|
883
|
-
defaultValue: config.defaultValue
|
|
884
|
-
}
|
|
885
|
-
);
|
|
886
|
-
case "file":
|
|
887
|
-
return /* @__PURE__ */ React12.createElement(
|
|
888
|
-
FileField,
|
|
889
|
-
{
|
|
890
|
-
...baseProps,
|
|
891
|
-
accept: config.accept,
|
|
892
|
-
control,
|
|
893
|
-
defaultValue: config.defaultValue,
|
|
894
|
-
fileProps: config.fileProps,
|
|
895
|
-
multiple: config.multiple
|
|
896
|
-
}
|
|
897
|
-
);
|
|
898
|
-
case "fontPicker":
|
|
899
|
-
return /* @__PURE__ */ React12.createElement(
|
|
900
|
-
FontPickerField,
|
|
901
|
-
{
|
|
902
|
-
...baseProps,
|
|
903
|
-
control,
|
|
904
|
-
defaultValue: config.defaultValue,
|
|
905
|
-
fontPickerProps: config.fontPickerProps
|
|
906
|
-
}
|
|
907
|
-
);
|
|
908
|
-
case "custom":
|
|
909
|
-
return config.render({
|
|
910
|
-
control,
|
|
911
|
-
errors: form.formState.errors,
|
|
912
|
-
form,
|
|
913
|
-
isSubmitting: submissionState.isSubmitting,
|
|
914
|
-
name: config.name
|
|
915
|
-
});
|
|
916
|
-
default: {
|
|
917
|
-
const fieldType = config.type;
|
|
918
|
-
console.warn(`Unknown field type: ${fieldType}`);
|
|
955
|
+
const { control } = form;
|
|
956
|
+
const watchedValues = useWatch3({ control });
|
|
957
|
+
if (config.condition && !config.condition(watchedValues)) {
|
|
919
958
|
return null;
|
|
920
959
|
}
|
|
960
|
+
if (config.dependsOn) {
|
|
961
|
+
const dependentValue = watchedValues[config.dependsOn];
|
|
962
|
+
if (config.dependsOnValue !== void 0 && dependentValue !== config.dependsOnValue) {
|
|
963
|
+
return null;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
const baseProps = {
|
|
967
|
+
ariaDescribedBy: config.ariaDescribedBy,
|
|
968
|
+
ariaLabel: config.ariaLabel,
|
|
969
|
+
className: config.className,
|
|
970
|
+
description: config.description,
|
|
971
|
+
isDisabled: config.isDisabled ?? submissionState.isSubmitting,
|
|
972
|
+
label: config.label,
|
|
973
|
+
name: config.name,
|
|
974
|
+
rules: config.rules
|
|
975
|
+
};
|
|
976
|
+
switch (config.type) {
|
|
977
|
+
case "input":
|
|
978
|
+
return /* @__PURE__ */ React15.createElement(
|
|
979
|
+
InputField,
|
|
980
|
+
{
|
|
981
|
+
...baseProps,
|
|
982
|
+
control,
|
|
983
|
+
defaultValue: config.defaultValue,
|
|
984
|
+
inputProps: config.inputProps
|
|
985
|
+
}
|
|
986
|
+
);
|
|
987
|
+
case "textarea":
|
|
988
|
+
return /* @__PURE__ */ React15.createElement(
|
|
989
|
+
TextareaField,
|
|
990
|
+
{
|
|
991
|
+
...baseProps,
|
|
992
|
+
control,
|
|
993
|
+
defaultValue: config.defaultValue,
|
|
994
|
+
textareaProps: config.textareaProps
|
|
995
|
+
}
|
|
996
|
+
);
|
|
997
|
+
case "select":
|
|
998
|
+
return /* @__PURE__ */ React15.createElement(
|
|
999
|
+
SelectField,
|
|
1000
|
+
{
|
|
1001
|
+
...baseProps,
|
|
1002
|
+
control,
|
|
1003
|
+
defaultValue: config.defaultValue,
|
|
1004
|
+
options: (config.options ?? []).map((opt) => ({
|
|
1005
|
+
label: opt.label,
|
|
1006
|
+
value: String(opt.value)
|
|
1007
|
+
})),
|
|
1008
|
+
selectProps: config.selectProps
|
|
1009
|
+
}
|
|
1010
|
+
);
|
|
1011
|
+
case "checkbox":
|
|
1012
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1013
|
+
CheckboxField,
|
|
1014
|
+
{
|
|
1015
|
+
...baseProps,
|
|
1016
|
+
checkboxProps: config.checkboxProps,
|
|
1017
|
+
control,
|
|
1018
|
+
defaultValue: config.defaultValue
|
|
1019
|
+
}
|
|
1020
|
+
);
|
|
1021
|
+
case "radio":
|
|
1022
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1023
|
+
RadioGroupField,
|
|
1024
|
+
{
|
|
1025
|
+
...baseProps,
|
|
1026
|
+
control,
|
|
1027
|
+
defaultValue: config.defaultValue,
|
|
1028
|
+
options: (config.radioOptions ?? []).map((opt) => ({
|
|
1029
|
+
label: opt.label,
|
|
1030
|
+
value: String(opt.value)
|
|
1031
|
+
})),
|
|
1032
|
+
radioGroupProps: config.radioProps
|
|
1033
|
+
}
|
|
1034
|
+
);
|
|
1035
|
+
case "switch":
|
|
1036
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1037
|
+
SwitchField,
|
|
1038
|
+
{
|
|
1039
|
+
...baseProps,
|
|
1040
|
+
control,
|
|
1041
|
+
defaultValue: config.defaultValue,
|
|
1042
|
+
switchProps: config.switchProps
|
|
1043
|
+
}
|
|
1044
|
+
);
|
|
1045
|
+
case "slider":
|
|
1046
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1047
|
+
SliderField,
|
|
1048
|
+
{
|
|
1049
|
+
...baseProps,
|
|
1050
|
+
control,
|
|
1051
|
+
defaultValue: config.defaultValue,
|
|
1052
|
+
sliderProps: config.sliderProps
|
|
1053
|
+
}
|
|
1054
|
+
);
|
|
1055
|
+
case "date":
|
|
1056
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1057
|
+
DateField,
|
|
1058
|
+
{
|
|
1059
|
+
...baseProps,
|
|
1060
|
+
control,
|
|
1061
|
+
dateProps: config.dateProps,
|
|
1062
|
+
defaultValue: config.defaultValue
|
|
1063
|
+
}
|
|
1064
|
+
);
|
|
1065
|
+
case "file":
|
|
1066
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1067
|
+
FileField,
|
|
1068
|
+
{
|
|
1069
|
+
...baseProps,
|
|
1070
|
+
accept: config.accept,
|
|
1071
|
+
control,
|
|
1072
|
+
defaultValue: config.defaultValue,
|
|
1073
|
+
fileProps: config.fileProps,
|
|
1074
|
+
multiple: config.multiple
|
|
1075
|
+
}
|
|
1076
|
+
);
|
|
1077
|
+
case "fontPicker":
|
|
1078
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1079
|
+
FontPickerField,
|
|
1080
|
+
{
|
|
1081
|
+
...baseProps,
|
|
1082
|
+
control,
|
|
1083
|
+
defaultValue: config.defaultValue,
|
|
1084
|
+
fontPickerProps: config.fontPickerProps
|
|
1085
|
+
}
|
|
1086
|
+
);
|
|
1087
|
+
case "custom":
|
|
1088
|
+
return config.render({
|
|
1089
|
+
control,
|
|
1090
|
+
errors: form.formState.errors,
|
|
1091
|
+
form,
|
|
1092
|
+
isSubmitting: submissionState.isSubmitting,
|
|
1093
|
+
name: config.name
|
|
1094
|
+
});
|
|
1095
|
+
case "conditional":
|
|
1096
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1097
|
+
ConditionalField,
|
|
1098
|
+
{
|
|
1099
|
+
config,
|
|
1100
|
+
control,
|
|
1101
|
+
className: config.className
|
|
1102
|
+
}
|
|
1103
|
+
);
|
|
1104
|
+
case "fieldArray":
|
|
1105
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1106
|
+
FieldArrayField,
|
|
1107
|
+
{
|
|
1108
|
+
config,
|
|
1109
|
+
className: config.className
|
|
1110
|
+
}
|
|
1111
|
+
);
|
|
1112
|
+
case "dynamicSection":
|
|
1113
|
+
return /* @__PURE__ */ React15.createElement(
|
|
1114
|
+
DynamicSectionField,
|
|
1115
|
+
{
|
|
1116
|
+
config,
|
|
1117
|
+
control,
|
|
1118
|
+
className: config.className
|
|
1119
|
+
}
|
|
1120
|
+
);
|
|
1121
|
+
default: {
|
|
1122
|
+
const fieldType = config.type;
|
|
1123
|
+
console.warn(`Unknown field type: ${fieldType}`);
|
|
1124
|
+
return null;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
921
1127
|
}
|
|
922
|
-
|
|
1128
|
+
);
|
|
923
1129
|
|
|
924
1130
|
// src/components/Form.tsx
|
|
925
1131
|
function ConfigurableForm({
|
|
@@ -956,16 +1162,16 @@ function ConfigurableForm({
|
|
|
956
1162
|
});
|
|
957
1163
|
const renderFields = () => {
|
|
958
1164
|
if (layout === "grid") {
|
|
959
|
-
return /* @__PURE__ */
|
|
1165
|
+
return /* @__PURE__ */ React16.createElement(
|
|
960
1166
|
"div",
|
|
961
1167
|
{
|
|
962
1168
|
className: `grid gap-${spacing} ${columns === 1 ? "grid-cols-1" : columns === 2 ? "grid-cols-1 md:grid-cols-2" : "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"}`
|
|
963
1169
|
},
|
|
964
|
-
fields.map((
|
|
1170
|
+
fields.map((field2) => /* @__PURE__ */ React16.createElement(
|
|
965
1171
|
FormField,
|
|
966
1172
|
{
|
|
967
|
-
key:
|
|
968
|
-
config:
|
|
1173
|
+
key: field2.name,
|
|
1174
|
+
config: field2,
|
|
969
1175
|
form,
|
|
970
1176
|
submissionState
|
|
971
1177
|
}
|
|
@@ -973,21 +1179,21 @@ function ConfigurableForm({
|
|
|
973
1179
|
);
|
|
974
1180
|
}
|
|
975
1181
|
if (layout === "horizontal") {
|
|
976
|
-
return /* @__PURE__ */
|
|
1182
|
+
return /* @__PURE__ */ React16.createElement("div", { className: `grid gap-${spacing} grid-cols-1 md:grid-cols-2` }, fields.map((field2) => /* @__PURE__ */ React16.createElement(
|
|
977
1183
|
FormField,
|
|
978
1184
|
{
|
|
979
|
-
key:
|
|
980
|
-
config:
|
|
1185
|
+
key: field2.name,
|
|
1186
|
+
config: field2,
|
|
981
1187
|
form,
|
|
982
1188
|
submissionState
|
|
983
1189
|
}
|
|
984
1190
|
)));
|
|
985
1191
|
}
|
|
986
|
-
return /* @__PURE__ */
|
|
1192
|
+
return /* @__PURE__ */ React16.createElement("div", { className: `space-y-${spacing}` }, fields.map((field2) => /* @__PURE__ */ React16.createElement(
|
|
987
1193
|
FormField,
|
|
988
1194
|
{
|
|
989
|
-
key:
|
|
990
|
-
config:
|
|
1195
|
+
key: field2.name,
|
|
1196
|
+
config: field2,
|
|
991
1197
|
form,
|
|
992
1198
|
submissionState
|
|
993
1199
|
}
|
|
@@ -997,24 +1203,24 @@ function ConfigurableForm({
|
|
|
997
1203
|
e.preventDefault();
|
|
998
1204
|
void handleSubmit();
|
|
999
1205
|
};
|
|
1000
|
-
return /* @__PURE__ */
|
|
1206
|
+
return /* @__PURE__ */ React16.createElement("form", { className, role: "form", onSubmit: handleFormSubmit }, title && /* @__PURE__ */ React16.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React16.createElement("h2", { className: "text-xl font-semibold text-foreground mb-2" }, title), subtitle && /* @__PURE__ */ React16.createElement("p", { className: "text-sm text-muted-foreground" }, subtitle)), isSubmitted && isSuccess && /* @__PURE__ */ React16.createElement(
|
|
1001
1207
|
"div",
|
|
1002
1208
|
{
|
|
1003
1209
|
className: "mb-6 p-4 bg-success-50 border border-success-200 rounded-lg",
|
|
1004
1210
|
"data-testid": "success-message"
|
|
1005
1211
|
},
|
|
1006
|
-
/* @__PURE__ */
|
|
1007
|
-
/* @__PURE__ */
|
|
1008
|
-
), error && /* @__PURE__ */
|
|
1212
|
+
/* @__PURE__ */ React16.createElement("p", { className: "text-success-800 font-medium" }, "Success!"),
|
|
1213
|
+
/* @__PURE__ */ React16.createElement("p", { className: "text-success-700 text-sm mt-1" }, "Your request has been processed successfully.")
|
|
1214
|
+
), error && /* @__PURE__ */ React16.createElement(
|
|
1009
1215
|
"div",
|
|
1010
1216
|
{
|
|
1011
1217
|
className: "mb-6 p-4 bg-danger-50 border border-danger-200 rounded-lg",
|
|
1012
1218
|
"data-testid": "error-message"
|
|
1013
1219
|
},
|
|
1014
|
-
/* @__PURE__ */
|
|
1015
|
-
/* @__PURE__ */
|
|
1016
|
-
), renderFields(), /* @__PURE__ */
|
|
1017
|
-
|
|
1220
|
+
/* @__PURE__ */ React16.createElement("p", { className: "text-danger-800 font-medium" }, "Error"),
|
|
1221
|
+
/* @__PURE__ */ React16.createElement("p", { className: "text-danger-700 text-sm mt-1" }, error)
|
|
1222
|
+
), renderFields(), /* @__PURE__ */ React16.createElement("div", { className: "mt-6 flex gap-3 justify-end" }, /* @__PURE__ */ React16.createElement(
|
|
1223
|
+
Button3,
|
|
1018
1224
|
{
|
|
1019
1225
|
color: "primary",
|
|
1020
1226
|
isDisabled: isSubmitting,
|
|
@@ -1023,8 +1229,8 @@ function ConfigurableForm({
|
|
|
1023
1229
|
...submitButtonProps
|
|
1024
1230
|
},
|
|
1025
1231
|
submitButtonText
|
|
1026
|
-
), showResetButton && /* @__PURE__ */
|
|
1027
|
-
|
|
1232
|
+
), showResetButton && /* @__PURE__ */ React16.createElement(
|
|
1233
|
+
Button3,
|
|
1028
1234
|
{
|
|
1029
1235
|
isDisabled: isSubmitting,
|
|
1030
1236
|
type: "button",
|
|
@@ -1035,10 +1241,338 @@ function ConfigurableForm({
|
|
|
1035
1241
|
)));
|
|
1036
1242
|
}
|
|
1037
1243
|
|
|
1244
|
+
// src/components/ServerActionForm.tsx
|
|
1245
|
+
import React17 from "react";
|
|
1246
|
+
import {
|
|
1247
|
+
Button as Button4,
|
|
1248
|
+
Checkbox as Checkbox2,
|
|
1249
|
+
Input as Input2,
|
|
1250
|
+
Select as Select2,
|
|
1251
|
+
SelectItem as SelectItem2,
|
|
1252
|
+
Textarea as Textarea2
|
|
1253
|
+
} from "@heroui/react";
|
|
1254
|
+
import { useActionState } from "react";
|
|
1255
|
+
function ServerActionForm({
|
|
1256
|
+
action,
|
|
1257
|
+
className,
|
|
1258
|
+
clientValidationSchema,
|
|
1259
|
+
columns = 1,
|
|
1260
|
+
defaultValues,
|
|
1261
|
+
fields,
|
|
1262
|
+
initialState,
|
|
1263
|
+
layout = "vertical",
|
|
1264
|
+
onError,
|
|
1265
|
+
onSuccess,
|
|
1266
|
+
resetButtonText = "Reset",
|
|
1267
|
+
showResetButton = false,
|
|
1268
|
+
spacing = "4",
|
|
1269
|
+
submitButtonProps = {},
|
|
1270
|
+
submitButtonText = "Submit",
|
|
1271
|
+
subtitle,
|
|
1272
|
+
title
|
|
1273
|
+
}) {
|
|
1274
|
+
const [state, formAction, pending] = useActionState(
|
|
1275
|
+
action,
|
|
1276
|
+
initialState ?? { errors: void 0, message: void 0, success: false }
|
|
1277
|
+
);
|
|
1278
|
+
const formRef = React17.useRef(null);
|
|
1279
|
+
const [clientErrors, setClientErrors] = React17.useState({});
|
|
1280
|
+
const lastSubmittedFormData = React17.useRef(null);
|
|
1281
|
+
React17.useEffect(() => {
|
|
1282
|
+
if (state && (state.errors || state.message && !state.success)) {
|
|
1283
|
+
onError?.({
|
|
1284
|
+
errors: state.errors,
|
|
1285
|
+
message: state.message
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
}, [state, onError]);
|
|
1289
|
+
React17.useEffect(() => {
|
|
1290
|
+
if (state?.success && lastSubmittedFormData.current) {
|
|
1291
|
+
onSuccess?.(lastSubmittedFormData.current);
|
|
1292
|
+
}
|
|
1293
|
+
}, [state?.success, onSuccess]);
|
|
1294
|
+
const handleReset = () => {
|
|
1295
|
+
formRef.current?.reset();
|
|
1296
|
+
setClientErrors({});
|
|
1297
|
+
};
|
|
1298
|
+
const handleSubmit = async (e) => {
|
|
1299
|
+
e.preventDefault();
|
|
1300
|
+
if (clientValidationSchema) {
|
|
1301
|
+
const formData2 = new FormData(e.currentTarget);
|
|
1302
|
+
const values = {};
|
|
1303
|
+
formData2.forEach((val, key) => {
|
|
1304
|
+
if (val === "on") {
|
|
1305
|
+
values[key] = true;
|
|
1306
|
+
} else if (val === "") {
|
|
1307
|
+
if (!values[key]) {
|
|
1308
|
+
values[key] = false;
|
|
1309
|
+
}
|
|
1310
|
+
} else {
|
|
1311
|
+
values[key] = val;
|
|
1312
|
+
}
|
|
1313
|
+
});
|
|
1314
|
+
const result = clientValidationSchema.safeParse(values);
|
|
1315
|
+
if (!result.success) {
|
|
1316
|
+
const errors = {};
|
|
1317
|
+
result.error.issues.forEach((issue) => {
|
|
1318
|
+
const path = issue.path.join(".");
|
|
1319
|
+
errors[path] = issue.message;
|
|
1320
|
+
});
|
|
1321
|
+
setClientErrors((prev) => ({ ...prev, ...errors }));
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
setClientErrors({});
|
|
1325
|
+
}
|
|
1326
|
+
const formData = new FormData(e.currentTarget);
|
|
1327
|
+
lastSubmittedFormData.current = formData;
|
|
1328
|
+
formAction(formData);
|
|
1329
|
+
};
|
|
1330
|
+
const renderFields = () => {
|
|
1331
|
+
if (layout === "grid") {
|
|
1332
|
+
return /* @__PURE__ */ React17.createElement(
|
|
1333
|
+
"div",
|
|
1334
|
+
{
|
|
1335
|
+
className: `grid gap-${spacing} ${columns === 1 ? "grid-cols-1" : columns === 2 ? "grid-cols-1 md:grid-cols-2" : "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"}`
|
|
1336
|
+
},
|
|
1337
|
+
fields.map((field2) => /* @__PURE__ */ React17.createElement(
|
|
1338
|
+
ServerActionField,
|
|
1339
|
+
{
|
|
1340
|
+
key: field2.name,
|
|
1341
|
+
clientErrors,
|
|
1342
|
+
defaultValues,
|
|
1343
|
+
errors: state?.errors,
|
|
1344
|
+
field: field2
|
|
1345
|
+
}
|
|
1346
|
+
))
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
if (layout === "horizontal") {
|
|
1350
|
+
return /* @__PURE__ */ React17.createElement("div", { className: `grid gap-${spacing} grid-cols-1 md:grid-cols-2` }, fields.map((field2) => /* @__PURE__ */ React17.createElement(
|
|
1351
|
+
ServerActionField,
|
|
1352
|
+
{
|
|
1353
|
+
key: field2.name,
|
|
1354
|
+
clientErrors,
|
|
1355
|
+
defaultValues,
|
|
1356
|
+
errors: state?.errors,
|
|
1357
|
+
field: field2
|
|
1358
|
+
}
|
|
1359
|
+
)));
|
|
1360
|
+
}
|
|
1361
|
+
return /* @__PURE__ */ React17.createElement("div", { className: `space-y-${spacing}` }, fields.map((field2) => /* @__PURE__ */ React17.createElement(
|
|
1362
|
+
ServerActionField,
|
|
1363
|
+
{
|
|
1364
|
+
key: field2.name,
|
|
1365
|
+
clientErrors,
|
|
1366
|
+
defaultValues,
|
|
1367
|
+
errors: state?.errors,
|
|
1368
|
+
field: field2
|
|
1369
|
+
}
|
|
1370
|
+
)));
|
|
1371
|
+
};
|
|
1372
|
+
return /* @__PURE__ */ React17.createElement(
|
|
1373
|
+
"form",
|
|
1374
|
+
{
|
|
1375
|
+
ref: formRef,
|
|
1376
|
+
className,
|
|
1377
|
+
role: "form",
|
|
1378
|
+
onSubmit: handleSubmit
|
|
1379
|
+
},
|
|
1380
|
+
title && /* @__PURE__ */ React17.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React17.createElement("h2", { className: "text-xl font-semibold text-foreground mb-2" }, title), subtitle && /* @__PURE__ */ React17.createElement("p", { className: "text-sm text-muted-foreground" }, subtitle)),
|
|
1381
|
+
state?.success && !pending && /* @__PURE__ */ React17.createElement(
|
|
1382
|
+
"div",
|
|
1383
|
+
{
|
|
1384
|
+
className: "mb-6 p-4 bg-success-50 border border-success-200 rounded-lg",
|
|
1385
|
+
"data-testid": "success-message"
|
|
1386
|
+
},
|
|
1387
|
+
/* @__PURE__ */ React17.createElement("p", { className: "text-success-800 font-medium" }, "Success!"),
|
|
1388
|
+
state.message && /* @__PURE__ */ React17.createElement("p", { className: "text-success-700 text-sm mt-1" }, state.message)
|
|
1389
|
+
),
|
|
1390
|
+
state?.message && !state.success && /* @__PURE__ */ React17.createElement(
|
|
1391
|
+
"div",
|
|
1392
|
+
{
|
|
1393
|
+
className: "mb-6 p-4 bg-danger-50 border border-danger-200 rounded-lg",
|
|
1394
|
+
"data-testid": "error-message"
|
|
1395
|
+
},
|
|
1396
|
+
/* @__PURE__ */ React17.createElement("p", { className: "text-danger-800 font-medium" }, "Error"),
|
|
1397
|
+
/* @__PURE__ */ React17.createElement("p", { className: "text-danger-700 text-sm mt-1" }, state.message)
|
|
1398
|
+
),
|
|
1399
|
+
renderFields(),
|
|
1400
|
+
/* @__PURE__ */ React17.createElement("div", { className: "mt-6 flex gap-3 justify-end" }, /* @__PURE__ */ React17.createElement(
|
|
1401
|
+
Button4,
|
|
1402
|
+
{
|
|
1403
|
+
color: "primary",
|
|
1404
|
+
isDisabled: pending,
|
|
1405
|
+
isLoading: pending,
|
|
1406
|
+
type: "submit",
|
|
1407
|
+
...submitButtonProps
|
|
1408
|
+
},
|
|
1409
|
+
submitButtonText
|
|
1410
|
+
), showResetButton && /* @__PURE__ */ React17.createElement(
|
|
1411
|
+
Button4,
|
|
1412
|
+
{
|
|
1413
|
+
isDisabled: pending,
|
|
1414
|
+
type: "button",
|
|
1415
|
+
variant: "bordered",
|
|
1416
|
+
onPress: handleReset
|
|
1417
|
+
},
|
|
1418
|
+
resetButtonText
|
|
1419
|
+
))
|
|
1420
|
+
);
|
|
1421
|
+
}
|
|
1422
|
+
function ServerActionField({
|
|
1423
|
+
clientErrors,
|
|
1424
|
+
defaultValues,
|
|
1425
|
+
errors,
|
|
1426
|
+
field: field2
|
|
1427
|
+
}) {
|
|
1428
|
+
const fieldName = field2.name;
|
|
1429
|
+
const fieldErrors = errors?.[fieldName];
|
|
1430
|
+
const clientError = clientErrors?.[fieldName];
|
|
1431
|
+
const errorMessage = clientError || (fieldErrors && fieldErrors.length > 0 ? fieldErrors[0] : void 0);
|
|
1432
|
+
const getDefaultValue = () => {
|
|
1433
|
+
const fromProps = defaultValues?.[fieldName];
|
|
1434
|
+
const fromField = field2.defaultValue;
|
|
1435
|
+
if (fromProps !== void 0 && fromProps !== null) {
|
|
1436
|
+
return typeof fromProps === "string" ? fromProps : String(fromProps);
|
|
1437
|
+
}
|
|
1438
|
+
if (fromField !== void 0 && fromField !== null) {
|
|
1439
|
+
return typeof fromField === "string" ? fromField : String(fromField);
|
|
1440
|
+
}
|
|
1441
|
+
return "";
|
|
1442
|
+
};
|
|
1443
|
+
const getDefaultChecked = () => {
|
|
1444
|
+
const fromProps = defaultValues?.[fieldName];
|
|
1445
|
+
const fromField = field2.defaultValue;
|
|
1446
|
+
if (fromProps !== void 0 && fromProps !== null) {
|
|
1447
|
+
return typeof fromProps === "boolean" ? fromProps : false;
|
|
1448
|
+
}
|
|
1449
|
+
if (fromField !== void 0 && fromField !== null) {
|
|
1450
|
+
return typeof fromField === "boolean" ? fromField : false;
|
|
1451
|
+
}
|
|
1452
|
+
return false;
|
|
1453
|
+
};
|
|
1454
|
+
const [value, setValue] = React17.useState(getDefaultValue);
|
|
1455
|
+
const [checked, setChecked] = React17.useState(getDefaultChecked);
|
|
1456
|
+
React17.useEffect(() => {
|
|
1457
|
+
const newDefaultValue = defaultValues?.[fieldName];
|
|
1458
|
+
if (newDefaultValue !== void 0 && newDefaultValue !== null) {
|
|
1459
|
+
if (field2.type === "checkbox") {
|
|
1460
|
+
setChecked(
|
|
1461
|
+
typeof newDefaultValue === "boolean" ? newDefaultValue : false
|
|
1462
|
+
);
|
|
1463
|
+
} else {
|
|
1464
|
+
setValue(
|
|
1465
|
+
typeof newDefaultValue === "string" ? newDefaultValue : String(newDefaultValue)
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}, [defaultValues, fieldName, field2.type]);
|
|
1470
|
+
React17.useEffect(() => {
|
|
1471
|
+
const hiddenInput = document.querySelector(
|
|
1472
|
+
`input[type="hidden"][name="${fieldName}"]`
|
|
1473
|
+
);
|
|
1474
|
+
if (hiddenInput) {
|
|
1475
|
+
if (field2.type === "checkbox") {
|
|
1476
|
+
hiddenInput.value = checked ? "on" : "";
|
|
1477
|
+
} else {
|
|
1478
|
+
hiddenInput.value = value;
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
}, [value, checked, fieldName, field2.type]);
|
|
1482
|
+
switch (field2.type) {
|
|
1483
|
+
case "input": {
|
|
1484
|
+
const inputType = field2.inputProps?.type || "text";
|
|
1485
|
+
return /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("input", { type: "hidden", name: fieldName, value }), /* @__PURE__ */ React17.createElement(
|
|
1486
|
+
Input2,
|
|
1487
|
+
{
|
|
1488
|
+
...field2.inputProps,
|
|
1489
|
+
"data-field-name": fieldName,
|
|
1490
|
+
type: inputType,
|
|
1491
|
+
label: field2.label,
|
|
1492
|
+
description: field2.description,
|
|
1493
|
+
isDisabled: field2.isDisabled,
|
|
1494
|
+
isInvalid: Boolean(errorMessage),
|
|
1495
|
+
errorMessage,
|
|
1496
|
+
value,
|
|
1497
|
+
onValueChange: setValue
|
|
1498
|
+
}
|
|
1499
|
+
));
|
|
1500
|
+
}
|
|
1501
|
+
case "textarea": {
|
|
1502
|
+
return /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("input", { type: "hidden", name: fieldName, value }), /* @__PURE__ */ React17.createElement(
|
|
1503
|
+
Textarea2,
|
|
1504
|
+
{
|
|
1505
|
+
...field2.textareaProps,
|
|
1506
|
+
"data-field-name": fieldName,
|
|
1507
|
+
label: field2.label,
|
|
1508
|
+
description: field2.description,
|
|
1509
|
+
isDisabled: field2.isDisabled,
|
|
1510
|
+
isInvalid: Boolean(errorMessage),
|
|
1511
|
+
errorMessage,
|
|
1512
|
+
value,
|
|
1513
|
+
onValueChange: setValue
|
|
1514
|
+
}
|
|
1515
|
+
));
|
|
1516
|
+
}
|
|
1517
|
+
case "checkbox": {
|
|
1518
|
+
return /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("input", { type: "hidden", name: fieldName, value: checked ? "on" : "" }), /* @__PURE__ */ React17.createElement(
|
|
1519
|
+
Checkbox2,
|
|
1520
|
+
{
|
|
1521
|
+
...field2.checkboxProps,
|
|
1522
|
+
"data-field-name": fieldName,
|
|
1523
|
+
isDisabled: field2.isDisabled,
|
|
1524
|
+
isSelected: checked,
|
|
1525
|
+
onValueChange: setChecked,
|
|
1526
|
+
isInvalid: Boolean(errorMessage),
|
|
1527
|
+
errorMessage
|
|
1528
|
+
},
|
|
1529
|
+
field2.label
|
|
1530
|
+
));
|
|
1531
|
+
}
|
|
1532
|
+
case "select": {
|
|
1533
|
+
const options = field2.options || [];
|
|
1534
|
+
return /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("input", { type: "hidden", name: fieldName, value }), /* @__PURE__ */ React17.createElement(
|
|
1535
|
+
Select2,
|
|
1536
|
+
{
|
|
1537
|
+
...field2.selectProps,
|
|
1538
|
+
"data-field-name": fieldName,
|
|
1539
|
+
label: field2.label,
|
|
1540
|
+
description: field2.description,
|
|
1541
|
+
isDisabled: field2.isDisabled,
|
|
1542
|
+
isInvalid: Boolean(errorMessage),
|
|
1543
|
+
errorMessage,
|
|
1544
|
+
selectedKeys: value ? [value] : [],
|
|
1545
|
+
onSelectionChange: (keys) => {
|
|
1546
|
+
const selectedValue = Array.from(keys)[0];
|
|
1547
|
+
setValue(selectedValue || "");
|
|
1548
|
+
}
|
|
1549
|
+
},
|
|
1550
|
+
options.map(
|
|
1551
|
+
(option) => /* @__PURE__ */ React17.createElement(SelectItem2, { key: String(option.value) }, option.label)
|
|
1552
|
+
)
|
|
1553
|
+
));
|
|
1554
|
+
}
|
|
1555
|
+
default:
|
|
1556
|
+
return /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("input", { type: "hidden", name: fieldName, value }), /* @__PURE__ */ React17.createElement(
|
|
1557
|
+
Input2,
|
|
1558
|
+
{
|
|
1559
|
+
"data-field-name": fieldName,
|
|
1560
|
+
label: field2.label,
|
|
1561
|
+
description: field2.description,
|
|
1562
|
+
isDisabled: field2.isDisabled,
|
|
1563
|
+
isInvalid: Boolean(errorMessage),
|
|
1564
|
+
errorMessage,
|
|
1565
|
+
value,
|
|
1566
|
+
onValueChange: setValue
|
|
1567
|
+
}
|
|
1568
|
+
));
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1038
1572
|
// src/hooks/useHeroForm.ts
|
|
1039
|
-
import { useFormContext } from "react-hook-form";
|
|
1573
|
+
import { useFormContext as useFormContext4 } from "react-hook-form";
|
|
1040
1574
|
function useHeroForm() {
|
|
1041
|
-
const form =
|
|
1575
|
+
const form = useFormContext4();
|
|
1042
1576
|
const defaults = useHeroHookFormDefaults();
|
|
1043
1577
|
return {
|
|
1044
1578
|
// All React Hook Form methods and state
|
|
@@ -1049,10 +1583,10 @@ function useHeroForm() {
|
|
|
1049
1583
|
}
|
|
1050
1584
|
|
|
1051
1585
|
// src/providers/FormProvider.tsx
|
|
1052
|
-
import
|
|
1586
|
+
import React18 from "react";
|
|
1053
1587
|
import { FormProvider as RHFProvider } from "react-hook-form";
|
|
1054
1588
|
function FormProvider(props) {
|
|
1055
|
-
return /* @__PURE__ */
|
|
1589
|
+
return /* @__PURE__ */ React18.createElement(RHFProvider, { ...props.methods }, /* @__PURE__ */ React18.createElement(
|
|
1056
1590
|
"form",
|
|
1057
1591
|
{
|
|
1058
1592
|
className: props.className,
|
|
@@ -1065,22 +1599,42 @@ function FormProvider(props) {
|
|
|
1065
1599
|
}
|
|
1066
1600
|
|
|
1067
1601
|
// src/submit/SubmitButton.tsx
|
|
1068
|
-
import
|
|
1602
|
+
import React19 from "react";
|
|
1069
1603
|
function SubmitButton(props) {
|
|
1070
|
-
const ctx =
|
|
1604
|
+
const ctx = useFormContext5();
|
|
1071
1605
|
const loading = props.isLoading ?? ctx.formState.isSubmitting;
|
|
1606
|
+
const enhancedState = props.enhancedState;
|
|
1072
1607
|
const isDisabledFromProps = props.buttonProps?.isDisabled ?? false;
|
|
1073
1608
|
const isDisabled = Boolean(isDisabledFromProps) || Boolean(loading);
|
|
1074
1609
|
const defaults = useHeroHookFormDefaults();
|
|
1075
|
-
|
|
1610
|
+
const getButtonContent = () => {
|
|
1611
|
+
if (enhancedState?.isSuccess) {
|
|
1612
|
+
return /* @__PURE__ */ React19.createElement("span", { className: "inline-flex items-center gap-2" }, "\u2705", props.successText || "Success!");
|
|
1613
|
+
}
|
|
1614
|
+
if (loading) {
|
|
1615
|
+
return /* @__PURE__ */ React19.createElement("span", { className: "inline-flex items-center gap-2" }, "\u23F3", props.loadingText || "Submitting...");
|
|
1616
|
+
}
|
|
1617
|
+
return props.children;
|
|
1618
|
+
};
|
|
1619
|
+
const getButtonColor = () => {
|
|
1620
|
+
if (enhancedState?.isSuccess) {
|
|
1621
|
+
return "success";
|
|
1622
|
+
}
|
|
1623
|
+
if (enhancedState?.isError) {
|
|
1624
|
+
return "danger";
|
|
1625
|
+
}
|
|
1626
|
+
return props.buttonProps?.color || defaults.submitButton.color;
|
|
1627
|
+
};
|
|
1628
|
+
return /* @__PURE__ */ React19.createElement(
|
|
1076
1629
|
Button,
|
|
1077
1630
|
{
|
|
1078
1631
|
type: "submit",
|
|
1079
1632
|
...defaults.submitButton,
|
|
1080
1633
|
...props.buttonProps,
|
|
1081
|
-
isDisabled
|
|
1634
|
+
isDisabled,
|
|
1635
|
+
color: getButtonColor()
|
|
1082
1636
|
},
|
|
1083
|
-
|
|
1637
|
+
getButtonContent()
|
|
1084
1638
|
);
|
|
1085
1639
|
}
|
|
1086
1640
|
|
|
@@ -1220,14 +1774,6 @@ var createPasswordSchema = (minLength = 8) => z.string().min(minLength, `Passwor
|
|
|
1220
1774
|
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
|
|
1221
1775
|
"Password must contain at least one uppercase letter, one lowercase letter, and one number"
|
|
1222
1776
|
);
|
|
1223
|
-
var createConfirmPasswordSchema = (passwordField) => z.string().refine(
|
|
1224
|
-
(val) => {
|
|
1225
|
-
return true;
|
|
1226
|
-
},
|
|
1227
|
-
{
|
|
1228
|
-
message: "Passwords do not match"
|
|
1229
|
-
}
|
|
1230
|
-
);
|
|
1231
1777
|
var createNumberRangeSchema = (min, max, fieldName) => z.number().min(min, `${fieldName} must be at least ${min}`).max(max, `${fieldName} must be no more than ${max}`);
|
|
1232
1778
|
var createDateSchema = (fieldName) => z.date({ message: `${fieldName} is required` });
|
|
1233
1779
|
var createFutureDateSchema = (fieldName) => z.date({ message: `${fieldName} is required` }).refine((date) => date > /* @__PURE__ */ new Date(), {
|
|
@@ -1246,17 +1792,61 @@ var createFileSchema = (maxSizeInMB = 5, allowedTypes = ["image/jpeg", "image/pn
|
|
|
1246
1792
|
var createRequiredCheckboxSchema = (fieldName) => z.boolean().refine((val) => val === true, {
|
|
1247
1793
|
message: `You must agree to ${fieldName}`
|
|
1248
1794
|
});
|
|
1249
|
-
var
|
|
1250
|
-
|
|
1251
|
-
|
|
1795
|
+
var crossFieldValidation = {
|
|
1796
|
+
/**
|
|
1797
|
+
* Conditional required field validation
|
|
1798
|
+
*/
|
|
1799
|
+
conditionalRequired: (field2, conditionField, conditionValue) => {
|
|
1800
|
+
return z.object({
|
|
1801
|
+
[conditionField]: z.any(),
|
|
1802
|
+
[field2]: z.string()
|
|
1803
|
+
}).refine(
|
|
1804
|
+
(data) => {
|
|
1805
|
+
if (data[conditionField] === conditionValue) {
|
|
1806
|
+
return data[field2] && data[field2].trim().length > 0;
|
|
1807
|
+
}
|
|
1808
|
+
return true;
|
|
1809
|
+
},
|
|
1810
|
+
{
|
|
1811
|
+
message: "This field is required",
|
|
1812
|
+
path: [field2]
|
|
1813
|
+
}
|
|
1814
|
+
);
|
|
1815
|
+
},
|
|
1816
|
+
/**
|
|
1817
|
+
* Date range validation
|
|
1818
|
+
*/
|
|
1819
|
+
dateRange: (startField, endField) => {
|
|
1820
|
+
return z.object({
|
|
1821
|
+
[endField]: z.string(),
|
|
1822
|
+
[startField]: z.string()
|
|
1823
|
+
}).refine(
|
|
1824
|
+
(data) => {
|
|
1825
|
+
const startDate = new Date(data[startField]);
|
|
1826
|
+
const endDate = new Date(data[endField]);
|
|
1827
|
+
return startDate < endDate;
|
|
1828
|
+
},
|
|
1829
|
+
{
|
|
1830
|
+
message: "End date must be after start date",
|
|
1831
|
+
path: [endField]
|
|
1832
|
+
}
|
|
1833
|
+
);
|
|
1252
1834
|
},
|
|
1253
|
-
|
|
1254
|
-
|
|
1835
|
+
/**
|
|
1836
|
+
* Password confirmation validation
|
|
1837
|
+
*/
|
|
1838
|
+
passwordConfirmation: (passwordField, confirmField) => {
|
|
1839
|
+
return z.object({
|
|
1840
|
+
[confirmField]: z.string(),
|
|
1841
|
+
[passwordField]: z.string()
|
|
1842
|
+
}).refine((data) => data[passwordField] === data[confirmField], {
|
|
1843
|
+
message: "Passwords do not match",
|
|
1844
|
+
path: [confirmField]
|
|
1845
|
+
});
|
|
1255
1846
|
}
|
|
1256
|
-
|
|
1847
|
+
};
|
|
1257
1848
|
var commonValidations = {
|
|
1258
|
-
|
|
1259
|
-
confirmPassword: (passwordField) => createConfirmPasswordSchema(passwordField),
|
|
1849
|
+
confirmPassword: (passwordField, confirmField) => crossFieldValidation.passwordConfirmation(passwordField, confirmField),
|
|
1260
1850
|
date: (fieldName) => createDateSchema(fieldName),
|
|
1261
1851
|
email: createEmailSchema(),
|
|
1262
1852
|
file: (maxSizeInMB, allowedTypes) => createFileSchema(maxSizeInMB, allowedTypes),
|
|
@@ -1273,17 +1863,17 @@ var commonValidations = {
|
|
|
1273
1863
|
};
|
|
1274
1864
|
|
|
1275
1865
|
// src/index.ts
|
|
1276
|
-
import { useFormContext as
|
|
1866
|
+
import { useFormContext as useFormContext5 } from "react-hook-form";
|
|
1277
1867
|
|
|
1278
1868
|
// src/components/ZodForm.tsx
|
|
1279
|
-
import
|
|
1280
|
-
import { Button as
|
|
1869
|
+
import React21 from "react";
|
|
1870
|
+
import { Button as Button6 } from "@heroui/react";
|
|
1281
1871
|
|
|
1282
1872
|
// src/zod-integration.ts
|
|
1283
1873
|
import { useForm as useForm2 } from "react-hook-form";
|
|
1284
1874
|
import { z as z2 } from "zod";
|
|
1285
1875
|
function createZodResolver(schema) {
|
|
1286
|
-
return async (values
|
|
1876
|
+
return async (values) => {
|
|
1287
1877
|
try {
|
|
1288
1878
|
const result = await schema.parseAsync(values);
|
|
1289
1879
|
return {
|
|
@@ -1314,10 +1904,185 @@ function useZodForm(config) {
|
|
|
1314
1904
|
}
|
|
1315
1905
|
function createZodFormConfig(schema, fields, defaultValues) {
|
|
1316
1906
|
return {
|
|
1317
|
-
schema,
|
|
1318
1907
|
fields,
|
|
1319
|
-
|
|
1908
|
+
schema,
|
|
1909
|
+
...defaultValues && {
|
|
1910
|
+
defaultValues
|
|
1911
|
+
}
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
// src/hooks/useEnhancedFormState.ts
|
|
1916
|
+
import { useCallback, useEffect, useState as useState2 } from "react";
|
|
1917
|
+
function useEnhancedFormState(form, options = {}) {
|
|
1918
|
+
const {
|
|
1919
|
+
autoReset = true,
|
|
1920
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1921
|
+
errorMessage: _errorMessage = "An error occurred. Please try again.",
|
|
1922
|
+
onError,
|
|
1923
|
+
onSuccess,
|
|
1924
|
+
resetDelay = 3e3,
|
|
1925
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1926
|
+
successMessage: _successMessage = "Form submitted successfully!"
|
|
1927
|
+
} = options;
|
|
1928
|
+
const [status, setStatus] = useState2("idle");
|
|
1929
|
+
const [error, setError] = useState2(void 0);
|
|
1930
|
+
const [submittedData, setSubmittedData] = useState2(void 0);
|
|
1931
|
+
const { formState, getValues: _getValues } = form;
|
|
1932
|
+
const { dirtyFields, errors, isSubmitting, touchedFields } = formState;
|
|
1933
|
+
useEffect(() => {
|
|
1934
|
+
if (isSubmitting) {
|
|
1935
|
+
setStatus("submitting");
|
|
1936
|
+
}
|
|
1937
|
+
}, [isSubmitting]);
|
|
1938
|
+
useEffect(() => {
|
|
1939
|
+
if (status === "success" && autoReset) {
|
|
1940
|
+
const timer = setTimeout(() => {
|
|
1941
|
+
setStatus("idle");
|
|
1942
|
+
setSubmittedData(void 0);
|
|
1943
|
+
setError(void 0);
|
|
1944
|
+
}, resetDelay);
|
|
1945
|
+
return () => clearTimeout(timer);
|
|
1946
|
+
}
|
|
1947
|
+
}, [status, autoReset, resetDelay]);
|
|
1948
|
+
const handleSuccess = useCallback(
|
|
1949
|
+
(data) => {
|
|
1950
|
+
setStatus("success");
|
|
1951
|
+
setSubmittedData(data);
|
|
1952
|
+
setError(void 0);
|
|
1953
|
+
onSuccess?.(data);
|
|
1954
|
+
},
|
|
1955
|
+
[onSuccess]
|
|
1956
|
+
);
|
|
1957
|
+
const handleError = useCallback(
|
|
1958
|
+
(errorMessage) => {
|
|
1959
|
+
setStatus("error");
|
|
1960
|
+
setError(errorMessage);
|
|
1961
|
+
setSubmittedData(void 0);
|
|
1962
|
+
onError?.(errorMessage);
|
|
1963
|
+
},
|
|
1964
|
+
[onError]
|
|
1965
|
+
);
|
|
1966
|
+
const reset = useCallback(() => {
|
|
1967
|
+
setStatus("idle");
|
|
1968
|
+
setError(void 0);
|
|
1969
|
+
setSubmittedData(void 0);
|
|
1970
|
+
}, []);
|
|
1971
|
+
return {
|
|
1972
|
+
dirtyFields: new Set(Object.keys(dirtyFields)),
|
|
1973
|
+
error,
|
|
1974
|
+
errorCount: Object.keys(errors).length,
|
|
1975
|
+
handleError,
|
|
1976
|
+
handleSuccess,
|
|
1977
|
+
hasErrors: Object.keys(errors).length > 0,
|
|
1978
|
+
isError: status === "error",
|
|
1979
|
+
isSubmitting,
|
|
1980
|
+
isSuccess: status === "success",
|
|
1981
|
+
reset,
|
|
1982
|
+
status,
|
|
1983
|
+
submittedData,
|
|
1984
|
+
touchedFields: new Set(Object.keys(touchedFields))
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
// src/components/FormStatus.tsx
|
|
1989
|
+
import React20 from "react";
|
|
1990
|
+
import { Button as Button5 } from "@heroui/react";
|
|
1991
|
+
function FormStatus({
|
|
1992
|
+
className = "",
|
|
1993
|
+
onDismiss,
|
|
1994
|
+
showDetails = false,
|
|
1995
|
+
state
|
|
1996
|
+
}) {
|
|
1997
|
+
const { error, isError, isSubmitting, isSuccess, status, submittedData } = state;
|
|
1998
|
+
if (status === "idle") {
|
|
1999
|
+
return null;
|
|
2000
|
+
}
|
|
2001
|
+
if (isSubmitting) {
|
|
2002
|
+
return /* @__PURE__ */ React20.createElement(
|
|
2003
|
+
"div",
|
|
2004
|
+
{
|
|
2005
|
+
className: `flex items-center gap-3 p-4 bg-blue-50 border border-blue-200 rounded-lg ${className}`
|
|
2006
|
+
},
|
|
2007
|
+
/* @__PURE__ */ React20.createElement("span", { className: "text-blue-600" }, "\u23F3"),
|
|
2008
|
+
/* @__PURE__ */ React20.createElement("div", null, /* @__PURE__ */ React20.createElement("p", { className: "text-sm font-medium text-blue-900" }, "Submitting form..."), showDetails && /* @__PURE__ */ React20.createElement("p", { className: "text-xs text-blue-700" }, "Please wait while we process your request."))
|
|
2009
|
+
);
|
|
2010
|
+
}
|
|
2011
|
+
if (isSuccess) {
|
|
2012
|
+
return /* @__PURE__ */ React20.createElement(
|
|
2013
|
+
"div",
|
|
2014
|
+
{
|
|
2015
|
+
className: `flex items-center gap-3 p-4 bg-green-50 border border-green-200 rounded-lg ${className}`,
|
|
2016
|
+
"data-testid": "success-message"
|
|
2017
|
+
},
|
|
2018
|
+
/* @__PURE__ */ React20.createElement("span", { className: "text-green-600" }, "\u2705"),
|
|
2019
|
+
/* @__PURE__ */ React20.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React20.createElement("p", { className: "text-sm font-medium text-green-900" }, "Form submitted successfully!"), showDetails && submittedData && /* @__PURE__ */ React20.createElement("p", { className: "text-xs text-green-700" }, "Your data has been saved. Thank you for your submission.")),
|
|
2020
|
+
onDismiss && /* @__PURE__ */ React20.createElement(
|
|
2021
|
+
Button5,
|
|
2022
|
+
{
|
|
2023
|
+
size: "sm",
|
|
2024
|
+
variant: "light",
|
|
2025
|
+
isIconOnly: true,
|
|
2026
|
+
onPress: onDismiss,
|
|
2027
|
+
"aria-label": "Dismiss success message"
|
|
2028
|
+
},
|
|
2029
|
+
"\u2715"
|
|
2030
|
+
)
|
|
2031
|
+
);
|
|
2032
|
+
}
|
|
2033
|
+
if (isError && error) {
|
|
2034
|
+
return /* @__PURE__ */ React20.createElement(
|
|
2035
|
+
"div",
|
|
2036
|
+
{
|
|
2037
|
+
className: `flex items-center gap-3 p-4 bg-red-50 border border-red-200 rounded-lg ${className}`,
|
|
2038
|
+
"data-testid": "error-message"
|
|
2039
|
+
},
|
|
2040
|
+
/* @__PURE__ */ React20.createElement("span", { className: "text-red-600" }, "\u26A0\uFE0F"),
|
|
2041
|
+
/* @__PURE__ */ React20.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React20.createElement("p", { className: "text-sm font-medium text-red-900" }, "Error submitting form"), /* @__PURE__ */ React20.createElement("p", { className: "text-xs text-red-700" }, error)),
|
|
2042
|
+
onDismiss && /* @__PURE__ */ React20.createElement(
|
|
2043
|
+
Button5,
|
|
2044
|
+
{
|
|
2045
|
+
size: "sm",
|
|
2046
|
+
variant: "light",
|
|
2047
|
+
isIconOnly: true,
|
|
2048
|
+
onPress: onDismiss,
|
|
2049
|
+
"aria-label": "Dismiss error message"
|
|
2050
|
+
},
|
|
2051
|
+
"\u2715"
|
|
2052
|
+
)
|
|
2053
|
+
);
|
|
2054
|
+
}
|
|
2055
|
+
return null;
|
|
2056
|
+
}
|
|
2057
|
+
function FormToast({
|
|
2058
|
+
duration = 5e3,
|
|
2059
|
+
onDismiss,
|
|
2060
|
+
position = "top-right",
|
|
2061
|
+
state
|
|
2062
|
+
}) {
|
|
2063
|
+
const [isVisible, setIsVisible] = React20.useState(false);
|
|
2064
|
+
React20.useEffect(() => {
|
|
2065
|
+
if (state.isSuccess || state.isError) {
|
|
2066
|
+
setIsVisible(true);
|
|
2067
|
+
if (duration > 0) {
|
|
2068
|
+
const timer = setTimeout(() => {
|
|
2069
|
+
setIsVisible(false);
|
|
2070
|
+
onDismiss?.();
|
|
2071
|
+
}, duration);
|
|
2072
|
+
return () => clearTimeout(timer);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
}, [state.isSuccess, state.isError, duration, onDismiss]);
|
|
2076
|
+
if (!isVisible) {
|
|
2077
|
+
return null;
|
|
2078
|
+
}
|
|
2079
|
+
const positionClasses = {
|
|
2080
|
+
"bottom-left": "bottom-4 left-4",
|
|
2081
|
+
"bottom-right": "bottom-4 right-4",
|
|
2082
|
+
"top-left": "top-4 left-4",
|
|
2083
|
+
"top-right": "top-4 right-4"
|
|
1320
2084
|
};
|
|
2085
|
+
return /* @__PURE__ */ React20.createElement("div", { className: `fixed z-50 ${positionClasses[position]}` }, /* @__PURE__ */ React20.createElement(FormStatus, { state, onDismiss }));
|
|
1321
2086
|
}
|
|
1322
2087
|
|
|
1323
2088
|
// src/components/ZodForm.tsx
|
|
@@ -1325,7 +2090,6 @@ function ZodForm({
|
|
|
1325
2090
|
className,
|
|
1326
2091
|
columns = 1,
|
|
1327
2092
|
config,
|
|
1328
|
-
errorDisplay = "inline",
|
|
1329
2093
|
layout = "vertical",
|
|
1330
2094
|
onError,
|
|
1331
2095
|
onSubmit,
|
|
@@ -1340,97 +2104,84 @@ function ZodForm({
|
|
|
1340
2104
|
title
|
|
1341
2105
|
}) {
|
|
1342
2106
|
const form = useZodForm(config);
|
|
1343
|
-
const
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
2107
|
+
const enhancedState = useEnhancedFormState(form, {
|
|
2108
|
+
autoReset: true,
|
|
2109
|
+
onError: (error) => onError?.({ field: "form", message: error }),
|
|
2110
|
+
onSuccess,
|
|
2111
|
+
resetDelay: 3e3
|
|
1348
2112
|
});
|
|
1349
2113
|
const handleSubmit = async () => {
|
|
1350
|
-
setSubmissionState((prev) => ({
|
|
1351
|
-
...prev,
|
|
1352
|
-
error: void 0,
|
|
1353
|
-
isSubmitting: true
|
|
1354
|
-
}));
|
|
1355
|
-
const isValid = await form.trigger();
|
|
1356
|
-
if (!isValid) {
|
|
1357
|
-
setSubmissionState({
|
|
1358
|
-
error: "Please fix the validation errors above",
|
|
1359
|
-
isSubmitted: true,
|
|
1360
|
-
isSubmitting: false,
|
|
1361
|
-
isSuccess: false
|
|
1362
|
-
});
|
|
1363
|
-
return;
|
|
1364
|
-
}
|
|
1365
2114
|
try {
|
|
1366
|
-
await form.handleSubmit(
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
2115
|
+
await form.handleSubmit(
|
|
2116
|
+
async (formData) => {
|
|
2117
|
+
await onSubmit(formData);
|
|
2118
|
+
enhancedState.handleSuccess(formData);
|
|
2119
|
+
},
|
|
2120
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2121
|
+
(_errors) => {
|
|
2122
|
+
enhancedState.handleError("Please fix the validation errors above");
|
|
2123
|
+
}
|
|
2124
|
+
)();
|
|
1376
2125
|
} catch (error) {
|
|
1377
2126
|
const errorMessage = error instanceof Error ? error.message : "An error occurred";
|
|
1378
|
-
|
|
1379
|
-
error: errorMessage,
|
|
1380
|
-
isSubmitted: true,
|
|
1381
|
-
isSubmitting: false,
|
|
1382
|
-
isSuccess: false
|
|
1383
|
-
});
|
|
1384
|
-
onError?.({
|
|
1385
|
-
message: errorMessage
|
|
1386
|
-
});
|
|
2127
|
+
enhancedState.handleError(errorMessage);
|
|
1387
2128
|
}
|
|
1388
2129
|
};
|
|
1389
2130
|
const resetForm = () => {
|
|
1390
2131
|
form.reset();
|
|
1391
|
-
|
|
1392
|
-
error: void 0,
|
|
1393
|
-
isSubmitted: false,
|
|
1394
|
-
isSubmitting: false,
|
|
1395
|
-
isSuccess: false
|
|
1396
|
-
});
|
|
2132
|
+
enhancedState.reset();
|
|
1397
2133
|
};
|
|
1398
2134
|
const renderFields = () => {
|
|
1399
2135
|
if (layout === "grid") {
|
|
1400
|
-
return /* @__PURE__ */
|
|
2136
|
+
return /* @__PURE__ */ React21.createElement(
|
|
1401
2137
|
"div",
|
|
1402
2138
|
{
|
|
1403
2139
|
className: `grid gap-${spacing} ${columns === 1 ? "grid-cols-1" : columns === 2 ? "grid-cols-1 md:grid-cols-2" : "grid-cols-1 md:grid-cols-2 lg:grid-cols-3"}`
|
|
1404
2140
|
},
|
|
1405
|
-
config.fields.map((
|
|
2141
|
+
config.fields.map((field2) => /* @__PURE__ */ React21.createElement(
|
|
1406
2142
|
FormField,
|
|
1407
2143
|
{
|
|
1408
|
-
key:
|
|
1409
|
-
config:
|
|
2144
|
+
key: field2.name,
|
|
2145
|
+
config: field2,
|
|
1410
2146
|
form,
|
|
1411
|
-
submissionState
|
|
2147
|
+
submissionState: {
|
|
2148
|
+
error: enhancedState.error,
|
|
2149
|
+
isSubmitted: enhancedState.status !== "idle",
|
|
2150
|
+
isSubmitting: enhancedState.isSubmitting,
|
|
2151
|
+
isSuccess: enhancedState.isSuccess
|
|
2152
|
+
}
|
|
1412
2153
|
}
|
|
1413
2154
|
))
|
|
1414
2155
|
);
|
|
1415
2156
|
}
|
|
1416
2157
|
if (layout === "horizontal") {
|
|
1417
|
-
return /* @__PURE__ */
|
|
2158
|
+
return /* @__PURE__ */ React21.createElement("div", { className: `grid gap-${spacing} grid-cols-1 md:grid-cols-2` }, config.fields.map((field2) => /* @__PURE__ */ React21.createElement(
|
|
1418
2159
|
FormField,
|
|
1419
2160
|
{
|
|
1420
|
-
key:
|
|
1421
|
-
config:
|
|
2161
|
+
key: field2.name,
|
|
2162
|
+
config: field2,
|
|
1422
2163
|
form,
|
|
1423
|
-
submissionState
|
|
2164
|
+
submissionState: {
|
|
2165
|
+
error: enhancedState.error,
|
|
2166
|
+
isSubmitted: enhancedState.status !== "idle",
|
|
2167
|
+
isSubmitting: enhancedState.isSubmitting,
|
|
2168
|
+
isSuccess: enhancedState.isSuccess
|
|
2169
|
+
}
|
|
1424
2170
|
}
|
|
1425
2171
|
)));
|
|
1426
2172
|
}
|
|
1427
|
-
return /* @__PURE__ */
|
|
2173
|
+
return /* @__PURE__ */ React21.createElement("div", { className: `space-y-${spacing}` }, config.fields.map((field2) => /* @__PURE__ */ React21.createElement(
|
|
1428
2174
|
FormField,
|
|
1429
2175
|
{
|
|
1430
|
-
key:
|
|
1431
|
-
config:
|
|
2176
|
+
key: field2.name,
|
|
2177
|
+
config: field2,
|
|
1432
2178
|
form,
|
|
1433
|
-
submissionState
|
|
2179
|
+
submissionState: {
|
|
2180
|
+
error: enhancedState.error,
|
|
2181
|
+
isSubmitted: enhancedState.status !== "idle",
|
|
2182
|
+
isSubmitting: enhancedState.isSubmitting,
|
|
2183
|
+
isSuccess: enhancedState.isSuccess
|
|
2184
|
+
}
|
|
1434
2185
|
}
|
|
1435
2186
|
)));
|
|
1436
2187
|
};
|
|
@@ -1438,7 +2189,7 @@ function ZodForm({
|
|
|
1438
2189
|
e.preventDefault();
|
|
1439
2190
|
void handleSubmit();
|
|
1440
2191
|
};
|
|
1441
|
-
|
|
2192
|
+
React21.useEffect(() => {
|
|
1442
2193
|
if (config.onError && Object.keys(form.formState.errors).length > 0) {
|
|
1443
2194
|
config.onError(form.formState.errors);
|
|
1444
2195
|
}
|
|
@@ -1447,42 +2198,33 @@ function ZodForm({
|
|
|
1447
2198
|
return render({
|
|
1448
2199
|
errors: form.formState.errors,
|
|
1449
2200
|
form,
|
|
1450
|
-
isSubmitted:
|
|
1451
|
-
isSubmitting:
|
|
1452
|
-
isSuccess:
|
|
2201
|
+
isSubmitted: enhancedState.status !== "idle",
|
|
2202
|
+
isSubmitting: enhancedState.isSubmitting,
|
|
2203
|
+
isSuccess: enhancedState.isSuccess,
|
|
1453
2204
|
values: form.getValues()
|
|
1454
2205
|
});
|
|
1455
2206
|
}
|
|
1456
|
-
return /* @__PURE__ */
|
|
1457
|
-
|
|
1458
|
-
{
|
|
1459
|
-
className: "mb-6 p-4 bg-success-50 border border-success-200 rounded-lg",
|
|
1460
|
-
"data-testid": "success-message"
|
|
1461
|
-
},
|
|
1462
|
-
/* @__PURE__ */ React16.createElement("p", { className: "text-success-800 font-medium" }, "Success!"),
|
|
1463
|
-
/* @__PURE__ */ React16.createElement("p", { className: "text-success-700 text-sm mt-1" }, "Your request has been processed successfully.")
|
|
1464
|
-
), submissionState.error && errorDisplay !== "none" && /* @__PURE__ */ React16.createElement(
|
|
1465
|
-
"div",
|
|
2207
|
+
return /* @__PURE__ */ React21.createElement("form", { className, role: "form", onSubmit: handleFormSubmit }, title && /* @__PURE__ */ React21.createElement("div", { className: "mb-6" }, /* @__PURE__ */ React21.createElement("h2", { className: "text-xl font-semibold text-foreground mb-2" }, title), subtitle && /* @__PURE__ */ React21.createElement("p", { className: "text-sm text-muted-foreground" }, subtitle)), /* @__PURE__ */ React21.createElement(
|
|
2208
|
+
FormStatus,
|
|
1466
2209
|
{
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
Button3,
|
|
2210
|
+
state: enhancedState,
|
|
2211
|
+
onDismiss: () => enhancedState.reset(),
|
|
2212
|
+
showDetails: true
|
|
2213
|
+
}
|
|
2214
|
+
), renderFields(), /* @__PURE__ */ React21.createElement("div", { className: "mt-6 flex gap-3 justify-end" }, /* @__PURE__ */ React21.createElement(
|
|
2215
|
+
Button6,
|
|
1474
2216
|
{
|
|
1475
2217
|
color: "primary",
|
|
1476
|
-
isDisabled:
|
|
1477
|
-
isLoading:
|
|
2218
|
+
isDisabled: enhancedState.isSubmitting,
|
|
2219
|
+
isLoading: enhancedState.isSubmitting,
|
|
1478
2220
|
type: "submit",
|
|
1479
2221
|
...submitButtonProps
|
|
1480
2222
|
},
|
|
1481
|
-
submitButtonText
|
|
1482
|
-
), showResetButton && /* @__PURE__ */
|
|
1483
|
-
|
|
2223
|
+
enhancedState.isSuccess ? "Success!" : submitButtonText
|
|
2224
|
+
), showResetButton && /* @__PURE__ */ React21.createElement(
|
|
2225
|
+
Button6,
|
|
1484
2226
|
{
|
|
1485
|
-
isDisabled:
|
|
2227
|
+
isDisabled: enhancedState.isSubmitting,
|
|
1486
2228
|
type: "button",
|
|
1487
2229
|
variant: "bordered",
|
|
1488
2230
|
onPress: resetForm
|
|
@@ -1490,29 +2232,1257 @@ function ZodForm({
|
|
|
1490
2232
|
resetButtonText
|
|
1491
2233
|
)));
|
|
1492
2234
|
}
|
|
2235
|
+
|
|
2236
|
+
// src/builders/BasicFormBuilder.ts
|
|
2237
|
+
var BasicFormBuilder = class {
|
|
2238
|
+
constructor() {
|
|
2239
|
+
this.fields = [];
|
|
2240
|
+
}
|
|
2241
|
+
/**
|
|
2242
|
+
* Add an input field
|
|
2243
|
+
*/
|
|
2244
|
+
input(name, label, type = "text") {
|
|
2245
|
+
this.fields.push({
|
|
2246
|
+
inputProps: { type },
|
|
2247
|
+
label,
|
|
2248
|
+
name,
|
|
2249
|
+
type: "input"
|
|
2250
|
+
});
|
|
2251
|
+
return this;
|
|
2252
|
+
}
|
|
2253
|
+
/**
|
|
2254
|
+
* Add a textarea field
|
|
2255
|
+
*/
|
|
2256
|
+
textarea(name, label, placeholder) {
|
|
2257
|
+
this.fields.push({
|
|
2258
|
+
label,
|
|
2259
|
+
name,
|
|
2260
|
+
textareaProps: { placeholder },
|
|
2261
|
+
type: "textarea"
|
|
2262
|
+
});
|
|
2263
|
+
return this;
|
|
2264
|
+
}
|
|
2265
|
+
/**
|
|
2266
|
+
* Add a select field
|
|
2267
|
+
*/
|
|
2268
|
+
select(name, label, options) {
|
|
2269
|
+
this.fields.push({
|
|
2270
|
+
label,
|
|
2271
|
+
name,
|
|
2272
|
+
options,
|
|
2273
|
+
type: "select"
|
|
2274
|
+
});
|
|
2275
|
+
return this;
|
|
2276
|
+
}
|
|
2277
|
+
/**
|
|
2278
|
+
* Add a checkbox field
|
|
2279
|
+
*/
|
|
2280
|
+
checkbox(name, label) {
|
|
2281
|
+
this.fields.push({
|
|
2282
|
+
label,
|
|
2283
|
+
name,
|
|
2284
|
+
type: "checkbox"
|
|
2285
|
+
});
|
|
2286
|
+
return this;
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Add a switch field
|
|
2290
|
+
*/
|
|
2291
|
+
switch(name, label) {
|
|
2292
|
+
this.fields.push({
|
|
2293
|
+
label,
|
|
2294
|
+
name,
|
|
2295
|
+
type: "switch"
|
|
2296
|
+
});
|
|
2297
|
+
return this;
|
|
2298
|
+
}
|
|
2299
|
+
/**
|
|
2300
|
+
* Build the final field configuration array
|
|
2301
|
+
*/
|
|
2302
|
+
build() {
|
|
2303
|
+
return this.fields;
|
|
2304
|
+
}
|
|
2305
|
+
};
|
|
2306
|
+
function createBasicFormBuilder() {
|
|
2307
|
+
return new BasicFormBuilder();
|
|
2308
|
+
}
|
|
2309
|
+
var FormFieldHelpers = {
|
|
2310
|
+
/**
|
|
2311
|
+
* Create a checkbox field
|
|
2312
|
+
*/
|
|
2313
|
+
checkbox: (name, label) => ({
|
|
2314
|
+
label,
|
|
2315
|
+
name,
|
|
2316
|
+
type: "checkbox"
|
|
2317
|
+
}),
|
|
2318
|
+
/**
|
|
2319
|
+
* Create an input field
|
|
2320
|
+
*/
|
|
2321
|
+
input: (name, label, type = "text") => ({
|
|
2322
|
+
inputProps: { type },
|
|
2323
|
+
label,
|
|
2324
|
+
name,
|
|
2325
|
+
type: "input"
|
|
2326
|
+
}),
|
|
2327
|
+
/**
|
|
2328
|
+
* Create a select field
|
|
2329
|
+
*/
|
|
2330
|
+
select: (name, label, options) => ({
|
|
2331
|
+
label,
|
|
2332
|
+
name,
|
|
2333
|
+
options,
|
|
2334
|
+
type: "select"
|
|
2335
|
+
}),
|
|
2336
|
+
/**
|
|
2337
|
+
* Create a switch field
|
|
2338
|
+
*/
|
|
2339
|
+
switch: (name, label) => ({
|
|
2340
|
+
label,
|
|
2341
|
+
name,
|
|
2342
|
+
type: "switch"
|
|
2343
|
+
}),
|
|
2344
|
+
/**
|
|
2345
|
+
* Create a textarea field
|
|
2346
|
+
*/
|
|
2347
|
+
textarea: (name, label, placeholder) => ({
|
|
2348
|
+
label,
|
|
2349
|
+
name,
|
|
2350
|
+
textareaProps: { placeholder },
|
|
2351
|
+
type: "textarea"
|
|
2352
|
+
})
|
|
2353
|
+
};
|
|
2354
|
+
var CommonFields = {
|
|
2355
|
+
/**
|
|
2356
|
+
* Address fields
|
|
2357
|
+
*/
|
|
2358
|
+
address: () => [
|
|
2359
|
+
FormFieldHelpers.input("street", "Street Address"),
|
|
2360
|
+
FormFieldHelpers.input("city", "City"),
|
|
2361
|
+
FormFieldHelpers.input("state", "State/Province"),
|
|
2362
|
+
FormFieldHelpers.input("zipCode", "ZIP/Postal Code"),
|
|
2363
|
+
FormFieldHelpers.select("country", "Country", [
|
|
2364
|
+
{ label: "Select a country", value: "" },
|
|
2365
|
+
{ label: "United States", value: "us" },
|
|
2366
|
+
{ label: "Canada", value: "ca" },
|
|
2367
|
+
{ label: "United Kingdom", value: "uk" },
|
|
2368
|
+
{ label: "Australia", value: "au" },
|
|
2369
|
+
{ label: "Germany", value: "de" },
|
|
2370
|
+
{ label: "France", value: "fr" }
|
|
2371
|
+
])
|
|
2372
|
+
],
|
|
2373
|
+
/**
|
|
2374
|
+
* Personal information fields
|
|
2375
|
+
*/
|
|
2376
|
+
personal: () => [
|
|
2377
|
+
FormFieldHelpers.input("firstName", "First Name"),
|
|
2378
|
+
FormFieldHelpers.input("lastName", "Last Name"),
|
|
2379
|
+
FormFieldHelpers.input("email", "Email", "email"),
|
|
2380
|
+
FormFieldHelpers.input("phone", "Phone", "tel")
|
|
2381
|
+
],
|
|
2382
|
+
/**
|
|
2383
|
+
* Terms and conditions fields
|
|
2384
|
+
*/
|
|
2385
|
+
terms: () => [
|
|
2386
|
+
FormFieldHelpers.checkbox(
|
|
2387
|
+
"terms",
|
|
2388
|
+
"I agree to the terms and conditions"
|
|
2389
|
+
),
|
|
2390
|
+
FormFieldHelpers.checkbox(
|
|
2391
|
+
"privacy",
|
|
2392
|
+
"I agree to the privacy policy"
|
|
2393
|
+
),
|
|
2394
|
+
FormFieldHelpers.checkbox(
|
|
2395
|
+
"newsletter",
|
|
2396
|
+
"Subscribe to newsletter"
|
|
2397
|
+
)
|
|
2398
|
+
]
|
|
2399
|
+
};
|
|
2400
|
+
|
|
2401
|
+
// src/builders/AdvancedFormBuilder.ts
|
|
2402
|
+
function inputField(name, label, props) {
|
|
2403
|
+
return {
|
|
2404
|
+
label,
|
|
2405
|
+
name,
|
|
2406
|
+
type: "input",
|
|
2407
|
+
...props && {
|
|
2408
|
+
inputProps: {
|
|
2409
|
+
className: props.className,
|
|
2410
|
+
description: props.description,
|
|
2411
|
+
disabled: props.isDisabled,
|
|
2412
|
+
placeholder: props.placeholder,
|
|
2413
|
+
type: props.type || "text"
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2418
|
+
function textareaField(name, label, props) {
|
|
2419
|
+
return {
|
|
2420
|
+
label,
|
|
2421
|
+
name,
|
|
2422
|
+
type: "textarea",
|
|
2423
|
+
...props && {
|
|
2424
|
+
textareaProps: {
|
|
2425
|
+
className: props.className,
|
|
2426
|
+
description: props.description,
|
|
2427
|
+
disabled: props.isDisabled,
|
|
2428
|
+
placeholder: props.placeholder,
|
|
2429
|
+
rows: props.rows
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
};
|
|
2433
|
+
}
|
|
2434
|
+
function selectField(name, label, options) {
|
|
2435
|
+
return {
|
|
2436
|
+
label,
|
|
2437
|
+
name,
|
|
2438
|
+
options,
|
|
2439
|
+
type: "select"
|
|
2440
|
+
};
|
|
2441
|
+
}
|
|
2442
|
+
function checkboxField(name, label, props) {
|
|
2443
|
+
return {
|
|
2444
|
+
label,
|
|
2445
|
+
name,
|
|
2446
|
+
type: "checkbox",
|
|
2447
|
+
...props && {
|
|
2448
|
+
checkboxProps: {
|
|
2449
|
+
className: props.className,
|
|
2450
|
+
disabled: props.isDisabled
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
function switchField(name, label, props) {
|
|
2456
|
+
return {
|
|
2457
|
+
label,
|
|
2458
|
+
name,
|
|
2459
|
+
type: "switch",
|
|
2460
|
+
...props && {
|
|
2461
|
+
switchProps: {
|
|
2462
|
+
className: props.className,
|
|
2463
|
+
disabled: props.isDisabled
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
function radioField(name, label, options, props) {
|
|
2469
|
+
return {
|
|
2470
|
+
label,
|
|
2471
|
+
name,
|
|
2472
|
+
radioOptions: options,
|
|
2473
|
+
type: "radio",
|
|
2474
|
+
...props && {
|
|
2475
|
+
radioProps: {
|
|
2476
|
+
className: props.className,
|
|
2477
|
+
isDisabled: props.isDisabled,
|
|
2478
|
+
orientation: props.orientation
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
};
|
|
2482
|
+
}
|
|
2483
|
+
function sliderField(name, label, props) {
|
|
2484
|
+
return {
|
|
2485
|
+
label,
|
|
2486
|
+
name,
|
|
2487
|
+
type: "slider",
|
|
2488
|
+
...props && {
|
|
2489
|
+
sliderProps: {
|
|
2490
|
+
className: props.className || "",
|
|
2491
|
+
disabled: props.isDisabled || false,
|
|
2492
|
+
max: props.max || 100,
|
|
2493
|
+
min: props.min || 0,
|
|
2494
|
+
step: props.step || 1
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
function dateField(name, label, props) {
|
|
2500
|
+
return {
|
|
2501
|
+
label,
|
|
2502
|
+
name,
|
|
2503
|
+
type: "date",
|
|
2504
|
+
...props && {
|
|
2505
|
+
dateProps: {
|
|
2506
|
+
className: props.className || "",
|
|
2507
|
+
disabled: props.isDisabled || false,
|
|
2508
|
+
placeholder: props.placeholder || ""
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
};
|
|
2512
|
+
}
|
|
2513
|
+
function fileField(name, label, props) {
|
|
2514
|
+
return {
|
|
2515
|
+
label,
|
|
2516
|
+
name,
|
|
2517
|
+
type: "file",
|
|
2518
|
+
...props && {
|
|
2519
|
+
fileProps: {
|
|
2520
|
+
accept: props.accept || "",
|
|
2521
|
+
className: props.className || "",
|
|
2522
|
+
disabled: props.isDisabled || false,
|
|
2523
|
+
multiple: props.multiple || false
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
function fontPickerField(name, label, props) {
|
|
2529
|
+
return {
|
|
2530
|
+
label,
|
|
2531
|
+
name,
|
|
2532
|
+
type: "fontPicker",
|
|
2533
|
+
...props && {
|
|
2534
|
+
fontPickerProps: {
|
|
2535
|
+
className: props.className || "",
|
|
2536
|
+
disabled: props.isDisabled || false
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
function createField(type, name, label, optionsOrProps, props) {
|
|
2542
|
+
switch (type) {
|
|
2543
|
+
case "input":
|
|
2544
|
+
return inputField(name, label, optionsOrProps);
|
|
2545
|
+
case "textarea":
|
|
2546
|
+
return textareaField(name, label, optionsOrProps);
|
|
2547
|
+
case "select":
|
|
2548
|
+
return selectField(name, label, optionsOrProps);
|
|
2549
|
+
case "checkbox":
|
|
2550
|
+
return checkboxField(name, label, optionsOrProps);
|
|
2551
|
+
case "switch":
|
|
2552
|
+
return switchField(name, label, optionsOrProps);
|
|
2553
|
+
case "radio":
|
|
2554
|
+
return radioField(name, label, optionsOrProps, props);
|
|
2555
|
+
case "slider":
|
|
2556
|
+
return sliderField(name, label, optionsOrProps);
|
|
2557
|
+
case "date":
|
|
2558
|
+
return dateField(name, label, optionsOrProps);
|
|
2559
|
+
case "file":
|
|
2560
|
+
return fileField(name, label, optionsOrProps);
|
|
2561
|
+
case "fontPicker":
|
|
2562
|
+
return fontPickerField(name, label, optionsOrProps);
|
|
2563
|
+
default:
|
|
2564
|
+
throw new Error(`Unknown field type: ${type}`);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
var AdvancedFieldBuilder = class {
|
|
2568
|
+
constructor() {
|
|
2569
|
+
this.fields = [];
|
|
2570
|
+
}
|
|
2571
|
+
field(type, name, label, optionsOrProps, props) {
|
|
2572
|
+
this.fields.push(createField(type, name, label, optionsOrProps, props));
|
|
2573
|
+
return this;
|
|
2574
|
+
}
|
|
2575
|
+
/**
|
|
2576
|
+
* Add a conditional field that shows/hides based on form data
|
|
2577
|
+
*/
|
|
2578
|
+
conditionalField(name, condition, field2) {
|
|
2579
|
+
this.fields.push({
|
|
2580
|
+
condition,
|
|
2581
|
+
field: field2,
|
|
2582
|
+
name,
|
|
2583
|
+
type: "conditional"
|
|
2584
|
+
});
|
|
2585
|
+
return this;
|
|
2586
|
+
}
|
|
2587
|
+
/**
|
|
2588
|
+
* Add a field array for dynamic repeating field groups
|
|
2589
|
+
*/
|
|
2590
|
+
fieldArray(name, label, fields, options) {
|
|
2591
|
+
this.fields.push({
|
|
2592
|
+
addButtonText: options?.addButtonText,
|
|
2593
|
+
fields,
|
|
2594
|
+
label,
|
|
2595
|
+
max: options?.max,
|
|
2596
|
+
min: options?.min,
|
|
2597
|
+
name,
|
|
2598
|
+
removeButtonText: options?.removeButtonText,
|
|
2599
|
+
type: "fieldArray"
|
|
2600
|
+
});
|
|
2601
|
+
return this;
|
|
2602
|
+
}
|
|
2603
|
+
/**
|
|
2604
|
+
* Add a dynamic section that shows/hides based on form data
|
|
2605
|
+
*/
|
|
2606
|
+
dynamicSection(name, condition, fields, options) {
|
|
2607
|
+
this.fields.push({
|
|
2608
|
+
condition,
|
|
2609
|
+
description: options?.description,
|
|
2610
|
+
fields,
|
|
2611
|
+
name,
|
|
2612
|
+
title: options?.title,
|
|
2613
|
+
type: "dynamicSection"
|
|
2614
|
+
});
|
|
2615
|
+
return this;
|
|
2616
|
+
}
|
|
2617
|
+
/**
|
|
2618
|
+
* Build the final field configuration array
|
|
2619
|
+
*/
|
|
2620
|
+
build() {
|
|
2621
|
+
return this.fields;
|
|
2622
|
+
}
|
|
2623
|
+
};
|
|
2624
|
+
var FieldArrayItemBuilder = class {
|
|
2625
|
+
constructor() {
|
|
2626
|
+
this.fields = [];
|
|
2627
|
+
}
|
|
2628
|
+
field(type, name, label, optionsOrProps, props) {
|
|
2629
|
+
this.fields.push(createField(type, name, label, optionsOrProps, props));
|
|
2630
|
+
return this;
|
|
2631
|
+
}
|
|
2632
|
+
/**
|
|
2633
|
+
* Build the field array item configuration
|
|
2634
|
+
*/
|
|
2635
|
+
build() {
|
|
2636
|
+
return this.fields;
|
|
2637
|
+
}
|
|
2638
|
+
};
|
|
2639
|
+
function createFieldArrayItemBuilder() {
|
|
2640
|
+
return new FieldArrayItemBuilder();
|
|
2641
|
+
}
|
|
2642
|
+
var FieldArrayBuilder = class {
|
|
2643
|
+
constructor(arrayName) {
|
|
2644
|
+
this.arrayName = arrayName;
|
|
2645
|
+
this.fields = [];
|
|
2646
|
+
}
|
|
2647
|
+
field(type, name, label, optionsOrProps, props) {
|
|
2648
|
+
const fullPath = `${this.arrayName}.${name}`;
|
|
2649
|
+
const fieldConfig = createField(
|
|
2650
|
+
type,
|
|
2651
|
+
fullPath,
|
|
2652
|
+
label,
|
|
2653
|
+
optionsOrProps,
|
|
2654
|
+
props
|
|
2655
|
+
);
|
|
2656
|
+
this.fields.push(fieldConfig);
|
|
2657
|
+
return this;
|
|
2658
|
+
}
|
|
2659
|
+
build() {
|
|
2660
|
+
return this.fields;
|
|
2661
|
+
}
|
|
2662
|
+
};
|
|
2663
|
+
function createFieldArrayBuilder(arrayName) {
|
|
2664
|
+
return new FieldArrayBuilder(arrayName);
|
|
2665
|
+
}
|
|
2666
|
+
function createAdvancedBuilder() {
|
|
2667
|
+
return new AdvancedFieldBuilder();
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
// src/builders/TypeInferredBuilder.ts
|
|
2671
|
+
import { z as z3 } from "zod";
|
|
2672
|
+
var TypeInferredBuilder = class {
|
|
2673
|
+
constructor() {
|
|
2674
|
+
this.schemaFields = {};
|
|
2675
|
+
this.formFields = [];
|
|
2676
|
+
}
|
|
2677
|
+
/**
|
|
2678
|
+
* Add a text field
|
|
2679
|
+
*/
|
|
2680
|
+
text(name, label, options) {
|
|
2681
|
+
const { maxLength, minLength, pattern, ...fieldOptions } = options || {};
|
|
2682
|
+
let zodType = z3.string();
|
|
2683
|
+
if (minLength)
|
|
2684
|
+
zodType = zodType.min(
|
|
2685
|
+
minLength,
|
|
2686
|
+
`${label} must be at least ${minLength} characters`
|
|
2687
|
+
);
|
|
2688
|
+
if (maxLength)
|
|
2689
|
+
zodType = zodType.max(
|
|
2690
|
+
maxLength,
|
|
2691
|
+
`${label} must be no more than ${maxLength} characters`
|
|
2692
|
+
);
|
|
2693
|
+
if (pattern)
|
|
2694
|
+
zodType = zodType.regex(
|
|
2695
|
+
new RegExp(pattern),
|
|
2696
|
+
`${label} format is invalid`
|
|
2697
|
+
);
|
|
2698
|
+
this.schemaFields[name] = zodType;
|
|
2699
|
+
this.formFields.push({
|
|
2700
|
+
inputProps: { type: "text", ...fieldOptions },
|
|
2701
|
+
label,
|
|
2702
|
+
name,
|
|
2703
|
+
type: "input"
|
|
2704
|
+
});
|
|
2705
|
+
return this;
|
|
2706
|
+
}
|
|
2707
|
+
/**
|
|
2708
|
+
* Add an email field
|
|
2709
|
+
*/
|
|
2710
|
+
email(name, label, options) {
|
|
2711
|
+
this.schemaFields[name] = z3.string().email(`Please enter a valid email address`);
|
|
2712
|
+
this.formFields.push({
|
|
2713
|
+
inputProps: { type: "email", ...options },
|
|
2714
|
+
label,
|
|
2715
|
+
name,
|
|
2716
|
+
type: "input"
|
|
2717
|
+
});
|
|
2718
|
+
return this;
|
|
2719
|
+
}
|
|
2720
|
+
/**
|
|
2721
|
+
* Add a number field
|
|
2722
|
+
*/
|
|
2723
|
+
number(name, label, options) {
|
|
2724
|
+
const { max, min, step, ...fieldOptions } = options || {};
|
|
2725
|
+
let zodType = z3.number();
|
|
2726
|
+
if (min !== void 0)
|
|
2727
|
+
zodType = zodType.min(min, `${label} must be at least ${min}`);
|
|
2728
|
+
if (max !== void 0)
|
|
2729
|
+
zodType = zodType.max(max, `${label} must be no more than ${max}`);
|
|
2730
|
+
this.schemaFields[name] = zodType;
|
|
2731
|
+
this.formFields.push({
|
|
2732
|
+
inputProps: { max, min, step, type: "number", ...fieldOptions },
|
|
2733
|
+
label,
|
|
2734
|
+
name,
|
|
2735
|
+
type: "input"
|
|
2736
|
+
});
|
|
2737
|
+
return this;
|
|
2738
|
+
}
|
|
2739
|
+
/**
|
|
2740
|
+
* Add a textarea field
|
|
2741
|
+
*/
|
|
2742
|
+
textarea(name, label, options) {
|
|
2743
|
+
const { minLength, ...fieldOptions } = options || {};
|
|
2744
|
+
let zodType = z3.string();
|
|
2745
|
+
if (minLength)
|
|
2746
|
+
zodType = zodType.min(
|
|
2747
|
+
minLength,
|
|
2748
|
+
`${label} must be at least ${minLength} characters`
|
|
2749
|
+
);
|
|
2750
|
+
this.schemaFields[name] = zodType;
|
|
2751
|
+
this.formFields.push({
|
|
2752
|
+
label,
|
|
2753
|
+
name,
|
|
2754
|
+
textareaProps: fieldOptions,
|
|
2755
|
+
type: "textarea"
|
|
2756
|
+
});
|
|
2757
|
+
return this;
|
|
2758
|
+
}
|
|
2759
|
+
/**
|
|
2760
|
+
* Add a select field
|
|
2761
|
+
*/
|
|
2762
|
+
select(name, label, options) {
|
|
2763
|
+
this.schemaFields[name] = z3.string().min(1, `Please select a ${label.toLowerCase()}`);
|
|
2764
|
+
this.formFields.push({
|
|
2765
|
+
label,
|
|
2766
|
+
name,
|
|
2767
|
+
options,
|
|
2768
|
+
type: "select"
|
|
2769
|
+
});
|
|
2770
|
+
return this;
|
|
2771
|
+
}
|
|
2772
|
+
/**
|
|
2773
|
+
* Add a checkbox field
|
|
2774
|
+
*/
|
|
2775
|
+
checkbox(name, label, options) {
|
|
2776
|
+
const { required = false, ...fieldOptions } = options || {};
|
|
2777
|
+
let zodType = z3.boolean();
|
|
2778
|
+
if (required) {
|
|
2779
|
+
zodType = zodType.refine(
|
|
2780
|
+
(val) => val === true,
|
|
2781
|
+
`You must agree to ${label.toLowerCase()}`
|
|
2782
|
+
);
|
|
2783
|
+
}
|
|
2784
|
+
this.schemaFields[name] = zodType;
|
|
2785
|
+
this.formFields.push({
|
|
2786
|
+
checkboxProps: fieldOptions,
|
|
2787
|
+
label,
|
|
2788
|
+
name,
|
|
2789
|
+
type: "checkbox"
|
|
2790
|
+
});
|
|
2791
|
+
return this;
|
|
2792
|
+
}
|
|
2793
|
+
/**
|
|
2794
|
+
* Add a switch field
|
|
2795
|
+
*/
|
|
2796
|
+
switch(name, label, options) {
|
|
2797
|
+
this.schemaFields[name] = z3.boolean().optional();
|
|
2798
|
+
this.formFields.push({
|
|
2799
|
+
label,
|
|
2800
|
+
name,
|
|
2801
|
+
switchProps: options,
|
|
2802
|
+
type: "switch"
|
|
2803
|
+
});
|
|
2804
|
+
return this;
|
|
2805
|
+
}
|
|
2806
|
+
/**
|
|
2807
|
+
* Add a radio field
|
|
2808
|
+
*/
|
|
2809
|
+
radio(name, label, options, fieldOptions) {
|
|
2810
|
+
this.schemaFields[name] = z3.string().min(1, `Please select a ${label.toLowerCase()}`);
|
|
2811
|
+
this.formFields.push({
|
|
2812
|
+
label,
|
|
2813
|
+
name,
|
|
2814
|
+
radioOptions: options,
|
|
2815
|
+
radioProps: fieldOptions,
|
|
2816
|
+
type: "radio"
|
|
2817
|
+
});
|
|
2818
|
+
return this;
|
|
2819
|
+
}
|
|
2820
|
+
/**
|
|
2821
|
+
* Add a slider field
|
|
2822
|
+
*/
|
|
2823
|
+
slider(name, label, options) {
|
|
2824
|
+
const { max = 100, min = 0, step = 1, ...fieldOptions } = options || {};
|
|
2825
|
+
let zodType = z3.number();
|
|
2826
|
+
if (min !== void 0)
|
|
2827
|
+
zodType = zodType.min(min, `${label} must be at least ${min}`);
|
|
2828
|
+
if (max !== void 0)
|
|
2829
|
+
zodType = zodType.max(max, `${label} must be no more than ${max}`);
|
|
2830
|
+
this.schemaFields[name] = zodType;
|
|
2831
|
+
this.formFields.push({
|
|
2832
|
+
label,
|
|
2833
|
+
name,
|
|
2834
|
+
sliderProps: { max, min, step, ...fieldOptions },
|
|
2835
|
+
type: "slider"
|
|
2836
|
+
});
|
|
2837
|
+
return this;
|
|
2838
|
+
}
|
|
2839
|
+
/**
|
|
2840
|
+
* Add a date field
|
|
2841
|
+
*/
|
|
2842
|
+
date(name, label, options) {
|
|
2843
|
+
this.schemaFields[name] = z3.string().min(1, `${label} is required`);
|
|
2844
|
+
this.formFields.push({
|
|
2845
|
+
dateProps: options,
|
|
2846
|
+
label,
|
|
2847
|
+
name,
|
|
2848
|
+
type: "date"
|
|
2849
|
+
});
|
|
2850
|
+
return this;
|
|
2851
|
+
}
|
|
2852
|
+
/**
|
|
2853
|
+
* Add a file field
|
|
2854
|
+
*/
|
|
2855
|
+
file(name, label, options) {
|
|
2856
|
+
this.schemaFields[name] = z3.any().optional();
|
|
2857
|
+
this.formFields.push({
|
|
2858
|
+
fileProps: options,
|
|
2859
|
+
label,
|
|
2860
|
+
name,
|
|
2861
|
+
type: "file"
|
|
2862
|
+
});
|
|
2863
|
+
return this;
|
|
2864
|
+
}
|
|
2865
|
+
/**
|
|
2866
|
+
* Build the final schema and fields
|
|
2867
|
+
*/
|
|
2868
|
+
build() {
|
|
2869
|
+
return {
|
|
2870
|
+
fields: this.formFields,
|
|
2871
|
+
schema: z3.object(this.schemaFields)
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
};
|
|
2875
|
+
function createTypeInferredBuilder() {
|
|
2876
|
+
return new TypeInferredBuilder();
|
|
2877
|
+
}
|
|
2878
|
+
function defineInferredForm(fieldDefinitions) {
|
|
2879
|
+
const builder = createTypeInferredBuilder();
|
|
2880
|
+
fieldDefinitions(builder);
|
|
2881
|
+
return builder.build();
|
|
2882
|
+
}
|
|
2883
|
+
var field = {
|
|
2884
|
+
checkbox: (name, label, options) => {
|
|
2885
|
+
const builder = new TypeInferredBuilder();
|
|
2886
|
+
return builder.checkbox(name, label, options);
|
|
2887
|
+
},
|
|
2888
|
+
date: (name, label, options) => {
|
|
2889
|
+
const builder = new TypeInferredBuilder();
|
|
2890
|
+
return builder.date(name, label, options);
|
|
2891
|
+
},
|
|
2892
|
+
email: (name, label, options) => {
|
|
2893
|
+
const builder = new TypeInferredBuilder();
|
|
2894
|
+
return builder.email(name, label, options);
|
|
2895
|
+
},
|
|
2896
|
+
file: (name, label, options) => {
|
|
2897
|
+
const builder = new TypeInferredBuilder();
|
|
2898
|
+
return builder.file(name, label, options);
|
|
2899
|
+
},
|
|
2900
|
+
number: (name, label, options) => {
|
|
2901
|
+
const builder = new TypeInferredBuilder();
|
|
2902
|
+
return builder.number(name, label, options);
|
|
2903
|
+
},
|
|
2904
|
+
radio: (name, label, options, fieldOptions) => {
|
|
2905
|
+
const builder = new TypeInferredBuilder();
|
|
2906
|
+
return builder.radio(name, label, options, fieldOptions);
|
|
2907
|
+
},
|
|
2908
|
+
select: (name, label, options) => {
|
|
2909
|
+
const builder = new TypeInferredBuilder();
|
|
2910
|
+
return builder.select(name, label, options);
|
|
2911
|
+
},
|
|
2912
|
+
slider: (name, label, options) => {
|
|
2913
|
+
const builder = new TypeInferredBuilder();
|
|
2914
|
+
return builder.slider(name, label, options);
|
|
2915
|
+
},
|
|
2916
|
+
switch: (name, label, options) => {
|
|
2917
|
+
const builder = new TypeInferredBuilder();
|
|
2918
|
+
return builder.switch(name, label, options);
|
|
2919
|
+
},
|
|
2920
|
+
text: (name, label, options) => {
|
|
2921
|
+
const builder = new TypeInferredBuilder();
|
|
2922
|
+
return builder.text(name, label, options);
|
|
2923
|
+
},
|
|
2924
|
+
textarea: (name, label, options) => {
|
|
2925
|
+
const builder = new TypeInferredBuilder();
|
|
2926
|
+
return builder.textarea(name, label, options);
|
|
2927
|
+
}
|
|
2928
|
+
};
|
|
2929
|
+
|
|
2930
|
+
// src/builders/NestedPathBuilder.ts
|
|
2931
|
+
var NestedPathBuilder = class {
|
|
2932
|
+
constructor() {
|
|
2933
|
+
this.fields = [];
|
|
2934
|
+
}
|
|
2935
|
+
/**
|
|
2936
|
+
* Create a nested object path builder
|
|
2937
|
+
* Usage: builder.nest("address").field("street", "Street Address")
|
|
2938
|
+
*/
|
|
2939
|
+
nest(path) {
|
|
2940
|
+
return new NestedObjectBuilder(this, path);
|
|
2941
|
+
}
|
|
2942
|
+
/**
|
|
2943
|
+
* Create a section-based path builder
|
|
2944
|
+
* Usage: builder.section("shipping").field("street", "Street Address")
|
|
2945
|
+
*/
|
|
2946
|
+
section(path) {
|
|
2947
|
+
return new SectionBuilder(this, path);
|
|
2948
|
+
}
|
|
2949
|
+
/**
|
|
2950
|
+
* Add a field with single path
|
|
2951
|
+
* Usage: builder.field("firstName", "First Name")
|
|
2952
|
+
*/
|
|
2953
|
+
field(name, label, type = "input", props) {
|
|
2954
|
+
this.fields.push({
|
|
2955
|
+
label,
|
|
2956
|
+
name,
|
|
2957
|
+
type,
|
|
2958
|
+
...props
|
|
2959
|
+
});
|
|
2960
|
+
return this;
|
|
2961
|
+
}
|
|
2962
|
+
/**
|
|
2963
|
+
* Add a field with path segments
|
|
2964
|
+
* Usage: builder.fieldPath(["user", "profile", "name"], "Full Name")
|
|
2965
|
+
*/
|
|
2966
|
+
fieldPath(path, label, type = "input", props) {
|
|
2967
|
+
const name = path.join(".");
|
|
2968
|
+
this.fields.push({
|
|
2969
|
+
label,
|
|
2970
|
+
name,
|
|
2971
|
+
type,
|
|
2972
|
+
...props
|
|
2973
|
+
});
|
|
2974
|
+
return this;
|
|
2975
|
+
}
|
|
2976
|
+
/**
|
|
2977
|
+
* Add a field with template literal path
|
|
2978
|
+
* Usage: builder.field`user.profile.name`("Full Name")
|
|
2979
|
+
*/
|
|
2980
|
+
fieldTemplate(path) {
|
|
2981
|
+
const pathString = path[0];
|
|
2982
|
+
return new FieldTemplateBuilder(this, pathString);
|
|
2983
|
+
}
|
|
2984
|
+
/**
|
|
2985
|
+
* Return to the parent builder (no-op for root builder)
|
|
2986
|
+
*/
|
|
2987
|
+
end() {
|
|
2988
|
+
return this;
|
|
2989
|
+
}
|
|
2990
|
+
build() {
|
|
2991
|
+
return this.fields;
|
|
2992
|
+
}
|
|
2993
|
+
};
|
|
2994
|
+
var NestedObjectBuilder = class _NestedObjectBuilder {
|
|
2995
|
+
constructor(parent, path) {
|
|
2996
|
+
this.parent = parent;
|
|
2997
|
+
this.path = path;
|
|
2998
|
+
}
|
|
2999
|
+
/**
|
|
3000
|
+
* Add a field to the current nested path
|
|
3001
|
+
*/
|
|
3002
|
+
field(fieldName, label, type = "input", props) {
|
|
3003
|
+
const fullPath = `${this.path}.${fieldName}`;
|
|
3004
|
+
this.parent.fields.push({
|
|
3005
|
+
label,
|
|
3006
|
+
name: fullPath,
|
|
3007
|
+
type,
|
|
3008
|
+
...props
|
|
3009
|
+
});
|
|
3010
|
+
return this;
|
|
3011
|
+
}
|
|
3012
|
+
/**
|
|
3013
|
+
* Nest deeper into the object
|
|
3014
|
+
*/
|
|
3015
|
+
nest(subPath) {
|
|
3016
|
+
return new _NestedObjectBuilder(
|
|
3017
|
+
this.parent,
|
|
3018
|
+
`${this.path}.${subPath}`
|
|
3019
|
+
);
|
|
3020
|
+
}
|
|
3021
|
+
/**
|
|
3022
|
+
* Return to the parent builder
|
|
3023
|
+
*/
|
|
3024
|
+
end() {
|
|
3025
|
+
return this.parent;
|
|
3026
|
+
}
|
|
3027
|
+
};
|
|
3028
|
+
var SectionBuilder = class {
|
|
3029
|
+
constructor(parent, path) {
|
|
3030
|
+
this.parent = parent;
|
|
3031
|
+
this.path = path;
|
|
3032
|
+
}
|
|
3033
|
+
/**
|
|
3034
|
+
* Add a field to the current section
|
|
3035
|
+
*/
|
|
3036
|
+
field(fieldName, label, type = "input", props) {
|
|
3037
|
+
const fullPath = `${this.path}.${fieldName}`;
|
|
3038
|
+
this.parent.fields.push({
|
|
3039
|
+
label,
|
|
3040
|
+
name: fullPath,
|
|
3041
|
+
type,
|
|
3042
|
+
...props
|
|
3043
|
+
});
|
|
3044
|
+
return this;
|
|
3045
|
+
}
|
|
3046
|
+
/**
|
|
3047
|
+
* Add multiple fields to the section
|
|
3048
|
+
*/
|
|
3049
|
+
fields(fieldDefinitions) {
|
|
3050
|
+
fieldDefinitions.forEach((field2) => {
|
|
3051
|
+
this.field(field2.name, field2.label, field2.type, field2.props);
|
|
3052
|
+
});
|
|
3053
|
+
return this;
|
|
3054
|
+
}
|
|
3055
|
+
/**
|
|
3056
|
+
* Nest deeper into the section
|
|
3057
|
+
*/
|
|
3058
|
+
nest(subPath) {
|
|
3059
|
+
return new NestedObjectBuilder(
|
|
3060
|
+
this.parent,
|
|
3061
|
+
`${this.path}.${subPath}`
|
|
3062
|
+
);
|
|
3063
|
+
}
|
|
3064
|
+
/**
|
|
3065
|
+
* Return to the parent builder
|
|
3066
|
+
*/
|
|
3067
|
+
end() {
|
|
3068
|
+
return this.parent;
|
|
3069
|
+
}
|
|
3070
|
+
};
|
|
3071
|
+
var FieldTemplateBuilder = class {
|
|
3072
|
+
constructor(parent, path) {
|
|
3073
|
+
this.parent = parent;
|
|
3074
|
+
this.path = path;
|
|
3075
|
+
}
|
|
3076
|
+
/**
|
|
3077
|
+
* Complete the field definition
|
|
3078
|
+
*/
|
|
3079
|
+
complete(label, type = "input", props) {
|
|
3080
|
+
this.parent.fields.push({
|
|
3081
|
+
label,
|
|
3082
|
+
name: this.path,
|
|
3083
|
+
type,
|
|
3084
|
+
...props
|
|
3085
|
+
});
|
|
3086
|
+
return this.parent;
|
|
3087
|
+
}
|
|
3088
|
+
};
|
|
3089
|
+
function createNestedPathBuilder() {
|
|
3090
|
+
return new NestedPathBuilder();
|
|
3091
|
+
}
|
|
3092
|
+
|
|
3093
|
+
// src/hooks/useDebouncedValidation.ts
|
|
3094
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef } from "react";
|
|
3095
|
+
function useDebouncedValidation(form, options = {}) {
|
|
3096
|
+
const { delay = 300, enabled = true, fields } = options;
|
|
3097
|
+
const timeoutRef = useRef(
|
|
3098
|
+
void 0
|
|
3099
|
+
);
|
|
3100
|
+
const lastValuesRef = useRef({});
|
|
3101
|
+
const debouncedTrigger = useCallback2(() => {
|
|
3102
|
+
if (!enabled) return;
|
|
3103
|
+
if (timeoutRef.current) {
|
|
3104
|
+
clearTimeout(timeoutRef.current);
|
|
3105
|
+
timeoutRef.current = void 0;
|
|
3106
|
+
}
|
|
3107
|
+
timeoutRef.current = setTimeout(async () => {
|
|
3108
|
+
const currentValues = form.getValues();
|
|
3109
|
+
const lastValues = lastValuesRef.current;
|
|
3110
|
+
const hasChanges = fields ? fields.some((field2) => currentValues[field2] !== lastValues[field2]) : Object.keys(currentValues).some(
|
|
3111
|
+
(key) => currentValues[key] !== lastValues[key]
|
|
3112
|
+
);
|
|
3113
|
+
if (hasChanges) {
|
|
3114
|
+
lastValuesRef.current = { ...currentValues };
|
|
3115
|
+
if (fields && fields.length > 0) {
|
|
3116
|
+
await form.trigger(fields);
|
|
3117
|
+
} else {
|
|
3118
|
+
await form.trigger();
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
}, delay);
|
|
3122
|
+
}, [form, delay, fields, enabled]);
|
|
3123
|
+
useEffect2(() => {
|
|
3124
|
+
return () => {
|
|
3125
|
+
if (timeoutRef.current) {
|
|
3126
|
+
clearTimeout(timeoutRef.current);
|
|
3127
|
+
timeoutRef.current = void 0;
|
|
3128
|
+
}
|
|
3129
|
+
};
|
|
3130
|
+
}, []);
|
|
3131
|
+
useEffect2(() => {
|
|
3132
|
+
if (form.formState.isSubmitSuccessful) {
|
|
3133
|
+
lastValuesRef.current = {};
|
|
3134
|
+
}
|
|
3135
|
+
}, [form.formState.isSubmitSuccessful]);
|
|
3136
|
+
return {
|
|
3137
|
+
debouncedTrigger,
|
|
3138
|
+
isDebouncing: !!timeoutRef.current
|
|
3139
|
+
};
|
|
3140
|
+
}
|
|
3141
|
+
function useDebouncedFieldValidation(form, fieldName, options = {}) {
|
|
3142
|
+
const { delay = 300, enabled = true } = options;
|
|
3143
|
+
const timeoutRef = useRef(
|
|
3144
|
+
void 0
|
|
3145
|
+
);
|
|
3146
|
+
const debouncedFieldTrigger = useCallback2(() => {
|
|
3147
|
+
if (!enabled) return;
|
|
3148
|
+
if (timeoutRef.current) {
|
|
3149
|
+
clearTimeout(timeoutRef.current);
|
|
3150
|
+
}
|
|
3151
|
+
timeoutRef.current = setTimeout(async () => {
|
|
3152
|
+
await form.trigger(fieldName);
|
|
3153
|
+
}, delay);
|
|
3154
|
+
}, [form, fieldName, delay, enabled]);
|
|
3155
|
+
useEffect2(() => {
|
|
3156
|
+
return () => {
|
|
3157
|
+
if (timeoutRef.current) {
|
|
3158
|
+
clearTimeout(timeoutRef.current);
|
|
3159
|
+
timeoutRef.current = void 0;
|
|
3160
|
+
}
|
|
3161
|
+
};
|
|
3162
|
+
}, []);
|
|
3163
|
+
return {
|
|
3164
|
+
debouncedFieldTrigger,
|
|
3165
|
+
isDebouncing: !!timeoutRef.current
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
// src/hooks/useInferredForm.ts
|
|
3170
|
+
import { useForm as useForm3 } from "react-hook-form";
|
|
3171
|
+
var zodResolver;
|
|
3172
|
+
try {
|
|
3173
|
+
zodResolver = __require("@hookform/resolvers/zod").zodResolver;
|
|
3174
|
+
} catch {
|
|
3175
|
+
}
|
|
3176
|
+
function useInferredForm(schema, fields, options = {}) {
|
|
3177
|
+
const {
|
|
3178
|
+
defaultValues,
|
|
3179
|
+
delayError = 0,
|
|
3180
|
+
mode = "onChange",
|
|
3181
|
+
reValidateMode = "onChange",
|
|
3182
|
+
shouldFocusError = true,
|
|
3183
|
+
shouldUnregister = false
|
|
3184
|
+
} = options;
|
|
3185
|
+
return useForm3({
|
|
3186
|
+
defaultValues,
|
|
3187
|
+
delayError,
|
|
3188
|
+
mode,
|
|
3189
|
+
resolver: zodResolver ? zodResolver(schema) : void 0,
|
|
3190
|
+
reValidateMode,
|
|
3191
|
+
shouldFocusError,
|
|
3192
|
+
shouldUnregister
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
3195
|
+
function useTypeInferredForm(formConfig, options = {}) {
|
|
3196
|
+
return useInferredForm(formConfig.schema, formConfig.fields, options);
|
|
3197
|
+
}
|
|
3198
|
+
|
|
3199
|
+
// src/utils/performance.ts
|
|
3200
|
+
import { useCallback as useCallback3, useMemo as useMemo2, useRef as useRef2 } from "react";
|
|
3201
|
+
function debounce(func, delay) {
|
|
3202
|
+
let timeoutId;
|
|
3203
|
+
return (...args) => {
|
|
3204
|
+
clearTimeout(timeoutId);
|
|
3205
|
+
timeoutId = setTimeout(() => func(...args), delay);
|
|
3206
|
+
};
|
|
3207
|
+
}
|
|
3208
|
+
function throttle(func, limit) {
|
|
3209
|
+
let inThrottle;
|
|
3210
|
+
return (...args) => {
|
|
3211
|
+
if (!inThrottle) {
|
|
3212
|
+
func(...args);
|
|
3213
|
+
inThrottle = true;
|
|
3214
|
+
setTimeout(() => inThrottle = false, limit);
|
|
3215
|
+
}
|
|
3216
|
+
};
|
|
3217
|
+
}
|
|
3218
|
+
function useMemoizedCallback(callback, deps) {
|
|
3219
|
+
const callbackRef = useRef2(callback);
|
|
3220
|
+
callbackRef.current = callback;
|
|
3221
|
+
return useCallback3(
|
|
3222
|
+
((...args) => callbackRef.current(...args)),
|
|
3223
|
+
deps
|
|
3224
|
+
);
|
|
3225
|
+
}
|
|
3226
|
+
function shallowEqual(prevProps, nextProps) {
|
|
3227
|
+
const prevKeys = Object.keys(prevProps);
|
|
3228
|
+
const nextKeys = Object.keys(nextProps);
|
|
3229
|
+
if (prevKeys.length !== nextKeys.length) {
|
|
3230
|
+
return false;
|
|
3231
|
+
}
|
|
3232
|
+
for (const key of prevKeys) {
|
|
3233
|
+
if (prevProps[key] !== nextProps[key]) {
|
|
3234
|
+
return false;
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
return true;
|
|
3238
|
+
}
|
|
3239
|
+
function deepEqual(prevProps, nextProps) {
|
|
3240
|
+
if (prevProps === nextProps) {
|
|
3241
|
+
return true;
|
|
3242
|
+
}
|
|
3243
|
+
if (typeof prevProps !== typeof nextProps) {
|
|
3244
|
+
return false;
|
|
3245
|
+
}
|
|
3246
|
+
if (typeof prevProps !== "object" || prevProps === null || nextProps === null) {
|
|
3247
|
+
return prevProps === nextProps;
|
|
3248
|
+
}
|
|
3249
|
+
const prevKeys = Object.keys(prevProps);
|
|
3250
|
+
const nextKeys = Object.keys(nextProps);
|
|
3251
|
+
if (prevKeys.length !== nextKeys.length) {
|
|
3252
|
+
return false;
|
|
3253
|
+
}
|
|
3254
|
+
for (const key of prevKeys) {
|
|
3255
|
+
if (!nextKeys.includes(key)) {
|
|
3256
|
+
return false;
|
|
3257
|
+
}
|
|
3258
|
+
if (!deepEqual(prevProps[key], nextProps[key])) {
|
|
3259
|
+
return false;
|
|
3260
|
+
}
|
|
3261
|
+
}
|
|
3262
|
+
return true;
|
|
3263
|
+
}
|
|
3264
|
+
function usePerformanceMonitor(componentName, enabled = false) {
|
|
3265
|
+
const renderCountRef = useRef2(0);
|
|
3266
|
+
const lastRenderTimeRef = useRef2(Date.now());
|
|
3267
|
+
if (enabled) {
|
|
3268
|
+
renderCountRef.current += 1;
|
|
3269
|
+
const now = Date.now();
|
|
3270
|
+
const timeSinceLastRender = now - lastRenderTimeRef.current;
|
|
3271
|
+
console.log(`[Performance] ${componentName}:`, {
|
|
3272
|
+
renderCount: renderCountRef.current,
|
|
3273
|
+
timeSinceLastRender: `${timeSinceLastRender}ms`
|
|
3274
|
+
});
|
|
3275
|
+
lastRenderTimeRef.current = now;
|
|
3276
|
+
}
|
|
3277
|
+
return {
|
|
3278
|
+
renderCount: renderCountRef.current,
|
|
3279
|
+
resetRenderCount: () => {
|
|
3280
|
+
renderCountRef.current = 0;
|
|
3281
|
+
}
|
|
3282
|
+
};
|
|
3283
|
+
}
|
|
3284
|
+
function createOptimizedFieldHandler(onChange, options = {}) {
|
|
3285
|
+
const { debounce: debounceMs, throttle: throttleMs } = options;
|
|
3286
|
+
let handler = onChange;
|
|
3287
|
+
if (throttleMs) {
|
|
3288
|
+
handler = throttle(handler, throttleMs);
|
|
3289
|
+
}
|
|
3290
|
+
if (debounceMs) {
|
|
3291
|
+
handler = debounce(handler, debounceMs);
|
|
3292
|
+
}
|
|
3293
|
+
return handler;
|
|
3294
|
+
}
|
|
3295
|
+
function useMemoizedFieldProps(props, deps) {
|
|
3296
|
+
return useMemo2(() => props, deps);
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
// src/builders/validation-helpers.ts
|
|
3300
|
+
import { z as z4 } from "zod";
|
|
3301
|
+
var validationPatterns = {
|
|
3302
|
+
// Credit card validation
|
|
3303
|
+
creditCard: z4.string().regex(
|
|
3304
|
+
// eslint-disable-next-line no-useless-escape
|
|
3305
|
+
/^[0-9]{4}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}$/,
|
|
3306
|
+
"Please enter a valid credit card number"
|
|
3307
|
+
),
|
|
3308
|
+
// Date validation (MM/DD/YYYY)
|
|
3309
|
+
date: z4.string().regex(
|
|
3310
|
+
/^(0[1-9]|1[0-2])\/(0[1-9]|[12][0-9]|3[01])\/\d{4}$/,
|
|
3311
|
+
"Please enter a valid date (MM/DD/YYYY)"
|
|
3312
|
+
),
|
|
3313
|
+
// Email validation
|
|
3314
|
+
email: z4.string().email("Please enter a valid email address"),
|
|
3315
|
+
// Password validation
|
|
3316
|
+
password: z4.string().min(8, "Password must be at least 8 characters").regex(/[A-Z]/, "Password must contain at least one uppercase letter").regex(/[a-z]/, "Password must contain at least one lowercase letter").regex(/[0-9]/, "Password must contain at least one number").regex(
|
|
3317
|
+
/[^A-Za-z0-9]/,
|
|
3318
|
+
"Password must contain at least one special character"
|
|
3319
|
+
),
|
|
3320
|
+
// Phone number validation (international)
|
|
3321
|
+
phoneInternational: z4.string().regex(/^\+?[\d\s\-\(\)]+$/, "Please enter a valid phone number"),
|
|
3322
|
+
// Phone number validation (US format)
|
|
3323
|
+
phoneUS: z4.string().regex(
|
|
3324
|
+
/^\(\d{3}\) \d{3}-\d{4}$/,
|
|
3325
|
+
"Please enter a valid phone number (XXX) XXX-XXXX"
|
|
3326
|
+
),
|
|
3327
|
+
// SSN validation
|
|
3328
|
+
ssn: z4.string().regex(/^\d{3}-\d{2}-\d{4}$/, "Please enter a valid SSN (XXX-XX-XXXX)"),
|
|
3329
|
+
// Strong password validation
|
|
3330
|
+
strongPassword: z4.string().min(12, "Password must be at least 12 characters").regex(/[A-Z]/, "Password must contain at least one uppercase letter").regex(/[a-z]/, "Password must contain at least one lowercase letter").regex(/[0-9]/, "Password must contain at least one number").regex(
|
|
3331
|
+
/[^A-Za-z0-9]/,
|
|
3332
|
+
"Password must contain at least one special character"
|
|
3333
|
+
),
|
|
3334
|
+
// Time validation (HH:MM AM/PM)
|
|
3335
|
+
time: z4.string().regex(
|
|
3336
|
+
/^(0[1-9]|1[0-2]):[0-5][0-9] (AM|PM)$/i,
|
|
3337
|
+
"Please enter a valid time (HH:MM AM/PM)"
|
|
3338
|
+
),
|
|
3339
|
+
// URL validation
|
|
3340
|
+
url: z4.string().url("Please enter a valid URL"),
|
|
3341
|
+
// ZIP code validation
|
|
3342
|
+
zipCode: z4.string().regex(/^\d{5}(-\d{4})?$/, "Please enter a valid ZIP code")
|
|
3343
|
+
};
|
|
3344
|
+
var asyncValidation = {
|
|
3345
|
+
/**
|
|
3346
|
+
* Email availability check
|
|
3347
|
+
*/
|
|
3348
|
+
emailAvailability: async (email) => {
|
|
3349
|
+
return new Promise((resolve) => {
|
|
3350
|
+
setTimeout(() => {
|
|
3351
|
+
const takenEmails = ["test@example.com", "admin@example.com"];
|
|
3352
|
+
resolve(!takenEmails.includes(email));
|
|
3353
|
+
}, 1e3);
|
|
3354
|
+
});
|
|
3355
|
+
},
|
|
3356
|
+
/**
|
|
3357
|
+
* Username availability check
|
|
3358
|
+
*/
|
|
3359
|
+
usernameAvailability: async (username) => {
|
|
3360
|
+
return new Promise((resolve) => {
|
|
3361
|
+
setTimeout(() => {
|
|
3362
|
+
const takenUsernames = ["admin", "test", "user"];
|
|
3363
|
+
resolve(!takenUsernames.includes(username.toLowerCase()));
|
|
3364
|
+
}, 1e3);
|
|
3365
|
+
});
|
|
3366
|
+
}
|
|
3367
|
+
};
|
|
3368
|
+
var errorMessages = {
|
|
3369
|
+
date: () => "Please enter a valid date",
|
|
3370
|
+
email: () => "Please enter a valid email address",
|
|
3371
|
+
max: (fieldName, max) => `${fieldName} must be no more than ${max}`,
|
|
3372
|
+
maxLength: (fieldName, max) => `${fieldName} must be no more than ${max} characters`,
|
|
3373
|
+
min: (fieldName, min) => `${fieldName} must be at least ${min}`,
|
|
3374
|
+
minLength: (fieldName, min) => `${fieldName} must be at least ${min} characters`,
|
|
3375
|
+
pattern: (fieldName) => `${fieldName} format is invalid`,
|
|
3376
|
+
phone: () => "Please enter a valid phone number",
|
|
3377
|
+
required: (fieldName) => `${fieldName} is required`,
|
|
3378
|
+
time: () => "Please enter a valid time",
|
|
3379
|
+
url: () => "Please enter a valid URL"
|
|
3380
|
+
};
|
|
3381
|
+
var serverValidation = {
|
|
3382
|
+
/**
|
|
3383
|
+
* Apply server errors to form
|
|
3384
|
+
*/
|
|
3385
|
+
applyServerErrors: (errors, setError) => {
|
|
3386
|
+
Object.entries(errors).forEach(([field2, messages]) => {
|
|
3387
|
+
setError(field2, {
|
|
3388
|
+
message: messages[0],
|
|
3389
|
+
type: "server"
|
|
3390
|
+
// Use first error message
|
|
3391
|
+
});
|
|
3392
|
+
});
|
|
3393
|
+
},
|
|
3394
|
+
/**
|
|
3395
|
+
* Clear server errors
|
|
3396
|
+
*/
|
|
3397
|
+
clearServerErrors: (fields, clearErrors) => {
|
|
3398
|
+
fields.forEach((field2) => {
|
|
3399
|
+
clearErrors(field2, "server");
|
|
3400
|
+
});
|
|
3401
|
+
}
|
|
3402
|
+
};
|
|
3403
|
+
var validationUtils = {
|
|
3404
|
+
/**
|
|
3405
|
+
* Debounced validation
|
|
3406
|
+
*/
|
|
3407
|
+
debounceValidation: (fn, delay = 300) => {
|
|
3408
|
+
let timeoutId;
|
|
3409
|
+
return (...args) => {
|
|
3410
|
+
clearTimeout(timeoutId);
|
|
3411
|
+
timeoutId = setTimeout(() => fn(...args), delay);
|
|
3412
|
+
};
|
|
3413
|
+
},
|
|
3414
|
+
/**
|
|
3415
|
+
* Get field error message
|
|
3416
|
+
*/
|
|
3417
|
+
getFieldError: (errors, field2) => {
|
|
3418
|
+
return errors[field2];
|
|
3419
|
+
},
|
|
3420
|
+
/**
|
|
3421
|
+
* Check if field has error
|
|
3422
|
+
*/
|
|
3423
|
+
hasFieldError: (errors, field2) => {
|
|
3424
|
+
return !!errors[field2];
|
|
3425
|
+
},
|
|
3426
|
+
/**
|
|
3427
|
+
* Validate form data against schema
|
|
3428
|
+
*/
|
|
3429
|
+
validateForm: async (data, schema) => {
|
|
3430
|
+
try {
|
|
3431
|
+
await schema.parseAsync(data);
|
|
3432
|
+
return { errors: {}, success: true };
|
|
3433
|
+
} catch (error) {
|
|
3434
|
+
if (error instanceof z4.ZodError) {
|
|
3435
|
+
const errors = {};
|
|
3436
|
+
error.issues.forEach((err) => {
|
|
3437
|
+
const path = err.path.join(".");
|
|
3438
|
+
errors[path] = err.message;
|
|
3439
|
+
});
|
|
3440
|
+
return { errors, success: false };
|
|
3441
|
+
}
|
|
3442
|
+
throw error;
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
};
|
|
1493
3446
|
export {
|
|
3447
|
+
AdvancedFieldBuilder,
|
|
3448
|
+
BasicFormBuilder,
|
|
1494
3449
|
CheckboxField,
|
|
3450
|
+
CommonFields,
|
|
3451
|
+
ConditionalField,
|
|
1495
3452
|
ConfigurableForm,
|
|
1496
3453
|
DateField,
|
|
3454
|
+
DynamicSectionField,
|
|
3455
|
+
FieldArrayBuilder,
|
|
3456
|
+
FieldArrayField,
|
|
3457
|
+
FieldArrayItemBuilder,
|
|
1497
3458
|
FileField,
|
|
1498
3459
|
FontPickerField,
|
|
1499
3460
|
FormField,
|
|
3461
|
+
FormFieldHelpers,
|
|
1500
3462
|
FormProvider,
|
|
3463
|
+
FormStatus,
|
|
3464
|
+
FormToast,
|
|
1501
3465
|
HeroHookFormProvider,
|
|
1502
3466
|
InputField,
|
|
1503
3467
|
RadioGroupField,
|
|
1504
3468
|
SelectField,
|
|
3469
|
+
ServerActionForm,
|
|
1505
3470
|
SliderField,
|
|
1506
3471
|
SubmitButton,
|
|
1507
3472
|
SwitchField,
|
|
1508
3473
|
TextareaField,
|
|
3474
|
+
TypeInferredBuilder,
|
|
1509
3475
|
ZodForm,
|
|
1510
3476
|
applyServerErrors,
|
|
3477
|
+
asyncValidation,
|
|
1511
3478
|
commonValidations,
|
|
1512
|
-
|
|
1513
|
-
|
|
3479
|
+
createAdvancedBuilder,
|
|
3480
|
+
createBasicFormBuilder,
|
|
1514
3481
|
createDateSchema,
|
|
1515
3482
|
createEmailSchema,
|
|
3483
|
+
createField,
|
|
3484
|
+
createFieldArrayBuilder,
|
|
3485
|
+
createFieldArrayItemBuilder,
|
|
1516
3486
|
createFileSchema,
|
|
1517
3487
|
createFormTestUtils,
|
|
1518
3488
|
createFutureDateSchema,
|
|
@@ -1520,24 +3490,46 @@ export {
|
|
|
1520
3490
|
createMinLengthSchema,
|
|
1521
3491
|
createMockFormData,
|
|
1522
3492
|
createMockFormErrors,
|
|
3493
|
+
createNestedPathBuilder,
|
|
1523
3494
|
createNumberRangeSchema,
|
|
3495
|
+
createOptimizedFieldHandler,
|
|
1524
3496
|
createPasswordSchema,
|
|
1525
3497
|
createPastDateSchema,
|
|
1526
3498
|
createPhoneSchema,
|
|
1527
3499
|
createRequiredCheckboxSchema,
|
|
1528
3500
|
createRequiredSchema,
|
|
3501
|
+
createTypeInferredBuilder,
|
|
1529
3502
|
createUrlSchema,
|
|
1530
3503
|
createZodFormConfig,
|
|
3504
|
+
crossFieldValidation,
|
|
3505
|
+
debounce,
|
|
3506
|
+
deepEqual,
|
|
3507
|
+
defineInferredForm,
|
|
3508
|
+
errorMessages,
|
|
3509
|
+
field,
|
|
1531
3510
|
getFieldError,
|
|
1532
3511
|
getFormErrors,
|
|
1533
3512
|
hasFieldError,
|
|
1534
3513
|
hasFormErrors,
|
|
3514
|
+
serverValidation,
|
|
3515
|
+
shallowEqual,
|
|
1535
3516
|
simulateFieldInput,
|
|
1536
3517
|
simulateFormSubmission,
|
|
1537
|
-
|
|
3518
|
+
throttle,
|
|
3519
|
+
useDebouncedFieldValidation,
|
|
3520
|
+
useDebouncedValidation,
|
|
3521
|
+
useEnhancedFormState,
|
|
3522
|
+
useFormContext5 as useFormContext,
|
|
1538
3523
|
useFormHelper,
|
|
1539
3524
|
useHeroForm,
|
|
1540
3525
|
useHeroHookFormDefaults,
|
|
3526
|
+
useInferredForm,
|
|
3527
|
+
useMemoizedCallback,
|
|
3528
|
+
useMemoizedFieldProps,
|
|
3529
|
+
usePerformanceMonitor,
|
|
3530
|
+
useTypeInferredForm,
|
|
1541
3531
|
useZodForm,
|
|
3532
|
+
validationPatterns,
|
|
3533
|
+
validationUtils,
|
|
1542
3534
|
waitForFormState
|
|
1543
3535
|
};
|