diff --git a/components/Header/MainMenu/MobileMenu/index.tsx b/components/Header/MainMenu/MobileMenu/index.tsx index 08c7a2d11..1f2660770 100644 --- a/components/Header/MainMenu/MobileMenu/index.tsx +++ b/components/Header/MainMenu/MobileMenu/index.tsx @@ -9,6 +9,7 @@ import useDropdownStore from "@/stores/main-menu" import { GiftIcon, SearchIcon, ServiceIcon } from "@/components/Icons" import LanguageSwitcher from "@/components/LanguageSwitcher" import { useHandleKeyUp } from "@/hooks/useHandleKeyUp" +import useMediaQuery from "@/hooks/useMediaQuery" import HeaderLink from "../../HeaderLink" @@ -37,6 +38,13 @@ export default function MobileMenu({ isHeaderLanguageSwitcherMobileOpen || isFooterLanguageSwitcherOpen + const isAboveMobile = useMediaQuery("(min-width: 768px)") + useEffect(() => { + if (isAboveMobile && isHamburgerMenuOpen) { + toggleDropdown(DropdownTypeEnum.HamburgerMenu) + } + }, [isAboveMobile, isHamburgerMenuOpen, toggleDropdown]) + useHandleKeyUp((event: KeyboardEvent) => { if (event.key === "Escape" && isHamburgerMenuOpen) { toggleDropdown(DropdownTypeEnum.HamburgerMenu) diff --git a/components/Header/MainMenu/MobileMenu/mobileMenu.module.css b/components/Header/MainMenu/MobileMenu/mobileMenu.module.css index 797b3b427..0d4be7cbc 100644 --- a/components/Header/MainMenu/MobileMenu/mobileMenu.module.css +++ b/components/Header/MainMenu/MobileMenu/mobileMenu.module.css @@ -97,7 +97,8 @@ } @media screen and (min-width: 768px) { - .hamburger { + .hamburger, + .modal { display: none; } } diff --git a/components/Header/MainMenu/MyPagesMenu/index.tsx b/components/Header/MainMenu/MyPagesMenu/index.tsx index 6f01ae2ff..a99f95a89 100644 --- a/components/Header/MainMenu/MyPagesMenu/index.tsx +++ b/components/Header/MainMenu/MyPagesMenu/index.tsx @@ -1,11 +1,13 @@ "use client" +import { useRef } from "react" import { useIntl } from "react-intl" import useDropdownStore from "@/stores/main-menu" import { ChevronDownIcon } from "@/components/Icons" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" +import useClickOutside from "@/hooks/useClickOutside" import { useHandleKeyUp } from "@/hooks/useHandleKeyUp" import { getInitials } from "@/utils/user" @@ -24,6 +26,7 @@ export default function MyPagesMenu({ user, }: MyPagesMenuProps) { const intl = useIntl() + const myPagesMenuRef = useRef(null) const { toggleDropdown, isMyPagesMenuOpen } = useDropdownStore() @@ -33,8 +36,12 @@ export default function MyPagesMenu({ } }) + useClickOutside(myPagesMenuRef, isMyPagesMenuOpen, () => { + toggleDropdown(DropdownTypeEnum.MyPagesMenu) + }) + return ( -
+
toggleDropdown(DropdownTypeEnum.MyPagesMenu)} > diff --git a/components/Header/MainMenu/MyPagesMobileMenu/index.tsx b/components/Header/MainMenu/MyPagesMobileMenu/index.tsx index 2daee99e2..04dc1aa4e 100644 --- a/components/Header/MainMenu/MyPagesMobileMenu/index.tsx +++ b/components/Header/MainMenu/MyPagesMobileMenu/index.tsx @@ -7,6 +7,7 @@ import { useIntl } from "react-intl" import useDropdownStore from "@/stores/main-menu" import { useHandleKeyUp } from "@/hooks/useHandleKeyUp" +import useMediaQuery from "@/hooks/useMediaQuery" import { getInitials } from "@/utils/user" import Avatar from "../Avatar" @@ -32,6 +33,13 @@ export default function MyPagesMobileMenu({ } }) + const isAboveMobile = useMediaQuery("(min-width: 768px)") + useEffect(() => { + if (isAboveMobile && isMyPagesMobileMenuOpen) { + toggleDropdown(DropdownTypeEnum.MyPagesMobileMenu) + } + }, [isAboveMobile, isMyPagesMobileMenuOpen, toggleDropdown]) + // Making sure the menu is always opened at the top of the page, just below the header. useEffect(() => { if (isMyPagesMobileMenuOpen) { diff --git a/components/Header/MainMenu/NavigationMenu/NavigationMenuItem/index.tsx b/components/Header/MainMenu/NavigationMenu/NavigationMenuItem/index.tsx index 222eb21e9..c57064b2a 100644 --- a/components/Header/MainMenu/NavigationMenu/NavigationMenuItem/index.tsx +++ b/components/Header/MainMenu/NavigationMenu/NavigationMenuItem/index.tsx @@ -1,9 +1,12 @@ "use client" +import { useRef } from "react" + import useDropdownStore from "@/stores/main-menu" import { ChevronDownIcon, ChevronRightIcon } from "@/components/Icons" import Link from "@/components/TempDesignSystem/Link" +import useClickOutside from "@/hooks/useClickOutside" import { useHandleKeyUp } from "@/hooks/useHandleKeyUp" import MainMenuButton from "../../MainMenuButton" @@ -15,8 +18,10 @@ import type { NavigationMenuItemProps } from "@/types/components/header/navigati export default function MenuItem({ item, isMobile }: NavigationMenuItemProps) { const { openMegaMenu, toggleMegaMenu } = useDropdownStore() + const megaMenuRef = useRef(null) const { submenu, title, link, seeAllLink, card } = item - const isMegaMenuOpen = openMegaMenu === title + const megaMenuTitle = `${title}-${isMobile ? "mobile" : "desktop"}` + const isMegaMenuOpen = openMegaMenu === megaMenuTitle useHandleKeyUp((event: KeyboardEvent) => { if (event.key === "Escape" && isMegaMenuOpen) { @@ -24,10 +29,14 @@ export default function MenuItem({ item, isMobile }: NavigationMenuItemProps) { } }) + useClickOutside(megaMenuRef, isMegaMenuOpen && !isMobile, () => { + toggleMegaMenu(false) + }) + return submenu.length ? ( <> toggleMegaMenu(title)} + onClick={() => toggleMegaMenu(megaMenuTitle)} className={`${styles.navigationMenuItem} ${isMobile ? styles.mobile : styles.desktop}`} > {title} @@ -41,6 +50,7 @@ export default function MenuItem({ item, isMobile }: NavigationMenuItemProps) { )}
{isMegaMenuOpen ? ( diff --git a/components/LanguageSwitcher/index.tsx b/components/LanguageSwitcher/index.tsx index b5cb2f25e..7ed5b4d75 100644 --- a/components/LanguageSwitcher/index.tsx +++ b/components/LanguageSwitcher/index.tsx @@ -1,12 +1,13 @@ "use client" -import { useEffect, useRef } from "react" +import { useRef } from "react" import { useIntl } from "react-intl" import { languages } from "@/constants/languages" import useDropdownStore from "@/stores/main-menu" import { ChevronDownIcon, GlobeIcon } from "@/components/Icons" +import useClickOutside from "@/hooks/useClickOutside" import { useHandleKeyUp } from "@/hooks/useHandleKeyUp" import useLang from "@/hooks/useLang" @@ -28,18 +29,13 @@ export default function LanguageSwitcher({ }: LanguageSwitcherProps) { const intl = useIntl() const currentLanguage = useLang() - const toggleDropdown = useDropdownStore((state) => state.toggleDropdown) + const { + toggleDropdown, + isFooterLanguageSwitcherOpen, + isHeaderLanguageSwitcherMobileOpen, + isHeaderLanguageSwitcherOpen, + } = useDropdownStore() const languageSwitcherRef = useRef(null) - const isFooterLanguageSwitcherOpen = useDropdownStore( - (state) => state.isFooterLanguageSwitcherOpen - ) - const isHeaderLanguageSwitcherOpen = useDropdownStore( - (state) => state.isHeaderLanguageSwitcherOpen - ) - const isHeaderLanguageSwitcherMobileOpen = useDropdownStore( - (state) => state.isHeaderLanguageSwitcherMobileOpen - ) - const isFooter = type === LanguageSwitcherTypesEnum.Footer const isHeader = !isFooter @@ -71,33 +67,11 @@ export default function LanguageSwitcher({ window.scrollTo(0, scrollPosition) }) } - - useEffect(() => { - function handleClickOutside(evt: Event) { - const target = evt.target as HTMLElement - if ( - languageSwitcherRef.current && - target && - !languageSwitcherRef.current.contains(target) && - isLanguageSwitcherOpen && - !isHeaderLanguageSwitcherMobileOpen - ) { - toggleDropdown(dropdownType) - } - } - - if (languageSwitcherRef.current) { - document.addEventListener("click", handleClickOutside) - } - return () => { - document.removeEventListener("click", handleClickOutside) - } - }, [ - dropdownType, - toggleDropdown, - isLanguageSwitcherOpen, - isHeaderLanguageSwitcherMobileOpen, - ]) + useClickOutside( + languageSwitcherRef, + isLanguageSwitcherOpen && !isHeaderLanguageSwitcherMobileOpen, + () => toggleDropdown(dropdownType) + ) const classNames = languageSwitcherVariants({ color, position }) diff --git a/hooks/useClickOutside.ts b/hooks/useClickOutside.ts new file mode 100644 index 000000000..b9862c059 --- /dev/null +++ b/hooks/useClickOutside.ts @@ -0,0 +1,24 @@ +import { useEffect } from "react" + +export default function useClickOutside( + ref: React.RefObject, + isOpen: boolean, + callback: () => void +) { + useEffect(() => { + function handleClickOutside(evt: Event) { + const target = evt.target as HTMLElement + if (ref.current && target && !ref.current.contains(target) && isOpen) { + callback() + } + } + + if (isOpen) { + document.addEventListener("click", handleClickOutside) + } + + return () => { + document.removeEventListener("click", handleClickOutside) + } + }, [ref, isOpen, callback]) +} diff --git a/hooks/useMediaQuery.ts b/hooks/useMediaQuery.ts new file mode 100644 index 000000000..bbb9eabac --- /dev/null +++ b/hooks/useMediaQuery.ts @@ -0,0 +1,21 @@ +import { useEffect, useState } from "react" + +function useMediaQuery(query: string) { + const [isMatch, setIsMatch] = useState(false) + + useEffect(() => { + const media = window.matchMedia(query) + if (media.matches !== isMatch) { + setIsMatch(media.matches) + } + + const listener = () => setIsMatch(media.matches) + media.addEventListener("change", listener) + + return () => media.removeEventListener("change", listener) + }, [isMatch, query]) + + return isMatch +} + +export default useMediaQuery