import { useState, ReactNode, PropsWithoutRef } from "react" import { FormProvider, useForm, UseFormProps } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import { z } from "zod" export interface FormProps> extends Omit, "onSubmit"> { /** All your form fields */ children?: ReactNode /** Text to display in the submit button */ submitText?: string schema?: S onSubmit: (values: z.infer) => Promise initialValues?: UseFormProps>["defaultValues"] } interface OnSubmitResult { FORM_ERROR?: string [prop: string]: any } export const FORM_ERROR = "FORM_ERROR" export function Form>({ children, submitText, schema, initialValues, onSubmit, ...props }: FormProps) { const ctx = useForm>({ mode: "onBlur", resolver: schema ? zodResolver(schema) : undefined, defaultValues: initialValues, }) const [formError, setFormError] = useState(null) return (
{ const result = (await onSubmit(values)) || {} for (const [key, value] of Object.entries(result)) { if (key === FORM_ERROR) { setFormError(value) } else { ctx.setError(key as any, { type: "submit", message: value, }) } } })} className="form" {...props} > {/* Form fields supplied as children are rendered here */} {children} {formError && (
{formError}
)} {submitText && ( )}
) } export default Form