import React, { useMemo } from 'react'; import type { TrendSearchResultDto } from '../../utils/trendSearchApi'; import { formatUpbitKrwPrice } from '../../utils/safeFormat'; import { VirtualScroll } from '../common/VirtualScroll'; interface Props { results: TrendSearchResultDto[]; selectedMarket: string | null; onSelect: (row: TrendSearchResultDto) => void; loading?: boolean; flashMarkets?: Set; } function fmtPrice(n: number): string { return formatUpbitKrwPrice(n, '-'); } function fmtPct(n: number): string { const sign = n >= 0 ? '+' : ''; return `${sign}${n.toFixed(1)}%`; } function coinLabel(market: string, name?: string): string { const code = market.replace(/^KRW-/, ''); return name ? `${code}/KRW` : `${code}/KRW`; } const TrendSearchResultsGrid: React.FC = ({ results, selectedMarket, onSelect, loading, flashMarkets, }) => { const avgMatch = useMemo(() => { if (!results.length) return 0; return Math.round(results.reduce((s, r) => s + r.matchRate, 0) / results.length); }, [results]); const emptyBody = loading && results.length === 0 ?
스캔 중…
:
조건에 맞는 종목이 없습니다. 필터를 조정해 보세요.
; return (

실시간 종목 일치율 그리드

Search Condition Match Rate % Sorting · 100ms 실시간 처리

평균 {avgMatch}% ● LIVE
종목명 현재가 전일대비 일치율 비고
row.market} empty={emptyBody} role="rowgroup" aria-label="추세검색 결과" > {row => { const up = row.changeRate >= 0; const active = selectedMarket === row.market; const flash = flashMarkets?.has(row.market); return (
onSelect(row)} >
{coinLabel(row.market, row.koreanName)}
{fmtPrice(row.currentPrice)}
{fmtPct(row.changeRate)}
{row.matchRate}%
{row.highMatch && HIGH MATCH} {!row.highMatch && row.matchRate >= 70 && MATCH}
); }}
); }; export default TrendSearchResultsGrid;