{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "field",
  "title": "Field",
  "description": "폼 필드 묶음 — 라벨·설명·오류·그룹·set·제목 10종, invalid/disabled 상태 context 전파, Label 재사용.",
  "dependencies": [
    "@unikit-bridge/ui",
    "class-variance-authority",
    "clsx",
    "tailwind-merge",
    "tailwindcss"
  ],
  "registryDependencies": [
    "utils",
    "label"
  ],
  "files": [
    {
      "path": "registry/ui/field.tsx",
      "content": "// Field — shadcn/ui new-york-v4 field（248줄）을 UniKit UI 유니버설로 이식.\n//\n// 포팅 판단（radix/base 엔진 의존 제거）:\n// - Radix Field가 data-* 속성으로 흘리던 상태（invalid·disabled）는 Field의\n//   boolean prop → 최소한의 React context로 전달하고, 소비 컴포넌트\n//   （FieldLabel·FieldTitle）가 조건부 클래스로 받는다. 원본의\n//   data-invalid=true → text-destructive（루트 상속）·\n//   group-data disabled → opacity-50 치환이다.\n// - FieldLabel은 원본처럼 ./label의 Label을 재사용한다（양쪽 트리 동일 상대\n//   경로 — registry 복사 구조에서도 성립）.\n// - fieldset/legend 시맨틱은 브리지 프리미티브（bridge role 계약에 group 없음）로\n//   표현 불가 → FieldSet은 View, FieldLegend는 Text로 평면화.\n// - orientation \"responsive\"（@container 쿼리）은 컨테이너 쿼리가 없어 제거 —\n//   vertical/horizontal만 유지. horizontal의 라벨 flex-auto는 context\n//   orientation으로 FieldLabel이 받는다（원본 자식 셀렉터 치환）.\n// - has-자손·nth-last-2·자식 앵커 등 자식/상태 셀렉터 토큰은 제거（네이티브에\n//   data 속성·셀렉터 없음）.\n// - FieldSeparator가 감싸던 Separator는 인라인 View（h-px bg-border）로 대리\n//   （신규 파일 6종 제한 — separator.tsx 생성 불가）.\n// - FieldError의 목록 글머리（list-disc）는 글리프 \"• \" 접두로 치환.\nimport {\n  createContext,\n  forwardRef,\n  useContext,\n  useMemo,\n  type ReactNode,\n} from \"react\";\nimport {\n  Text,\n  View,\n  type BridgeTextRef,\n  type BridgeViewRef,\n  type TextProps,\n  type ViewProps,\n} from \"@unikit-bridge/ui/primitives\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Label, type LabelProps } from \"./label\";\n\n// Field가 제공하는 상태 — 라벨·타이틀 스타일 연동의 단일 출처.\ntype FieldContextValue = {\n  invalid: boolean;\n  disabled: boolean;\n  orientation: \"vertical\" | \"horizontal\";\n};\n\nconst FieldContext = createContext<FieldContextValue | null>(null);\n\n// ---------------------------------------------------------------------------\n// FieldSet — 폼 묶음（원본 <fieldset> → View 평면화）.\n// ---------------------------------------------------------------------------\nexport type FieldSetProps = ViewProps;\n\nconst FieldSet = forwardRef<BridgeViewRef, FieldSetProps>(\n  ({ className, ...props }, ref) => (\n    <View\n      ref={ref}\n      className={cn(\"flex flex-col gap-6\", className)}\n      {...props}\n    />\n  ),\n);\nFieldSet.displayName = \"FieldSet\";\n\n// ---------------------------------------------------------------------------\n// FieldLegend — 묶음 제목（원본 <legend> → Text 평면화）.\n// ---------------------------------------------------------------------------\nconst fieldLegendVariants = cva(\"font-medium\", {\n  variants: {\n    variant: {\n      legend: \"text-base\",\n      label: \"text-sm\",\n    },\n  },\n  defaultVariants: {\n    variant: \"legend\",\n  },\n});\n\nexport type FieldLegendProps = TextProps &\n  VariantProps<typeof fieldLegendVariants>;\n\nconst FieldLegend = forwardRef<BridgeTextRef, FieldLegendProps>(\n  ({ className, variant, ...props }, ref) => (\n    <Text\n      ref={ref}\n      className={cn(\n        \"mb-3 font-sans text-foreground\",\n        fieldLegendVariants({ variant }),\n        className,\n      )}\n      {...props}\n    />\n  ),\n);\nFieldLegend.displayName = \"FieldLegend\";\n\n// ---------------------------------------------------------------------------\n// FieldGroup — 필드 묶음（원본 gap-7 유지, 중첩 gap-4 셀렉터는 제거）.\n// ---------------------------------------------------------------------------\nexport type FieldGroupProps = ViewProps;\n\nconst FieldGroup = forwardRef<BridgeViewRef, FieldGroupProps>(\n  ({ className, ...props }, ref) => (\n    <View\n      ref={ref}\n      className={cn(\"flex w-full flex-col gap-7\", className)}\n      {...props}\n    />\n  ),\n);\nFieldGroup.displayName = \"FieldGroup\";\n\n// ---------------------------------------------------------------------------\n// Field — 단일 폼 필드 루트. 상태 제공자역을 겸한다.\n// ---------------------------------------------------------------------------\n// 원본의 자식 w-full 셀렉터（［&>*］:w-full）는 제거 — Input/Textarea가 기본\n// w-full이므로 실사용에 영향 없다. role=\"group\"은 bridge role 계약에 없어 생략.\nconst fieldVariants = cva(\"flex w-full gap-3\", {\n  variants: {\n    orientation: {\n      vertical: \"flex-col\",\n      horizontal: \"flex-row items-center\",\n    },\n  },\n  defaultVariants: {\n    orientation: \"vertical\",\n  },\n});\n\nexport type FieldProps = ViewProps &\n  VariantProps<typeof fieldVariants> & {\n    /** 오류 상태 — 라벨·타이틀 전경색을 destructive로（원본 data-invalid 치환） */\n    invalid?: boolean;\n    /** 비활성 상태 — 라벨·타이틀 불투명도（원본 data-disabled 치환） */\n    disabled?: boolean;\n  };\n\nconst Field = forwardRef<BridgeViewRef, FieldProps>(\n  ({ className, orientation, invalid, disabled, ...props }, ref) => {\n    const resolvedOrientation = orientation ?? \"vertical\";\n    const contextValue = useMemo<FieldContextValue>(\n      () => ({\n        invalid: invalid === true,\n        disabled: disabled === true,\n        orientation: resolvedOrientation,\n      }),\n      [invalid, disabled, resolvedOrientation],\n    );\n    return (\n      <FieldContext.Provider value={contextValue}>\n        <View\n          ref={ref}\n          className={cn(fieldVariants({ orientation }), className)}\n          {...props}\n        />\n      </FieldContext.Provider>\n    );\n  },\n);\nField.displayName = \"Field\";\n\n// ---------------------------------------------------------------------------\n// FieldContent — 라벨 옆 본문 영역（컨트롤·설명·오류 묶음）.\n// ---------------------------------------------------------------------------\nexport type FieldContentProps = ViewProps;\n\nconst FieldContent = forwardRef<BridgeViewRef, FieldContentProps>(\n  ({ className, ...props }, ref) => (\n    <View\n      ref={ref}\n      className={cn(\n        \"flex flex-1 flex-col gap-1.5 leading-snug\",\n        className,\n      )}\n      {...props}\n    />\n  ),\n);\nFieldContent.displayName = \"FieldContent\";\n\n// ---------------------------------------------------------------------------\n// FieldLabel — 필드 라벨. Label（./label） 재사용 + Field 상태 연동.\n// ---------------------------------------------------------------------------\nexport type FieldLabelProps = LabelProps;\n\nconst FieldLabel = forwardRef<BridgeTextRef, FieldLabelProps>(\n  ({ className, ...props }, ref) => {\n    const field = useContext(FieldContext);\n    return (\n      <Label\n        ref={ref}\n        className={cn(\n          \"w-fit leading-snug\",\n          // horizontal 배치에서 라벨이 남은 폭을 차지（원본 자식 셀렉터 치환）\n          field?.orientation === \"horizontal\" && \"flex-auto\",\n          field?.invalid && \"text-destructive\",\n          field?.disabled && \"opacity-50\",\n          className,\n        )}\n        {...props}\n      />\n    );\n  },\n);\nFieldLabel.displayName = \"FieldLabel\";\n\n// ---------------------------------------------------------------------------\n// FieldTitle — 라벨 대신 쓰는 제목형 텍스트（상태 연동은 Label과 동일）.\n// ---------------------------------------------------------------------------\nexport type FieldTitleProps = TextProps;\n\nconst FieldTitle = forwardRef<BridgeTextRef, FieldTitleProps>(\n  ({ className, ...props }, ref) => {\n    const field = useContext(FieldContext);\n    return (\n      <Text\n        ref={ref}\n        className={cn(\n          \"w-fit font-sans text-sm font-medium leading-snug text-foreground\",\n          field?.invalid && \"text-destructive\",\n          field?.disabled && \"opacity-50\",\n          className,\n        )}\n        {...props}\n      />\n    );\n  },\n);\nFieldTitle.displayName = \"FieldTitle\";\n\n// ---------------------------------------------------------------------------\n// FieldDescription — 보조 설명.\n// ---------------------------------------------------------------------------\nexport type FieldDescriptionProps = TextProps;\n\nconst FieldDescription = forwardRef<BridgeTextRef, FieldDescriptionProps>(\n  ({ className, ...props }, ref) => (\n    <Text\n      ref={ref}\n      className={cn(\n        \"font-sans text-sm font-normal leading-normal text-muted-foreground web:text-balance\",\n        className,\n      )}\n      {...props}\n    />\n  ),\n);\nFieldDescription.displayName = \"FieldDescription\";\n\n// ---------------------------------------------------------------------------\n// FieldSeparator — 사이 구분선（가운데 문구 optional）.\n// ---------------------------------------------------------------------------\nexport type FieldSeparatorProps = ViewProps & {\n  /** 가운데 문구 — 원본과 같이 children이 있으면 선 위에 표시 */\n  children?: ReactNode;\n};\n\nconst FieldSeparator = forwardRef<BridgeViewRef, FieldSeparatorProps>(\n  ({ className, children, ...props }, ref) => (\n    <View ref={ref} className={cn(\"relative -my-2 h-5\", className)} {...props}>\n      {/* 인라인 구분선 — 원본 Separator 대리（신규 파일 제한） */}\n      <View className=\"absolute inset-x-0 top-1/2 h-px bg-border\" />\n      {children ? (\n        <Text className=\"relative mx-auto w-fit bg-background px-2 font-sans text-sm text-muted-foreground\">\n          {children}\n        </Text>\n      ) : null}\n    </View>\n  ),\n);\nFieldSeparator.displayName = \"FieldSeparator\";\n\n// ---------------------------------------------------------------------------\n// FieldError — 검증 오류 표시（role=\"alert\"）. children 우선, 없으면 errors로 조립.\n// ---------------------------------------------------------------------------\nexport type FieldErrorProps = ViewProps & {\n  /** 유효성 오류 목록 — message 기준 중복 제거（원본 로직 그대로） */\n  errors?: Array<{ message?: string } | undefined>;\n};\n\nconst FieldError = forwardRef<BridgeViewRef, FieldErrorProps>(\n  ({ className, children, errors, ...props }, ref) => {\n    const content = useMemo<ReactNode>(() => {\n      // 이중 지정 원칙: children은 문자열 권장 — 내부 Text가 색을 담당한다.\n      if (children) {\n        return (\n          <Text className=\"font-sans text-sm text-destructive\">{children}</Text>\n        );\n      }\n      if (!errors?.length) {\n        return null;\n      }\n      const uniqueErrors = [\n        ...new Map(errors.map((error) => [error?.message, error])).values(),\n      ];\n      if (uniqueErrors.length === 1) {\n        return (\n          <Text className=\"font-sans text-sm text-destructive\">\n            {uniqueErrors[0]?.message}\n          </Text>\n        );\n      }\n      // 다중 오류 — 원본 ul/list-disc를 글리프 \"• \" 목록으로 치환.\n      return (\n        <View className=\"ml-4 flex flex-col gap-1\">\n          {uniqueErrors.map((error, index) =>\n            error?.message ? (\n              <Text\n                key={index}\n                className=\"font-sans text-sm text-destructive\"\n              >{`• ${error.message}`}</Text>\n            ) : null,\n          )}\n        </View>\n      );\n    }, [children, errors]);\n\n    if (!content) {\n      return null;\n    }\n\n    return (\n      <View\n        ref={ref}\n        accessibilityRole=\"alert\"\n        className={cn(\"text-sm font-normal\", className)}\n        {...props}\n      >\n        {content}\n      </View>\n    );\n  },\n);\nFieldError.displayName = \"FieldError\";\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": "components/unikit/field.tsx"
    }
  ],
  "type": "registry:ui"
}
