가상투자 화면 추가
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import React, { memo, useEffect, useRef, useState } from 'react';
|
||||
import type { ChangeDir, TickerData } from '../../hooks/useMarketTicker';
|
||||
|
||||
function fmtKrw(price: number | null | undefined): string {
|
||||
if (price == null || !Number.isFinite(price)) return '-';
|
||||
if (price >= 1_000_000) return price.toLocaleString('ko-KR', { maximumFractionDigits: 0 });
|
||||
if (price >= 1_000) return price.toLocaleString('ko-KR', { maximumFractionDigits: 0 });
|
||||
if (price >= 1) return price.toLocaleString('ko-KR', { maximumFractionDigits: 2 });
|
||||
if (price >= 0.01) return price.toFixed(4);
|
||||
return price.toFixed(8);
|
||||
}
|
||||
|
||||
function fmtPct(rate: number | null | undefined): string {
|
||||
if (rate == null || !Number.isFinite(rate)) return '-';
|
||||
const sign = rate >= 0 ? '+' : '';
|
||||
return `${sign}${(rate * 100).toFixed(2)}%`;
|
||||
}
|
||||
|
||||
const ChangeArrow = memo(function ChangeArrow({ change }: { change: ChangeDir }) {
|
||||
if (change === 'RISE') return <span className="vtd-target-arrow vtd-target-arrow--rise">▲</span>;
|
||||
if (change === 'FALL') return <span className="vtd-target-arrow vtd-target-arrow--fall">▼</span>;
|
||||
return <span className="vtd-target-arrow vtd-target-arrow--even">─</span>;
|
||||
});
|
||||
|
||||
interface Props {
|
||||
market: string;
|
||||
ticker?: TickerData;
|
||||
}
|
||||
|
||||
const VirtualTargetQuote: React.FC<Props> = ({ market, ticker }) => {
|
||||
const code = market.replace(/^KRW-/, '');
|
||||
const change: ChangeDir = ticker?.change ?? 'EVEN';
|
||||
const colorCls = change === 'RISE' ? 'vtd-target-up' : change === 'FALL' ? 'vtd-target-dn' : 'vtd-target-even';
|
||||
|
||||
const [flashClass, setFlashClass] = useState('');
|
||||
const prevPriceRef = useRef<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const price = ticker?.tradePrice ?? null;
|
||||
if (price === null) return;
|
||||
const prev = prevPriceRef.current;
|
||||
if (prev !== null && prev !== price) {
|
||||
const cls = price > prev ? 'vtd-target-quote--flash-rise' : 'vtd-target-quote--flash-fall';
|
||||
setFlashClass(cls);
|
||||
const t = setTimeout(() => setFlashClass(''), 700);
|
||||
prevPriceRef.current = price;
|
||||
return () => clearTimeout(t);
|
||||
}
|
||||
prevPriceRef.current = price;
|
||||
}, [ticker?.tradePrice]);
|
||||
|
||||
return (
|
||||
<div className={`vtd-target-quote ${flashClass}`.trim()} aria-label="현재가 및 전일 대비">
|
||||
<div className="vtd-target-quote-left">
|
||||
<ChangeArrow change={change} />
|
||||
<span className="vtd-target-quote-pair">{code}/KRW</span>
|
||||
</div>
|
||||
<div className="vtd-target-quote-right">
|
||||
<span className={`vtd-target-price ${colorCls}`}>
|
||||
{ticker ? fmtKrw(ticker.tradePrice) : '-'}
|
||||
</span>
|
||||
<span className={`vtd-target-change ${colorCls}`}>
|
||||
{ticker ? fmtPct(ticker.changeRate) : '-'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default VirtualTargetQuote;
|
||||
Reference in New Issue
Block a user