Added several files to the AutoForm component for better form functionality including a file for each type of input field (checkbox, date, enum, file, number, radio-group, switch, etc). Managed the configurations in a separate file and handled dependencies to control form behaviours. This commit enhances form handling capabilities and simplifies the process for creating versatile forms.
118 lines
3.1 KiB
TypeScript
118 lines
3.1 KiB
TypeScript
"use client";
|
|
import { Form } from "@/components/ui/form";
|
|
import type React from "react";
|
|
import { useEffect } from "react";
|
|
import { type DefaultValues, type FormState, useForm } from "react-hook-form";
|
|
import type { z } from "zod";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
import { cn } from "@/lib/utils";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
|
|
import AutoFormObject from "@/components/auto-form/fields/object";
|
|
import type { Dependency, FieldConfig } from "@/components/auto-form/types";
|
|
import {
|
|
type ZodObjectOrWrapped,
|
|
getDefaultValues,
|
|
getObjectFormSchema,
|
|
} from "@/components/auto-form/utils";
|
|
|
|
export function AutoFormSubmit({
|
|
children,
|
|
className,
|
|
disabled,
|
|
}: {
|
|
children?: React.ReactNode;
|
|
className?: string;
|
|
disabled?: boolean;
|
|
}) {
|
|
return (
|
|
<Button type="submit" disabled={disabled} className={className}>
|
|
{children ?? "Submit"}
|
|
</Button>
|
|
);
|
|
}
|
|
|
|
function AutoForm<SchemaType extends ZodObjectOrWrapped>({
|
|
formSchema,
|
|
values: valuesProp,
|
|
onValuesChange: onValuesChangeProp,
|
|
onParsedValuesChange,
|
|
onSubmit: onSubmitProp,
|
|
fieldConfig,
|
|
children,
|
|
className,
|
|
dependencies,
|
|
}: {
|
|
formSchema: SchemaType;
|
|
values?: Partial<z.infer<SchemaType>>;
|
|
onValuesChange?: (values: Partial<z.infer<SchemaType>>) => void;
|
|
onParsedValuesChange?: (values: Partial<z.infer<SchemaType>>) => void;
|
|
onSubmit?: (values: z.infer<SchemaType>) => void;
|
|
fieldConfig?: FieldConfig<z.infer<SchemaType>>;
|
|
children?:
|
|
| React.ReactNode
|
|
| ((formState: FormState<z.infer<SchemaType>>) => React.ReactNode);
|
|
className?: string;
|
|
dependencies?: Dependency<z.infer<SchemaType>>[];
|
|
}) {
|
|
const objectFormSchema = getObjectFormSchema(formSchema);
|
|
const defaultValues: DefaultValues<z.infer<typeof objectFormSchema>> | null =
|
|
getDefaultValues(objectFormSchema, fieldConfig);
|
|
|
|
const form = useForm<z.infer<typeof objectFormSchema>>({
|
|
resolver: zodResolver(formSchema),
|
|
defaultValues: defaultValues ?? undefined,
|
|
values: valuesProp,
|
|
});
|
|
|
|
function onSubmit(values: z.infer<typeof formSchema>) {
|
|
const parsedValues = formSchema.safeParse(values);
|
|
if (parsedValues.success) {
|
|
onSubmitProp?.(parsedValues.data);
|
|
}
|
|
}
|
|
|
|
const values = form.watch();
|
|
// valuesString is needed because form.watch() returns a new object every time
|
|
const valuesString = JSON.stringify(values);
|
|
|
|
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
|
|
useEffect(() => {
|
|
onValuesChangeProp?.(values);
|
|
const parsedValues = formSchema.safeParse(values);
|
|
if (parsedValues.success) {
|
|
onParsedValuesChange?.(parsedValues.data);
|
|
}
|
|
}, [valuesString]);
|
|
|
|
const renderChildren =
|
|
typeof children === "function"
|
|
? children(form.formState as FormState<z.infer<SchemaType>>)
|
|
: children;
|
|
|
|
return (
|
|
<div className="w-full">
|
|
<Form {...form}>
|
|
<form
|
|
onSubmit={(e) => {
|
|
form.handleSubmit(onSubmit)(e);
|
|
}}
|
|
className={cn("space-y-5", className)}
|
|
>
|
|
<AutoFormObject
|
|
schema={objectFormSchema}
|
|
form={form}
|
|
dependencies={dependencies}
|
|
fieldConfig={fieldConfig}
|
|
/>
|
|
|
|
{renderChildren}
|
|
</form>
|
|
</Form>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default AutoForm;
|