{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "compare",
  "type": "registry:ui",
  "title": "Compare",
  "description": "Before/after image slider with a draggable divider, for comparisons and product demos.",
  "dependencies": [
    "@saasflare/ui"
  ],
  "files": [
    {
      "path": "components/ui/compare.tsx",
      "type": "registry:ui",
      "content": "\"use client\"\n\n/**\n * @fileoverview Before/after comparison slider.\n * @author Saasflare™\n * A draggable divider that reveals two overlapping images or components,\n * creating a before/after comparison effect.\n * @module packages/ui/components/ui/compare\n * @package ui\n *\n * @component\n * @example\n * import { Compare } from '@saasflare/ui';\n * <Compare\n *   before={<img src=\"/before.png\" alt=\"Before\" />}\n *   after={<img src=\"/after.png\" alt=\"After\" />}\n * />\n *\n * @example\n * // With labels and custom initial position\n * <Compare\n *   before={<img src=\"/old-design.png\" alt=\"Old design\" />}\n *   after={<img src=\"/new-design.png\" alt=\"New design\" />}\n *   beforeLabel=\"Before\"\n *   afterLabel=\"After\"\n *   initialPosition={30}\n * />\n */\n\nimport * as React from \"react\"\nimport { useState, useRef, useCallback, type ReactNode, type PointerEvent } from \"react\"\nimport { cn } from \"@saasflare/ui\"\nimport { useSaasflareProps, type SaasflareComponentProps } from \"@saasflare/ui\"\n\n/** Props for the Compare component. */\nexport interface CompareProps\n  extends Omit<React.ComponentProps<\"div\">, keyof SaasflareComponentProps>,\n    SaasflareComponentProps {\n  /** Content shown on the left (before) side. */\n  before: ReactNode\n  /** Content shown on the right (after) side. */\n  after: ReactNode\n  /** Label for the before side. */\n  beforeLabel?: string\n  /** Label for the after side. */\n  afterLabel?: string\n  /** Initial divider position as percentage (0–100). Default: `50` */\n  initialPosition?: number\n  /** CSS aspect-ratio for the container. Default: `\"16/9\"` */\n  aspectRatio?: string\n  /** Additional class names. */\n  className?: string\n}\n\n/**\n * Draggable before/after comparison slider.\n *\n * - Pointer-based dragging (works on mouse and touch)\n * - Optional before/after labels\n * - Content is clipped, not resized\n * - Keyboard support: Arrow keys nudge, Home/End jump to edges, PageUp/PageDown step\n *\n * @component\n * @package ui\n */\nexport function Compare({\n  before,\n  after,\n  beforeLabel,\n  afterLabel,\n  initialPosition = 50,\n  aspectRatio = \"16/9\",\n  className,\n  surface,\n  radius,\n  animated,\n  iconWeight,\n  ...props\n}: CompareProps) {\n  const sf = useSaasflareProps({ surface, radius, animated, iconWeight })\n  const [position, setPosition] = useState(initialPosition)\n  const containerRef = useRef<HTMLDivElement>(null)\n  const dragging = useRef(false)\n\n  const updatePosition = useCallback((clientX: number) => {\n    if (!containerRef.current) return\n    const rect = containerRef.current.getBoundingClientRect()\n    const x = Math.max(0, Math.min(clientX - rect.left, rect.width))\n    setPosition((x / rect.width) * 100)\n  }, [])\n\n  const onPointerDown = useCallback(\n    (e: PointerEvent) => {\n      dragging.current = true\n      ;(e.target as HTMLElement).setPointerCapture(e.pointerId)\n      updatePosition(e.clientX)\n    },\n    [updatePosition],\n  )\n\n  const onPointerMove = useCallback(\n    (e: PointerEvent) => {\n      if (!dragging.current) return\n      updatePosition(e.clientX)\n    },\n    [updatePosition],\n  )\n\n  const onPointerUp = useCallback(() => {\n    dragging.current = false\n  }, [])\n\n  const onKeyDown = useCallback((e: React.KeyboardEvent) => {\n    if (e.key === \"ArrowLeft\" || e.key === \"ArrowDown\") {\n      setPosition((p) => Math.max(0, p - 2))\n    } else if (e.key === \"ArrowRight\" || e.key === \"ArrowUp\") {\n      setPosition((p) => Math.min(100, p + 2))\n    } else if (e.key === \"PageDown\") {\n      setPosition((p) => Math.max(0, p - 10))\n    } else if (e.key === \"PageUp\") {\n      setPosition((p) => Math.min(100, p + 10))\n    } else if (e.key === \"Home\") {\n      setPosition(0)\n    } else if (e.key === \"End\") {\n      setPosition(100)\n    } else {\n      return\n    }\n    e.preventDefault()\n  }, [])\n\n  return (\n    <div\n      {...props}\n      ref={containerRef}\n      className={cn(\n        \"relative select-none overflow-hidden rounded-xl border shadow-sm\",\n        className,\n      )}\n      style={{ aspectRatio, ...props.style }}\n      onPointerDown={onPointerDown}\n      onPointerMove={onPointerMove}\n      onPointerUp={onPointerUp}\n      data-slot=\"compare\"\n      data-surface={sf.surface}\n      data-radius={sf.radius}\n      data-animated={String(sf.animated)}\n    >\n      {/* After (full width, behind) */}\n      <div className=\"absolute inset-0\">{after}</div>\n\n      {/* Before (clipped, not resized — rendered at full width, revealed via clip-path) */}\n      <div\n        className=\"absolute inset-0 overflow-hidden\"\n        style={{ clipPath: `inset(0 ${100 - position}% 0 0)` }}\n      >\n        {before}\n      </div>\n\n      {/* Divider handle */}\n      <div\n        className=\"absolute inset-y-0 z-10 flex w-1 cursor-col-resize items-center bg-background shadow-lg\"\n        style={{ left: `${position}%`, transform: \"translateX(-50%)\" }}\n        role=\"slider\"\n        aria-label=\"Comparison slider\"\n        aria-orientation=\"horizontal\"\n        aria-valuenow={Math.round(position)}\n        aria-valuemin={0}\n        aria-valuemax={100}\n        tabIndex={0}\n        onKeyDown={onKeyDown}\n      >\n        <div className=\"flex size-8 items-center justify-center rounded-full bg-background text-foreground shadow-md\">\n          <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" aria-hidden=\"true\">\n            <path d=\"M4 1L1 7L4 13M10 1L13 7L10 13\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n          </svg>\n        </div>\n      </div>\n\n      {/* Labels */}\n      {beforeLabel && (\n        <span className=\"absolute left-3 top-3 rounded-md bg-foreground/70 px-2 py-1 text-xs font-medium text-background backdrop-blur-sm\">\n          {beforeLabel}\n        </span>\n      )}\n      {afterLabel && (\n        <span className=\"absolute right-3 top-3 rounded-md bg-foreground/70 px-2 py-1 text-xs font-medium text-background backdrop-blur-sm\">\n          {afterLabel}\n        </span>\n      )}\n    </div>\n  )\n}\n",
      "target": "components/ui/compare.tsx"
    }
  ],
  "$meta": {
    "source": "https://ui.saasflare.io/r/compare.json"
  }
}
