Files
better-auth/examples/nuxt-example/components/ui/auto-form/AutoFormFieldArray.vue
Bereket Engida 9f2e45b8c7 chore: cleanup
2025-01-06 14:30:39 +03:00

103 lines
2.9 KiB
Vue

<script setup lang="ts" generic="T extends z.ZodAny">
import * as z from "zod";
import { computed, provide } from "vue";
import { FieldContextKey, useField } from "vee-validate";
import type { Config } from "./interface";
import { getBaseType } from "./utils";
const props = defineProps<{
fieldName: string;
required?: boolean;
config?: Config<T>;
schema?: z.ZodArray<T>;
disabled?: boolean;
}>();
function isZodArray(
item: z.ZodArray<any> | z.ZodDefault<any>,
): item is z.ZodArray<any> {
return item instanceof z.ZodArray;
}
function isZodDefault(
item: z.ZodArray<any> | z.ZodDefault<any>,
): item is z.ZodDefault<any> {
return item instanceof z.ZodDefault;
}
const itemShape = computed(() => {
if (!props.schema) return;
const schema: z.ZodAny = isZodArray(props.schema)
? props.schema._def.type
: isZodDefault(props.schema)
? // @ts-expect-error missing schema
props.schema._def.innerType._def.type
: null;
return {
type: getBaseType(schema),
schema,
};
});
const fieldContext = useField(props.fieldName);
// @ts-expect-error ignore missing `id`
provide(FieldContextKey, fieldContext);
</script>
<template>
<FieldArray v-slot="{ fields, remove, push }" as="section" :name="fieldName">
<slot v-bind="props">
<Accordion type="multiple" class="w-full" collapsible :disabled="disabled" as-child>
<FormItem>
<AccordionItem :value="fieldName" class="border-none">
<AccordionTrigger>
<AutoFormLabel class="text-base" :required="required">
{{ schema?.description || beautifyObjectName(fieldName) }}
</AutoFormLabel>
</AccordionTrigger>
<AccordionContent>
<template v-for="(field, index) of fields" :key="field.key">
<div class="mb-4 p-1">
<AutoFormField
:field-name="`${fieldName}[${index}]`"
:label="fieldName"
:shape="itemShape!"
:config="config as ConfigItem"
/>
<div class="!my-4 flex justify-end">
<Button
type="button"
size="icon"
variant="secondary"
@click="remove(index)"
>
<TrashIcon :size="16" />
</Button>
</div>
<Separator v-if="!field.isLast" />
</div>
</template>
<Button
type="button"
variant="secondary"
class="mt-4 flex items-center"
@click="push(null)"
>
<PlusIcon class="mr-2" :size="16" />
Add
</Button>
</AccordionContent>
<FormMessage />
</AccordionItem>
</FormItem>
</Accordion>
</slot>
</FieldArray>
</template>