Field

Conteneur de mise en page associant un libelle, un controle, une description et un message d'erreur. Installez Field depuis le registre VLLNT UI avec la CLI shadcn.

Signaler un bug

Conteneur de mise en page associant un libelle, un controle, une description et un message d'erreur. Fait partie de la famille form dans VLLNT UI, il est publie comme entree de registre lisible par machine — copiez la source directement dans votre application avec la CLI shadcn et possedez-la, sans dependance d'execution a une bibliotheque de composants.

Apercu

Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.

Installation

Ajoutez Field a votre projet avec la CLI shadcn. La source arrive dans votre code, prete a etre adaptee :

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/field.json

Source

"use client"; import * as React from "react"; import { Slot } from "@radix-ui/react-slot"; import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "../../lib/utils"; import { Label } from "../label/label"; type FieldContextValue = { descriptionId: string; errorId: string; id: string; invalid: boolean; }; const FieldContext = React.createContext<FieldContextValue | null>(null); function useFieldContext(): FieldContextValue { const context = React.use(FieldContext); if (!context) { throw new Error("Field subcomponents must be used within a Field"); } return context; } const fieldVariants = cva("flex", { defaultVariants: { orientation: "vertical", }, variants: { orientation: { horizontal: "flex-row items-center gap-3", vertical: "flex-col gap-1.5", }, }, }); /** Groups a label, control, description, and error message for one input. */ export type FieldProps = { children: React.ReactNode; className?: string; invalid?: boolean; } & VariantProps<typeof fieldVariants>; const Field = ({ children, className, invalid = false, orientation, ref, }: FieldProps & { ref?: React.Ref<HTMLDivElement> }) => { const id = React.useId(); const value = React.useMemo<FieldContextValue>( () => ({ descriptionId: `${id}-description`, errorId: `${id}-error`, id: `${id}-control`, invalid, }), [id, invalid], ); return ( <FieldContext.Provider value={value}> <div className={cn(fieldVariants({ orientation }), className)} data-slot="field" ref={ref} > {children} </div> </FieldContext.Provider> ); }; Field.displayName = "Field"; /** Label wired to the field control through htmlFor. */ export type FieldLabelProps = React.ComponentPropsWithoutRef<typeof Label>; const FieldLabel = ({ className, ref, ...props }: FieldLabelProps & { ref?: React.Ref<React.ComponentRef<typeof Label>> }) => { const { id, invalid } = useFieldContext(); return ( <Label className={cn(invalid && "text-destructive", className)} htmlFor={id} ref={ref} {...props} /> ); }; FieldLabel.displayName = "FieldLabel"; /** Wraps the control and injects id plus aria wiring from the Field. */ export type FieldControlProps = { children: React.ReactNode; }; const FieldControl = ({ children, ref, }: FieldControlProps & { ref?: React.Ref<HTMLElement> }) => { const { descriptionId, errorId, id, invalid } = useFieldContext(); const describedBy = [descriptionId, invalid ? errorId : null].filter(Boolean).join(" ") || undefined; return ( <Slot aria-describedby={describedBy} aria-invalid={invalid || undefined} id={id} ref={ref} > {children} </Slot> ); }; FieldControl.displayName = "FieldControl"; /** Helper text describing the field, announced to assistive tech. */ export type FieldDescriptionProps = React.ComponentPropsWithoutRef<"p">; const FieldDescription = ({ className, ref, ...props }: FieldDescriptionProps & { ref?: React.Ref<HTMLParagraphElement> }) => { const { descriptionId } = useFieldContext(); return ( <p className={cn("text-sm text-muted-foreground", className)} id={descriptionId} ref={ref} {...props} /> ); }; FieldDescription.displayName = "FieldDescription"; /** Validation message that renders when children are present. */ export type FieldErrorProps = React.ComponentPropsWithoutRef<"p">; const FieldError = ({ children, className, ref, ...props }: FieldErrorProps & { ref?: React.Ref<HTMLParagraphElement> }) => { const { errorId } = useFieldContext(); if (!children) { return null; } return ( <p className={cn("text-sm font-medium text-destructive", className)} id={errorId} ref={ref} role="alert" {...props} > {children} </p> ); }; FieldError.displayName = "FieldError"; export { Field, FieldControl, FieldDescription, FieldError, FieldLabel, fieldVariants, };

Stories

Explorez chaque variante et etat dans le Storybook interactif :

Apercu

Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.

Dependances

  • @vllnt/ui@^0.3.0