import type { ReactNode } from "react" import React, { createContext, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, } from "react" import type { GlobalOptions as ConfettiGlobalOptions, CreateTypes as ConfettiInstance, Options as ConfettiOptions, } from "canvas-confetti" import confetti from "canvas-confetti" import { Button } from "@/components/ui/button" type Api = { fire: (options?: ConfettiOptions) => void } type Props = React.ComponentPropsWithRef<"canvas"> & { options?: ConfettiOptions globalOptions?: ConfettiGlobalOptions manualstart?: boolean children?: ReactNode } export type ConfettiRef = Api | null const ConfettiContext = createContext({} as Api) // Define component first const ConfettiComponent = forwardRef((props, ref) => { const { options, globalOptions = { resize: true, useWorker: true }, manualstart = false, children, ...rest } = props const instanceRef = useRef(null) const canvasRef = useCallback( (node: HTMLCanvasElement) => { if (node !== null) { if (instanceRef.current) return instanceRef.current = confetti.create(node, { ...globalOptions, resize: true, }) } else { if (instanceRef.current) { instanceRef.current.reset() instanceRef.current = null } } }, [globalOptions] ) const fire = useCallback( async (opts = {}) => { try { await instanceRef.current?.({ ...options, ...opts }) } catch (error) { console.error("Confetti error:", error) } }, [options] ) const api = useMemo( () => ({ fire, }), [fire] ) useImperativeHandle(ref, () => api, [api]) useEffect(() => { if (!manualstart) { ;(async () => { try { await fire() } catch (error) { console.error("Confetti effect error:", error) } })() } }, [manualstart, fire]) return ( {children} ) }) // Set display name immediately ConfettiComponent.displayName = "Confetti" // Export as Confetti export const Confetti = ConfettiComponent interface ConfettiButtonProps extends React.ComponentProps<"button"> { options?: ConfettiOptions & ConfettiGlobalOptions & { canvas?: HTMLCanvasElement } } const ConfettiButtonComponent = ({ options, children, ...props }: ConfettiButtonProps) => { const handleClick = async (event: React.MouseEvent) => { try { const rect = event.currentTarget.getBoundingClientRect() const x = rect.left + rect.width / 2 const y = rect.top + rect.height / 2 await confetti({ ...options, origin: { x: x / window.innerWidth, y: y / window.innerHeight, }, }) } catch (error) { console.error("Confetti button error:", error) } } return ( ) } ConfettiButtonComponent.displayName = "ConfettiButton" export const ConfettiButton = ConfettiButtonComponent