UNPKG

el-form-react

Version:

React form components and hooks powered by Zod validation

412 lines (408 loc) 12.7 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; 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 __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default")); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/hooks.ts var hooks_exports = {}; __export(hooks_exports, { useForm: () => useForm }); module.exports = __toCommonJS(hooks_exports); __reExport(hooks_exports, require("el-form-core"), module.exports); // src/useForm.ts var import_react = require("react"); var import_zod = require("zod"); var import_el_form_core2 = require("el-form-core"); // src/utils/index.ts var import_el_form_core = require("el-form-core"); function addArrayItemReact(obj, path, item) { const result = { ...obj }; const normalizedPath = path.replace(/\[(\d+)\]/g, ".$1"); const keys = normalizedPath.split(".").filter((key) => key !== ""); let current = result; for (let i = 0; i < keys.length - 1; i++) { const key = keys[i]; if (!isNaN(Number(key))) { if (Array.isArray(current)) { current[Number(key)] = Array.isArray(current[Number(key)]) ? [...current[Number(key)]] : { ...current[Number(key)] }; current = current[Number(key)]; } } else { if (typeof current[key] !== "object" || current[key] === null) { current[key] = {}; } else { current[key] = Array.isArray(current[key]) ? [...current[key]] : { ...current[key] }; } current = current[key]; } } const arrayKey = keys[keys.length - 1]; if (!isNaN(Number(arrayKey))) { if (Array.isArray(current)) { current = [...current]; current[Number(arrayKey)] = item; } } else { if (!Array.isArray(current[arrayKey])) { current[arrayKey] = []; } else { current[arrayKey] = [...current[arrayKey]]; } current[arrayKey].push(item); } return result; } // src/useForm.ts function useForm(options) { const { schema, initialValues = {}, validateOnChange = false, validateOnBlur = false } = options; const fieldRefs = (0, import_react.useRef)(/* @__PURE__ */ new Map()); const [formState, setFormState] = (0, import_react.useState)({ values: initialValues, errors: {}, touched: {}, isSubmitting: false, isValid: false, isDirty: false }); const validate = (0, import_react.useCallback)( (values) => { try { schema.parse(values); return { isValid: true, errors: {} }; } catch (error) { if (error instanceof import_zod.z.ZodError) { return { isValid: false, errors: (0, import_el_form_core2.parseZodErrors)(error) }; } return { isValid: false, errors: {} }; } }, [schema] ); const checkIsDirty = (0, import_react.useCallback)( (currentValues) => { return JSON.stringify(initialValues || {}) !== JSON.stringify(currentValues || {}); }, [initialValues] ); const checkFieldIsDirty = (0, import_react.useCallback)( (fieldName) => { const initialValue = initialValues[fieldName]; const currentValue = formState.values[fieldName]; return JSON.stringify(initialValue) !== JSON.stringify(currentValue); }, [initialValues, formState.values] ); const register = (0, import_react.useCallback)( (name) => { const fieldValue = name.includes(".") ? (0, import_el_form_core2.getNestedValue)(formState.values, name) : formState.values[name]; const isCheckbox = typeof fieldValue === "boolean"; const baseProps = { name, onChange: (e) => { const target = e.target; const value = target.type === "checkbox" ? target.checked : target.type === "number" ? target.value ? Number(target.value) : void 0 : target.value; setFormState((prev) => { const newValues = name.includes(".") ? (0, import_el_form_core2.setNestedValue)(prev.values, name, value) : { ...prev.values, [name]: value }; let newErrors = { ...prev.errors }; if (name.includes(".")) { const nestedError = (0, import_el_form_core2.getNestedValue)(newErrors, name); if (nestedError) { newErrors = (0, import_el_form_core2.setNestedValue)(newErrors, name, void 0); } } else { delete newErrors[name]; } if (validateOnChange) { const { errors } = validate(newValues); newErrors = errors; } return { ...prev, values: newValues, errors: newErrors, isDirty: checkIsDirty(newValues) }; }); }, onBlur: (_e) => { setFormState((prev) => { const newTouched = name.includes(".") ? (0, import_el_form_core2.setNestedValue)(prev.touched, name, true) : { ...prev.touched, [name]: true }; let newErrors = prev.errors; if (validateOnBlur) { const { errors } = validate(prev.values); newErrors = errors; } return { ...prev, touched: newTouched, errors: newErrors, isDirty: checkIsDirty(prev.values) }; }); } }; if (isCheckbox) { return { ...baseProps, checked: Boolean(fieldValue) }; } return { ...baseProps, value: fieldValue || "" }; }, [formState.values, validateOnChange, validateOnBlur, validate, checkIsDirty] ); const handleSubmit = (0, import_react.useCallback)( (onValid, onError) => { return (e) => { e.preventDefault(); setFormState((prev) => ({ ...prev, isSubmitting: true })); const { isValid, errors } = validate(formState.values); setFormState((prev) => ({ ...prev, errors, isValid, isSubmitting: false, isDirty: checkIsDirty(formState.values) })); if (isValid) { onValid(formState.values); } else { if (onError) { onError(errors); } } }; }, [formState.values, validate, checkIsDirty] ); const reset = (0, import_react.useCallback)( (options2) => { const newValues = options2?.values ?? initialValues; setFormState({ values: newValues, errors: options2?.keepErrors ? formState.errors : {}, touched: options2?.keepTouched ? formState.touched : {}, isSubmitting: false, isValid: false, isDirty: options2?.keepDirty ? formState.isDirty : false }); }, [initialValues, formState] ); const setValue = (0, import_react.useCallback)( (path, value) => { setFormState((prev) => { const newValues = (0, import_el_form_core2.setNestedValue)(prev.values, path, value); const { errors } = validate(newValues); return { ...prev, values: newValues, errors, isDirty: checkIsDirty(newValues) }; }); }, [validate, checkIsDirty] ); const addArrayItemHandler = (0, import_react.useCallback)( (path, item) => { setFormState((prev) => { const newValues = addArrayItemReact(prev.values, path, item); const { errors } = validate(newValues); return { ...prev, values: newValues, errors, isDirty: checkIsDirty(newValues) }; }); }, [validate, checkIsDirty] ); const removeArrayItemHandler = (0, import_react.useCallback)( (path, index) => { setFormState((prev) => { const newValues = (0, import_el_form_core2.removeArrayItem)(prev.values, path, index); const { errors } = validate(newValues); return { ...prev, values: newValues, errors, isDirty: checkIsDirty(newValues) }; }); }, [validate, checkIsDirty] ); const watch = (0, import_react.useCallback)( (nameOrNames) => { if (!nameOrNames) { return formState.values; } if (Array.isArray(nameOrNames)) { const result = {}; nameOrNames.forEach((name) => { result[name] = formState.values[name]; }); return result; } return formState.values[nameOrNames]; }, [formState.values] ); const isDirty = (0, import_react.useCallback)( (name) => { if (name) { return checkFieldIsDirty(name); } return formState.isDirty; }, [formState.isDirty, checkFieldIsDirty] ); const getFieldState = (0, import_react.useCallback)( (name) => { return { isDirty: checkFieldIsDirty(name), isTouched: !!formState.touched[name], error: formState.errors[name] }; }, [formState, checkFieldIsDirty] ); const getDirtyFields = (0, import_react.useCallback)(() => { const dirtyFields = {}; Object.keys(formState.values).forEach((key) => { const fieldName = key; if (checkFieldIsDirty(fieldName)) { dirtyFields[fieldName] = true; } }); return dirtyFields; }, [formState.values, checkFieldIsDirty]); const getTouchedFields = (0, import_react.useCallback)(() => { return { ...formState.touched }; }, [formState.touched]); const trigger = (0, import_react.useCallback)( async (nameOrNames) => { if (!nameOrNames) { const { isValid: isValid2 } = validate(formState.values); return isValid2; } if (Array.isArray(nameOrNames)) { const fieldsToValidate = {}; nameOrNames.forEach((name) => { fieldsToValidate[name] = formState.values[name]; }); const { isValid: isValid2 } = validate(fieldsToValidate); return isValid2; } const fieldToValidate = {}; fieldToValidate[nameOrNames] = formState.values[nameOrNames]; const { isValid } = validate(fieldToValidate); return isValid; }, [formState.values, validate] ); const clearErrors = (0, import_react.useCallback)((name) => { setFormState((prev) => { if (name) { const newErrors = { ...prev.errors }; delete newErrors[name]; return { ...prev, errors: newErrors }; } return { ...prev, errors: {} }; }); }, []); const setError = (0, import_react.useCallback)( (name, error) => { setFormState((prev) => ({ ...prev, errors: { ...prev.errors, [name]: error } })); }, [] ); const setFocus = (0, import_react.useCallback)( (name, options2) => { const fieldRef = fieldRefs.current.get(name); if (fieldRef) { fieldRef.focus(); if (options2?.shouldSelect && "select" in fieldRef) { fieldRef.select(); } } }, [] ); const resetField = (0, import_react.useCallback)( (name) => { setFormState((prev) => { const newValues = { ...prev.values }; newValues[name] = initialValues[name]; const newErrors = { ...prev.errors }; delete newErrors[name]; const newTouched = { ...prev.touched }; delete newTouched[name]; return { ...prev, values: newValues, errors: newErrors, touched: newTouched, isDirty: checkIsDirty(newValues) }; }); }, [initialValues, checkIsDirty] ); return { register, handleSubmit, formState, reset, setValue, watch, getFieldState, isDirty, getDirtyFields, getTouchedFields, trigger, clearErrors, setError, setFocus, addArrayItem: addArrayItemHandler, removeArrayItem: removeArrayItemHandler, resetField }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { useForm, ...require("el-form-core") }); //# sourceMappingURL=hooks.js.map