Skip to content

Commit 41252ac

Browse files
committed
refactor: simplify component mapping logic in AutoForm
1 parent 60c7c85 commit 41252ac

File tree

1 file changed

+54
-57
lines changed

1 file changed

+54
-57
lines changed

src/runtime/components/AutoForm.vue

Lines changed: 54 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ import UFormField from '@nuxt/ui/components/FormField.vue'
99
import UInput from '@nuxt/ui/components/Input.vue'
1010
import UInputNumber from '@nuxt/ui/components/InputNumber.vue'
1111
import USelect from '@nuxt/ui/components/Select.vue'
12-
import defu from 'defu'
1312
13+
import defu from 'defu'
1414
import { splitByCase, upperFirst } from 'scule'
15-
1615
import { computed, reactive, ref, toRaw, useTemplateRef } from 'vue'
1716
import * as z from 'zod'
1817
@@ -38,76 +37,74 @@ const shape = (props.schema as z.ZodObject<any>).shape
3837
3938
const 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

Comments
 (0)