Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import { createContext, useContext, useEffect, useState } from 'react';
- /** Local storage key for preferred theme */
- const key = 'theme_v4';
- const themes = [ 'auto', 'light', 'dark' ] as const;
- type ValidTheme = typeof themes[number];
- type ThemeContext = {
- setTheme: (theme: ValidTheme) => void
- themes: typeof themes,
- theme: ValidTheme,
- resolved_theme: Exclude<ValidTheme, 'auto'>
- };
- const Context = createContext<ThemeContext>(null);
- function getPreferredTheme(): 'dark' | 'light' {
- if (typeof window === 'undefined') return 'light';
- if (window.matchMedia('(prefers-color-scheme: dark)').matches) return 'dark';
- return 'light';
- }
- function getStoredTheme(key: string): 'dark' | 'light' | undefined {
- if (typeof localStorage === 'undefined') return;
- const stored = localStorage.getItem(key);
- if (stored === 'dark' || stored === 'light') return stored;
- }
- /**
- * Set CSS theme
- */
- export function useTheme() {
- const ctx = useContext(Context);
- if (! ctx) throw new Error("ThemeProvider not loaded!");
- return ctx;
- }
- interface ThemeProviderProps {
- children: React.ReactNode;
- theme?: ValidTheme
- }
- function ThemeProvider(props: ThemeProviderProps) {
- const { children } = props;
- const [ theme, setTheme ] = useState<ValidTheme>(
- props.theme ||
- getStoredTheme(key) ||
- 'auto'
- );
- useEffect(() => {
- if (theme === 'auto') {
- document.documentElement.setAttribute('data-bs-theme', getPreferredTheme());
- localStorage.removeItem(key);
- } else {
- document.documentElement.setAttribute('data-bs-theme', theme);
- localStorage.setItem(key, theme);
- }
- }, [theme]);
- return <Context.Provider value={{
- setTheme,
- themes,
- theme,
- resolved_theme: theme === 'auto' ? getPreferredTheme() : theme
- }}>
- { children }
- </Context.Provider>;
- }
- export default ThemeProvider;
Advertisement
Add Comment
Please, Sign In to add comment