{
    "name": "text-fall-button",
    "title": "Text Fall Button",
    "description": "A button with dynamic text that falls into place with a smooth, elastic motion on hover or click.",
    "type": "registry:ui",
    "dependencies": ["gsap", "@gsap/react"],
    "files": [
        {
            "path": "components/gsap/text-fall-button.tsx",
            "type": "registry:ui",
            "target": "~/components/gsap/text-fall-button.tsx",
            "content": "\"use client\";\n\nimport { ComponentProps, MouseEvent, useEffect, useRef } from \"react\";\n\nimport { useGSAP } from \"@gsap/react\";\nimport gsap from \"gsap\";\nimport { SplitText } from \"gsap/SplitText\";\n\nimport { cn } from \"@/lib/utils\";\n\ngsap.registerPlugin(SplitText);\n\ntype TextFallButtonProps = {\n    effectOnHover?: boolean;\n    effectOnLoad?: boolean;\n} & ComponentProps<\"button\">;\n\nexport const TextFallButton = ({\n    className,\n    children,\n    effectOnHover = true,\n    effectOnLoad = true,\n    ...props\n}: TextFallButtonProps) => {\n    const buttonRef = useRef<HTMLButtonElement | null>(null);\n    const textRef = useRef<HTMLSpanElement | null>(null);\n    const splitTextRef = useRef<SplitText | null>(null);\n    const timelineRef = useRef<gsap.core.Timeline | null>(null);\n\n    const { contextSafe } = useGSAP();\n\n    const triggerTextFallEffect = contextSafe(() => {\n        const element = buttonRef.current;\n        const splitText = splitTextRef.current;\n        if (!element || !splitText) return;\n\n        timelineRef.current?.kill();\n\n        timelineRef.current = gsap.timeline();\n        gsap.to(splitText.chars, {\n            duration: 0,\n            y: -60,\n        });\n        timelineRef.current\n            .add(\"start\")\n            .to(element, {\n                scale: 0.95,\n                y: 4,\n                duration: 0.1,\n            })\n            .to(element, {\n                scale: 1,\n                y: 0,\n                duration: 0.2,\n            })\n            .to(\n                splitText.chars,\n                {\n                    duration: 1,\n                    y: 0,\n                    stagger: 0.05,\n                    ease: \"elastic.out(0.75, 0.25)\",\n                },\n                \"start\",\n            );\n    });\n\n    useEffect(() => {\n        if (textRef.current)\n            splitTextRef.current = new SplitText(textRef.current, {\n                type: \"chars\",\n            });\n        if (effectOnLoad) {\n            triggerTextFallEffect();\n        }\n        return () => {\n            splitTextRef.current?.revert();\n            splitTextRef.current = null;\n        };\n    }, [effectOnLoad, triggerTextFallEffect]);\n\n    const onClick = (e: MouseEvent<HTMLButtonElement>) => {\n        triggerTextFallEffect();\n        props.onClick?.(e);\n    };\n\n    return (\n        <button\n            {...props}\n            ref={buttonRef}\n            onMouseEnter={() => effectOnHover && triggerTextFallEffect()}\n            onClick={onClick}\n            className={cn(\"\", className)}>\n            <span ref={textRef} className=\"absolute\">\n                {children}\n            </span>\n            <span className=\"opacity-0\">{children}</span>\n        </button>\n    );\n};\n"
        }
    ]
}
