Skip to content

Commit

Permalink
feat: dark mode (#11)
Browse files Browse the repository at this point in the history
* chore: add @mui/system

* feat: add ThemeSwitcher

* refactor: remove unneeded export modifier

* refactor: refactor theme state management codes

* chore: add jotai

* refactor: rework theme data management system

* style: change track color

* chore: change tailwind darkmode management mode to manual

* feat: add theme selector

theme-dark in style.css is WIP

* refactor: remove redundant settings

* feat: add prism dark theme

* feat: implement dark theme colors

* docs: update readme
  • Loading branch information
turtton authored Feb 10, 2023
1 parent 1bcd6c0 commit cbd1cce
Show file tree
Hide file tree
Showing 14 changed files with 306 additions and 21 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ Volglass documentation is build my self!!

https://volglass.turtton.net

# Features

- **Search** documents
- **Dark** Theme

Also, I fixed many issues that the original has.

## Getting started
### Run on your local machine

Expand All @@ -25,7 +32,6 @@ See [volglass-docs repository](https://github.com/turtton/volglass-docs) for an
## Future development

- Embed markdown/image rendering
- Text search
- Tag list view

## For developers
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@mui/icons-material": "latest",
"@mui/lab": "latest",
"@mui/material": "latest",
"@mui/system": "^5.11.8",
"@types/wanakana": "^4.0.3",
"cytoscape-d3-force": "^1.1.4",
"cytoscape-node-html-label": "^1.2.1",
Expand All @@ -28,6 +29,7 @@
"fs": "^0.0.1-security",
"fuse.js": "^6.6.2",
"gray-matter": "^4.0.2",
"jotai": "^2.0.1",
"jsnetworkx": "^0.3.4",
"mdast-util-to-string": "^3.1.0",
"next": "^12.1.0",
Expand Down
16 changes: 16 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/components/FolderTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function FolderTree(props: {
flattenNodes: MdObject[];
}): JSX.Element {
const renderTree = (nodes: MdObject): JSX.Element => (
<TCTreeItem key={nodes.id} nodeId={nodes.id} label={nodes.name}>
<TCTreeItem key={nodes.id} nodeId={nodes.id} label={nodes.name} className="dark:text-gray-200">
{nodes.children.map((node) => renderTree(node))}
</TCTreeItem>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";

export default function Footer(): JSX.Element {
return (
<footer className="absolute top-auto bottom-0 w-full bg-gray-50 text-center">
<footer className="brand absolute top-auto bottom-0 w-full bg-gray-50 text-center">
Powered by <a href="https://github.com/turtton/volglass">Volglass</a>, © 2023
</footer>
);
Expand Down
21 changes: 11 additions & 10 deletions src/components/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import CytoscapeComponent from "react-cytoscapejs";
import { Core } from "cytoscape";
import { LocalGraphData } from "../lib/graph";
import { MdObject } from "../lib/markdown";
import { useCurrentTheme } from "./ThemeSwitcher";
import { PaletteMode } from "@mui/material";

const layout = {
name: "circle",
Expand All @@ -32,7 +34,8 @@ const layout = {
}, // transform a given node position. Useful for changing flow direction in discrete layouts
};

const styleSheet = [
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const styleSheet = (theme: PaletteMode) => [
{
selector: "node",
style: {
Expand All @@ -45,7 +48,10 @@ const styleSheet = [
},
{
selector: "label",
style: { "font-size": "12px" },
style: {
"font-size": "12px",
color: theme === "dark" ? "#e1e2e6" : "#000",
},
},
{
selector: "edge",
Expand All @@ -68,18 +74,13 @@ function Graph({ graph }: { graph: LocalGraphData }): JSX.Element {
elementSetter(CytoscapeComponent.normalizeElements(graph));
}, [graph]);

const theme = useCurrentTheme();
const router = useRouter();
return (
<>
<div className="right-bar-container">
<h3>Interactive Graph</h3>
<div
style={{
border: "1px solid #ddd",
backgroundColor: "#f5f6fe",
borderRadius: "8px",
}}
>
<div className="rounded-lg border-2 border-solid border-gray-300 bg-white dark:border-gray-600 dark:bg-dark-primary-alt ">
<CytoscapeComponent
elements={elements}
// pan={{ x: 200, y: 200 }}
Expand All @@ -90,7 +91,7 @@ function Graph({ graph }: { graph: LocalGraphData }): JSX.Element {
autounselectify={false}
boxSelectionEnabled={true}
layout={layout}
stylesheet={styleSheet}
stylesheet={styleSheet(theme)}
cy={(cy) => {
// console.log("EVT", cy);
cy.layout(layout).run();
Expand Down
22 changes: 21 additions & 1 deletion src/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
import { useCurrentTheme } from "./ThemeSwitcher";
import { useMemo } from "react";
import { createTheme, ThemeProvider } from "@mui/material/styles";
import { purple } from "@mui/material/colors";

export default function Layout({ children }): JSX.Element {
const currentTheme = useCurrentTheme();
const muiTheme = useMemo(
() =>
createTheme({
palette: {
mode: currentTheme,
primary: {
main: purple[500],
},
},
}),
[currentTheme],
);
return (
<div className="fixed grid h-full min-h-full w-full grid-cols-1 grid-rows-1 flex-row overflow-hidden">
<main className="theme-light">{children}</main>
<ThemeProvider theme={muiTheme}>
<main className={currentTheme === "dark" ? "theme-dark" : "theme-light"}>{children}</main>
</ThemeProvider>
</div>
);
}
6 changes: 6 additions & 0 deletions src/components/RootContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ interface HomeElement extends HTMLElement {
checked: boolean;
}

const DynamicThemeSwitcher = dynamic(async () => await import("./ThemeSwitcher"), {
loading: () => <p>Loading...</p>,
ssr: false,
});

// This trick is to dynamically load component that interact with window object (browser only)
const DynamicGraph = dynamic(async () => await import("./Graph"), {
loading: () => <p>Loading ...</p>,
Expand Down Expand Up @@ -46,6 +51,7 @@ export default function RootContainer({
</div>
<div>
<nav className="nav-bar">
<DynamicThemeSwitcher />
<SearchBar index={searchIndex} />
<FolderTree tree={tree} flattenNodes={flattenNodes} />
</nav>
Expand Down
1 change: 0 additions & 1 deletion src/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export function SearchBar(prop: SearchProp): JSX.Element {
}}
>
<TextField
className="bg-white"
fullWidth
label="Search"
onFocus={() => {
Expand Down
92 changes: 92 additions & 0 deletions src/components/ThemeSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { styled } from "@mui/material/styles";
import { FormControlLabel, PaletteMode, Switch } from "@mui/material";
import { useEffect } from "react";
import { atom, useAtomValue, useSetAtom } from "jotai";
import { atomWithStorage } from "jotai/utils";

export default function ThemeSwitcher(): JSX.Element {
const currentMode = useCurrentTheme();
const setCurrentTheme = useSetAtom(themeAtom);
useEffect(() => {
if (currentMode === "dark") {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
}, [currentMode]);
return (
<FormControlLabel
className="w-fit px-2"
control={
<ThemeSwitch
sx={{ m: 1 }}
checked={currentMode === "dark"}
onChange={(e) => {
const newMode = e.target.checked ? "dark" : "light";
setCurrentTheme(newMode);
}}
/>
}
label=""
/>
);
}

const ThemeSwitch = styled(Switch)(({ theme }) => ({
padding: 8,
"& .MuiSwitch-switchBase": {
"&.Mui-checked": {
color: "#fff",
},
"& + .MuiSwitch-track": {
opacity: 1,
backgroundColor: theme.palette.mode === "dark" ? "#2a2b2b" : "#dedfe1",
},
},
"& .MuiSwitch-track": {
borderRadius: 22 / 2,
backgroundColor: theme.palette.mode === "dark" ? "#383E44" : "#aab4be",
"&:before, &:after": {
content: '""',
position: "absolute",
top: "50%",
transform: "translateY(-50%)",
width: 16,
height: 16,
},
"&:before": {
// moon
backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 24 24"><path fill="${encodeURIComponent(
"#fff",
)}" d="M4.2 4l-1.9-.7-.6-1.8zm15 8.3a6.7 6.7 0 11-6.6-6.6 5.8 5.8 0 006.6 6.6z"/></svg>')`,
left: 12,
},
"&:after": {
// sun
backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 24 24"><path fill="${encodeURIComponent(
"#000",
)}" d="M9.305 1.667V3.75h1.389V1.667h-1.39zm-4.707 1.95l-.982.982L5.09 6.072l.982-.982-1.473-1.473zm10.802 0L13.927 5.09l.982.982 1.473-1.473-.982-.982zM10 5.139a4.872 4.872 0 00-4.862 4.86A4.872 4.872 0 0010 14.862 4.872 4.872 0 0014.86 10 4.872 4.872 0 0010 5.139zm0 1.389A3.462 3.462 0 0113.471 10a3.462 3.462 0 01-3.473 3.472A3.462 3.462 0 016.527 10 3.462 3.462 0 0110 6.528zM1.665 9.305v1.39h2.083v-1.39H1.666zm14.583 0v1.39h2.084v-1.39h-2.084zM5.09 13.928L3.616 15.4l.982.982 1.473-1.473-.982-.982zm9.82 0l-.982.982 1.473 1.473.982-.982-1.473-1.473zM9.305 16.25v2.083h1.389V16.25h-1.39z"/></svg>')`,
right: 12,
},
},
"& .MuiSwitch-thumb": {
boxShadow: "none",
width: 16,
height: 16,
margin: 2,
backgroundColor: theme.palette.mode === "dark" ? "#F5F2F0" : "#fff",
},
}));

const themeKey = "theme";
const themeAtom = atomWithStorage<PaletteMode | null>(themeKey, null);

const currentThemeAtom = atom((get) => {
const currentTheme = get(themeAtom);
if (currentTheme !== null) {
return currentTheme;
}
return "light";
});

export const useCurrentTheme = (): PaletteMode => useAtomValue(currentThemeAtom);
1 change: 1 addition & 0 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "../styles/global.css";
import "../styles/style.css";
import "../styles/prism.css";
import "../styles/prism-dark.css";
import { AppProps } from "next/app";

export default function App({ Component, pageProps }: AppProps): JSX.Element {
Expand Down
Loading

0 comments on commit cbd1cce

Please sign in to comment.