codpro-validator 1.0.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/README.md ADDED
@@ -0,0 +1,282 @@
1
+ # codpro-validator
2
+
3
+ 🚀 A lightweight, powerful and flexible React validation hook.
4
+
5
+ Built for developers who want full control without heavy form libraries.
6
+
7
+ ---
8
+
9
+ ## ✨ Features
10
+
11
+ - Lightweight & zero external dependencies
12
+ - Custom validation rules
13
+ - Cross-field validation (match passwords)
14
+ - Multiple validation modes
15
+ - `onSubmit`
16
+ - `onBlur`
17
+ - `onChange`
18
+ - `isDirty` tracking
19
+ - `touchedFields` tracking
20
+ - `isSubmitting` state
21
+ - `isValid` state
22
+ - `reset()` support
23
+ - Async submit support
24
+ - Fully extensible
25
+ - TypeScript friendly
26
+
27
+ ---
28
+
29
+ ## 📦 Installation
30
+
31
+ ```bash
32
+ npm install codpro-validator
33
+ ```
34
+
35
+ or
36
+
37
+ ```bash
38
+ yarn add codpro-validator
39
+ ```
40
+
41
+ ---
42
+
43
+ ## 🚀 Basic Usage
44
+
45
+ ```js
46
+ import {
47
+ useValidator,
48
+ required,
49
+ email,
50
+ min,
51
+ match
52
+ } from "codpro-validator"
53
+
54
+ function RegisterForm() {
55
+ const {
56
+ register,
57
+ handleSubmit,
58
+ errors,
59
+ isDirty,
60
+ isSubmitting,
61
+ isValid,
62
+ touchedFields,
63
+ reset
64
+ } = useValidator(
65
+ {
66
+ email: {
67
+ rules: [required(), email()]
68
+ },
69
+ password: {
70
+ rules: [required(), min(6)]
71
+ },
72
+ confirmPassword: {
73
+ rules: [
74
+ required(),
75
+ match("password", "Passwords must match")
76
+ ]
77
+ }
78
+ },
79
+ { mode: "onSubmit" }
80
+ )
81
+
82
+ const onSubmit = async (data) => {
83
+ await new Promise((r) => setTimeout(r, 1000))
84
+ console.log(data)
85
+ reset()
86
+ }
87
+
88
+ return (
89
+ <form onSubmit={handleSubmit(onSubmit)}>
90
+ <input {...register("email")} placeholder="Email" />
91
+ {errors.email && <p>{errors.email}</p>}
92
+
93
+ <input type="password" {...register("password")} placeholder="Password" />
94
+ {errors.password && <p>{errors.password}</p>}
95
+
96
+ <input type="password" {...register("confirmPassword")} placeholder="Confirm Password" />
97
+ {errors.confirmPassword && <p>{errors.confirmPassword}</p>}
98
+
99
+ <button disabled={!isDirty || isSubmitting}>
100
+ {isSubmitting ? "Submitting..." : "Submit"}
101
+ </button>
102
+ </form>
103
+ )
104
+ }
105
+ ```
106
+
107
+ ---
108
+
109
+ ## 🧠 Validation Modes
110
+
111
+ ### onSubmit (Default)
112
+
113
+ Validation runs when form is submitted.
114
+
115
+ ```js
116
+ useValidator(schema, { mode: "onSubmit" })
117
+ ```
118
+
119
+ ---
120
+
121
+ ### onBlur
122
+
123
+ Validation runs when input loses focus.
124
+
125
+ ```js
126
+ useValidator(schema, { mode: "onBlur" })
127
+ ```
128
+
129
+ ---
130
+
131
+ ### onChange
132
+
133
+ Validation runs on every keystroke.
134
+
135
+ ```js
136
+ useValidator(schema, { mode: "onChange" })
137
+ ```
138
+
139
+ ---
140
+
141
+ ## 📘 API
142
+
143
+ ### useValidator(schema, options)
144
+
145
+ #### Parameters
146
+
147
+ | Name | Type | Description |
148
+ |---------|--------|------------|
149
+ | schema | object | Validation schema |
150
+ | options | object | Config options |
151
+
152
+ ---
153
+
154
+ ### Returned Properties
155
+
156
+ | Property | Description |
157
+ |----------------|------------|
158
+ | register | Connect input field |
159
+ | handleSubmit | Wrap submit handler |
160
+ | errors | Validation errors |
161
+ | isDirty | True if any field changed |
162
+ | isSubmitting | True during submit |
163
+ | touchedFields | Tracks touched inputs |
164
+ | isValid | True if no errors |
165
+ | reset | Reset form |
166
+
167
+ ---
168
+
169
+ ## 🛠 Built-in Validators
170
+
171
+ ### required(message?)
172
+
173
+ ```js
174
+ required("This field is required")
175
+ ```
176
+
177
+ ---
178
+
179
+ ### email(message?)
180
+
181
+ ```js
182
+ email("Invalid email address")
183
+ ```
184
+
185
+ ---
186
+
187
+ ### min(length, message?)
188
+
189
+ ```js
190
+ min(6, "Minimum 6 characters required")
191
+ ```
192
+
193
+ ---
194
+
195
+ ### max(length, message?)
196
+
197
+ ```js
198
+ max(20, "Maximum 20 characters allowed")
199
+ ```
200
+
201
+ ---
202
+
203
+ ### match(fieldName, message?)
204
+
205
+ ```js
206
+ match("password", "Passwords must match")
207
+ ```
208
+
209
+ ---
210
+
211
+ ## 🔁 Reset Form
212
+
213
+ ```js
214
+ reset()
215
+ ```
216
+
217
+ Resets form back to initial values.
218
+
219
+ ---
220
+
221
+ ## 🧩 Example Project Structure
222
+
223
+ ```
224
+ src/
225
+ ├── guards/
226
+ │ ├── required.js
227
+ │ ├── email.js
228
+ │ ├── min.js
229
+ │ ├── max.js
230
+ │ └── match.js
231
+
232
+ ├── hooks/
233
+ │ └── useValidator.js
234
+
235
+ └── index.js
236
+ ```
237
+
238
+ ---
239
+
240
+ ## 🔥 Why codpro-validator?
241
+
242
+ - Smaller than heavy form libraries
243
+ - Easy to understand
244
+ - Fully customizable
245
+ - Great for learning & production
246
+ - Clean API design
247
+
248
+ ---
249
+
250
+ ## 🗺 Roadmap
251
+
252
+ - Async validation rules
253
+ - Debounced validation
254
+ - Nested object validation
255
+ - Field arrays support
256
+ - DevTools integration
257
+
258
+ ---
259
+
260
+ ## 🏷 Keywords
261
+
262
+ react validation hook, react form validation, lightweight form library, custom validation, react validator hook, form state management
263
+
264
+ ---
265
+
266
+ ## 📄 License
267
+
268
+ MIT © CodPro Sui
269
+
270
+ ---
271
+
272
+ ## ⭐ Support
273
+
274
+ If you like this project:
275
+
276
+ - Star the repository
277
+ - Share it with developers
278
+ - Contribute improvements
279
+
280
+ ---
281
+
282
+ Built with ❤️ by CodPro Sui
package/dist/index.cjs ADDED
@@ -0,0 +1,212 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/index.js
20
+ var index_exports = {};
21
+ __export(index_exports, {
22
+ default: () => index_default,
23
+ email: () => email_default,
24
+ match: () => match_default,
25
+ max: () => max_default,
26
+ min: () => min_default,
27
+ password: () => password_default,
28
+ required: () => required_default,
29
+ useValidator: () => useValidator_default
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/guards/email.js
34
+ var email = (reg, mess = "Invalid email") => {
35
+ return (value) => {
36
+ if (!value || value.trim() === "admin@gmail.com") return mess;
37
+ let regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z.-]{2,}\.[a-zA-Z]{2,}$/;
38
+ if (reg) {
39
+ regex = reg;
40
+ }
41
+ return regex.test(value) ? null : mess;
42
+ };
43
+ };
44
+ var email_default = email;
45
+
46
+ // src/guards/match.js
47
+ var match = (fieldName, mess = "No Match!") => {
48
+ return (value, values) => {
49
+ return value === values[fieldName] ? null : mess;
50
+ };
51
+ };
52
+ var match_default = match;
53
+
54
+ // src/guards/max.js
55
+ var max = (length, mess) => {
56
+ return (value) => {
57
+ if (!value.trim() || typeof value !== "string") return null;
58
+ if (value.trim().length > length) {
59
+ return mess || `The size must be less than ${length}`;
60
+ }
61
+ return null;
62
+ };
63
+ };
64
+ var max_default = max;
65
+
66
+ // src/guards/min.js
67
+ var min = (length, mess) => {
68
+ return (value) => {
69
+ if (!value || typeof value !== "string") return null;
70
+ if (value.trim().length < length)
71
+ return mess || `The size must be at least ${length}`;
72
+ return null;
73
+ };
74
+ };
75
+ var min_default = min;
76
+
77
+ // src/guards/password.js
78
+ var password = (regex, mess = "Password must include at least one uppercase letter, one lowercase letter, one number, one special character (@$!%*?&) and be at least 8 characters long.") => {
79
+ return (value) => {
80
+ if (!value || typeof value !== "string") return null;
81
+ let reg = regex || /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
82
+ return reg.test(value.trim()) ? null : mess;
83
+ };
84
+ };
85
+ var password_default = password;
86
+
87
+ // src/guards/required.js
88
+ var required = (mess = "This is required") => {
89
+ return (value) => {
90
+ if (typeof value === "string" && value.trim() === "" || value === null || value === void 0)
91
+ return mess;
92
+ return null;
93
+ };
94
+ };
95
+ var required_default = required;
96
+
97
+ // src/hooks/useValidator.js
98
+ var import_react = require("react");
99
+
100
+ // src/engine/runRules.js
101
+ var runRules = async (value, rules = [], values = {}) => {
102
+ for (let rule of rules) {
103
+ let res = await rule(value, values);
104
+ if (res) return res;
105
+ }
106
+ return null;
107
+ };
108
+ var runRules_default = runRules;
109
+
110
+ // src/hooks/useValidator.js
111
+ var useValidator = (schema = {}, options = {}) => {
112
+ const [values, setValues] = (0, import_react.useState)({});
113
+ const [errors, setErrors] = (0, import_react.useState)({});
114
+ const [isSubmitting, setIsSubmitting] = (0, import_react.useState)(false);
115
+ const [touch, setTouch] = (0, import_react.useState)({});
116
+ const [isDirty, setIsDirty] = (0, import_react.useState)({});
117
+ let mode = options.mode || "onSubmit";
118
+ for (let field in schema) {
119
+ setIsDirty((pre) => ({ ...pre, [field]: "" }));
120
+ }
121
+ const register = (name) => {
122
+ if (typeof name !== "string") {
123
+ console.warn("In the register must be a string");
124
+ return {};
125
+ }
126
+ let config = schema[name] || {};
127
+ return {
128
+ name,
129
+ value: values[name] || "",
130
+ disabled: config.disabled || false,
131
+ onChange: async (e) => {
132
+ setValues((pre) => ({ ...pre, [name]: e.target.value }));
133
+ setIsDirty((pre) => ({ ...pre, [name]: e.target.value !== isDirty[name] }));
134
+ if (mode === "onChange") {
135
+ let res = await runRules_default(e.target.value, config.rules, values);
136
+ setErrors((pre) => ({ ...pre, [name]: res }));
137
+ }
138
+ },
139
+ onBlur: async () => {
140
+ setTouch((pre) => ({ ...pre, [name]: true }));
141
+ if (mode === "onBlur") {
142
+ let res = await runRules_default(values[name], config.rules, values);
143
+ setErrors((pre) => ({ ...pre, [name]: res }));
144
+ }
145
+ }
146
+ };
147
+ };
148
+ async function isValid() {
149
+ let error = {};
150
+ for (let field in schema) {
151
+ let config = schema[field];
152
+ if (config.disabled) continue;
153
+ let res = await runRules_default(values[field], config.rules, values);
154
+ if (res) error[field] = res;
155
+ }
156
+ setErrors(error);
157
+ return Object.keys(error).length === 0;
158
+ }
159
+ ;
160
+ const handleSubmit = (onSuccess, onError = () => {
161
+ }) => {
162
+ return async (e) => {
163
+ e.preventDefault();
164
+ setIsSubmitting(true);
165
+ try {
166
+ const valid = await isValid();
167
+ if (!valid) {
168
+ onError(errors);
169
+ return;
170
+ }
171
+ await onSuccess(values);
172
+ } catch (err) {
173
+ console.error("Submit Error:", err);
174
+ } finally {
175
+ setIsSubmitting(false);
176
+ }
177
+ };
178
+ };
179
+ const reset = () => {
180
+ let resetObj = {};
181
+ for (let field in schema) {
182
+ resetObj[field] = "";
183
+ }
184
+ setValues(resetObj);
185
+ setErrors({});
186
+ };
187
+ return {
188
+ values,
189
+ errors,
190
+ register,
191
+ isValid,
192
+ handleSubmit,
193
+ reset,
194
+ isSubmitting,
195
+ isDirty,
196
+ touch
197
+ };
198
+ };
199
+ var useValidator_default = useValidator;
200
+
201
+ // src/index.js
202
+ var index_default = useValidator_default;
203
+ // Annotate the CommonJS export names for ESM import in node:
204
+ 0 && (module.exports = {
205
+ email,
206
+ match,
207
+ max,
208
+ min,
209
+ password,
210
+ required,
211
+ useValidator
212
+ });
@@ -0,0 +1,177 @@
1
+ import { useState } from 'react';
2
+
3
+ const email = (reg, mess = "Invalid email") => {
4
+ return (value) => {
5
+ if (!value || value.trim() === "admin@gmail.com") return mess;
6
+ let regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z.-]{2,}\.[a-zA-Z]{2,}$/;
7
+ if (reg) {
8
+ regex = reg;
9
+ }
10
+ return regex.test(value) ? null : mess;
11
+ };
12
+ };
13
+
14
+ const match = (fieldName,mess="No Match!") =>{
15
+ return (value,values) =>{
16
+ return value === values[fieldName]?null:mess;
17
+ }
18
+ };
19
+
20
+ const max = (length, mess) => {
21
+ return (value) => {
22
+ if (!value.trim() || typeof value !== "string") return null;
23
+ if (value.trim().length > length) {
24
+ return mess || `The size must be less than ${length}`;
25
+ }
26
+ return null;
27
+ };
28
+ };
29
+
30
+ const min = (length, mess) => {
31
+ return (value) => {
32
+ if (!value || typeof value !== "string") return null;
33
+ if (value.trim().length < length)
34
+ return mess || `The size must be at least ${length}`;
35
+ return null;
36
+ };
37
+ };
38
+
39
+ const password = (
40
+ regex,
41
+ mess = "Password must include at least one uppercase letter, one lowercase letter, one number, one special character (@$!%*?&) and be at least 8 characters long.",
42
+ ) => {
43
+ return (value) => {
44
+ if (!value || typeof value !== "string") return null;
45
+ let reg = regex || /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
46
+
47
+ return reg.test(value.trim()) ? null : mess;
48
+ };
49
+ };
50
+
51
+ const required = (mess = "This is required") => {
52
+ return (value) => {
53
+ if ((typeof value === "string" && value.trim() === "") || value === null || value === undefined)
54
+ return mess;
55
+ return null;
56
+ };
57
+ };
58
+
59
+ const runRules = async (value,rules = [],values = {}) =>{
60
+ for(let rule of rules){
61
+ let res = await rule(value,values);
62
+ if(res) return res;
63
+ }
64
+ return null
65
+ };
66
+
67
+ const useValidator = (schema = {}, options = {}) => {
68
+ const [values, setValues] = useState({});
69
+ const [errors, setErrors] = useState({});
70
+ const [isSubmitting,setIsSubmitting] = useState(false);
71
+ const [touch, setTouch] = useState({});
72
+ const [isDirty,setIsDirty] = useState({});
73
+ //define default
74
+ let mode = options.mode || "onSubmit"; // onSubmit, onBlur, onChange
75
+
76
+ //dirty
77
+ for(let field in schema){
78
+ setIsDirty(pre => ({...pre,[field]: ""}));
79
+ }
80
+
81
+ // register for input
82
+ const register = (name) => {
83
+ if (typeof name !== "string") {
84
+ console.warn("In the register must be a string");
85
+ return {};
86
+ }
87
+ let config = schema[name] || {};
88
+ return {
89
+ name,
90
+ value: values[name] || "",
91
+ disabled: config.disabled || false,
92
+ onChange: async (e) => {
93
+ setValues((pre) => ({ ...pre, [name]: e.target.value }));
94
+
95
+ //dirty field
96
+ setIsDirty(pre => ({...pre,[name]:e.target.value !== isDirty[name]}));
97
+
98
+ if (mode === "onChange") {
99
+ let res = await runRules(e.target.value, config.rules,values);
100
+ setErrors((pre) => ({ ...pre, [name]: res }));
101
+ }
102
+ },
103
+ onBlur: async () => {
104
+ setTouch(pre => ({...pre,[name]:true}));
105
+ if (mode === "onBlur") {
106
+ let res = await runRules(values[name], config.rules,values);
107
+ setErrors((pre) => ({ ...pre, [name]: res }));
108
+ }
109
+ },
110
+ };
111
+ };
112
+
113
+ //checker
114
+ async function isValid(){
115
+ let error = {};
116
+ for (let field in schema) {
117
+ let config = schema[field];
118
+ if (config.disabled) continue;
119
+ let res = await runRules(values[field], config.rules,values);
120
+ if (res) error[field] = res;
121
+ }
122
+ setErrors(error);
123
+ return Object.keys(error).length === 0;
124
+ }
125
+ //submit data
126
+
127
+ const handleSubmit = (onSuccess, onError = () => {}) => {
128
+ return async (e) => {
129
+ e.preventDefault();
130
+
131
+ setIsSubmitting(true);
132
+
133
+ try {
134
+ const valid = await isValid();
135
+
136
+ if (!valid) {
137
+ onError(errors);
138
+ return;
139
+ }
140
+
141
+ await onSuccess(values);
142
+
143
+ } catch (err) {
144
+ console.error("Submit Error:", err);
145
+ } finally {
146
+ setIsSubmitting(false);
147
+ }
148
+ };
149
+ };
150
+
151
+ //reset data
152
+
153
+ const reset = () => {
154
+ let resetObj = {};
155
+ for(let field in schema){
156
+ resetObj[field] = "";
157
+ }
158
+ setValues(resetObj);
159
+ setErrors({});
160
+ };
161
+
162
+
163
+
164
+ return {
165
+ values,
166
+ errors,
167
+ register,
168
+ isValid,
169
+ handleSubmit,
170
+ reset,
171
+ isSubmitting,
172
+ isDirty,
173
+ touch
174
+ };
175
+ };
176
+
177
+ export { useValidator as default, email, match, max, min, password, required, useValidator };
@@ -0,0 +1,177 @@
1
+ import { useState } from 'react';
2
+
3
+ const email = (reg, mess = "Invalid email") => {
4
+ return (value) => {
5
+ if (!value || value.trim() === "admin@gmail.com") return mess;
6
+ let regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z.-]{2,}\.[a-zA-Z]{2,}$/;
7
+ if (reg) {
8
+ regex = reg;
9
+ }
10
+ return regex.test(value) ? null : mess;
11
+ };
12
+ };
13
+
14
+ const match = (fieldName,mess="No Match!") =>{
15
+ return (value,values) =>{
16
+ return value === values[fieldName]?null:mess;
17
+ }
18
+ };
19
+
20
+ const max = (length, mess) => {
21
+ return (value) => {
22
+ if (!value.trim() || typeof value !== "string") return null;
23
+ if (value.trim().length > length) {
24
+ return mess || `The size must be less than ${length}`;
25
+ }
26
+ return null;
27
+ };
28
+ };
29
+
30
+ const min = (length, mess) => {
31
+ return (value) => {
32
+ if (!value || typeof value !== "string") return null;
33
+ if (value.trim().length < length)
34
+ return mess || `The size must be at least ${length}`;
35
+ return null;
36
+ };
37
+ };
38
+
39
+ const password = (
40
+ regex,
41
+ mess = "Password must include at least one uppercase letter, one lowercase letter, one number, one special character (@$!%*?&) and be at least 8 characters long.",
42
+ ) => {
43
+ return (value) => {
44
+ if (!value || typeof value !== "string") return null;
45
+ let reg = regex || /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
46
+
47
+ return reg.test(value.trim()) ? null : mess;
48
+ };
49
+ };
50
+
51
+ const required = (mess = "This is required") => {
52
+ return (value) => {
53
+ if ((typeof value === "string" && value.trim() === "") || value === null || value === undefined)
54
+ return mess;
55
+ return null;
56
+ };
57
+ };
58
+
59
+ const runRules = async (value,rules = [],values = {}) =>{
60
+ for(let rule of rules){
61
+ let res = await rule(value,values);
62
+ if(res) return res;
63
+ }
64
+ return null
65
+ };
66
+
67
+ const useValidator = (schema = {}, options = {}) => {
68
+ const [values, setValues] = useState({});
69
+ const [errors, setErrors] = useState({});
70
+ const [isSubmitting,setIsSubmitting] = useState(false);
71
+ const [touch, setTouch] = useState({});
72
+ const [isDirty,setIsDirty] = useState({});
73
+ //define default
74
+ let mode = options.mode || "onSubmit"; // onSubmit, onBlur, onChange
75
+
76
+ //dirty
77
+ for(let field in schema){
78
+ setIsDirty(pre => ({...pre,[field]: ""}));
79
+ }
80
+
81
+ // register for input
82
+ const register = (name) => {
83
+ if (typeof name !== "string") {
84
+ console.warn("In the register must be a string");
85
+ return {};
86
+ }
87
+ let config = schema[name] || {};
88
+ return {
89
+ name,
90
+ value: values[name] || "",
91
+ disabled: config.disabled || false,
92
+ onChange: async (e) => {
93
+ setValues((pre) => ({ ...pre, [name]: e.target.value }));
94
+
95
+ //dirty field
96
+ setIsDirty(pre => ({...pre,[name]:e.target.value !== isDirty[name]}));
97
+
98
+ if (mode === "onChange") {
99
+ let res = await runRules(e.target.value, config.rules,values);
100
+ setErrors((pre) => ({ ...pre, [name]: res }));
101
+ }
102
+ },
103
+ onBlur: async () => {
104
+ setTouch(pre => ({...pre,[name]:true}));
105
+ if (mode === "onBlur") {
106
+ let res = await runRules(values[name], config.rules,values);
107
+ setErrors((pre) => ({ ...pre, [name]: res }));
108
+ }
109
+ },
110
+ };
111
+ };
112
+
113
+ //checker
114
+ async function isValid(){
115
+ let error = {};
116
+ for (let field in schema) {
117
+ let config = schema[field];
118
+ if (config.disabled) continue;
119
+ let res = await runRules(values[field], config.rules,values);
120
+ if (res) error[field] = res;
121
+ }
122
+ setErrors(error);
123
+ return Object.keys(error).length === 0;
124
+ }
125
+ //submit data
126
+
127
+ const handleSubmit = (onSuccess, onError = () => {}) => {
128
+ return async (e) => {
129
+ e.preventDefault();
130
+
131
+ setIsSubmitting(true);
132
+
133
+ try {
134
+ const valid = await isValid();
135
+
136
+ if (!valid) {
137
+ onError(errors);
138
+ return;
139
+ }
140
+
141
+ await onSuccess(values);
142
+
143
+ } catch (err) {
144
+ console.error("Submit Error:", err);
145
+ } finally {
146
+ setIsSubmitting(false);
147
+ }
148
+ };
149
+ };
150
+
151
+ //reset data
152
+
153
+ const reset = () => {
154
+ let resetObj = {};
155
+ for(let field in schema){
156
+ resetObj[field] = "";
157
+ }
158
+ setValues(resetObj);
159
+ setErrors({});
160
+ };
161
+
162
+
163
+
164
+ return {
165
+ values,
166
+ errors,
167
+ register,
168
+ isValid,
169
+ handleSubmit,
170
+ reset,
171
+ isSubmitting,
172
+ isDirty,
173
+ touch
174
+ };
175
+ };
176
+
177
+ export { useValidator as default, email, match, max, min, password, required, useValidator };
package/dist/index.js ADDED
@@ -0,0 +1,180 @@
1
+ // src/guards/email.js
2
+ var email = (reg, mess = "Invalid email") => {
3
+ return (value) => {
4
+ if (!value || value.trim() === "admin@gmail.com") return mess;
5
+ let regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z.-]{2,}\.[a-zA-Z]{2,}$/;
6
+ if (reg) {
7
+ regex = reg;
8
+ }
9
+ return regex.test(value) ? null : mess;
10
+ };
11
+ };
12
+ var email_default = email;
13
+
14
+ // src/guards/match.js
15
+ var match = (fieldName, mess = "No Match!") => {
16
+ return (value, values) => {
17
+ return value === values[fieldName] ? null : mess;
18
+ };
19
+ };
20
+ var match_default = match;
21
+
22
+ // src/guards/max.js
23
+ var max = (length, mess) => {
24
+ return (value) => {
25
+ if (!value.trim() || typeof value !== "string") return null;
26
+ if (value.trim().length > length) {
27
+ return mess || `The size must be less than ${length}`;
28
+ }
29
+ return null;
30
+ };
31
+ };
32
+ var max_default = max;
33
+
34
+ // src/guards/min.js
35
+ var min = (length, mess) => {
36
+ return (value) => {
37
+ if (!value || typeof value !== "string") return null;
38
+ if (value.trim().length < length)
39
+ return mess || `The size must be at least ${length}`;
40
+ return null;
41
+ };
42
+ };
43
+ var min_default = min;
44
+
45
+ // src/guards/password.js
46
+ var password = (regex, mess = "Password must include at least one uppercase letter, one lowercase letter, one number, one special character (@$!%*?&) and be at least 8 characters long.") => {
47
+ return (value) => {
48
+ if (!value || typeof value !== "string") return null;
49
+ let reg = regex || /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
50
+ return reg.test(value.trim()) ? null : mess;
51
+ };
52
+ };
53
+ var password_default = password;
54
+
55
+ // src/guards/required.js
56
+ var required = (mess = "This is required") => {
57
+ return (value) => {
58
+ if (typeof value === "string" && value.trim() === "" || value === null || value === void 0)
59
+ return mess;
60
+ return null;
61
+ };
62
+ };
63
+ var required_default = required;
64
+
65
+ // src/hooks/useValidator.js
66
+ import { useRef, useState } from "react";
67
+
68
+ // src/engine/runRules.js
69
+ var runRules = async (value, rules = [], values = {}) => {
70
+ for (let rule of rules) {
71
+ let res = await rule(value, values);
72
+ if (res) return res;
73
+ }
74
+ return null;
75
+ };
76
+ var runRules_default = runRules;
77
+
78
+ // src/hooks/useValidator.js
79
+ var useValidator = (schema = {}, options = {}) => {
80
+ const [values, setValues] = useState({});
81
+ const [errors, setErrors] = useState({});
82
+ const [isSubmitting, setIsSubmitting] = useState(false);
83
+ const [touch, setTouch] = useState({});
84
+ const [isDirty, setIsDirty] = useState({});
85
+ let mode = options.mode || "onSubmit";
86
+ for (let field in schema) {
87
+ setIsDirty((pre) => ({ ...pre, [field]: "" }));
88
+ }
89
+ const register = (name) => {
90
+ if (typeof name !== "string") {
91
+ console.warn("In the register must be a string");
92
+ return {};
93
+ }
94
+ let config = schema[name] || {};
95
+ return {
96
+ name,
97
+ value: values[name] || "",
98
+ disabled: config.disabled || false,
99
+ onChange: async (e) => {
100
+ setValues((pre) => ({ ...pre, [name]: e.target.value }));
101
+ setIsDirty((pre) => ({ ...pre, [name]: e.target.value !== isDirty[name] }));
102
+ if (mode === "onChange") {
103
+ let res = await runRules_default(e.target.value, config.rules, values);
104
+ setErrors((pre) => ({ ...pre, [name]: res }));
105
+ }
106
+ },
107
+ onBlur: async () => {
108
+ setTouch((pre) => ({ ...pre, [name]: true }));
109
+ if (mode === "onBlur") {
110
+ let res = await runRules_default(values[name], config.rules, values);
111
+ setErrors((pre) => ({ ...pre, [name]: res }));
112
+ }
113
+ }
114
+ };
115
+ };
116
+ async function isValid() {
117
+ let error = {};
118
+ for (let field in schema) {
119
+ let config = schema[field];
120
+ if (config.disabled) continue;
121
+ let res = await runRules_default(values[field], config.rules, values);
122
+ if (res) error[field] = res;
123
+ }
124
+ setErrors(error);
125
+ return Object.keys(error).length === 0;
126
+ }
127
+ ;
128
+ const handleSubmit = (onSuccess, onError = () => {
129
+ }) => {
130
+ return async (e) => {
131
+ e.preventDefault();
132
+ setIsSubmitting(true);
133
+ try {
134
+ const valid = await isValid();
135
+ if (!valid) {
136
+ onError(errors);
137
+ return;
138
+ }
139
+ await onSuccess(values);
140
+ } catch (err) {
141
+ console.error("Submit Error:", err);
142
+ } finally {
143
+ setIsSubmitting(false);
144
+ }
145
+ };
146
+ };
147
+ const reset = () => {
148
+ let resetObj = {};
149
+ for (let field in schema) {
150
+ resetObj[field] = "";
151
+ }
152
+ setValues(resetObj);
153
+ setErrors({});
154
+ };
155
+ return {
156
+ values,
157
+ errors,
158
+ register,
159
+ isValid,
160
+ handleSubmit,
161
+ reset,
162
+ isSubmitting,
163
+ isDirty,
164
+ touch
165
+ };
166
+ };
167
+ var useValidator_default = useValidator;
168
+
169
+ // src/index.js
170
+ var index_default = useValidator_default;
171
+ export {
172
+ index_default as default,
173
+ email_default as email,
174
+ match_default as match,
175
+ max_default as max,
176
+ min_default as min,
177
+ password_default as password,
178
+ required_default as required,
179
+ useValidator_default as useValidator
180
+ };
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "codpro-validator",
3
+ "version": "1.0.0",
4
+ "description": "A lightweight, flexible React validation hook with cross-field validation and powerful rule engine",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "type": "module",
9
+
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+
17
+ "files": [
18
+ "dist"
19
+ ],
20
+
21
+ "sideEffects": false,
22
+
23
+ "scripts": {
24
+ "build": "tsup src/index.js --format esm,cjs --dts",
25
+ "dev": "tsup src/index.js --watch",
26
+ "test": "echo \"Error: no test specified\" && exit 1"
27
+ },
28
+
29
+ "keywords": [
30
+ "react",
31
+ "react-hook",
32
+ "form-validation",
33
+ "react-form",
34
+ "validation",
35
+ "react-validation",
36
+ "custom-hook",
37
+ "form-library",
38
+ "cross-field-validation",
39
+ "lightweight-form"
40
+ ],
41
+
42
+ "peerDependencies": {
43
+ "react": ">=18"
44
+ },
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/CodPro-Sui/codpro-validator.git"
48
+ },
49
+ "homepage": "https://github.com/CodPro-Sui/codpro-validator#readme",
50
+
51
+ "author": "CodPro Sui",
52
+ "license": "MIT",
53
+
54
+ "devDependencies": {
55
+ "tsup": "^8.5.1",
56
+ "typescript": "^5.9.3"
57
+ }
58
+ }