{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "image-comparison",
  "type": "registry:component",
  "title": "Image Comparison",
  "author": "Toby Belhome",
  "description": "Interactively compare two images with a draggable slider to reveal differences.",
  "dependencies": [],
  "registryDependencies": [
    "http://localhost:3000/r/image-comparison.json"
  ],
  "files": [
    {
      "path": "examples/motion/components/image-comparison/01/image-comparison.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { useState, createContext, useContext } from \"react\";\nimport {\n  motion,\n  MotionValue,\n  SpringOptions,\n  useMotionValue,\n  useSpring,\n  useTransform\n} from \"motion/react\";\n\nconst ImageComparisonContext = createContext<\n  | {\n      sliderPosition: number;\n      setSliderPosition: (pos: number) => void;\n      motionSliderPosition: MotionValue<number>;\n    }\n  | undefined\n>(undefined);\n\nexport type ImageComparisonProps = {\n  children: React.ReactNode;\n  className?: string;\n  enableHover?: boolean;\n  springOptions?: SpringOptions;\n};\n\nconst DEFAULT_SPRING_OPTIONS = {\n  bounce: 0,\n  duration: 0\n};\n\nfunction ImageComparison({\n  children,\n  className,\n  enableHover,\n  springOptions\n}: ImageComparisonProps) {\n  const [isDragging, setIsDragging] = useState(false);\n  const motionValue = useMotionValue(50);\n  const motionSliderPosition = useSpring(motionValue, springOptions ?? DEFAULT_SPRING_OPTIONS);\n  const [sliderPosition, setSliderPosition] = useState(50);\n\n  const handleDrag = (event: React.MouseEvent | React.TouchEvent) => {\n    if (!isDragging && !enableHover) return;\n\n    const containerRect = (event.currentTarget as HTMLElement).getBoundingClientRect();\n    const x =\n      \"touches\" in event\n        ? event.touches[0].clientX - containerRect.left\n        : (event as React.MouseEvent).clientX - containerRect.left;\n\n    const percentage = Math.min(Math.max((x / containerRect.width) * 100, 0), 100);\n    motionValue.set(percentage);\n    setSliderPosition(percentage);\n  };\n\n  return (\n    <ImageComparisonContext.Provider\n      value={{ sliderPosition, setSliderPosition, motionSliderPosition }}>\n      <div\n        className={cn(\n          \"relative overflow-hidden select-none\",\n          enableHover && \"cursor-ew-resize\",\n          className\n        )}\n        onMouseMove={handleDrag}\n        onMouseDown={() => !enableHover && setIsDragging(true)}\n        onMouseUp={() => !enableHover && setIsDragging(false)}\n        onMouseLeave={() => !enableHover && setIsDragging(false)}\n        onTouchMove={handleDrag}\n        onTouchStart={() => !enableHover && setIsDragging(true)}\n        onTouchEnd={() => !enableHover && setIsDragging(false)}>\n        {children}\n      </div>\n    </ImageComparisonContext.Provider>\n  );\n}\n\nconst ImageComparisonImage = ({\n  className,\n  alt,\n  src,\n  position\n}: {\n  className?: string;\n  alt: string;\n  src: string;\n  position: \"left\" | \"right\";\n}) => {\n  const { motionSliderPosition } = useContext(ImageComparisonContext)!;\n  const leftClipPath = useTransform(motionSliderPosition, (value) => `inset(0 0 0 ${value}%)`);\n  const rightClipPath = useTransform(\n    motionSliderPosition,\n    (value) => `inset(0 ${100 - value}% 0 0)`\n  );\n\n  return (\n    <motion.img\n      src={src}\n      alt={alt}\n      className={cn(\"absolute inset-0 h-full w-full object-cover\", className)}\n      style={{\n        clipPath: position === \"left\" ? leftClipPath : rightClipPath\n      }}\n    />\n  );\n};\n\nconst ImageComparisonSlider = ({\n  className,\n  children\n}: {\n  className: string;\n  children?: React.ReactNode;\n}) => {\n  const { motionSliderPosition } = useContext(ImageComparisonContext)!;\n\n  const left = useTransform(motionSliderPosition, (value) => `${value}%`);\n\n  return (\n    <motion.div\n      className={cn(\"absolute top-0 bottom-0 w-1 cursor-ew-resize\", className)}\n      style={{\n        left\n      }}>\n      {children}\n    </motion.div>\n  );\n};\n\nexport { ImageComparison, ImageComparisonImage, ImageComparisonSlider };\n",
      "type": "registry:component",
      "target": "components/image-comparison/image-comparison.tsx"
    },
    {
      "path": "examples/motion/components/image-comparison/01/page.tsx",
      "content": "import { ImageComparison, ImageComparisonImage, ImageComparisonSlider } from \"./image-comparison\";\n\nexport default function ImageComparisonBasic() {\n  return (\n    <ImageComparison className=\"aspect-16/9 w-full rounded-lg\" enableHover>\n      <ImageComparisonImage\n        className=\"grayscale\"\n        src=\"https://images.unsplash.com/photo-1755593574938-6d66d28f8e57?q=80&w=750&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\"\n        alt=\"bundui dark image\"\n        position=\"left\"\n      />\n      <ImageComparisonImage\n        src=\"https://images.unsplash.com/photo-1755593574938-6d66d28f8e57?q=80&w=750&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\"\n        alt=\"bundui light image\"\n        position=\"right\"\n      />\n      <ImageComparisonSlider className=\"w-0.5 bg-white/30 backdrop-blur-xs\">\n        <div className=\"absolute top-1/2 left-1/2 size-4 -translate-x-1/2 -translate-y-1/2 rounded-full bg-white\"></div>\n      </ImageComparisonSlider>\n    </ImageComparison>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/image-comparison/image-comparison-example.tsx"
    }
  ],
  "meta": {
    "isPro": false
  }
}