From 7450bc82e3dd9de4c2f55d475c95527841d2d7a4 Mon Sep 17 00:00:00 2001 From: Owen Rees Date: Fri, 16 Jun 2023 12:35:06 +0200 Subject: [PATCH] menu close on click outside --- components/Hamburger.tsx | 5 ++++- components/HamburgerMenu.tsx | 43 ++++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/components/Hamburger.tsx b/components/Hamburger.tsx index f4a4450..7af1d84 100644 --- a/components/Hamburger.tsx +++ b/components/Hamburger.tsx @@ -1,13 +1,15 @@ "use client"; import HamburgerMenu from "@/components/HamburgerMenu"; -import { useState } from "react"; +import { useRef, useState } from "react"; const Hamburger = () => { const [menuVisible, setMenuVisible] = useState(false); + const hamburgerButtonRef = useRef(null); return ( <>
setMenuVisible(!menuVisible)} > @@ -31,6 +33,7 @@ const Hamburger = () => { } diff --git a/components/HamburgerMenu.tsx b/components/HamburgerMenu.tsx index eeb5c6e..7a00c68 100644 --- a/components/HamburgerMenu.tsx +++ b/components/HamburgerMenu.tsx @@ -1,15 +1,49 @@ import Link from "next/link"; import ThemeSwitcher from "@/components/ThemeSwitcher"; -import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react"; +import { + Dispatch, + RefObject, + SetStateAction, + useEffect, + useRef, + useState, +} from "react"; -type menuState = { +type hamburgerMenuState = { menuVisible: boolean; setMenuVisible: Dispatch>; + hamburgerButtonRef: RefObject; }; -const HamburgerMenu = ({ menuVisible, setMenuVisible }: menuState) => { +const HamburgerMenu = ({ + menuVisible, + setMenuVisible, + hamburgerButtonRef, +}: hamburgerMenuState) => { const [mouseOverMenu, setMouseOverMenu] = useState(false); const timeoutRef = useRef(); + const menuRef = useRef(null); + + useEffect(() => { + const handleMouseDownOutsideMenu = (event: MouseEvent) => { + if ( + menuVisible && + menuRef.current && + !menuRef.current.contains(event.target as Node) && + hamburgerButtonRef.current && + !hamburgerButtonRef.current.contains(event.target as Node) + ) { + setMenuVisible(false); + setMouseOverMenu(false); + } + }; + + document.addEventListener("mousedown", handleMouseDownOutsideMenu); + + return () => { + document.removeEventListener("mousedown", handleMouseDownOutsideMenu); + }; + }, [menuVisible]); useEffect(() => { if (menuVisible && !mouseOverMenu) { @@ -17,7 +51,7 @@ const HamburgerMenu = ({ menuVisible, setMenuVisible }: menuState) => { } else { clearTimeout(timeoutRef.current); } - }, [mouseOverMenu]); + }, [mouseOverMenu, menuVisible]); const handleMouseEnterMenu = () => { setMouseOverMenu(true); @@ -38,6 +72,7 @@ const HamburgerMenu = ({ menuVisible, setMenuVisible }: menuState) => { return (