buster/web/src/hooks/dom/useThemeDetector.ts

58 lines
1.7 KiB
TypeScript

'use client';
import { ENABLE_DARK_MODE } from '@/context/BusterStyles/BusterStyles';
import { useMemoizedFn } from 'ahooks';
import { useEffect, useLayoutEffect, useState } from 'react';
import { isServer } from '@tanstack/react-query';
const DARK_MODE_MEDIA = '(prefers-color-scheme: dark)';
export const useThemeDetector = ({ addDarkClass }: { addDarkClass?: boolean }) => {
const [isDarkTheme, setIsDarkTheme] = useState(() => getSystemTheme());
const getSystemTheme = (e?: MediaQueryList | MediaQueryListEvent) => {
if (!ENABLE_DARK_MODE) return false;
if (isServer) return false;
if (!e) e = window.matchMedia(DARK_MODE_MEDIA);
const isDark = e.matches;
return isDark;
};
const getCurrentTheme = useMemoizedFn(() => {
if (!isServer) {
document.documentElement.style.display = 'none';
const isDarkMode = getSystemTheme();
if (addDarkClass) {
const d = document.documentElement;
d.classList.toggle('dark', isDarkMode);
d.classList.toggle('bg-black', isDarkMode);
d.setAttribute('data-color-scheme', !isDarkMode ? 'light' : 'dark');
}
// trigger reflow so that overflow style is applied
document.documentElement.style.display = '';
return isDarkMode;
}
return false;
});
const mqListener = useMemoizedFn(() => {
setIsDarkTheme(getCurrentTheme());
});
useEffect(() => {
const darkThemeMq = window.matchMedia(DARK_MODE_MEDIA);
darkThemeMq.addEventListener('change', mqListener);
return () => darkThemeMq.removeEventListener('change', mqListener);
}, []);
useLayoutEffect(() => {
setIsDarkTheme(getCurrentTheme());
}, []);
return isDarkTheme;
};