import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react' import type { Theme, ThemeProvider as ThemeProviderType } from '../types' export interface ThemeContextValue extends ThemeProviderType { /** The resolved theme (light or dark, never system) */ resolvedTheme: 'light' | 'dark' /** Toggle between light and dark themes */ toggleTheme: () => void } export const ThemeContext = createContext(null) export interface ThemeProviderProps { children: ReactNode defaultTheme?: Theme storageKey?: string } /** * Provides theme state and methods to child components. * Handles system theme detection and localStorage persistence. * * @example * ```tsx * * * * ``` */ export function ThemeProvider({ children, defaultTheme = 'system', storageKey = 'mdxui-app-theme', }: ThemeProviderProps) { const [theme, setThemeState] = useState(() => { if (typeof window === 'undefined') return defaultTheme return (localStorage.getItem(storageKey) as Theme) || defaultTheme }) const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('light') // Resolve system theme useEffect(() => { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') const updateResolvedTheme = () => { if (theme === 'system') { setResolvedTheme(mediaQuery.matches ? 'dark' : 'light') } else { setResolvedTheme(theme) } } updateResolvedTheme() mediaQuery.addEventListener('change', updateResolvedTheme) return () => mediaQuery.removeEventListener('change', updateResolvedTheme) }, [theme]) // Apply theme to document useEffect(() => { const root = window.document.documentElement root.classList.remove('light', 'dark') root.classList.add(resolvedTheme) }, [resolvedTheme]) const setTheme = useCallback((newTheme: Theme) => { localStorage.setItem(storageKey, newTheme) setThemeState(newTheme) }, [storageKey]) const toggleTheme = useCallback(() => { setTheme(resolvedTheme === 'light' ? 'dark' : 'light') }, [resolvedTheme, setTheme]) return ( {children} ) } /** * Hook to access theme state and methods. * Must be used within a ThemeProvider. * * @example * ```tsx * const { theme, setTheme, resolvedTheme, toggleTheme } = useTheme() * * // Toggle theme * * ``` */ export function useTheme(): ThemeContextValue { const context = useContext(ThemeContext) if (!context) { throw new Error('useTheme must be used within a ThemeProvider') } return context }