@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/react/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";
|
|
@@ -438,18 +438,23 @@ function FieldArrayField({
|
|
|
438
438
|
}) {
|
|
439
439
|
const {
|
|
440
440
|
addButtonText = "Add Item",
|
|
441
|
+
defaultItem,
|
|
442
|
+
enableReordering = false,
|
|
441
443
|
fields: fieldConfigs,
|
|
442
444
|
max = 10,
|
|
443
445
|
min = 0,
|
|
444
446
|
name,
|
|
445
|
-
removeButtonText = "Remove"
|
|
447
|
+
removeButtonText = "Remove",
|
|
448
|
+
renderAddButton,
|
|
449
|
+
renderItem,
|
|
450
|
+
reorderButtonText = { down: "\u2193", up: "\u2191" }
|
|
446
451
|
} = config;
|
|
447
452
|
const form = useFormContext3();
|
|
448
453
|
if (!form || !form.control) {
|
|
449
454
|
return null;
|
|
450
455
|
}
|
|
451
456
|
const { control } = form;
|
|
452
|
-
const { append, fields, remove } = useFieldArray({
|
|
457
|
+
const { append, fields, move, remove } = useFieldArray({
|
|
453
458
|
control,
|
|
454
459
|
name
|
|
455
460
|
// FieldArray name
|
|
@@ -458,18 +463,22 @@ function FieldArrayField({
|
|
|
458
463
|
const canRemove = fields.length > min;
|
|
459
464
|
const handleAdd = () => {
|
|
460
465
|
if (canAdd) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
466
|
+
if (defaultItem) {
|
|
467
|
+
append(defaultItem());
|
|
468
|
+
} else {
|
|
469
|
+
const defaultValues = fieldConfigs.reduce((acc, fieldConfig) => {
|
|
470
|
+
const fieldName = fieldConfig.name;
|
|
471
|
+
if (fieldConfig.type === "checkbox" || fieldConfig.type === "switch") {
|
|
472
|
+
acc[fieldName] = false;
|
|
473
|
+
} else if (fieldConfig.type === "slider") {
|
|
474
|
+
acc[fieldName] = 0;
|
|
475
|
+
} else {
|
|
476
|
+
acc[fieldName] = "";
|
|
477
|
+
}
|
|
478
|
+
return acc;
|
|
479
|
+
}, {});
|
|
480
|
+
append(defaultValues);
|
|
481
|
+
}
|
|
473
482
|
}
|
|
474
483
|
};
|
|
475
484
|
const handleRemove = (index) => {
|
|
@@ -477,60 +486,131 @@ function FieldArrayField({
|
|
|
477
486
|
remove(index);
|
|
478
487
|
}
|
|
479
488
|
};
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
489
|
+
const handleMoveUp = (index) => {
|
|
490
|
+
if (index > 0) {
|
|
491
|
+
move(index, index - 1);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
const handleMoveDown = (index) => {
|
|
495
|
+
if (index < fields.length - 1) {
|
|
496
|
+
move(index, index + 1);
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
const renderFieldArrayItems = () => {
|
|
500
|
+
return fields.map((field2, index) => {
|
|
501
|
+
const canMoveUp = enableReordering && index > 0;
|
|
502
|
+
const canMoveDown = enableReordering && index < fields.length - 1;
|
|
503
|
+
const itemCanRemove = canRemove;
|
|
504
|
+
const fieldElements = fieldConfigs.map((fieldConfig) => {
|
|
505
|
+
const fieldName = fieldConfig.name;
|
|
506
|
+
const fullPath = `${name}.${index}.${fieldName}`;
|
|
507
|
+
let processedConfig = { ...fieldConfig, name: fullPath };
|
|
508
|
+
if ("dependsOn" in fieldConfig && fieldConfig.dependsOn && typeof fieldConfig.dependsOn === "string") {
|
|
509
|
+
const dependsOnPath = fieldConfig.dependsOn;
|
|
510
|
+
if (!dependsOnPath.startsWith(`${name}.`)) {
|
|
511
|
+
processedConfig = {
|
|
512
|
+
...processedConfig,
|
|
513
|
+
dependsOn: `${name}.${index}.${dependsOnPath}`,
|
|
514
|
+
// Preserve dependsOnValue if it exists
|
|
515
|
+
..."dependsOnValue" in fieldConfig && {
|
|
516
|
+
dependsOnValue: fieldConfig.dependsOnValue
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return /* @__PURE__ */ React8.createElement(
|
|
522
|
+
FormField,
|
|
523
|
+
{
|
|
524
|
+
key: `${fieldConfig.name}-${index}`,
|
|
525
|
+
config: processedConfig,
|
|
526
|
+
form,
|
|
527
|
+
submissionState: {
|
|
528
|
+
error: void 0,
|
|
529
|
+
isSubmitted: false,
|
|
530
|
+
isSubmitting: false,
|
|
531
|
+
isSuccess: false
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
);
|
|
535
|
+
});
|
|
536
|
+
if (renderItem) {
|
|
537
|
+
return /* @__PURE__ */ React8.createElement(React8.Fragment, { key: field2.id }, renderItem({
|
|
538
|
+
canMoveDown,
|
|
539
|
+
canMoveUp,
|
|
540
|
+
canRemove: itemCanRemove,
|
|
541
|
+
children: /* @__PURE__ */ React8.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, fieldElements),
|
|
542
|
+
field: field2,
|
|
543
|
+
fields,
|
|
544
|
+
index,
|
|
545
|
+
onMoveDown: () => handleMoveDown(index),
|
|
546
|
+
onMoveUp: () => handleMoveUp(index),
|
|
547
|
+
onRemove: () => handleRemove(index)
|
|
548
|
+
}));
|
|
549
|
+
}
|
|
550
|
+
return /* @__PURE__ */ React8.createElement(
|
|
551
|
+
"div",
|
|
552
|
+
{
|
|
553
|
+
key: field2.id,
|
|
554
|
+
className: "border border-gray-200 rounded-lg p-4 space-y-4"
|
|
555
|
+
},
|
|
556
|
+
/* @__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(
|
|
557
|
+
Button2,
|
|
558
|
+
{
|
|
559
|
+
size: "sm",
|
|
560
|
+
variant: "light",
|
|
561
|
+
isDisabled: !canMoveUp,
|
|
562
|
+
onPress: () => handleMoveUp(index),
|
|
563
|
+
"aria-label": `Move ${config.label} ${index + 1} up`
|
|
564
|
+
},
|
|
565
|
+
reorderButtonText.up
|
|
566
|
+
), /* @__PURE__ */ React8.createElement(
|
|
567
|
+
Button2,
|
|
568
|
+
{
|
|
569
|
+
size: "sm",
|
|
570
|
+
variant: "light",
|
|
571
|
+
isDisabled: !canMoveDown,
|
|
572
|
+
onPress: () => handleMoveDown(index),
|
|
573
|
+
"aria-label": `Move ${config.label} ${index + 1} down`
|
|
574
|
+
},
|
|
575
|
+
reorderButtonText.down
|
|
576
|
+
)), itemCanRemove && /* @__PURE__ */ React8.createElement(
|
|
577
|
+
Button2,
|
|
578
|
+
{
|
|
579
|
+
size: "sm",
|
|
580
|
+
variant: "light",
|
|
581
|
+
color: "danger",
|
|
582
|
+
startContent: "\u{1F5D1}\uFE0F",
|
|
583
|
+
onPress: () => handleRemove(index),
|
|
584
|
+
"aria-label": `${removeButtonText} ${config.label} ${index + 1}`
|
|
585
|
+
},
|
|
586
|
+
removeButtonText
|
|
587
|
+
))),
|
|
588
|
+
/* @__PURE__ */ React8.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, fieldElements)
|
|
589
|
+
);
|
|
590
|
+
});
|
|
591
|
+
};
|
|
592
|
+
const renderAddButtonElement = () => {
|
|
593
|
+
if (renderAddButton) {
|
|
594
|
+
return renderAddButton({
|
|
595
|
+
canAdd,
|
|
596
|
+
onAdd: handleAdd
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
if (!canAdd) {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
return /* @__PURE__ */ React8.createElement(
|
|
487
603
|
Button2,
|
|
488
604
|
{
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
onPress: () => handleRemove(index),
|
|
494
|
-
"aria-label": `${removeButtonText} ${config.label} ${index + 1}`
|
|
605
|
+
variant: "bordered",
|
|
606
|
+
startContent: "\u2795",
|
|
607
|
+
onPress: handleAdd,
|
|
608
|
+
className: "w-full"
|
|
495
609
|
},
|
|
496
|
-
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
{
|
|
501
|
-
key: `${fieldConfig.name}-${index}`,
|
|
502
|
-
config: {
|
|
503
|
-
...fieldConfig,
|
|
504
|
-
name: `${name}.${index}.${fieldConfig.name}`
|
|
505
|
-
},
|
|
506
|
-
form,
|
|
507
|
-
submissionState: {
|
|
508
|
-
error: void 0,
|
|
509
|
-
isSubmitted: false,
|
|
510
|
-
isSubmitting: false,
|
|
511
|
-
isSuccess: false
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
)))
|
|
515
|
-
)), canAdd && /* @__PURE__ */ React8.createElement(
|
|
516
|
-
Button2,
|
|
517
|
-
{
|
|
518
|
-
variant: "bordered",
|
|
519
|
-
startContent: "\u2795",
|
|
520
|
-
onPress: handleAdd,
|
|
521
|
-
className: "w-full"
|
|
522
|
-
},
|
|
523
|
-
addButtonText
|
|
524
|
-
), 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(
|
|
525
|
-
Button2,
|
|
526
|
-
{
|
|
527
|
-
variant: "bordered",
|
|
528
|
-
startContent: "\u2795",
|
|
529
|
-
onPress: handleAdd,
|
|
530
|
-
className: "mt-2"
|
|
531
|
-
},
|
|
532
|
-
addButtonText
|
|
533
|
-
))));
|
|
610
|
+
addButtonText
|
|
611
|
+
);
|
|
612
|
+
};
|
|
613
|
+
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()));
|
|
534
614
|
}
|
|
535
615
|
|
|
536
616
|
// src/fields/FileField.tsx
|
|
@@ -1056,7 +1136,7 @@ var FormField = React17.memo(
|
|
|
1056
1136
|
return null;
|
|
1057
1137
|
}
|
|
1058
1138
|
if (config.dependsOn) {
|
|
1059
|
-
const dependentValue = watchedValues
|
|
1139
|
+
const dependentValue = get(watchedValues, config.dependsOn);
|
|
1060
1140
|
if (config.dependsOnValue !== void 0 && dependentValue !== config.dependsOnValue) {
|
|
1061
1141
|
return null;
|
|
1062
1142
|
}
|
|
@@ -2340,6 +2420,44 @@ function ZodForm({
|
|
|
2340
2420
|
))));
|
|
2341
2421
|
}
|
|
2342
2422
|
|
|
2423
|
+
// src/components/SimpleForm.tsx
|
|
2424
|
+
import React24 from "react";
|
|
2425
|
+
function SimpleForm({
|
|
2426
|
+
className,
|
|
2427
|
+
defaultValues,
|
|
2428
|
+
field: field2,
|
|
2429
|
+
hideSubmitButton = false,
|
|
2430
|
+
onError,
|
|
2431
|
+
onSubmit,
|
|
2432
|
+
onSuccess,
|
|
2433
|
+
schema,
|
|
2434
|
+
submitButton,
|
|
2435
|
+
subtitle,
|
|
2436
|
+
title
|
|
2437
|
+
}) {
|
|
2438
|
+
return /* @__PURE__ */ React24.createElement(
|
|
2439
|
+
ZodForm,
|
|
2440
|
+
{
|
|
2441
|
+
className,
|
|
2442
|
+
config: {
|
|
2443
|
+
defaultValues,
|
|
2444
|
+
fields: [field2],
|
|
2445
|
+
schema
|
|
2446
|
+
},
|
|
2447
|
+
onError,
|
|
2448
|
+
onSubmit,
|
|
2449
|
+
onSuccess,
|
|
2450
|
+
showResetButton: false,
|
|
2451
|
+
submitButtonText: hideSubmitButton ? "" : "Submit",
|
|
2452
|
+
subtitle,
|
|
2453
|
+
title,
|
|
2454
|
+
submitButtonProps: hideSubmitButton && submitButton ? {
|
|
2455
|
+
style: { display: "none" }
|
|
2456
|
+
} : {}
|
|
2457
|
+
}
|
|
2458
|
+
);
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2343
2461
|
// src/builders/BasicFormBuilder.ts
|
|
2344
2462
|
var BasicFormBuilder = class {
|
|
2345
2463
|
constructor() {
|
|
@@ -2444,9 +2562,27 @@ function createBasicFormBuilder() {
|
|
|
2444
2562
|
var FormFieldHelpers = {
|
|
2445
2563
|
/**
|
|
2446
2564
|
* Create an autocomplete field
|
|
2565
|
+
*
|
|
2566
|
+
* @example
|
|
2567
|
+
* ```tsx
|
|
2568
|
+
* // Simple autocomplete
|
|
2569
|
+
* FormFieldHelpers.autocomplete("country", "Country", options)
|
|
2570
|
+
*
|
|
2571
|
+
* // With placeholder
|
|
2572
|
+
* FormFieldHelpers.autocomplete("country", "Country", options, "Search countries")
|
|
2573
|
+
*
|
|
2574
|
+
* // With full customization
|
|
2575
|
+
* FormFieldHelpers.autocomplete("country", "Country", options, "Search countries", {
|
|
2576
|
+
* classNames: { base: "custom-autocomplete" },
|
|
2577
|
+
* allowsCustomValue: true
|
|
2578
|
+
* })
|
|
2579
|
+
* ```
|
|
2447
2580
|
*/
|
|
2448
|
-
autocomplete: (name, label, items, placeholder) => ({
|
|
2449
|
-
autocompleteProps:
|
|
2581
|
+
autocomplete: (name, label, items, placeholder, autocompleteProps) => ({
|
|
2582
|
+
autocompleteProps: {
|
|
2583
|
+
...placeholder && { placeholder },
|
|
2584
|
+
...autocompleteProps
|
|
2585
|
+
},
|
|
2450
2586
|
label,
|
|
2451
2587
|
name,
|
|
2452
2588
|
options: items,
|
|
@@ -2454,8 +2590,21 @@ var FormFieldHelpers = {
|
|
|
2454
2590
|
}),
|
|
2455
2591
|
/**
|
|
2456
2592
|
* Create a checkbox field
|
|
2593
|
+
*
|
|
2594
|
+
* @example
|
|
2595
|
+
* ```tsx
|
|
2596
|
+
* // Simple checkbox
|
|
2597
|
+
* FormFieldHelpers.checkbox("newsletter", "Subscribe to newsletter")
|
|
2598
|
+
*
|
|
2599
|
+
* // With full customization
|
|
2600
|
+
* FormFieldHelpers.checkbox("newsletter", "Subscribe to newsletter", {
|
|
2601
|
+
* classNames: { base: "custom-checkbox" },
|
|
2602
|
+
* size: "lg"
|
|
2603
|
+
* })
|
|
2604
|
+
* ```
|
|
2457
2605
|
*/
|
|
2458
|
-
checkbox: (name, label) => ({
|
|
2606
|
+
checkbox: (name, label, checkboxProps) => ({
|
|
2607
|
+
checkboxProps,
|
|
2459
2608
|
label,
|
|
2460
2609
|
name,
|
|
2461
2610
|
type: "checkbox"
|
|
@@ -2517,6 +2666,19 @@ var FormFieldHelpers = {
|
|
|
2517
2666
|
},
|
|
2518
2667
|
/**
|
|
2519
2668
|
* Create a date field
|
|
2669
|
+
*
|
|
2670
|
+
* @example
|
|
2671
|
+
* ```tsx
|
|
2672
|
+
* // Simple date field
|
|
2673
|
+
* FormFieldHelpers.date("birthDate", "Birth Date")
|
|
2674
|
+
*
|
|
2675
|
+
* // With full customization
|
|
2676
|
+
* FormFieldHelpers.date("birthDate", "Birth Date", {
|
|
2677
|
+
* label: "Select your birth date",
|
|
2678
|
+
* granularity: "day",
|
|
2679
|
+
* minValue: new CalendarDate(1900, 1, 1)
|
|
2680
|
+
* })
|
|
2681
|
+
* ```
|
|
2520
2682
|
*/
|
|
2521
2683
|
date: (name, label, dateProps) => ({
|
|
2522
2684
|
dateProps,
|
|
@@ -2524,40 +2686,211 @@ var FormFieldHelpers = {
|
|
|
2524
2686
|
name,
|
|
2525
2687
|
type: "date"
|
|
2526
2688
|
}),
|
|
2689
|
+
/**
|
|
2690
|
+
* Create a file upload field
|
|
2691
|
+
*
|
|
2692
|
+
* @example
|
|
2693
|
+
* ```tsx
|
|
2694
|
+
* // Simple file field
|
|
2695
|
+
* FormFieldHelpers.file("avatar", "Profile Picture")
|
|
2696
|
+
*
|
|
2697
|
+
* // With accept and multiple
|
|
2698
|
+
* FormFieldHelpers.file("avatar", "Profile Picture", {
|
|
2699
|
+
* accept: "image/*",
|
|
2700
|
+
* multiple: true
|
|
2701
|
+
* })
|
|
2702
|
+
*
|
|
2703
|
+
* // With full customization
|
|
2704
|
+
* FormFieldHelpers.file("avatar", "Profile Picture", {
|
|
2705
|
+
* accept: "image/*",
|
|
2706
|
+
* multiple: false,
|
|
2707
|
+
* fileProps: { className: "custom-file-input" }
|
|
2708
|
+
* })
|
|
2709
|
+
* ```
|
|
2710
|
+
*/
|
|
2711
|
+
file: (name, label, options) => ({
|
|
2712
|
+
accept: options?.accept,
|
|
2713
|
+
fileProps: options?.fileProps,
|
|
2714
|
+
label,
|
|
2715
|
+
multiple: options?.multiple,
|
|
2716
|
+
name,
|
|
2717
|
+
type: "file"
|
|
2718
|
+
}),
|
|
2719
|
+
/**
|
|
2720
|
+
* Create a font picker field
|
|
2721
|
+
*
|
|
2722
|
+
* @example
|
|
2723
|
+
* ```tsx
|
|
2724
|
+
* // Simple font picker
|
|
2725
|
+
* FormFieldHelpers.fontPicker("font", "Choose Font")
|
|
2726
|
+
*
|
|
2727
|
+
* // With full customization
|
|
2728
|
+
* FormFieldHelpers.fontPicker("font", "Choose Font", {
|
|
2729
|
+
* showFontPreview: true,
|
|
2730
|
+
* loadAllVariants: false,
|
|
2731
|
+
* fontsLoadedTimeout: 5000
|
|
2732
|
+
* })
|
|
2733
|
+
* ```
|
|
2734
|
+
*/
|
|
2735
|
+
fontPicker: (name, label, fontPickerProps) => ({
|
|
2736
|
+
fontPickerProps,
|
|
2737
|
+
label,
|
|
2738
|
+
name,
|
|
2739
|
+
type: "fontPicker"
|
|
2740
|
+
}),
|
|
2527
2741
|
/**
|
|
2528
2742
|
* Create an input field
|
|
2743
|
+
*
|
|
2744
|
+
* @example
|
|
2745
|
+
* ```tsx
|
|
2746
|
+
* // Simple input
|
|
2747
|
+
* FormFieldHelpers.input("name", "Name")
|
|
2748
|
+
*
|
|
2749
|
+
* // With type
|
|
2750
|
+
* FormFieldHelpers.input("email", "Email", "email")
|
|
2751
|
+
*
|
|
2752
|
+
* // With full customization
|
|
2753
|
+
* FormFieldHelpers.input("email", "Email", "email", {
|
|
2754
|
+
* placeholder: "Enter your email",
|
|
2755
|
+
* classNames: { input: "custom-input" },
|
|
2756
|
+
* startContent: <MailIcon />,
|
|
2757
|
+
* description: "We'll never share your email"
|
|
2758
|
+
* })
|
|
2759
|
+
* ```
|
|
2529
2760
|
*/
|
|
2530
|
-
input: (name, label, type
|
|
2531
|
-
inputProps: {
|
|
2761
|
+
input: (name, label, type, inputProps) => ({
|
|
2762
|
+
inputProps: {
|
|
2763
|
+
type: type || "text",
|
|
2764
|
+
...inputProps
|
|
2765
|
+
},
|
|
2532
2766
|
label,
|
|
2533
2767
|
name,
|
|
2534
2768
|
type: "input"
|
|
2535
2769
|
}),
|
|
2770
|
+
/**
|
|
2771
|
+
* Create a radio group field
|
|
2772
|
+
*
|
|
2773
|
+
* @example
|
|
2774
|
+
* ```tsx
|
|
2775
|
+
* // Simple radio group
|
|
2776
|
+
* FormFieldHelpers.radio("gender", "Gender", [
|
|
2777
|
+
* { label: "Male", value: "male" },
|
|
2778
|
+
* { label: "Female", value: "female" }
|
|
2779
|
+
* ])
|
|
2780
|
+
*
|
|
2781
|
+
* // With full customization
|
|
2782
|
+
* FormFieldHelpers.radio("gender", "Gender", options, {
|
|
2783
|
+
* orientation: "horizontal",
|
|
2784
|
+
* classNames: { base: "custom-radio" }
|
|
2785
|
+
* })
|
|
2786
|
+
* ```
|
|
2787
|
+
*/
|
|
2788
|
+
radio: (name, label, options, radioProps) => ({
|
|
2789
|
+
label,
|
|
2790
|
+
name,
|
|
2791
|
+
radioOptions: options,
|
|
2792
|
+
radioProps,
|
|
2793
|
+
type: "radio"
|
|
2794
|
+
}),
|
|
2536
2795
|
/**
|
|
2537
2796
|
* Create a select field
|
|
2797
|
+
*
|
|
2798
|
+
* @example
|
|
2799
|
+
* ```tsx
|
|
2800
|
+
* // Simple select
|
|
2801
|
+
* FormFieldHelpers.select("country", "Country", options)
|
|
2802
|
+
*
|
|
2803
|
+
* // With full customization
|
|
2804
|
+
* FormFieldHelpers.select("country", "Country", options, {
|
|
2805
|
+
* placeholder: "Select a country",
|
|
2806
|
+
* classNames: { trigger: "custom-select" },
|
|
2807
|
+
* selectionMode: "multiple"
|
|
2808
|
+
* })
|
|
2809
|
+
* ```
|
|
2538
2810
|
*/
|
|
2539
|
-
select: (name, label, options) => ({
|
|
2811
|
+
select: (name, label, options, selectProps) => ({
|
|
2540
2812
|
label,
|
|
2541
2813
|
name,
|
|
2542
2814
|
options,
|
|
2815
|
+
selectProps,
|
|
2543
2816
|
type: "select"
|
|
2544
2817
|
}),
|
|
2818
|
+
/**
|
|
2819
|
+
* Create a slider field
|
|
2820
|
+
*
|
|
2821
|
+
* @example
|
|
2822
|
+
* ```tsx
|
|
2823
|
+
* // Simple slider
|
|
2824
|
+
* FormFieldHelpers.slider("rating", "Rating")
|
|
2825
|
+
*
|
|
2826
|
+
* // With full customization
|
|
2827
|
+
* FormFieldHelpers.slider("rating", "Rating", {
|
|
2828
|
+
* minValue: 1,
|
|
2829
|
+
* maxValue: 5,
|
|
2830
|
+
* step: 1,
|
|
2831
|
+
* showSteps: true,
|
|
2832
|
+
* classNames: { base: "custom-slider" }
|
|
2833
|
+
* })
|
|
2834
|
+
* ```
|
|
2835
|
+
*/
|
|
2836
|
+
slider: (name, label, sliderProps) => ({
|
|
2837
|
+
label,
|
|
2838
|
+
name,
|
|
2839
|
+
sliderProps,
|
|
2840
|
+
type: "slider"
|
|
2841
|
+
}),
|
|
2545
2842
|
/**
|
|
2546
2843
|
* Create a switch field
|
|
2844
|
+
*
|
|
2845
|
+
* @example
|
|
2846
|
+
* ```tsx
|
|
2847
|
+
* // Simple switch
|
|
2848
|
+
* FormFieldHelpers.switch("notifications", "Enable notifications")
|
|
2849
|
+
*
|
|
2850
|
+
* // With description
|
|
2851
|
+
* FormFieldHelpers.switch("notifications", "Enable notifications", "Receive email notifications")
|
|
2852
|
+
*
|
|
2853
|
+
* // With full customization
|
|
2854
|
+
* FormFieldHelpers.switch("notifications", "Enable notifications", "Receive email notifications", {
|
|
2855
|
+
* classNames: { base: "custom-switch" },
|
|
2856
|
+
* size: "lg",
|
|
2857
|
+
* color: "primary"
|
|
2858
|
+
* })
|
|
2859
|
+
* ```
|
|
2547
2860
|
*/
|
|
2548
|
-
switch: (name, label, description) => ({
|
|
2861
|
+
switch: (name, label, description, switchProps) => ({
|
|
2549
2862
|
description,
|
|
2550
2863
|
label,
|
|
2551
2864
|
name,
|
|
2865
|
+
switchProps,
|
|
2552
2866
|
type: "switch"
|
|
2553
2867
|
}),
|
|
2554
2868
|
/**
|
|
2555
2869
|
* Create a textarea field
|
|
2870
|
+
*
|
|
2871
|
+
* @example
|
|
2872
|
+
* ```tsx
|
|
2873
|
+
* // Simple textarea
|
|
2874
|
+
* FormFieldHelpers.textarea("message", "Message")
|
|
2875
|
+
*
|
|
2876
|
+
* // With placeholder
|
|
2877
|
+
* FormFieldHelpers.textarea("message", "Message", "Enter your message")
|
|
2878
|
+
*
|
|
2879
|
+
* // With full customization
|
|
2880
|
+
* FormFieldHelpers.textarea("message", "Message", "Enter your message", {
|
|
2881
|
+
* classNames: { input: "custom-textarea" },
|
|
2882
|
+
* minRows: 3,
|
|
2883
|
+
* maxRows: 10
|
|
2884
|
+
* })
|
|
2885
|
+
* ```
|
|
2556
2886
|
*/
|
|
2557
|
-
textarea: (name, label, placeholder) => ({
|
|
2887
|
+
textarea: (name, label, placeholder, textareaProps) => ({
|
|
2558
2888
|
label,
|
|
2559
2889
|
name,
|
|
2560
|
-
textareaProps: {
|
|
2890
|
+
textareaProps: {
|
|
2891
|
+
...placeholder && { placeholder },
|
|
2892
|
+
...textareaProps
|
|
2893
|
+
},
|
|
2561
2894
|
type: "textarea"
|
|
2562
2895
|
})
|
|
2563
2896
|
};
|
|
@@ -2698,11 +3031,10 @@ function sliderField(name, label, props) {
|
|
|
2698
3031
|
type: "slider",
|
|
2699
3032
|
...props && {
|
|
2700
3033
|
sliderProps: {
|
|
2701
|
-
className: props.className
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
step: props.step || 1
|
|
3034
|
+
className: props.className,
|
|
3035
|
+
maxValue: props.max ?? 100,
|
|
3036
|
+
minValue: props.min ?? 0,
|
|
3037
|
+
step: props.step ?? 1
|
|
2706
3038
|
}
|
|
2707
3039
|
}
|
|
2708
3040
|
};
|
|
@@ -2714,9 +3046,8 @@ function dateField(name, label, props) {
|
|
|
2714
3046
|
type: "date",
|
|
2715
3047
|
...props && {
|
|
2716
3048
|
dateProps: {
|
|
2717
|
-
className: props.className
|
|
2718
|
-
|
|
2719
|
-
placeholder: props.placeholder || ""
|
|
3049
|
+
className: props.className,
|
|
3050
|
+
placeholder: props.placeholder
|
|
2720
3051
|
}
|
|
2721
3052
|
}
|
|
2722
3053
|
};
|
|
@@ -2738,15 +3069,12 @@ function fileField(name, label, props) {
|
|
|
2738
3069
|
}
|
|
2739
3070
|
function fontPickerField(name, label, props) {
|
|
2740
3071
|
return {
|
|
3072
|
+
className: props?.className,
|
|
3073
|
+
description: props?.description,
|
|
3074
|
+
fontPickerProps: props?.fontPickerProps,
|
|
2741
3075
|
label,
|
|
2742
3076
|
name,
|
|
2743
|
-
type: "fontPicker"
|
|
2744
|
-
...props && {
|
|
2745
|
-
fontPickerProps: {
|
|
2746
|
-
className: props.className || "",
|
|
2747
|
-
disabled: props.isDisabled || false
|
|
2748
|
-
}
|
|
2749
|
-
}
|
|
3077
|
+
type: "fontPicker"
|
|
2750
3078
|
};
|
|
2751
3079
|
}
|
|
2752
3080
|
function contentField(title, description, options) {
|
|
@@ -3068,7 +3396,12 @@ var TypeInferredBuilder = class {
|
|
|
3068
3396
|
this.formFields.push({
|
|
3069
3397
|
label,
|
|
3070
3398
|
name,
|
|
3071
|
-
sliderProps: {
|
|
3399
|
+
sliderProps: {
|
|
3400
|
+
maxValue: max,
|
|
3401
|
+
minValue: min,
|
|
3402
|
+
step,
|
|
3403
|
+
...fieldOptions
|
|
3404
|
+
},
|
|
3072
3405
|
type: "slider"
|
|
3073
3406
|
});
|
|
3074
3407
|
return this;
|
|
@@ -3533,6 +3866,131 @@ function useMemoizedFieldProps(props, deps) {
|
|
|
3533
3866
|
return useMemo2(() => props, deps);
|
|
3534
3867
|
}
|
|
3535
3868
|
|
|
3869
|
+
// src/utils/arraySync.ts
|
|
3870
|
+
function syncArrays(options) {
|
|
3871
|
+
const { current, existing, getId } = options;
|
|
3872
|
+
const existingMap = /* @__PURE__ */ new Map();
|
|
3873
|
+
const currentMap = /* @__PURE__ */ new Map();
|
|
3874
|
+
existing.forEach((item) => {
|
|
3875
|
+
const id = getId(item);
|
|
3876
|
+
if (id !== void 0) {
|
|
3877
|
+
existingMap.set(id, item);
|
|
3878
|
+
}
|
|
3879
|
+
});
|
|
3880
|
+
current.forEach((item) => {
|
|
3881
|
+
const id = getId(item);
|
|
3882
|
+
if (id !== void 0) {
|
|
3883
|
+
currentMap.set(id, item);
|
|
3884
|
+
}
|
|
3885
|
+
});
|
|
3886
|
+
const toDelete = [];
|
|
3887
|
+
existingMap.forEach((item, id) => {
|
|
3888
|
+
if (!currentMap.has(id)) {
|
|
3889
|
+
toDelete.push(item);
|
|
3890
|
+
}
|
|
3891
|
+
});
|
|
3892
|
+
const toUpdate = [];
|
|
3893
|
+
existingMap.forEach((existingItem, id) => {
|
|
3894
|
+
const currentItem = currentMap.get(id);
|
|
3895
|
+
if (currentItem) {
|
|
3896
|
+
toUpdate.push({ current: currentItem, existing: existingItem });
|
|
3897
|
+
}
|
|
3898
|
+
});
|
|
3899
|
+
const toCreate = [];
|
|
3900
|
+
currentMap.forEach((item, id) => {
|
|
3901
|
+
if (!existingMap.has(id)) {
|
|
3902
|
+
toCreate.push(item);
|
|
3903
|
+
}
|
|
3904
|
+
});
|
|
3905
|
+
return {
|
|
3906
|
+
toCreate,
|
|
3907
|
+
toDelete,
|
|
3908
|
+
toUpdate
|
|
3909
|
+
};
|
|
3910
|
+
}
|
|
3911
|
+
|
|
3912
|
+
// src/utils/createFieldArrayCustomConfig.tsx
|
|
3913
|
+
import React25 from "react";
|
|
3914
|
+
import { useFieldArray as useFieldArray2 } from "react-hook-form";
|
|
3915
|
+
import { Button as Button6 } from "@heroui/react";
|
|
3916
|
+
function createFieldArrayCustomConfig(options) {
|
|
3917
|
+
const {
|
|
3918
|
+
className,
|
|
3919
|
+
defaultItem,
|
|
3920
|
+
enableReordering = false,
|
|
3921
|
+
label,
|
|
3922
|
+
max = 10,
|
|
3923
|
+
min = 0,
|
|
3924
|
+
name,
|
|
3925
|
+
renderAddButton,
|
|
3926
|
+
renderItem
|
|
3927
|
+
} = options;
|
|
3928
|
+
return {
|
|
3929
|
+
className,
|
|
3930
|
+
label,
|
|
3931
|
+
name,
|
|
3932
|
+
// ArrayPath is compatible with Path for CustomFieldConfig
|
|
3933
|
+
render: ({ control, errors, form }) => {
|
|
3934
|
+
const { append, fields, move, remove } = useFieldArray2({
|
|
3935
|
+
control,
|
|
3936
|
+
name
|
|
3937
|
+
});
|
|
3938
|
+
const canAdd = fields.length < max;
|
|
3939
|
+
const canRemove = fields.length > min;
|
|
3940
|
+
const handleAdd = () => {
|
|
3941
|
+
if (canAdd) {
|
|
3942
|
+
if (defaultItem) {
|
|
3943
|
+
append(defaultItem());
|
|
3944
|
+
} else {
|
|
3945
|
+
append({});
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
};
|
|
3949
|
+
const handleRemove = (index) => {
|
|
3950
|
+
if (canRemove) {
|
|
3951
|
+
remove(index);
|
|
3952
|
+
}
|
|
3953
|
+
};
|
|
3954
|
+
const handleMoveUp = (index) => {
|
|
3955
|
+
if (enableReordering && index > 0) {
|
|
3956
|
+
move(index, index - 1);
|
|
3957
|
+
}
|
|
3958
|
+
};
|
|
3959
|
+
const handleMoveDown = (index) => {
|
|
3960
|
+
if (enableReordering && index < fields.length - 1) {
|
|
3961
|
+
move(index, index + 1);
|
|
3962
|
+
}
|
|
3963
|
+
};
|
|
3964
|
+
return /* @__PURE__ */ React25.createElement("div", { className }, /* @__PURE__ */ React25.createElement("div", { className: "space-y-4" }, fields.map((field2, index) => {
|
|
3965
|
+
const canMoveUp = enableReordering && index > 0;
|
|
3966
|
+
const canMoveDown = enableReordering && index < fields.length - 1;
|
|
3967
|
+
return /* @__PURE__ */ React25.createElement(React25.Fragment, { key: field2.id }, renderItem({
|
|
3968
|
+
canMoveDown,
|
|
3969
|
+
canMoveUp,
|
|
3970
|
+
control,
|
|
3971
|
+
errors,
|
|
3972
|
+
field: field2,
|
|
3973
|
+
fields,
|
|
3974
|
+
form,
|
|
3975
|
+
index,
|
|
3976
|
+
onMoveDown: () => handleMoveDown(index),
|
|
3977
|
+
onMoveUp: () => handleMoveUp(index),
|
|
3978
|
+
onRemove: () => handleRemove(index)
|
|
3979
|
+
}));
|
|
3980
|
+
}), 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(
|
|
3981
|
+
Button6,
|
|
3982
|
+
{
|
|
3983
|
+
variant: "bordered",
|
|
3984
|
+
onPress: handleAdd,
|
|
3985
|
+
className: "w-full"
|
|
3986
|
+
},
|
|
3987
|
+
"Add Item"
|
|
3988
|
+
)));
|
|
3989
|
+
},
|
|
3990
|
+
type: "custom"
|
|
3991
|
+
};
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3536
3994
|
// src/builders/validation-helpers.ts
|
|
3537
3995
|
import { z as z4 } from "zod";
|
|
3538
3996
|
var validationPatterns = {
|
|
@@ -3706,6 +4164,7 @@ export {
|
|
|
3706
4164
|
RadioGroupField,
|
|
3707
4165
|
SelectField,
|
|
3708
4166
|
ServerActionForm,
|
|
4167
|
+
SimpleForm,
|
|
3709
4168
|
SliderField,
|
|
3710
4169
|
SubmitButton,
|
|
3711
4170
|
SwitchField,
|
|
@@ -3721,6 +4180,7 @@ export {
|
|
|
3721
4180
|
createEmailSchema,
|
|
3722
4181
|
createField,
|
|
3723
4182
|
createFieldArrayBuilder,
|
|
4183
|
+
createFieldArrayCustomConfig,
|
|
3724
4184
|
createFieldArrayItemBuilder,
|
|
3725
4185
|
createFileSchema,
|
|
3726
4186
|
createFormTestUtils,
|
|
@@ -3754,6 +4214,7 @@ export {
|
|
|
3754
4214
|
shallowEqual,
|
|
3755
4215
|
simulateFieldInput,
|
|
3756
4216
|
simulateFormSubmission,
|
|
4217
|
+
syncArrays,
|
|
3757
4218
|
throttle,
|
|
3758
4219
|
useDebouncedFieldValidation,
|
|
3759
4220
|
useDebouncedValidation,
|