import { get } from "lodash" import { FieldPath, FieldValues, UseFormReturn } from "react-hook-form" import { Get } from "type-fest" export type NestedForm = UseFormReturn<{ __nested__: TValues }> & { path(this: void): `__nested__` path>( this: void, p?: TPath ): `__nested__.${TPath}` get(this: void, obj: TObj): Get get, TObj>( this: void, obj: TObj, p?: TPath ): Get } /** * Utility function to create a nested form. This is useful when you want to use a reusable form component within a form. * This is especially useful when you want to use a reusable form component within a form multiple times. For example, an form * that contains both a billing and a shipping address. * @example * const MyForm = () => { * const form = useForm<{ email: string, shipping_address: AddressPayload, billing_address: AddressPayload }>() * * return ( *
* * * *
* ) * } * * type AddressFormProps = { * form: NestedForm * } * * const AddressForm = ({ form }: AddressFormProps) => { * const { register, path } = form * * return ( *
* // path("city") resolves as "shipping_address.city" or "billing_address.city" depending on the second argument passed to nestedForm * *
* ) * } */ export function nestedForm( form: UseFormReturn | NestedForm ): NestedForm export function nestedForm< TValues extends FieldValues, TPath extends FieldPath >( form: UseFormReturn | NestedForm, path: TPath ): NestedForm> export function nestedForm( form: UseFormReturn | NestedForm, path?: string | number ): NestedForm { return { ...form, path(field?: string | number) { const fullPath = path && field ? `${path}.${field}` : path ? path : field if ("path" in form) { return form.path(path as any) } return (fullPath || "") as any }, get(obj: any, field?: string | number) { const fullPath = path && field ? `${path}.${field}` : path ? path : field if ("get" in form) { return form.get(path) } return fullPath ? get(obj, fullPath) : obj }, } }