'use client'; import './major-quotes.scss'; import { useEffect, useMemo, useState } from 'react'; import TradingViewChart from './TradingViewChart'; import { moveDir, formatFlucRate, formatIndexClose } from '@/types/worldIndex'; import { type MarketQuoteRow, groupLabel, tvSymbolForQuote, formatVolume, formatChangeVal } from '@/types/marketQuote'; // 주요 종목 현황 — 그룹 탭(미국/아시아/…) + 데이터 그리드(종목·현재가·전일비·등락률·거래량·시간·차트). // 차트는 TradingView 드릴다운(미국주만 심볼 확정 매핑, 그 외는 미표시). 전일 마감 기준. type Props = { rows: MarketQuoteRow[]; }; export default function MajorQuotesGrid({ rows }: Props) { const [group, setGroup] = useState(null); const [chart, setChart] = useState<{ symbol: string; name: string }|null>(null); // 차트 모달 Esc 닫기 useEffect(() => { if (!chart) { return; } const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') { setChart(null); } }; window.addEventListener('keydown', onKey); return () => { window.removeEventListener('keydown', onKey); }; }, [chart]); // 그룹 목록 — 등장 순서 유지(데이터 있는 그룹만) const groups = useMemo(() => { const seen: string[] = []; for (const r of rows) { if (!seen.includes(r.groupCode)) { seen.push(r.groupCode); } } return seen; }, [rows]); const activeGroup = group && groups.includes(group) ? group : (groups[0] ?? null); const gridRows = useMemo(() => { return rows.filter((r) => r.groupCode === activeGroup).sort((a, b) => b.flucRateBp - a.flucRateBp); }, [rows, activeGroup]); const asOf = useMemo(() => { return rows.reduce((max, c) => (max === null || c.tradeDate > max ? c.tradeDate : max), null); }, [rows]); if (groups.length === 0) { return null; } return (

주요 종목 현황

{asOf && 전일 마감 기준 {asOf}}
{groups.map((g) => ( ))}
{gridRows.map((row) => { const dir = moveDir(row.flucRateBp); const tv = tvSymbolForQuote(row.symbol); return ( ); })}
종목 현재가 전일비 등락률 거래량 시간 차트
{row.name} {formatIndexClose(row.close)} {formatChangeVal(row.changeVal)} {formatFlucRate(row.flucRateBp)} {formatVolume(row.volume)} {row.tradeDate} {tv ? ( ) : ( )}
{chart && (
setChart(null)} >
e.stopPropagation()}>
{chart.name}

실시간 차트 제공 · TradingView

)}
); }