UNPKG

@explita/daily-toolset-form

Version:

A lightweight form toolkit for React built with developer ergonomics in mind. Includes a flexible Form component, useForm, useField, and useFormContext hooks for managing form state and validation with ease. Designed to simplify complex forms while remain

253 lines (252 loc) 9.75 kB
"use client"; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useForm = useForm; const react_1 = require("react"); const utils_1 = require("../utils"); const utils_2 = require("../lib/utils"); function useForm(props = {} //@ts-ignore ) { const previousErrorsRef = (0, react_1.useRef)({}); const previousDefaultValuesRef = (0, react_1.useRef)({}); const { schema, defaultValues = {}, errors = {}, mode = "controlled", resetOnSuccess = false, errorParser, // persist, persistKey, onSubmit, onSuccess, onError, } = props; const [currentSchema, setCurrentSchema] = (0, react_1.useState)(schema); const generatePlaceholders = (0, react_1.useMemo)(() => { if (mode === "controlled") { return { ...(0, utils_2.createEmptyValues)(currentSchema), ...defaultValues, }; } return {}; }, [mode, currentSchema, defaultValues]); const [formValues, setFormValues] = (0, react_1.useState)(generatePlaceholders); const [formErrors, setFormErrors] = (0, react_1.useState)({}); const [isValidated, setIsValidated] = (0, react_1.useState)(false); const [validatedFormValues, setValidatedFormValues] = (0, react_1.useState)(defaultValues); const setSchema = (0, react_1.useCallback)((newSchema) => { setCurrentSchema(newSchema); }, [setCurrentSchema]); (0, react_1.useEffect)(() => { setCurrentSchema(schema); }, [schema]); const parseFormErrors = (0, react_1.useCallback)((errors) => { // @ts-ignore setFormErrors((prev) => { const newErrors = { ...errors }; if (errorParser && typeof errorParser === "function") { return Object.keys(newErrors).reduce((acc, key) => { var _a, _b; acc[key] = (_b = errorParser((_a = newErrors[key]) !== null && _a !== void 0 ? _a : "")) !== null && _b !== void 0 ? _b : ""; return acc; }, {}); } return newErrors; }); }, [errorParser]); const setValue = (0, react_1.useCallback)((name, value) => { if (mode === "uncontrolled" || !name) return; setFormValues((prev) => ({ ...prev, [name]: value, })); }, [mode]); const setValues = (0, react_1.useCallback)((values, options) => { setFormValues((prev) => ({ ...((options === null || options === void 0 ? void 0 : options.overwrite) ? {} : prev), ...values, })); }, []); const setErrors = (0, react_1.useCallback)((errors) => { parseFormErrors(errors); }, [parseFormErrors]); const validateValue = (0, react_1.useCallback)((0, utils_1.debounce)(async (name, inputValue) => { var _a, _b; if (!name || !currentSchema || mode === "uncontrolled") return; const result = await (0, utils_1.validateForm)(currentSchema, { [name]: (_a = inputValue === null || inputValue === void 0 ? void 0 : inputValue.toString()) !== null && _a !== void 0 ? _a : "", }); if (!result.success) { const fieldError = (_b = result.errors[name]) !== null && _b !== void 0 ? _b : ""; setFormErrors((prev) => ({ ...prev, [name]: errorParser ? errorParser(fieldError) : fieldError, })); } else { setFormErrors((prev) => ({ ...prev, [name]: undefined })); } }, 200), [currentSchema, mode, errorParser]); (0, react_1.useEffect)(() => { if (persistKey) { const savedValues = (0, utils_1.readDraft)(persistKey); if (savedValues) { setFormValues({ ...generatePlaceholders, ...savedValues }); } } }, [persistKey]); (0, react_1.useEffect)(() => { if (persistKey) { const savedValues = (0, utils_1.readDraft)(persistKey); (0, utils_1.writeDraft)({ ...(savedValues || {}), ...formValues, }, persistKey); } }, [formValues]); async function formValidation() { if (!currentSchema || mode === "uncontrolled") return { isValidated: false, formValues }; const result = await (0, utils_1.validateForm)(currentSchema, formValues); return { isValidated: result.success, formValues: result.data, formErrors: !result.success ? result.errors : undefined, }; } (0, react_1.useEffect)(() => { const handler = (0, utils_1.debounce)(() => { formValidation().then(({ isValidated, formValues }) => { setIsValidated(isValidated); setValidatedFormValues(formValues); }); }, 200); handler(); return () => handler.cancel(); }, [formValues, currentSchema, mode]); (0, react_1.useEffect)(() => { if (defaultValues) { const previousDefaultValues = previousDefaultValuesRef.current; const valuesEqual = Object.keys(previousDefaultValues).length === Object.keys(defaultValues).length && Object.keys(defaultValues).every((key) => previousDefaultValues[key] === defaultValues[key]); if (!valuesEqual) { setFormValues(generatePlaceholders); previousDefaultValuesRef.current = defaultValues; // Update the ref to the new state if (persistKey) { const savedValues = (0, utils_1.readDraft)(persistKey); (0, utils_1.writeDraft)({ ...(savedValues || {}), ...defaultValues, }, persistKey); } } } }, [defaultValues, persistKey]); (0, react_1.useEffect)(() => { if (errors) { if (Object.keys(errors).length === 0) { // Only clear errors if they are different from the previous state if (Object.keys(previousErrorsRef.current).length > 0) { setFormErrors({}); previousErrorsRef.current = {}; // Update the ref to the new state } } else { const hasErrorsChanged = Object.keys(errors).length !== Object.keys(previousErrorsRef.current).length || Object.keys(errors).some((key) => previousErrorsRef.current[key] !== errors[key]); if (hasErrorsChanged) { parseFormErrors(errors); previousErrorsRef.current = errors; // Update the ref to the new state } } } }, [errors]); const combinedFormValues = (0, react_1.useMemo)(() => ({ ...formValues, ...validatedFormValues, }), [validatedFormValues, formValues]); function register(name) { return { name, id: name === null || name === void 0 ? void 0 : name.toString(), defaultValue: name ? combinedFormValues[name] : undefined, onChange: (e) => { setValue(name, e.currentTarget.value); validateValue(name, e.currentTarget.value); }, onInput: (e) => { setValue(name, e.currentTarget.value); validateValue(name, e.currentTarget.value); }, onBlur: () => { // future: setTouched(name); }, }; } function reset() { setFormValues(generatePlaceholders); setFormErrors({}); if (persistKey) (0, utils_1.deleteDraft)(persistKey); } function handleSubmit(onValid) { return async (event) => { if (event) event.preventDefault(); if (!currentSchema) { return onValid(formValues); } const validate = await formValidation(); if (!validate.isValidated) { setIsValidated(false); setErrors(validate.formErrors); return; } onValid(validate.formValues); }; } const [state, formAction, isSubmitting] = (0, react_1.useActionState)(async (prev, formData) => { if (currentSchema) { const validate = await formValidation(); if (!validate.isValidated) { setIsValidated(false); setErrors(validate.formErrors); return; } } if (!onSubmit) return; try { const res = await onSubmit(combinedFormValues, formData); onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(res, { reset, values: combinedFormValues, formData, }); setErrors({}); if (resetOnSuccess) reset(); return res; } catch (error) { onError === null || onError === void 0 ? void 0 : onError(error, { setErrors, setValues }); return error; } }, undefined); return { isValidated, values: combinedFormValues, errors: formErrors, setSchema, validateValue, setValue, getValue: (name) => combinedFormValues[name], setValues, setErrors, reset, handleSubmit, register, // @ts-ignore errorParser, formAction: onSubmit ? formAction : undefined, isSubmitting, state: state, }; }