feat: show required field for object

This commit is contained in:
zernonia 2024-04-24 09:41:58 +08:00
parent 2891dd2a15
commit 63081e8d21
6 changed files with 62 additions and 42 deletions

View File

@ -20,8 +20,6 @@ const props = defineProps<{
disabled?: boolean disabled?: boolean
}>() }>()
const fieldContext = useField(props.fieldName)
function isZodArray( function isZodArray(
item: z.ZodArray<any> | z.ZodDefault<any>, item: z.ZodArray<any> | z.ZodDefault<any>,
): item is z.ZodArray<any> { ): item is z.ZodArray<any> {
@ -51,6 +49,7 @@ const itemShape = computed(() => {
} }
}) })
const fieldContext = useField(props.fieldName)
// @ts-expect-error ignore missing `id` // @ts-expect-error ignore missing `id`
provide(FieldContextKey, fieldContext) provide(FieldContextKey, fieldContext)
</script> </script>

View File

@ -1,10 +1,13 @@
<script setup lang="ts" generic="T extends ZodRawShape"> <script setup lang="ts" generic="T extends ZodRawShape">
import type { ZodAny, ZodObject, ZodRawShape } from 'zod' import type { ZodAny, ZodObject, ZodRawShape } from 'zod'
import { computed } from 'vue' import { computed, provide } from 'vue'
import { FieldContextKey, useField } from 'vee-validate'
import AutoFormField from './AutoFormField.vue' import AutoFormField from './AutoFormField.vue'
import type { Config, ConfigItem, Shape } from './interface' import type { Config, ConfigItem, Shape } from './interface'
import { beautifyObjectName, getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils' import { beautifyObjectName, getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'
import AutoFormLabel from './AutoFormLabel.vue'
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion' import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'
import { FormItem } from '@/lib/registry/default/ui/form'
const props = defineProps<{ const props = defineProps<{
fieldName: string fieldName: string
@ -39,15 +42,22 @@ const shapes = computed(() => {
}) })
return val return val
}) })
const fieldContext = useField(props.fieldName)
// @ts-expect-error ignore missing `id`
provide(FieldContextKey, fieldContext)
</script> </script>
<template> <template>
<section> <section>
<slot v-bind="props"> <slot v-bind="props">
<Accordion type="multiple" class="w-full" collapsible :disabled="disabled"> <Accordion type="single" as-child class="w-full" collapsible :disabled="disabled">
<FormItem>
<AccordionItem :value="fieldName" class="border-none"> <AccordionItem :value="fieldName" class="border-none">
<AccordionTrigger class="text-base"> <AccordionTrigger>
<AutoFormLabel class="text-base" :required="required">
{{ schema?.description || beautifyObjectName(fieldName) }} {{ schema?.description || beautifyObjectName(fieldName) }}
</AutoFormLabel>
</AccordionTrigger> </AccordionTrigger>
<AccordionContent class="p-1 space-y-5"> <AccordionContent class="p-1 space-y-5">
<template v-for="(shape, key) in shapes" :key="key"> <template v-for="(shape, key) in shapes" :key="key">
@ -60,6 +70,7 @@ const shapes = computed(() => {
</template> </template>
</AccordionContent> </AccordionContent>
</AccordionItem> </AccordionItem>
</FormItem>
</Accordion> </Accordion>
</slot> </slot>
</section> </section>

View File

@ -20,8 +20,6 @@ const props = defineProps<{
disabled?: boolean disabled?: boolean
}>() }>()
const fieldContext = useField(props.fieldName)
function isZodArray( function isZodArray(
item: z.ZodArray<any> | z.ZodDefault<any>, item: z.ZodArray<any> | z.ZodDefault<any>,
): item is z.ZodArray<any> { ): item is z.ZodArray<any> {
@ -51,6 +49,7 @@ const itemShape = computed(() => {
} }
}) })
const fieldContext = useField(props.fieldName)
// @ts-expect-error ignore missing `id` // @ts-expect-error ignore missing `id`
provide(FieldContextKey, fieldContext) provide(FieldContextKey, fieldContext)
</script> </script>

