diff --git a/frontend/public/images/icons/dark-mode.svg b/frontend/public/images/icons/dark-mode.svg new file mode 100644 index 0000000..6de1aee --- /dev/null +++ b/frontend/public/images/icons/dark-mode.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/images/icons/light-mode.svg b/frontend/public/images/icons/light-mode.svg new file mode 100644 index 0000000..93058fd --- /dev/null +++ b/frontend/public/images/icons/light-mode.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index ffdb2a4..f4b056d 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -1,16 +1,56 @@ +import { useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; import { Menu } from "./Menu"; export const Layout = ({ children }: { children: React.ReactNode }) => { const { pathname } = useLocation(); + const [isDark, setIsDark] = useState(false); + + useEffect(() => { + // set the initial state of the theme based on the user's preference + if ( + window.matchMedia && + window.matchMedia("(prefers-color-scheme: dark)").matches && + localStorage.getItem("theme") !== "light" + ) { + setIsDark(true); + } + + const handleColorSchemeChange = (event: MediaQueryListEvent) => { + // if there is a theme set in local storage, don't change the theme + if ( + localStorage.getItem("theme") === "light" || + localStorage.getItem("theme") === "dark" + ) + return; + + // else set the theme based on the event change + if (event.matches) { + setIsDark(true); + } else { + setIsDark(false); + } + }; + + window + .matchMedia("(prefers-color-scheme: dark)") + .addEventListener("change", handleColorSchemeChange); + + // Clean up the event listener + return () => { + window + .matchMedia("(prefers-color-scheme: dark)") + .removeEventListener("change", handleColorSchemeChange); + }; + }, []); return ( - <> - +
+ setIsDark(dark)} />
{
- +
); }; diff --git a/frontend/src/components/Menu.tsx b/frontend/src/components/Menu.tsx index 721f2ef..be5c111 100644 --- a/frontend/src/components/Menu.tsx +++ b/frontend/src/components/Menu.tsx @@ -11,16 +11,27 @@ const defaultTools = [ { name: "HWT Path Profiler", url: "https://heywhatsthat.com/profiler.html" }, ]; -export const Menu = () => { +export const Menu = ({ + isDark, + onDarkChange, +}: { + isDark: boolean; + onDarkChange: (dark: boolean) => void; +}) => { const { data: config } = useGetConfigQuery(); const [showMenu, setShowMenu] = useState(false); + const handleDarkChange = (dark: boolean) => { + onDarkChange(dark); + localStorage.setItem("theme", dark ? "dark" : "light"); + }; + return ( <>
-
+
{config?.mesh?.name?.split(" ").map((word, index) => ( @@ -72,6 +85,15 @@ export const Menu = () => {
+ +
diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts index 5fef7c2..32dd6c4 100644 --- a/frontend/tailwind.config.ts +++ b/frontend/tailwind.config.ts @@ -9,4 +9,5 @@ export default { corePlugins: { preflight: true, }, + darkMode: "class", } satisfies Config; diff --git a/templates/static/layout.html.j2 b/templates/static/layout.html.j2 index 3efb10e..da1a6d0 100644 --- a/templates/static/layout.html.j2 +++ b/templates/static/layout.html.j2 @@ -37,6 +37,10 @@
{% endif %} +
+ Try the new UI! +
+