{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "comparison-slider",
  "title": "Comparison Slider",
  "description": "Draggable clip-path divider that reveals one live layer over another.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/components/trovecn/motion-demos/comparison-slider.tsx",
      "content": "\"use client\";\n\nimport { useCallback, useRef, useState, type PointerEvent as ReactPointerEvent } from \"react\";\nimport { ChevronsLeftRight } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface ComparisonSliderProps {\n  /** Layer revealed on the left of the divider. */\n  before: React.ReactNode;\n  /** Layer revealed on the right of the divider — sits underneath, full-bleed. */\n  after: React.ReactNode;\n  beforeLabel?: string;\n  afterLabel?: string;\n  /** Divider position as a percentage (0-100) of the container width. */\n  defaultValue?: number;\n  className?: string;\n}\n\nconst STEP = 4;\nconst RESET_TRANSITION = \"clip-path 300ms cubic-bezier(0.23, 1, 0.32, 1)\";\n\nexport function ComparisonSlider({\n  before,\n  after,\n  beforeLabel = \"Before\",\n  afterLabel = \"After\",\n  defaultValue = 50,\n  className,\n}: ComparisonSliderProps) {\n  const [value, setValue] = useState(defaultValue);\n  const [isSettling, setIsSettling] = useState(false);\n  const containerRef = useRef<HTMLDivElement>(null);\n  const draggingRef = useRef(false);\n\n  const updateFromClientX = useCallback((clientX: number) => {\n    const el = containerRef.current;\n    if (!el) return;\n    const rect = el.getBoundingClientRect();\n    const pct = ((clientX - rect.left) / rect.width) * 100;\n    setValue(Math.min(100, Math.max(0, pct)));\n  }, []);\n\n  function onHandlePointerDown(event: ReactPointerEvent<HTMLDivElement>) {\n    draggingRef.current = true;\n    setIsSettling(false);\n    event.currentTarget.setPointerCapture(event.pointerId);\n    updateFromClientX(event.clientX);\n  }\n\n  function onHandlePointerMove(event: ReactPointerEvent<HTMLDivElement>) {\n    if (!draggingRef.current) return;\n    updateFromClientX(event.clientX);\n  }\n\n  function onHandlePointerUp(event: ReactPointerEvent<HTMLDivElement>) {\n    draggingRef.current = false;\n    event.currentTarget.releasePointerCapture(event.pointerId);\n  }\n\n  function onHandleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n    if (event.key === \"ArrowLeft\") setValue((v) => Math.max(0, v - STEP));\n    else if (event.key === \"ArrowRight\") setValue((v) => Math.min(100, v + STEP));\n    else if (event.key === \"Home\") setValue(0);\n    else if (event.key === \"End\") setValue(100);\n    else return;\n    setIsSettling(true);\n    event.preventDefault();\n  }\n\n  function onDoubleClick() {\n    draggingRef.current = false;\n    setIsSettling(true);\n    setValue(50);\n  }\n\n  return (\n    <div\n      ref={containerRef}\n      className={cn(\n        \"relative aspect-video w-full touch-none overflow-hidden rounded-2xl border border-border bg-card select-none\",\n        className,\n      )}\n      onDoubleClick={onDoubleClick}\n    >\n      <div className=\"absolute inset-0\">{after}</div>\n      <div\n        className=\"absolute inset-0 overflow-hidden\"\n        style={{\n          clipPath: `inset(0 ${100 - value}% 0 0)`,\n          transition: isSettling ? RESET_TRANSITION : undefined,\n        }}\n        onTransitionEnd={() => setIsSettling(false)}\n      >\n        {before}\n      </div>\n\n      <div className=\"pointer-events-none absolute top-3 left-3 rounded-full bg-background/80 px-2.5 py-1 text-2xs font-medium text-foreground shadow-sm backdrop-blur\">\n        {beforeLabel}\n      </div>\n      <div className=\"pointer-events-none absolute top-3 right-3 rounded-full bg-background/80 px-2.5 py-1 text-2xs font-medium text-foreground shadow-sm backdrop-blur\">\n        {afterLabel}\n      </div>\n\n      <div\n        className=\"pointer-events-none absolute inset-y-0 w-px bg-border\"\n        style={{\n          left: `${value}%`,\n          transition: isSettling ? \"left 300ms cubic-bezier(0.23, 1, 0.32, 1)\" : undefined,\n        }}\n      >\n        <div\n          role=\"slider\"\n          tabIndex={0}\n          aria-valuemin={0}\n          aria-valuemax={100}\n          aria-valuenow={Math.round(value)}\n          aria-label=\"Comparison position\"\n          onPointerDown={onHandlePointerDown}\n          onPointerMove={onHandlePointerMove}\n          onPointerUp={onHandlePointerUp}\n          onPointerCancel={onHandlePointerUp}\n          onKeyDown={onHandleKeyDown}\n          className=\"pointer-events-auto absolute top-1/2 left-1/2 flex size-9 -translate-x-1/2 -translate-y-1/2 cursor-ew-resize items-center justify-center rounded-full border border-border bg-background text-muted-foreground shadow-md transition-transform duration-quick ease-out outline-none active:scale-95 focus-visible:ring-3 focus-visible:ring-ring/50\"\n        >\n          <ChevronsLeftRight className=\"size-4\" />\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}