Files
stock_chart_site/web/components/PeriodTabs.tsx
claude-owner 093ac86d1a feat(symbol-page): keyboard navigation for period tabs and watchlist
- ←/→ : 기간 탭 이전/다음 (양끝 clamp — wrap-around 안 함, 사용자가
  끝에 갇혔는지 인지 가능).
- S : 관심종목 토글 (watchlist.toggle, StarButton 자동 동기화).
- 입력 필드 포커스 시 무시 — 검색창/메모 텍스트 편집 방해 금지.
- modifier(Ctrl/Cmd/Alt) 가 눌리면 단축키 양보 — 브라우저/OS 단축키
  보존 (ex. Cmd+→ 줄 끝 이동, Alt+← 뒤로가기 등).
- ShortcutsHelp 에 새 단축키 등록 → ? 키 헬프 오버레이에서 발견 가능.
- PeriodTabs 에 `periodNeighbor(id, dir)` 헬퍼 export — 페이지가
  PERIODS 순서를 직접 알 필요 없도록.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 00:24:03 +09:00

62 lines
2.1 KiB
TypeScript

"use client";
import type { ChartInterval } from "../lib/api";
// 7-탭 기간 셀렉터. 각 탭이 (interval, days) 쌍으로 풀린다.
// 1D 만 10분봉, 5Y/MAX 는 자동으로 주봉/월봉 사용 (포인트 수 폭주 방지).
export type Period = "1D" | "1W" | "1M" | "3M" | "1Y" | "5Y" | "ALL";
type PeriodSpec = { id: Period; label: string; interval: ChartInterval; days: number };
const PERIODS: PeriodSpec[] = [
{ id: "1D", label: "1일", interval: "10m", days: 1 },
{ id: "1W", label: "1주", interval: "1d", days: 7 },
{ id: "1M", label: "1달", interval: "1d", days: 30 },
{ id: "3M", label: "3달", interval: "1d", days: 90 },
{ id: "1Y", label: "1년", interval: "1d", days: 365 },
{ id: "5Y", label: "5년", interval: "1w", days: 365 * 5 },
{ id: "ALL", label: "최대", interval: "1mo", days: 365 * 20 },
];
export function periodSpec(id: Period): PeriodSpec {
return PERIODS.find((p) => p.id === id) ?? PERIODS[2];
}
// ←/→ 키 네비게이션용 — 양끝 clamp (wrap-around 안 함, 사용자가 끝에 갇혔는지 인지 가능).
export function periodNeighbor(id: Period, dir: -1 | 1): Period {
const idx = PERIODS.findIndex((p) => p.id === id);
if (idx < 0) return id;
const next = idx + dir;
if (next < 0 || next >= PERIODS.length) return id;
return PERIODS[next].id;
}
type Props = {
value: Period;
onChange: (id: Period, interval: ChartInterval, days: number) => void;
};
export function PeriodTabs({ value, onChange }: Props) {
return (
<div className="inline-flex rounded-full border border-zinc-800 bg-zinc-900 p-1 text-xs">
{PERIODS.map((p) => {
const active = value === p.id;
return (
<button
key={p.id}
onClick={() => onChange(p.id, p.interval, p.days)}
className={
active
? "rounded-full bg-zinc-700 px-3 py-1 font-medium text-zinc-50"
: "rounded-full px-3 py-1 text-zinc-400 hover:text-zinc-100"
}
>
{p.label}
</button>
);
})}
</div>
);
}