Files
buzz/components/form-submit.ts
T
dmgnr acb16777d2 feat: add FormIf, FormWrapper, and form reset support
- FormIf for conditional field rendering based on form values
- FormWrapper for wrapping inputs with shared props
- Reset support in FormProvider and FormAction via return { reset: true }
2026-06-07 23:04:48 +00:00

67 lines
2.0 KiB
TypeScript

import type { z } from "zod";
export type FormSubmitResult =
| {
toast?: string;
close?: boolean;
reset?: boolean;
error?:
| string
| { what: string; where: string }
| { what: string; where: string }[];
}
| undefined;
export function defineFormHandler<TSchema extends z.ZodType>(
schema: TSchema,
action: (input: z.output<TSchema>) => Promise<FormSubmitResult>,
): (form: FormData) => Promise<FormSubmitResult>;
export function defineFormHandler<
TAction extends (form: FormData) => Promise<FormSubmitResult>,
>(action: TAction): TAction;
export function defineFormHandler(
schemaOrAction: z.ZodType | ((form: FormData) => Promise<FormSubmitResult>),
action?: (input: unknown) => Promise<FormSubmitResult>,
): (form: FormData) => Promise<FormSubmitResult> {
if (!action)
return schemaOrAction as (form: FormData) => Promise<FormSubmitResult>;
const schema = schemaOrAction as z.ZodType;
return async (form: FormData) => {
// "use server";
const raw = Object.fromEntries([...form.entries()]);
const parsed = schema.safeParse(raw);
if (!parsed.success) {
return {
error: parsed.error.issues.map((issue) => ({
what: issue.message,
where: issue.path.join("."),
})),
};
}
return action(parsed.data);
};
}
export function formParse<TSchema extends z.ZodType>(
schema: TSchema,
data: FormData,
): { $: z.output<TSchema>; error: { what: string; where: string }[] | string } {
const raw = Object.fromEntries([...data.entries()]);
const parsed = schema.safeParse(raw);
if (!parsed.success) {
if (
parsed.error.issues.length === 1 &&
parsed.error.issues[0].path.length === 0
)
return { error: parsed.error.issues[0].message, $: undefined as any };
return {
error: parsed.error.issues.map((issue) => ({
what: issue.message,
where: issue.path.join("."),
})),
$: undefined as any,
};
}
return { $: parsed.data, error: undefined as any };
}