"use client"; // 글로벌 sticky 헤더 — 모든 페이지 상단에 고정. 토스 톱바 톤. // 좌: 로고/타이틀, 중: 컴팩트 검색 (debounced + popover), 우: 관심종목 링크. // // 홈의 큰 SearchBox 와 별개로 동작. 결과 클릭 시 popover 가 닫히고 페이지 이동. // Esc 또는 바깥 클릭으로 popover 닫힘. import Link from "next/link"; import { useEffect, useRef, useState } from "react"; import { api, type Symbol } from "../lib/api"; import { recent } from "../lib/recent"; export function TopNav() { const [q, setQ] = useState(""); const [items, setItems] = useState([]); const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); const [recentCodes, setRecentCodes] = useState([]); const wrapRef = useRef(null); // 최근 본 종목 구독 — popover 빈 상태에서 보여줌. useEffect(() => { const refresh = () => setRecentCodes(recent.get()); refresh(); return recent.subscribe(refresh); }, []); // 디바운스 검색. 입력 비면 결과 클리어. useEffect(() => { const term = q.trim(); if (!term) { setItems([]); return; } setLoading(true); const h = setTimeout(async () => { try { const r = await api.search(term, false, 8, false); setItems(r.items); } catch { setItems([]); } finally { setLoading(false); } }, 200); return () => clearTimeout(h); }, [q]); // 바깥 클릭으로 popover 닫기. useEffect(() => { const onDoc = (e: MouseEvent) => { if (!wrapRef.current) return; if (!wrapRef.current.contains(e.target as Node)) setOpen(false); }; const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") setOpen(false); }; document.addEventListener("mousedown", onDoc); document.addEventListener("keydown", onKey); return () => { document.removeEventListener("mousedown", onDoc); document.removeEventListener("keydown", onKey); }; }, []); return (
Stock Chart
{ setQ(e.target.value); setOpen(true); }} onFocus={() => setOpen(true)} placeholder="종목 검색" className="w-full rounded-md border border-zinc-800 bg-zinc-900 px-3 py-1.5 text-xs text-zinc-100 outline-none focus:border-zinc-600" /> {open && q.trim() && (
{loading && (
검색중…
)} {!loading && items.length === 0 && (
결과 없음
)}
    {items.map((it) => (
  • { setOpen(false); setQ(""); }} className="flex items-center justify-between gap-2 px-3 py-2 text-xs hover:bg-zinc-800/60" > {it.name} {it.is_seed && ( SEED )} {it.code} · {it.market}
  • ))}
)} {open && !q.trim() && recentCodes.length > 0 && (
최근 본 종목
    {recentCodes.map((c) => (
  • setOpen(false)} className="block px-3 py-2 text-xs text-zinc-200 hover:bg-zinc-800/60" > {c}
  • ))}
)}
🔔 ★ 관심
); }