import { PropsWithChildren, useContext, useEffect, useMemo, useState } from 'react'; import { ThemedChildren } from './ThemedChildren'; import type { Theming } from './const'; import { DEFAULT_BASE_THEME, DEFAULT_SCREEN_MODE } from './const'; import { getThemeClassName, normalizeTheme } from './helpers'; import { ThemeContext } from './ThemeProviderContext'; export type ThemeProviderProps = PropsWithChildren & { className?: string }; // RegEx to check for `np-theme-` class name const themeClass = /\bnp-theme-[a-z-]+\b/g; export const ThemeProvider = ({ theme: initialTheme = DEFAULT_BASE_THEME, screenMode: initialScreenMode = DEFAULT_SCREEN_MODE, isNotRootProvider: isLocal = false, children, className = undefined, }: ThemeProviderProps) => { const isContextRoot = useContext(ThemeContext) === undefined; const [theme, setTheme] = useState(normalizeTheme(initialTheme)); const [screenMode, setScreenMode] = useState(initialScreenMode); // Update state when props change (for controlled usage) useEffect(() => { setTheme(normalizeTheme(initialTheme)); }, [initialTheme]); useEffect(() => { setScreenMode(initialScreenMode); }, [initialScreenMode]); // useEffect hook used to apply the theme class to the HTML element useEffect(() => { if (!isLocal && isContextRoot) { // Remove all the theme classes from the documentElement document.documentElement.className.match(themeClass)?.forEach((item) => { document.documentElement.classList.remove(item); }); getThemeClassName(theme, screenMode) .split(' ') .forEach((item) => { document.documentElement.classList.add(item); }); } }, [isLocal, isContextRoot, theme, screenMode]); const contextValue = useMemo( () => ({ theme, screenMode, setTheme, setScreenMode }), [theme, screenMode, setTheme, setScreenMode], ); return ( {children} ); };