Skip to content

Commit

Permalink
Get rid of flash of erroneously styled content
Browse files Browse the repository at this point in the history
Using MUI's experimental `useColorScheme`. Fixes #1.
  • Loading branch information
pharmpy-dev-123 committed Oct 17, 2022
1 parent fe698fb commit 17180e8
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 91 deletions.
5 changes: 5 additions & 0 deletions gatsby-ssr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {getInitColorSchemeScript} from '@mui/material/styles';

export function onRenderBody({setPreBodyComponents}) {
setPreBodyComponents([getInitColorSchemeScript()]);
}
13 changes: 8 additions & 5 deletions src/layouts/default.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import '@fontsource/roboto/700.css';

import type {PropsOf} from '@emotion/react/types/helper';

import {ThemeProvider} from '@mui/material/styles';
import {
ThemeProvider,
Experimental_CssVarsProvider as CssVarsProvider,
} from '@mui/material/styles';
// eslint-disable-next-line import/no-unassigned-import
import type {} from '@mui/material/themeCssVarsAugmentation';
import CssBaseline from '@mui/material/CssBaseline';

import ModeProvider from '../ui/theme/ModeProvider';

import useTheme from '../ui/useTheme';
import Header from '../ui/Header';
import Main from '../ui/Main';
Expand All @@ -33,9 +36,9 @@ function Layout({path, ...rest}: LayoutProps) {

function App(props: LayoutProps) {
return (
<ModeProvider>
<CssVarsProvider defaultMode="system">
<Layout {...props} />
</ModeProvider>
</CssVarsProvider>
);
}

Expand Down
70 changes: 55 additions & 15 deletions src/ui/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

import {useTheme} from '@mui/material/styles';
import {useTheme, useColorScheme} from '@mui/material/styles';

import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
Expand All @@ -9,37 +9,77 @@ import IconButton from '@mui/material/IconButton';
import Box from '@mui/material/Box';

import EditIcon from '@mui/icons-material/Edit';
import CircleIcon from '@mui/icons-material/Circle';
import LightModeIcon from '@mui/icons-material/LightMode';
import DarkModeIcon from '@mui/icons-material/DarkMode';
import SystemModeIcon from '@mui/icons-material/Brightness4';

import useIsMounted from './lib/component/useIsMounted';
import Breadcrumbs from './navigation/BreadCrumbs';
import useMode from './theme/useMode';

type HeaderProps = {
path: string;
};

type Mode = 'system' | 'light' | 'dark';

type ModeMapOptions<T> = {
system: T;
light: T;
dark: T;
};

function modeMap<T>(mode: Mode, options: ModeMapOptions<T>): T {
return options[mode];
}

function ModeSwitch() {
const isMounted = useIsMounted();
const {mode, setMode} = useColorScheme();

let onClick;
let ariaLabel;
let Icon = CircleIcon;
if (mode !== undefined && isMounted()) {
const nextMode = modeMap<Mode>(mode, {
system: 'light',
light: 'dark',
dark: 'system',
});
onClick = () => {
setMode(nextMode);
};

ariaLabel = `switch to ${nextMode} mode`;
Icon = modeMap(mode, {
system: SystemModeIcon,
light: LightModeIcon,
dark: DarkModeIcon,
});
}

return (
<IconButton
size="large"
disabled={!onClick}
aria-label={ariaLabel}
color="inherit"
onClick={onClick}
>
<Icon />
</IconButton>
);
}

function Header({path}: HeaderProps) {
const {
palette: {mode},
} = useTheme();
const [, setMode] = useMode();
return (
<AppBar position="static">
<Toolbar>
<Breadcrumbs path={path} />
<Box sx={{flexGrow: 1}} />
<Box sx={{display: {xs: 'none', md: 'flex'}}}>
<IconButton
size="large"
aria-label={`switch to ${mode === 'dark' ? 'light' : 'dark'} mode`}
color="inherit"
onClick={() => {
setMode(mode === 'dark' ? 'light' : 'dark');
}}
>
{mode === 'dark' ? <LightModeIcon /> : <DarkModeIcon />}
</IconButton>
<ModeSwitch />

<IconButton
size="large"
Expand Down
11 changes: 11 additions & 0 deletions src/ui/lib/component/useForceUpdate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {useCallback, useState} from 'react';

const useForceUpdate = () => {
// eslint-disable-next-line react/hook-use-state
const [, updateState] = useState<Record<string, unknown>>();
return useCallback(() => {
updateState({});
}, []);
};

export default useForceUpdate;
25 changes: 25 additions & 0 deletions src/ui/lib/component/useIsMounted.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {useRef, useEffect, useState} from 'react';
import useForceUpdate from './useForceUpdate';

/**
* See https://gist.github.com/jaydenseric/a67cfb1b809b1b789daa17dfe6f83daa
*
* Do not use to avoid warning when calling setState on an unmounted component.
* See https://github.com/facebook/react/pull/22114
*/
const useIsMounted = () => {
const componentIsMounted = useRef(false);
const forceUpdate = useForceUpdate();

useEffect(() => {
componentIsMounted.current = true;
forceUpdate();
return () => {
componentIsMounted.current = false;
};
}, [forceUpdate]);

return () => componentIsMounted.current;
};

export default useIsMounted;
7 changes: 3 additions & 4 deletions src/ui/lib/text/HighlightGrammar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import r from 'react-syntax-highlighter/dist/esm/languages/prism/r';
import bash from 'react-syntax-highlighter/dist/esm/languages/prism/bash';
import dark from 'react-syntax-highlighter/dist/esm/styles/prism/material-dark';
import light from 'react-syntax-highlighter/dist/esm/styles/prism/material-light';
import {useTheme} from '@mui/material/styles';

import useMode from '../../theme/useMode';
import saveTextToClipboard from '../output/saveTextToClipboard';

SyntaxHighlighter.registerLanguage('python', python);
Expand Down Expand Up @@ -60,9 +61,7 @@ const customStyle = {margin: 0, display: 'flex', flex: '1'};
type Style = typeof dark | typeof light;

function HighlightGrammar({language, word}: Props) {
const {
palette: {mode},
} = useTheme();
const mode = useMode();
const style: Style = mode === 'dark' ? dark : light;
const [tooltipText, setTooltipText] = useState<State>(init);
const [open, setOpen] = useState<boolean>(false);
Expand Down
19 changes: 0 additions & 19 deletions src/ui/theme/ModeContext.ts

This file was deleted.

27 changes: 0 additions & 27 deletions src/ui/theme/ModeProvider.tsx

This file was deleted.

7 changes: 5 additions & 2 deletions src/ui/theme/useMode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {useContext} from 'react';
import ModeContext from './ModeContext';
import {useColorScheme} from '@mui/material/styles';

const useMode = () => useContext(ModeContext);
const useMode = () => {
const {mode: colorSchemeMode, systemMode} = useColorScheme();
return colorSchemeMode === 'system' ? systemMode : colorSchemeMode;
};

export default useMode;
7 changes: 0 additions & 7 deletions src/ui/theme/useNavigatorMode.ts

This file was deleted.

13 changes: 1 addition & 12 deletions src/ui/useTheme.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
import {useMemo} from 'react';
import {createTheme} from '@mui/material/styles';

import useMode from './theme/useMode';

const useTheme = () => {
const [mode] = useMode();
return useMemo(
() =>
createTheme({
palette: {
mode,
},
}),
[mode],
);
return useMemo(() => createTheme(), []);
};

export default useTheme;

0 comments on commit 17180e8

Please sign in to comment.