{ "$schema": "https://blode.co/ui/schema/registry-item.json", "name": "field", "title": "Field", "author": "Matthew Blode", "description": "A form field wrapper that pairs a label, input, and helper text.", "registryDependencies": ["label", "separator"], "files": [ { "path": "ui/field.tsx", "content": "\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type { VariantProps } from \"class-variance-authority\";\nimport { useMemo } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Label } from \"@/components/ui/label\";\nimport { Separator } from \"@/components/ui/separator\";\n\nconst FieldSet = ({ className, ...props }: React.ComponentProps<\"fieldset\">) => (\n [data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3\",\n className,\n )}\n data-slot=\"field-set\"\n {...props}\n />\n);\n\nconst FieldLegend = ({\n className,\n variant = \"legend\",\n ...props\n}: React.ComponentProps<\"legend\"> & { variant?: \"legend\" | \"label\" }) => (\n \n);\n\nconst FieldGroup = ({ className, ...props }: React.ComponentProps<\"div\">) => (\n [data-slot=field-group]]:gap-4\",\n className,\n )}\n data-slot=\"field-group\"\n {...props}\n />\n);\n\nconst fieldVariants = cva(\"group/field flex w-full gap-3 data-[invalid=true]:text-destructive\", {\n defaultVariants: {\n orientation: \"vertical\",\n },\n variants: {\n orientation: {\n horizontal: [\n \"flex-row items-center\",\n \"[&>[data-slot=field-label]]:flex-auto\",\n \"has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px\",\n ],\n responsive: [\n \"@md/field-group:flex-row flex-col @md/field-group:items-center @md/field-group:[&>*]:w-auto [&>*]:w-full [&>.sr-only]:w-auto\",\n \"@md/field-group:[&>[data-slot=field-label]]:flex-auto\",\n \"@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px\",\n ],\n vertical: [\"flex-col [&>*]:w-full [&>.sr-only]:w-auto\"],\n },\n },\n});\n\nconst Field = ({\n className,\n orientation = \"vertical\",\n ...props\n}: React.ComponentProps<\"fieldset\"> & VariantProps) => (\n \n);\n\nconst FieldContent = ({ className, ...props }: React.ComponentProps<\"div\">) => (\n \n);\n\nconst FieldLabel = ({ className, ...props }: React.ComponentProps) => (\n [data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4\",\n \"has-data-[state=checked]:border-primary has-data-[state=checked]:bg-primary/5 dark:has-data-[state=checked]:bg-primary/10\",\n className,\n )}\n data-slot=\"field-label\"\n {...props}\n />\n);\n\nconst FieldTitle = ({ className, ...props }: React.ComponentProps<\"div\">) => (\n \n);\n\nconst FieldDescription = ({ className, ...props }: React.ComponentProps<\"p\">) => (\n a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4\",\n className,\n )}\n data-slot=\"field-description\"\n {...props}\n />\n);\n\nconst FieldSeparator = ({\n children,\n className,\n ...props\n}: React.ComponentProps<\"div\"> & {\n children?: React.ReactNode;\n}) => (\n \n \n {children && (\n \n {children}\n \n )}\n \n);\n\nconst FieldError = ({\n className,\n children,\n errors,\n ...props\n}: React.ComponentProps<\"div\"> & {\n errors?: ({ message?: string } | undefined)[];\n}) => {\n const content = useMemo(() => {\n if (children) {\n return children;\n }\n\n if (!errors?.length) {\n return null;\n }\n\n const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()];\n\n if (uniqueErrors?.length === 1) {\n return uniqueErrors[0]?.message;\n }\n\n return (\n
    \n {uniqueErrors.map((error, index) => error?.message &&
  • {error.message}
  • )}\n
\n );\n }, [children, errors]);\n\n if (!content) {\n return null;\n }\n\n return (\n \n {content}\n \n );\n};\n\nexport {\n Field,\n FieldLabel,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLegend,\n FieldSeparator,\n FieldSet,\n FieldContent,\n FieldTitle,\n};\n", "type": "registry:ui", "target": "" } ], "type": "registry:ui" }