{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "ripple-button", "title": "Ripple Button", "description": "Button with an expanding ripple animation on click, in three variants.", "dependencies": [ "motion" ], "files": [ { "path": "registry/pioneerui/ripple-button.tsx", "content": "\"use client\"\n\nimport React, { useRef, useState } from \"react\"\nimport { AnimatePresence, motion } from \"motion/react\"\nimport { cn } from \"@/lib/utils\"\n\ninterface RipplePoint {\n id: number\n x: number\n y: number\n}\n\nexport interface RippleButtonProps extends React.ButtonHTMLAttributes {\n children?: React.ReactNode\n rippleColor?: string\n variant?: \"default\" | \"outline\" | \"ghost\"\n}\n\nconst variantClasses = {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n outline: \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n}\n\nexport function RippleButton({\n children,\n className,\n rippleColor = \"rgba(255,255,255,0.35)\",\n variant = \"default\",\n onClick,\n ...props\n}: RippleButtonProps) {\n const [ripples, setRipples] = useState([])\n const btnRef = useRef(null)\n const nextId = useRef(0)\n\n const handleClick = (e: React.MouseEvent) => {\n if (!btnRef.current) return\n const rect = btnRef.current.getBoundingClientRect()\n const x = e.clientX - rect.left\n const y = e.clientY - rect.top\n const id = nextId.current++\n setRipples((prev) => [...prev, { id, x, y }])\n setTimeout(() => {\n setRipples((prev) => prev.filter((r) => r.id !== id))\n }, 700)\n onClick?.(e)\n }\n\n return (\n \n \n {ripples.map((r) => (\n \n ))}\n \n {children}\n \n )\n}\n", "type": "registry:ui", "target": "components/pioneerui/ripple-button.tsx" } ], "type": "registry:ui" }