Skip to content

Commit a842501

Browse files
authored
Enchantment Dark Mode Toggle shows a popup (#735)
* Refactor DarkModeToggle component to include a theme selection dropdown * Refactor DarkModeToggle component to include a theme selection dropdown Fix dark mode toggle icon rendering issue
1 parent eda0dcf commit a842501

File tree

6 files changed

+109
-44
lines changed

6 files changed

+109
-44
lines changed

components/DarkModeToggle.tsx

+89-28
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,100 @@ import { useTheme } from 'next-themes';
22
import { useEffect, useState } from 'react';
33
import React from 'react';
44

5-
export default function DarkModeToggle() {
6-
const { theme, setTheme } = useTheme();
7-
const [isDarkMode, setIsDarkMode] = useState(theme === 'dark');
8-
const [isClickable, setIsClickable] = useState(true);
9-
const [img, setImg] = useState('/icons/moon.svg');
10-
11-
const toggleDarkMode = () => {
12-
if (!isClickable) return;
13-
14-
setIsClickable(false);
15-
const newTheme = isDarkMode ? 'light' : 'dark';
16-
setTheme(newTheme);
17-
setIsDarkMode(!isDarkMode);
5+
function ListItem({
6+
children,
7+
onClick,
8+
}: {
9+
children: React.ReactNode;
10+
onClick: () => void;
11+
}) {
12+
return (
13+
<div
14+
onClick={onClick}
15+
className='p-2 hover:bg-gray-200 dark:hover:bg-gray-700 cursor-pointer rounded-md transition duration-150 flex row gap-2 w-full text-sm'
16+
>
17+
{children}
18+
</div>
19+
);
20+
}
1821

19-
setTimeout(() => {
20-
setIsClickable(true);
21-
}, 500);
22-
};
22+
export default function DarkModeToggle() {
23+
const { theme, setTheme, resolvedTheme } = useTheme();
24+
const [isDarkMode, setIsDarkMode] = useState(false);
2325

2426
useEffect(() => {
25-
if (!theme) setTheme('light');
27+
setIsDarkMode(resolvedTheme === 'dark');
28+
}, [resolvedTheme]);
2629

27-
const img = theme === 'dark' ? '/icons/sun.svg' : '/icons/moon.svg';
28-
setImg(img);
29-
}, [theme, setTheme]);
30+
const [showSelect, setShowSelect] = useState(false);
31+
const [activeThemeIcon, setActiveThemeIcon] = useState('');
32+
useEffect(() => {
33+
switch (theme) {
34+
case 'system':
35+
return setActiveThemeIcon('/icons/theme-switch.svg');
36+
case 'light':
37+
return setActiveThemeIcon('/icons/sun.svg');
38+
case 'dark':
39+
return setActiveThemeIcon('/icons/moon.svg');
40+
}
41+
}, [theme, resolvedTheme]);
3042

3143
return (
32-
<button
33-
onClick={toggleDarkMode}
34-
className='dark-mode-toggle rounded-md dark:hover:bg-gray-700 p-1.5 hover:bg-gray-100 focus:bg-gray-100 focus:outline-none transition duration-150'
35-
disabled={!isClickable}
36-
>
37-
<img src={img} alt='Dark Mode' width={25} height={25} />
38-
</button>
44+
<div className='relative w-10 h-10 dark-mode-toggle-container'>
45+
<button
46+
onClick={() => setShowSelect(!showSelect)}
47+
className='dark-mode-toggle rounded-md dark:hover:bg-gray-700 p-1.5 hover:bg-gray-100 transition duration-150 '
48+
>
49+
<img
50+
src={activeThemeIcon}
51+
alt='Dark Mode'
52+
width={25}
53+
height={25}
54+
style={{
55+
// Invert the icon color based on the theme, theme of the light mode is dark
56+
filter: isDarkMode ? 'invert(1)' : 'invert(0)',
57+
}}
58+
/>
59+
</button>
60+
<div
61+
className='absolute right-0 p-2 bg-white dark:bg-gray-800 rounded-lg border dark:border-gray-700 z-10 w-max'
62+
style={{ display: showSelect ? 'block' : 'none' }}
63+
onMouseLeave={() => {
64+
setShowSelect(false);
65+
}}
66+
tabIndex={0}
67+
>
68+
<ListItem onClick={() => setTheme('system')}>
69+
<img
70+
src={'/icons/theme-switch.svg'}
71+
alt='System theme'
72+
width={18}
73+
height={18}
74+
style={{ filter: isDarkMode ? 'invert(1)' : 'invert(0)' }}
75+
/>
76+
System
77+
</ListItem>
78+
<ListItem onClick={() => setTheme('light')}>
79+
<img
80+
src={'/icons/sun.svg'}
81+
alt='System theme'
82+
width={18}
83+
height={18}
84+
style={{ filter: isDarkMode ? 'invert(1)' : 'invert(0)' }}
85+
/>
86+
Light
87+
</ListItem>
88+
<ListItem onClick={() => setTheme('dark')}>
89+
<img
90+
src={'/icons/moon.svg'}
91+
alt='System theme'
92+
width={18}
93+
height={18}
94+
style={{ filter: isDarkMode ? 'invert(1)' : 'invert(0)' }}
95+
/>
96+
Dark
97+
</ListItem>
98+
</div>
99+
</div>
39100
);
40101
}

components/Layout.tsx

