UNPKG

zod-auto-form

Version:

Auto-generate typed React forms from Zod schemas

3 lines (2 loc) 11.7 kB
"use strict";var e=require("react/jsx-runtime"),r=require("react"),t=require("zod"),a=require("zod-helper"),n=require("@tanstack/react-form");const o={Input:()=>null,Select:()=>null,MultiSelect:()=>null,RadioGroup:()=>null,NumberInput:()=>null,Checkbox:()=>null,Switch:()=>null,TextArea:()=>null,DateTimePicker:()=>null,ObjectWrapper:()=>null,ArrayWrapper:()=>null,Form:()=>null,SubmitButton:()=>null,GroupedFieldsWrapper:()=>null,ArrayInputFieldWrapper:()=>null,arrayString:()=>null,arrayNumber:()=>null,arrayBoolean:()=>null,arrayDate:()=>null},i=r.createContext(o),useComponentContext=()=>r.useContext(i);function GroupedFieldsWrapper({children:r,groupName:t}){const{GroupedFieldsWrapper:a}=useComponentContext();return e.jsx(a,{groupName:t,children:r})}const generateRandomKey=()=>"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID():Math.random().toString(36).substring(2,15)+Math.random().toString(36).substring(2,15);function generateEmptyData(e){const r={};for(const t of e)switch(t.type){case"string":r[t.name]=t.defaultValue||"";break;case"boolean":r[t.name]=t.defaultValue||!1;break;case"number":r[t.name]=Number(t.defaultValue??0)||0;break;case"object":r[t.name]=generateEmptyData(t.fields||[]);break;case"array":r[t.name]=t.defaultValue||[];break;default:r[t.name]=t.defaultValue||null}return r}function getArrayFieldError(e,r,t){if(!Array.isArray(e))return;const a=`${r}[${t}]`,findErrorDeep=e=>{if("object"==typeof e&&null!==e)for(const r in e){if(r===a){const t=e[r];if(Array.isArray(t)&&t[0]?.message)return t[0].message}const t=e[r],n=findErrorDeep(t);if(n)return n}};for(const r of e){const e=findErrorDeep(r);if(e)return e}}const{fieldContext:l,formContext:s,useFieldContext:u,useFormContext:d}=n.createFormHookContexts(),c={FieldComponent:function GenericFormField({componentKey:r,...t}){const a=u(),n=useComponentContext()[r],o=a.state.meta.isTouched&&a.state.meta.errors.length>0;return e.jsx(n,{id:a.name,onChange:a.handleChange,value:a.state.value,errorMessages:a.state.meta.errors,isError:o,onBlur:a.handleBlur,...t})},ArrayField:function ArrayFieldForArrayOfObjects({children:e}){const t=u(),[a,n]=r.useState([]),o=r.useRef([]),i=r.useRef([]);return r.useEffect((()=>{i.current=a}),[a]),r.useEffect((()=>{const e=t.state.value;if(!Array.isArray(e))return;if(JSON.stringify(e)===JSON.stringify(o.current))return;const r=e.map(((e,r)=>{const t=i.current[r],a=t?.j1sjhghgtytfjiu971||generateRandomKey();return"object"!=typeof e||null===e||e instanceof Date?{j1sjhghgtytfjiu971:a,value:e}:{j1sjhghgtytfjiu971:a,...e}}));n(r),o.current=e}),[t.state.value]),e({fields:a,append:e=>{const r=generateEmptyData(e),i="object"!=typeof r||null===r||r instanceof Date?{j1sjhghgtytfjiu971:generateRandomKey(),value:r}:{j1sjhghgtytfjiu971:generateRandomKey(),...r},l=[...a,i];n(l);const s=l.map((e=>"value"in e?e.value:e));t.handleChange(s),o.current=s},remove:e=>{const r=[...a];r.splice(e,1),n(r);const i=r.map((e=>"value"in e?e.value:e));t.handleChange(i),o.current=i}})},ArrayFieldForArrayOfStrings:function ArrayFieldForArrayOfStrings({children:e}){const t=u(),[a,n]=r.useState([]);return r.useEffect((()=>{if(Array.isArray(t.state.value)){const e=t.state.value.map((e=>"object"!=typeof e||null===e||e instanceof Date?{j1sjhghgtytfjiu971:generateRandomKey(),value:e}:{j1sjhghgtytfjiu971:generateRandomKey(),...e}));n(e)}}),[t.state.value]),e({fields:a,append:e=>{const r="object"!=typeof e||null===e||e instanceof Date?{j1sjhghgtytfjiu971:generateRandomKey(),value:e}:{j1sjhghgtytfjiu971:generateRandomKey(),...e},o=[...a,r];n(o),t.handleChange(o.map((e=>"value"in e?e.value:e)))},remove:e=>{const r=[...a];r.splice(e,1),n(r),t.handleChange(r.map((e=>"value"in e?e.value:e)))}})},ObjectWrapper:function ObjectWrapper({children:r,title:t,description:a,label:n,required:o}){const{ObjectWrapper:i}=useComponentContext();return e.jsx(i,{title:t,description:a,label:n,required:o,children:r})},ArrayWrapper:function ArrayWrapper({children:r,label:t,title:a,description:n,onAddField:o,onRemoveField:i}){const{ArrayWrapper:l}=useComponentContext();return e.jsx(l,{label:t,title:a,description:n,onAddField:o,onRemoveField:i,children:r})},GroupedFieldsWrapper:GroupedFieldsWrapper,ArrayInputFieldWrapper:function ArrayInputFieldWrapper({label:t,title:a,description:n,onAddField:o,onRemoveField:i,disabled:l,placeholder:s,fields:d,componentKey:c,all_errors:p,path:m}){const f=u(),[y,h]=r.useState([]),[g,b]=r.useState({});r.useEffect((()=>{Array.isArray(f.state.value)&&h(f.state.value)}),[f.state.value]);const{ArrayInputFieldWrapper:F,...j}=useComponentContext(),A=j[c],getFieldValue=(e,r,t)=>{let a;return a=void 0!==e?.value?e.value:t[r],a instanceof Date||void 0!==a?a:""};return r.useEffect((()=>{p?.form?.errors&&b({})}),[p?.form?.errors]),e.jsx(F,{onAddField:o,onRemoveField:i,description:n,label:t,title:a,children:d.map(((r,o)=>{const i=getArrayFieldError(p?.form?.errors,m??"",o)&&!g[o];return e.jsx(A,{id:r.id,value:getFieldValue(r,o,y),onChange:e=>{((e,r)=>{const t=[...y];t[e]=r,h(t),f.handleChange(t)})(o,e),b((e=>({...e,[o]:!0})))},onBlur:()=>{f.handleChange(y)},errorMessages:i?[{message:getArrayFieldError(p?.form?.errors,m??"",o)||""}]:[],isError:!!i,label:t,title:a,description:n,disabled:l,placeholder:s},r.id??o)}))})}},{useAppForm:p,withForm:m}=n.createFormHook({fieldComponents:c,formComponents:{},fieldContext:l,formContext:s}),shouldApplyRule=(e,r)=>{const t=e.sourceFields.map((e=>{return t=r,a=e,(Array.isArray(a)?a:a.replace(/\.\[/g,"[").replace(/\[(\d+)\]/g,".$1").split(".").filter(Boolean)).reduce(((e,r)=>{if(null!=e&&"object"==typeof e){if(Array.isArray(e)){const t=Number(r);return Number.isInteger(t)&&t>=0&&t<e.length?e[t]:void 0}if(r in e)return e[r]}}),t);var t,a})),a=Object.fromEntries(e.sourceFields.map(((e,r)=>[Array.isArray(e)?e.join("."):e,t[r]])));return e.when(a)},cleanPath=e=>e.replace(/\.\[/g,"[").replace(/\[(\d+)\]/g,".$1"),mergeDefaultValues=(e,r)=>{const t={...r};for(const[r,a]of Object.entries(e))a&&"object"==typeof a&&!Array.isArray(a)&&t[r]&&"object"==typeof t[r]&&!Array.isArray(t[r])?t[r]=mergeDefaultValues(a,t[r]):t[r]=a;return t};function removeNullValues(e){if(Array.isArray(e)){return e.map((e=>removeNullValues(e))).filter((e=>null!=e))}if(null!=e&&"object"==typeof e){const r=Object.entries(e).filter((([,e])=>null!=e)).map((([e,r])=>[e,removeNullValues(r)]));return Array.isArray(e)?[]:Object.fromEntries(r)}return e}const f=Object.getPrototypeOf((async()=>{})).constructor;function useFormData({schema:e,changePositionOfFields:n,fieldTypeOverrides:o,groupByZodKeys:i,visibilityRules:l,defaultValues:s,onSubmit:u,onSubmitError:d,onValuesChange:c,isRemoveNullValues:m}){const[y,h]=r.useState(0),[g,b]=r.useState(!1),F=r.useRef(!1),j=r.useMemo((()=>{const r=new a.ZodFormClassed(e);return n&&r.changeJsonFieldPositions(n),o&&r.overrideFieldTypes(o),i&&r.groupByZodKeys(i),r}),[e,o,n,i]),A=r.useMemo((()=>mergeDefaultValues(s||{},j.getZodDefaultValues()||{})),[s,j]),v=r.useCallback((r=>{b(!0);try{if(!u)throw new Error("onSubmit is required");F.current=!0;let t=r.value;m&&(t=removeNullValues(t));const a=e.parse(t);return!function isAsync(e){return e instanceof f}(u)?(u?.(a),b(!1)):u(a).then((()=>{b(!1)})).catch((e=>{b(!1)})),a}catch(e){throw e instanceof t.z.ZodError&&d?.(e,a.extractZodErrorMessages(e.errors),r.value),b(!1),e}}),[e,u,d,m]),S=p({defaultState:{values:A},onSubmit:v,onSubmitInvalid:e=>{d?.(e.formApi.state.errors,a.extractZodErrorMessages(e.formApi.state.errors),e.value)},validators:{onSubmit:e,onChange:e=>{l?.length&&h((e=>e+1)),c&&c(e.value)}}}),x=r.useCallback(((e,r)=>{if(!l?.length)return{hidden:!1,disabled:!1};const t=l.filter((r=>Array.isArray(r.targetField)?r.targetField.some((r=>cleanPath(r)===cleanPath(e))):cleanPath(r.targetField)===cleanPath(e)));if(!t.length)return{hidden:!1,disabled:!1};const a=t.some((e=>"hide"===e.type&&shouldApplyRule(e,r))),n=t.some((e=>"disable"===e.type&&shouldApplyRule(e,r))),o=t.some((e=>"show"===e.type&&shouldApplyRule(e,r)));return{hidden:a||!o&&t.some((e=>"show"===e.type)),disabled:n}}),[l]);return{zodFormData:j,formVersion:y,setFormVersion:h,mergedDefaultValues:A,formSubmittedRef:F,handleSubmit:v,appForm:S,getFieldVisibilityState:x,isLoading:g,all_errors:S.getAllErrors()}}function getComponentKey(e){switch(e){case"string":default:return"Input";case"number":return"NumberInput";case"boolean":return"Checkbox";case"select":return"Select";case"multi-select":return"MultiSelect";case"date":return"DateTimePicker";case"textarea":return"TextArea";case"switch":return"Switch";case"radio":return"RadioGroup";case"array-string":return"arrayString";case"array-number":return"arrayNumber";case"array-boolean":return"arrayBoolean";case"array-date":return"arrayDate"}}const RenderField=({field:t,fieldPath:a,getFieldVisibilityState:n,form:o,all_errors:i})=>{const l=a?`${a}.${t.name}`:t.name,{hidden:s,disabled:u}=n(l,o.state.values);if(s)return null;const d={label:t.label,placeholder:t.placeholder,title:t.title,description:t.description,disabled:u,required:t.required,selectOptions:t?.selectOptions};return e.jsx(o.AppField,{name:l,children:a=>{const s=e.jsx(a.FieldComponent,{componentKey:getComponentKey(t.type),...d,..."multi-select"===t.type?{value:Array.isArray(a.state.value)?a.state.value:[]}:{}});return"object"===t.type?e.jsx(a.ObjectWrapper,{label:t.label,title:t.title,description:t.description,required:t.required,children:t.fields.map((e=>RenderField({field:e,fieldPath:l,getFieldVisibilityState:n,form:o,all_errors:i})))}):"array"===t.type?e.jsx(a.ArrayField,{children:({fields:s,append:u,remove:d})=>e.jsx(a.ArrayWrapper,{label:t.label,title:t.title,description:t.description,onAddField:()=>{u(t.fields)},onRemoveField:e=>d(e),children:s.map(((a,s)=>e.jsx(r.Fragment,{children:t.fields?.map((e=>RenderField({field:e,fieldPath:`${l}[${s}]`,getFieldVisibilityState:n,form:o,all_errors:i})))},a.j1sjhghgtytfjiu971)))})}):t.type.includes("array-")?e.jsx(a.ArrayFieldForArrayOfStrings,{children:({fields:r,append:n,remove:o})=>e.jsx(a.ArrayInputFieldWrapper,{path:l,name:t.name,componentKey:getComponentKey(t.type),label:t.label,title:t.title,description:t.description,disabled:u,required:t.required,placeholder:t.placeholder,selectOptions:t?.selectOptions,all_errors:i,onAddField:()=>n({value:"array-date"===t.type?new Date:"array-boolean"!==t.type&&("array-string"===t.type?"":"array-number"===t.type?0:"")}),onRemoveField:e=>o(e),fields:r})}):s}},t.name)};exports.AutoForm=function AutoForm(r){const{zodFormData:t,appForm:a,getFieldVisibilityState:n,isLoading:o,all_errors:i}=useFormData(r),{SubmitButton:l,Form:s}=useComponentContext();if(!s)throw new Error("Please provide a SubmitButton and Form component and all the components are required");return t.getGroupedFields&&t.getGroupedFields.length>0?e.jsxs(s,{formId:r.formId,onSubmit:e=>{e.preventDefault(),a.handleSubmit()},children:[t.getGroupedFields.map((r=>e.jsx(GroupedFieldsWrapper,{groupName:r.name,children:r.fields.map((e=>RenderField({field:e,fieldPath:"",getFieldVisibilityState:n,form:a,all_errors:i})))},r.name))),!r.hideSubmitButton&&e.jsx(l,{isLoading:r.isLoadingButton||o,formId:r.formId,children:r?.submitButtonText||"Submit"})]}):e.jsxs(s,{formId:r.formId,onSubmit:e=>{e.preventDefault(),a.handleSubmit()},children:[t.getInputFields.map((r=>e.jsx(RenderField,{field:r,fieldPath:"",getFieldVisibilityState:n,form:a,all_errors:i},r.name))),!r.hideSubmitButton&&e.jsx(l,{isLoading:r.isLoadingButton||o,formId:r.formId,children:r?.submitButtonText||"Submit"})]})},exports.AutoFormComponentsProvider=({children:r,components:t})=>{const a={...o,...t};return e.jsx(i.Provider,{value:a,children:r})},exports.ComponentRegistry=e=>e; //# sourceMappingURL=index.js.map