vojo/src/app/hooks/useTheme.ts

72 lines
2.1 KiB
TypeScript

import { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { onDarkFontWeight, onLightFontWeight } from '../../config.css';
import { darkTheme, lightTheme } from '../../colors.css';
import { settingsAtom, ThemeId } from '../state/settings';
import { useSetting } from '../state/hooks/settings';
export enum ThemeKind {
Light = 'light',
Dark = 'dark',
}
export type Theme = {
id: ThemeId;
kind: ThemeKind;
classNames: string[];
};
export const LightTheme: Theme = {
id: 'light-theme',
kind: ThemeKind.Light,
classNames: ['light-theme', lightTheme, onLightFontWeight, 'prism-light'],
};
export const DarkTheme: Theme = {
id: 'dark-theme',
kind: ThemeKind.Dark,
classNames: ['dark-theme', darkTheme, onDarkFontWeight, 'prism-dark'],
};
export const useSystemThemeKind = (): ThemeKind => {
const darkModeQueryList = useMemo(() => window.matchMedia('(prefers-color-scheme: dark)'), []);
const [themeKind, setThemeKind] = useState<ThemeKind>(
darkModeQueryList.matches ? ThemeKind.Dark : ThemeKind.Light
);
useEffect(() => {
const handleMediaQueryChange = () => {
setThemeKind(darkModeQueryList.matches ? ThemeKind.Dark : ThemeKind.Light);
};
darkModeQueryList.addEventListener('change', handleMediaQueryChange);
return () => {
darkModeQueryList.removeEventListener('change', handleMediaQueryChange);
};
}, [darkModeQueryList, setThemeKind]);
return themeKind;
};
export const useActiveTheme = (): Theme => {
const systemThemeKind = useSystemThemeKind();
const [systemTheme] = useSetting(settingsAtom, 'useSystemTheme');
const [themeId] = useSetting(settingsAtom, 'themeId');
if (!systemTheme) {
return themeId === DarkTheme.id ? DarkTheme : LightTheme;
}
return systemThemeKind === ThemeKind.Dark ? DarkTheme : LightTheme;
};
const ThemeContext = createContext<Theme | null>(null);
export const ThemeContextProvider = ThemeContext.Provider;
export const useTheme = (): Theme => {
const theme = useContext(ThemeContext);
if (!theme) {
throw new Error('No theme provided!');
}
return theme;
};