From 66ac8a983d264c3c6c48cf4bd040327f064d1d39 Mon Sep 17 00:00:00 2001 From: Andy Rae Date: Sun, 13 Jul 2025 17:19:21 +0100 Subject: [PATCH 01/30] Add form component --- app/next-client-app/components/ui/form.tsx | 173 +++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 app/next-client-app/components/ui/form.tsx diff --git a/app/next-client-app/components/ui/form.tsx b/app/next-client-app/components/ui/form.tsx new file mode 100644 index 000000000..27c6e3551 --- /dev/null +++ b/app/next-client-app/components/ui/form.tsx @@ -0,0 +1,173 @@ +"use client" + +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { Slot } from "@radix-ui/react-slot" +import { + Formik, + FormikContextType, + useFormikContext, + Field, + FieldProps, + type FormikConfig, + type FormikValues, +} from "formik" + +import { cn } from "@/lib/utils" +import { Label } from "@/components/ui/label" + +const Form = Formik + +type FormFieldContextValue = { + name: string +} + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue +) + +interface FormFieldProps { + name: string + children: (props: FieldProps) => React.ReactNode +} + +const FormField = ({ name, children }: FormFieldProps) => { + return ( + + + {children} + + + ) +} + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext) + const itemContext = React.useContext(FormItemContext) + const formik = useFormikContext() + + if (!fieldContext) { + throw new Error("useFormField should be used within ") + } + + const { id } = itemContext + const fieldName = fieldContext.name + + // Get field state from Formik + const fieldState = { + error: formik.errors[fieldName] as string | undefined, + invalid: !!(formik.errors[fieldName] && formik.touched[fieldName]), + isDirty: formik.dirty, + isTouched: formik.touched[fieldName] as boolean | undefined, + } + + return { + id, + name: fieldName, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + } +} + +type FormItemContextValue = { + id: string +} + +const FormItemContext = React.createContext( + {} as FormItemContextValue +) + +function FormItem({ className, ...props }: React.ComponentProps<"div">) { + const id = React.useId() + + return ( + +
+ + ) +} + +function FormLabel({ + className, + ...props +}: React.ComponentProps) { + const { error, formItemId } = useFormField() + + return ( +