Skip to content

Commit 20f77e3

Browse files
committed
refactor: move COMPONENTS_MAP to a separate file
1 parent 93a8de4 commit 20f77e3

File tree

4 files changed

+87
-66
lines changed

4 files changed

+87
-66
lines changed

docs/content/2.customization/1.config.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,26 +44,31 @@ You can do this by passing a `config` prop to the `<AutoForm>` component:
4444

4545
### `components`
4646

47-
When a field in a form is rendered, the module automatically generates key
48-
```ts
49-
zodType._def.format ?? zodType._def.type
50-
```
51-
52-
This key is used to look up the component to render the field.
53-
You can override the default components used for rendering fields by specifying a mapping in the `components` option.
54-
5547
::caution
5648
If you use string to define the component it **must** be [globally registered](https://nuxt.com/docs/4.x/guide/directory-structure/app/components#dynamic-components) to work.
5749
To do this, name your component with the `.global.vue` suffix, or place it in `components/global/` directory.
5850
::
5951

52+
This is one of the most important configuration options.
53+
It allows you to specify which Vue component should be used to render each field type in the form.
54+
55+
When a field in a form is rendered, the module automatically generates a key:
56+
```ts
57+
zodType._def.format ?? zodType._def.type
58+
```
59+
60+
This key is used to look up the component to render the field from the `components` mapping.
61+
By providing a custom mapping you can override or add support for additional field types.
62+
6063
```ts-type
6164
components: {
6265
email: () => ({ component: 'UInput', componentProps: { type: 'email' } }),
6366
}
6467
```
6568

66-
For default values consult the source code.
69+
::tip
70+
For default implementation and inspiration consult the [source code](https://github.com/Norbiros/nuxt-auto-form/tree/master/src/utils/components_map.ts).
71+
::
6772

6873
### `submit`
6974

src/runtime/components/AutoForm.vue

Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
<script setup lang="ts" generic="T extends z.ZodObject<any>">
22
import type { FormSubmitEvent, InferInput, InferOutput } from '@nuxt/ui'
3-
import type { AutoFormConfig, ComponentsMap } from '../types'
3+
import type * as z from 'zod'
4+
import type { AutoFormConfig } from '../types'
45
import { useAppConfig } from '#app'
56
import UButton from '@nuxt/ui/components/Button.vue'
6-
import UCheckbox from '@nuxt/ui/components/Checkbox.vue'
77
import UForm from '@nuxt/ui/components/Form.vue'
88
import UFormField from '@nuxt/ui/components/FormField.vue'
9-
import UInput from '@nuxt/ui/components/Input.vue'
10-
import UInputNumber from '@nuxt/ui/components/InputNumber.vue'
11-
import USelect from '@nuxt/ui/components/Select.vue'
129
1310
import defu from 'defu'
1411
import { splitByCase, upperFirst } from 'scule'
1512
import { computed, reactive, ref, toRaw, useTemplateRef } from 'vue'
16-
import * as z from 'zod'
13+
import { COMPONENTS_MAP, mapZodTypeToComponent } from '../../utils/components_map'
1714
1815
const props = withDefaults(defineProps<{
1916
schema: T
@@ -37,45 +34,6 @@ const shape = (props.schema as z.ZodObject<any>).shape
3734
3835
const isButtonDisabled = computed(() => !props.schema.safeParse(state).success)
3936
40-
interface ComponentDefinition {
41-
// FIXME: Try using proper types for components
42-
component: any
43-
componentProps?: Record<string, any>
44-
}
45-
46-
const COMPONENTS_MAP: ComponentsMap = {
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) => {
57-
const innerType = zodType.def.element
58-
if (innerType instanceof z.ZodEnum) {
59-
const result = mapZodTypeToComponent(key, zodType.unwrap()) as any
60-
result.componentProps.multiple = true
61-
return result
62-
}
63-
},
64-
default: (key, zodType) => {
65-
(state as any)[key] = zodType.def.defaultValue
66-
return mapZodTypeToComponent(key, zodType.unwrap())
67-
},
68-
email: (key, _) => {
69-
const stringComponent = mapZodTypeToComponent(key, 'string')!
70-
71-
return defu({
72-
componentProps: {
73-
type: 'email',
74-
},
75-
}, stringComponent)
76-
},
77-
}
78-
7937
const defaults: Partial<AutoFormConfig> = {
8038
components: COMPONENTS_MAP,
8139
}
@@ -85,7 +43,7 @@ const appConfig = computed<AutoFormConfig>(() => {
8543
})
8644
8745
const fields = Object.entries(shape).map(([key, zodType]: [string, any]) => {
88-
const result = mapZodTypeToComponent(key, zodType)
46+
const result = mapZodTypeToComponent(key, zodType, appConfig.value, state)
8947
if (!result)
9048
return null
9149
@@ -113,16 +71,6 @@ function parseMeta(meta: any, key: string) {
11371
}
11472
}
11573
116-
function mapZodTypeToComponent(key: string, zodType: any): ComponentDefinition | null {
117-
const zodTypeKey = zodType._def.format ?? zodType._def.type
118-
const component = appConfig.value.components?.[zodTypeKey]
119-
if (!component) {
120-
console.warn(`Unsupported Zod type: ${zodTypeKey}`)
121-
return null
122-
}
123-
return component(key, zodType)
124-
}
125-
12674
async function onSubmit(event: FormSubmitEvent<InferOutput<T>>) {
12775
event.preventDefault()
12876
loading.value = true

src/runtime/types/index.d.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import type { ButtonProps } from '@nuxt/ui'
44
import type { ConcreteComponent } from '@vue/runtime-core'
55

6-
export type ComponentsMap = Record<string, (key: string, zodType: any) => ComponentDefinition | null>
6+
export type ComponentsMap = Record<string, (params: { key: string, state: any, zodType: any, config: AutoFormConfig }) => ComponentDefinition | null>
77

88
export interface AutoFormConfig {
99
/** Submit button configuration */
@@ -17,6 +17,16 @@ export interface AutoFormConfig {
1717
/** Props to pass to the Nuxt UI `UButton` component */
1818
props?: ButtonProps
1919
}
20+
21+
theme: {
22+
/**
23+
* Use floating labels for form fields
24+
* @see https://ui.nuxt.com/components/form-field#description
25+
* @default true
26+
*/
27+
floatingLabels?: boolean
28+
}
29+
2030
/**
2131
* Customize the default components used for rendering form fields.
2232
*

src/utils/components_map.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import type { AutoFormConfig, ComponentsMap } from '../runtime/types'
2+
import UCheckbox from '@nuxt/ui/components/Checkbox.vue'
3+
import UInput from '@nuxt/ui/components/Input.vue'
4+
import UInputNumber from '@nuxt/ui/components/InputNumber.vue'
5+
import USelect from '@nuxt/ui/components/Select.vue'
6+
import defu from 'defu'
7+
import * as z from 'zod'
8+
9+
export interface ComponentDefinition {
10+
// FIXME: Try using proper types for components
11+
component: any
12+
componentProps?: Record<string, any>
13+
}
14+
15+
export const COMPONENTS_MAP: ComponentsMap = {
16+
number: () => ({ component: UInputNumber }),
17+
string: () => ({ component: UInput }),
18+
boolean: () => ({ component: UCheckbox }),
19+
enum: ({ zodType }) => ({
20+
component: USelect,
21+
componentProps: {
22+
items: Object.values(zodType.def.entries),
23+
},
24+
}),
25+
array: ({ key, zodType, config, state }) => {
26+
const innerType = zodType.def.element
27+
if (innerType instanceof z.ZodEnum) {
28+
const result = mapZodTypeToComponent(key, zodType.unwrap(), config, state) as any
29+
result.componentProps.multiple = true
30+
return result
31+
}
32+
},
33+
default: ({ key, zodType, state, config }) => {
34+
(state as any)[key] = zodType.def.defaultValue
35+
return mapZodTypeToComponent(key, zodType.unwrap(), config, state)
36+
},
37+
email: ({ key, config, state }) => {
38+
const stringComponent = mapZodTypeToComponent(key, 'string', config, state)!
39+
40+
return defu({
41+
componentProps: {
42+
type: 'email',
43+
},
44+
}, stringComponent)
45+
},
46+
}
47+
48+
export function mapZodTypeToComponent(key: string, zodType: any, config: AutoFormConfig, state: any): ComponentDefinition | null {
49+
const zodTypeKey = zodType._def.format ?? zodType._def.type
50+
const component = config.components?.[zodTypeKey]
51+
if (!component) {
52+
console.warn(`Unsupported Zod type: ${zodTypeKey}`)
53+
console.warn('Please update your @norbiros/nuxt-auto-form configuration')
54+
console.warn('Or report it here https://github.com/Norbiros/nuxt-auto-form')
55+
return null
56+
}
57+
return component({ key, zodType, config, state })
58+
}

0 commit comments

Comments
 (0)