@mehdashti/forms 0.3.1 → 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 +224 -1
- package/dist/index.js +467 -7
- 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([]);
|
|
@@ -655,6 +655,425 @@ function FormMultiSelect({
|
|
|
655
655
|
}
|
|
656
656
|
);
|
|
657
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
|
+
}
|
|
658
1077
|
var spacingClasses = {
|
|
659
1078
|
sm: "gap-3",
|
|
660
1079
|
md: "gap-4",
|
|
@@ -766,9 +1185,9 @@ function FormWizard({
|
|
|
766
1185
|
className,
|
|
767
1186
|
showStepNumbers = true
|
|
768
1187
|
}) {
|
|
769
|
-
const [currentStep, setCurrentStep] =
|
|
770
|
-
const [isValidating, setIsValidating] =
|
|
771
|
-
const [isCompleting, setIsCompleting] =
|
|
1188
|
+
const [currentStep, setCurrentStep] = React4.useState(0);
|
|
1189
|
+
const [isValidating, setIsValidating] = React4.useState(false);
|
|
1190
|
+
const [isCompleting, setIsCompleting] = React4.useState(false);
|
|
772
1191
|
const isFirstStep = currentStep === 0;
|
|
773
1192
|
const isLastStep = currentStep === steps.length - 1;
|
|
774
1193
|
const handleNext = async () => {
|
|
@@ -928,6 +1347,47 @@ function useSmartFieldArray({
|
|
|
928
1347
|
append
|
|
929
1348
|
};
|
|
930
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
|
+
}
|
|
931
1391
|
var emailSchema = z.string().min(1, "Email is required").email("Invalid email address");
|
|
932
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");
|
|
933
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");
|
|
@@ -1098,6 +1558,6 @@ function formatValidationError(error) {
|
|
|
1098
1558
|
return formatted;
|
|
1099
1559
|
}
|
|
1100
1560
|
|
|
1101
|
-
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 };
|
|
1102
1562
|
//# sourceMappingURL=index.js.map
|
|
1103
1563
|
//# sourceMappingURL=index.js.map
|