"use client"; import { AnimatePresence, motion } from "motion/react"; import { createContext, useCallback, useContext, useRef, useState, type ReactNode, } from "react"; type Ball = { id: number; startX: number; startY: number; endX: number; endY: number }; type CartFlyContextValue = { registerCartIcon: (el: HTMLElement | null) => void; fly: (sourceEl: HTMLElement) => void; /** Real cart count minus this = what the navbar badge should currently * show — held back for as long as a ball is still mid-flight, so the * badge only "counts up" once the ball visually lands there. */ pendingCount: number; }; const CartFlyContext = createContext(null); let ballSeq = 0; /** * Mounted once at the root (app/layout.tsx) so both the cart icon (in * Navbar, one branch of the tree) and any add-to-cart button (in page * content, a different branch) can share one flight target/queue via * context instead of prop-drilling across unrelated component trees. */ export function CartFlyProvider({ children }: { children: ReactNode }) { const cartIconRef = useRef(null); const [balls, setBalls] = useState([]); const [pendingCount, setPendingCount] = useState(0); const registerCartIcon = useCallback((el: HTMLElement | null) => { cartIconRef.current = el; }, []); const fly = useCallback((sourceEl: HTMLElement) => { const target = cartIconRef.current; if (!target) return; const from = sourceEl.getBoundingClientRect(); const to = target.getBoundingClientRect(); setBalls((prev) => [ ...prev, { id: ++ballSeq, startX: from.left + from.width / 2, startY: from.top + from.height / 2, endX: to.left + to.width / 2, endY: to.top + to.height / 2, }, ]); setPendingCount((n) => n + 1); }, []); function handleArrive(id: number) { setBalls((prev) => prev.filter((b) => b.id !== id)); setPendingCount((n) => Math.max(0, n - 1)); } return ( {children} {balls.map((ball) => ( handleArrive(ball.id)} style={{ marginLeft: -12, marginTop: -12 }} className="fixed left-0 top-0 z-[100] size-6 rounded-full bg-brand pointer-events-none" /> ))} ); } export function useCartFly() { const ctx = useContext(CartFlyContext); if (!ctx) throw new Error("useCartFly must be used within CartFlyProvider"); return ctx; }