View File

@ -1,10 +1,13 @@
<script setup lang="ts" generic="T extends ZodRawShape"> <script setup lang="ts" generic="T extends ZodRawShape">
import type { ZodAny, ZodObject, ZodRawShape } from 'zod' import type { ZodAny, ZodObject, ZodRawShape } from 'zod'
import { computed } from 'vue' import { computed, provide } from 'vue'
import { FieldContextKey, useField } from 'vee-validate'
import AutoFormField from './AutoFormField.vue' import AutoFormField from './AutoFormField.vue'
import type { Config, ConfigItem, Shape } from './interface' import type { Config, ConfigItem, Shape } from './interface'
import { beautifyObjectName, getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils' import { beautifyObjectName, getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'
import AutoFormLabel from './AutoFormLabel.vue'
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/new-york/ui/accordion' import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/new-york/ui/accordion'
import { FormItem } from '@/lib/registry/new-york/ui/form'
const props = defineProps<{ const props = defineProps<{
fieldName: string fieldName: string
@ -39,17 +42,24 @@ const shapes = computed(() => {
}) })
return val return val
}) })
const fieldContext = useField(props.fieldName)
// @ts-expect-error ignore missing `id`
provide(FieldContextKey, fieldContext)
</script> </script>
<template> <template>
<section> <section>
<slot v-bind="props"> <slot v-bind="props">
<Accordion type="multiple" class="w-full" collapsible :disabled="disabled"> <Accordion type="single" as-child class="w-full" collapsible :disabled="disabled">
<FormItem>
<AccordionItem :value="fieldName" class="border-none"> <AccordionItem :value="fieldName" class="border-none">
<AccordionTrigger class="text-base"> <AccordionTrigger>
<AutoFormLabel class="text-base" :required="required">
{{ schema?.description || beautifyObjectName(fieldName) }} {{ schema?.description || beautifyObjectName(fieldName) }}
</AutoFormLabel>
</AccordionTrigger> </AccordionTrigger>
<AccordionContent class="p-[1px] space-y-5"> <AccordionContent class="p-1 space-y-5">
<template v-for="(shape, key) in shapes" :key="key"> <template v-for="(shape, key) in shapes" :key="key">
<AutoFormField <AutoFormField
:config="config?.[key as keyof typeof config] as ConfigItem" :config="config?.[key as keyof typeof config] as ConfigItem"
@ -60,6 +70,7 @@ const shapes = computed(() => {
</template> </template>
</AccordionContent> </AccordionContent>
</AccordionItem> </AccordionItem>
</FormItem>
</Accordion> </Accordion>
</slot> </slot>
</section> </section>

View File

@ -32,7 +32,7 @@
}, },
{ {
"name": "AutoFormFieldArray.vue", "name": "AutoFormFieldArray.vue",
"content": "<script setup lang=\"ts\" generic=\"T extends z.ZodAny\">\nimport * as z from 'zod'\nimport { computed, provide } from 'vue'\nimport { PlusIcon, TrashIcon } from 'lucide-vue-next'\nimport { FieldArray, FieldContextKey, useField } from 'vee-validate'\nimport type { Config, ConfigItem } from './interface'\nimport { beautifyObjectName, getBaseType } from './utils'\nimport AutoFormField from './AutoFormField.vue'\nimport AutoFormLabel from './AutoFormLabel.vue'\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'\nimport { Button } from '@/lib/registry/default/ui/button'\nimport { Separator } from '@/lib/registry/default/ui/separator'\nimport { FormItem, FormMessage } from '@/lib/registry/default/ui/form'\n\nconst props = defineProps<{\n fieldName: string\n required?: boolean\n config?: Config<T>\n schema?: z.ZodArray<T>\n disabled?: boolean\n}>()\n\nconst fieldContext = useField(props.fieldName)\n\nfunction isZodArray(\n item: z.ZodArray<any> | z.ZodDefault<any>,\n): item is z.ZodArray<any> {\n return item instanceof z.ZodArray\n}\n\nfunction isZodDefault(\n item: z.ZodArray<any> | z.ZodDefault<any>,\n): item is z.ZodDefault<any> {\n return item instanceof z.ZodDefault\n}\n\nconst itemShape = computed(() => {\n if (!props.schema)\n return\n\n const schema: z.ZodAny = isZodArray(props.schema)\n ? props.schema._def.type\n : isZodDefault(props.schema)\n // @ts-expect-error missing schema\n ? props.schema._def.innerType._def.type\n : null\n\n return {\n type: getBaseType(schema),\n schema,\n }\n})\n\n// @ts-expect-error ignore missing `id`\nprovide(FieldContextKey, fieldContext)\n</script>\n\n<template>\n <FieldArray v-slot=\"{ fields, remove, push }\" as=\"section\" :name=\"fieldName\">\n <slot v-bind=\"props\">\n <Accordion type=\"multiple\" class=\"w-full\" collapsible :disabled=\"disabled\" as-child>\n <FormItem>\n <AccordionItem :value=\"fieldName\" class=\"border-none\">\n <AccordionTrigger>\n <AutoFormLabel class=\"text-base\" :required=\"required\">\n {{ schema?.description || beautifyObjectName(fieldName) }}\n </AutoFormLabel>\n </AccordionTrigger>\n\n <AccordionContent>\n <template v-for=\"(field, index) of fields\" :key=\"field.key\">\n <div class=\"mb-4 p-1\">\n <AutoFormField\n :field-name=\"`${fieldName}[${index}]`\"\n :label=\"fieldName\"\n :shape=\"itemShape!\"\n :config=\"config as ConfigItem\"\n />\n\n <div class=\"!my-4 flex justify-end\">\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"secondary\"\n @click=\"remove(index)\"\n >\n <TrashIcon :size=\"16\" />\n </Button>\n </div>\n <Separator v-if=\"!field.isLast\" />\n </div>\n </template>\n\n <Button\n type=\"button\"\n variant=\"secondary\"\n class=\"mt-4 flex items-center\"\n @click=\"push(null)\"\n >\n <PlusIcon class=\"mr-2\" :size=\"16\" />\n Add\n </Button>\n </AccordionContent>\n\n <FormMessage />\n </AccordionItem>\n </FormItem>\n </Accordion>\n </slot>\n </FieldArray>\n</template>\n" "content": "<script setup lang=\"ts\" generic=\"T extends z.ZodAny\">\nimport * as z from 'zod'\nimport { computed, provide } from 'vue'\nimport { PlusIcon, TrashIcon } from 'lucide-vue-next'\nimport { FieldArray, FieldContextKey, useField } from 'vee-validate'\nimport type { Config, ConfigItem } from './interface'\nimport { beautifyObjectName, getBaseType } from './utils'\nimport AutoFormField from './AutoFormField.vue'\nimport AutoFormLabel from './AutoFormLabel.vue'\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'\nimport { Button } from '@/lib/registry/default/ui/button'\nimport { Separator } from '@/lib/registry/default/ui/separator'\nimport { FormItem, FormMessage } from '@/lib/registry/default/ui/form'\n\nconst props = defineProps<{\n fieldName: string\n required?: boolean\n config?: Config<T>\n schema?: z.ZodArray<T>\n disabled?: boolean\n}>()\n\nfunction isZodArray(\n item: z.ZodArray<any> | z.ZodDefault<any>,\n): item is z.ZodArray<any> {\n return item instanceof z.ZodArray\n}\n\nfunction isZodDefault(\n item: z.ZodArray<any> | z.ZodDefault<any>,\n): item is z.ZodDefault<any> {\n return item instanceof z.ZodDefault\n}\n\nconst itemShape = computed(() => {\n if (!props.schema)\n return\n\n const schema: z.ZodAny = isZodArray(props.schema)\n ? props.schema._def.type\n : isZodDefault(props.schema)\n // @ts-expect-error missing schema\n ? props.schema._def.innerType._def.type\n : null\n\n return {\n type: getBaseType(schema),\n schema,\n }\n})\n\nconst fieldContext = useField(props.fieldName)\n// @ts-expect-error ignore missing `id`\nprovide(FieldContextKey, fieldContext)\n</script>\n\n<template>\n <FieldArray v-slot=\"{ fields, remove, push }\" as=\"section\" :name=\"fieldName\">\n <slot v-bind=\"props\">\n <Accordion type=\"multiple\" class=\"w-full\" collapsible :disabled=\"disabled\" as-child>\n <FormItem>\n <AccordionItem :value=\"fieldName\" class=\"border-none\">\n <AccordionTrigger>\n <AutoFormLabel class=\"text-base\" :required=\"required\">\n {{ schema?.description || beautifyObjectName(fieldName) }}\n </AutoFormLabel>\n </AccordionTrigger>\n\n <AccordionContent>\n <template v-for=\"(field, index) of fields\" :key=\"field.key\">\n <div class=\"mb-4 p-1\">\n <AutoFormField\n :field-name=\"`${fieldName}[${index}]`\"\n :label=\"fieldName\"\n :shape=\"itemShape!\"\n :config=\"config as ConfigItem\"\n />\n\n <div class=\"!my-4 flex justify-end\">\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"secondary\"\n @click=\"remove(index)\"\n >\n <TrashIcon :size=\"16\" />\n </Button>\n </div>\n <Separator v-if=\"!field.isLast\" />\n </div>\n </template>\n\n <Button\n type=\"button\"\n variant=\"secondary\"\n class=\"mt-4 flex items-center\"\n @click=\"push(null)\"\n >\n <PlusIcon class=\"mr-2\" :size=\"16\" />\n Add\n </Button>\n </AccordionContent>\n\n <FormMessage />\n </AccordionItem>\n </FormItem>\n </Accordion>\n </slot>\n </FieldArray>\n</template>\n"
}, },
{ {
"name": "AutoFormFieldBoolean.vue", "name": "AutoFormFieldBoolean.vue",
@ -60,7 +60,7 @@
}, },
{ {
"name": "AutoFormFieldObject.vue", "name": "AutoFormFieldObject.vue",
"content": "<script setup lang=\"ts\" generic=\"T extends ZodRawShape\">\nimport type { ZodAny, ZodObject, ZodRawShape } from 'zod'\nimport { computed } from 'vue'\nimport AutoFormField from './AutoFormField.vue'\nimport type { Config, ConfigItem, Shape } from './interface'\nimport { beautifyObjectName, getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'\n\nconst props = defineProps<{\n fieldName: string\n required?: boolean\n config?: Config<T>\n schema?: ZodObject<T>\n disabled?: boolean\n}>()\n\nconst shapes = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof T]: Shape } = {}\n\n if (!props.schema)\n return\n const shape = getBaseSchema(props.schema)?.shape\n if (!shape)\n return\n Object.keys(shape).forEach((name) => {\n const item = shape[name] as ZodAny\n let options = 'values' in item._def ? item._def.values as string[] : undefined\n if (!Array.isArray(options) && typeof options === 'object')\n options = Object.values(options)\n\n val[name as keyof T] = {\n type: getBaseType(item),\n default: getDefaultValueInZodStack(item),\n options,\n required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),\n schema: item,\n }\n })\n return val\n})\n</script>\n\n<template>\n <section>\n <slot v-bind=\"props\">\n <Accordion type=\"multiple\" class=\"w-full\" collapsible :disabled=\"disabled\">\n <AccordionItem :value=\"fieldName\" class=\"border-none\">\n <AccordionTrigger class=\"text-base\">\n {{ schema?.description || beautifyObjectName(fieldName) }}\n </AccordionTrigger>\n <AccordionContent class=\"p-1 space-y-5\">\n <template v-for=\"(shape, key) in shapes\" :key=\"key\">\n <AutoFormField\n :config=\"config?.[key as keyof typeof config] as ConfigItem\"\n :field-name=\"`${fieldName}.${key.toString()}`\"\n :label=\"key.toString()\"\n :shape=\"shape\"\n />\n </template>\n </AccordionContent>\n </AccordionItem>\n </Accordion>\n </slot>\n </section>\n</template>\n" "content": "<script setup lang=\"ts\" generic=\"T extends ZodRawShape\">\nimport type { ZodAny, ZodObject, ZodRawShape } from 'zod'\nimport { computed, provide } from 'vue'\nimport { FieldContextKey, useField } from 'vee-validate'\nimport AutoFormField from './AutoFormField.vue'\nimport type { Config, ConfigItem, Shape } from './interface'\nimport { beautifyObjectName, getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'\nimport AutoFormLabel from './AutoFormLabel.vue'\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/default/ui/accordion'\nimport { FormItem } from '@/lib/registry/default/ui/form'\n\nconst props = defineProps<{\n fieldName: string\n required?: boolean\n config?: Config<T>\n schema?: ZodObject<T>\n disabled?: boolean\n}>()\n\nconst shapes = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof T]: Shape } = {}\n\n if (!props.schema)\n return\n const shape = getBaseSchema(props.schema)?.shape\n if (!shape)\n return\n Object.keys(shape).forEach((name) => {\n const item = shape[name] as ZodAny\n let options = 'values' in item._def ? item._def.values as string[] : undefined\n if (!Array.isArray(options) && typeof options === 'object')\n options = Object.values(options)\n\n val[name as keyof T] = {\n type: getBaseType(item),\n default: getDefaultValueInZodStack(item),\n options,\n required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),\n schema: item,\n }\n })\n return val\n})\n\nconst fieldContext = useField(props.fieldName)\n// @ts-expect-error ignore missing `id`\nprovide(FieldContextKey, fieldContext)\n</script>\n\n<template>\n <section>\n <slot v-bind=\"props\">\n <Accordion type=\"single\" as-child class=\"w-full\" collapsible :disabled=\"disabled\">\n <FormItem>\n <AccordionItem :value=\"fieldName\" class=\"border-none\">\n <AccordionTrigger>\n <AutoFormLabel class=\"text-base\" :required=\"required\">\n {{ schema?.description || beautifyObjectName(fieldName) }}\n </AutoFormLabel>\n </AccordionTrigger>\n <AccordionContent class=\"p-1 space-y-5\">\n <template v-for=\"(shape, key) in shapes\" :key=\"key\">\n <AutoFormField\n :config=\"config?.[key as keyof typeof config] as ConfigItem\"\n :field-name=\"`${fieldName}.${key.toString()}`\"\n :label=\"key.toString()\"\n :shape=\"shape\"\n />\n </template>\n </AccordionContent>\n </AccordionItem>\n </FormItem>\n </Accordion>\n </slot>\n </section>\n</template>\n"
}, },
{ {
"name": "AutoFormLabel.vue", "name": "AutoFormLabel.vue",

View File

@ -32,7 +32,7 @@
}, },
{ {
"name": "AutoFormFieldArray.vue", "name": "AutoFormFieldArray.vue",
"content": "<script setup lang=\"ts\" generic=\"T extends z.ZodAny\">\nimport * as z from 'zod'\nimport { computed, provide } from 'vue'\nimport { PlusIcon, TrashIcon } from 'lucide-vue-next'\nimport { FieldArray, FieldContextKey, useField } from 'vee-validate'\nimport type { Config, ConfigItem } from './interface'\nimport { beautifyObjectName, getBaseType } from './utils'\nimport AutoFormField from './AutoFormField.vue'\nimport AutoFormLabel from './AutoFormLabel.vue'\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/new-york/ui/accordion'\nimport { Button } from '@/lib/registry/new-york/ui/button'\nimport { Separator } from '@/lib/registry/new-york/ui/separator'\nimport { FormItem, FormMessage } from '@/lib/registry/new-york/ui/form'\n\nconst props = defineProps<{\n fieldName: string\n required?: boolean\n config?: Config<T>\n schema?: z.ZodArray<T>\n disabled?: boolean\n}>()\n\nconst fieldContext = useField(props.fieldName)\n\nfunction isZodArray(\n item: z.ZodArray<any> | z.ZodDefault<any>,\n): item is z.ZodArray<any> {\n return item instanceof z.ZodArray\n}\n\nfunction isZodDefault(\n item: z.ZodArray<any> | z.ZodDefault<any>,\n): item is z.ZodDefault<any> {\n return item instanceof z.ZodDefault\n}\n\nconst itemShape = computed(() => {\n if (!props.schema)\n return\n\n const schema: z.ZodAny = isZodArray(props.schema)\n ? props.schema._def.type\n : isZodDefault(props.schema)\n // @ts-expect-error missing schema\n ? props.schema._def.innerType._def.type\n : null\n\n return {\n type: getBaseType(schema),\n schema,\n }\n})\n\n// @ts-expect-error ignore missing `id`\nprovide(FieldContextKey, fieldContext)\n</script>\n\n<template>\n <FieldArray v-slot=\"{ fields, remove, push }\" as=\"section\" :name=\"fieldName\">\n <slot v-bind=\"props\">\n <Accordion type=\"multiple\" class=\"w-full\" collapsible :disabled=\"disabled\" as-child>\n <FormItem>\n <AccordionItem :value=\"fieldName\" class=\"border-none\">\n <AccordionTrigger>\n <AutoFormLabel class=\"text-base\" :required=\"required\">\n {{ schema?.description || beautifyObjectName(fieldName) }}\n </AutoFormLabel>\n </AccordionTrigger>\n\n <AccordionContent>\n <template v-for=\"(field, index) of fields\" :key=\"field.key\">\n <div class=\"mb-4 p-1\">\n <AutoFormField\n :field-name=\"`${fieldName}[${index}]`\"\n :label=\"fieldName\"\n :shape=\"itemShape!\"\n :config=\"config as ConfigItem\"\n />\n\n <div class=\"!my-4 flex justify-end\">\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"secondary\"\n @click=\"remove(index)\"\n >\n <TrashIcon :size=\"16\" />\n </Button>\n </div>\n <Separator v-if=\"!field.isLast\" />\n </div>\n </template>\n\n <Button\n type=\"button\"\n variant=\"secondary\"\n class=\"mt-4 flex items-center\"\n @click=\"push(null)\"\n >\n <PlusIcon class=\"mr-2\" :size=\"16\" />\n Add\n </Button>\n </AccordionContent>\n\n <FormMessage />\n </AccordionItem>\n </FormItem>\n </Accordion>\n </slot>\n </FieldArray>\n</template>\n" "content": "<script setup lang=\"ts\" generic=\"T extends z.ZodAny\">\nimport * as z from 'zod'\nimport { computed, provide } from 'vue'\nimport { PlusIcon, TrashIcon } from 'lucide-vue-next'\nimport { FieldArray, FieldContextKey, useField } from 'vee-validate'\nimport type { Config, ConfigItem } from './interface'\nimport { beautifyObjectName, getBaseType } from './utils'\nimport AutoFormField from './AutoFormField.vue'\nimport AutoFormLabel from './AutoFormLabel.vue'\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/new-york/ui/accordion'\nimport { Button } from '@/lib/registry/new-york/ui/button'\nimport { Separator } from '@/lib/registry/new-york/ui/separator'\nimport { FormItem, FormMessage } from '@/lib/registry/new-york/ui/form'\n\nconst props = defineProps<{\n fieldName: string\n required?: boolean\n config?: Config<T>\n schema?: z.ZodArray<T>\n disabled?: boolean\n}>()\n\nfunction isZodArray(\n item: z.ZodArray<any> | z.ZodDefault<any>,\n): item is z.ZodArray<any> {\n return item instanceof z.ZodArray\n}\n\nfunction isZodDefault(\n item: z.ZodArray<any> | z.ZodDefault<any>,\n): item is z.ZodDefault<any> {\n return item instanceof z.ZodDefault\n}\n\nconst itemShape = computed(() => {\n if (!props.schema)\n return\n\n const schema: z.ZodAny = isZodArray(props.schema)\n ? props.schema._def.type\n : isZodDefault(props.schema)\n // @ts-expect-error missing schema\n ? props.schema._def.innerType._def.type\n : null\n\n return {\n type: getBaseType(schema),\n schema,\n }\n})\n\nconst fieldContext = useField(props.fieldName)\n// @ts-expect-error ignore missing `id`\nprovide(FieldContextKey, fieldContext)\n</script>\n\n<template>\n <FieldArray v-slot=\"{ fields, remove, push }\" as=\"section\" :name=\"fieldName\">\n <slot v-bind=\"props\">\n <Accordion type=\"multiple\" class=\"w-full\" collapsible :disabled=\"disabled\" as-child>\n <FormItem>\n <AccordionItem :value=\"fieldName\" class=\"border-none\">\n <AccordionTrigger>\n <AutoFormLabel class=\"text-base\" :required=\"required\">\n {{ schema?.description || beautifyObjectName(fieldName) }}\n </AutoFormLabel>\n </AccordionTrigger>\n\n <AccordionContent>\n <template v-for=\"(field, index) of fields\" :key=\"field.key\">\n <div class=\"mb-4 p-1\">\n <AutoFormField\n :field-name=\"`${fieldName}[${index}]`\"\n :label=\"fieldName\"\n :shape=\"itemShape!\"\n :config=\"config as ConfigItem\"\n />\n\n <div class=\"!my-4 flex justify-end\">\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"secondary\"\n @click=\"remove(index)\"\n >\n <TrashIcon :size=\"16\" />\n </Button>\n </div>\n <Separator v-if=\"!field.isLast\" />\n </div>\n </template>\n\n <Button\n type=\"button\"\n variant=\"secondary\"\n class=\"mt-4 flex items-center\"\n @click=\"push(null)\"\n >\n <PlusIcon class=\"mr-2\" :size=\"16\" />\n Add\n </Button>\n </AccordionContent>\n\n <FormMessage />\n </AccordionItem>\n </FormItem>\n </Accordion>\n </slot>\n </FieldArray>\n</template>\n"
}, },
{ {
"name": "AutoFormFieldBoolean.vue", "name": "AutoFormFieldBoolean.vue",
@ -60,7 +60,7 @@
}, },
{ {
"name": "AutoFormFieldObject.vue", "name": "AutoFormFieldObject.vue",
"content": "<script setup lang=\"ts\" generic=\"T extends ZodRawShape\">\nimport type { ZodAny, ZodObject, ZodRawShape } from 'zod'\nimport { computed } from 'vue'\nimport AutoFormField from './AutoFormField.vue'\nimport type { Config, ConfigItem, Shape } from './interface'\nimport { beautifyObjectName, getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/new-york/ui/accordion'\n\nconst props = defineProps<{\n fieldName: string\n required?: boolean\n config?: Config<T>\n schema?: ZodObject<T>\n disabled?: boolean\n}>()\n\nconst shapes = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof T]: Shape } = {}\n\n if (!props.schema)\n return\n const shape = getBaseSchema(props.schema)?.shape\n if (!shape)\n return\n Object.keys(shape).forEach((name) => {\n const item = shape[name] as ZodAny\n let options = 'values' in item._def ? item._def.values as string[] : undefined\n if (!Array.isArray(options) && typeof options === 'object')\n options = Object.values(options)\n\n val[name as keyof T] = {\n type: getBaseType(item),\n default: getDefaultValueInZodStack(item),\n options,\n required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),\n schema: item,\n }\n })\n return val\n})\n</script>\n\n<template>\n <section>\n <slot v-bind=\"props\">\n <Accordion type=\"multiple\" class=\"w-full\" collapsible :disabled=\"disabled\">\n <AccordionItem :value=\"fieldName\" class=\"border-none\">\n <AccordionTrigger class=\"text-base\">\n {{ schema?.description || beautifyObjectName(fieldName) }}\n </AccordionTrigger>\n <AccordionContent class=\"p-[1px] space-y-5\">\n <template v-for=\"(shape, key) in shapes\" :key=\"key\">\n <AutoFormField\n :config=\"config?.[key as keyof typeof config] as ConfigItem\"\n :field-name=\"`${fieldName}.${key.toString()}`\"\n :label=\"key.toString()\"\n :shape=\"shape\"\n />\n </template>\n </AccordionContent>\n </AccordionItem>\n </Accordion>\n </slot>\n </section>\n</template>\n" "content": "<script setup lang=\"ts\" generic=\"T extends ZodRawShape\">\nimport type { ZodAny, ZodObject, ZodRawShape } from 'zod'\nimport { computed, provide } from 'vue'\nimport { FieldContextKey, useField } from 'vee-validate'\nimport AutoFormField from './AutoFormField.vue'\nimport type { Config, ConfigItem, Shape } from './interface'\nimport { beautifyObjectName, getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'\nimport AutoFormLabel from './AutoFormLabel.vue'\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/new-york/ui/accordion'\nimport { FormItem } from '@/lib/registry/new-york/ui/form'\n\nconst props = defineProps<{\n fieldName: string\n required?: boolean\n config?: Config<T>\n schema?: ZodObject<T>\n disabled?: boolean\n}>()\n\nconst shapes = computed(() => {\n // @ts-expect-error ignore {} not assignable to object\n const val: { [key in keyof T]: Shape } = {}\n\n if (!props.schema)\n return\n const shape = getBaseSchema(props.schema)?.shape\n if (!shape)\n return\n Object.keys(shape).forEach((name) => {\n const item = shape[name] as ZodAny\n let options = 'values' in item._def ? item._def.values as string[] : undefined\n if (!Array.isArray(options) && typeof options === 'object')\n options = Object.values(options)\n\n val[name as keyof T] = {\n type: getBaseType(item),\n default: getDefaultValueInZodStack(item),\n options,\n required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),\n schema: item,\n }\n })\n return val\n})\n\nconst fieldContext = useField(props.fieldName)\n// @ts-expect-error ignore missing `id`\nprovide(FieldContextKey, fieldContext)\n</script>\n\n<template>\n <section>\n <slot v-bind=\"props\">\n <Accordion type=\"single\" as-child class=\"w-full\" collapsible :disabled=\"disabled\">\n <FormItem>\n <AccordionItem :value=\"fieldName\" class=\"border-none\">\n <AccordionTrigger>\n <AutoFormLabel class=\"text-base\" :required=\"required\">\n {{ schema?.description || beautifyObjectName(fieldName) }}\n </AutoFormLabel>\n </AccordionTrigger>\n <AccordionContent class=\"p-1 space-y-5\">\n <template v-for=\"(shape, key) in shapes\" :key=\"key\">\n <AutoFormField\n :config=\"config?.[key as keyof typeof config] as ConfigItem\"\n :field-name=\"`${fieldName}.${key.toString()}`\"\n :label=\"key.toString()\"\n :shape=\"shape\"\n />\n </template>\n </AccordionContent>\n </AccordionItem>\n </FormItem>\n </Accordion>\n </slot>\n </section>\n</template>\n"
}, },
{ {
"name": "AutoFormLabel.vue", "name": "AutoFormLabel.vue",