UNPKG

el-form-react

Version:

React form components and hooks powered by Zod validation

948 lines (943 loc) 31.8 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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/components.ts var components_exports = {}; __export(components_exports, { AutoForm: () => AutoForm }); module.exports = __toCommonJS(components_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 }; } // src/AutoForm.tsx var import_zod2 = require("zod"); var import_jsx_runtime = require("react/jsx-runtime"); var DefaultErrorComponent = ({ errors, touched }) => { const errorEntries = Object.entries(errors).filter( ([field]) => touched[field] ); if (errorEntries.length === 0) return null; return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "el-form-error-summary", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u26A0\uFE0F Please fix the following errors:" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children: errorEntries.map(([field, error]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#ef4444", marginRight: "0.5rem" }, children: "\u2022" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { textTransform: "capitalize" }, children: [ field, ":" ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { marginLeft: "0.25rem" }, children: String(error) }) ] }, field)) }) ] }); }; var DefaultField = ({ name, label, type = "text", placeholder, value, onChange, onBlur, error, touched, options }) => { const fieldId = `field-${name}`; if (type === "checkbox") { return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-x-2", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "input", { id: fieldId, name, type: "checkbox", checked: !!value, onChange, onBlur, className: "h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: fieldId, className: "text-sm font-medium text-gray-900", children: label }) ] }); } const inputClasses = ` w-full px-3 py-2 border rounded-md text-sm text-gray-900 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${touched && error ? "border-red-500 focus:ring-red-500 focus:border-red-500" : "border-gray-300"} `.trim().replace(/\s+/g, " "); return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-1", children: [ label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "label", { htmlFor: fieldId, className: "block text-sm font-medium text-gray-700", children: label } ), type === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "textarea", { id: fieldId, name, value: value || "", onChange, onBlur, placeholder, className: `${inputClasses} resize-none`, rows: 4 } ) : type === "select" && options ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( "select", { id: fieldId, name, value: value || "", onChange, onBlur, className: inputClasses, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: placeholder || "Select an option" }), options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.value, children: option.label }, option.value)) ] } ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "input", { id: fieldId, name, type, value: value || "", onChange, onBlur, placeholder, className: inputClasses } ), touched && error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-red-500 text-xs mt-1", children: error }) ] }); }; var ArrayField = ({ fieldConfig, value = [], path, onAddItem, onRemoveItem, onValueChange, register, formState }) => { const arrayValue = Array.isArray(value) ? value : []; const createEmptyItem = () => { if (!fieldConfig.fields) return {}; if (fieldConfig.fields.length === 1 && fieldConfig.fields[0].name === "value") { const fieldType = fieldConfig.fields[0].type; if (fieldType === "number") { return 0; } else if (fieldType === "checkbox") { return false; } else { return ""; } } const emptyItem = {}; fieldConfig.fields.forEach((field) => { if (field.type === "array") { emptyItem[field.name] = []; } else if (field.type === "number") { emptyItem[field.name] = 0; } else { emptyItem[field.name] = ""; } }); return emptyItem; }; const handleAddItem = () => { onAddItem(path, createEmptyItem()); }; const handleRemoveItem = (index) => { onRemoveItem(path, index); }; const renderNestedField = (nestedFieldConfig, itemIndex, itemPath) => { if (nestedFieldConfig.name === "value") { const fieldValue2 = arrayValue[itemIndex] || ""; return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "space-y-1", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "input", { type: nestedFieldConfig.type || "text", value: fieldValue2, onChange: (e) => { const newValue = nestedFieldConfig.type === "number" ? e.target.value ? Number(e.target.value) : 0 : e.target.value; onValueChange(itemPath, newValue); }, placeholder: nestedFieldConfig.placeholder || "Enter value", className: "w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" } ) }, itemPath); } const fieldPath = `${itemPath}.${nestedFieldConfig.name}`; const fieldValue = arrayValue[itemIndex]?.[nestedFieldConfig.name] || ""; if (nestedFieldConfig.type === "array") { return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( ArrayField, { fieldConfig: nestedFieldConfig, value: fieldValue, path: fieldPath, onAddItem, onRemoveItem, onValueChange, register, formState }, fieldPath ); } return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-1", children: [ nestedFieldConfig.label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "block text-sm font-medium text-gray-700", children: nestedFieldConfig.label }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "input", { type: nestedFieldConfig.type || "text", value: fieldValue, onChange: (e) => { const newValue = nestedFieldConfig.type === "number" ? e.target.value ? Number(e.target.value) : 0 : e.target.value; onValueChange(fieldPath, newValue); }, placeholder: nestedFieldConfig.placeholder, className: "w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" } ) ] }, fieldPath); }; return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-3", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "block text-sm font-medium text-gray-700", children: fieldConfig.label || fieldConfig.name }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( "button", { type: "button", onClick: handleAddItem, className: "px-3 py-1 bg-green-600 text-white text-xs rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500", children: [ "+ Add ", fieldConfig.label || fieldConfig.name ] } ) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "space-y-4", children: arrayValue.map((_, index) => { const itemPath = `${path}[${index}]`; return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( "div", { className: "p-4 border border-gray-200 rounded-lg bg-gray-50", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between items-center mb-3", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h4", { className: "text-sm font-medium text-gray-700", children: [ fieldConfig.label || fieldConfig.name, " #", index + 1 ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "button", { type: "button", onClick: () => handleRemoveItem(index), className: "px-2 py-1 bg-red-600 text-white text-xs rounded hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500", children: "Remove" } ) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3", children: fieldConfig.fields?.map( (nestedField) => renderNestedField(nestedField, index, itemPath) ) }) ] }, index ); }) }), arrayValue.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "text-gray-500 text-sm italic text-center py-4", children: [ "No ", fieldConfig.label?.toLowerCase() || fieldConfig.name, ' added yet. Click "Add" to create one.' ] }) ] }); }; function generateFieldsFromSchema(schema) { if (!(schema instanceof import_zod2.z.ZodObject)) { return []; } const shape = schema.shape; const fields = []; for (const key in shape) { if (Object.prototype.hasOwnProperty.call(shape, key)) { const zodType = shape[key]; const typeName = zodType._def.typeName; const fieldConfig = { name: key, label: key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()), type: "text" // Default to text }; if (typeName === "ZodString") { const checks = zodType._def.checks || []; if (checks.some((c) => c.kind === "email")) { fieldConfig.type = "email"; } else if (checks.some((c) => c.kind === "url")) { fieldConfig.type = "url"; } } else if (typeName === "ZodNumber") { fieldConfig.type = "number"; } else if (typeName === "ZodBoolean") { fieldConfig.type = "checkbox"; } else if (typeName === "ZodEnum") { fieldConfig.type = "select"; fieldConfig.options = zodType._def.values.map((v) => ({ value: v, label: v })); } else if (typeName === "ZodDate") { fieldConfig.type = "date"; } else if (typeName === "ZodArray") { fieldConfig.type = "array"; const arrayElementType = zodType._def.type; if (arrayElementType instanceof import_zod2.z.ZodObject) { fieldConfig.fields = generateFieldsFromSchema(arrayElementType); } else { const elementTypeName = arrayElementType._def.typeName; let elementType = "text"; if (elementTypeName === "ZodString") { elementType = "text"; } else if (elementTypeName === "ZodNumber") { elementType = "number"; } else if (elementTypeName === "ZodBoolean") { elementType = "checkbox"; } fieldConfig.fields = [ { name: "value", type: elementType, label: "Value" } ]; } } fields.push(fieldConfig); } } return fields; } function mergeFields(autoFields, manualFields) { const manualFieldsMap = new Map( manualFields.map((field) => [field.name, field]) ); const mergedFields = autoFields.map((autoField) => { const manualField = manualFieldsMap.get(autoField.name); if (manualField) { return { ...autoField, ...manualField }; } return autoField; }); manualFields.forEach((manualField) => { if (!autoFields.some((autoField) => autoField.name === manualField.name)) { mergedFields.push(manualField); } }); return mergedFields; } function AutoForm({ schema, fields, initialValues = {}, layout = "flex", columns = 12, onSubmit, onError, children, customErrorComponent, componentMap }) { const formApi = useForm({ schema, initialValues, validateOnChange: true, validateOnBlur: true }); const { register, handleSubmit, formState, reset, setValue, addArrayItem, removeArrayItem: removeArrayItem3 } = formApi; const autoGeneratedFields = generateFieldsFromSchema(schema); const fieldsToRender = fields ? mergeFields(autoGeneratedFields, fields) : autoGeneratedFields; const ErrorComponent = customErrorComponent || DefaultErrorComponent; const renderField = (fieldConfig) => { const fieldName = fieldConfig.name; const getColSpanClass = (colSpan) => { const spanMap = { 1: "col-span-1", 2: "col-span-2", 3: "col-span-3", 4: "col-span-4", 5: "col-span-5", 6: "col-span-6", 7: "col-span-7", 8: "col-span-8", 9: "col-span-9", 10: "col-span-10", 11: "col-span-11", 12: "col-span-12" }; return spanMap[colSpan || 1]; }; const getFlexClass = (colSpan) => { const flexMap = { 1: "w-1/12", 2: "w-2/12", 3: "w-3/12", 4: "w-4/12", 5: "w-5/12", 6: "w-6/12", 7: "w-7/12", 8: "w-8/12", 9: "w-9/12", 10: "w-10/12", 11: "w-11/12", 12: "w-full" }; return flexMap[colSpan || 12]; }; const fieldContainerClasses = layout === "grid" ? getColSpanClass(fieldConfig.colSpan) : `flex-none ${getFlexClass(fieldConfig.colSpan)}`; if (fieldConfig.type === "array") { const fieldProps2 = register(String(fieldName)); const fieldValue2 = "value" in fieldProps2 ? fieldProps2.value : []; const arrayValue = Array.isArray(fieldValue2) ? fieldValue2 : []; return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: fieldContainerClasses, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( ArrayField, { fieldConfig, value: arrayValue, path: fieldConfig.name, onAddItem: addArrayItem, onRemoveItem: removeArrayItem3, onValueChange: setValue, register, formState } ) }, fieldConfig.name); } const fieldProps = register(String(fieldName)); const error = formState.errors[fieldName]; const touched = formState.touched[fieldName]; const fieldValue = "checked" in fieldProps ? fieldProps.checked : "value" in fieldProps ? fieldProps.value : void 0; const FieldComponent = fieldConfig.component || fieldConfig.type && componentMap?.[fieldConfig.type] || DefaultField; return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: fieldContainerClasses, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( FieldComponent, { name: fieldConfig.name, label: fieldConfig.label || fieldConfig.name, type: fieldConfig.type, placeholder: fieldConfig.placeholder, value: fieldValue, onChange: fieldProps.onChange, onBlur: fieldProps.onBlur, error, touched, options: fieldConfig.options } ) }, fieldConfig.name); }; const getGridClass = (cols) => { const gridMap = { 1: "grid-cols-1", 2: "grid-cols-2", 3: "grid-cols-3", 4: "grid-cols-4", 5: "grid-cols-5", 6: "grid-cols-6", 7: "grid-cols-7", 8: "grid-cols-8", 9: "grid-cols-9", 10: "grid-cols-10", 11: "grid-cols-11", 12: "grid-cols-12" }; return gridMap[cols]; }; const containerClasses = layout === "grid" ? `grid ${getGridClass(columns)} gap-4` : `flex flex-wrap gap-4`; const defaultForm = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( "form", { onSubmit: handleSubmit( (data) => onSubmit(data), onError || ((errors) => console.error("Form validation errors:", errors)) ), className: "w-full", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( ErrorComponent, { errors: formState.errors, touched: formState.touched } ), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: containerClasses, children: [ fieldsToRender.map(renderField), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( "div", { className: ` flex gap-3 mt-6 ${layout === "grid" ? "col-span-full" : "w-full"} `.trim().replace(/\s+/g, " "), children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "button", { type: "submit", disabled: formState.isSubmitting, className: "p-2 bg-blue-600 text-white rounded-md text-sm font-medium hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-60 disabled:cursor-not-allowed transition-colors duration-200 cursor-pointer", children: formState.isSubmitting ? "Submitting..." : "Submit" } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "button", { type: "button", onClick: () => reset(), className: "p-2 bg-gray-600 text-white rounded-md text-sm font-medium hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-colors duration-200 cursor-pointer", children: "Reset" } ) ] } ) ] }) ] } ); if (children) { return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( "form", { onSubmit: handleSubmit( (data) => onSubmit(data), onError || ((errors) => console.error("Form validation errors:", errors)) ), className: "w-full", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( ErrorComponent, { errors: formState.errors, touched: formState.touched } ), children(formApi), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: containerClasses, children: fieldsToRender.map(renderField) }) ] } ) }); } return defaultForm; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { AutoForm }); //# sourceMappingURL=components.js.map