Skip to content

Commit d1dcc46

Browse files
Implement click outside of menus to close
1 parent 4a4c76c commit d1dcc46

File tree

2 files changed

+36
-8
lines changed

2 files changed

+36
-8
lines changed

src/app/page.js

+9-8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import IconButton from "@/components/IconButton";
2424
import ImageCanvas from "@/components/ImageCanvas";
2525
import UploadArea from "@/components/UploadArea";
2626
import AboutDropDownMenu from "@/components/AboutDropDownMenu";
27+
import useClickOutside from "@/hooks/ClickOutsideHook";
2728

2829

2930
const aboutMenuItemsConst = [
@@ -52,13 +53,13 @@ export default function Home() {
5253

5354
const [darkMode, setDarkMode] = useState(true);
5455

55-
const [aboutMenuOpen, setAboutMenuOpen] = useState(false);
56+
const [aboutMenuRef, aboutMenuOpen, setAboutMenuOpen] = useClickOutside();
5657

5758
const [gridMode, setGridMode] = useState(0);
5859
const [imageFilters, setImageFilters] = useState([true, false, false, false]);
5960

60-
const [gridModeMenuOpen, setGridModeMenuOpen] = useState(false);
61-
const [imageFiltersMenuOpen, setImageFiltersMenuOpen] = useState(false);
61+
const [gridModeMenuRef, gridModeMenuOpen, setGridModeMenuOpen] = useClickOutside();
62+
const [imageFiltersMenuRef, imageFiltersMenuOpen, setImageFiltersMenuOpen] = useClickOutside();
6263

6364
const [file, setFile] = useState(null);
6465
const [fileIsValid, setFileIsValid] = useState(null);
@@ -117,7 +118,7 @@ export default function Home() {
117118
<div ref={fullscreenRef} className={`flex flex-col overflow-hidden ${darkMode == true ? "dark bg-background" : "bg-foreground/10"}`}>
118119
<nav className={`flex z-50 flex-row justify-between items-center px-5 py-3 ${isFullscreen ? "absolute top-1 left-1 w-full rounded-lg opacity-0 transition-opacity duration-300 bg-foreground/40 hover:opacity-100" : "bg-foreground"}`}>
119120
<div className="flex relative justify-center items-center">
120-
<div onClick={() => setAboutMenuOpen(!aboutMenuOpen)} className="inline-flex items-center space-x-2 group hover:cursor-pointer">
121+
<div ref={aboutMenuRef} tabIndex={0} className="inline-flex items-center space-x-2 group hover:cursor-pointer">
121122
<Image
122123
className={`h-min ${isFullscreen ? "opacity-70" : ""}`}
123124
src="/logo-icon.png"
@@ -132,13 +133,13 @@ export default function Home() {
132133
</div>
133134

134135
<div className="flex flex-row space-x-4">
135-
<div className="flex relative">
136-
<IconDropDownButton Icon={ShadowInnerIcon} active={imageFiltersMenuOpen} onClick={() => setImageFiltersMenuOpen(!imageFiltersMenuOpen)}>
136+
<div className="flex relative" ref={imageFiltersMenuRef} tabIndex={0}>
137+
<IconDropDownButton Icon={ShadowInnerIcon} active={imageFiltersMenuOpen}>
137138
</IconDropDownButton>
138139
{imageFiltersMenuOpen && <ImageFiltersDropDownMenu items={imageFiltersConst} selected={imageFilters} onClick={handleSetImageFilters}></ImageFiltersDropDownMenu>}
139140
</div>
140-
<div className="flex relative">
141-
<IconDropDownButton Icon={gridModesConst[gridMode].icon} active={gridModeMenuOpen} onClick={() => setGridModeMenuOpen(!gridModeMenuOpen)}>
141+
<div className="flex relative" ref={gridModeMenuRef} tabIndex={0} >
142+
<IconDropDownButton Icon={gridModesConst[gridMode].icon} active={gridModeMenuOpen}>
142143
</IconDropDownButton>
143144
{gridModeMenuOpen && <DropDownMenu items={gridModesConst} selected={gridMode} onClick={handleSetGridMode}></DropDownMenu>}
144145
</div>

src/hooks/ClickOutsideHook.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useRef, useState, useEffect } from "react";
2+
3+
// Logic from https://stackoverflow.com/a/62681847
4+
const useClickOutside = () => {
5+
const buttonRef = useRef(null);
6+
const [isOpen, setIsOpen] = useState(false);
7+
8+
useEffect(() => {
9+
const handleClick = (ev) => {
10+
if (buttonRef.current && buttonRef.current.contains(ev.target)) {
11+
setIsOpen(!isOpen);
12+
} else {
13+
setIsOpen(false);
14+
}
15+
};
16+
17+
window.addEventListener("click", handleClick);
18+
19+
return () => {
20+
window.removeEventListener("click", handleClick);
21+
};
22+
}, []);
23+
24+
return [buttonRef, isOpen, setIsOpen];
25+
}
26+
27+
export default useClickOutside;

0 commit comments

Comments
 (0)