{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "shutter-cta",
  "type": "registry:component",
  "title": "Shutter CTA",
  "author": "Harsh Kanjiya <https://harshkanjiya.com>",
  "description": "A slide-to-action button with animated dot pulses.",
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/shutter-cta/shutter-cta.tsx",
      "content": "\"use client\"\r\n\r\nimport Link from \"next/link\"\r\nimport { useRef, useState } from \"react\"\r\nimport { useRouter } from \"next/navigation\"\r\n\r\ntype Props = {\r\n    baseDelay?: number; // starting time offset\r\n};\r\n\r\nexport function ArrowBlock({ baseDelay = 0 }: Props) {\r\n    const pattern = [\r\n        [1, 1, 0, 0],\r\n        [0, 1, 1, 0],\r\n        [0, 0, 1, 1],\r\n        [0, 1, 1, 0],\r\n        [1, 1, 0, 0],\r\n    ];\r\n\r\n    return (\r\n        <div className=\"grid gap-px grid-cols-4\">\r\n            {pattern.flatMap((row, r) =>\r\n                row.map((cell, c) => {\r\n                    if (!cell) {\r\n                        return (\r\n                            <div\r\n                                key={`${r}-${c}`}\r\n                                className=\"h-[4px] w-[4px]\"\r\n                            />\r\n                        );\r\n                    }\r\n                    const delay = baseDelay + (c * 120 + r * 60);\r\n                    return (\r\n                        <div\r\n                            key={`${r}-${c}`}\r\n                            className=\"h-[4px] w-[4px] bg-white rounded-full\"\r\n                            style={{\r\n                                animation: `dotPulse 1.5s ease-in-out infinite`,\r\n                                animationDelay: `${delay}ms`,\r\n                            }}\r\n                        />\r\n                    );\r\n                })\r\n            )}\r\n            <style jsx>{`\r\n                @keyframes dotPulse {\r\n                    0%, 100% { opacity: 0.2; }\r\n                    50% { opacity: 1; }\r\n                }\r\n            `}</style>\r\n        </div>\r\n    );\r\n}\r\n\r\nconst arrows = [\r\n    { delay: 0 },\r\n    { delay: 200 },\r\n    { delay: 400 },\r\n    { delay: 600 },\r\n    { delay: 800 },\r\n];\r\n\r\nconst INITIAL_W = 42\r\nconst SLIDE_THRESHOLD = 0.6 // 60% of max drag triggers navigation\r\n\r\nexport function ShutterCta() {\r\n    const router = useRouter()\r\n    const buttonRef = useRef<HTMLButtonElement>(null)\r\n    const touchStartX = useRef(0)\r\n    const [dragWidth, setDragWidth] = useState<number | null>(null)\r\n    const isDragging = useRef(false)\r\n\r\n    const handleTouchStart = (e: React.TouchEvent) => {\r\n        touchStartX.current = e.touches[0].clientX\r\n        isDragging.current = true\r\n        setDragWidth(INITIAL_W)\r\n    }\r\n\r\n    const handleTouchMove = (e: React.TouchEvent) => {\r\n        if (!isDragging.current || !buttonRef.current) return\r\n        const btnW = buttonRef.current.offsetWidth\r\n        const maxW = btnW - 8\r\n        const dragX = e.touches[0].clientX - touchStartX.current\r\n        setDragWidth(Math.max(INITIAL_W, Math.min(maxW, INITIAL_W + dragX)))\r\n    }\r\n\r\n    const handleTouchEnd = (e: React.TouchEvent) => {\r\n        isDragging.current = false\r\n        if (!buttonRef.current) return\r\n        const btnW = buttonRef.current.offsetWidth\r\n        const maxW = btnW - 8\r\n        const dragged = (dragWidth ?? INITIAL_W) - INITIAL_W\r\n        const maxDrag = maxW - INITIAL_W\r\n\r\n        if (dragged > 10 && dragged / maxDrag >= SLIDE_THRESHOLD) {\r\n            // Slid far enough — fill to full width then navigate\r\n            e.preventDefault()\r\n            setDragWidth(maxW)\r\n            setTimeout(() => router.push(\"#\"), 250)\r\n        } else if (dragged > 10) {\r\n            // Partial drag — snap back, don't navigate\r\n            e.preventDefault()\r\n            setDragWidth(null)\r\n        } else {\r\n            // Minimal movement — treat as tap, let Link navigate\r\n            setDragWidth(null)\r\n        }\r\n    }\r\n\r\n    return (\r\n        <div className=\"flex flex-wrap items-center justify-center flex-col md:flex-row md:justify-start md:gap-8 gap-6\">\r\n            <Link\r\n                prefetch\r\n                href=\"#\" className=\"relative\">\r\n                <button\r\n                    ref={buttonRef}\r\n                    onTouchStart={handleTouchStart}\r\n                    onTouchMove={handleTouchMove}\r\n                    onTouchEnd={handleTouchEnd}\r\n                    className=\"\r\n                    relative font-medium text-[17px] px-4 py-[0.35em] pl-5 h-[48px] rounded-[10px] group\r\n                    bg-[#222] dark:bg-[#343434] text-white flex items-center overflow-hidden cursor-pointer\r\n                    dark:shadow-[inset_0px_0px_6px_6px_rgba(12,12,12,0.61),inset_0px_0px_2px_1px_rgba(67,67,67,0.5)]\r\n                    \"\r\n                >\r\n                    <div\r\n                        className=\"\r\n                        group-hover:w-[calc(100%-8px)] top-[4px] left-[4px] h-[40px] w-[42px] bg-[#454545] dark:bg-[#111]\r\n                        flex items-center gap-2 absolute rounded-[7px] overflow-hidden ease-in-out\r\n                        shadow-[0px_0px_17px_4px_#000000,inset_0px_0px_6px_6px_rgba(4,47,41,0.31),inset_0px_0px_2px_1px_rgba(67,67,67,0.35)]\r\n                        dark:shadow-[0px_0px_4px_2px_#333]\r\n                        \"\r\n                        style={{\r\n                            width: dragWidth !== null ? `${dragWidth}px` : undefined,\r\n                            transition: dragWidth !== null\r\n                                ? isDragging.current ? \"none\" : \"width 250ms ease-in-out\"\r\n                                : \"width 300ms ease-in-out\",\r\n                        }}\r\n                    >\r\n                        <div className=\"absolute w-[235px] h-full flex gap-6 items-center justify-between pl-4! pr-5!\">\r\n                            {arrows.map((a, i) => (\r\n                                <ArrowBlock key={i} baseDelay={a.delay} />\r\n                            ))}\r\n                        </div>\r\n                    </div>\r\n                    <span className=\"ml-10\">Slide to Action</span>\r\n                </button>\r\n            </Link>\r\n        </div>\r\n    )\r\n}\r\n",
      "type": "registry:component"
    },
    {
      "path": "components/shutter-cta/index.ts",
      "content": "export { ShutterCta } from \"./shutter-cta\"\r\n",
      "type": "registry:component"
    }
  ]
}