{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "image-comparison-slider",
  "title": "Image Comparison Slider",
  "description": "Draggable before-and-after image comparison control.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/examples/image-comparison-slider.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { GripVertical } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport type ImageComparisonSliderProps = {\n  beforeImage: string\n  afterImage: string\n  alt: string\n  position?: number\n  defaultPosition?: number\n  onPositionChange?: (position: number) => void\n  beforeLabel?: string\n  afterLabel?: string\n  className?: string\n}\n\nexport function ImageComparisonSlider({\n  beforeImage,\n  afterImage,\n  alt,\n  position: positionProp,\n  defaultPosition = 50,\n  onPositionChange,\n  beforeLabel = \"Before\",\n  afterLabel = \"After\",\n  className,\n}: ImageComparisonSliderProps) {\n  const [uncontrolledPosition, setUncontrolledPosition] = React.useState(() => clamp(defaultPosition))\n  const position = clamp(positionProp ?? uncontrolledPosition)\n\n  const setPosition = (nextPosition: number) => {\n    const next = clamp(nextPosition)\n    if (positionProp === undefined) setUncontrolledPosition(next)\n    onPositionChange?.(next)\n  }\n\n  return (\n    <div className={cn(\"group relative aspect-[3/2] w-full max-w-3xl touch-none select-none overflow-hidden rounded-xl bg-muted has-[input:focus-visible]:ring-2 has-[input:focus-visible]:ring-ring\", className)}>\n      <img src={afterImage} alt={alt} className=\"absolute inset-0 size-full object-cover\" draggable={false} />\n      <div className=\"pointer-events-none absolute inset-0 overflow-hidden\" style={{ clipPath: `inset(0 ${100 - position}% 0 0)` }}>\n        <img src={beforeImage} alt=\"\" className=\"size-full object-cover\" draggable={false} />\n      </div>\n      <span className=\"pointer-events-none absolute left-3 top-3 rounded-full bg-background/85 px-2.5 py-1 text-[0.625rem] font-medium text-foreground backdrop-blur\">{beforeLabel}</span>\n      <span className=\"pointer-events-none absolute right-3 top-3 rounded-full bg-background/85 px-2.5 py-1 text-[0.625rem] font-medium text-foreground backdrop-blur\">{afterLabel}</span>\n      <div aria-hidden className=\"pointer-events-none absolute inset-y-0 z-10 w-px bg-background\" style={{ left: `${position}%` }} />\n      <div aria-hidden className=\"pointer-events-none absolute top-1/2 z-20 grid size-9 -translate-x-1/2 -translate-y-1/2 place-items-center rounded-full border bg-background text-foreground shadow-sm\" style={{ left: `${position}%` }}>\n        <GripVertical className=\"size-4\" />\n      </div>\n      <input\n        aria-label=\"Adjust image comparison\"\n        aria-valuetext={`${position}% ${beforeLabel.toLowerCase()}`}\n        className=\"absolute inset-0 z-30 h-full w-full cursor-ew-resize appearance-none bg-transparent opacity-0\"\n        type=\"range\"\n        min=\"0\"\n        max=\"100\"\n        value={position}\n        onChange={(event) => setPosition(Number(event.target.value))}\n      />\n    </div>\n  )\n}\n\nfunction clamp(value: number) {\n  return Math.min(100, Math.max(0, value))\n}\n",
      "type": "registry:block"
    }
  ],
  "type": "registry:block"
}