feat: typing for nested config
This commit is contained in:
parent
9f37b12c21
commit
49ad6c85e4
|
|
@ -146,7 +146,6 @@ const onSubmit = handleSubmit((values) => {
|
|||
placeholder: 'Choose a color',
|
||||
},
|
||||
},
|
||||
|
||||
marshmallows: {
|
||||
component: 'radio',
|
||||
},
|
||||
|
|
@ -158,7 +157,15 @@ const onSubmit = handleSubmit((values) => {
|
|||
},
|
||||
|
||||
subObject: {
|
||||
|
||||
subField: {
|
||||
label: 'custom labvel',
|
||||
description: '123',
|
||||
},
|
||||
subSubObject: {
|
||||
subSubField: {
|
||||
label: 'sub suuuub',
|
||||
},
|
||||
},
|
||||
},
|
||||
}"
|
||||
@submit="onSubmit"
|
||||
|
|
@ -175,6 +182,10 @@ const onSubmit = handleSubmit((values) => {
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<template #subObject="componentField">
|
||||
<AutoFormField v-bind="{ ...componentField, name: 'subObject' }" />
|
||||
</template>
|
||||
|
||||
<Button type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
<script setup lang="ts" generic="T extends ZodRawShape">
|
||||
<script setup lang="ts" generic="U extends ZodRawShape, T extends ZodObject<U>">
|
||||
import { computed } from 'vue'
|
||||
import type { ZodAny, ZodObject, ZodRawShape } from 'zod'
|
||||
import type { ZodAny, ZodObject, ZodRawShape, z } from 'zod'
|
||||
import { getBaseType, getDefaultValueInZodStack } from './utils'
|
||||
import type { Config } from './interface'
|
||||
import type { Config, ConfigItem } from './interface'
|
||||
import AutoFormField from './AutoFormField.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
schema: ZodObject<T>
|
||||
fieldConfig?: {
|
||||
[key in keyof T]?: Config
|
||||
}
|
||||
schema: T
|
||||
fieldConfig?: Config<z.infer<T>>
|
||||
}>()
|
||||
|
||||
const shapes = computed(() => {
|
||||
|
|
@ -48,16 +46,14 @@ const shapes = computed(() => {
|
|||
<slot
|
||||
:shape="shape"
|
||||
:name="key.toString()"
|
||||
:config="fieldConfig?.[key as keyof typeof fieldConfig] as Config"
|
||||
:config="fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem"
|
||||
>
|
||||
<AutoFormField
|
||||
:config="fieldConfig?.[key as keyof typeof fieldConfig] as Config"
|
||||
:config="fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem"
|
||||
:name="key.toString()"
|
||||
:shape="shape"
|
||||
/>
|
||||
</slot>
|
||||
|
||||
<!-- {{ shape.schema }} -->
|
||||
</template>
|
||||
|
||||
<slot :shapes="shapes" />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
<script setup lang="ts" generic="U extends ZodAny">
|
||||
import type { ZodAny } from 'zod'
|
||||
|
||||
import type { Config } from './interface'
|
||||
import type { Config, ConfigItem } from './interface'
|
||||
import { DEFAULT_ZOD_HANDLERS, INPUT_COMPONENTS } from './constant'
|
||||
|
||||
defineProps<{
|
||||
|
|
@ -12,16 +11,21 @@ defineProps<{
|
|||
options?: any[]
|
||||
schema?: ZodAny
|
||||
}
|
||||
config?: Config
|
||||
config?: ConfigItem | Config<U>
|
||||
}>()
|
||||
|
||||
function isValidConfig(config: any): config is ConfigItem {
|
||||
return !!config?.component
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component
|
||||
:is="INPUT_COMPONENTS[config?.component ?? DEFAULT_ZOD_HANDLERS[shape.type]] "
|
||||
:is="isValidConfig(config) ? INPUT_COMPONENTS[config.component!] : INPUT_COMPONENTS[DEFAULT_ZOD_HANDLERS[shape.type]] "
|
||||
:name="name"
|
||||
:required="shape.required"
|
||||
:options="shape.options"
|
||||
:schema="shape.schema"
|
||||
:config="config"
|
||||
>
|
||||
<slot />
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import FieldEnum from './fields/Enum.vue'
|
|||
import FieldFile from './fields/File.vue'
|
||||
import FieldInput from './fields/Input.vue'
|
||||
import FieldNumber from './fields/Number.vue'
|
||||
import FieldObject from './fields/Object.vue'
|
||||
|
||||
export const INPUT_COMPONENTS = {
|
||||
date: FieldDate,
|
||||
|
|
@ -15,6 +16,7 @@ export const INPUT_COMPONENTS = {
|
|||
number: FieldNumber,
|
||||
string: FieldInput,
|
||||
file: FieldFile,
|
||||
object: FieldObject,
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -30,4 +32,5 @@ export const DEFAULT_ZOD_HANDLERS: {
|
|||
ZodEnum: 'select',
|
||||
ZodNativeEnum: 'select',
|
||||
ZodNumber: 'number',
|
||||
ZodObject: 'object',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { beautifyObjectName } from '../utils'
|
||||
import type { Config } from '../interface'
|
||||
import type { ConfigItem } from '../interface'
|
||||
import AutoFormLabel from '../AutoFormLabel.vue'
|
||||
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/new-york/ui/form'
|
||||
import { Switch } from '@/lib/registry/new-york/ui/switch'
|
||||
|
|
@ -9,7 +9,7 @@ import { Checkbox } from '@/lib/registry/new-york/ui/checkbox'
|
|||
defineProps<{
|
||||
name: string
|
||||
required?: boolean
|
||||
config?: Config
|
||||
config?: ConfigItem
|
||||
}>()
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
import { DateFormatter, getLocalTimeZone } from '@internationalized/date'
|
||||
import { CalendarIcon } from '@radix-icons/vue'
|
||||
import { beautifyObjectName } from '../utils'
|
||||
import type { Config } from '../interface'
|
||||
import AutoFormLabel from '../AutoFormLabel.vue'
|
||||
import type { ConfigItem } from '../interface'
|
||||
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/new-york/ui/form'
|
||||
|
||||
import { Calendar } from '@/lib/registry/new-york/ui/calendar'
|
||||
|
|
@ -14,7 +14,7 @@ import { cn } from '@/lib/utils'
|
|||
defineProps<{
|
||||
name: string
|
||||
required?: boolean
|
||||
config?: Config
|
||||
config?: ConfigItem
|
||||
}>()
|
||||
|
||||
const df = new DateFormatter('en-US', {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { beautifyObjectName } from '../utils'
|
||||
import type { Config } from '../interface'
|
||||
import type { ConfigItem } from '../interface'
|
||||
import AutoFormLabel from '../AutoFormLabel.vue'
|
||||
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/new-york/ui/form'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/lib/registry/new-york/ui/select'
|
||||
|
|
@ -12,7 +12,7 @@ const props = defineProps<{
|
|||
name: string
|
||||
required?: boolean
|
||||
options?: string[]
|
||||
config?: Config
|
||||
config?: ConfigItem
|
||||
}>()
|
||||
|
||||
const computedOptions = computed(() => props.config?.enumProps?.options || props.options)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { beautifyObjectName } from '../utils'
|
||||
import type { Config } from '../interface'
|
||||
import type { ConfigItem } from '../interface'
|
||||
import AutoFormLabel from '../AutoFormLabel.vue'
|
||||
import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/lib/registry/new-york/ui/form'
|
||||
import { Input } from '@/lib/registry/new-york/ui/input'
|
||||
|
|
@ -8,7 +8,7 @@ import { Input } from '@/lib/registry/new-york/ui/input'
|
|||
defineProps<{
|
||||
name: string
|
||||
required?: boolean
|
||||
config?: Config
|
||||
config?: ConfigItem
|
||||
}>()
|
||||
|
||||
async function parseFileAsString(file: File | undefined): Promise<string> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { beautifyObjectName } from '../utils'
|
||||
import type { Config } from '../interface'
|
||||
import type { Config, ConfigItem } from '../interface'
|
||||
import AutoFormLabel from '../AutoFormLabel.vue'
|
||||
import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/lib/registry/new-york/ui/form'
|
||||
import { Input } from '@/lib/registry/new-york/ui/input'
|
||||
|
|
@ -9,7 +9,7 @@ import { Textarea } from '@/lib/registry/new-york/ui/textarea'
|
|||
defineProps<{
|
||||
name: string
|
||||
required?: boolean
|
||||
config?: Config
|
||||
config?: ConfigItem
|
||||
}>()
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { beautifyObjectName } from '../utils'
|
||||
import type { Config } from '../interface'
|
||||
import type { Config, ConfigItem } from '../interface'
|
||||
import AutoFormLabel from '../AutoFormLabel.vue'
|
||||
import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/lib/registry/new-york/ui/form'
|
||||
import { Input } from '@/lib/registry/new-york/ui/input'
|
||||
|
|
@ -12,7 +12,7 @@ defineOptions({
|
|||
defineProps<{
|
||||
name: string
|
||||
required?: boolean
|
||||
config?: Config
|
||||
config?: ConfigItem
|
||||
}>()
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
<script setup lang="ts" generic="T extends ZodRawShape">
|
||||
import type { ZodAny, ZodObject, ZodRawShape } from 'zod'
|
||||
import { computed } from 'vue'
|
||||
import type { Config, ConfigItem } from '../interface'
|
||||
import { beautifyObjectName, getBaseSchema, getBaseType, getDefaultValueInZodStack } from '../utils'
|
||||
import AutoFormField from '../AutoFormField.vue'
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/new-york/ui/accordion'
|
||||
|
||||
const props = defineProps<{
|
||||
name: string
|
||||
required?: boolean
|
||||
config?: Config<T>
|
||||
schema?: ZodObject<T>
|
||||
}>()
|
||||
|
||||
const shapes = computed(() => {
|
||||
// @ts-expect-error ignore {} not assignable to object
|
||||
const val: {
|
||||
[key in keyof T]: {
|
||||
type: string
|
||||
default: any
|
||||
required?: boolean
|
||||
options?: string[]
|
||||
schema?: ZodAny
|
||||
}
|
||||
} = {}
|
||||
|
||||
if (!props.schema)
|
||||
return
|
||||
const shape = getBaseSchema(props.schema)?.shape
|
||||
if (!shape)
|
||||
return
|
||||
Object.keys(shape).forEach((name) => {
|
||||
const item = shape[name] as ZodAny
|
||||
let options = 'values' in item._def ? item._def.values as string[] : undefined
|
||||
if (!Array.isArray(options) && typeof options === 'object')
|
||||
options = Object.values(options)
|
||||
|
||||
val[name as keyof T] = {
|
||||
type: getBaseType(item),
|
||||
default: getDefaultValueInZodStack(item),
|
||||
options,
|
||||
required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),
|
||||
schema: item,
|
||||
}
|
||||
})
|
||||
return val
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Accordion type="multiple" class="w-full" collapsible>
|
||||
<AccordionItem :value="name" class="border-none">
|
||||
<AccordionTrigger class="text-base">
|
||||
{{ beautifyObjectName(name) }}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent class="p-2 space-y-5">
|
||||
<template v-for="(shape, key) in shapes" :key="key">
|
||||
<AutoFormField
|
||||
:config="config?.[key as keyof typeof config] as ConfigItem"
|
||||
:name="key.toString()"
|
||||
:shape="shape"
|
||||
/>
|
||||
</template>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</template>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import type { InputHTMLAttributes, SelectHTMLAttributes } from 'vue'
|
||||
import type * as z from 'zod'
|
||||
import type { z } from 'zod'
|
||||
import type { INPUT_COMPONENTS } from './constant'
|
||||
|
||||
export interface Config {
|
||||
export interface ConfigItem {
|
||||
label?: string
|
||||
description?: string
|
||||
component?: keyof typeof INPUT_COMPONENTS
|
||||
|
|
@ -10,11 +10,11 @@ export interface Config {
|
|||
enumProps?: SelectHTMLAttributes & { options?: any[] }
|
||||
}
|
||||
|
||||
export type FieldConfig<SchemaType extends z.infer<z.ZodObject<any, any>>> = {
|
||||
// If SchemaType.key is an object, create a nested FieldConfig, otherwise FieldConfigItem
|
||||
export type Config<SchemaType extends object> = {
|
||||
// If SchemaType.key is an object, create a nested Config, otherwise ConfigItem
|
||||
[Key in keyof SchemaType]?: SchemaType[Key] extends object
|
||||
? FieldConfig<z.infer<SchemaType[Key]>>
|
||||
: FieldConfig<z.infer<SchemaType[Key]>> // TODO;
|
||||
? Config<SchemaType[Key]>
|
||||
: ConfigItem;
|
||||
}
|
||||
|
||||
export enum DependencyType {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user