lms/resources/js/hooks/use-appearance.tsx
2025-12-15 12:26:23 +01:00

72 lines
2.1 KiB
TypeScript

import { useCallback, useEffect, useState } from 'react';
const prefersDark = () => {
if (typeof window === 'undefined') {
return false;
}
return window.matchMedia('(prefers-color-scheme: dark)').matches;
};
const setCookie = (name: string, value: string, days = 365) => {
if (typeof document === 'undefined') {
return;
}
const maxAge = days * 24 * 60 * 60;
document.cookie = `${name}=${value};path=/;max-age=${maxAge};SameSite=Lax`;
};
const applyTheme = (appearance: Appearance) => {
const isDark = appearance === 'dark' || (appearance === 'system' && prefersDark());
document.documentElement.classList.toggle('dark', isDark);
};
const mediaQuery = () => {
if (typeof window === 'undefined') {
return null;
}
return window.matchMedia('(prefers-color-scheme: dark)');
};
const handleSystemThemeChange = () => {
const currentAppearance = localStorage.getItem('appearance') as Appearance;
applyTheme(currentAppearance || 'system');
};
export function initializeTheme() {
const savedAppearance = (localStorage.getItem('appearance') as Appearance) || 'light';
applyTheme(savedAppearance);
// Add the event listener for system theme changes...
mediaQuery()?.addEventListener('change', handleSystemThemeChange);
}
export function useAppearance(defaultTheme: Appearance = 'system') {
const [appearance, setAppearance] = useState<Appearance>(defaultTheme);
const updateAppearance = useCallback((mode: Appearance) => {
setAppearance(mode);
// Store in localStorage for client-side persistence...
localStorage.setItem('appearance', mode);
// Store in cookie for SSR...
setCookie('appearance', mode);
applyTheme(mode);
}, []);
useEffect(() => {
const savedAppearance = localStorage.getItem('appearance') as Appearance | null;
updateAppearance(savedAppearance || defaultTheme);
return () => mediaQuery()?.removeEventListener('change', handleSystemThemeChange);
}, [defaultTheme, updateAppearance]);
return { appearance, updateAppearance } as const;
}