UNPKG

@explita/daily-toolset-components

Version:

A lightweight and versatile collection of TypeScript utility functions and form components, inspired by ShadCN UI, designed for seamless everyday development. Enhance your Node.js, React, and Next.js projects with a well-structured suite of helpers for st

219 lines (218 loc) 9.41 kB
"use client"; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useForm = useForm; const daily_toolset_utils_1 = require("@explita/daily-toolset-utils"); const react_1 = require("react"); const validation_1 = require("@explita/daily-toolset-utils/validation"); // 4. Actual implementation /** * Creates a form hook for managing form state, validation, and persistence. * * @param props - Options for the form hook. * @param props.schema - The schema for the form, if using `zod`. * @param props.defaultValues - The default values for the form, if not using `schema`. * @param props.errors - The initial errors for the form. * @param props.mode - The mode of the form, either "controlled" or "uncontrolled". * @param props.errorParser - A function to parse errors. * @param props.persist - If true, the form will be persisted to local storage. * @param props.persistKey - The key to use for persisting the form to local storage. * * @returns An object with the following properties: * - `isValidated`: A boolean indicating whether the form is validated. * - `values`: The current values of the form. * - `errors`: The current errors of the form. * - `validateValue`: A function to validate a single value. * - `setValue`: A function to set the value of a single field. * - `getValue`: A function to get the value of a single field. * - `setValues`: A function to set the values of the form. * - `setErrors`: A function to set the errors of the form. * - `clearStorage`: A function to clear the form from local storage. */ function useForm(props = {}) { const previousErrorsRef = (0, react_1.useRef)({}); const previousDefaultValuesRef = (0, react_1.useRef)({}); const { schema = undefined, defaultValues = {}, errors = {}, mode = "controlled", errorParser, persist, persistKey, } = props; const [currentSchema, setCurrentSchema] = (0, react_1.useState)(schema); const [formValues, setFormValues] = (0, react_1.useState)({}); 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) => { 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, daily_toolset_utils_1.debounce)(async (name, inputValue) => { var _a, _b; if (!name || !currentSchema || mode === "uncontrolled") return; const result = await (0, validation_1.formValidation)(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]); const saveFormToLocalStorage = (0, react_1.useCallback)((0, daily_toolset_utils_1.debounce)(() => { if (persist && persistKey) { const savedValues = localStorage.getItem(persistKey); localStorage.setItem(persistKey, JSON.stringify({ ...JSON.parse(savedValues || "{}"), ...formValues, })); } }, 500), [formValues, persist, persistKey]); const loadFormFromLocalStorage = (0, react_1.useCallback)(() => { if (persist && persistKey) { const savedValues = localStorage.getItem(persistKey); if (savedValues) { setFormValues(JSON.parse(savedValues)); } } }, [persist, persistKey]); (0, react_1.useEffect)(() => { loadFormFromLocalStorage(); }, [loadFormFromLocalStorage]); (0, react_1.useEffect)(() => { saveFormToLocalStorage(); }, [formValues, saveFormToLocalStorage]); async function validateForm() { if (!currentSchema || mode === "uncontrolled") return { isValidated: false, formValues }; const result = await (0, validation_1.formValidation)(currentSchema, formValues); // console.log(result.errorData.errors); return { isValidated: result.success, formValues: result.data, formErrors: !result.success ? result.errors : undefined, }; } (0, react_1.useEffect)(() => { const handler = (0, daily_toolset_utils_1.debounce)(() => { validateForm().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(defaultValues); previousDefaultValuesRef.current = defaultValues; // Update the ref to the new state if (persist && persistKey) { const savedValues = localStorage.getItem(persistKey); localStorage.setItem(persistKey, JSON.stringify({ ...JSON.parse(savedValues || "{}"), ...defaultValues, })); } } } }, [defaultValues, persist, 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]); function reset() { setFormValues({}); setFormErrors({}); if (!persistKey) return; localStorage.removeItem(persistKey); } const combinedFormValues = (0, react_1.useMemo)(() => ({ ...formValues, ...validatedFormValues, }), [validatedFormValues, formValues]); function handleSubmit(onValid) { return async (event) => { if (event) event.preventDefault(); if (!currentSchema) { return onValid(formValues); } const validate = await validateForm(); if (!validate.isValidated) { setIsValidated(false); setErrors(validate.formErrors); return; } onValid(validate.formValues); }; } return { isValidated, values: combinedFormValues, errors: formErrors, setSchema, validateValue, setValue, getValue: (name) => combinedFormValues[name], setValues, setErrors, reset, handleSubmit, }; }