Files
live-hub-py/web/frontend/src/store/theme.tsx
T

51 lines
1.5 KiB
TypeScript

import { useState, useEffect, useCallback, type ReactNode } from 'react';
import {
DEFAULT_THEME_MODE,
THEME_STORAGE_KEY,
ThemeContext,
type ThemeMode,
} from './themeContext';
export type { ThemeMode } from './themeContext';
export function ThemeProvider({ children }: { children: ReactNode }) {
const [mode, setModeState] = useState<ThemeMode>(() => {
const saved = localStorage.getItem(THEME_STORAGE_KEY);
return (saved as ThemeMode) || DEFAULT_THEME_MODE;
});
// 监听系统主题变化
const [systemIsDark, setSystemIsDark] = useState(() =>
window.matchMedia('(prefers-color-scheme: dark)').matches
);
useEffect(() => {
const mql = window.matchMedia('(prefers-color-scheme: dark)');
const handler = (e: MediaQueryListEvent) => setSystemIsDark(e.matches);
mql.addEventListener('change', handler);
return () => mql.removeEventListener('change', handler);
}, []);
const isDark = mode === 'dark' || (mode === 'system' && systemIsDark);
const setMode = useCallback((newMode: ThemeMode) => {
setModeState(newMode);
localStorage.setItem(THEME_STORAGE_KEY, newMode);
}, []);
const toggle = useCallback(() => {
setMode(isDark ? 'light' : 'dark');
}, [isDark, setMode]);
// 同步到 HTML root,方便 CSS 使用
useEffect(() => {
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
}, [isDark]);
return (
<ThemeContext.Provider value={{ mode, isDark, setMode, toggle }}>
{children}
</ThemeContext.Provider>
);
}