import { useEffect, useRef, useState } from "react"; import { cn } from "@/lib/utils"; import { AnimatePresence, motion } from "framer-motion"; interface AnimatedQuantityProps { value: number; format?: (val: number) => string; className?: string; /** 값 변동 시 배경 깜빡임 */ useColor?: boolean; /** 정렬 방향 (ask: 우측 정렬/왼쪽으로 확장, bid: 좌측 정렬/오른쪽으로 확장) */ side?: "ask" | "bid"; } /** * 실시간 수량 표시 — 값이 변할 때 ±diff를 인라인으로 보여줍니다. */ export function AnimatedQuantity({ value, format = (v) => v.toLocaleString(), className, useColor = false, side = "bid", }: AnimatedQuantityProps) { const prevRef = useRef(value); const [diff, setDiff] = useState(null); const [flash, setFlash] = useState<"up" | "down" | null>(null); useEffect(() => { if (prevRef.current === value) return; const delta = value - prevRef.current; prevRef.current = value; if (delta === 0) return; setDiff(delta); setFlash(delta > 0 ? "up" : "down"); const timer = setTimeout(() => { setDiff(null); setFlash(null); }, 1200); return () => clearTimeout(timer); }, [value]); return ( {/* 배경 깜빡임 */} {useColor && flash && ( )} {/* 매도(Ask)일 경우 Diff가 먼저 와야 텍스트가 우측 정렬된 상태에서 흔들리지 않음 */} {side === "ask" && } {/* 수량 값 */} {format(value)} {/* 매수(Bid)일 경우 Diff가 뒤에 와야 텍스트가 좌측 정렬된 상태에서 흔들리지 않음 */} {side !== "ask" && } ); } function DiffChange({ diff }: { diff: number | null }) { return ( {diff != null && diff !== 0 && ( 0 ? "text-red-500" : "text-blue-600 dark:text-blue-400", )} > {diff > 0 ? `+${diff.toLocaleString()}` : diff.toLocaleString()} )} ); }