{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "side-by-side-slide",
  "title": "Side By Side Slide",
  "description": "An image comparison slider with a spring-animated divider that follows the cursor on hover, revealing before/after images.",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/components/unlumen/side-by-side-slide/index.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport {\n  motion,\n  useMotionValue,\n  useSpring,\n  useTransform,\n  type SpringOptions,\n} from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport type SideBySideSlideProps = {\n  /** Source URL for the \"before\" image (left / top). */\n  beforeImage: string;\n  /** Source URL for the \"after\" image (right / bottom). */\n  afterImage: string;\n  /** Alt text for the before image. */\n  beforeAlt?: string;\n  /** Alt text for the after image. */\n  afterAlt?: string;\n  /** Divider direction. */\n  orientation?: \"horizontal\" | \"vertical\";\n  /** Initial divider position as a percentage (0–100). */\n  initialPosition?: number;\n  /** CSS color of the divider line. */\n  dividerColor?: string;\n  /** Width (or height for vertical) of the divider in px. */\n  dividerWidth?: number;\n  /** Box-shadow applied to the divider. */\n  dividerShadow?: string;\n  /** Whether to show the circular handle on the divider. */\n  showHandle?: boolean;\n  /** Diameter of the handle circle in px. */\n  handleSize?: number;\n  /** Background color of the handle. */\n  handleColor?: string;\n  /** Cursor style when hovering over the component. */\n  cursor?: \"none\" | \"col-resize\" | \"row-resize\" | \"pointer\";\n  /** Spring configuration for the divider animation. */\n  springOptions?: SpringOptions;\n  /** Extra CSS classes on the root container. */\n  className?: string;\n};\n\nexport function SideBySideSlide({\n  beforeImage,\n  afterImage,\n  beforeAlt = \"Before\",\n  afterAlt = \"After\",\n  orientation = \"horizontal\",\n  initialPosition = 50,\n  dividerColor = \"white\",\n  dividerWidth = 2,\n  dividerShadow = \"0 0 8px rgba(0,0,0,0.3)\",\n  showHandle = true,\n  handleSize = 40,\n  handleColor = \"white\",\n  cursor = \"none\",\n  springOptions = { stiffness: 300, damping: 30 },\n  className,\n}: SideBySideSlideProps) {\n  const ref = React.useRef<HTMLDivElement>(null);\n  const isHorizontal = orientation === \"horizontal\";\n\n  const raw = useMotionValue(initialPosition);\n  const position = useSpring(raw, springOptions);\n\n  const clipPath = useTransform(position, (v) =>\n    isHorizontal ? `inset(0 ${100 - v}% 0 0)` : `inset(0 0 ${100 - v}% 0)`,\n  );\n\n  const dividerPos = useTransform(position, (v) => `${v}%`);\n\n  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {\n    if (!ref.current) return;\n    const rect = ref.current.getBoundingClientRect();\n    const pct = isHorizontal\n      ? ((e.clientX - rect.left) / rect.width) * 100\n      : ((e.clientY - rect.top) / rect.height) * 100;\n    raw.set(Math.max(0, Math.min(100, pct)));\n  };\n\n  const handleMouseLeave = () => {\n    raw.set(initialPosition);\n  };\n\n  return (\n    <div\n      ref={ref}\n      className={cn(\"relative select-none overflow-hidden\", className)}\n      style={{ cursor }}\n      onMouseMove={handleMouseMove}\n      onMouseLeave={handleMouseLeave}\n    >\n      {/* After image (bottom layer — in flow to give the container intrinsic height) */}\n      {/* eslint-disable-next-line @next/next/no-img-element */}\n      <img\n        src={afterImage}\n        alt={afterAlt}\n        draggable={false}\n        className=\"block h-full w-full object-cover\"\n      />\n\n      {/* Before image (clipped top layer) */}\n      <motion.div className=\"absolute inset-0\" style={{ clipPath }}>\n        {/* eslint-disable-next-line @next/next/no-img-element */}\n        <img\n          src={beforeImage}\n          alt={beforeAlt}\n          draggable={false}\n          className=\"block h-full w-full object-cover\"\n        />\n      </motion.div>\n\n      {/* Divider line */}\n      <motion.div\n        className=\"absolute\"\n        style={\n          isHorizontal\n            ? {\n                left: dividerPos,\n                top: 0,\n                bottom: 0,\n                width: dividerWidth,\n                x: \"-50%\",\n                backgroundColor: dividerColor,\n                boxShadow: dividerShadow,\n              }\n            : {\n                top: dividerPos,\n                left: 0,\n                right: 0,\n                height: dividerWidth,\n                y: \"-50%\",\n                backgroundColor: dividerColor,\n                boxShadow: dividerShadow,\n              }\n        }\n      >\n        {/* Handle */}\n        {showHandle && (\n          <div\n            className=\"absolute rounded-full flex items-center justify-center\"\n            style={\n              isHorizontal\n                ? {\n                    width: handleSize,\n                    height: handleSize,\n                    top: \"50%\",\n                    left: \"50%\",\n                    transform: \"translate(-50%, -50%)\",\n                    backgroundColor: handleColor,\n                    boxShadow: dividerShadow,\n                  }\n                : {\n                    width: handleSize,\n                    height: handleSize,\n                    left: \"50%\",\n                    top: \"50%\",\n                    transform: \"translate(-50%, -50%)\",\n                    backgroundColor: handleColor,\n                    boxShadow: dividerShadow,\n                  }\n            }\n          ></div>\n        )}\n      </motion.div>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/unlumen-ui/side-by-side-slide.tsx"
    }
  ],
  "type": "registry:ui"
}