easy-form-handler
Version:
A powerful, lightweight React form handling library with built-in validation, customizable styling, and intuitive components.
660 lines (643 loc) • 39 kB
JavaScript
import React, { useId, useState } from 'react';
function styleInject(css, ref) {
if ( ref === void 0 ) ref = {};
var insertAt = ref.insertAt;
if (!css || typeof document === 'undefined') { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css_248z$1 = "@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');\n*{\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n font-family: \"Poppins\", sans-serif;\n}\n:root{\n --primary_color: #3f51b5;\n --error_color: #f50057;\n --background_color: #f5f5f5;\n --text_color: #333;\n --white: #fff;\n --black: #000;\n --gray: #808080;\n --light_gray: #d3d3d3;\n --dark_gray: #696969;\n --blue: #2196f3;\n --light_blue: #70b8ff;\n --dark_blue: #1976d2;\n --green: #4caf50;\n --light_green: #c8e6c9;\n --dark_green: #388e3c;\n --red: #f44336;\n --light_red: #ef9a9a;\n --dark_red: #d32f2f;\n --yellow: #ffeb3b;\n --light_yellow: #fff9c4;\n --dark_yellow: #fbc02d;\n --purple: #9c27b0;\n --light_purple: #e1bee7;\n --dark_purple: #8e24aa;\n --pink: #e91e63;\n --light_pink: #f8bbd0;\n --dark_pink: #d81b60;\n --orange: #ff9800;\n --light_orange: #ffe0b2;\n --dark_orange: #f57c00;\n --teal: #009688;\n --light_teal: #b2dfdb;\n --dark_teal: #00796b;\n --cyan: #00bcd4;\n --light_cyan: #b2ebf2;\n --dark_cyan: #0097a7;\n --indigo: #3f51b5;\n --light_indigo: #c5cae9;\n --dark_indigo: #303f9f;\n --brown: #795548;\n --light_brown: #d7ccc8;\n --dark_brown: #5d4037;\n --amber: #ffc107;\n --light_amber: #ffe57f;\n --dark_amber: #ff8f00;\n --lime: #cddc39;\n --light_lime: #f0e68c;\n --dark_lime: #c0ca33;\n --deep_orange: #ff5722; \n --light_deep_orange: #ffccbc;\n --dark_deep_orange: #e64a19;\n --deep_purple: #673ab7;\n --light_deep_purple: #d1c4e9;\n --dark_deep_purple: #512da8;\n --light_gray: #d3d3d3;\n --dark_gray: #696969;\n /* --light_blue: #bbdefb; */\n --dark_blue: #1976d2;\n --light_green: #c8e6c9;\n --dark_green: #388e3c;\n --light_red: #ef9a9a;\n --dark_red: #d32f2f;\n --light_yellow: #fff9c4;\n --dark_yellow: #fbc02d;\n --light_purple: #e1bee7;\n --dark_purple: #8e24aa; \n --light_pink: #f8bbd0;\n --dark_pink: #d81b60;\n --light_orange: #ffe0b2; \n}\n\n.index_App__N3uO9{\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n}";
styleInject(css_248z$1);
var css_248z = ".Form-module_Form__JzhAc {\r\n border: 1px solid var(--light_gray);\r\n width: fit-content;\r\n padding: 20px;\r\n padding-top: 35px;\r\n border-radius: 10px;\r\n width: 450px;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n gap: 20px;\r\n flex-direction: column;\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_heading__X4bMW {\r\n font-size: 28px;\r\n font-weight: 500;\r\n color: var(--text_color);\r\n text-align: center;\r\n margin-bottom: 10px;\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_inputContainer__aMSM2 {\r\n width: 100%;\r\n /* border: 1px solid red; */\r\n display: flex;\r\n justify-content: center;\r\n align-items: start;\r\n flex-direction: column;\r\n gap: 5px;\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_inputContainer__aMSM2 label {\r\n font-size: 15px;\r\n font-weight: 500;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n gap: 5px;\r\n color: var(--text_color);\r\n margin-bottom: 5px;\r\n}\r\n.Form-module_Form__JzhAc .Form-module_inputContainer__aMSM2 label span {\r\n font-size: 15px;\r\n font-weight: 400;\r\n color: var(--red);\r\n}\r\n.Form-module_Form__JzhAc .Form-module_inputContainer__aMSM2 label:hover {\r\n cursor: pointer;\r\n}\r\n.Form-module_Form__JzhAc .Form-module_inputContainer__aMSM2 input {\r\n width: 100%;\r\n padding: 10px;\r\n font-size: 15px;\r\n border-radius: 5px;\r\n border: 2px solid var(--light_gray);\r\n transition: all 0.2s ease-in-out;\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_inputContainer__aMSM2 input:focus {\r\n outline: none;\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_inputContainer__aMSM2 input:focus-within {\r\n border: 2px solid var(--blue);\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_submit__8sV37 {\r\n background-color: var(--blue);\r\n /* border: 1px solid red; */\r\n width: 100%;\r\n padding: 8px 20px;\r\n font-size: 18px;\r\n border-radius: 5px;\r\n border: none;\r\n cursor: pointer;\r\n color: var(--white);\r\n font-weight: 400;\r\n transition: all 0.2s ease-in-out;\r\n}\r\n.Form-module_Form__JzhAc .Form-module_submit__8sV37:hover {\r\n background-color: var(--light_blue);\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_error__E28cD {\r\n font-size: 12px;\r\n font-weight: 400;\r\n color: var(--red);\r\n margin-top: 5px;\r\n text-align: left;\r\n width: 100%;\r\n gap: 5px;\r\n}\r\n.Form-module_inputContainer__aMSM2.Form-module_inputError__iBZiD input:focus-within {\r\n border: 2px solid var(--red);\r\n}\r\n.Form-module_inputContainer__aMSM2.Form-module_inputErrorBlur__-tOTg input {\r\n border: 2px solid var(--red);\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_passwordContainer__Hyfhn {\r\n width: 100%;\r\n /* border: 1px solid red; */\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n gap: 5px;\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_eye__8iqBD {\r\n height: 46.4px;\r\n width: 50px;\r\n border: 2px solid var(--light_gray);\r\n border-radius: 5px;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n cursor: pointer;\r\n transition: all 0.2s ease-in-out;\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_eye__8iqBD svg {\r\n height: 20px;\r\n width: 20px;\r\n color: var(--text_color);\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_eye__8iqBD:hover {\r\n background-color: var(--light_gray);\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_eye__8iqBD.Form-module_active__7GtwM {\r\n /* color: var(--blue); */\r\n border: 2px solid var(--blue);\r\n}\r\n.Form-module_Form__JzhAc .Form-module_eye__8iqBD.Form-module_active__7GtwM svg {\r\n color: var(--blue);\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_selectContainer__GEd3r {\r\n width: 100%;\r\n position: relative;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 5px;\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_selectContainer__GEd3r .Form-module_selectedOption__7CtTy {\r\n width: 100%;\r\n padding: 10px;\r\n font-size: 15px;\r\n border-radius: 5px;\r\n border: 2px solid var(--light_gray);\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n cursor: pointer;\r\n transition: all 0.2s ease-in-out;\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_selectContainer__GEd3r .Form-module_selectedOption__7CtTy:hover {\r\n background-color: var(--light_gray);\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_selectContainer__GEd3r .Form-module_dropdown__c-mnY {\r\n position: absolute;\r\n top: 100%;\r\n left: 0;\r\n width: 100%;\r\n background-color: var(--white);\r\n border: 2px solid var(--light_gray);\r\n border-radius: 5px;\r\n margin-top: 5px;\r\n z-index: 10;\r\n max-height: 200px;\r\n overflow-y: auto;\r\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_selectContainer__GEd3r .Form-module_dropdownItem__HTPjT {\r\n padding: 10px;\r\n font-size: 15px;\r\n color: var(--text_color);\r\n cursor: pointer;\r\n transition: all 0.2s ease-in-out;\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_selectContainer__GEd3r .Form-module_dropdownItem__HTPjT:hover {\r\n background-color: var(--light_gray);\r\n}\r\n\r\n.Form-module_Form__JzhAc .Form-module_selectContainer__GEd3r .Form-module_dropdownItem__HTPjT.Form-module_active__7GtwM {\r\n background-color: var(--blue);\r\n color: var(--white);\r\n}";
var Style = {"Form":"Form-module_Form__JzhAc","heading":"Form-module_heading__X4bMW","inputContainer":"Form-module_inputContainer__aMSM2","submit":"Form-module_submit__8sV37","error":"Form-module_error__E28cD","inputError":"Form-module_inputError__iBZiD","inputErrorBlur":"Form-module_inputErrorBlur__-tOTg","passwordContainer":"Form-module_passwordContainer__Hyfhn","eye":"Form-module_eye__8iqBD","active":"Form-module_active__7GtwM"};
styleInject(css_248z);
function Form({ children, isActiveDefaultStyle = true, className = "", onSubmit, ...props }) {
const handleSubmit = (e) => {
e.preventDefault();
const data = {};
const form = Array.from(e.target.elements);
for (const element of form) {
if (element.tagName === 'INPUT' && element.type !== 'submit') {
// console.log(`${element.name}: ${element.value}`);
data[element.name] = element.value;
}
if (element.tagName === 'SELECT') {
// console.log(`${element}: ${element}`);
data[element.name] = element.value;
}
if (element.tagName === 'TEXTAREA') {
data[element.name] = element.value;
}
if (element.tagName === 'FIELDSET') {
const inputs = Array.from(element.elements);
inputs.forEach(input => {
if (input.name) {
data[input.name] = input.value;
}
});
}
if (element.tagName === 'LEGEND') {
// LEGEND typically doesn't hold data, so it can be skipped or logged if needed
console.log(`Legend: ${element.textContent}`);
}
if (element.tagName === 'DATALIST') {
const options = Array.from(element.children);
options.forEach(option => {
if (option.value) {
data[element.id] = option.value; // Assuming the datalist has an id
}
});
}
}
onSubmit(data);
};
return (React.createElement(React.Fragment, null,
React.createElement("form", { onSubmit: handleSubmit, className: `${isActiveDefaultStyle ? Style.Form : ""} ${className}`, ...props }, children)));
}
const Heading = ({ value, className = "", children, ...props }) => {
return (React.createElement(React.Fragment, null,
React.createElement("h1", { className: `${Style.heading} ${className}`, ...props },
value,
children)));
};
function validate(data, error, rule) {
const errors = {};
for (const key in rule) {
const rules = rule[key];
const value = data[key];
for (const ruleKey in rules) {
const ruleValue = rules[ruleKey];
// Presence rules
if (ruleKey === 'required' && (!value || value === '')) {
errors[key] = `${key} is required`;
break;
}
if (ruleKey === 'required_if') {
const [field, expectedValue] = ruleValue.split(',');
if (data[field] === expectedValue && (!value || value === '')) {
errors[key] = `${key} is required when ${field} is ${expectedValue}`;
break;
}
}
if (ruleKey === 'required_unless') {
const [field, expectedValue] = ruleValue.split(',');
if (data[field] !== expectedValue && (!value || value === '')) {
errors[key] = `${key} is required unless ${field} is ${expectedValue}`;
break;
}
}
if (ruleKey === 'required_with') {
const fields = ruleValue.split(',');
if (fields.some((f) => data[f] !== undefined) && (!value || value === '')) {
errors[key] = `${key} is required when any of ${fields.join(', ')} is present`;
break;
}
}
if (ruleKey === 'required_without') {
const fields = ruleValue.split(',');
if (fields.some((f) => data[f] === undefined) && (!value || value === '')) {
errors[key] = `${key} is required when any of ${fields.join(', ')} is missing`;
break;
}
}
if (ruleKey === 'present' && !(key in data)) {
errors[key] = `${key} must be present`;
break;
}
if (ruleKey === 'filled' && (!value || value === '')) {
errors[key] = `${key} must be present and not empty if present`;
break;
}
// String rules
if (ruleKey === 'isAlpha' && (typeof value !== 'string' || !/^[a-zA-Z]+$/.test(value))) {
errors[key] = `${key} must contain only alphabetic characters`;
break;
}
if (ruleKey === 'noSpecialChars' && /[^a-zA-Z0-9]/.test(value)) {
errors[key] = `${key} must not contain any special characters`;
break;
}
if (ruleKey === 'string' && typeof value !== 'string') {
errors[key] = `${key} must be a string`;
break;
}
// Size rules
if (ruleKey === 'min') {
const min = parseInt(ruleValue, 10);
if (typeof value === 'string' && value.length < min) {
errors[key] = `${key} must be at least ${min} characters long`;
break;
}
if (typeof value === 'number' && value < min) {
errors[key] = `${key} must be at least ${min}`;
break;
}
}
if (ruleKey === 'max') {
const max = parseInt(ruleValue, 10);
if (typeof value === 'string' && value.length > max) {
errors[key] = `${key} must not exceed ${max} characters`;
break;
}
if (typeof value === 'number' && value > max) {
errors[key] = `${key} must not exceed ${max}`;
break;
}
}
if (ruleKey === 'between') {
const [min, max] = ruleValue.split(',').map(Number);
if (typeof value === 'string' && (value.length < min || value.length > max)) {
errors[key] = `${key} must be between ${min} and ${max} characters long`;
break;
}
if (typeof value === 'number' && (value < min || value > max)) {
errors[key] = `${key} must be between ${min} and ${max}`;
break;
}
}
if (ruleKey === 'size') {
const size = parseInt(ruleValue, 10);
if (typeof value === 'string' && value.length !== size) {
errors[key] = `${key} must be exactly ${size} characters long`;
break;
}
if (typeof value === 'number' && value !== size) {
errors[key] = `${key} must be exactly ${size}`;
break;
}
if (Array.isArray(value) && value.length !== size) {
errors[key] = `${key} must contain exactly ${size} items`;
break;
}
}
if (ruleKey === 'length') {
const length = parseInt(ruleValue, 10);
if (typeof value === 'string' && value.length !== length) {
errors[key] = `${key} must be exactly ${length} characters long`;
break;
}
}
// Type rules
if (ruleKey === 'numeric' && typeof value !== 'number') {
errors[key] = `${key} must be a number`;
break;
}
if (ruleKey === 'integer' && !Number.isInteger(value)) {
errors[key] = `${key} must be an integer`;
break;
}
if (ruleKey === 'boolean' && typeof value !== 'boolean' && value !== 0 && value !== 1) {
errors[key] = `${key} must be true/false, 0/1`;
break;
}
if (ruleKey === 'array' && !Array.isArray(value)) {
errors[key] = `${key} must be an array`;
break;
}
if (ruleKey === 'date' && isNaN(Date.parse(value))) {
errors[key] = `${key} must be a valid date`;
break;
}
if (ruleKey === 'file' && (typeof value !== 'string' || !value.trim())) {
errors[key] = `${key} must be a valid file path`;
break;
}
if (ruleKey === 'image' && Array.isArray(value)) {
const invalidFiles = value.filter((file) => !/\.(jpeg|jpg|png|bmp|gif|svg|webp)$/i.test(file));
if (invalidFiles.length > 0) {
errors[key] = `${key} contains invalid image files. Allowed extensions are: jpeg, jpg, png, bmp, gif, svg, webp`;
break;
}
break;
}
if (ruleKey === 'mimes') {
const mimes = Array.isArray(rules[ruleKey]) ? rules[ruleKey] : rules[ruleKey].split(',');
const ext = value.split('.').pop();
if (!mimes.includes(ext)) {
errors[key] = `${key} must be a file of type: ${mimes.join(', ')}`;
break;
}
}
// Format rules
if (ruleKey === 'email' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
errors[key] = `${key} must be a valid email address`;
break;
}
if (ruleKey === 'url' && !/^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/.test(value)) {
errors[key] = `${key} must be a valid URL`;
break;
}
if (ruleKey === 'uuid' && !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)) {
errors[key] = `${key} must be a valid UUID`;
break;
}
if (ruleKey === 'ip' && !/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(value)) {
errors[key] = `${key} must be a valid IP address`;
break;
}
if (ruleKey === 'json') {
try {
JSON.parse(value);
}
catch {
errors[key] = `${key} must be a valid JSON string`;
break;
}
}
if (ruleKey === 'regex') {
const regex = new RegExp(ruleValue);
if (!regex.test(value)) {
errors[key] = `${key} must satisfy the regex pattern: ${ruleValue}`;
break;
}
}
if (ruleKey === 'contains') {
const substrings = Array.isArray(ruleValue) ? ruleValue : ruleValue.split(',');
if (typeof value !== 'string' || !substrings.some((substring) => value.includes(substring))) {
errors[key] = `${key} must contain at least one of the following: ${substrings.join(', ')}`;
break;
}
}
if (ruleKey === 'in') {
const options = Array.isArray(ruleValue) ? ruleValue : ruleValue.split(',');
if (!options.includes(value)) {
errors[key] = `${key} must be one of the following: ${options.join(', ')}`;
break;
}
}
if (ruleKey === 'not_in') {
const options = Array.isArray(ruleValue) ? ruleValue : ruleValue.split(',');
if (options.includes(value)) {
errors[key] = `${key} must not be one of the following: ${options.join(', ')}`;
break;
}
}
if (ruleKey === 'same') {
const otherField = ruleValue;
if (data[otherField] !== value) {
errors[key] = `${key} must be the same as ${otherField}`;
break;
}
}
if (ruleKey === 'different') {
const otherField = ruleValue;
if (data[otherField] === value) {
errors[key] = `${key} must be different from ${otherField}`;
break;
}
}
if (ruleKey === 'not_regex') {
const regexPattern = ruleValue;
const regex = new RegExp(regexPattern);
if (regex.test(value)) {
errors[key] = `${key} must not satisfy the regex pattern: ${regexPattern}`;
break;
}
}
if (ruleKey === 'not_contains') {
const substrings = Array.isArray(ruleValue) ? ruleValue : ruleValue.split(',');
if (typeof value !== 'string' || substrings.some((substring) => value.includes(substring))) {
errors[key] = `${key} must not contain any of the following: ${substrings.join(', ')}`;
break;
}
}
if (ruleKey === 'not_in') {
const options = Array.isArray(ruleValue) ? ruleValue : ruleValue.split(',');
if (options.includes(value)) {
errors[key] = `${key} must not be one of the following: ${options.join(', ')}`;
break;
}
}
if (ruleKey === 'not_same') {
const otherField = ruleValue;
if (data[otherField] === value) {
errors[key] = `${key} must not be the same as ${otherField}`;
break;
}
}
if (ruleKey === 'not_different') {
const otherField = ruleValue;
if (data[otherField] !== value) {
errors[key] = `${key} must not be different from ${otherField}`;
break;
}
}
if (ruleKey === 'not_present' && (key in data)) {
errors[key] = `${key} must not be present`;
break;
}
if (ruleKey === 'not_filled' && (typeof value === 'string' && value.trim())) {
errors[key] = `${key} must not be filled`;
break;
}
if (ruleKey === 'not_required' && (typeof value !== 'undefined' && value !== null)) {
errors[key] = `${key} must not be required`;
break;
}
if (ruleKey === 'not_string' && typeof value === 'string') {
errors[key] = `${key} must not be a string`;
break;
}
if (ruleKey === 'not_numeric' && typeof value === 'number') {
errors[key] = `${key} must not be a number`;
break;
}
if (ruleKey === 'not_integer' && Number.isInteger(value)) {
errors[key] = `${key} must not be an integer`;
break;
}
if (ruleKey === 'not_boolean' && (typeof value === 'boolean' || value === 0 || value === 1)) {
errors[key] = `${key} must not be true/false, 0/1`;
break;
}
if (ruleKey === 'not_array' && Array.isArray(value)) {
errors[key] = `${key} must not be an array`;
break;
}
if (ruleKey === 'not_date' && !isNaN(Date.parse(value))) {
errors[key] = `${key} must not be a valid date`;
break;
}
if (ruleKey === 'not_file' && (typeof value === 'string' && value.trim())) {
errors[key] = `${key} must not be a valid file path`;
break;
}
if (ruleKey === 'not_image' && Array.isArray(value)) {
const invalidFiles = value.filter((file) => !/\.(jpeg|jpg|png|bmp|gif|svg|webp)$/i.test(file));
if (invalidFiles.length > 0) {
errors[key] = `${key} contains invalid image files. Allowed extensions are: jpeg, jpg, png, bmp, gif, svg, webp`;
break;
}
break;
}
if (ruleKey === 'not_mimes') {
const mimes = Array.isArray(ruleValue) ? ruleValue : ruleValue.split(',');
const ext = value.split('.').pop();
if (!mimes.includes(ext)) {
errors[key] = `${key} must not be a file of type: ${mimes.join(', ')}`;
break;
}
}
if (ruleKey === 'not_email' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
errors[key] = `${key} must not be a valid email address`;
break;
}
if (ruleKey === 'not_url' && /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/.test(value)) {
errors[key] = `${key} must not be a valid URL`;
break;
}
if (ruleKey === 'not_uuid' && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)) {
errors[key] = `${key} must not be a valid UUID`;
break;
}
if (ruleKey === 'not_ip' && /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(value)) {
errors[key] = `${key} must not be a valid IP address`;
break;
}
if (ruleKey === 'not_json') {
try {
JSON.parse(value);
}
catch {
errors[key] = `${key} must not be a valid JSON string`;
break;
}
}
if (ruleKey === 'not_regex') {
const regexPattern = ruleValue;
const regex = new RegExp(regexPattern);
if (regex.test(value)) {
errors[key] = `${key} must not satisfy the regex pattern: ${regexPattern}`;
break;
}
}
if (ruleKey === 'not_in') {
const options = Array.isArray(ruleValue) ? ruleValue : ruleValue.split(',');
if (options.includes(value)) {
errors[key] = `${key} must not be one of the following: ${options.join(', ')}`;
break;
}
}
if (ruleKey === 'not_same') {
const otherField = ruleValue;
if (data[otherField] === value) {
errors[key] = `${key} must not be the same as ${otherField}`;
break;
}
}
if (ruleKey === 'not_different') {
const otherField = ruleValue;
if (data[otherField] !== value) {
errors[key] = `${key} must not be different from ${otherField}`;
break;
}
}
if (ruleKey === 'not_present' && (key in data)) {
errors[key] = `${key} must not be present`;
break;
}
if (ruleKey === 'not_filled' && (typeof value === 'string' && value.trim())) {
errors[key] = `${key} must not be filled`;
break;
}
if (ruleKey === 'not_required' && (typeof value !== 'undefined' && value !== null)) {
errors[key] = `${key} must not be required`;
break;
}
if (ruleKey === 'not_string' && typeof value === 'string') {
errors[key] = `${key} must not be a string`;
break;
}
if (ruleKey === 'not_numeric' && typeof value === 'number') {
errors[key] = `${key} must not be a number`;
break;
}
if (ruleKey === 'not_integer' && Number.isInteger(value)) {
errors[key] = `${key} must not be an integer`;
break;
}
if (ruleKey === 'not_boolean' && (typeof value === 'boolean' || value === 0 || value === 1)) {
errors[key] = `${key} must not be true/false, 0/1`;
break;
}
if (ruleKey === 'not_array' && Array.isArray(value)) {
errors[key] = `${key} must not be an array`;
break;
}
if (ruleKey === 'not_date' && !isNaN(Date.parse(value))) {
errors[key] = `${key} must not be a valid date`;
break;
}
if (ruleKey === 'not_file' && (typeof value === 'string' && value.trim())) {
errors[key] = `${key} must not be a valid file path`;
break;
}
if (ruleKey === 'not_image' && Array.isArray(value)) {
const invalidFiles = value.filter((file) => !/\.(jpeg|jpg|png|bmp|gif|svg|webp)$/i.test(file));
if (invalidFiles.length > 0) {
errors[key] = `${key} contains invalid image files. Allowed extensions are: jpeg, jpg, png, bmp, gif, svg, webp`;
break;
}
break;
}
if (ruleKey === 'not_mimes') {
const mimes = Array.isArray(ruleValue) ? ruleValue : ruleValue.split(',');
const ext = value.split('.').pop();
if (!mimes.includes(ext)) {
errors[key] = `${key} must not be a file of type: ${mimes.join(', ')}`;
break;
}
}
if (ruleKey === 'not_email' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
errors[key] = `${key} must not be a valid email address`;
break;
}
if (ruleKey === 'not_url' && /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/.test(value)) {
errors[key] = `${key} must not be a valid URL`;
break;
}
if (ruleKey === 'not_uuid' && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)) {
errors[key] = `${key} must not be a valid UUID`;
break;
}
if (ruleKey === 'not_ip' && /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(value)) {
errors[key] = `${key} must not be a valid IP address`;
break;
}
if (ruleKey === 'not_json') {
try {
JSON.parse(value);
}
catch {
errors[key] = `${key} must not be a valid JSON string`;
break;
}
}
if (ruleKey === 'not_regex') {
const regexPattern = ruleValue;
const regex = new RegExp(regexPattern);
if (regex.test(value)) {
errors[key] = `${key} must not satisfy the regex pattern: ${regexPattern}`;
break;
}
}
}
}
error(errors);
return errors;
}
const Input = ({ name, type, label, value, watch, error, className, required, placeholder, rule, checkRuleOnBlur, autoComplete, ...props }) => {
const id = useId();
const [inputError, setInputError] = useState({});
const [blur, setBlur] = useState(!checkRuleOnBlur);
const [eye, setEye] = useState(false);
const [inputType, setInputType] = useState(type);
const handleBlur = (e) => {
if (name && rule && checkRuleOnBlur) {
validate({ [name]: e.target.value }, setInputError, { [name]: rule });
setBlur(true);
}
else {
setBlur(false);
}
};
const handleChange = (e) => {
if (watch && name && type !== "submit") {
watch((prev) => ({
...prev,
[name]: e.target.value,
}));
}
if (rule) {
if (name && rule && !blur) {
validate({ [name]: e.target.value }, setInputError, { [name]: rule });
}
}
};
return (React.createElement("div", { className: `${Style.inputContainer} ${className} ${Object.keys(inputError).length > 0 && name && !checkRuleOnBlur ? Style.inputError : ""} ${error ? Style.inputError : ""}
${blur && name && inputError[name] ? Style.inputErrorBlur : ""}
`, key: id, ...props },
label && (React.createElement("label", { htmlFor: id },
label,
required && React.createElement("span", { className: Style.required }, "*"))),
React.createElement("input", { type: inputType, id: id, className: "text-input", name: name, value: value, onChange: handleChange, required: required, placeholder: placeholder, onBlur: handleBlur, onFocus: () => setBlur(false), autoComplete: autoComplete }),
error && React.createElement("span", { className: Style.error }, error),
React.createElement("span", { className: Style.error },
inputError && name && !checkRuleOnBlur ? inputError[name] : "",
blur && name && inputError[name] ? inputError[name] : "")));
};
const Password = ({ name, type, label, value, watch, error, className, required, placeholder, rule, checkRuleOnBlur, autoComplete, ...props }) => {
const id = useId();
const [inputError, setInputError] = useState({});
const [blur, setBlur] = useState(!checkRuleOnBlur);
const [eye, setEye] = useState(false);
const [inputType, setInputType] = useState(type);
const handleEye = () => {
setEye(!eye);
setInputType(!eye ? "text" : "password");
};
const handleBlur = (e) => {
if (name && rule && checkRuleOnBlur) {
validate({ [name]: e.target.value }, setInputError, { [name]: rule });
setBlur(true);
}
else {
setBlur(false);
}
};
const handleChange = (e) => {
if (watch && name && type !== "submit") {
watch((prev) => ({
...prev,
[name]: e.target.value,
}));
}
if (rule) {
if (name && rule && !blur) {
validate({ [name]: e.target.value }, setInputError, { [name]: rule });
}
}
};
return (React.createElement("div", { className: `${Style.inputContainer} ${className} ${Object.keys(inputError).length > 0 && name && !checkRuleOnBlur
? Style.inputError
: ""} ${error ? Style.inputError : ""}
${blur && name && inputError[name] ? Style.inputErrorBlur : ""}
`, key: id, ...props },
label && (React.createElement("label", { htmlFor: id },
label,
required && React.createElement("span", { className: Style.required }, "*"))),
React.createElement("div", { className: Style.passwordContainer },
React.createElement("input", { type: inputType, id: id, className: "text-input", name: name, value: value, onChange: handleChange, required: required, placeholder: placeholder, onBlur: handleBlur, onFocus: () => setBlur(false), autoComplete: autoComplete }),
React.createElement("span", { className: `${Style.eye} ${eye ? Style.active : ""}`, onClick: handleEye }, eye ? (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", fill: "currentColor", className: "bi bi-eye", viewBox: "0 0 16 16" },
React.createElement("path", { d: "M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8M1.173 8a13 13 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5s3.879 1.168 5.168 2.457A13 13 0 0 1 14.828 8q-.086.13-.195.288c-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5s-3.879-1.168-5.168-2.457A13 13 0 0 1 1.172 8z" }),
React.createElement("path", { d: "M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5M4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0" }))) : (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", fill: "currentColor", className: "bi bi-eye-slash", viewBox: "0 0 16 16" },
React.createElement("path", { d: "M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7 7 0 0 0-2.79.588l.77.771A6 6 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13 13 0 0 1 14.828 8q-.086.13-.195.288c-.335.48-.83 1.12-1.465 1.755q-.247.248-.517.486z" }),
React.createElement("path", { d: "M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829" }),
React.createElement("path", { d: "M3.35 5.47q-.27.24-.518.487A13 13 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7 7 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12z" }))))),
error && React.createElement("span", { className: Style.error }, error),
React.createElement("span", { className: Style.error },
inputError && name && !checkRuleOnBlur ? inputError[name] : "",
blur && name && inputError[name] ? inputError[name] : "")));
};
const Submit = ({ className, value, children, ...props }) => {
return (React.createElement(React.Fragment, null,
React.createElement("button", { type: "submit", ...props, className: `${Style.submit} ${className}` },
value,
children)));
};
export { Form, Heading, Input, Password, Submit, validate };