'use client'; import { createContext, type ReactNode, useCallback, useContext, useEffect, useMemo, useState, } from 'react'; import { isThemeMode, resolveTheme, THEME_MEDIA_QUERY, THEME_STORAGE_KEY, type ResolvedTheme, type ThemeMode, } from '@/lib/theme/theme'; interface ThemeContextValue { themeMode: ThemeMode; resolvedTheme: ResolvedTheme; setThemeMode: (mode: ThemeMode) => void; } const ThemeContext = createContext(null); interface ThemeProviderProps { children: ReactNode; } function getSystemPreference() { return window.matchMedia(THEME_MEDIA_QUERY).matches; } function applyTheme(mode: ThemeMode) { const resolvedTheme = resolveTheme(mode, getSystemPreference()); const root = document.documentElement; root.dataset.themeMode = mode; root.dataset.theme = resolvedTheme; root.style.colorScheme = resolvedTheme; window.localStorage.setItem(THEME_STORAGE_KEY, mode); return resolvedTheme; } export function ThemeProvider({ children }: ThemeProviderProps) { const [themeMode, setThemeModeState] = useState('system'); const [resolvedTheme, setResolvedTheme] = useState('dark'); useEffect(() => { const root = document.documentElement; const domThemeMode = root.dataset.themeMode; const nextThemeMode = isThemeMode(domThemeMode) ? domThemeMode : 'system'; const nextResolvedTheme = applyTheme(nextThemeMode); setThemeModeState(nextThemeMode); setResolvedTheme(nextResolvedTheme); }, []); useEffect(() => { const mediaQuery = window.matchMedia(THEME_MEDIA_QUERY); const handleChange = () => { if (themeMode !== 'system') { return; } setResolvedTheme(applyTheme('system')); }; mediaQuery.addEventListener('change', handleChange); return () => { mediaQuery.removeEventListener('change', handleChange); }; }, [themeMode]); const setThemeMode = useCallback((mode: ThemeMode) => { setThemeModeState(mode); setResolvedTheme(applyTheme(mode)); }, []); const value = useMemo( () => ({ themeMode, resolvedTheme, setThemeMode, }), [resolvedTheme, setThemeMode, themeMode], ); return {children}; } export function useTheme() { const context = useContext(ThemeContext); if (!context) { throw new Error('useTheme must be used within ThemeProvider'); } return context; }