Files
better-auth/examples/nuxt-example/components/ui/auto-form/AutoForm.vue
2024-09-27 13:36:20 +03:00

120 lines
3.2 KiB
Vue

<script setup lang="ts" generic="T extends ZodObjectOrWrapped">
import { computed, toRefs } from "vue";
import type { ZodAny, z } from "zod";
import { toTypedSchema } from "@vee-validate/zod";
import type { FormContext, GenericObject } from "vee-validate";
import {
type ZodObjectOrWrapped,
getBaseSchema,
getBaseType,
getDefaultValueInZodStack,
getObjectFormSchema,
} from "./utils";
import type { Config, ConfigItem, Dependency, Shape } from "./interface";
import AutoFormField from "./AutoFormField.vue";
import { provideDependencies } from "./dependencies";
import { Form } from "@/components/ui/form";
const props = defineProps<{
schema: T;
form?: FormContext<GenericObject>;
fieldConfig?: Config<z.infer<T>>;
dependencies?: Dependency<z.infer<T>>[];
}>();
const emits = defineEmits<{
submit: [event: z.infer<T>];
}>();
const { dependencies } = toRefs(props);
provideDependencies(dependencies);
const shapes = computed(() => {
// @ts-expect-error ignore {} not assignable to object
const val: { [key in keyof T]: Shape } = {};
const baseSchema = getObjectFormSchema(props.schema);
const shape = baseSchema.shape;
Object.keys(shape).forEach((name) => {
const item = shape[name] as ZodAny;
const baseItem = getBaseSchema(item) as ZodAny;
let options =
baseItem && "values" in baseItem._def
? (baseItem._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: baseItem,
};
});
return val;
});
const fields = computed(() => {
// @ts-expect-error ignore {} not assignable to object
const val: {
[key in keyof z.infer<T>]: {
shape: Shape;
fieldName: string;
config: ConfigItem;
};
} = {};
for (const key in shapes.value) {
const shape = shapes.value[key];
val[key as keyof z.infer<T>] = {
shape,
config: props.fieldConfig?.[key] as ConfigItem,
fieldName: key,
};
}
return val;
});
const formComponent = computed(() => (props.form ? "form" : Form));
const formComponentProps = computed(() => {
if (props.form) {
return {
onSubmit: props.form.handleSubmit((val) => emits("submit", val)),
};
} else {
const formSchema = toTypedSchema(props.schema);
return {
keepValues: true,
validationSchema: formSchema,
onSubmit: (val: GenericObject) => emits("submit", val),
};
}
});
</script>
<template>
<component
:is="formComponent"
v-bind="formComponentProps"
>
<slot name="customAutoForm" :fields="fields">
<template v-for="(shape, key) of shapes" :key="key">
<slot
:shape="shape"
:name="key.toString() as keyof z.infer<T>"
:field-name="key.toString()"
:config="fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem"
>
<AutoFormField
:config="fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem"
:field-name="key.toString()"
:shape="shape"
/>
</slot>
</template>
</slot>
<slot :shapes="shapes" />
</component>
</template>