Files
stock_chart_site/web/lib/recent.ts
claude-owner aa58761176 feat(ui): 최근 본 종목 (localStorage + 홈 카드 + TopNav popover)
- web/lib/recent.ts: stockchart.recent KEY, push/get/clear/subscribe (storage + custom event)
- [code]/page.tsx: 페이지 진입 시 recent.push(code) — 자동 트래킹
- web/components/RecentTiles.tsx: 홈 카드 그리드 (최대 10개, 2~5컬럼)
- web/app/page.tsx: SearchBox 아래에 RecentTiles 배치 (빈 리스트면 자동 숨김)
- TopNav: 검색어 비면서 포커스 시 최근 본 종목 popover 표시 + "지우기" 버튼

토스 "최근 본 종목" 톤 — 검색 빈 상태에서도 popover 가 비어 있지 않게.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 15:14:40 +09:00

53 lines
1.5 KiB
TypeScript

// 최근 본 종목 — localStorage 기반. 종목 페이지 진입 시 자동으로 push.
// 단순 string[] 직렬화. 최신이 앞쪽, 최대 MAX 개로 캡.
const KEY = "stockchart.recent";
const MAX = 10;
function read(): string[] {
if (typeof window === "undefined") return [];
try {
const raw = window.localStorage.getItem(KEY);
if (!raw) return [];
const v = JSON.parse(raw);
return Array.isArray(v) ? v.filter((s) => typeof s === "string") : [];
} catch {
return [];
}
}
function write(codes: string[]) {
if (typeof window === "undefined") return;
window.localStorage.setItem(KEY, JSON.stringify(codes));
// 같은 탭은 storage 이벤트가 안 떠서 수동 dispatch.
window.dispatchEvent(new CustomEvent("recent:change"));
}
export const recent = {
get(): string[] {
return read();
},
push(code: string): void {
if (!code) return;
const cur = read().filter((c) => c !== code);
cur.unshift(code);
write(cur.slice(0, MAX));
},
clear(): void {
write([]);
},
subscribe(cb: () => void): () => void {
if (typeof window === "undefined") return () => {};
const onStorage = (e: StorageEvent) => {
if (e.key === KEY) cb();
};
const onCustom = () => cb();
window.addEventListener("storage", onStorage);
window.addEventListener("recent:change", onCustom as EventListener);
return () => {
window.removeEventListener("storage", onStorage);
window.removeEventListener("recent:change", onCustom as EventListener);
};
},
};