prmbr-image-mksaas/src/components/layout/active-theme-provider.tsx

80 lines
2.0 KiB
TypeScript

'use client';
import { websiteConfig } from '@/config/website';
import {
type ReactNode,
createContext,
useContext,
useEffect,
useState,
} from 'react';
const COOKIE_NAME = 'active_theme';
const DEFAULT_THEME = websiteConfig.ui.theme?.defaultTheme ?? 'default';
function setThemeCookie(theme: string) {
if (typeof window === 'undefined') return;
document.cookie = `${COOKIE_NAME}=${theme}; path=/; max-age=31536000; SameSite=Lax; ${
window.location.protocol === 'https:' ? 'Secure;' : ''
}`;
}
type ThemeContextType = {
activeTheme: string;
setActiveTheme: (theme: string) => void;
};
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
/**
* This component is used to provide the active theme to the application
* It also sets the theme cookie and updates the body class when the theme changes.
*
* NOTICE: Since custom theme is set in useEffect,
* it will not be applied until the component is mounted,
* for better user experience, we recommend to replace the
* default theme with the custom theme in global.css.
*
* docs:
* https://mksaas.com/docs/themes
*/
export function ActiveThemeProvider({
children,
initialTheme,
}: {
children: ReactNode;
initialTheme?: string;
}) {
const [activeTheme, setActiveTheme] = useState<string>(
() => initialTheme || DEFAULT_THEME
);
useEffect(() => {
setThemeCookie(activeTheme);
Array.from(document.body.classList)
.filter((className) => className.startsWith('theme-'))
.forEach((className) => {
document.body.classList.remove(className);
});
document.body.classList.add(`theme-${activeTheme}`);
}, [activeTheme]);
return (
<ThemeContext.Provider value={{ activeTheme, setActiveTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useThemeConfig() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error(
'useThemeConfig must be used within an ActiveThemeProvider'
);
}
return context;
}