{
    "name": "swap",
    "title": "Swap",
    "description": "A flexible swap component that animates content changes with smooth customizable transition effects.",
    "type": "registry:ui",
    "dependencies": ["gsap", "@gsap/react"],
    "files": [
        {
            "path": "components/gsap/swap.tsx",
            "type": "registry:ui",
            "target": "~/components/gsap/swap.tsx",
            "content": "import { ComponentProps, ReactNode, useMemo, useRef, useState } from \"react\";\n\nimport { useGSAP } from \"@gsap/react\";\nimport gsap from \"gsap\";\n\nconst effectPresets = {\n    slideUp: [\n        {\n            y: \"-100%\",\n        },\n        {\n            y: \"100%\",\n        },\n        {\n            y: 0,\n        },\n    ],\n    slideDown: [\n        {\n            y: \"100%\",\n        },\n        {\n            y: \"-100%\",\n        },\n        {\n            y: 0,\n        },\n    ],\n    opacity: [\n        {\n            opacity: 0,\n        },\n        {\n            opacity: 0,\n        },\n        {\n            opacity: 1,\n        },\n    ],\n    blur: [\n        {\n            filter: \"blur(12px)\",\n        },\n        {\n            filter: \"blur(12px)\",\n        },\n        {\n            filter: \"blur(0px)\",\n        },\n    ],\n    grayscale: [\n        {\n            filter: \"grayscale(100%)\",\n        },\n        {\n            filter: \"grayscale(100%)\",\n        },\n        {\n            filter: \"grayscale(0%)\",\n        },\n    ],\n} as const;\n\nexport type SwapProps<T = object> = Omit<ComponentProps<\"div\">, \"children\"> & {\n    state: T;\n    children: (state: T) => ReactNode;\n    effects?: (keyof typeof effectPresets)[];\n    duration?: number;\n};\n\nconst mergeVars = (varsArray: Record<string, string | number>[]) => {\n    const merged: Record<string, string | number> = {};\n\n    for (const vars of varsArray) {\n        for (const [key, value] of Object.entries(vars)) {\n            if (key === \"filter\" && merged.filter) {\n                merged.filter += ` ${value}`; // combine filters\n            } else {\n                merged[key] = value; // last one wins for other props\n            }\n        }\n    }\n\n    return merged;\n};\n\nexport const Swap = <T,>({ state, children, duration = 0.4, effects = [], ...props }: SwapProps<T>) => {\n    const [currentState, setCurrentState] = useState(state);\n\n    const containerRef = useRef<HTMLDivElement>(null);\n    const contentRef = useRef<HTMLDivElement>(null);\n\n    const [enterVars, exitVars, finalVars] = useMemo(\n        () => Array.from({ length: 3 }, (_, i) => mergeVars(effects.map((e) => effectPresets[e][i]))),\n        [effects],\n    );\n\n    useGSAP(\n        () => {\n            if (currentState === state) return;\n            const content = contentRef.current;\n            if (!content) return;\n\n            gsap.to(content, {\n                ...enterVars,\n                duration,\n                ease: \"power2.in\",\n                onComplete: () => {\n                    setCurrentState(state);\n                    gsap.fromTo(content, exitVars, {\n                        ...finalVars,\n                        duration,\n                        ease: \"power2.out\",\n                    });\n                },\n            });\n        },\n        { dependencies: [state] },\n    );\n\n    useGSAP(\n        () => {\n            if (!containerRef.current || !contentRef.current) return;\n\n            const box = contentRef.current.getBoundingClientRect();\n\n            gsap.to(containerRef.current, {\n                width: box.width,\n                height: box.height,\n                duration: 0.4,\n                ease: \"power2.out\",\n            });\n        },\n        { dependencies: [currentState] },\n    );\n\n    return (\n        <div {...props} ref={containerRef}>\n            <div ref={contentRef} className=\"inline-block\">\n                {children(currentState)}\n            </div>\n        </div>\n    );\n};\n"
        }
    ]
}
