Add button to toggle between light- and dark-mode

Sascha created

Change summary

webui/src/components/Header/Header.tsx |  7 +++
webui/src/components/Themer.tsx        | 64 ++++++++++++++++++++++++++++
webui/src/index.tsx                    | 10 ---
webui/src/theme.ts                     |  1 
4 files changed, 73 insertions(+), 9 deletions(-)

Detailed changes

webui/src/components/Header/Header.tsx 🔗

@@ -5,6 +5,7 @@ import AppBar from '@material-ui/core/AppBar';
 import Toolbar from '@material-ui/core/Toolbar';
 import { makeStyles } from '@material-ui/core/styles';
 
+import { LightSwitch } from '../../components/Themer';
 import CurrentIdentity from '../CurrentIdentity/CurrentIdentity';
 
 const useStyles = makeStyles((theme) => ({
@@ -21,6 +22,9 @@ const useStyles = makeStyles((theme) => ({
     display: 'flex',
     alignItems: 'center',
   },
+  lightSwitch: {
+    padding: '0 20px',
+  },
   logo: {
     height: '42px',
     marginRight: theme.spacing(2),
@@ -39,6 +43,9 @@ function Header() {
             git-bug
           </Link>
           <div className={classes.filler}></div>
+          <div className={classes.lightSwitch}>
+            <LightSwitch />
+          </div>
           <CurrentIdentity />
         </Toolbar>
       </AppBar>

webui/src/components/Themer.tsx 🔗

@@ -0,0 +1,64 @@
+import React, { createContext, useCallback, useContext, useState } from 'react';
+
+import { ThemeProvider } from '@material-ui/core';
+import IconButton from '@material-ui/core/IconButton/IconButton';
+import Tooltip from '@material-ui/core/Tooltip/Tooltip';
+import { createMuiTheme, ThemeOptions } from '@material-ui/core/styles';
+import { NightsStayRounded, WbSunnyRounded } from '@material-ui/icons';
+
+const defaultTheme: ThemeOptions = {
+  palette: {
+    type: 'light',
+    primary: {
+      main: '#263238',
+    },
+  },
+};
+
+const ThemeContext = createContext({
+  toggleMode: () => {},
+  mode: '',
+});
+
+const LightSwitch = () => {
+  const { mode, toggleMode } = useContext(ThemeContext);
+
+  return (
+    <Tooltip title="Toggle Dark-/Lightmode">
+      <IconButton onClick={toggleMode} aria-label="Toggle Dark-/Lightmode">
+        {mode === 'light' ? (
+          <WbSunnyRounded color="secondary" />
+        ) : (
+          <NightsStayRounded color="secondary" />
+        )}
+      </IconButton>
+    </Tooltip>
+  );
+};
+
+type Props = { children: React.ReactNode };
+const Themer = ({ children }: Props) => {
+  const [theme, setTheme] = useState(defaultTheme);
+
+  const toggleMode = useCallback(() => {
+    const newMode = theme.palette?.type === 'dark' ? 'light' : 'dark';
+    const adjustedTheme: ThemeOptions = {
+      ...theme,
+      palette: {
+        ...theme.palette,
+        type: newMode,
+      },
+    };
+    setTheme(adjustedTheme);
+  }, [theme, setTheme]);
+
+  const newMode = theme.palette?.type === 'dark' ? 'light' : 'dark';
+
+  return (
+    <ThemeContext.Provider value={{ toggleMode: toggleMode, mode: newMode }}>
+      <ThemeProvider theme={createMuiTheme(theme)}> {children} </ThemeProvider>
+    </ThemeContext.Provider>
+  );
+};
+
+export { Themer as default, LightSwitch };

webui/src/index.tsx 🔗

@@ -1,21 +1,13 @@
 import { ApolloProvider } from '@apollo/client';
 import React from 'react';
 import ReactDOM from 'react-dom';
-import { BrowserRouter } from 'react-router-dom';
-
-import ThemeProvider from '@material-ui/styles/ThemeProvider';
 
 import App from './App';
 import apolloClient from './apollo';
-import theme from './theme';
 
 ReactDOM.render(
   <ApolloProvider client={apolloClient}>
-    <BrowserRouter>
-      <ThemeProvider theme={theme}>
-        <App />
-      </ThemeProvider>
-    </BrowserRouter>
+    <App />
   </ApolloProvider>,
   document.getElementById('root')
 );

webui/src/theme.ts 🔗

@@ -2,6 +2,7 @@ import { createMuiTheme } from '@material-ui/core/styles';
 
 const theme = createMuiTheme({
   palette: {
+    type: 'dark',
     primary: {
       main: '#263238',
     },