feat: improve array

This commit is contained in:
zernonia 2024-04-19 13:21:52 +08:00
parent 1120f044e2
commit 1c562463b2
6 changed files with 55 additions and 42 deletions

View File

@ -18,23 +18,18 @@ const schema = z.object({
}), }),
).default([ ).default([
{ name: '123', age: 30 }, { name: '123', age: 30 },
{ name: '456', age: 30 },
]).describe('How many guests'), ]).describe('How many guests'),
list: z.array(z.string()).describe('test the config'), list: z.array(z.string()).describe('test the config').min(1, 'Please add some item').default([]),
}) })
const formSchema = toTypedSchema(schema) function onSubmit(values: Record<string, any>) {
const { handleSubmit } = useForm({
validationSchema: formSchema,
})
const onSubmit = handleSubmit((values) => {
toast({ toast({
title: 'You submitted the following values:', title: 'You submitted the following values:',
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))), description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
}) })
}) }
</script> </script>
<template> <template>

View File

@ -1,10 +1,12 @@
<script setup lang="ts" generic="U extends ZodRawShape, T extends ZodObject<U>"> <script setup lang="ts" generic="U extends ZodRawShape, T extends ZodObject<U>">
import { computed } from 'vue' import { computed } from 'vue'
import type { ZodAny, ZodObject, ZodRawShape, z } from 'zod' import type { ZodAny, ZodObject, ZodRawShape, z } from 'zod'
import { toTypedSchema } from '@vee-validate/zod'
import type { GenericObject } from 'vee-validate'
import { getBaseType, getDefaultValueInZodStack } from './utils' import { getBaseType, getDefaultValueInZodStack } from './utils'
import type { Config, ConfigItem, Shape } from './interface' import type { Config, ConfigItem, Shape } from './interface'
import AutoFormField from './AutoFormField.vue' import AutoFormField from './AutoFormField.vue'
import { Accordion } from '@/lib/registry/new-york/ui/accordion' import { Form } from '@/lib/registry/new-york/ui/form'
const props = defineProps<{ const props = defineProps<{
schema: T schema: T
@ -12,7 +14,7 @@ const props = defineProps<{
}>() }>()
const emits = defineEmits<{ const emits = defineEmits<{
submit: [event: Event] submit: [event: GenericObject]
}>() }>()
const shapes = computed(() => { const shapes = computed(() => {
@ -35,10 +37,16 @@ const shapes = computed(() => {
}) })
return val return val
}) })
const formSchema = computed(() => toTypedSchema(props.schema))
</script> </script>
<template> <template>
<form @submit="emits('submit', $event)"> <Form
:keep-values="true"
:validation-schema="formSchema"
@submit="emits('submit', $event)"
>
<template v-for="(shape, key) of shapes" :key="key"> <template v-for="(shape, key) of shapes" :key="key">
<slot <slot
:shape="shape" :shape="shape"
@ -54,5 +62,5 @@ const shapes = computed(() => {
</template> </template>
<slot :shapes="shapes" /> <slot :shapes="shapes" />
</form> </Form>
</template> </template>

View File

@ -1,14 +1,16 @@
<script setup lang="ts" generic="T extends z.ZodAny"> <script setup lang="ts" generic="T extends z.ZodAny">
import * as z from 'zod' import * as z from 'zod'
import { computed } from 'vue' import { computed, provide } from 'vue'
import { PlusIcon, TrashIcon } from '@radix-icons/vue' import { PlusIcon, TrashIcon } from '@radix-icons/vue'
import { useFieldArray } from 'vee-validate' import { FieldArray, FieldContextKey, useField, useFieldArray } from 'vee-validate'
import type { Config, ConfigItem } from '../interface' import type { Config, ConfigItem } from '../interface'
import { beautifyObjectName, getBaseType } from '../utils' import { beautifyObjectName, getBaseType } from '../utils'
import AutoFormField from '../AutoFormField.vue' import AutoFormField from '../AutoFormField.vue'
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 { Button } from '@/lib/registry/new-york/ui/button' import { Button } from '@/lib/registry/new-york/ui/button'
import { Separator } from '@/lib/registry/new-york/ui/separator' import { Separator } from '@/lib/registry/new-york/ui/separator'
import { FormMessage } from '@/lib/registry/new-york/ui/form'
const props = defineProps<{ const props = defineProps<{
name: string name: string
@ -17,7 +19,7 @@ const props = defineProps<{
schema?: z.ZodArray<T> schema?: z.ZodArray<T>
}>() }>()
const { remove, push, fields } = useFieldArray(props.name) const fieldContext = useField(props.name)
function isZodArray( function isZodArray(
item: z.ZodArray<any> | z.ZodDefault<any>, item: z.ZodArray<any> | z.ZodDefault<any>,
@ -47,19 +49,25 @@ const itemShape = computed(() => {
schema, schema,
} }
}) })
// @ts-expect-error ignore missing `id`
provide(FieldContextKey, fieldContext)
</script> </script>
<template> <template>
<section> <FieldArray v-slot="{ fields, remove, push }" as="section" :name="name">
<slot v-bind="props"> <slot v-bind="props">
<Accordion type="multiple" class="w-full" collapsible> <Accordion type="multiple" class="w-full" collapsible>
<AccordionItem :value="name" class="border-none"> <AccordionItem :value="name" class="border-none">
<AccordionTrigger class="text-base"> <AccordionTrigger>
<AutoFormLabel class="text-base" :required="required">
{{ schema?.description || beautifyObjectName(name) }} {{ schema?.description || beautifyObjectName(name) }}
</AutoFormLabel>
</AccordionTrigger> </AccordionTrigger>
{{ fields }}
<AccordionContent class="p-2 space-y-5"> <AccordionContent>
<template v-for="(field, index) of fields" :key="field.key"> <template v-for="(field, index) of fields" :key="field.key">
<div class="mb-4 p-[1px]">
<AutoFormField <AutoFormField
:name="`${name}[${index}]`" :name="`${name}[${index}]`"
:label="name" :label="name"
@ -67,7 +75,7 @@ const itemShape = computed(() => {
:config="config as ConfigItem" :config="config as ConfigItem"
/> />
<div class="!mt-2 flex justify-end"> <div class="!my-4 flex justify-end">
<Button <Button
type="button" type="button"
size="icon" size="icon"
@ -77,7 +85,8 @@ const itemShape = computed(() => {
<TrashIcon /> <TrashIcon />
</Button> </Button>
</div> </div>
<Separator /> <Separator v-if="!field.isLast" />
</div>
</template> </template>
<Button <Button
@ -90,8 +99,10 @@ const itemShape = computed(() => {
Add Add
</Button> </Button>
</AccordionContent> </AccordionContent>
<FormMessage />
</AccordionItem> </AccordionItem>
</Accordion> </Accordion>
</slot> </slot>
</section> </FieldArray>
</template> </template>

View File

@ -48,7 +48,7 @@ const shapes = computed(() => {
<AccordionTrigger class="text-base"> <AccordionTrigger class="text-base">
{{ schema?.description || beautifyObjectName(name) }} {{ schema?.description || beautifyObjectName(name) }}
</AccordionTrigger> </AccordionTrigger>
<AccordionContent class="p-2 space-y-5"> <AccordionContent class="p-[1px] 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"

View File

@ -1,4 +1,4 @@
export { Form, Field as FormField } from 'vee-validate' export { Form, Field as FormField, FieldArray as FormFieldArray } from 'vee-validate'
export { default as FormItem } from './FormItem.vue' export { default as FormItem } from './FormItem.vue'
export { default as FormLabel } from './FormLabel.vue' export { default as FormLabel } from './FormLabel.vue'
export { default as FormControl } from './FormControl.vue' export { default as FormControl } from './FormControl.vue'

View File

@ -5,7 +5,6 @@ import { FORM_ITEM_INJECTION_KEY } from './FormItem.vue'
export function useFormField() { export function useFormField() {
const fieldContext = inject(FieldContextKey) const fieldContext = inject(FieldContextKey)
const fieldItemContext = inject(FORM_ITEM_INJECTION_KEY) const fieldItemContext = inject(FORM_ITEM_INJECTION_KEY)
const fieldState = { const fieldState = {
valid: useIsFieldValid(), valid: useIsFieldValid(),
isDirty: useIsFieldDirty(), isDirty: useIsFieldDirty(),