{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "switch",
  "title": "Switch",
  "description": "유니버설 토글 스위치 — 트랙+썸 구조, default/sm 크기, role=switch + aria-checked. 썸 이동은 justify 전환으로 재현.",
  "dependencies": [
    "@unikit-bridge/ui",
    "class-variance-authority",
    "clsx",
    "tailwind-merge",
    "tailwindcss"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/switch.tsx",
      "content": "// Switch — shadcn/ui new-york-v4 switch를 UniKit UI 브리지로 이식.\n// 포팅 판단:\n// - radix Switch（Root/Thumb） 제거 — 토글 상태는 React state（제어/비제어）로 관리.\n// - 원본의 data-state·data-size·group-data 셀렉터 전개 — 크기는 cva size\n//   변형（default/sm）으로, 트랙 배경과 썸 위치는 상태 조건부 클래스로.\n// - 썸 이동은 원본의 translate-x 계산값 대신 트랙 justify-start/end +\n//   px-0.5（안쪽 2px 여백） — transform·calc 없이 양 플랫폼에서 동일한 레이아웃.\n//   이에 따라 썸 미끄러짐 애니메이션（transition-transform）은 재현 불가해 제거.\n// - 이중 지정 원칙（§0.5）: 트랙 배경은 Pressable, 썸 배경은 내부 View에 명시.\nimport { forwardRef, useState } from \"react\";\nimport {\n  animateLayout,\n  Pressable,\n  View,\n  type BridgePressableRef,\n  type PressableProps,\n} from \"@unikit-bridge/ui/primitives\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\n\n// 트랙（Pressable） — 원본 크기 토큰: default 18.4px（h-4.5 근사）·w-8, sm h-3.5·w-6.\nconst switchTrackVariants = cva(\n  \"flex shrink-0 flex-row items-center rounded-full border border-transparent px-0.5 web:transition-all web:outline-none web:focus-visible:border-ring web:focus-visible:ring-3 web:focus-visible:ring-ring/50\",\n  {\n    variants: {\n      size: {\n        default: \"h-4.5 w-8\",\n        sm: \"h-3.5 w-6\",\n      },\n    },\n    defaultVariants: {\n      size: \"default\",\n    },\n  },\n);\n\n// 썸（View） — 원본의 size=default→size-4 · sm→size-3 대응.\nconst switchThumbVariants = cva(\"rounded-full\", {\n  variants: {\n    size: {\n      default: \"size-4\",\n      sm: \"size-3\",\n    },\n  },\n  defaultVariants: {\n    size: \"default\",\n  },\n});\n\nexport type SwitchProps = Omit<PressableProps, \"onPress\" | \"aria-checked\"> &\n  VariantProps<typeof switchTrackVariants> & {\n    /** 제어형 토글 상태（지정 시 내부 state를 대체） */\n    checked?: boolean;\n    /** 비제어형 초기 토글 상태 */\n    defaultChecked?: boolean;\n    /** 토글 전환 콜백 */\n    onCheckedChange?: (checked: boolean) => void;\n  };\n\n// 터치 타깃 44px 보정: default 트랙 18.4px → 13px, sm 14px → 15px.\nconst HIT_SLOP = { default: 13, sm: 15 } as const;\n\nconst Switch = forwardRef<BridgePressableRef, SwitchProps>(\n  (\n    {\n      className,\n      checked,\n      defaultChecked = false,\n      onCheckedChange,\n      disabled,\n      size,\n      hitSlop,\n      ...props\n    },\n    ref,\n  ) => {\n    // 제어/비제어 통합 — 자식 없음（원본과 동일하게 트랙+썸만 렌더）.\n    const [uncontrolled, setUncontrolled] = useState(defaultChecked);\n    const isControlled = checked !== undefined;\n    const isChecked = isControlled ? checked : uncontrolled;\n    const resolvedSize = size ?? \"default\";\n\n    const handlePress = () => {\n      animateLayout();\n      const next = !isChecked;\n      if (!isControlled) setUncontrolled(next);\n      onCheckedChange?.(next);\n    };\n\n    return (\n      <Pressable\n        ref={ref}\n        accessibilityRole=\"switch\"\n        aria-checked={isChecked}\n        aria-disabled={disabled ? true : undefined}\n        disabled={disabled}\n        hitSlop={hitSlop ?? HIT_SLOP[resolvedSize]}\n        onPress={handlePress}\n        className={cn(\n          switchTrackVariants({ size }),\n          // 원본 상태 셀렉터（checked→bg-primary / unchecked→bg-input） 전개 —\n          // 썸 이동도 같은 조건의 justify-start/end 및 웹 translate-x 전이로 표현.\n          // 꺼짐 트랙은 bg-input（라이트 1.27:1·다크 1.2:1）이 배경에 묻혀\n          // \"꺼짐\" 상태를 오독시켜 foreground/50（양 모드 ≥3:1, G4）로 조정.\n          isChecked\n            ? \"justify-end bg-primary web:justify-start\"\n            : \"justify-start bg-foreground/50\",\n          disabled && \"web:pointer-events-none web:cursor-not-allowed opacity-50\",\n          className,\n        )}\n        {...props}\n      >\n        {/* 썸 — 장식 요소（aria-hidden）. 다크 모드 썸 색은 상태별로 다르다（원본 준수）. */}\n        <View\n          aria-hidden\n          className={cn(\n            switchThumbVariants({ size }),\n            \"web:transition-transform web:duration-200\",\n            isChecked\n              ? resolvedSize === \"sm\"\n                ? \"bg-background dark:bg-primary-foreground web:translate-x-2.5\"\n                : \"bg-background dark:bg-primary-foreground web:translate-x-3.5\"\n              : \"bg-background dark:bg-foreground web:translate-x-0\",\n          )}\n        />\n      </Pressable>\n    );\n  },\n);\nSwitch.displayName = \"Switch\";\n\nexport { Switch, switchTrackVariants, switchThumbVariants };\n",
      "type": "registry:ui",
      "target": "components/unikit/switch.tsx"
    }
  ],
  "type": "registry:ui"
}
