UNPKG

react-hook-formify

Version:

A smart wrapper around react-hook-form + zustand

221 lines (211 loc) 8.36 kB
"use strict"; 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); // index.ts var index_exports = {}; __export(index_exports, { Field: () => field_default, Form: () => form_default, useFormStore: () => useFormStore }); module.exports = __toCommonJS(index_exports); // src/components/form.tsx var import_react2 = require("react"); var import_react_hook_form = require("react-hook-form"); var import_react_i18next = require("react-i18next"); var import_yup = require("@hookform/resolvers/yup"); // ../../node_modules/zustand/esm/vanilla.mjs var createStoreImpl = (createState) => { let state; const listeners = /* @__PURE__ */ new Set(); const setState = (partial, replace) => { const nextState = typeof partial === "function" ? partial(state) : partial; if (!Object.is(nextState, state)) { const previousState = state; state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState); listeners.forEach((listener) => listener(state, previousState)); } }; const getState = () => state; const getInitialState = () => initialState; const subscribe = (listener) => { listeners.add(listener); return () => listeners.delete(listener); }; const api = { setState, getState, getInitialState, subscribe }; const initialState = state = createState(setState, getState, api); return api; }; var createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl; // ../../node_modules/zustand/esm/react.mjs var import_react = __toESM(require("react"), 1); var identity = (arg) => arg; function useStore(api, selector = identity) { const slice = import_react.default.useSyncExternalStore( api.subscribe, () => selector(api.getState()), () => selector(api.getInitialState()) ); import_react.default.useDebugValue(slice); return slice; } var createImpl = (createState) => { const api = createStore(createState); const useBoundStore = (selector) => useStore(api, selector); Object.assign(useBoundStore, api); return useBoundStore; }; var create = (createState) => createState ? createImpl(createState) : createImpl; // src/store/form.store.ts var useFormStore = create((set) => ({ formData: {}, setFormData: (key, data) => set((state) => ({ formData: { ...state.formData, [key]: data } })), resetForm: () => set({ formData: {} }) })); // src/utils/debounce.ts function debounce(func, delay) { let timer; return function(...args) { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, delay); }; } // src/utils/validation.ts var yup = __toESM(require("yup")); var generateSchema = (fields = [], t) => { const shape = {}; fields?.forEach((field) => { const getBaseValidator = () => { if (typeof field?.type === "function") { return field.type(yup); } switch (field.type) { case "email": return yup.string().email(t("validation.email")); case "object": return yup.object().typeError(t("validation.required")); case "array": return yup.array(); default: return yup[field.type || "string"](); } }; const applyRules = (schema, rules) => { if (rules.required) { schema = schema.required(t("validation.required")); } if (rules.min !== void 0) { schema = schema.min(rules.min, t("validation.min", { max: rules.min })); } if (rules.max !== void 0) { schema = schema.max(rules.max, t("validation.max", { max: rules.max })); } return schema; }; let baseValidator = getBaseValidator(); if (field?.when) { const whenFields = Array.isArray(field.when.fields) ? field.when.fields : [field.when.field]; const conditionFn = field.when.is; shape[field.name] = yup.mixed().when(whenFields, (...args) => { const values = args.slice(0, whenFields.length); const schema = getBaseValidator(); return conditionFn(...values) ? applyRules(schema, field.when.then || {}) : applyRules(schema, field.when.otherwise || {}); }); } else { shape[field.name] = applyRules(baseValidator, field); } }); return yup.object().shape(shape); }; // src/components/form.tsx var import_jsx_runtime = require("react/jsx-runtime"); var FormComponent = (0, import_react2.forwardRef)(({ name = "form", children, onSubmit, enableStore, isLoading = false, fields = [] }, ref) => { const { t } = (0, import_react_i18next.useTranslation)(); const initialValues = Object.fromEntries(fields.map((f) => [f.name, f.value ?? ""])); const methods = (0, import_react_hook_form.useForm)({ defaultValues: initialValues, values: initialValues, resolver: (0, import_yup.yupResolver)(generateSchema(fields, t)), mode: "onChange", shouldFocusError: false }); (0, import_react2.useEffect)(() => { if (!enableStore) return; const debouncedUpdate = debounce((values) => { useFormStore.getState().setFormData(name, values); }, 300); const subscription = methods.watch((values) => { debouncedUpdate(values); }); return () => { subscription.unsubscribe(); debouncedUpdate.cancel(); }; }, [enableStore, methods, methods.watch, name]); const handleSubmit = async (values) => { const isValid = await methods.trigger(); if (isValid) { const renderedValues = Object.fromEntries( fields.map((field) => { const rawValue = values[field.name]; const transformed = typeof field.onSubmitValue === "function" ? field.onSubmitValue(rawValue) : rawValue; return [field.name, transformed]; }) ); onSubmit?.({ values: renderedValues, methods }); } else { console.log("Form error"); } }; return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_hook_form.FormProvider, { ...methods, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", { ref, onSubmit: methods.handleSubmit(handleSubmit), children: typeof children === "function" ? children({ ...methods, isLoading, values: methods.watch(), errors: methods.formState.errors }) : children }) }); }); var form_default = FormComponent; // src/components/field.tsx var import_react_hook_form2 = require("react-hook-form"); var import_jsx_runtime2 = require("react/jsx-runtime"); var FieldComponent = ({ name, component, ...rest }) => { const { control, getFieldState } = (0, import_react_hook_form2.useFormContext)(); const Component = component || null; return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_hook_form2.Controller, { name, control, render: ({ field }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Component, { ...getFieldState(name), ...rest, ...field }) }); }; var field_default = FieldComponent; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Field, Form, useFormStore });