react-form-manage 1.0.6-beta.0 → 1.0.6-beta.1
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 +44 -0
- package/dist/components/Form/FormCleanUp.js +46 -0
- package/dist/components/Form/FormItem.js +46 -0
- package/dist/components/Form/FormList.js +11 -0
- package/dist/components/Form/InputWrapper.js +7 -0
- package/dist/components/Input.d.ts +4 -5
- package/dist/components/Input.js +8 -0
- package/dist/constants/validation.js +33 -0
- package/dist/contexts/FormContext.js +2 -0
- package/dist/hooks/useFormItemControl.js +390 -0
- package/dist/hooks/useFormListControl.js +280 -0
- package/dist/index.d.ts +8 -185
- package/dist/index.js +9 -0
- package/dist/providers/Form.js +322 -0
- package/dist/stores/formStore.js +304 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/public.d.ts +51 -0
- package/dist/utils/obj.util.js +24 -0
- package/package.json +30 -45
- package/src/App.css +49 -0
- package/src/App.tsx +36 -0
- package/src/assets/react.svg +1 -0
- package/src/components/Form/FormCleanUp.tsx +57 -0
- package/src/components/Form/FormItem.tsx +60 -0
- package/src/components/Form/FormList.tsx +13 -0
- package/src/components/Form/InputWrapper.tsx +17 -0
- package/src/components/Input.jsx +13 -0
- package/src/components/Input.tsx +21 -0
- package/src/constants/validation.ts +63 -0
- package/src/contexts/FormContext.ts +3 -0
- package/src/hooks/useFormItemControl.ts +558 -0
- package/src/hooks/useFormListControl.ts +378 -0
- package/src/index.css +68 -0
- package/src/index.ts +30 -0
- package/src/main.tsx +10 -0
- package/src/providers/Form.tsx +429 -0
- package/src/stores/formStore.ts +477 -0
- package/src/types/index.ts +1 -0
- package/src/types/public.ts +50 -0
- package/src/utils/obj.util.ts +42 -0
- package/dist/App.d.ts +0 -2
- package/dist/index.cjs +0 -2
- package/dist/index.cjs.d.ts +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.esm.d.ts +0 -1
- package/dist/index.esm.js +0 -2
- package/dist/index.esm.js.map +0 -1
- package/src/types/components.d.ts +0 -135
- /package/dist/{main.d.ts → types/public.js} +0 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [1.0.6-beta.1] - 2026-01-22
|
|
6
|
+
|
|
7
|
+
- Full TypeScript migration: Convert all source files from JS to TS
|
|
8
|
+
- Replace JS entrypoints (main.jsx, Input.jsx) with TypeScript versions
|
|
9
|
+
- Add proper type declarations generated from source
|
|
10
|
+
- Update build to emit both JS and .d.ts files
|
|
11
|
+
- Add @types/node for Node.js type support
|
|
12
|
+
- Remove legacy copy-types scripts
|
|
13
|
+
- Add antd, react, react-dom to devDependencies for local development
|
|
14
|
+
|
|
15
|
+
## [1.0.1] - 2026-01-21
|
|
16
|
+
|
|
17
|
+
- Publish patch release v1.0.1
|
|
18
|
+
- Minor packaging and release metadata updates
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## [1.0.0] - initial publish
|
|
23
|
+
|
|
24
|
+
- Initial public release `react-form-manage@1.0.0`.
|
|
25
|
+
|
|
26
|
+
## [1.0.2] - 2026-01-21
|
|
27
|
+
|
|
28
|
+
- Add distributed TypeScript declaration `dist/index.d.ts` and update `package.json` `types` field.
|
|
29
|
+
|
|
30
|
+
## [1.0.3] - 2026-01-21
|
|
31
|
+
|
|
32
|
+
- Provide `dist/index.esm.d.ts` and `dist/index.cjs.d.ts` re-exports so Node-style module resolution finds typings correctly.
|
|
33
|
+
|
|
34
|
+
## [1.0.4] - 2026-01-21
|
|
35
|
+
|
|
36
|
+
- Add `build:types` script and `scripts/copy-types.js` to automatically copy `src/types/components.d.ts` into `dist/` during publish builds.
|
|
37
|
+
|
|
38
|
+
## [1.0.5] - 2026-01-21
|
|
39
|
+
|
|
40
|
+
- Export `Form` as a named export in addition to default to improve import ergonomics for consumers.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { useShallow } from "zustand/react/shallow";
|
|
4
|
+
import { useFormCleanUp, useFormListeners, useFormStore, } from "../../stores/formStore";
|
|
5
|
+
const FormCleanUp = () => {
|
|
6
|
+
const { cleanUpStack, clearCleanUpStack } = useFormCleanUp(useShallow((state) => ({
|
|
7
|
+
cleanUpStack: state.cleanUpStack,
|
|
8
|
+
clearCleanUpStack: state.clearCleanUpStack,
|
|
9
|
+
})));
|
|
10
|
+
const { revokeListener } = useFormListeners(useShallow((state) => ({
|
|
11
|
+
revokeListener: state.revokeListener,
|
|
12
|
+
})));
|
|
13
|
+
const { clearObjKeyItem, clearArrItem } = useFormStore(useShallow((state) => {
|
|
14
|
+
return {
|
|
15
|
+
clearObjKeyItem: state.clearObjKeyItem,
|
|
16
|
+
clearArrItem: state.clearArrItem,
|
|
17
|
+
};
|
|
18
|
+
}));
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (Array.isArray(cleanUpStack) && cleanUpStack?.length) {
|
|
21
|
+
// console.log("Trigger clean up: ", cleanUpStack);
|
|
22
|
+
cleanUpStack.forEach((c) => {
|
|
23
|
+
if (c.itemKey) {
|
|
24
|
+
console.log("Revoke in Clean up: ", c);
|
|
25
|
+
revokeListener(c.itemKey, (listener, same) => {
|
|
26
|
+
if (!same.length) {
|
|
27
|
+
console.log("Trigger after revoke: ", listener, same);
|
|
28
|
+
clearObjKeyItem(listener.formName, listener.name);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
if (c.type === "array") {
|
|
34
|
+
console.log(c);
|
|
35
|
+
clearArrItem(c.formName, c.name);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
clearCleanUpStack();
|
|
42
|
+
}
|
|
43
|
+
}, [cleanUpStack]);
|
|
44
|
+
return _jsx(_Fragment, {});
|
|
45
|
+
};
|
|
46
|
+
export default FormCleanUp;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { cloneElement, useRef, useState } from "react";
|
|
2
|
+
import { v4 } from "uuid";
|
|
3
|
+
import useFormItemControl from "../../hooks/useFormItemControl";
|
|
4
|
+
export default function FormItem({ children, name, formName, initialValue, formItemId: externalFormItemId, rules, valuePropName = "value", getValueFromEvent, }) {
|
|
5
|
+
const elRef = useRef(null);
|
|
6
|
+
const [formItemId] = useState(externalFormItemId ?? v4());
|
|
7
|
+
const { value, onChange, errors, state, onFocus, isDirty } = useFormItemControl({
|
|
8
|
+
formName,
|
|
9
|
+
name,
|
|
10
|
+
initialValue,
|
|
11
|
+
formItemId,
|
|
12
|
+
rules,
|
|
13
|
+
elementRef: elRef,
|
|
14
|
+
});
|
|
15
|
+
// console.log("re-render", formName, name);
|
|
16
|
+
// useEffect(() => {
|
|
17
|
+
// console.log({ value });
|
|
18
|
+
// }, [value]);
|
|
19
|
+
return cloneElement(children, {
|
|
20
|
+
// ref: inputRef,
|
|
21
|
+
[valuePropName]: value,
|
|
22
|
+
onChange: (e) => {
|
|
23
|
+
let val = e;
|
|
24
|
+
if (getValueFromEvent && typeof getValueFromEvent === "function") {
|
|
25
|
+
val = getValueFromEvent(e);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
if (e && e.target) {
|
|
29
|
+
val = e.target.value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
onChange(val);
|
|
33
|
+
},
|
|
34
|
+
// onFocus: () => {
|
|
35
|
+
// setIsTouched(true);
|
|
36
|
+
// },
|
|
37
|
+
// isTouched: isTouched,
|
|
38
|
+
isDirty: isDirty,
|
|
39
|
+
// errors: errors,
|
|
40
|
+
// formState,
|
|
41
|
+
errors,
|
|
42
|
+
onFocus,
|
|
43
|
+
validateState: state,
|
|
44
|
+
ref: elRef,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import useTestRrenderListControl from "../../hooks/useFormListControl";
|
|
2
|
+
const FormList = ({ name, initialValues, form, formName, children }) => {
|
|
3
|
+
const { listFields, ...actions } = useTestRrenderListControl({
|
|
4
|
+
name,
|
|
5
|
+
initialValues,
|
|
6
|
+
form,
|
|
7
|
+
formName,
|
|
8
|
+
});
|
|
9
|
+
return children(listFields, { ...actions });
|
|
10
|
+
};
|
|
11
|
+
export default FormList;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cloneElement } from "react";
|
|
3
|
+
const InputWrapper = ({ children, errors = [], ...props }) => {
|
|
4
|
+
return (_jsxs("div", { children: [cloneElement(children, props), (props.isDirty || props.formState === "submitted") &&
|
|
5
|
+
errors?.map((r) => (_jsx("div", { children: r?.message }, `${props.name}-${props.formItemId}-${r.ruleName}`)))] }));
|
|
6
|
+
};
|
|
7
|
+
export default InputWrapper;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
+
import { type InputHTMLAttributes } from "react";
|
|
2
|
+
declare const Input: import("react").ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "onChange"> & {
|
|
3
|
+
onChange?: (value: any) => void;
|
|
4
|
+
} & import("react").RefAttributes<HTMLInputElement>>;
|
|
1
5
|
export default Input;
|
|
2
|
-
declare function Input({ onChange, ref, ...props }: {
|
|
3
|
-
[x: string]: any;
|
|
4
|
-
onChange: any;
|
|
5
|
-
ref: any;
|
|
6
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
const Input = forwardRef(({ onChange, ...props }, ref) => {
|
|
4
|
+
return (_jsx("input", { ref: ref, onChange: (e) => {
|
|
5
|
+
onChange?.(e.target.value);
|
|
6
|
+
}, ...props }));
|
|
7
|
+
});
|
|
8
|
+
export default Input;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Rules regex
|
|
2
|
+
export const IS_STRING_NUMBER_REGEX = /^\d+$/;
|
|
3
|
+
export const IS_USERNAME_REGEX = /^[a-zA-Z0-9._]{3,16}$/;
|
|
4
|
+
export const IS_PASSWORD_REGEX = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
|
|
5
|
+
export const IS_POSITIVE_STRING_NUMBER_REGEX = /^[1-9]\d*$/;
|
|
6
|
+
export const IS_POSITIVE_INTEGER_STRING_NUMBER_REGEX = /^(?:0|[1-9]\d*)(?:\.\d+)?$/;
|
|
7
|
+
export const IS_VIETNAMESE_PHONE_NUMBER_REGEX = /^(?:\+84|0)[\s\-\.]?(?:3[2-9]|5[6|8|9]|7[06-9]|8[1-5]|9[0-9])[\s\-\.]?\d[\d\s\-\.]{6,8}$/;
|
|
8
|
+
export const IS_EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
|
9
|
+
export const IS_NAME_REGEX = /^[A-Za-zÀÁÂÃÈÉÊÌÍÒÓÔÕÙÚĂĐĨŨƠàáâãèéêìíòóôõùúăđĩũơƯĂÂÊÔƠƯăâêôơưÇçÝýỳỵỷỹ\s]{2,50}$/;
|
|
10
|
+
export const IS_NOSPACE_STRING_AND_NUMBER_REGEX = /^[A-Za-zÀ-ỹ0-9]+$/;
|
|
11
|
+
export const IS_STRING_AND_NUMBER_REGEX = /^[A-Za-zÀ-ỹ0-9][A-Za-zÀ-ỹ0-9\s]*$/;
|
|
12
|
+
export const IS_NO_SPACE_ALPHABET_STRING_AND_NUMBER_REGEX = /^[A-Za-z0-9]+$/;
|
|
13
|
+
export const IS_ALPHABET_STRING_AND_NUMBER_REGEX = /^[A-Za-z0-9]+(?:\s[A-Za-z0-9]+)*$/;
|
|
14
|
+
export const IS_NO_SPACE_ALPHABET_STRING_REGEX = /^[A-Za-z]+$/;
|
|
15
|
+
// default message
|
|
16
|
+
export const HANDLER_INVALID_MESSAGE = "Invalid value!";
|
|
17
|
+
export const MIN_INVALID_MESSAGE = "Min value invalid!";
|
|
18
|
+
export const MAX_INVALID_MESSAGE = "Max value invalid!";
|
|
19
|
+
export const MIN_INVALID_LENGTH_MESSAGE = "Max length invalid!";
|
|
20
|
+
export const MAX_INVALID_LENGTH_MESSAGE = "Max length invalid!";
|
|
21
|
+
export const IS_STRING_NUMBER_INVALID_MESSAGE = "Must be number!";
|
|
22
|
+
export const IS_USERNAME_INVALID_MESSAGE = `Username must be containes string, number, '.' or '_'!`;
|
|
23
|
+
export const IS_PASSWORD_INVALID_MESSAGE = "Password must be contains Minimum 8 characters, with uppercase, lowercase, number, special character!";
|
|
24
|
+
export const IS_POSITIVE_STRING_NUMBER_INVALID_MESSAGE = "Must be positive number!";
|
|
25
|
+
export const IS_POSITIVE_INTEGER_STRING_NUMBER_INVALID_MESSAGE = "Must be positive integer number!";
|
|
26
|
+
export const IS_VIETNAMESE_PHONE_NUMBER_INVALID_MESSAGE = "Invalid phone number format!";
|
|
27
|
+
export const IS_EMAIL_INVALID_MESSAGE = "Invalid email format!";
|
|
28
|
+
export const IS_NAME_INVALID_MESSAGE = "Invalid name format!";
|
|
29
|
+
export const IS_NOSPACE_STRING_AND_NUMBER_MESSAGE = "String must only includes string and number without whitespace!";
|
|
30
|
+
export const IS_STRING_AND_NUMBER_MESSAGE = "String must only includes string, number and whitespace!";
|
|
31
|
+
export const IS_NO_SPACE_ALPHABET_STRING_AND_NUMBER_MESSAGE = "String must only includes alphabet characters and numbers!";
|
|
32
|
+
export const IS_ALPHABET_STRING_AND_NUMBER_MESSAGE = "String must only includes alphabet charaters, numbers and whitespace";
|
|
33
|
+
export const IS_NO_SPACE_ALPHABET_STRING_MESSAGE = "String must only includes alphabet";
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { get, isNil } from "lodash";
|
|
2
|
+
import { useTaskEffect } from "minh-custom-hooks-release";
|
|
3
|
+
import { useEffect, useMemo } from "react";
|
|
4
|
+
import { useShallow } from "zustand/react/shallow"; // Import useShallow
|
|
5
|
+
import { IS_ALPHABET_STRING_AND_NUMBER_REGEX, IS_EMAIL_REGEX, IS_NAME_REGEX, IS_NO_SPACE_ALPHABET_STRING_AND_NUMBER_REGEX, IS_NO_SPACE_ALPHABET_STRING_REGEX, IS_NOSPACE_STRING_AND_NUMBER_REGEX, IS_PASSWORD_REGEX, IS_POSITIVE_INTEGER_STRING_NUMBER_REGEX, IS_POSITIVE_STRING_NUMBER_REGEX, IS_STRING_AND_NUMBER_REGEX, IS_STRING_NUMBER_REGEX, IS_USERNAME_REGEX, IS_VIETNAMESE_PHONE_NUMBER_REGEX, } from "../constants/validation";
|
|
6
|
+
import { useFormContext } from "../providers/Form";
|
|
7
|
+
import { useFormCleanUp, useFormListeners, useFormStore, } from "../stores/formStore";
|
|
8
|
+
const VALID_PREMITIVE_TYPE = ["string", "number", "undefined"];
|
|
9
|
+
export default function useFormItemControl({ formName, form, name, initialValue, formItemId, rules, elementRef, }) {
|
|
10
|
+
const contextForm = useFormContext();
|
|
11
|
+
const { value, setData, getCacheData, getFormValues } = useFormStore(useShallow((state) => {
|
|
12
|
+
return {
|
|
13
|
+
value: get(state.forms, `${formName || form?.formName || contextForm?.formName}.${name}`),
|
|
14
|
+
setData: state.setData,
|
|
15
|
+
getCacheData: state.getCacheData,
|
|
16
|
+
getFormValues: state.getFormValues,
|
|
17
|
+
};
|
|
18
|
+
}));
|
|
19
|
+
const { setCleanUpStack } = useFormCleanUp(useShallow((state) => ({
|
|
20
|
+
setCleanUpStack: state.setCleanUpStack,
|
|
21
|
+
})));
|
|
22
|
+
const { initValue: internalInitValue, setInitData, getInitData, } = useFormStore(useShallow((state) => {
|
|
23
|
+
// console.log(
|
|
24
|
+
// "Shallow get initValue: ",
|
|
25
|
+
// formName || form?.formName || contextForm?.formName,
|
|
26
|
+
// name,
|
|
27
|
+
// get(
|
|
28
|
+
// state.initialValues,
|
|
29
|
+
// `${formName || form?.formName || contextForm?.formName}.${name}`
|
|
30
|
+
// )
|
|
31
|
+
// );
|
|
32
|
+
return {
|
|
33
|
+
initValue: get(state.initialValues, `${formName || form?.formName || contextForm?.formName}.${name}`),
|
|
34
|
+
setInitData: state.setInitData,
|
|
35
|
+
getInitData: state.getInitData,
|
|
36
|
+
};
|
|
37
|
+
}));
|
|
38
|
+
const { listener, setListener } = useFormListeners(useShallow((state) => {
|
|
39
|
+
// console.log(
|
|
40
|
+
// "Get listener from store: ",
|
|
41
|
+
// state.listeners.find((l) => l.formItemId === formItemId)
|
|
42
|
+
// );
|
|
43
|
+
return {
|
|
44
|
+
listener: state.listeners.find((l) => l.formItemId === formItemId),
|
|
45
|
+
setListener: state.setListener,
|
|
46
|
+
};
|
|
47
|
+
}));
|
|
48
|
+
const onInitData = (value) => {
|
|
49
|
+
// console.log(
|
|
50
|
+
// "Init:",
|
|
51
|
+
// formName || form?.formName || contextForm?.formName,
|
|
52
|
+
// name,
|
|
53
|
+
// value
|
|
54
|
+
// );
|
|
55
|
+
setInitData(formName || form?.formName || contextForm?.formName, name, value);
|
|
56
|
+
};
|
|
57
|
+
const onFocus = () => {
|
|
58
|
+
setListener({
|
|
59
|
+
formItemId,
|
|
60
|
+
isTouched: true,
|
|
61
|
+
isDirty: listener?.isDirty,
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
const emitFocus = () => {
|
|
65
|
+
if (elementRef?.current) {
|
|
66
|
+
elementRef.current.focus();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const onChange = (value, options) => {
|
|
70
|
+
if (options?.notTriggerDirty !== true) {
|
|
71
|
+
setListener({
|
|
72
|
+
formItemId,
|
|
73
|
+
isDirty: true,
|
|
74
|
+
isTouched: listener?.isTouched,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
setData(formName || form?.formName || contextForm?.formName, name, value);
|
|
78
|
+
};
|
|
79
|
+
const onReset = (value) => {
|
|
80
|
+
console.log("Reset", value, getInitData(formName || form?.formName || contextForm?.formName, name));
|
|
81
|
+
onChange(isNil(value)
|
|
82
|
+
? getInitData(formName || form?.formName || contextForm?.formName, name)
|
|
83
|
+
: value);
|
|
84
|
+
};
|
|
85
|
+
// validate field rules
|
|
86
|
+
const internalRules = useMemo(() => {
|
|
87
|
+
return rules || [];
|
|
88
|
+
}, [rules]);
|
|
89
|
+
const { data: errors, state } = useTaskEffect({
|
|
90
|
+
async task() {
|
|
91
|
+
const listErrors = [];
|
|
92
|
+
const formValues = getFormValues(formName || form?.formName || contextForm?.formName);
|
|
93
|
+
const ruleToTask = internalRules?.map((r, index) => async () => {
|
|
94
|
+
const ruleName = r?.name || index;
|
|
95
|
+
const fieldValue = value?.toString?.();
|
|
96
|
+
if (typeof r.handler === "function") {
|
|
97
|
+
if (!(await r.handler(value, formValues))) {
|
|
98
|
+
listErrors.push({
|
|
99
|
+
ruleName,
|
|
100
|
+
message: r.message,
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (VALID_PREMITIVE_TYPE.includes(typeof value)) {
|
|
106
|
+
// REQUIRE
|
|
107
|
+
if (r.required && !(fieldValue?.toString()?.trim()?.length > 0)) {
|
|
108
|
+
// tempMessage.push("This field is required!");
|
|
109
|
+
listErrors.push({
|
|
110
|
+
ruleName,
|
|
111
|
+
message: r.message,
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Min Max
|
|
116
|
+
if (!isNil(r.min) || !isNil(r.max)) {
|
|
117
|
+
if (isNil(value)) {
|
|
118
|
+
listErrors.push({
|
|
119
|
+
ruleName,
|
|
120
|
+
message: r.message,
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if ((r.min ?? Number.MIN_SAFE_INTEGER) <=
|
|
125
|
+
(r.max ?? Number.MAX_SAFE_INTEGER)) {
|
|
126
|
+
if (!isNil(r.min)) {
|
|
127
|
+
if (!isNaN(Number(fieldValue)) && Number(fieldValue) <= r.min) {
|
|
128
|
+
// tempMessage.push(MIN_INVALID_MESSAGE);
|
|
129
|
+
listErrors.push({
|
|
130
|
+
ruleName,
|
|
131
|
+
message: r.message,
|
|
132
|
+
});
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!isNil(r.max)) {
|
|
137
|
+
if (!isNaN(Number(fieldValue)) && Number(fieldValue) >= r.max) {
|
|
138
|
+
// tempMessage.push(MAX_INVALID_MESSAGE);
|
|
139
|
+
listErrors.push({
|
|
140
|
+
ruleName,
|
|
141
|
+
message: r.message,
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// MIN LENGTH MAX LENGTH
|
|
149
|
+
if (!isNil(r.minLength) || !isNil(r.maxLength)) {
|
|
150
|
+
if ((r.minLength ?? Number.MIN_SAFE_INTEGER) <=
|
|
151
|
+
(r.maxLength ?? Number.MAX_SAFE_INTEGER)) {
|
|
152
|
+
if (!isNil(r.minLength)) {
|
|
153
|
+
if (fieldValue?.length <= r.minLength) {
|
|
154
|
+
// tempMessage.push(MIN_INVALID_LENGTH_MESSAGE);
|
|
155
|
+
listErrors.push({
|
|
156
|
+
ruleName,
|
|
157
|
+
message: r.message,
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (!isNil(r.maxLength)) {
|
|
163
|
+
if (fieldValue?.length >= r.maxLength) {
|
|
164
|
+
// tempMessage.push(MAX_INVALID_LENGTH_MESSAGE);
|
|
165
|
+
listErrors.push({
|
|
166
|
+
ruleName,
|
|
167
|
+
message: r.message,
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Password
|
|
175
|
+
if (r.isPassword && !IS_PASSWORD_REGEX.test(fieldValue)) {
|
|
176
|
+
// tempMessage.push(IS_PASSWORD_INVALID_MESSAGE);
|
|
177
|
+
listErrors.push({
|
|
178
|
+
ruleName,
|
|
179
|
+
message: r.message,
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Username
|
|
184
|
+
if (r.isUsername && !IS_USERNAME_REGEX.test(fieldValue)) {
|
|
185
|
+
// tempMessage.push(IS_USERNAME_INVALID_MESSAGE);
|
|
186
|
+
listErrors.push({
|
|
187
|
+
ruleName,
|
|
188
|
+
message: r.message,
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
// Is Vietnamese Phone number
|
|
193
|
+
if (r.isPhoneNumber &&
|
|
194
|
+
!IS_VIETNAMESE_PHONE_NUMBER_REGEX.test(fieldValue)) {
|
|
195
|
+
// tempMessage.push(IS_VIETNAMESE_PHONE_NUMBER_INVALID_MESSAGE);
|
|
196
|
+
listErrors.push({
|
|
197
|
+
ruleName,
|
|
198
|
+
message: r.message,
|
|
199
|
+
});
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
// String number
|
|
203
|
+
if (r.isStringNumber && !IS_STRING_NUMBER_REGEX.test(fieldValue)) {
|
|
204
|
+
// tempMessage.push(IS_STRING_NUMBER_INVALID_MESSAGE);
|
|
205
|
+
listErrors.push({
|
|
206
|
+
ruleName,
|
|
207
|
+
message: r.message,
|
|
208
|
+
});
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// String positive number
|
|
212
|
+
if (r.isPositiveStringNumber &&
|
|
213
|
+
!IS_POSITIVE_STRING_NUMBER_REGEX.test(fieldValue)) {
|
|
214
|
+
// tempMessage.push(IS_POSITIVE_STRING_NUMBER_INVALID_MESSAGE);
|
|
215
|
+
listErrors.push({
|
|
216
|
+
ruleName,
|
|
217
|
+
message: r.message,
|
|
218
|
+
});
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
// String positive integer number
|
|
222
|
+
if (r.isPositiveIntegerStringNumber &&
|
|
223
|
+
!IS_POSITIVE_INTEGER_STRING_NUMBER_REGEX.test(fieldValue)) {
|
|
224
|
+
// tempMessage.push(
|
|
225
|
+
listErrors.push({
|
|
226
|
+
ruleName,
|
|
227
|
+
message: r.message,
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
// IS_POSITIVE_INTEGER_STRING_NUMBER_INVALID_MESSAGE
|
|
231
|
+
// );
|
|
232
|
+
}
|
|
233
|
+
// Email
|
|
234
|
+
if (r.isEmail && !IS_EMAIL_REGEX.test(fieldValue)) {
|
|
235
|
+
// tempMessage.push(IS_EMAIL_INVALID_MESSAGE);
|
|
236
|
+
listErrors.push({
|
|
237
|
+
ruleName,
|
|
238
|
+
message: r.message,
|
|
239
|
+
});
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
// Vietnamese Name
|
|
243
|
+
if (r.isVietnameseName && !IS_NAME_REGEX.test(fieldValue)) {
|
|
244
|
+
// tempMessage.push(IS_NAME_INVALID_MESSAGE);
|
|
245
|
+
listErrors.push({
|
|
246
|
+
ruleName,
|
|
247
|
+
message: r.message,
|
|
248
|
+
});
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
// String and number without whitespace
|
|
252
|
+
if (r.isNoSpaceStringAndNumber &&
|
|
253
|
+
!IS_NOSPACE_STRING_AND_NUMBER_REGEX.test(fieldValue)) {
|
|
254
|
+
// tempMessage.push(IS_NOSPACE_STRING_AND_NUMBER_MESSAGE);
|
|
255
|
+
listErrors.push({
|
|
256
|
+
ruleName,
|
|
257
|
+
message: r.message,
|
|
258
|
+
});
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
// String and number with
|
|
262
|
+
if (r.isStringAndNumber &&
|
|
263
|
+
!IS_STRING_AND_NUMBER_REGEX.test(fieldValue)) {
|
|
264
|
+
// tempMessage.push(IS_STRING_AND_NUMBER_MESSAGE);
|
|
265
|
+
listErrors.push({
|
|
266
|
+
ruleName,
|
|
267
|
+
message: r.message,
|
|
268
|
+
});
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (r.isNoSpaceOnlyAlphabetStringAndNumber &&
|
|
272
|
+
!IS_NO_SPACE_ALPHABET_STRING_AND_NUMBER_REGEX.test(fieldValue)) {
|
|
273
|
+
// tempMessage.push(IS_NO_SPACE_ALPHABET_STRING_AND_NUMBER_MESSAGE);
|
|
274
|
+
listErrors.push({
|
|
275
|
+
ruleName,
|
|
276
|
+
message: r.message,
|
|
277
|
+
});
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (r.isOnlyAlphabetStringAndNumber &&
|
|
281
|
+
!IS_ALPHABET_STRING_AND_NUMBER_REGEX.test(fieldValue)) {
|
|
282
|
+
// tempMessage.push(IS_ALPHABET_STRING_AND_NUMBER_MESSAGE);
|
|
283
|
+
listErrors.push({
|
|
284
|
+
ruleName,
|
|
285
|
+
message: r.message,
|
|
286
|
+
});
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (r.isNoSpaceAlphabetString &&
|
|
290
|
+
!IS_NO_SPACE_ALPHABET_STRING_REGEX.test(fieldValue)) {
|
|
291
|
+
// tempMessage.push(IS_NO_SPACE_ALPHABET_STRING_MESSAGE);
|
|
292
|
+
listErrors.push({
|
|
293
|
+
ruleName,
|
|
294
|
+
message: r.message,
|
|
295
|
+
});
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
// console.log("Trigger validate: ", listErrors);
|
|
301
|
+
for (const rAsync of ruleToTask) {
|
|
302
|
+
await rAsync();
|
|
303
|
+
}
|
|
304
|
+
setListener({ formItemId, internalErrors: listErrors });
|
|
305
|
+
return listErrors;
|
|
306
|
+
},
|
|
307
|
+
deps: [internalRules, value],
|
|
308
|
+
onError(err) {
|
|
309
|
+
console.log(err);
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
// useEffect(() => {
|
|
313
|
+
// console.log({ name, errors });
|
|
314
|
+
// }, [errors]);
|
|
315
|
+
// useEffect(() => {
|
|
316
|
+
// console.log({ name, listener });
|
|
317
|
+
// }, [listener]);
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
// console.log({ internalInitValue, value, initialValue });
|
|
320
|
+
// console.log("Init item value: ", name, value, internalInitValue);
|
|
321
|
+
// console.log("internalInitValue: ", internalInitValue);
|
|
322
|
+
if (!value) {
|
|
323
|
+
if (!internalInitValue) {
|
|
324
|
+
if (!isNil(initialValue)) {
|
|
325
|
+
console.log("On init data", initialValue);
|
|
326
|
+
onInitData(initialValue);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
onChange(internalInitValue);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}, [internalInitValue]);
|
|
334
|
+
useEffect(() => {
|
|
335
|
+
if (!listener) {
|
|
336
|
+
setListener({
|
|
337
|
+
onChange,
|
|
338
|
+
emitFocus,
|
|
339
|
+
isTouched: false,
|
|
340
|
+
isDirty: false,
|
|
341
|
+
name,
|
|
342
|
+
formName: formName || form?.formName || contextForm?.formName,
|
|
343
|
+
formItemId,
|
|
344
|
+
onReset,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
// return () => {
|
|
348
|
+
// console.log("Revoke listener", listener);
|
|
349
|
+
// };
|
|
350
|
+
}, []);
|
|
351
|
+
useEffect(() => {
|
|
352
|
+
if (listener) {
|
|
353
|
+
setListener({
|
|
354
|
+
formItemId,
|
|
355
|
+
onChange,
|
|
356
|
+
name,
|
|
357
|
+
formName: formName || form?.formName || contextForm?.formName,
|
|
358
|
+
onReset,
|
|
359
|
+
isTouched: listener?.isTouched,
|
|
360
|
+
isDirty: listener?.isDirty,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
const cacheData = getCacheData(formName || form?.formName || contextForm?.formName);
|
|
364
|
+
// console.log("Get cache Data after list change: ", cacheData);
|
|
365
|
+
if (cacheData) {
|
|
366
|
+
const getNewDataFromCache = get(cacheData, name);
|
|
367
|
+
// console.log("Init data when change form ite: ", name, cacheData);
|
|
368
|
+
if (!getNewDataFromCache) {
|
|
369
|
+
onChange(initialValue);
|
|
370
|
+
}
|
|
371
|
+
else
|
|
372
|
+
onChange(getNewDataFromCache);
|
|
373
|
+
}
|
|
374
|
+
}, [name, formName || form?.formName || contextForm?.formName]);
|
|
375
|
+
useEffect(() => {
|
|
376
|
+
return () => {
|
|
377
|
+
setCleanUpStack({
|
|
378
|
+
itemKey: formItemId,
|
|
379
|
+
});
|
|
380
|
+
};
|
|
381
|
+
}, []);
|
|
382
|
+
return {
|
|
383
|
+
value: value,
|
|
384
|
+
onChange,
|
|
385
|
+
state,
|
|
386
|
+
errors,
|
|
387
|
+
onFocus,
|
|
388
|
+
isDirty: listener?.isDirty,
|
|
389
|
+
};
|
|
390
|
+
}
|