'use client'; import React, { createContext, forwardRef, useContext, useId, useMemo } from 'react'; import { Controller, FormProvider, useFormContext, type ControllerFieldState, type ControllerRenderProps, type FieldPath, type FieldValues, type RegisterOptions, type UseControllerProps, type UseFormReturn, } from 'react-hook-form'; import { Surface, cn, surfaceClasses, useEffectiveSurface, } from '../common'; export type { UseFormReturn, FieldValues } from 'react-hook-form'; export type { FieldPath as Path } from 'react-hook-form'; /* ────────────────────────────────────────────────────────────────────────── PixelForm — shadcn-style wrapper around react-hook-form. Composable surface for: Root / Field / Item / Label / Control / Description / Message. Item generates linked ids + aria-* automatically. ────────────────────────────────────────────────────────────────────────── */ interface PixelFormItemCtxValue { /** id of the Control element */ id: string; /** id of the Description element (may not be rendered) */ descriptionId: string; /** id of the Message element (may not be rendered) */ messageId: string; } const PixelFormItemContext = createContext(null); function useItemCtx(): PixelFormItemCtxValue { const ctx = useContext(PixelFormItemContext); if (!ctx) { throw new Error('PixelForm.Label/Control/Description/Message must be used inside PixelForm.Item'); } return ctx; } interface PixelFormFieldCtxValue { name: string; } const PixelFormFieldContext = createContext(null); function useFieldName(): string | null { return useContext(PixelFormFieldContext)?.name ?? null; } /* ── Root ─────────────────────────────────────────────────────────────── */ /** Public prop bag for {@link PixelFormRoot}. */ export interface PixelFormRootProps { form: UseFormReturn; onSubmit: (data: T) => void | Promise; children: React.ReactNode; className?: string; surface?: Surface; } function PixelFormRootInner( { form, onSubmit, children, className, surface: surfaceProp }: PixelFormRootProps, ref: React.Ref, ) { const surface = useEffectiveSurface(surfaceProp); const s = surfaceClasses(surface); return (
{children}
); } type PixelFormRootGeneric = (( props: PixelFormRootProps & { ref?: React.Ref }, ) => React.ReactElement) & { displayName?: string }; export const PixelFormRoot = forwardRef(PixelFormRootInner) as PixelFormRootGeneric; PixelFormRoot.displayName = 'PixelForm.Root'; /* ── Field ────────────────────────────────────────────────────────────── */ /** Public prop bag for {@link PixelFormField}. */ export interface PixelFormFieldProps< TFieldValues extends FieldValues = FieldValues, TName extends FieldPath = FieldPath, > { name: TName; rules?: Omit, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'>; defaultValue?: UseControllerProps['defaultValue']; shouldUnregister?: boolean; render: (args: { field: ControllerRenderProps; fieldState: ControllerFieldState; }) => React.ReactElement; } export function PixelFormField< TFieldValues extends FieldValues = FieldValues, TName extends FieldPath = FieldPath, >({ name, rules, defaultValue, shouldUnregister, render }: PixelFormFieldProps) { const ctx = useMemo(() => ({ name: name as string }), [name]); return ( name={name} rules={rules} defaultValue={defaultValue} shouldUnregister={shouldUnregister} render={render} /> ); } (PixelFormField as React.FC & { displayName?: string }).displayName = 'PixelForm.Field'; /* ── Item ─────────────────────────────────────────────────────────────── */ /** Public prop bag for {@link PixelFormItem}. */ export interface PixelFormItemProps extends React.HTMLAttributes { children: React.ReactNode; } export const PixelFormItem = forwardRef(function PixelFormItem( { children, className, ...rest }, ref, ) { const baseId = useId(); const value = useMemo( () => ({ id: `${baseId}-control`, descriptionId: `${baseId}-description`, messageId: `${baseId}-message`, }), [baseId], ); return (
{children}
); }); PixelFormItem.displayName = 'PixelForm.Item'; /* ── Label ────────────────────────────────────────────────────────────── */ /** Public prop bag for {@link PixelFormLabel}. */ export interface PixelFormLabelProps extends React.LabelHTMLAttributes { surface?: Surface; } export const PixelFormLabel = forwardRef(function PixelFormLabel( { children, className, surface: surfaceProp, ...rest }, ref, ) { const { id } = useItemCtx(); const surface = useEffectiveSurface(surfaceProp); const s = surfaceClasses(surface); return ( ); }); PixelFormLabel.displayName = 'PixelForm.Label'; /* ── Control ──────────────────────────────────────────────────────────── */ /** Public prop bag for {@link PixelFormControl}. Wraps a single child and clones aria-*/ export interface PixelFormControlProps { children: React.ReactElement; } interface ControlChildProps { id?: string; 'aria-describedby'?: string; 'aria-invalid'?: boolean | 'true' | 'false'; } export const PixelFormControl = forwardRef(function PixelFormControl( { children }, ref, ) { const { id, descriptionId, messageId } = useItemCtx(); const name = useFieldName(); const formCtx = useFormContext(); const error = name && formCtx ? (formCtx.getFieldState(name, formCtx.formState).error ?? undefined) : undefined; const hasError = !!error; const describedBy = [descriptionId, hasError ? messageId : null].filter(Boolean).join(' '); const child = React.Children.only(children) as React.ReactElement; return React.cloneElement(child, { id, 'aria-describedby': describedBy || undefined, 'aria-invalid': hasError ? 'true' : undefined, ref: ref as React.Ref, } as ControlChildProps & { ref?: React.Ref }); }); PixelFormControl.displayName = 'PixelForm.Control'; /* ── Description ─────────────────────────────────────────────────────── */ /** Public prop bag for {@link PixelFormDescription}. */ export interface PixelFormDescriptionProps extends React.HTMLAttributes { surface?: Surface; } export const PixelFormDescription = forwardRef(function PixelFormDescription( { children, className, surface: surfaceProp, ...rest }, ref, ) { const { descriptionId } = useItemCtx(); const surface = useEffectiveSurface(surfaceProp); const s = surfaceClasses(surface); return (

{children}

); }); PixelFormDescription.displayName = 'PixelForm.Description'; /* ── Message ─────────────────────────────────────────────────────────── */ /** Public prop bag for {@link PixelFormMessage}. */ export interface PixelFormMessageProps extends React.HTMLAttributes { surface?: Surface; } export const PixelFormMessage = forwardRef(function PixelFormMessage( { children, className, surface: surfaceProp, ...rest }, ref, ) { const { messageId } = useItemCtx(); const name = useFieldName(); const formCtx = useFormContext(); const error = name && formCtx ? (formCtx.getFieldState(name, formCtx.formState).error ?? undefined) : undefined; const body = children ?? error?.message; const surface = useEffectiveSurface(surfaceProp); const s = surfaceClasses(surface); if (body == null || body === false || body === '') return null; return (

{body}

); }); PixelFormMessage.displayName = 'PixelForm.Message'; /* ── Namespace export ────────────────────────────────────────────────── Match PixelDrawer / PixelPopover dot-notation: each subcomponent is a distinct top-level named export above (tree-shakeable) AND attached to the Root for the `` ergonomics. */ type PixelFormNamespace = PixelFormRootGeneric & { Root: typeof PixelFormRoot; Field: typeof PixelFormField; Item: typeof PixelFormItem; Label: typeof PixelFormLabel; Control: typeof PixelFormControl; Description: typeof PixelFormDescription; Message: typeof PixelFormMessage; }; const PixelFormBase = PixelFormRoot as PixelFormNamespace; PixelFormBase.Root = PixelFormRoot; PixelFormBase.Field = PixelFormField; PixelFormBase.Item = PixelFormItem; PixelFormBase.Label = PixelFormLabel; PixelFormBase.Control = PixelFormControl; PixelFormBase.Description = PixelFormDescription; PixelFormBase.Message = PixelFormMessage; export const PixelForm = PixelFormBase;