+12-9
Original file line numberDiff line numberDiff line change
@@ -164,26 +164,29 @@ const MainNavigation = () => {
164164
const section = useContext(SectionContext);
165165
const showMobileNav = useStore((s: any) => s.overlayNavigation === 'docs');
166166

167-
const { theme } = useTheme();
167+
const { resolvedTheme, theme } = useTheme();
168168
const [icon, setIcon] = useState('');
169169
const [menu, setMenu] = useState('bg-black');
170170
const [closeMenu, setCLoseMenu] = useState('url("/icons/cancel.svg")');
171171

172172
useEffect(() => {
173173
const icon = theme === 'dark' ? 'herobtn' : '';
174-
const menu = theme === 'dark' ? 'bg-white' : 'bg-black';
175-
const dataTheme = theme === 'dark' ? 'dark' : 'light';
174+
const menu = resolvedTheme === 'dark' ? 'bg-white' : 'bg-black';
175+
const dataTheme = resolvedTheme === 'dark' ? 'dark' : 'light';
176176
const closeMenu =
177-
theme === 'dark'
177+
resolvedTheme === 'dark'
178178
? 'url("/icons/cancel-dark.svg")'
179179
: 'url("/icons/cancel.svg")';
180180
document.documentElement.setAttribute('data-theme', dataTheme);
181-
document.documentElement.setAttribute('class', 'keygrad keyshadow');
181+
document.documentElement.setAttribute(
182+
'class',
183+
`keygrad keyshadow ${dataTheme}`,
184+
);
182185

183186
setIcon(icon);
184187
setMenu(menu);
185188
setCLoseMenu(closeMenu);
186-
}, [theme]);
189+
}, [theme, resolvedTheme]);
187190

188191
return (
189192
<div className='flex justify-end md:mr-8 w-full '>
@@ -402,16 +405,16 @@ const Footer = () => (
402405
);
403406

404407
const Logo = () => {
405-
const { theme } = useTheme();
408+
const { resolvedTheme } = useTheme();
406409
const [imageSrc, setImageSrc] = useState('/img/logos/logo-blue.svg'); // Default to match the server-side render
407410

408411
useEffect(() => {
409412
const src =
410-
theme === 'dark'
413+
resolvedTheme === 'dark'
411414
? '/img/logos/logo-white.svg'
412415
: '/img/logos/logo-blue.svg';
413416
setImageSrc(src);
414-
}, [theme]);
417+
}, [resolvedTheme]);
415418

416419
return (
417420
<div>

components/Sidebar.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -281,15 +281,15 @@ export const DocsNav = ({
281281
const rotateR = active.getReference ? 'rotate(180deg)' : 'rotate(0)';
282282
const rotateSpec = active.getSpecification ? 'rotate(180deg)' : 'rotate(0)';
283283

284-
const { theme } = useTheme();
284+
const { resolvedTheme } = useTheme();
285285

286286
const [learn_icon, setLearn_icon] = useState('');
287287
const [reference_icon, setReference_icon] = useState('');
288288
const [spec_icon, setSpec_icon] = useState('');
289289
const [overview_icon, setOverview_icon] = useState('');
290290

291291
useEffect(() => {
292-
if (theme === 'dark') {
292+
if (resolvedTheme === 'dark') {
293293
setOverview_icon('/icons/eye-dark.svg');
294294
setLearn_icon('/icons/compass-dark.svg');
295295
setReference_icon('/icons/book-dark.svg');
@@ -300,7 +300,7 @@ export const DocsNav = ({
300300
setReference_icon('/icons/book.svg');
301301
setSpec_icon('/icons/clipboard.svg');
302302
}
303-
}, [theme]);
303+
}, [resolvedTheme]);
304304

305305
return (
306306
<div id='sidebar' className='lg:mt-8 w-4/5 mx-auto lg:ml-4'>

pages/index.page.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ export function AlgoliaSearch() {
194194
const Home = (props: any) => {
195195
const blogPosts = props.blogPosts;
196196
const timeToRead = Math.ceil(readingTime(blogPosts[0].content).minutes);
197-
const { theme } = useTheme();
197+
const { resolvedTheme } = useTheme();
198198

199199
const [asyncapi_logo, setAsyncapi_logo] = useState('');
200200
const [vpsserver_logo, setVPSserver_logo] = useState('');
@@ -207,7 +207,7 @@ const Home = (props: any) => {
207207
const [slack_logo, setSlack_logo] = useState('');
208208

209209
useEffect(() => {
210-
if (theme === 'dark') {
210+
if (resolvedTheme === 'dark') {
211211
setAsyncapi_logo('/img/logos/dark-mode/asyncapi_white.svg');
212212
setAirbnb_logo('/img/logos/dark-mode/airbnb_white.png');
213213
setPostman_logo('/img/logos/usedby/postman-white.png');
@@ -228,7 +228,7 @@ const Home = (props: any) => {
228228
setVPSserver_logo('/img/logos/sponsors/vps-server-logo.svg');
229229
setItflashcards_logo('/img/logos/sponsors/it_flashcards.svg');
230230
}
231-
}, [theme]);
231+
}, [resolvedTheme]);
232232
return (
233233
<div>
234234
<div className='flex flex-col items-center'>

public/icons/sun.svg

+1-1
Loading

public/icons/theme-switch.svg

+1
Loading

0 commit comments

Comments
 (0)