diff --git a/src/registry/components/ui/field-baseui.tsx b/src/registry/components/ui/field-baseui.tsx new file mode 100644 index 0000000..ab3d21c --- /dev/null +++ b/src/registry/components/ui/field-baseui.tsx @@ -0,0 +1,137 @@ +"use client"; + +import * as React from "react"; +import { Field as BaseField } from "@base-ui/react/field"; +import { cn } from "@/lib/utils"; +import { cva, type VariantProps } from "class-variance-authority"; + +const fieldVariants = cva( + "group/field flex w-full gap-3 data-[invalid=true]:text-destructive", + { + variants: { + orientation: { + vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"], + horizontal: [ + "flex-row items-center", + "[&>[data-slot=field-label]]:flex-auto", + "has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", + ], + responsive: [ + "flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto", + "@md/field-group:[&>[data-slot=field-label]]:flex-auto", + "@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", + ], + }, + }, + defaultVariants: { + orientation: "vertical", + }, + }, +); + +const FieldRoot = React.forwardRef( + ({ className, ...props }, ref) => ( + .sr-only]:w-auto", className)} + {...props} + /> + ), +); +FieldRoot.displayName = "FieldRoot"; + +const FieldItem = React.forwardRef( + ({ className, ...props }, ref) => ( + [data-slot=field-group]]:gap-4", + className, + )} + {...props} + /> + ), +); +FieldItem.displayName = "FieldItem"; + +const FieldLabel = React.forwardRef( + ({ className, ...props }, ref) => ( + [data-slot=field-root]]:w-full has-[>[data-slot=field-root]]:flex-col has-[>[data-slot=field-root]]:rounded-md has-[>[data-slot=field-root]]:border [&>*]:data-[slot=field-root]:p-4", + "has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10", + "data-[invalid]:text-destructive", + className, + )} + {...props} + /> + ), +); +FieldLabel.displayName = "FieldLabel"; + +const FieldControl = React.forwardRef( + ({ className, ...props }, ref) => ( + + ), +); +FieldControl.displayName = "FieldControl"; + +const FieldDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", + className, + )} + {...props} + /> +)); +FieldDescription.displayName = "FieldDescription"; + +const FieldError = React.forwardRef< + HTMLParagraphElement, + BaseField.Error.Props +>(({ className, children, ...props }, ref) => { + return ( + + {children} + + ); +}); +FieldError.displayName = "FieldError"; + +export { + FieldRoot, + FieldLabel, + FieldControl, + FieldDescription, + FieldError, + FieldItem, +}; diff --git a/src/registry/components/ui/fieldset-baseui.tsx b/src/registry/components/ui/fieldset-baseui.tsx new file mode 100644 index 0000000..b9eff6a --- /dev/null +++ b/src/registry/components/ui/fieldset-baseui.tsx @@ -0,0 +1,40 @@ +"use client"; + +import * as React from "react"; +import { Fieldset as BaseFieldset} from "@base-ui/react/fieldset"; +import { cn } from "@/lib/utils"; + +const FieldsetRoot = React.forwardRef( + ({ className, ...props }, ref) => ( + [data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3", + className, + )} + {...props} + /> + ), +); +FieldsetRoot.displayName = "FieldsetRoot"; + +const FieldsetLegend = React.forwardRef< + HTMLDivElement, + BaseFieldset.Legend.Props +>(({ className, ...props }, ref) => ( + +)); +FieldsetLegend.displayName = "FieldsetLegend"; +export { FieldsetRoot, FieldsetLegend }; diff --git a/src/registry/components/ui/form-tanstack-baseui.tsx b/src/registry/components/ui/form-tanstack-baseui.tsx new file mode 100644 index 0000000..f99e873 --- /dev/null +++ b/src/registry/components/ui/form-tanstack-baseui.tsx @@ -0,0 +1,133 @@ +"use client"; + +import { createFormHookContexts, createFormHook } from "@tanstack/react-form"; +import * as React from "react"; + +import { cn } from "@/lib/utils"; +// import { Label } from "@/components/ui/label"; +import { + FieldLabel as FieldLabelPrimitive, + FieldControl as FieldControlPrimitive, + FieldItem, + FieldDescription as FieldDescriptionPrimitive, + FieldError as FieldErrorPrimitive, +} from "./field-baseui"; + +const { fieldContext, formContext, useFieldContext } = createFormHookContexts(); + +const { useAppForm } = createFormHook({ + fieldComponents: { + Label: FieldLabel, + Control: FieldControl, + Description: FieldDescription, + Message: FieldError, + }, + formComponents: { + Item: FormItem, + }, + fieldContext, + formContext, +}); + +const useFormField = () => { + const itemContext = React.useContext(FormItemContext); + const fieldContext = useFieldContext(); + + 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`, + ...fieldContext.state.meta, + }; +}; + +type FormItemContextValue = { + id: string; +}; + +const FormItemContext = React.createContext( + {} as FormItemContextValue, +); + +function FormItem({ className, ...props }: React.ComponentProps<"div">) { + const id = React.useId(); + + return ( + + + + ); +} +function FieldLabel({ + className, + ...props +}: React.ComponentProps) { + const { formItemId, isValid } = useFormField(); + + return ( + + ); +} + +function FieldControl({ + className, + ...props +}: React.ComponentProps) { + const { formItemId, isValid } =useFormField(); + return( + + ) +} + +function FieldDescription({ className, ...props }: React.ComponentProps) { + const { formDescriptionId } = useFormField(); + + return ( + + ); +} + +function FieldError({ className, ...props }: React.ComponentProps) { + const { formMessageId, isValid, errors } = useFormField(); + + if (props.children) return props.children; + + const body = isValid + ? props.children + : String(errors.map((error) => error.message).join(", ") ?? ""); + + if (!body) return null; + + return ( + + {body} + + ); +} + +export { useAppForm };