Skip to content

Commit 3c97d9e

Browse files
Add color mode switch for light and dark mode (#5)
* feat(): add color mode switch for light and dark mode * feat(): place tooltip below icon
1 parent 4a3e666 commit 3c97d9e

File tree

5 files changed

+135
-45
lines changed

5 files changed

+135
-45
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { DBSwitch, DBTooltip } from "@db-ux/react-core-components";
2+
import { useColorMode } from "@template/context/color-mode-context.tsx";
3+
4+
const ColorModeSwitch = () => {
5+
const { colorMode, toggleColorMode } = useColorMode();
6+
const isDark = colorMode === "dark";
7+
8+
return (
9+
<DBSwitch
10+
checked={isDark}
11+
visualAid
12+
icon="sun"
13+
iconTrailing="moon"
14+
showLabel={false}
15+
onChange={toggleColorMode}>
16+
<DBTooltip>
17+
Switch color scheme (light/dark)
18+
</DBTooltip>
19+
Switch color scheme (light/dark)
20+
</DBSwitch>
21+
);
22+
};
23+
24+
export default ColorModeSwitch;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import {
2+
createContext,
3+
useContext,
4+
useState,
5+
useEffect,
6+
type ReactNode,
7+
} from "react";
8+
9+
type ColorMode = "light" | "dark";
10+
11+
interface ColorModeContextValue {
12+
colorMode: ColorMode;
13+
setColorMode: (colorMode: ColorMode) => void;
14+
toggleColorMode: () => void;
15+
}
16+
17+
const STORAGE_KEY = "db-ux-mode";
18+
const SHELL_SELECTOR = ".db-shell";
19+
20+
const ColorModeContext = createContext<ColorModeContextValue | undefined>(undefined);
21+
22+
function getInitialColorMode(): ColorMode {
23+
if (typeof window === "undefined") {
24+
return "light";
25+
}
26+
27+
const stored = window.localStorage.getItem(STORAGE_KEY);
28+
if (stored === "light" || stored === "dark") {
29+
return stored;
30+
}
31+
32+
return "light";
33+
}
34+
35+
export const ColorModeProvider = ({ children }: { children: ReactNode }) => {
36+
const [colorMode, setColorMode] = useState<ColorMode>(getInitialColorMode);
37+
38+
useEffect(() => {
39+
window.localStorage.setItem("db-ux-mode", colorMode);
40+
41+
const shell = document.querySelector(SHELL_SELECTOR);
42+
if (shell instanceof HTMLElement) {
43+
shell.setAttribute("data-mode", colorMode);
44+
}
45+
}, [colorMode]);
46+
47+
const setMode = (next: ColorMode) => {
48+
setColorMode(next);
49+
};
50+
51+
const toggleColorMode = () => {
52+
setColorMode((prev) => (prev === "light" ? "dark" : "light"));
53+
};
54+
55+
const value: ColorModeContextValue = { colorMode, setColorMode: setMode, toggleColorMode };
56+
57+
return <ColorModeContext.Provider value={value}>{children}</ColorModeContext.Provider>;
58+
};
59+
60+
export const useColorMode = (): ColorModeContextValue => {
61+
const ctx = useContext(ColorModeContext);
62+
if (!ctx) {
63+
throw new Error("useColorMode must be used inside a ColorModeProvider");
64+
}
65+
return ctx;
66+
};
Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,47 @@
11
import { type PropsWithChildren, type ReactElement } from "react";
2-
3-
import {
4-
DBControlPanelDesktop,
5-
DBControlPanelMobile,
6-
DBShell,
7-
} from "@db-ux/react-core-components";
2+
import { ColorModeProvider } from "@template/context/color-mode-context.tsx";
3+
import { DBControlPanelDesktop, DBControlPanelMobile, DBShell } from "@db-ux/react-core-components";
84
import PrimaryActions from "@template/layouts/default/shell/control-panel/primary-actions.tsx";
95
import MainNavigation from "@template/layouts/default/shell/control-panel/main-navigation.tsx";
106
import SubNavigation from "@template/layouts/default/shell/control-panel/sub-navigation.tsx";
117
import Brand from "@template/layouts/default/shell/control-panel/brand.tsx";
128
import { findSubNavigation } from "@template/utils/navigation.utils.ts";
139

1410
export function Shell({ children }: PropsWithChildren): ReactElement {
15-
const subNavigation = findSubNavigation(window.location.pathname);
11+
const subNavigation = findSubNavigation(window.location.pathname);
1612

17-
/*
18-
* TODO: We need to get the subNavigation if we are inside a subNavigation Item as well
19-
* */
13+
/*
14+
* TODO: We need to get the subNavigation if we are inside a subNavigation Item as well
15+
* */
2016

21-
return (
22-
<DBShell
23-
fadeIn
24-
subNavigationDesktopPosition="left"
25-
subNavigation={
26-
subNavigation ? <SubNavigation navigationItems={subNavigation} /> : null
27-
}
28-
subNavigationMobilePosition="none"
29-
controlPanelDesktop={
30-
<DBControlPanelDesktop
31-
brand={<Brand />}
32-
primaryActions={<PrimaryActions />}
33-
>
34-
<MainNavigation />
35-
</DBControlPanelDesktop>
36-
}
37-
controlPanelMobile={
38-
<DBControlPanelMobile
39-
brand={<Brand />}
40-
primaryActions={<PrimaryActions />}
41-
>
42-
<MainNavigation mobile />
43-
</DBControlPanelMobile>
44-
}
45-
>
46-
{children}
47-
</DBShell>
48-
);
17+
return (
18+
<ColorModeProvider>
19+
<DBShell
20+
fadeIn
21+
subNavigationDesktopPosition="left"
22+
subNavigation={
23+
subNavigation ? <SubNavigation navigationItems={subNavigation} /> : null
24+
}
25+
subNavigationMobilePosition="none"
26+
controlPanelDesktop={
27+
<DBControlPanelDesktop
28+
brand={<Brand />}
29+
primaryActions={<PrimaryActions />}
30+
>
31+
<MainNavigation />
32+
</DBControlPanelDesktop>
33+
}
34+
controlPanelMobile={
35+
<DBControlPanelMobile
36+
brand={<Brand />}
37+
primaryActions={<PrimaryActions />}
38+
>
39+
<MainNavigation mobile />
40+
</DBControlPanelMobile>
41+
}
42+
>
43+
{children}
44+
</DBShell>
45+
</ColorModeProvider>
46+
);
4947
}
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { DBControlPanelPrimaryActions } from "@db-ux/react-core-components";
22
import { appConfig } from "@root/app.config.ts";
33
import { Search } from "@template/layouts/default/shell/search";
4+
import ColorModeSwitch from "@template/components/color-mode-switch/ColorModeSwitch.tsx";
45

56
const PrimaryActions = () => (
6-
<DBControlPanelPrimaryActions>
7-
{ <Search /> }
8-
<a className="db-button" href={`${appConfig.basePath}resources/documentation/getting-started`} data-variant="brand">
9-
Start now
10-
</a>
11-
</DBControlPanelPrimaryActions>
7+
<DBControlPanelPrimaryActions>
8+
<ColorModeSwitch />
9+
<Search />
10+
<a className="db-button" href={`${appConfig.basePath}resources/documentation/getting-started`} data-variant="brand">
11+
Start now
12+
</a>
13+
</DBControlPanelPrimaryActions>
1214
);
1315

1416
export default PrimaryActions;

template/layouts/default/shell/search/Search.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export function Search(): ReactElement {
7373
onClick={() => setSearchOpen(true)}
7474
>
7575
Open Search
76-
<DBTooltip placement="left">Open Search</DBTooltip>
76+
<DBTooltip>Open Search</DBTooltip>
7777
</DBButton>
7878
<DBDrawer open={searchOpen} onClose={() => setSearchOpen(false)}>
7979
<div className="dba-search-result-container">

0 commit comments

Comments
 (0)