@mehdashti/forms 0.3.0 → 0.4.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/dist/index.d.ts +857 -0
- package/dist/index.js +469 -10
- package/dist/index.js.map +1 -1
- package/package.json +9 -3
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { useForm, Controller, useFieldArray } from 'react-hook-form';
|
|
1
|
+
import { useForm, Controller, useFormContext, useFieldArray } from 'react-hook-form';
|
|
2
2
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
3
3
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
|
-
import * as
|
|
4
|
+
import * as React4 from 'react';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
export { z } from 'zod';
|
|
7
7
|
|
|
@@ -273,7 +273,7 @@ function FormFileUpload({
|
|
|
273
273
|
showPreview = true,
|
|
274
274
|
onFileChange
|
|
275
275
|
}) {
|
|
276
|
-
const [previews, setPreviews] =
|
|
276
|
+
const [previews, setPreviews] = React4.useState([]);
|
|
277
277
|
const handleFileChange = (files, onChange) => {
|
|
278
278
|
if (!files || files.length === 0) {
|
|
279
279
|
setPreviews([]);
|
|
@@ -455,7 +455,6 @@ function FormDatePicker({
|
|
|
455
455
|
{
|
|
456
456
|
name,
|
|
457
457
|
label,
|
|
458
|
-
placeholder,
|
|
459
458
|
required,
|
|
460
459
|
helpText,
|
|
461
460
|
disabled,
|
|
@@ -603,7 +602,7 @@ function FormMultiSelect({
|
|
|
603
602
|
field.onChange(newValues);
|
|
604
603
|
};
|
|
605
604
|
const isSelected = (optionValue) => selectedValues.includes(optionValue);
|
|
606
|
-
const isMaxReached = maxSelection && selectedValues.length >= maxSelection;
|
|
605
|
+
const isMaxReached = maxSelection !== void 0 && selectedValues.length >= maxSelection;
|
|
607
606
|
return /* @__PURE__ */ jsxs("div", { className: `space-y-3 ${className || ""}`, children: [
|
|
608
607
|
/* @__PURE__ */ jsxs("label", { className: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", children: [
|
|
609
608
|
label,
|
|
@@ -656,6 +655,425 @@ function FormMultiSelect({
|
|
|
656
655
|
}
|
|
657
656
|
);
|
|
658
657
|
}
|
|
658
|
+
function FormRadioGroup({
|
|
659
|
+
name,
|
|
660
|
+
label,
|
|
661
|
+
options,
|
|
662
|
+
orientation = "vertical",
|
|
663
|
+
helperText,
|
|
664
|
+
required,
|
|
665
|
+
disabled,
|
|
666
|
+
className
|
|
667
|
+
}) {
|
|
668
|
+
const {
|
|
669
|
+
register,
|
|
670
|
+
formState: { errors }
|
|
671
|
+
} = useFormContext();
|
|
672
|
+
const error = errors[name]?.message;
|
|
673
|
+
const fieldId = `field-${name}`;
|
|
674
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
675
|
+
label && /* @__PURE__ */ jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxs("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: [
|
|
676
|
+
label,
|
|
677
|
+
required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-1", children: "*" })
|
|
678
|
+
] }) }),
|
|
679
|
+
/* @__PURE__ */ jsx(
|
|
680
|
+
"div",
|
|
681
|
+
{
|
|
682
|
+
role: "radiogroup",
|
|
683
|
+
"aria-labelledby": label ? fieldId : void 0,
|
|
684
|
+
className: orientation === "horizontal" ? "flex flex-wrap gap-4" : "space-y-3",
|
|
685
|
+
children: options.map((option) => {
|
|
686
|
+
const optionId = `${fieldId}-${option.value}`;
|
|
687
|
+
const isDisabled = disabled || option.disabled;
|
|
688
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-start", children: [
|
|
689
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center h-5", children: /* @__PURE__ */ jsx(
|
|
690
|
+
"input",
|
|
691
|
+
{
|
|
692
|
+
...register(name),
|
|
693
|
+
type: "radio",
|
|
694
|
+
id: optionId,
|
|
695
|
+
value: option.value,
|
|
696
|
+
disabled: isDisabled,
|
|
697
|
+
className: `
|
|
698
|
+
h-4 w-4 border-gray-300 text-blue-600
|
|
699
|
+
focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
|
|
700
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
701
|
+
dark:border-gray-600 dark:bg-gray-800 dark:focus:ring-offset-gray-900
|
|
702
|
+
`
|
|
703
|
+
}
|
|
704
|
+
) }),
|
|
705
|
+
/* @__PURE__ */ jsxs("div", { className: "ml-3", children: [
|
|
706
|
+
/* @__PURE__ */ jsx(
|
|
707
|
+
"label",
|
|
708
|
+
{
|
|
709
|
+
htmlFor: optionId,
|
|
710
|
+
className: `
|
|
711
|
+
text-sm font-medium
|
|
712
|
+
${isDisabled ? "text-gray-400 cursor-not-allowed" : "text-gray-700 dark:text-gray-300 cursor-pointer"}
|
|
713
|
+
`,
|
|
714
|
+
children: option.label
|
|
715
|
+
}
|
|
716
|
+
),
|
|
717
|
+
option.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: option.description })
|
|
718
|
+
] })
|
|
719
|
+
] }, option.value);
|
|
720
|
+
})
|
|
721
|
+
}
|
|
722
|
+
),
|
|
723
|
+
helperText && !error && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-500 dark:text-gray-400", children: helperText }),
|
|
724
|
+
error && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-red-600 dark:text-red-400", role: "alert", children: error })
|
|
725
|
+
] });
|
|
726
|
+
}
|
|
727
|
+
function FormSwitch({
|
|
728
|
+
name,
|
|
729
|
+
label,
|
|
730
|
+
description,
|
|
731
|
+
helperText,
|
|
732
|
+
required,
|
|
733
|
+
disabled,
|
|
734
|
+
className,
|
|
735
|
+
onChange
|
|
736
|
+
}) {
|
|
737
|
+
const {
|
|
738
|
+
register,
|
|
739
|
+
watch,
|
|
740
|
+
setValue,
|
|
741
|
+
formState: { errors }
|
|
742
|
+
} = useFormContext();
|
|
743
|
+
const value = watch(name);
|
|
744
|
+
const checked = Boolean(value);
|
|
745
|
+
const error = errors[name]?.message;
|
|
746
|
+
const fieldId = `field-${name}`;
|
|
747
|
+
const handleChange = (e) => {
|
|
748
|
+
const newValue = e.target.checked;
|
|
749
|
+
setValue(name, newValue, { shouldValidate: true, shouldDirty: true });
|
|
750
|
+
onChange?.(newValue);
|
|
751
|
+
};
|
|
752
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
753
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start", children: [
|
|
754
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center h-5", children: [
|
|
755
|
+
/* @__PURE__ */ jsxs(
|
|
756
|
+
"button",
|
|
757
|
+
{
|
|
758
|
+
type: "button",
|
|
759
|
+
role: "switch",
|
|
760
|
+
"aria-checked": checked,
|
|
761
|
+
"aria-labelledby": label ? `${fieldId}-label` : void 0,
|
|
762
|
+
"aria-describedby": description ? `${fieldId}-description` : void 0,
|
|
763
|
+
disabled,
|
|
764
|
+
onClick: () => {
|
|
765
|
+
if (!disabled) {
|
|
766
|
+
const newValue = !checked;
|
|
767
|
+
setValue(name, newValue, { shouldValidate: true, shouldDirty: true });
|
|
768
|
+
onChange?.(newValue);
|
|
769
|
+
}
|
|
770
|
+
},
|
|
771
|
+
className: `
|
|
772
|
+
relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent
|
|
773
|
+
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
|
|
774
|
+
disabled:opacity-50 disabled:cursor-not-allowed
|
|
775
|
+
dark:focus:ring-offset-gray-900
|
|
776
|
+
${checked ? "bg-blue-600" : "bg-gray-200 dark:bg-gray-700"}
|
|
777
|
+
`,
|
|
778
|
+
children: [
|
|
779
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
|
|
780
|
+
/* @__PURE__ */ jsx(
|
|
781
|
+
"span",
|
|
782
|
+
{
|
|
783
|
+
className: `
|
|
784
|
+
pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0
|
|
785
|
+
transition duration-200 ease-in-out
|
|
786
|
+
${checked ? "translate-x-5" : "translate-x-0"}
|
|
787
|
+
`
|
|
788
|
+
}
|
|
789
|
+
)
|
|
790
|
+
]
|
|
791
|
+
}
|
|
792
|
+
),
|
|
793
|
+
/* @__PURE__ */ jsx(
|
|
794
|
+
"input",
|
|
795
|
+
{
|
|
796
|
+
...register(name),
|
|
797
|
+
type: "checkbox",
|
|
798
|
+
id: fieldId,
|
|
799
|
+
checked,
|
|
800
|
+
onChange: handleChange,
|
|
801
|
+
className: "sr-only",
|
|
802
|
+
disabled
|
|
803
|
+
}
|
|
804
|
+
)
|
|
805
|
+
] }),
|
|
806
|
+
(label || description) && /* @__PURE__ */ jsxs("div", { className: "ml-3", children: [
|
|
807
|
+
label && /* @__PURE__ */ jsxs(
|
|
808
|
+
"label",
|
|
809
|
+
{
|
|
810
|
+
id: `${fieldId}-label`,
|
|
811
|
+
htmlFor: fieldId,
|
|
812
|
+
className: `
|
|
813
|
+
text-sm font-medium
|
|
814
|
+
${disabled ? "text-gray-400" : "text-gray-700 dark:text-gray-300"}
|
|
815
|
+
`,
|
|
816
|
+
children: [
|
|
817
|
+
label,
|
|
818
|
+
required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-1", children: "*" })
|
|
819
|
+
]
|
|
820
|
+
}
|
|
821
|
+
),
|
|
822
|
+
description && /* @__PURE__ */ jsx(
|
|
823
|
+
"p",
|
|
824
|
+
{
|
|
825
|
+
id: `${fieldId}-description`,
|
|
826
|
+
className: "text-xs text-gray-500 dark:text-gray-400",
|
|
827
|
+
children: description
|
|
828
|
+
}
|
|
829
|
+
)
|
|
830
|
+
] })
|
|
831
|
+
] }),
|
|
832
|
+
helperText && !error && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-500 dark:text-gray-400", children: helperText }),
|
|
833
|
+
error && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-red-600 dark:text-red-400", role: "alert", children: error })
|
|
834
|
+
] });
|
|
835
|
+
}
|
|
836
|
+
function FormTagInput({
|
|
837
|
+
name,
|
|
838
|
+
label,
|
|
839
|
+
placeholder = "Type and press Enter...",
|
|
840
|
+
helperText,
|
|
841
|
+
required,
|
|
842
|
+
disabled,
|
|
843
|
+
maxTags,
|
|
844
|
+
separators = ["Enter", ","],
|
|
845
|
+
validateTag,
|
|
846
|
+
className
|
|
847
|
+
}) {
|
|
848
|
+
const {
|
|
849
|
+
watch,
|
|
850
|
+
setValue,
|
|
851
|
+
formState: { errors }
|
|
852
|
+
} = useFormContext();
|
|
853
|
+
const [inputValue, setInputValue] = React4.useState("");
|
|
854
|
+
const [tagError, setTagError] = React4.useState("");
|
|
855
|
+
const tags = watch(name) || [];
|
|
856
|
+
const error = errors[name]?.message;
|
|
857
|
+
const fieldId = `field-${name}`;
|
|
858
|
+
const addTag = (tag) => {
|
|
859
|
+
const trimmed = tag.trim();
|
|
860
|
+
if (!trimmed) return;
|
|
861
|
+
if (tags.includes(trimmed)) {
|
|
862
|
+
setTagError("Tag already exists");
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
if (maxTags && tags.length >= maxTags) {
|
|
866
|
+
setTagError(`Maximum ${maxTags} tags allowed`);
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
if (validateTag) {
|
|
870
|
+
const validation = validateTag(trimmed);
|
|
871
|
+
if (validation === false) {
|
|
872
|
+
setTagError("Invalid tag");
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
if (typeof validation === "string") {
|
|
876
|
+
setTagError(validation);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
setValue(name, [...tags, trimmed], {
|
|
881
|
+
shouldValidate: true,
|
|
882
|
+
shouldDirty: true
|
|
883
|
+
});
|
|
884
|
+
setInputValue("");
|
|
885
|
+
setTagError("");
|
|
886
|
+
};
|
|
887
|
+
const removeTag = (index) => {
|
|
888
|
+
const newTags = tags.filter((_, i) => i !== index);
|
|
889
|
+
setValue(name, newTags, {
|
|
890
|
+
shouldValidate: true,
|
|
891
|
+
shouldDirty: true
|
|
892
|
+
});
|
|
893
|
+
};
|
|
894
|
+
const handleKeyDown = (e) => {
|
|
895
|
+
if (separators.includes(e.key)) {
|
|
896
|
+
e.preventDefault();
|
|
897
|
+
addTag(inputValue);
|
|
898
|
+
} else if (e.key === "Backspace" && !inputValue && tags.length > 0) {
|
|
899
|
+
removeTag(tags.length - 1);
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
const handleBlur = () => {
|
|
903
|
+
if (inputValue.trim()) {
|
|
904
|
+
addTag(inputValue);
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
908
|
+
label && /* @__PURE__ */ jsxs(
|
|
909
|
+
"label",
|
|
910
|
+
{
|
|
911
|
+
htmlFor: fieldId,
|
|
912
|
+
className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1",
|
|
913
|
+
children: [
|
|
914
|
+
label,
|
|
915
|
+
required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-1", children: "*" })
|
|
916
|
+
]
|
|
917
|
+
}
|
|
918
|
+
),
|
|
919
|
+
/* @__PURE__ */ jsxs(
|
|
920
|
+
"div",
|
|
921
|
+
{
|
|
922
|
+
className: `
|
|
923
|
+
flex flex-wrap gap-2 p-2 border rounded-md min-h-[42px]
|
|
924
|
+
${disabled ? "bg-gray-50 dark:bg-gray-900 cursor-not-allowed" : "bg-white dark:bg-gray-800"}
|
|
925
|
+
${error || tagError ? "border-red-300 dark:border-red-700" : "border-gray-300 dark:border-gray-600"}
|
|
926
|
+
focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500
|
|
927
|
+
dark:focus-within:ring-blue-400 dark:focus-within:border-blue-400
|
|
928
|
+
`,
|
|
929
|
+
children: [
|
|
930
|
+
tags.map((tag, index) => /* @__PURE__ */ jsxs(
|
|
931
|
+
"span",
|
|
932
|
+
{
|
|
933
|
+
className: "inline-flex items-center gap-1 px-2 py-1 text-sm bg-blue-100 text-blue-800 rounded dark:bg-blue-900 dark:text-blue-200",
|
|
934
|
+
children: [
|
|
935
|
+
tag,
|
|
936
|
+
!disabled && /* @__PURE__ */ jsx(
|
|
937
|
+
"button",
|
|
938
|
+
{
|
|
939
|
+
type: "button",
|
|
940
|
+
onClick: () => removeTag(index),
|
|
941
|
+
className: "ml-1 hover:text-blue-600 dark:hover:text-blue-300",
|
|
942
|
+
"aria-label": `Remove ${tag}`,
|
|
943
|
+
children: "\xD7"
|
|
944
|
+
}
|
|
945
|
+
)
|
|
946
|
+
]
|
|
947
|
+
},
|
|
948
|
+
index
|
|
949
|
+
)),
|
|
950
|
+
/* @__PURE__ */ jsx(
|
|
951
|
+
"input",
|
|
952
|
+
{
|
|
953
|
+
id: fieldId,
|
|
954
|
+
type: "text",
|
|
955
|
+
value: inputValue,
|
|
956
|
+
onChange: (e) => {
|
|
957
|
+
setInputValue(e.target.value);
|
|
958
|
+
setTagError("");
|
|
959
|
+
},
|
|
960
|
+
onKeyDown: handleKeyDown,
|
|
961
|
+
onBlur: handleBlur,
|
|
962
|
+
placeholder: tags.length === 0 ? placeholder : "",
|
|
963
|
+
disabled: disabled || maxTags !== void 0 && tags.length >= maxTags,
|
|
964
|
+
className: "flex-1 min-w-[120px] outline-none bg-transparent text-sm text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 disabled:cursor-not-allowed"
|
|
965
|
+
}
|
|
966
|
+
)
|
|
967
|
+
]
|
|
968
|
+
}
|
|
969
|
+
),
|
|
970
|
+
helperText && !error && !tagError && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500 dark:text-gray-400", children: helperText }),
|
|
971
|
+
(error || tagError) && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-red-600 dark:text-red-400", role: "alert", children: error || tagError }),
|
|
972
|
+
maxTags && /* @__PURE__ */ jsxs("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400", children: [
|
|
973
|
+
tags.length,
|
|
974
|
+
" / ",
|
|
975
|
+
maxTags,
|
|
976
|
+
" tags"
|
|
977
|
+
] })
|
|
978
|
+
] });
|
|
979
|
+
}
|
|
980
|
+
function FormErrorSummary({
|
|
981
|
+
title = "Please fix the following errors:",
|
|
982
|
+
fieldLabels = {},
|
|
983
|
+
show,
|
|
984
|
+
className,
|
|
985
|
+
onErrorClick
|
|
986
|
+
}) {
|
|
987
|
+
const {
|
|
988
|
+
formState: { errors }
|
|
989
|
+
} = useFormContext();
|
|
990
|
+
const errorList = React4.useMemo(() => {
|
|
991
|
+
return flattenErrors(errors, fieldLabels);
|
|
992
|
+
}, [errors, fieldLabels]);
|
|
993
|
+
const shouldShow = show !== void 0 ? show : errorList.length > 0;
|
|
994
|
+
if (!shouldShow) {
|
|
995
|
+
return null;
|
|
996
|
+
}
|
|
997
|
+
const handleErrorClick = (fieldName) => {
|
|
998
|
+
const field = document.getElementById(`field-${fieldName}`) || document.querySelector(`[name="${fieldName}"]`);
|
|
999
|
+
if (field) {
|
|
1000
|
+
field.focus();
|
|
1001
|
+
field.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
1002
|
+
}
|
|
1003
|
+
onErrorClick?.(fieldName);
|
|
1004
|
+
};
|
|
1005
|
+
return /* @__PURE__ */ jsx(
|
|
1006
|
+
"div",
|
|
1007
|
+
{
|
|
1008
|
+
role: "alert",
|
|
1009
|
+
"aria-live": "polite",
|
|
1010
|
+
className: `
|
|
1011
|
+
rounded-md bg-red-50 dark:bg-red-900/20 p-4 border border-red-200 dark:border-red-800
|
|
1012
|
+
${className}
|
|
1013
|
+
`,
|
|
1014
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
|
|
1015
|
+
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(
|
|
1016
|
+
"svg",
|
|
1017
|
+
{
|
|
1018
|
+
className: "h-5 w-5 text-red-400 dark:text-red-600",
|
|
1019
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1020
|
+
viewBox: "0 0 20 20",
|
|
1021
|
+
fill: "currentColor",
|
|
1022
|
+
"aria-hidden": "true",
|
|
1023
|
+
children: /* @__PURE__ */ jsx(
|
|
1024
|
+
"path",
|
|
1025
|
+
{
|
|
1026
|
+
fillRule: "evenodd",
|
|
1027
|
+
d: "M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z",
|
|
1028
|
+
clipRule: "evenodd"
|
|
1029
|
+
}
|
|
1030
|
+
)
|
|
1031
|
+
}
|
|
1032
|
+
) }),
|
|
1033
|
+
/* @__PURE__ */ jsxs("div", { className: "ml-3 flex-1", children: [
|
|
1034
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-red-800 dark:text-red-300", children: title }),
|
|
1035
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 text-sm text-red-700 dark:text-red-400", children: /* @__PURE__ */ jsx("ul", { className: "list-disc space-y-1 pl-5", children: errorList.map(({ fieldName, fieldLabel, message }) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
1036
|
+
"button",
|
|
1037
|
+
{
|
|
1038
|
+
type: "button",
|
|
1039
|
+
onClick: () => handleErrorClick(fieldName),
|
|
1040
|
+
className: "hover:underline focus:outline-none focus:underline text-left",
|
|
1041
|
+
children: [
|
|
1042
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
1043
|
+
fieldLabel,
|
|
1044
|
+
":"
|
|
1045
|
+
] }),
|
|
1046
|
+
" ",
|
|
1047
|
+
message
|
|
1048
|
+
]
|
|
1049
|
+
}
|
|
1050
|
+
) }, fieldName)) }) })
|
|
1051
|
+
] })
|
|
1052
|
+
] })
|
|
1053
|
+
}
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
function flattenErrors(errors, fieldLabels, prefix = "") {
|
|
1057
|
+
const result = [];
|
|
1058
|
+
for (const [key, value] of Object.entries(errors)) {
|
|
1059
|
+
const fieldName = prefix ? `${prefix}.${key}` : key;
|
|
1060
|
+
if (value && typeof value === "object") {
|
|
1061
|
+
if ("message" in value && typeof value.message === "string") {
|
|
1062
|
+
result.push({
|
|
1063
|
+
fieldName,
|
|
1064
|
+
fieldLabel: fieldLabels[fieldName] || formatFieldName(fieldName),
|
|
1065
|
+
message: value.message
|
|
1066
|
+
});
|
|
1067
|
+
} else {
|
|
1068
|
+
result.push(...flattenErrors(value, fieldLabels, fieldName));
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
return result;
|
|
1073
|
+
}
|
|
1074
|
+
function formatFieldName(name) {
|
|
1075
|
+
return name.split(".").pop().replace(/([A-Z])/g, " $1").replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase()).trim();
|
|
1076
|
+
}
|
|
659
1077
|
var spacingClasses = {
|
|
660
1078
|
sm: "gap-3",
|
|
661
1079
|
md: "gap-4",
|
|
@@ -767,9 +1185,9 @@ function FormWizard({
|
|
|
767
1185
|
className,
|
|
768
1186
|
showStepNumbers = true
|
|
769
1187
|
}) {
|
|
770
|
-
const [currentStep, setCurrentStep] =
|
|
771
|
-
const [isValidating, setIsValidating] =
|
|
772
|
-
const [isCompleting, setIsCompleting] =
|
|
1188
|
+
const [currentStep, setCurrentStep] = React4.useState(0);
|
|
1189
|
+
const [isValidating, setIsValidating] = React4.useState(false);
|
|
1190
|
+
const [isCompleting, setIsCompleting] = React4.useState(false);
|
|
773
1191
|
const isFirstStep = currentStep === 0;
|
|
774
1192
|
const isLastStep = currentStep === steps.length - 1;
|
|
775
1193
|
const handleNext = async () => {
|
|
@@ -929,6 +1347,47 @@ function useSmartFieldArray({
|
|
|
929
1347
|
append
|
|
930
1348
|
};
|
|
931
1349
|
}
|
|
1350
|
+
function useUnsavedChanges(options = {}) {
|
|
1351
|
+
const {
|
|
1352
|
+
message = "You have unsaved changes. Are you sure you want to leave?",
|
|
1353
|
+
enabled = true,
|
|
1354
|
+
onBeforeUnload
|
|
1355
|
+
} = options;
|
|
1356
|
+
const {
|
|
1357
|
+
formState: { isDirty, isSubmitting, isSubmitSuccessful }
|
|
1358
|
+
} = useFormContext();
|
|
1359
|
+
const hasUnsavedChanges = isDirty && !isSubmitting && !isSubmitSuccessful;
|
|
1360
|
+
React4.useEffect(() => {
|
|
1361
|
+
if (!enabled) return;
|
|
1362
|
+
const handleBeforeUnload = (event) => {
|
|
1363
|
+
if (hasUnsavedChanges) {
|
|
1364
|
+
event.preventDefault();
|
|
1365
|
+
event.returnValue = message;
|
|
1366
|
+
onBeforeUnload?.(hasUnsavedChanges);
|
|
1367
|
+
return message;
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
1371
|
+
return () => {
|
|
1372
|
+
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
1373
|
+
};
|
|
1374
|
+
}, [hasUnsavedChanges, message, enabled, onBeforeUnload]);
|
|
1375
|
+
return {
|
|
1376
|
+
hasUnsavedChanges,
|
|
1377
|
+
isDirty
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
function useUnsavedChangesBlocker() {
|
|
1381
|
+
const {
|
|
1382
|
+
formState: { isDirty, isSubmitting, isSubmitSuccessful }
|
|
1383
|
+
} = useFormContext();
|
|
1384
|
+
const hasUnsavedChanges = isDirty && !isSubmitting && !isSubmitSuccessful;
|
|
1385
|
+
return {
|
|
1386
|
+
hasUnsavedChanges,
|
|
1387
|
+
isDirty,
|
|
1388
|
+
shouldBlock: hasUnsavedChanges
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
932
1391
|
var emailSchema = z.string().min(1, "Email is required").email("Invalid email address");
|
|
933
1392
|
var passwordSchema = z.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");
|
|
934
1393
|
var strongPasswordSchema = z.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(/[^A-Za-z0-9]/, "Password must contain at least one special character");
|
|
@@ -1002,7 +1461,7 @@ function getAllErrors(errors) {
|
|
|
1002
1461
|
});
|
|
1003
1462
|
return messages;
|
|
1004
1463
|
}
|
|
1005
|
-
function conditionalSchema(
|
|
1464
|
+
function conditionalSchema(_dependentField, _condition, schema) {
|
|
1006
1465
|
return z.any().superRefine((value, ctx) => {
|
|
1007
1466
|
const parent = ctx.path[ctx.path.length - 2];
|
|
1008
1467
|
if (!parent) return;
|
|
@@ -1099,6 +1558,6 @@ function formatValidationError(error) {
|
|
|
1099
1558
|
return formatted;
|
|
1100
1559
|
}
|
|
1101
1560
|
|
|
1102
|
-
export { FormCheckbox, FormDatePicker, FormField, FormFileUpload, FormGrid, FormGridItem, FormLayout, FormMultiSelect, FormSection, FormSelect, FormTextarea, FormWizard, conditionalSchema, createConfirmPasswordSchema, createFileSizeSchema, createFileTypeSchema, dateStringSchema, emailSchema, formatValidationError, futureDateSchema, getAllErrors, getErrorMessage, hasError, imageFileSchema, invalidFormatMessage, lengthMessage, optionalEmailSchema, optionalUrlSchema, passwordSchema, pastDateSchema, percentageSchema, phoneSchema, positiveIntegerSchema, positiveNumberSchema, priceSchema, requiredMessage, sanitizeString, slugSchema, strongPasswordSchema, urlSchema, useSmartFieldArray, useSmartForm, usernameSchema, validateCreditCard, validateFileExtension, validateIBAN, validatePasswordStrength };
|
|
1561
|
+
export { FormCheckbox, FormDatePicker, FormErrorSummary, FormField, FormFileUpload, FormGrid, FormGridItem, FormLayout, FormMultiSelect, FormRadioGroup, FormSection, FormSelect, FormSwitch, FormTagInput, FormTextarea, FormWizard, conditionalSchema, createConfirmPasswordSchema, createFileSizeSchema, createFileTypeSchema, dateStringSchema, emailSchema, formatValidationError, futureDateSchema, getAllErrors, getErrorMessage, hasError, imageFileSchema, invalidFormatMessage, lengthMessage, optionalEmailSchema, optionalUrlSchema, passwordSchema, pastDateSchema, percentageSchema, phoneSchema, positiveIntegerSchema, positiveNumberSchema, priceSchema, requiredMessage, sanitizeString, slugSchema, strongPasswordSchema, urlSchema, useSmartFieldArray, useSmartForm, useUnsavedChanges, useUnsavedChangesBlocker, usernameSchema, validateCreditCard, validateFileExtension, validateIBAN, validatePasswordStrength };
|
|
1103
1562
|
//# sourceMappingURL=index.js.map
|
|
1104
1563
|
//# sourceMappingURL=index.js.map
|