@rachelallyson/hero-hook-form 2.5.1 → 2.7.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 +76 -0
- package/dist/index.d.ts +584 -38
- package/dist/index.js +553 -92
- package/dist/react/index.d.ts +583 -37
- package/dist/react/index.js +553 -92
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -76,7 +76,7 @@ function useFormHelper({
|
|
|
76
76
|
|
|
77
77
|
// src/components/FormField.tsx
|
|
78
78
|
import React17 from "react";
|
|
79
|
-
import { useWatch as useWatch3 } from "react-hook-form";
|
|
79
|
+
import { get, useWatch as useWatch3 } from "react-hook-form";
|
|
80
80
|
|
|
81
81
|
// src/fields/AutocompleteField.tsx
|
|
82
82
|
import React from "react";
|
|
@@ -432,18 +432,23 @@ function FieldArrayField({
|
|
|
432
432
|
}) {
|
|
433
433
|
const {
|
|
434
434
|
addButtonText = "Add Item",
|
|
435
|
+
defaultItem,
|
|
436
|
+
enableReordering = false,
|
|
435
437
|
fields: fieldConfigs,
|
|
436
438
|
max = 10,
|
|
437
439
|
min = 0,
|
|
438
440
|
name,
|
|
439
|
-
removeButtonText = "Remove"
|
|
441
|
+
removeButtonText = "Remove",
|
|
442
|
+
renderAddButton,
|
|
443
|
+
renderItem,
|
|
444
|
+
reorderButtonText = { down: "\u2193", up: "\u2191" }
|
|
440
445
|
} = config;
|
|
441
446
|
const form = useFormContext3();
|
|
442
447
|
if (!form || !form.control) {
|
|
443
448
|
return null;
|
|
444
449
|
}
|
|
445
450
|
const { control } = form;
|
|
446
|
-
const { append, fields, remove } = useFieldArray({
|
|
451
|
+
const { append, fields, move, remove } = useFieldArray({
|
|
447
452
|
control,
|
|
448
453
|
name
|
|
449
454
|
// FieldArray name
|
|
@@ -452,18 +457,22 @@ function FieldArrayField({
|
|
|
452
457
|
const canRemove = fields.length > min;
|
|
453
458
|
const handleAdd = () => {
|
|
454
459
|
if (canAdd) {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
460
|
+
if (defaultItem) {
|
|
461
|
+
append(defaultItem());
|
|
462
|
+
} else {
|
|
463
|
+
const defaultValues = fieldConfigs.reduce((acc, fieldConfig) => {
|
|
464
|
+
const fieldName = fieldConfig.name;
|
|
465
|
+
if (fieldConfig.type === "checkbox" || fieldConfig.type === "switch") {
|
|
466
|
+
acc[fieldName] = false;
|
|
467
|
+
} else if (fieldConfig.type === "slider") {
|
|
468
|
+
acc[fieldName] = 0;
|
|
469
|
+
} else {
|
|
470
|
+
acc[fieldName] = "";
|
|
471
|
+
}
|
|
472
|
+
return acc;
|
|
473
|
+
}, {});
|
|
474
|
+
append(defaultValues);
|
|
475
|
+
}
|
|
467
476
|
}
|
|
468
477
|
};
|
|
469
478
|
const handleRemove = (index) => {
|
|
@@ -471,60 +480,131 @@ function FieldArrayField({
|
|
|
471
480
|
remove(index);
|
|
472
481
|
}
|
|
473
482
|
};
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
483
|
+
const handleMoveUp = (index) => {
|
|
484
|
+
if (index > 0) {
|
|
485
|
+
move(index, index - 1);
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
const handleMoveDown = (index) => {
|
|
489
|
+
if (index < fields.length - 1) {
|
|
490
|
+
move(index, index + 1);
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
const renderFieldArrayItems = () => {
|
|
494
|
+
return fields.map((field2, index) => {
|
|
495
|
+
const canMoveUp = enableReordering && index > 0;
|
|
496
|
+
const canMoveDown = enableReordering && index < fields.length - 1;
|
|
497
|
+
const itemCanRemove = canRemove;
|
|
498
|
+
const fieldElements = fieldConfigs.map((fieldConfig) => {
|
|
499
|
+
const fieldName = fieldConfig.name;
|
|
500
|
+
const fullPath = `${name}.${index}.${fieldName}`;
|
|
501
|
+
let processedConfig = { ...fieldConfig, name: fullPath };
|
|
502
|
+
if ("dependsOn" in fieldConfig && fieldConfig.dependsOn && typeof fieldConfig.dependsOn === "string") {
|
|
503
|
+
const dependsOnPath = fieldConfig.dependsOn;
|
|
504
|
+
if (!dependsOnPath.startsWith(`${name}.`)) {
|
|
505
|
+
processedConfig = {
|
|
506
|
+
...processedConfig,
|
|
507
|
+
dependsOn: `${name}.${index}.${dependsOnPath}`,
|
|
508
|
+
// Preserve dependsOnValue if it exists
|
|
509
|
+
..."dependsOnValue" in fieldConfig && {
|
|
510
|
+
dependsOnValue: fieldConfig.dependsOnValue
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return /* @__PURE__ */ React8.createElement(
|
|
516
|
+
FormField,
|
|
517
|
+
{
|
|
518
|
+
key: `${fieldConfig.name}-${index}`,
|
|
519
|
+
config: processedConfig,
|
|
520
|
+
form,
|
|
521
|
+
submissionState: {
|
|
522
|
+
error: void 0,
|
|
523
|
+
isSubmitted: false,
|
|
524
|
+
isSubmitting: false,
|
|
525
|
+
isSuccess: false
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
);
|
|
529
|
+
});
|
|
530
|
+
if (renderItem) {
|
|
531
|
+
return /* @__PURE__ */ React8.createElement(React8.Fragment, { key: field2.id }, renderItem({
|
|
532
|
+
canMoveDown,
|
|
533
|
+
canMoveUp,
|
|
534
|
+
canRemove: itemCanRemove,
|
|
535
|
+
children: /* @__PURE__ */ React8.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, fieldElements),
|
|
536
|
+
field: field2,
|
|
537
|
+
fields,
|
|
538
|
+
index,
|
|
539
|
+
onMoveDown: () => handleMoveDown(index),
|
|
540
|
+
onMoveUp: () => handleMoveUp(index),
|
|
541
|
+
onRemove: () => handleRemove(index)
|
|
542
|
+
}));
|
|
543
|
+
}
|
|
544
|
+
return /* @__PURE__ */ React8.createElement(
|
|
545
|
+
"div",
|
|
546
|
+
{
|
|
547
|
+
key: field2.id,
|
|
548
|
+
className: "border border-gray-200 rounded-lg p-4 space-y-4"
|
|
549
|
+
},
|
|
550
|
+
/* @__PURE__ */ React8.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React8.createElement("h4", { className: "text-sm font-medium text-gray-700" }, config.label, " #", index + 1), /* @__PURE__ */ React8.createElement("div", { className: "flex gap-2" }, enableReordering && /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(
|
|
551
|
+
Button2,
|
|
552
|
+
{
|
|
553
|
+
size: "sm",
|
|
554
|
+
variant: "light",
|
|
555
|
+
isDisabled: !canMoveUp,
|
|
556
|
+
onPress: () => handleMoveUp(index),
|
|
557
|
+
"aria-label": `Move ${config.label} ${index + 1} up`
|
|
558
|
+
},
|
|
559
|
+
reorderButtonText.up
|
|
560
|
+
), /* @__PURE__ */ React8.createElement(
|
|
561
|
+
Button2,
|
|
562
|
+
{
|
|
563
|
+
size: "sm",
|
|
564
|
+
variant: "light",
|
|
565
|
+
isDisabled: !canMoveDown,
|
|
566
|
+
onPress: () => handleMoveDown(index),
|
|
567
|
+
"aria-label": `Move ${config.label} ${index + 1} down`
|
|
568
|
+
},
|
|
569
|
+
reorderButtonText.down
|
|
570
|
+
)), itemCanRemove && /* @__PURE__ */ React8.createElement(
|
|
571
|
+
Button2,
|
|
572
|
+
{
|
|
573
|
+
size: "sm",
|
|
574
|
+
variant: "light",
|
|
575
|
+
color: "danger",
|
|
576
|
+
startContent: "\u{1F5D1}\uFE0F",
|
|
577
|
+
onPress: () => handleRemove(index),
|
|
578
|
+
"aria-label": `${removeButtonText} ${config.label} ${index + 1}`
|
|
579
|
+
},
|
|
580
|
+
removeButtonText
|
|
581
|
+
))),
|
|
582
|
+
/* @__PURE__ */ React8.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, fieldElements)
|
|
583
|
+
);
|
|
584
|
+
});
|
|
585
|
+
};
|
|
586
|
+
const renderAddButtonElement = () => {
|
|
587
|
+
if (renderAddButton) {
|
|
588
|
+
return renderAddButton({
|
|
589
|
+
canAdd,
|
|
590
|
+
onAdd: handleAdd
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
if (!canAdd) {
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
return /* @__PURE__ */ React8.createElement(
|
|
481
597
|
Button2,
|
|
482
598
|
{
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
onPress: () => handleRemove(index),
|
|
488
|
-
"aria-label": `${removeButtonText} ${config.label} ${index + 1}`
|
|
599
|
+
variant: "bordered",
|
|
600
|
+
startContent: "\u2795",
|
|
601
|
+
onPress: handleAdd,
|
|
602
|
+
className: "w-full"
|
|
489
603
|
},
|
|
490
|
-
|
|
491
|
-
)
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
{
|
|
495
|
-
key: `${fieldConfig.name}-${index}`,
|
|
496
|
-
config: {
|
|
497
|
-
...fieldConfig,
|
|
498
|
-
name: `${name}.${index}.${fieldConfig.name}`
|
|
499
|
-
},
|
|
500
|
-
form,
|
|
501
|
-
submissionState: {
|
|
502
|
-
error: void 0,
|
|
503
|
-
isSubmitted: false,
|
|
504
|
-
isSubmitting: false,
|
|
505
|
-
isSuccess: false
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
)))
|
|
509
|
-
)), canAdd && /* @__PURE__ */ React8.createElement(
|
|
510
|
-
Button2,
|
|
511
|
-
{
|
|
512
|
-
variant: "bordered",
|
|
513
|
-
startContent: "\u2795",
|
|
514
|
-
onPress: handleAdd,
|
|
515
|
-
className: "w-full"
|
|
516
|
-
},
|
|
517
|
-
addButtonText
|
|
518
|
-
), fields.length === 0 && /* @__PURE__ */ React8.createElement("div", { className: "text-center py-8 text-gray-500" }, /* @__PURE__ */ React8.createElement("p", null, "No ", config.label?.toLowerCase(), " added yet."), /* @__PURE__ */ React8.createElement(
|
|
519
|
-
Button2,
|
|
520
|
-
{
|
|
521
|
-
variant: "bordered",
|
|
522
|
-
startContent: "\u2795",
|
|
523
|
-
onPress: handleAdd,
|
|
524
|
-
className: "mt-2"
|
|
525
|
-
},
|
|
526
|
-
addButtonText
|
|
527
|
-
))));
|
|
604
|
+
addButtonText
|
|
605
|
+
);
|
|
606
|
+
};
|
|
607
|
+
return /* @__PURE__ */ React8.createElement("div", { className }, /* @__PURE__ */ React8.createElement("div", { className: "space-y-4" }, fields.length > 0 ? renderFieldArrayItems() : /* @__PURE__ */ React8.createElement("div", { className: "text-center py-8 text-gray-500" }, /* @__PURE__ */ React8.createElement("p", null, "No ", config.label?.toLowerCase(), " added yet."), renderAddButtonElement()), fields.length > 0 && renderAddButtonElement()));
|
|
528
608
|
}
|
|
529
609
|
|
|
530
610
|
// src/fields/FileField.tsx
|
|
@@ -1050,7 +1130,7 @@ var FormField = React17.memo(
|
|
|
1050
1130
|
return null;
|
|
1051
1131
|
}
|
|
1052
1132
|
if (config.dependsOn) {
|
|
1053
|
-
const dependentValue = watchedValues
|
|
1133
|
+
const dependentValue = get(watchedValues, config.dependsOn);
|
|
1054
1134
|
if (config.dependsOnValue !== void 0 && dependentValue !== config.dependsOnValue) {
|
|
1055
1135
|
return null;
|
|
1056
1136
|
}
|
|
@@ -2334,6 +2414,44 @@ function ZodForm({
|
|
|
2334
2414
|
))));
|
|
2335
2415
|
}
|
|
2336
2416
|
|
|
2417
|
+
// src/components/SimpleForm.tsx
|
|
2418
|
+
import React24 from "react";
|
|
2419
|
+
function SimpleForm({
|
|
2420
|
+
className,
|
|
2421
|
+
defaultValues,
|
|
2422
|
+
field: field2,
|
|
2423
|
+
hideSubmitButton = false,
|
|
2424
|
+
onError,
|
|
2425
|
+
onSubmit,
|
|
2426
|
+
onSuccess,
|
|
2427
|
+
schema,
|
|
2428
|
+
submitButton,
|
|
2429
|
+
subtitle,
|
|
2430
|
+
title
|
|
2431
|
+
}) {
|
|
2432
|
+
return /* @__PURE__ */ React24.createElement(
|
|
2433
|
+
ZodForm,
|
|
2434
|
+
{
|
|
2435
|
+
className,
|
|
2436
|
+
config: {
|
|
2437
|
+
defaultValues,
|
|
2438
|
+
fields: [field2],
|
|
2439
|
+
schema
|
|
2440
|
+
},
|
|
2441
|
+
onError,
|
|
2442
|
+
onSubmit,
|
|
2443
|
+
onSuccess,
|
|
2444
|
+
showResetButton: false,
|
|
2445
|
+
submitButtonText: hideSubmitButton ? "" : "Submit",
|
|
2446
|
+
subtitle,
|
|
2447
|
+
title,
|
|
2448
|
+
submitButtonProps: hideSubmitButton && submitButton ? {
|
|
2449
|
+
style: { display: "none" }
|
|
2450
|
+
} : {}
|
|
2451
|
+
}
|
|
2452
|
+
);
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2337
2455
|
// src/builders/BasicFormBuilder.ts
|
|
2338
2456
|
var BasicFormBuilder = class {
|
|
2339
2457
|
constructor() {
|
|
@@ -2438,9 +2556,27 @@ function createBasicFormBuilder() {
|
|
|
2438
2556
|
var FormFieldHelpers = {
|
|
2439
2557
|
/**
|
|
2440
2558
|
* Create an autocomplete field
|
|
2559
|
+
*
|
|
2560
|
+
* @example
|
|
2561
|
+
* ```tsx
|
|
2562
|
+
* // Simple autocomplete
|
|
2563
|
+
* FormFieldHelpers.autocomplete("country", "Country", options)
|
|
2564
|
+
*
|
|
2565
|
+
* // With placeholder
|
|
2566
|
+
* FormFieldHelpers.autocomplete("country", "Country", options, "Search countries")
|
|
2567
|
+
*
|
|
2568
|
+
* // With full customization
|
|
2569
|
+
* FormFieldHelpers.autocomplete("country", "Country", options, "Search countries", {
|
|
2570
|
+
* classNames: { base: "custom-autocomplete" },
|
|
2571
|
+
* allowsCustomValue: true
|
|
2572
|
+
* })
|
|
2573
|
+
* ```
|
|
2441
2574
|
*/
|
|
2442
|
-
autocomplete: (name, label, items, placeholder) => ({
|
|
2443
|
-
autocompleteProps:
|
|
2575
|
+
autocomplete: (name, label, items, placeholder, autocompleteProps) => ({
|
|
2576
|
+
autocompleteProps: {
|
|
2577
|
+
...placeholder && { placeholder },
|
|
2578
|
+
...autocompleteProps
|
|
2579
|
+
},
|
|
2444
2580
|
label,
|
|
2445
2581
|
name,
|
|
2446
2582
|
options: items,
|
|
@@ -2448,8 +2584,21 @@ var FormFieldHelpers = {
|
|
|
2448
2584
|
}),
|
|
2449
2585
|
/**
|
|
2450
2586
|
* Create a checkbox field
|
|
2587
|
+
*
|
|
2588
|
+
* @example
|
|
2589
|
+
* ```tsx
|
|
2590
|
+
* // Simple checkbox
|
|
2591
|
+
* FormFieldHelpers.checkbox("newsletter", "Subscribe to newsletter")
|
|
2592
|
+
*
|
|
2593
|
+
* // With full customization
|
|
2594
|
+
* FormFieldHelpers.checkbox("newsletter", "Subscribe to newsletter", {
|
|
2595
|
+
* classNames: { base: "custom-checkbox" },
|
|
2596
|
+
* size: "lg"
|
|
2597
|
+
* })
|
|
2598
|
+
* ```
|
|
2451
2599
|
*/
|
|
2452
|
-
checkbox: (name, label) => ({
|
|
2600
|
+
checkbox: (name, label, checkboxProps) => ({
|
|
2601
|
+
checkboxProps,
|
|
2453
2602
|
label,
|
|
2454
2603
|
name,
|
|
2455
2604
|
type: "checkbox"
|
|
@@ -2511,6 +2660,19 @@ var FormFieldHelpers = {
|
|
|
2511
2660
|
},
|
|
2512
2661
|
/**
|
|
2513
2662
|
* Create a date field
|
|
2663
|
+
*
|
|
2664
|
+
* @example
|
|
2665
|
+
* ```tsx
|
|
2666
|
+
* // Simple date field
|
|
2667
|
+
* FormFieldHelpers.date("birthDate", "Birth Date")
|
|
2668
|
+
*
|
|
2669
|
+
* // With full customization
|
|
2670
|
+
* FormFieldHelpers.date("birthDate", "Birth Date", {
|
|
2671
|
+
* label: "Select your birth date",
|
|
2672
|
+
* granularity: "day",
|
|
2673
|
+
* minValue: new CalendarDate(1900, 1, 1)
|
|
2674
|
+
* })
|
|
2675
|
+
* ```
|
|
2514
2676
|
*/
|
|
2515
2677
|
date: (name, label, dateProps) => ({
|
|
2516
2678
|
dateProps,
|
|
@@ -2518,40 +2680,211 @@ var FormFieldHelpers = {
|
|
|
2518
2680
|
name,
|
|
2519
2681
|
type: "date"
|
|
2520
2682
|
}),
|
|
2683
|
+
/**
|
|
2684
|
+
* Create a file upload field
|
|
2685
|
+
*
|
|
2686
|
+
* @example
|
|
2687
|
+
* ```tsx
|
|
2688
|
+
* // Simple file field
|
|
2689
|
+
* FormFieldHelpers.file("avatar", "Profile Picture")
|
|
2690
|
+
*
|
|
2691
|
+
* // With accept and multiple
|
|
2692
|
+
* FormFieldHelpers.file("avatar", "Profile Picture", {
|
|
2693
|
+
* accept: "image/*",
|
|
2694
|
+
* multiple: true
|
|
2695
|
+
* })
|
|
2696
|
+
*
|
|
2697
|
+
* // With full customization
|
|
2698
|
+
* FormFieldHelpers.file("avatar", "Profile Picture", {
|
|
2699
|
+
* accept: "image/*",
|
|
2700
|
+
* multiple: false,
|
|
2701
|
+
* fileProps: { className: "custom-file-input" }
|
|
2702
|
+
* })
|
|
2703
|
+
* ```
|
|
2704
|
+
*/
|
|
2705
|
+
file: (name, label, options) => ({
|
|
2706
|
+
accept: options?.accept,
|
|
2707
|
+
fileProps: options?.fileProps,
|
|
2708
|
+
label,
|
|
2709
|
+
multiple: options?.multiple,
|
|
2710
|
+
name,
|
|
2711
|
+
type: "file"
|
|
2712
|
+
}),
|
|
2713
|
+
/**
|
|
2714
|
+
* Create a font picker field
|
|
2715
|
+
*
|
|
2716
|
+
* @example
|
|
2717
|
+
* ```tsx
|
|
2718
|
+
* // Simple font picker
|
|
2719
|
+
* FormFieldHelpers.fontPicker("font", "Choose Font")
|
|
2720
|
+
*
|
|
2721
|
+
* // With full customization
|
|
2722
|
+
* FormFieldHelpers.fontPicker("font", "Choose Font", {
|
|
2723
|
+
* showFontPreview: true,
|
|
2724
|
+
* loadAllVariants: false,
|
|
2725
|
+
* fontsLoadedTimeout: 5000
|
|
2726
|
+
* })
|
|
2727
|
+
* ```
|
|
2728
|
+
*/
|
|
2729
|
+
fontPicker: (name, label, fontPickerProps) => ({
|
|
2730
|
+
fontPickerProps,
|
|
2731
|
+
label,
|
|
2732
|
+
name,
|
|
2733
|
+
type: "fontPicker"
|
|
2734
|
+
}),
|
|
2521
2735
|
/**
|
|
2522
2736
|
* Create an input field
|
|
2737
|
+
*
|
|
2738
|
+
* @example
|
|
2739
|
+
* ```tsx
|
|
2740
|
+
* // Simple input
|
|
2741
|
+
* FormFieldHelpers.input("name", "Name")
|
|
2742
|
+
*
|
|
2743
|
+
* // With type
|
|
2744
|
+
* FormFieldHelpers.input("email", "Email", "email")
|
|
2745
|
+
*
|
|
2746
|
+
* // With full customization
|
|
2747
|
+
* FormFieldHelpers.input("email", "Email", "email", {
|
|
2748
|
+
* placeholder: "Enter your email",
|
|
2749
|
+
* classNames: { input: "custom-input" },
|
|
2750
|
+
* startContent: <MailIcon />,
|
|
2751
|
+
* description: "We'll never share your email"
|
|
2752
|
+
* })
|
|
2753
|
+
* ```
|
|
2523
2754
|
*/
|
|
2524
|
-
input: (name, label, type
|
|
2525
|
-
inputProps: {
|
|
2755
|
+
input: (name, label, type, inputProps) => ({
|
|
2756
|
+
inputProps: {
|
|
2757
|
+
type: type || "text",
|
|
2758
|
+
...inputProps
|
|
2759
|
+
},
|
|
2526
2760
|
label,
|
|
2527
2761
|
name,
|
|
2528
2762
|
type: "input"
|
|
2529
2763
|
}),
|
|
2764
|
+
/**
|
|
2765
|
+
* Create a radio group field
|
|
2766
|
+
*
|
|
2767
|
+
* @example
|
|
2768
|
+
* ```tsx
|
|
2769
|
+
* // Simple radio group
|
|
2770
|
+
* FormFieldHelpers.radio("gender", "Gender", [
|
|
2771
|
+
* { label: "Male", value: "male" },
|
|
2772
|
+
* { label: "Female", value: "female" }
|
|
2773
|
+
* ])
|
|
2774
|
+
*
|
|
2775
|
+
* // With full customization
|
|
2776
|
+
* FormFieldHelpers.radio("gender", "Gender", options, {
|
|
2777
|
+
* orientation: "horizontal",
|
|
2778
|
+
* classNames: { base: "custom-radio" }
|
|
2779
|
+
* })
|
|
2780
|
+
* ```
|
|
2781
|
+
*/
|
|
2782
|
+
radio: (name, label, options, radioProps) => ({
|
|
2783
|
+
label,
|
|
2784
|
+
name,
|
|
2785
|
+
radioOptions: options,
|
|
2786
|
+
radioProps,
|
|
2787
|
+
type: "radio"
|
|
2788
|
+
}),
|
|
2530
2789
|
/**
|
|
2531
2790
|
* Create a select field
|
|
2791
|
+
*
|
|
2792
|
+
* @example
|
|
2793
|
+
* ```tsx
|
|
2794
|
+
* // Simple select
|
|
2795
|
+
* FormFieldHelpers.select("country", "Country", options)
|
|
2796
|
+
*
|
|
2797
|
+
* // With full customization
|
|
2798
|
+
* FormFieldHelpers.select("country", "Country", options, {
|
|
2799
|
+
* placeholder: "Select a country",
|
|
2800
|
+
* classNames: { trigger: "custom-select" },
|
|
2801
|
+
* selectionMode: "multiple"
|
|
2802
|
+
* })
|
|
2803
|
+
* ```
|
|
2532
2804
|
*/
|
|
2533
|
-
select: (name, label, options) => ({
|
|
2805
|
+
select: (name, label, options, selectProps) => ({
|
|
2534
2806
|
label,
|
|
2535
2807
|
name,
|
|
2536
2808
|
options,
|
|
2809
|
+
selectProps,
|
|
2537
2810
|
type: "select"
|
|
2538
2811
|
}),
|
|
2812
|
+
/**
|
|
2813
|
+
* Create a slider field
|
|
2814
|
+
*
|
|
2815
|
+
* @example
|
|
2816
|
+
* ```tsx
|
|
2817
|
+
* // Simple slider
|
|
2818
|
+
* FormFieldHelpers.slider("rating", "Rating")
|
|
2819
|
+
*
|
|
2820
|
+
* // With full customization
|
|
2821
|
+
* FormFieldHelpers.slider("rating", "Rating", {
|
|
2822
|
+
* minValue: 1,
|
|
2823
|
+
* maxValue: 5,
|
|
2824
|
+
* step: 1,
|
|
2825
|
+
* showSteps: true,
|
|
2826
|
+
* classNames: { base: "custom-slider" }
|
|
2827
|
+
* })
|
|
2828
|
+
* ```
|
|
2829
|
+
*/
|
|
2830
|
+
slider: (name, label, sliderProps) => ({
|
|
2831
|
+
label,
|
|
2832
|
+
name,
|
|
2833
|
+
sliderProps,
|
|
2834
|
+
type: "slider"
|
|
2835
|
+
}),
|
|
2539
2836
|
/**
|
|
2540
2837
|
* Create a switch field
|
|
2838
|
+
*
|
|
2839
|
+
* @example
|
|
2840
|
+
* ```tsx
|
|
2841
|
+
* // Simple switch
|
|
2842
|
+
* FormFieldHelpers.switch("notifications", "Enable notifications")
|
|
2843
|
+
*
|
|
2844
|
+
* // With description
|
|
2845
|
+
* FormFieldHelpers.switch("notifications", "Enable notifications", "Receive email notifications")
|
|
2846
|
+
*
|
|
2847
|
+
* // With full customization
|
|
2848
|
+
* FormFieldHelpers.switch("notifications", "Enable notifications", "Receive email notifications", {
|
|
2849
|
+
* classNames: { base: "custom-switch" },
|
|
2850
|
+
* size: "lg",
|
|
2851
|
+
* color: "primary"
|
|
2852
|
+
* })
|
|
2853
|
+
* ```
|
|
2541
2854
|
*/
|
|
2542
|
-
switch: (name, label, description) => ({
|
|
2855
|
+
switch: (name, label, description, switchProps) => ({
|
|
2543
2856
|
description,
|
|
2544
2857
|
label,
|
|
2545
2858
|
name,
|
|
2859
|
+
switchProps,
|
|
2546
2860
|
type: "switch"
|
|
2547
2861
|
}),
|
|
2548
2862
|
/**
|
|
2549
2863
|
* Create a textarea field
|
|
2864
|
+
*
|
|
2865
|
+
* @example
|
|
2866
|
+
* ```tsx
|
|
2867
|
+
* // Simple textarea
|
|
2868
|
+
* FormFieldHelpers.textarea("message", "Message")
|
|
2869
|
+
*
|
|
2870
|
+
* // With placeholder
|
|
2871
|
+
* FormFieldHelpers.textarea("message", "Message", "Enter your message")
|
|
2872
|
+
*
|
|
2873
|
+
* // With full customization
|
|
2874
|
+
* FormFieldHelpers.textarea("message", "Message", "Enter your message", {
|
|
2875
|
+
* classNames: { input: "custom-textarea" },
|
|
2876
|
+
* minRows: 3,
|
|
2877
|
+
* maxRows: 10
|
|
2878
|
+
* })
|
|
2879
|
+
* ```
|
|
2550
2880
|
*/
|
|
2551
|
-
textarea: (name, label, placeholder) => ({
|
|
2881
|
+
textarea: (name, label, placeholder, textareaProps) => ({
|
|
2552
2882
|
label,
|
|
2553
2883
|
name,
|
|
2554
|
-
textareaProps: {
|
|
2884
|
+
textareaProps: {
|
|
2885
|
+
...placeholder && { placeholder },
|
|
2886
|
+
...textareaProps
|
|
2887
|
+
},
|
|
2555
2888
|
type: "textarea"
|
|
2556
2889
|
})
|
|
2557
2890
|
};
|
|
@@ -2692,11 +3025,10 @@ function sliderField(name, label, props) {
|
|
|
2692
3025
|
type: "slider",
|
|
2693
3026
|
...props && {
|
|
2694
3027
|
sliderProps: {
|
|
2695
|
-
className: props.className
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
step: props.step || 1
|
|
3028
|
+
className: props.className,
|
|
3029
|
+
maxValue: props.max ?? 100,
|
|
3030
|
+
minValue: props.min ?? 0,
|
|
3031
|
+
step: props.step ?? 1
|
|
2700
3032
|
}
|
|
2701
3033
|
}
|
|
2702
3034
|
};
|
|
@@ -2708,9 +3040,8 @@ function dateField(name, label, props) {
|
|
|
2708
3040
|
type: "date",
|
|
2709
3041
|
...props && {
|
|
2710
3042
|
dateProps: {
|
|
2711
|
-
className: props.className
|
|
2712
|
-
|
|
2713
|
-
placeholder: props.placeholder || ""
|
|
3043
|
+
className: props.className,
|
|
3044
|
+
placeholder: props.placeholder
|
|
2714
3045
|
}
|
|
2715
3046
|
}
|
|
2716
3047
|
};
|
|
@@ -2732,15 +3063,12 @@ function fileField(name, label, props) {
|
|
|
2732
3063
|
}
|
|
2733
3064
|
function fontPickerField(name, label, props) {
|
|
2734
3065
|
return {
|
|
3066
|
+
className: props?.className,
|
|
3067
|
+
description: props?.description,
|
|
3068
|
+
fontPickerProps: props?.fontPickerProps,
|
|
2735
3069
|
label,
|
|
2736
3070
|
name,
|
|
2737
|
-
type: "fontPicker"
|
|
2738
|
-
...props && {
|
|
2739
|
-
fontPickerProps: {
|
|
2740
|
-
className: props.className || "",
|
|
2741
|
-
disabled: props.isDisabled || false
|
|
2742
|
-
}
|
|
2743
|
-
}
|
|
3071
|
+
type: "fontPicker"
|
|
2744
3072
|
};
|
|
2745
3073
|
}
|
|
2746
3074
|
function contentField(title, description, options) {
|
|
@@ -3062,7 +3390,12 @@ var TypeInferredBuilder = class {
|
|
|
3062
3390
|
this.formFields.push({
|
|
3063
3391
|
label,
|
|
3064
3392
|
name,
|
|
3065
|
-
sliderProps: {
|
|
3393
|
+
sliderProps: {
|
|
3394
|
+
maxValue: max,
|
|
3395
|
+
minValue: min,
|
|
3396
|
+
step,
|
|
3397
|
+
...fieldOptions
|
|
3398
|
+
},
|
|
3066
3399
|
type: "slider"
|
|
3067
3400
|
});
|
|
3068
3401
|
return this;
|
|
@@ -3527,6 +3860,131 @@ function useMemoizedFieldProps(props, deps) {
|
|
|
3527
3860
|
return useMemo2(() => props, deps);
|
|
3528
3861
|
}
|
|
3529
3862
|
|
|
3863
|
+
// src/utils/arraySync.ts
|
|
3864
|
+
function syncArrays(options) {
|
|
3865
|
+
const { current, existing, getId } = options;
|
|
3866
|
+
const existingMap = /* @__PURE__ */ new Map();
|
|
3867
|
+
const currentMap = /* @__PURE__ */ new Map();
|
|
3868
|
+
existing.forEach((item) => {
|
|
3869
|
+
const id = getId(item);
|
|
3870
|
+
if (id !== void 0) {
|
|
3871
|
+
existingMap.set(id, item);
|
|
3872
|
+
}
|
|
3873
|
+
});
|
|
3874
|
+
current.forEach((item) => {
|
|
3875
|
+
const id = getId(item);
|
|
3876
|
+
if (id !== void 0) {
|
|
3877
|
+
currentMap.set(id, item);
|
|
3878
|
+
}
|
|
3879
|
+
});
|
|
3880
|
+
const toDelete = [];
|
|
3881
|
+
existingMap.forEach((item, id) => {
|
|
3882
|
+
if (!currentMap.has(id)) {
|
|
3883
|
+
toDelete.push(item);
|
|
3884
|
+
}
|
|
3885
|
+
});
|
|
3886
|
+
const toUpdate = [];
|
|
3887
|
+
existingMap.forEach((existingItem, id) => {
|
|
3888
|
+
const currentItem = currentMap.get(id);
|
|
3889
|
+
if (currentItem) {
|
|
3890
|
+
toUpdate.push({ current: currentItem, existing: existingItem });
|
|
3891
|
+
}
|
|
3892
|
+
});
|
|
3893
|
+
const toCreate = [];
|
|
3894
|
+
currentMap.forEach((item, id) => {
|
|
3895
|
+
if (!existingMap.has(id)) {
|
|
3896
|
+
toCreate.push(item);
|
|
3897
|
+
}
|
|
3898
|
+
});
|
|
3899
|
+
return {
|
|
3900
|
+
toCreate,
|
|
3901
|
+
toDelete,
|
|
3902
|
+
toUpdate
|
|
3903
|
+
};
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3906
|
+
// src/utils/createFieldArrayCustomConfig.tsx
|
|
3907
|
+
import React25 from "react";
|
|
3908
|
+
import { useFieldArray as useFieldArray2 } from "react-hook-form";
|
|
3909
|
+
import { Button as Button6 } from "@heroui/react";
|
|
3910
|
+
function createFieldArrayCustomConfig(options) {
|
|
3911
|
+
const {
|
|
3912
|
+
className,
|
|
3913
|
+
defaultItem,
|
|
3914
|
+
enableReordering = false,
|
|
3915
|
+
label,
|
|
3916
|
+
max = 10,
|
|
3917
|
+
min = 0,
|
|
3918
|
+
name,
|
|
3919
|
+
renderAddButton,
|
|
3920
|
+
renderItem
|
|
3921
|
+
} = options;
|
|
3922
|
+
return {
|
|
3923
|
+
className,
|
|
3924
|
+
label,
|
|
3925
|
+
name,
|
|
3926
|
+
// ArrayPath is compatible with Path for CustomFieldConfig
|
|
3927
|
+
render: ({ control, errors, form }) => {
|
|
3928
|
+
const { append, fields, move, remove } = useFieldArray2({
|
|
3929
|
+
control,
|
|
3930
|
+
name
|
|
3931
|
+
});
|
|
3932
|
+
const canAdd = fields.length < max;
|
|
3933
|
+
const canRemove = fields.length > min;
|
|
3934
|
+
const handleAdd = () => {
|
|
3935
|
+
if (canAdd) {
|
|
3936
|
+
if (defaultItem) {
|
|
3937
|
+
append(defaultItem());
|
|
3938
|
+
} else {
|
|
3939
|
+
append({});
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
};
|
|
3943
|
+
const handleRemove = (index) => {
|
|
3944
|
+
if (canRemove) {
|
|
3945
|
+
remove(index);
|
|
3946
|
+
}
|
|
3947
|
+
};
|
|
3948
|
+
const handleMoveUp = (index) => {
|
|
3949
|
+
if (enableReordering && index > 0) {
|
|
3950
|
+
move(index, index - 1);
|
|
3951
|
+
}
|
|
3952
|
+
};
|
|
3953
|
+
const handleMoveDown = (index) => {
|
|
3954
|
+
if (enableReordering && index < fields.length - 1) {
|
|
3955
|
+
move(index, index + 1);
|
|
3956
|
+
}
|
|
3957
|
+
};
|
|
3958
|
+
return /* @__PURE__ */ React25.createElement("div", { className }, /* @__PURE__ */ React25.createElement("div", { className: "space-y-4" }, fields.map((field2, index) => {
|
|
3959
|
+
const canMoveUp = enableReordering && index > 0;
|
|
3960
|
+
const canMoveDown = enableReordering && index < fields.length - 1;
|
|
3961
|
+
return /* @__PURE__ */ React25.createElement(React25.Fragment, { key: field2.id }, renderItem({
|
|
3962
|
+
canMoveDown,
|
|
3963
|
+
canMoveUp,
|
|
3964
|
+
control,
|
|
3965
|
+
errors,
|
|
3966
|
+
field: field2,
|
|
3967
|
+
fields,
|
|
3968
|
+
form,
|
|
3969
|
+
index,
|
|
3970
|
+
onMoveDown: () => handleMoveDown(index),
|
|
3971
|
+
onMoveUp: () => handleMoveUp(index),
|
|
3972
|
+
onRemove: () => handleRemove(index)
|
|
3973
|
+
}));
|
|
3974
|
+
}), fields.length === 0 && renderAddButton ? /* @__PURE__ */ React25.createElement("div", { className: "text-center py-8 text-gray-500" }, /* @__PURE__ */ React25.createElement("p", null, "No ", label?.toLowerCase() || "items", " added yet."), renderAddButton({ canAdd, onAdd: handleAdd })) : null, fields.length > 0 && renderAddButton ? renderAddButton({ canAdd, onAdd: handleAdd }) : canAdd && /* @__PURE__ */ React25.createElement(
|
|
3975
|
+
Button6,
|
|
3976
|
+
{
|
|
3977
|
+
variant: "bordered",
|
|
3978
|
+
onPress: handleAdd,
|
|
3979
|
+
className: "w-full"
|
|
3980
|
+
},
|
|
3981
|
+
"Add Item"
|
|
3982
|
+
)));
|
|
3983
|
+
},
|
|
3984
|
+
type: "custom"
|
|
3985
|
+
};
|
|
3986
|
+
}
|
|
3987
|
+
|
|
3530
3988
|
// src/builders/validation-helpers.ts
|
|
3531
3989
|
import { z as z4 } from "zod";
|
|
3532
3990
|
var validationPatterns = {
|
|
@@ -3700,6 +4158,7 @@ export {
|
|
|
3700
4158
|
RadioGroupField,
|
|
3701
4159
|
SelectField,
|
|
3702
4160
|
ServerActionForm,
|
|
4161
|
+
SimpleForm,
|
|
3703
4162
|
SliderField,
|
|
3704
4163
|
SubmitButton,
|
|
3705
4164
|
SwitchField,
|
|
@@ -3715,6 +4174,7 @@ export {
|
|
|
3715
4174
|
createEmailSchema,
|
|
3716
4175
|
createField,
|
|
3717
4176
|
createFieldArrayBuilder,
|
|
4177
|
+
createFieldArrayCustomConfig,
|
|
3718
4178
|
createFieldArrayItemBuilder,
|
|
3719
4179
|
createFileSchema,
|
|
3720
4180
|
createFormTestUtils,
|
|
@@ -3748,6 +4208,7 @@ export {
|
|
|
3748
4208
|
shallowEqual,
|
|
3749
4209
|
simulateFieldInput,
|
|
3750
4210
|
simulateFormSubmission,
|
|
4211
|
+
syncArrays,
|
|
3751
4212
|
throttle,
|
|
3752
4213
|
useDebouncedFieldValidation,
|
|
3753
4214
|
useDebouncedValidation,
|