Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 87 88 89 90 | 3x 3x 3x 25x 23x 23x 30x 30x 49x 49x 49x 49x 12x 12x 12x 12x 12x 12x 12x 49x 29x 17x 17x 17x 49x 29x 11x 11x 1x 1x 1x 11x 11x 49x 6x 6x 49x 3x 49x 25x 49x | 'use client';
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import type { ReactNode } from 'react';
type Theme = 'light' | 'dark' | 'system';
type ResolvedTheme = 'light' | 'dark';
interface ThemeContextValue {
theme: Theme;
resolvedTheme: ResolvedTheme;
setTheme: (theme: Theme) => void;
toggleTheme: () => void;
}
const STORAGE_KEY = 'slds-theme';
const ThemeContext = createContext<ThemeContextValue>({
theme: 'system',
resolvedTheme: 'light',
setTheme: () => {},
toggleTheme: () => {},
});
export const useTheme = () => useContext(ThemeContext);
function getSystemPreference(): ResolvedTheme {
Iif (typeof window === 'undefined') return 'light';
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
function resolveTheme(theme: Theme): ResolvedTheme {
return theme === 'system' ? getSystemPreference() : theme;
}
function applyTheme(resolved: ResolvedTheme) {
document.documentElement.setAttribute('data-theme', resolved);
}
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setThemeState] = useState<Theme>('system');
const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>('light');
const [mounted, setMounted] = useState(false);
useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEY);
const initial: Theme = stored === 'light' || stored === 'dark' || stored === 'system' ? stored : 'system';
const resolved = resolveTheme(initial);
setThemeState(initial);
setResolvedTheme(resolved);
applyTheme(resolved);
setMounted(true);
}, []);
useEffect(() => {
if (!mounted) return;
const resolved = resolveTheme(theme);
setResolvedTheme(resolved);
applyTheme(resolved);
}, [theme, mounted]);
useEffect(() => {
if (!mounted || theme !== 'system') return;
const mql = window.matchMedia('(prefers-color-scheme: dark)');
const handler = () => {
const resolved = resolveTheme('system');
setResolvedTheme(resolved);
applyTheme(resolved);
};
mql.addEventListener('change', handler);
return () => mql.removeEventListener('change', handler);
}, [theme, mounted]);
const setTheme = useCallback((next: Theme) => {
setThemeState(next);
localStorage.setItem(STORAGE_KEY, next);
}, []);
const toggleTheme = useCallback(() => {
setTheme(resolvedTheme === 'dark' ? 'light' : 'dark');
}, [resolvedTheme, setTheme]);
const value = useMemo(
() => ({ theme, resolvedTheme, setTheme, toggleTheme }),
[theme, resolvedTheme, setTheme, toggleTheme],
);
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
}
|