auto-form-validator
Version:
A smart, zero-setup form validation library for React with automatic rule detection, i18n support, async validation, and UI integration helpers.
210 lines (202 loc) • 7.92 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
ErrorMessage: () => ErrorMessage_default,
useAutoValidator: () => useAutoValidator
});
module.exports = __toCommonJS(index_exports);
// src/hooks/useAutoValidator.ts
var import_react = __toESM(require("react"));
// src/utils/validators.ts
var validateValue = async (value, rules, messages) => {
if (rules.required && !value.trim()) {
return typeof rules.required === "object" && rules.required.message ? rules.required.message : messages.required;
}
if (rules.minLength && value.length < (typeof rules.minLength === "object" ? rules.minLength.value : rules.minLength)) {
const min = typeof rules.minLength === "object" ? rules.minLength.value : rules.minLength;
return typeof rules.minLength === "object" && rules.minLength.message ? rules.minLength.message : messages.minLength(min);
}
if (rules.maxLength && value.length > (typeof rules.maxLength === "object" ? rules.maxLength.value : rules.maxLength)) {
const max = typeof rules.maxLength === "object" ? rules.maxLength.value : rules.maxLength;
return typeof rules.maxLength === "object" && rules.maxLength.message ? rules.maxLength.message : messages.maxLength(max);
}
if (rules.pattern && !(typeof rules.pattern === "object" && "test" in rules.pattern ? rules.pattern.test(value) : rules.pattern.test(value))) {
if (typeof rules.pattern === "object" && "message" in rules.pattern && rules.pattern.message) {
return rules.pattern.message;
}
return messages.pattern;
}
if (rules.custom) {
const result = rules.custom(value);
if (result) return result;
}
if (rules.async) {
const asyncError = await rules.async(value);
if (asyncError) return asyncError || messages.async;
}
return null;
};
// src/utils/messages.ts
var messagesMap = {
en: {
required: "This field is required",
minLength: (n) => `Minimum length is ${n}`,
maxLength: (n) => `Maximum length is ${n}`,
pattern: "Invalid format",
async: "Validation failed"
},
hi: {
required: "\u092F\u0939 \u092B\u093C\u0940\u0932\u094D\u0921 \u0906\u0935\u0936\u094D\u092F\u0915 \u0939\u0948",
minLength: (n) => `\u0928\u094D\u092F\u0942\u0928\u0924\u092E \u0932\u0902\u092C\u093E\u0908 ${n} \u0939\u094B\u0928\u0940 \u091A\u093E\u0939\u093F\u090F`,
maxLength: (n) => `\u0905\u0927\u093F\u0915\u0924\u092E \u0932\u0902\u092C\u093E\u0908 ${n} \u0939\u094B\u0928\u0940 \u091A\u093E\u0939\u093F\u090F`,
pattern: "\u0905\u092E\u093E\u0928\u094D\u092F \u092A\u094D\u0930\u093E\u0930\u0942\u092A",
async: "\u092E\u093E\u0928\u094D\u092F\u0915\u0930\u0923 \u0935\u093F\u092B\u0932 \u0939\u0941\u0906"
}
};
// src/components/validationPatterns.ts
var defaultValidationPatterns = {
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
number: /^-?\d+(\.\d+)?$/,
tel: /^\+?[1-9]\d{1,14}$/,
url: /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([\/\w .-]*)*\/?$/,
date: /^\d{4}-\d{2}-\d{2}$/,
time: /^([01]\d|2[0-3]):([0-5]\d)$/,
color: /^#([0-9A-F]{3}){1,2}$/i
};
// src/hooks/useAutoValidator.ts
function debounce(func, wait = 300) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}
var useAutoValidator = (manualSchema = {}, language = "en") => {
const fields = (0, import_react.useRef)({});
const [errors, setErrors] = (0, import_react.useState)({});
const messages = messagesMap[language];
const extractRules = (0, import_react.useCallback)(
(el) => {
const rules = {};
if (el.required) rules.required = true;
if ("minLength" in el && el.minLength > 0) rules.minLength = el.minLength;
if ("maxLength" in el && el.maxLength > 0) rules.maxLength = el.maxLength;
if ("pattern" in el && el.pattern) rules.pattern = new RegExp(el.pattern);
const typePattern = defaultValidationPatterns[el.type];
if (typePattern) {
rules.pattern = typePattern;
}
return rules;
},
[]
);
const getField = (0, import_react.useCallback)((name) => {
if (!fields.current[name]) {
const ref = import_react.default.createRef();
fields.current[name] = {
ref,
rules: {}
};
}
return fields.current[name];
}, []);
(0, import_react.useEffect)(() => {
Object.entries(fields.current).forEach(([name, { ref }]) => {
const el = ref.current;
if (el) {
const extractedRules = extractRules(el);
fields.current[name].rules = extractedRules;
}
});
}, [extractRules]);
const validateAndSetError = async (name) => {
const field = fields.current[name];
if (!field || !field.ref.current) return;
const rules = manualSchema[name] || field.rules;
const value = field.ref.current.value;
const error = await validateValue(value, rules, messages);
setErrors((prev) => ({ ...prev, [name]: error }));
};
const debouncedValidateAndSetError = (0, import_react.useCallback)(
debounce(validateAndSetError, 300),
[manualSchema, messages]
);
const register = (name) => {
const field = getField(name);
const handleBlur = async () => {
await validateAndSetError(name);
};
const handleChange = () => {
debouncedValidateAndSetError(name);
};
return {
name,
ref: (el) => {
field.ref.current = el;
},
onBlur: handleBlur,
onChange: handleChange
};
};
const validateForm = async (values) => {
let valid = true;
const newErrors = {};
for (const name in values) {
const field = fields.current[name];
const rules = manualSchema[name] || (field == null ? void 0 : field.rules) || {};
const value = values[name];
const error = await validateValue(value, rules, messages);
newErrors[name] = error;
if (error) valid = false;
}
setErrors(newErrors);
return valid;
};
const resetErrors = () => setErrors({});
return {
register,
errors,
validateForm,
resetErrors
};
};
// src/components/ErrorMessage.tsx
var import_react2 = __toESM(require("react"));
var ErrorMessage = ({ name, errors }) => {
if (!errors[name]) return null;
return /* @__PURE__ */ import_react2.default.createElement("p", { style: { color: "red", fontSize: "0.875rem", marginTop: "4px" } }, errors[name]);
};
var ErrorMessage_default = ErrorMessage;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ErrorMessage,
useAutoValidator
});