@@ -9,10 +9,9 @@ import UFormField from '@nuxt/ui/components/FormField.vue'
99import UInput from ' @nuxt/ui/components/Input.vue'
1010import UInputNumber from ' @nuxt/ui/components/InputNumber.vue'
1111import USelect from ' @nuxt/ui/components/Select.vue'
12- import defu from ' defu'
1312
13+ import defu from ' defu'
1414import { splitByCase , upperFirst } from ' scule'
15-
1615import { computed , reactive , ref , toRaw , useTemplateRef } from ' vue'
1716import * as z from ' zod'
1817
@@ -38,76 +37,74 @@ const shape = (props.schema as z.ZodObject<any>).shape
3837
3938const isButtonEnabled = computed (() => props .schema .safeParse (state ).success )
4039
41- type Output = InferOutput <T >
42-
43- const fields = Object .entries (shape ).map (([key , zodType ]) =>
44- getComponentForZodType (key , zodType ),
45- )
46-
47- function parseMeta(zodType : any , key : string ) {
48- const meta = typeof zodType .meta === ' function' ? zodType .meta () || {} : {}
49-
50- return {
51- label: meta .title ?? upperFirst (splitByCase (key ).join (' ' ).toLowerCase ()),
52- description: meta .description ,
53- class: meta .autoForm ?.floatRight ? ' flex items-center justify-between text-left' : ' ' ,
54- }
40+ interface ComponentDefinition {
41+ // FIXME: Try using proper types for components
42+ component: any
43+ componentProps? : Record <string , any >
5544}
5645
57- function getComponentForZodType(key : string , zodType : any ) {
58- let component = UInput as any
59- let componentProps = {}
60-
61- if (zodType instanceof z .ZodEmail ) {
62- componentProps = { type: ' email' }
63- }
64- else if (zodType instanceof z .ZodNumber ) {
65- component = UInputNumber
66- }
67- else if (zodType instanceof z .ZodBoolean ) {
68- component = UCheckbox
69- }
70- else if (zodType instanceof z .ZodEnum ) {
71- component = USelect
72- componentProps = { items: [Object .values (zodType .def .entries )] }
73- }
74- else if (zodType instanceof z .ZodArray ) {
46+ const COMPONENTS_MAP: Record <string , (key : string , zodType : any ) => ComponentDefinition | null > = {
47+ number : () => ({ component: UInputNumber }),
48+ string : () => ({ component: UInput }),
49+ boolean : () => ({ component: UCheckbox }),
50+ enum : (_key , zodType ) => ({
51+ component: USelect ,
52+ componentProps: {
53+ items: Object .values (zodType .def .entries ),
54+ },
55+ }),
56+ array : (key , zodType ) => {
7557 const sub_element = zodType .def .element
7658 if (sub_element instanceof z .ZodEnum ) {
77- const result = getComponentForZodType (key , zodType .unwrap ()) as any
78- result .props .multiple = true
79-
80- result .formField = {
81- ... result .formField ,
82- ... parseMeta (zodType , key ),
83- }
59+ const result = mapZodTypeToComponent (key , zodType .unwrap ()) as any
60+ result .componentProps .multiple = true
8461 return result
8562 }
86- }
87- else if ( zodType instanceof z . ZodDefault ) {
63+ },
64+ default : ( key , zodType ) => {
8865 (state as any )[key ] = zodType .def .defaultValue
89- return getComponentForZodType (key , zodType .unwrap ())
90- }
91- else if (zodType instanceof z .ZodString ) {
92- component = UInput
93- }
94- else {
95- console .warn (' Unknown zod type' , zodType .constructor .name )
96- }
66+ return mapZodTypeToComponent (key , zodType .unwrap ())
67+ },
68+ email : () => ({ component: UInput , componentProps: { type: ' email' } }),
69+ }
70+
71+ const fields = Object .entries (shape ).map (([key , zodType ]) => {
72+ const result = mapZodTypeToComponent (key , zodType )
73+ if (! result )
74+ return null
9775
98- const meta = parseMeta (zodType , key )
9976 return {
10077 key ,
10178 formField: {
10279 name: key ,
103- ... meta ,
80+ ... parseMeta ( zodType , key ) ,
10481 },
105- component ,
106- props: componentProps ,
82+ component: result .component ,
83+ props: result .componentProps ?? {},
84+ }
85+ }).filter ((field ): field is NonNullable <typeof field > => field != null )
86+
87+ function parseMeta(zodType : any , key : string ) {
88+ const meta = typeof zodType .meta === ' function' ? zodType .meta () || {} : {}
89+
90+ return {
91+ label: meta .title ?? upperFirst (splitByCase (key ).join (' ' ).toLowerCase ()),
92+ description: meta .description ,
93+ class: meta .autoForm ?.floatRight ? ' flex items-center justify-between text-left' : ' ' ,
94+ }
95+ }
96+
97+ function mapZodTypeToComponent(key : string , zodType : any ): ComponentDefinition | null {
98+ const zodTypeKey = zodType ._def .format ?? zodType ._def .type
99+ const component = COMPONENTS_MAP [zodTypeKey ]
100+ if (! component ) {
101+ console .warn (` Unsupported Zod type: ${zodTypeKey } ` )
102+ return null
107103 }
104+ return component (key , zodType )
108105}
109106
110- async function onSubmit(event : FormSubmitEvent <Output >) {
107+ async function onSubmit(event : FormSubmitEvent <InferOutput < T > >) {
111108 event .preventDefault ()
112109 loading .value = true
113110 try {
@@ -141,7 +138,7 @@ const submitButtonComponent = computed(() => {
141138 >
142139 <UFormField
143140 v-for =" field in fields"
144- :key =" field.formField. key"
141+ :key =" field.key"
145142 :ui =" { description: 'text-left' }"
146143 v-bind =" field.formField"
147144 >
0 commit comments