Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 30x 30x 18x 18x 18x 30x 30x 30x 1x 1x 1x 1x 1x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x | import { useAppContext } from '@/context';
import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles';
import { FC, ReactNode, useEffect } from 'react';
import {
darkPalette,
darkTheme,
lightPalette,
lightTheme,
ThemeContext,
ThemeContextType,
ThemeMode,
} from '../themes';
/**
* Props for {@link ThemeProvider}.
* @example
* ```tsx
* <ThemeProvider><App /></ThemeProvider>
* ```
* @source
*/
interface ThemeProviderProps {
/** The app subtree to render under the theme. */
children: ReactNode;
}
// `DisplaySettings.fontSize` is optional — narrow to the non-undefined union so
// Record can key off valid size names only. The fallback path in the effect
// below handles the undefined case at runtime.
type FontSize = NonNullable<DisplaySettings['fontSize']>;
const FONT_SIZE_PX: Record<FontSize, string> = {
small: '14px',
medium: '16px',
large: '18px',
};
/**
* Wraps the app in MUI's `ThemeProvider` plus a local `ThemeContext`, and
* keeps the root `<html>` font-size in sync with `userSettings.fontSize`
* (small = 14px, medium = 16px, large = 18px). Because every styled
* component in the project uses `rem`, changing the root size rescales the
* whole UI proportionally.
* @param props - Component props.
* - `children` - App tree to render under the theme.
* @returns The children wrapped in `ThemeContext.Provider` and MUI's theme.
* @example
* ```tsx
* <AppContext.Provider value={appValue}>
* <ThemeProvider>
* <App />
* </ThemeProvider>
* </AppContext.Provider>
* // Mounting with userSettings.fontSize === "large" sets
* // <html style="font-size: 18px"> and picks the light/dark theme based on
* // userSettings.theme.
* ```
* @source
*/
export const ThemeProvider: FC<ThemeProviderProps> = ({ children }) => {
const { userSettings, setUserSettings } = useAppContext();
useEffect(() => {
const size = userSettings.display?.fontSize;
document.documentElement.style.fontSize =
(size !== undefined && FONT_SIZE_PX[size]) || FONT_SIZE_PX.medium;
}, [userSettings.display?.fontSize]);
const mode: ThemeMode = userSettings.display?.theme === 'dark' ? 'dark' : 'light';
const toggleTheme = () => {
setUserSettings({
...userSettings,
display: { ...userSettings.display, theme: mode === 'light' ? 'dark' : 'light' },
});
};
const currentTheme = mode === 'light' ? lightTheme : darkTheme;
const currentPalette = mode === 'light' ? lightPalette : darkPalette;
const themeContextValue: ThemeContextType = {
mode,
toggleTheme,
currentTheme,
currentPalette,
};
return (
<ThemeContext.Provider value={themeContextValue}>
<MuiThemeProvider theme={currentTheme}>{children}</MuiThemeProvider>
</ThemeContext.Provider>
);
};
|