Spaces:
Running
Running
File size: 2,426 Bytes
ffcad5d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
const ThemeContext = createContext();
const THEME_KEY = 'theme';
const getSystemTheme = () =>
(typeof window !== 'undefined' &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
? 'dark'
: 'light';
const readPreference = () => {
try {
const saved = localStorage.getItem(THEME_KEY);
if (saved === 'light' || saved === 'dark' || saved === 'system') return saved;
// Legacy values or empty → System (follow OS)
if (saved === 'auto' || saved === 'default' || saved === '') return 'system';
} catch {
/* ignore */
}
return 'system';
};
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
export const ThemeProvider = ({ children }) => {
const [preference, setPreferenceState] = useState(readPreference);
const [systemTheme, setSystemTheme] = useState(getSystemTheme);
useEffect(() => {
const mq = window.matchMedia('(prefers-color-scheme: dark)');
const onChange = (e) => setSystemTheme(e.matches ? 'dark' : 'light');
mq.addEventListener('change', onChange);
setSystemTheme(mq.matches ? 'dark' : 'light');
return () => mq.removeEventListener('change', onChange);
}, []);
const theme = preference === 'system' ? systemTheme : preference;
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
}, [theme]);
useEffect(() => {
try {
localStorage.setItem(THEME_KEY, preference);
} catch {
/* ignore */
}
}, [preference]);
const setThemePreference = useCallback((next) => {
if (next === 'light' || next === 'dark' || next === 'system') {
setPreferenceState(next);
}
}, []);
const toggleTheme = useCallback(() => {
setPreferenceState((prev) => {
const resolved = prev === 'system' ? getSystemTheme() : prev;
return resolved === 'light' ? 'dark' : 'light';
});
}, []);
const value = {
theme,
preference,
setThemePreference,
toggleTheme,
isLight: theme === 'light',
isDark: theme === 'dark',
};
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
};
|