Skip to content

Commit 823d83a

Browse files
committed
add dark mode toggle (#7)
Co-authored-by: lukqw <[email protected]>
1 parent 3f608fe commit 823d83a

File tree

6 files changed

+114
-28
lines changed

6 files changed

+114
-28
lines changed

packages/frontend/src/Routes.tsx

+23-14
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,29 @@ import ShowNote from "./content/ShowNote";
77
import NotFound from "./content/NotFound";
88

99
import { BASE_URL } from "./config";
10+
import { useThemeContext } from "./hooks/useThemeContext";
1011

11-
const Routes = () => (
12-
<div className="mt-md-4 d-flex flex-column justify-content-center">
13-
<Suspense fallback={<div>Loading...</div>}>
14-
<BrowserRouter basename={BASE_URL}>
15-
<RouterRoutes>
16-
<Route path="/" element={<ListNotes/>} />
17-
<Route path="/note/new" element={<CreateNote/>} />
18-
<Route path="/notes/:noteId" element={<ShowNote/>} />
19-
<Route element={<NotFound/>} />
20-
</RouterRoutes>
21-
</BrowserRouter>
22-
</Suspense>
23-
</div>
24-
);
12+
const Routes = () => {
13+
const { darkMode } = useThemeContext();
14+
15+
return (
16+
<div className={`${darkMode ? 'bg-dark' : ''}`} style={{ height: "100%", overflowY: 'hidden', minHeight: '100vh' }}>
17+
<div className="container" >
18+
<div className="mt-md-4 d-flex flex-column justify-content-center">
19+
<Suspense fallback={<div>Loading...</div>}>
20+
<BrowserRouter basename={BASE_URL}>
21+
<RouterRoutes>
22+
<Route path="/" element={<ListNotes/>} />
23+
<Route path="/note/new" element={<CreateNote/>} />
24+
<Route path="/notes/:noteId" element={<ShowNote/>} />
25+
<Route element={<NotFound/>} />
26+
</RouterRoutes>
27+
</BrowserRouter>
28+
</Suspense>
29+
</div>
30+
</div>
31+
</div>
32+
);
33+
}
2534

2635
export { Routes };
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
import React from "react";
22
import { Card } from "react-bootstrap";
3+
import { ThemeSwitch } from "../content/ThemeSwitch";
34

45
const PageContainer = (props: {
56
header: React.ReactNode;
67
children: React.ReactNode;
7-
}) => (
8-
<Card>
9-
<Card.Header>{props.header}</Card.Header>
10-
<Card.Body>{props.children}</Card.Body>
11-
</Card>
12-
);
8+
}) => {
9+
return (
10+
<>
11+
<Card>
12+
<Card.Header>{props.header}</Card.Header>
13+
<Card.Body>{props.children}</Card.Body>
14+
</Card>
15+
<ThemeSwitch />
16+
</>
17+
)
18+
};
1319

1420
export { PageContainer };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React, { useState } from "react";
2+
import { useThemeContext } from "../hooks/useThemeContext";
3+
import { Form } from "react-bootstrap";
4+
5+
6+
export const ThemeSwitch = () => {
7+
const { darkMode, setDarkMode } = useThemeContext();
8+
9+
const toggleTheme = () => {
10+
setDarkMode(!darkMode);
11+
};
12+
13+
return (
14+
<Form style={{paddingTop: '1rem', display: 'flex', justifyContent: 'flex-end'}}>
15+
<Form.Check // prettier-ignore
16+
type="switch"
17+
onClick={toggleTheme}
18+
id="custom-switch"
19+
defaultChecked={darkMode}
20+
style={{color: darkMode ? 'white' : ''}}
21+
label="Dark Mode"
22+
/>
23+
</Form>
24+
);
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { useEffect, useState } from "react";
2+
3+
// to store the state to local storage
4+
export default function usePersistedState(key: string, defaultValue: any) {
5+
const [state, setState] = useState(() => {
6+
return JSON.parse(localStorage.getItem(key) ?? '{}') || defaultValue;
7+
});
8+
9+
useEffect(() => {
10+
localStorage.setItem(key, JSON.stringify(state));
11+
}, [key, state]);
12+
13+
return [state, setState];
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from "react";
2+
import { createContext, useContext, ReactElement } from "react";
3+
import usePersistedState from "./usePersistedState";
4+
5+
type ThemeContextValue = {
6+
darkMode: boolean;
7+
setDarkMode: React.Dispatch<React.SetStateAction<boolean>>;
8+
};
9+
10+
// register the context
11+
const ThemeContext = createContext<ThemeContextValue>(undefined as any);
12+
13+
type Props = {
14+
children: React.ReactNode;
15+
};
16+
17+
export const ThemeProvider = ({ children }: Props): ReactElement => {
18+
/** usePersistedState for storing state in local store */
19+
const [darkMode, setDarkMode] = usePersistedState("darkmode", false);
20+
21+
return (
22+
<ThemeContext.Provider value={{ darkMode, setDarkMode }}>
23+
{children}
24+
</ThemeContext.Provider>
25+
);
26+
}
27+
28+
// export a custom hook to use this specific context
29+
export const useThemeContext = (): ThemeContextValue => {
30+
return useContext(ThemeContext);
31+
}
32+

packages/frontend/src/index.tsx

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { Buffer } from "buffer";
22
import process from "process";
33
import React from "react";
4-
import { createRoot } from "react-dom/client";
4+
import ReactDOM from "react-dom";
55
import { Routes } from "./Routes";
6+
import { ThemeProvider } from "./hooks/useThemeContext";
67

78
// Polyfills required for MicrophoneStream
89
if (typeof (window as any).global === "undefined") {
@@ -15,11 +16,10 @@ if (typeof (window as any).process === "undefined") {
1516
(window as any).process = process;
1617
}
1718

18-
const container = document.getElementById("root");
19-
// @ts-ignore
20-
const root = createRoot(container);
21-
root.render(
22-
<div className="container" style={{ height: "100vh" }}>
23-
<Routes />
24-
</div>
19+
20+
ReactDOM.render(
21+
<ThemeProvider>
22+
<Routes />
23+
</ThemeProvider>,
24+
document.getElementById("root")
2525
);

0 commit comments

Comments
 (0)