Files
edr-platform/apps/edr-passenger-web/portal/src/components/ThemeProvider.tsx
2026-05-31 13:15:44 +03:00

95 lines
2.7 KiB
TypeScript

'use client';
import { createContext, useContext, useEffect, useState } from 'react';
type Theme = 'light' | 'dark' | 'system';
interface ThemeContextType {
theme: Theme;
setTheme: (theme: Theme) => void;
resolvedTheme: 'light' | 'dark';
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<Theme>(() => {
if (typeof window !== 'undefined') {
const savedTheme = localStorage.getItem('theme') as Theme | null;
if (savedTheme) return savedTheme;
}
return 'system';
});
const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>(() => {
if (typeof window !== 'undefined') {
const savedTheme = localStorage.getItem('theme') as Theme | null;
if (savedTheme === 'dark') return 'dark';
if (savedTheme === 'light') return 'light';
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
return 'light';
});
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
useEffect(() => {
if (!mounted) return;
const root = window.document.documentElement;
// Remove previous theme classes
root.classList.remove('light', 'dark');
let effectiveTheme: 'light' | 'dark';
if (theme === 'system') {
// Use system preference
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
effectiveTheme = systemTheme;
} else {
effectiveTheme = theme;
}
// Apply theme
root.classList.add(effectiveTheme);
setResolvedTheme(effectiveTheme);
// Save to localStorage
localStorage.setItem('theme', theme);
}, [theme, mounted]);
// Listen for system theme changes
useEffect(() => {
if (theme !== 'system') return;
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleChange = (e: MediaQueryListEvent) => {
const root = window.document.documentElement;
root.classList.remove('light', 'dark');
const newTheme = e.matches ? 'dark' : 'light';
root.classList.add(newTheme);
setResolvedTheme(newTheme);
};
mediaQuery.addEventListener('change', handleChange);
return () => mediaQuery.removeEventListener('change', handleChange);
}, [theme]);
return (
<ThemeContext.Provider value={{ theme, setTheme, resolvedTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}