"use client"; import { useState, type ReactNode } from "react"; import { AnimatePresence, motion, useReducedMotion } from "motion/react"; type MorphActionProps = { children: ReactNode; className?: string; /** Runs on click. Return a promise and the button waits for it. */ onClick?: () => unknown; /** Read out to screen readers when the action finishes. */ doneLabel?: string; /** Seconds the tick stays before the label comes back. */ doneFor?: number; }; type State = "idle" | "busy" | "done"; /* MorphAction: the pill shrinks into a spinner while the action runs, shows a tick when it is done, then turns back. */ export function MorphAction({ children, className = "bg-[#a78bfa] text-[15px] font-medium text-[#1a1530]", onClick, doneLabel = "Done", doneFor = 1.6, }: MorphActionProps) { const reduce = useReducedMotion(); const [state, setState] = useState("idle"); const run = async () => { if (state !== "idle") return; setState("busy"); try { await onClick?.(); setState("done"); setTimeout(() => setState("idle"), doneFor * 1000); } catch { setState("idle"); } }; const fade = { initial: { opacity: 0, scale: 0.6 }, animate: { opacity: 1, scale: 1 }, exit: { opacity: 0, scale: 0.6 }, transition: { duration: reduce ? 0 : 0.15 }, }; return ( {state === "idle" && ( {children} )} {state === "busy" && ( )} {state === "done" && ( )} {state === "busy" ? "Working" : state === "done" ? doneLabel : ""} ); }