From 476c3b54762d6f3e2e9aabf9fd091566e0bb8e3b Mon Sep 17 00:00:00 2001 From: Michael Kinder Date: Mon, 12 Feb 2024 19:24:12 -0500 Subject: [PATCH 01/14] Add UI components for label, input, popover, and form --- components/ui/form.tsx | 181 +++++++++++++++++++++++++++++++++ components/ui/input.tsx | 25 +++++ components/ui/label.tsx | 26 +++++ components/ui/popover.tsx | 33 ++++++ package-lock.json | 205 +++++++++++++++++++++++++++++++++++++- package.json | 7 +- 6 files changed, 475 insertions(+), 2 deletions(-) create mode 100644 components/ui/form.tsx create mode 100644 components/ui/input.tsx create mode 100644 components/ui/label.tsx create mode 100644 components/ui/popover.tsx diff --git a/components/ui/form.tsx b/components/ui/form.tsx new file mode 100644 index 00000000..b1a5c1ed --- /dev/null +++ b/components/ui/form.tsx @@ -0,0 +1,181 @@ +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { Slot } from "@radix-ui/react-slot"; +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from "react-hook-form"; + +import { cn } from "@/lib/utils"; +import { Label } from "@/components/ui/label"; + +const Form = FormProvider; + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName; +}; + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue +); + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ); +}; + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error("useFormField should be used within "); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + 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 +); + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + +
+ + ); +}); +FormItem.displayName = "FormItem"; + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return ( +