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 }
This commit is contained in:
2026-06-07 23:04:48 +00:00 Unverified
parent e62202b2a8
commit acb16777d2
2 changed files with 59 additions and 4 deletions
+1
View File
@@ -4,6 +4,7 @@ export type FormSubmitResult =
| {
toast?: string;
close?: boolean;
reset?: boolean;
error?:
| string
| { what: string; where: string }
+58 -4
View File
@@ -105,7 +105,10 @@ function getInputValue(eventOrValue: unknown) {
return eventOrValue;
}
type FormProviderProps = Omit<React.ComponentProps<"form">, "id" | "onSubmit"> & {
type FormProviderProps = Omit<
React.ComponentProps<"form">,
"id" | "onSubmit"
> & {
// Unique Autosave Key
id: string;
onSubmit?: (form: FormData) => FormSubmitResult | Promise<FormSubmitResult>;
@@ -218,6 +221,10 @@ export function FormProvider({
if (close) closerRef.current?.click();
}
}
if ("reset" in res) {
const { reset } = res;
if (reset) clear();
}
}
} catch (error) {
const message =
@@ -230,7 +237,7 @@ export function FormProvider({
} finally {
setLoading(false);
}
}, [onSubmit, values]);
}, [onSubmit, values, clear]);
const contextValue = React.useMemo<FormContextValue>(
() => ({
@@ -245,7 +252,18 @@ export function FormProvider({
closeDialog,
setErrors,
}),
[id, values, errors, loading, setValue, updateValues, clear, submit, closeDialog, setErrors],
[
id,
values,
errors,
loading,
setValue,
updateValues,
clear,
submit,
closeDialog,
setErrors,
],
);
return (
@@ -391,7 +409,12 @@ export function FormAction({
loading: loadingComponent,
...props
}: FormActionProps) {
const { loading: formLoading, clear, closeDialog, setErrors } = useFormContext();
const {
loading: formLoading,
clear,
closeDialog,
setErrors,
} = useFormContext();
const [actionLoading, setActionLoading] = React.useState(false);
const loading = formLoading || actionLoading;
@@ -432,6 +455,10 @@ export function FormAction({
if (close) closeDialog();
}
}
if ("reset" in res) {
const { reset } = res;
if (reset) clear();
}
}
} catch (error) {
const message =
@@ -528,4 +555,31 @@ export function FormChoice({
return <>{children}</>;
}
export function FormIf({
children,
...conditions
}: {
children: React.ReactNode;
} & Record<string, unknown>) {
const { values } = useFormContext();
const match = Object.entries(conditions).every(
([name, equals]) => values[name] === equals,
);
return <div className={cn(!match && "hidden")}>{children}</div>;
}
export function FormWrapper({
children,
...props
}: React.ComponentProps<"div">) {
return (
<div {...props}>
{React.Children.map(children, (child) =>
React.isValidElement(child)
? React.cloneElement(child, props as Record<string, unknown>)
: child,
)}
</div>
);
}