kcg-monitoring/frontend/src/hooks/useTheme.ts
htlee 2534faa488 feat: 프론트엔드 모노레포 이관 + signal-batch 연동 + Tailwind/i18n/테마 전환
- frontend/ 폴더로 프론트엔드 전체 이관
- signal-batch API 연동 (한국 선박 위치 데이터)
- Tailwind CSS 4 + CSS 변수 테마 토큰 (dark/light)
- i18next 다국어 (ko/en) 인프라 + 28개 컴포넌트 적용
- 레이어 패널 트리 구조 재설계 (카테고리별 온/오프, 범례)
- Google OAuth 로그인 화면 + DEV LOGIN 우회
- 외부 API CORS 프록시 전환 (Airplanes.live, OpenSky, CelesTrak)
- ShipLayer 이미지 탭 전환 (signal-batch / MarineTraffic)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 13:54:41 +09:00

48 lines
1.0 KiB
TypeScript

import { useState, useCallback, useEffect } from 'react';
type Theme = 'dark' | 'light';
const STORAGE_KEY = 'kcg:theme';
const DEFAULT_THEME: Theme = 'dark';
function readStoredTheme(): Theme {
try {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored === 'dark' || stored === 'light') return stored;
} catch {
// localStorage unavailable
}
return DEFAULT_THEME;
}
function applyTheme(theme: Theme) {
document.documentElement.dataset.theme = theme;
}
export function useTheme() {
const [theme, setThemeState] = useState<Theme>(() => {
const t = readStoredTheme();
applyTheme(t);
return t;
});
useEffect(() => {
applyTheme(theme);
}, [theme]);
const setTheme = useCallback((t: Theme) => {
setThemeState(t);
try {
localStorage.setItem(STORAGE_KEY, t);
} catch {
// localStorage unavailable
}
}, []);
const toggleTheme = useCallback(() => {
setTheme(theme === 'dark' ? 'light' : 'dark');
}, [theme, setTheme]);
return { theme, setTheme, toggleTheme } as const;
}