'use client'; import { useEffect, useRef, useState, useCallback } from 'react'; import { useCryptoContext } from '@/contexts/cryptoProvider'; import './market-header.scss'; function formatPrice(price: number): string { if (price >= 1000) { return price.toLocaleString('ko-KR', { maximumFractionDigits: 0 }); } if (price >= 1) { return price.toLocaleString('ko-KR', { maximumFractionDigits: 2 }); } return price.toLocaleString('ko-KR', { maximumFractionDigits: 4 }); } function formatVolume(volume: number): string { if (volume >= 1_000_000_000) { return `${(volume / 1_000_000_000).toFixed(1)}B`; } if (volume >= 1_000_000) { return `${(volume / 1_000_000).toFixed(1)}M`; } if (volume >= 1_000) { return `${(volume / 1_000).toFixed(1)}K`; } return volume.toFixed(2); } const MAX_SPARKLINE_POINTS = 60; function VolumeSparkline({ value, market }: { value: number; market: string }) { const historyRef = useRef([]); const prevMarketRef = useRef(market); const [open, setOpen] = useState(false); const wrapperRef = useRef(null); // 마켓 변경 시 히스토리 초기화 if (market !== prevMarketRef.current) { historyRef.current = []; prevMarketRef.current = market; } useEffect(() => { if (value <= 0) return; const h = historyRef.current; h.push(value); if (h.length > MAX_SPARKLINE_POINTS) { h.shift(); } }, [value]); // 외부 클릭 시 닫기 const handleClickOutside = useCallback((e: MouseEvent) => { if (wrapperRef.current && !wrapperRef.current.contains(e.target as Node)) { setOpen(false); } }, []); useEffect(() => { if (open) { document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); } }, [open, handleClickOutside]); const history = historyRef.current; if (history.length < 2) return null; const min = Math.min(...history); const max = Math.max(...history); const range = max - min || 1; const w = 80; const h = 20; const points = history.map((v, i) => { const x = (i / (history.length - 1)) * w; const y = h - ((v - min) / range) * h; return `${x.toFixed(1)},${y.toFixed(1)}`; }).join(' '); const isUp = history[history.length - 1] >= history[history.length - 2]; const color = isUp ? 'hsl(var(--crypto-up))' : 'hsl(var(--crypto-down))'; return (
setOpen((prev) => !prev)} > {open && (
거래량(24h) {formatVolume(value)} KRW
)}
); } export default function MarketHeader() { const { selectedMarket, tickers } = useCryptoContext(); const ticker = tickers.get(selectedMarket); if (!ticker) { return (
준비 중...
); } const changeClass = ticker.change === 'RISE' ? 'up' : ticker.change === 'FALL' ? 'down' : 'neutral'; const changeRate = (ticker.signedChangeRate * 100).toFixed(2); const changeSign = ticker.signedChangePrice >= 0 ? '+' : ''; return (
{ticker.symbol} /{selectedMarket.split('-')[0]}
{formatPrice(ticker.tradePrice)}
{changeSign}{formatPrice(ticker.signedChangePrice)} ({changeSign}{changeRate}%)
고가 {formatPrice(ticker.highPrice)}
저가 {formatPrice(ticker.lowPrice)}
거래량(24h)
); }