{
  "title": "Spring Button",
  "description": "A button that shrinks on press and springs back with bounce.",
  "name": "gsap-spring-button",
  "dependencies": [
    "gsap",
    "@gsap/react"
  ],
  "registryDependencies": [],
  "type": "registry:ui",
  "files": [
    {
      "path": "components/paceui/gsap/spring-button.tsx",
      "type": "registry:ui",
      "content": "import { type ComponentProps, useRef } from \"react\";\n\nimport { useGSAP } from \"@gsap/react\";\nimport gsap from \"gsap\";\n\ntype SpringButtonProps = {\n    scale?: number;\n    shaking?: boolean;\n} & ComponentProps<\"button\">;\n\nexport const SpringButton = ({ scale = 0.85, shaking = true, ...props }: SpringButtonProps) => {\n    const buttonRef = useRef<HTMLButtonElement | null>(null);\n    const pressed = useRef(false);\n\n    useGSAP(\n        () => {\n            const element = buttonRef.current;\n            if (!element) return;\n            let pressTimeline = gsap.timeline();\n            let shakingTimeline = gsap.timeline({ repeat: -1 });\n\n            const press = () => {\n                pressed.current = true;\n                pressTimeline.pause(0);\n                pressTimeline.kill();\n                pressTimeline = gsap.timeline();\n                shakingTimeline = gsap.timeline({ repeat: -1 });\n\n                pressTimeline.to(element, {\n                    scale: scale,\n                    duration: 1,\n                    ease: \"power4.out\",\n                });\n\n                if (shaking) {\n                    pressTimeline.add(shakingTimeline);\n                    shakingTimeline\n                        .to(element, { x: -1, y: 1, duration: 0.08 })\n                        .to(element, { x: 1, y: -1, duration: 0.08 })\n                        .to(element, { x: -0.5, y: -0.5, duration: 0.08 })\n                        .to(element, { x: 0.5, y: 0.5, duration: 0.08 })\n                        .to(element, { x: 0, y: 0, duration: 0.08 });\n                }\n            };\n\n            const release = () => {\n                pressed.current = false;\n                if (shakingTimeline) {\n                    shakingTimeline.pause(0);\n                    shakingTimeline.kill();\n                }\n                gsap.to(element, {\n                    scale: 1,\n                    duration: 0.5,\n                    ease: \"elastic.out(1.4, 0.2)\",\n                    onComplete: () => {\n                        if (!pressed.current && pressTimeline) {\n                            pressTimeline.pause(0);\n                            pressTimeline.kill();\n                        }\n                    },\n                });\n            };\n\n            element.addEventListener(\"mousedown\", press);\n            element.addEventListener(\"touchstart\", press);\n            element.addEventListener(\"mouseup\", release);\n            element.addEventListener(\"mouseleave\", release);\n            element.addEventListener(\"touchend\", release);\n\n            return () => {\n                element.removeEventListener(\"mousedown\", press);\n                element.removeEventListener(\"touchstart\", press);\n                element.removeEventListener(\"mouseup\", release);\n                element.removeEventListener(\"mouseleave\", release);\n                element.removeEventListener(\"touchend\", release);\n            };\n        },\n        { scope: buttonRef },\n    );\n\n    return <button {...props} ref={buttonRef} />;\n};\n"
    }
  ],
  "mcp": {
    "description": "Provides refined UI interactions and accessible controls. A button that shrinks on press and springs back with bounce. Configurable props include: {; scale?: number;; shaking?: boolean;. Relies on gsap, @gsap/react. Optimal for building reusable interface elements and user input flows.",
    "tags": [
      "component",
      "gsap",
      "animation"
    ]
  }
}