Simplify burger handling

This commit is contained in:
Timothy Armes
2023-10-11 08:36:45 +02:00
committed by Owen Rees
parent effb912641
commit 94cea267c9
7 changed files with 23 additions and 130 deletions
+10 -14
View File
@@ -1,48 +1,44 @@
"use client"; "use client";
import { useRef, useState } from "react"; import { useAtom } from "jotai";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { burgerMenuIsOpenAtom } from "@/app/atoms";
import HamburgerMenu from "./HamburgerMenu"; import HamburgerMenu from "./HamburgerMenu";
const Hamburger = () => { const Hamburger = () => {
const [menuVisible, setMenuVisible] = useState(false); const [burgerMenuIsOpen, setBurgerMenuIsOpen] = useAtom(burgerMenuIsOpenAtom);
const hamburgerButtonRef = useRef<HTMLDivElement>(null);
return ( return (
<> <>
<div <div
ref={hamburgerButtonRef}
className="hamburger-button relative z-[99999] space-y-2" className="hamburger-button relative z-[99999] space-y-2"
data-test="hamburger-button" data-test="hamburger-button"
onClick={() => setMenuVisible(!menuVisible)} onClick={() => setBurgerMenuIsOpen(!burgerMenuIsOpen)}
> >
<div <div
className={twMerge( className={twMerge(
"h-0.5 w-8 bg-gray-600 transition-all duration-300 ease-in-out dark:bg-white", "h-0.5 w-8 bg-gray-600 transition-all duration-300 ease-in-out dark:bg-white",
menuVisible && burgerMenuIsOpen &&
"translate-x-[1px] translate-y-2.5 rotate-45 bg-white", "translate-x-[1px] translate-y-2.5 rotate-45 bg-white",
)} )}
></div> ></div>
<div <div
className={twMerge( className={twMerge(
"h-0.5 w-8 bg-gray-600 transition-transform duration-300 ease-linear dark:bg-white", "h-0.5 w-8 bg-gray-600 transition-transform duration-300 ease-linear dark:bg-white",
menuVisible && "scale-0 bg-white opacity-0", burgerMenuIsOpen && "scale-0 bg-white opacity-0",
)} )}
></div> ></div>
<div <div
className={twMerge( className={twMerge(
"h-0.5 w-8 bg-gray-600 transition-transform duration-300 ease-in-out dark:bg-white", "h-0.5 w-8 bg-gray-600 transition-transform duration-300 ease-in-out dark:bg-white",
menuVisible && "-translate-y-2.5 -rotate-45 bg-white", burgerMenuIsOpen && "-translate-y-2.5 -rotate-45 bg-white",
)} )}
></div> ></div>
</div> </div>
<HamburgerMenu <HamburgerMenu />
menuVisible={menuVisible}
setMenuVisible={setMenuVisible}
hamburgerButtonRef={hamburgerButtonRef}
/>
</> </>
); );
}; };
+10 -47
View File
@@ -1,69 +1,30 @@
import { Dispatch, RefObject, SetStateAction, useRef, useState } from "react"; import { Dispatch, RefObject, SetStateAction, useRef, useState } from "react";
import { useAtom } from "jotai";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import useHamburgerClose from "@/hooks/useHamburgerClose"; import { burgerMenuIsOpenAtom } from "@/app/atoms";
import { Link } from "@/utils/navigation"; import { Link } from "@/utils/navigation";
interface HamburgerMenuState { const HamburgerMenu = () => {
menuVisible: boolean;
setMenuVisible: Dispatch<SetStateAction<boolean>>;
hamburgerButtonRef: RefObject<HTMLDivElement>;
}
const HamburgerMenu = ({
menuVisible,
setMenuVisible,
hamburgerButtonRef,
}: HamburgerMenuState) => {
const t = useTranslations("Nav"); const t = useTranslations("Nav");
const [mouseOverMenu, setMouseOverMenu] = useState(false); const [burgerMenuIsOpen, setBurgerMenuIsOpen] = useAtom(burgerMenuIsOpenAtom);
const timeoutRef = useRef<NodeJS.Timeout | undefined>();
const menuRef = useRef<HTMLDivElement>(null);
const menuTimeout = () => { const closeMenu = () => setBurgerMenuIsOpen(false);
timeoutRef.current = setTimeout(() => {
setMenuVisible(false);
setMouseOverMenu(false);
}, 4000);
};
const handleMouseEnterMenu = () => {
setMouseOverMenu(true);
clearTimeout(timeoutRef.current);
};
const handleMouseLeaveMenu = () => {
setMouseOverMenu(false);
clearTimeout(timeoutRef.current);
};
useHamburgerClose({
menuVisible,
setMenuVisible,
menuRef,
hamburgerButtonRef,
mouseOverMenu,
setMouseOverMenu,
timeoutRef,
menuTimeout,
});
return ( return (
<div <div
ref={menuRef}
className={twMerge( className={twMerge(
"fixed right-0 top-0 z-[9999] h-[calc(100svh-5rem)] w-full", "fixed right-0 top-0 z-[9999] h-[calc(100svh-3rem)] w-full",
"flex items-center justify-center bg-primary-600 transition-transform duration-200 ease-linear dark:bg-gray-600 md:hidden", "flex items-center justify-center bg-primary-600 transition-transform duration-200 ease-linear dark:bg-gray-600 md:hidden",
!menuVisible && "translate-x-full", !burgerMenuIsOpen && "translate-x-full",
)} )}
onMouseEnter={handleMouseEnterMenu}
onMouseLeave={handleMouseLeaveMenu}
> >
<ul className="list-reset text-white"> <ul className="list-reset text-white">
<li className="py-5 text-center text-xl"> <li className="py-5 text-center text-xl">
<Link <Link
onClick={closeMenu}
href="/tournaments" href="/tournaments"
className="border-b-2 border-transparent transition-all duration-300 ease-in-out hover:border-white" className="border-b-2 border-transparent transition-all duration-300 ease-in-out hover:border-white"
> >
@@ -73,6 +34,7 @@ const HamburgerMenu = ({
<li className="py-5 text-center text-xl"> <li className="py-5 text-center text-xl">
<Link <Link
onClick={closeMenu}
href="/clubs" href="/clubs"
className="border-b-2 border-transparent transition-all duration-300 ease-in-out hover:border-white" className="border-b-2 border-transparent transition-all duration-300 ease-in-out hover:border-white"
> >
@@ -82,6 +44,7 @@ const HamburgerMenu = ({
<li className="py-5 text-center text-xl"> <li className="py-5 text-center text-xl">
<Link <Link
onClick={closeMenu}
href="/elo" href="/elo"
className="border-b-2 border-transparent transition-all duration-300 ease-in-out hover:border-white" className="border-b-2 border-transparent transition-all duration-300 ease-in-out hover:border-white"
> >
+1
View File
@@ -5,6 +5,7 @@ import { Club, TimeControl, Tournament } from "@/types";
import atomWithDebounce from "@/utils/atomWithDebounce"; import atomWithDebounce from "@/utils/atomWithDebounce";
import { normalizedContains } from "@/utils/string"; import { normalizedContains } from "@/utils/string";
export const burgerMenuIsOpenAtom = atom(false);
export const mapBoundsAtom = atom<LatLngBounds | null>(null); export const mapBoundsAtom = atom<LatLngBounds | null>(null);
export const syncVisibleAtom = atom(true); export const syncVisibleAtom = atom(true);
export const searchStringAtom = atom(""); export const searchStringAtom = atom("");
+1 -1
View File
@@ -220,7 +220,7 @@ export const Map = ({
return ( return (
<section id="tournament-map" className="flex h-content flex-col"> <section id="tournament-map" className="flex h-content flex-col">
<div className="p-3 lg:hidden">{filters}</div> {filters && <div className="p-3 lg:hidden">{filters}</div>}
<MapContainer <MapContainer
center={center} center={center}
+1 -1
View File
@@ -4,9 +4,9 @@ import { useEffect, useRef, useState } from "react";
import { FaArrowUp } from "react-icons/fa"; import { FaArrowUp } from "react-icons/fa";
import { handleScrollToTop } from "@/handlers/scrollHandlers";
import { useBreakpoint } from "@/hooks/tailwind"; import { useBreakpoint } from "@/hooks/tailwind";
import { ScrollableElement } from "@/types"; import { ScrollableElement } from "@/types";
import { handleScrollToTop } from "@/utils/scrollHandlers";
const ScrollToTopButton = () => { const ScrollToTopButton = () => {
const scrollToTopElementRef = useRef<ScrollableElement | null>(null); const scrollToTopElementRef = useRef<ScrollableElement | null>(null);
-67
View File
@@ -1,67 +0,0 @@
import { Dispatch, RefObject, SetStateAction, useEffect } from "react";
interface HamburgerClose {
menuVisible: boolean;
setMenuVisible: Dispatch<SetStateAction<boolean>>;
menuRef: RefObject<HTMLDivElement>;
hamburgerButtonRef: RefObject<HTMLDivElement>;
mouseOverMenu: boolean;
setMouseOverMenu: Dispatch<SetStateAction<boolean>>;
timeoutRef: RefObject<NodeJS.Timeout | undefined>;
menuTimeout: () => void;
}
const useHamburgerClose = ({
menuVisible,
setMenuVisible,
menuRef,
hamburgerButtonRef,
mouseOverMenu,
setMouseOverMenu,
timeoutRef,
menuTimeout,
}: HamburgerClose) => {
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);
}
};
const handleLinkClick = () => {
setMenuVisible(false);
};
document.addEventListener("mousedown", handleMouseDownOutsideMenu);
const links = document.querySelectorAll("a");
links.forEach((link) => {
link.addEventListener("click", handleLinkClick);
});
return () => {
document.removeEventListener("mousedown", handleMouseDownOutsideMenu);
links.forEach((link) => {
link.removeEventListener("click", handleLinkClick);
});
};
}, [menuVisible]);
useEffect(() => {
if (menuVisible && !mouseOverMenu) {
menuTimeout();
} else {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
}
}, [mouseOverMenu, menuVisible]);
};
export default useHamburgerClose;