@rachelallyson/hero-hook-form 2.6.0 → 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 +35 -0
- package/dist/index.d.ts +374 -25
- package/dist/index.js +313 -67
- package/dist/react/index.d.ts +374 -25
- package/dist/react/index.js +313 -67
- 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() {
|
|
@@ -3742,6 +3860,131 @@ function useMemoizedFieldProps(props, deps) {
|
|
|
3742
3860
|
return useMemo2(() => props, deps);
|
|
3743
3861
|
}
|
|
3744
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
|
+
|
|
3745
3988
|
// src/builders/validation-helpers.ts
|
|
3746
3989
|
import { z as z4 } from "zod";
|
|
3747
3990
|
var validationPatterns = {
|
|
@@ -3915,6 +4158,7 @@ export {
|
|
|
3915
4158
|
RadioGroupField,
|
|
3916
4159
|
SelectField,
|
|
3917
4160
|
ServerActionForm,
|
|
4161
|
+
SimpleForm,
|
|
3918
4162
|
SliderField,
|
|
3919
4163
|
SubmitButton,
|
|
3920
4164
|
SwitchField,
|
|
@@ -3930,6 +4174,7 @@ export {
|
|
|
3930
4174
|
createEmailSchema,
|
|
3931
4175
|
createField,
|
|
3932
4176
|
createFieldArrayBuilder,
|
|
4177
|
+
createFieldArrayCustomConfig,
|
|
3933
4178
|
createFieldArrayItemBuilder,
|
|
3934
4179
|
createFileSchema,
|
|
3935
4180
|
createFormTestUtils,
|
|
@@ -3963,6 +4208,7 @@ export {
|
|
|
3963
4208
|
shallowEqual,
|
|
3964
4209
|
simulateFieldInput,
|
|
3965
4210
|
simulateFormSubmission,
|
|
4211
|
+
syncArrays,
|
|
3966
4212
|
throttle,
|
|
3967
4213
|
useDebouncedFieldValidation,
|
|
3968
4214
|
useDebouncedValidation,
|