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([
{ name: '123', age: 30 },
{ name: '456', age: 30 },
]).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)
const { handleSubmit } = useForm({
validationSchema: formSchema,
})
const onSubmit = handleSubmit((values) => {
function onSubmit(values: Record<string, any>) {
toast({
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))),
})
})
}
</script>
<template>

View File

@ -1,10 +1,12 @@
<script setup lang="ts" generic="U extends ZodRawShape, T extends ZodObject<U>">
import { computed } from 'vue'
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 type { Config, ConfigItem, Shape } from './interface'
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<{
schema: T
@ -12,7 +14,7 @@ const props = defineProps<{
}>()
const emits = defineEmits<{
submit: [event: Event]
submit: [event: GenericObject]
}>()
const shapes = computed(() => {
@ -35,10 +37,16 @@ const shapes = computed(() => {
})
return val
})
const formSchema = computed(() => toTypedSchema(props.schema))
</script>
<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">
<slot
:shape="shape"
@ -54,5 +62,5 @@ const shapes = computed(() => {
</template>
<slot :shapes="shapes" />
</form>
</Form>
</template>

View File

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

View File

@ -48,7 +48,7 @@ const shapes = computed(() => {
<AccordionTrigger class="text-base">
{{ schema?.description || beautifyObjectName(name) }}
</AccordionTrigger>
<AccordionContent class="p-2 space-y-5">
<AccordionContent class="p-[1px] space-y-5">
<template v-for="(shape, key) in shapes" :key="key">
<AutoFormField
: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 FormLabel } from './FormLabel.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() {
const fieldContext = inject(FieldContextKey)
const fieldItemContext = inject(FORM_ITEM_INJECTION_KEY)
const fieldState = {
valid: useIsFieldValid(),
isDirty: useIsFieldDirty(),