From 818b31f2ac0218a3b61cdd7853991ea7f29261db Mon Sep 17 00:00:00 2001 From: Fran McDade Date: Tue, 20 Aug 2024 13:34:19 +1000 Subject: [PATCH] fix: fix header navigation active nested menu highlight (#167) (#169) * fix: fix header navigation active nested menu highlight (#167) * feat: update selected match util (#167) --------- Co-authored-by: Fran McDade --- .../components/Header/common/entities.ts | 7 +- .../Layout/components/Header/common/utils.ts | 121 ++++++++++++++++-- .../Actions/components/Menu/menu.tsx | 4 +- .../components/Navigation/common/utils.ts | 26 ++-- .../NavigationDrawer/navigationDrawer.tsx | 27 ++-- .../NavigationMenu/navigationMenu.tsx | 8 +- .../navigationMenuItems.tsx | 8 +- .../components/Navigation/navigation.tsx | 17 ++- .../components/Header/header.stories.tsx | 1 - .../Layout/components/Header/header.tsx | 32 ++--- .../Header/hooks/useHeaderNavigation.ts | 19 +++ .../Header/hooks/useHeaderVisibility.ts | 6 +- .../components/Header}/hooks/useMenu.ts | 4 +- .../common/Dialog/hooks/useDialog.ts | 28 ++++ .../common/Menu/hooks/useMenu.ts} | 8 +- 15 files changed, 223 insertions(+), 93 deletions(-) create mode 100644 src/components/Layout/components/Header/hooks/useHeaderNavigation.ts rename src/{ => components/Layout/components/Header}/hooks/useMenu.ts (91%) create mode 100644 src/components/common/Dialog/hooks/useDialog.ts rename src/{hooks/useMenuWithPosition.ts => components/common/Menu/hooks/useMenu.ts} (87%) diff --git a/src/components/Layout/components/Header/common/entities.ts b/src/components/Layout/components/Header/common/entities.ts index 0db64c11..809924c8 100644 --- a/src/components/Layout/components/Header/common/entities.ts +++ b/src/components/Layout/components/Header/common/entities.ts @@ -1,4 +1,4 @@ -import { ReactNode } from "react"; +import { BreakpointKey } from "../../../../../hooks/useBreakpointHelper"; import { Social } from "../../../../common/Socials/socials"; import { NavLinkItem } from "../components/Content/components/Navigation/navigation"; @@ -8,12 +8,15 @@ export type Navigation = [ NavLinkItem[] | undefined ]; // [LEFT, CENTER, RIGHT] +export type SelectedMatch = + | SELECTED_MATCH + | Partial>; + export enum SELECTED_MATCH { EQUALS = "EQUALS", STARTS_WITH = "STARTS_WITH", // Default value. } export interface SocialMedia { - label: ReactNode; socials: Social[]; } diff --git a/src/components/Layout/components/Header/common/utils.ts b/src/components/Layout/components/Header/common/utils.ts index 7013a043..b3c52c1a 100644 --- a/src/components/Layout/components/Header/common/utils.ts +++ b/src/components/Layout/components/Header/common/utils.ts @@ -1,24 +1,45 @@ import { Breakpoint } from "@mui/material"; +import { isClientSideNavigation } from "../../../../Links/common/utils"; import { NavLinkItem } from "../components/Content/components/Navigation/navigation"; -import { Navigation } from "./entities"; +import { Navigation, SelectedMatch, SELECTED_MATCH } from "./entities"; /** - * Returns the configured menu navigation links, for the current breakpoint. + * Adds to the set of selected patterns, for the navigation link, at the current breakpoint. + * @param setOfPatterns - Set of selected patterns. + * @param navLinkItem - Navigation link. + * @param breakpoint - Breakpoint. + */ +function addSelectedPattern( + setOfPatterns: Set, + navLinkItem: NavLinkItem, + breakpoint?: Breakpoint +): void { + if (!navLinkItem.url) return; + // Exclude external links. + if (!isClientSideNavigation(navLinkItem.url)) return; + // Get the configured selected match for the current breakpoint. + const selectedMatch = getSelectedMatch(navLinkItem.selectedMatch, breakpoint); + if (!selectedMatch) return; + // Add the selected pattern for the navigation link. + if (selectedMatch === SELECTED_MATCH.EQUALS) { + setOfPatterns.add(getPatternEquals(navLinkItem.url)); + return; + } + setOfPatterns.add(getPatternStartsWith(navLinkItem.url)); +} + +/** + * Returns the configured menu navigation links. * @param navigation - Navigation links. - * @param breakpoint - Current breakpoint. * @returns navigation links. */ -export function getMenuNavigationLinks( - navigation?: Navigation, - breakpoint?: Breakpoint -): NavLinkItem[] { +export function getMenuNavigationLinks(navigation?: Navigation): NavLinkItem[] { if (!navigation) return []; - const navLinkItems = navigation.reduce((acc: NavLinkItem[], navLinkItems) => { + return navigation.reduce((acc: NavLinkItem[], navLinkItems) => { if (!navLinkItems) return acc; acc.push(...navLinkItems); return acc; }, []); - return getNavigationLinks(navLinkItems, breakpoint); } /** @@ -32,16 +53,62 @@ export function getNavigationLinks( breakpoint?: Breakpoint ): NavLinkItem[] { if (!navigationLinks) return []; - return navigationLinks.reduce( - (acc: NavLinkItem[], navLinkItem: NavLinkItem) => { + return navigationLinks + .map((navigationLink) => mapSelectedMatches(navigationLink, breakpoint)) + .reduce((acc: NavLinkItem[], navLinkItem: NavLinkItem) => { const processedNavLink = processNavLinkItem(navLinkItem, breakpoint); if (processedNavLink) { acc.push(...processedNavLink); } return acc; - }, - [] - ); + }, []); +} + +/** + * Returns the pattern for an exact match, for the given URL e.g. "^/about$". + * @param url - URL. + * @returns pattern for an exact match. + */ +function getPatternEquals(url: string): string { + return `^${url}$`; +} + +/** + * Returns the pattern for a match that starts with the given URL e.g. "^/about". + * @param url - URL. + * @returns pattern for a match that starts with the given URL. + */ +function getPatternStartsWith(url: string): string { + return `^${url}`; +} + +/** + * Returns the configured selected match. + * @param selectedMatch - Selected match. + * @param breakpoint - Breakpoint. + * @returns selected match. + */ +function getSelectedMatch( + selectedMatch?: SelectedMatch, + breakpoint?: Breakpoint +): SELECTED_MATCH | undefined { + if (!selectedMatch) return SELECTED_MATCH.STARTS_WITH; + if (typeof selectedMatch === "string") return selectedMatch; + if (!breakpoint) return; + return getSelectMatchValue(selectedMatch[breakpoint]); +} + +/** + * Returns the selected match value, for the current breakpoint. + * @param selectedMatchValue - Selected match value. + * @returns selected match. + */ +function getSelectMatchValue( + selectedMatchValue?: boolean | SELECTED_MATCH +): SELECTED_MATCH | undefined { + if (selectedMatchValue === false) return undefined; + if (selectedMatchValue === true) return SELECTED_MATCH.STARTS_WITH; + return selectedMatchValue || SELECTED_MATCH.STARTS_WITH; } /** @@ -74,6 +141,32 @@ function isLinkVisible( return navLinkItem.visible[breakpoint] !== false; } +/** + * Returns the navigation link with the selected matches, for the current breakpoint. + * @param navLinkItem - Navigation link. + * @param breakpoint - Breakpoint. + * @returns navigation link with the selected matches. + */ +function mapSelectedMatches( + navLinkItem: NavLinkItem, + breakpoint?: Breakpoint +): NavLinkItem { + const setOfPatterns = new Set(); + // Add selected pattern for the current navigation link. + addSelectedPattern(setOfPatterns, navLinkItem, breakpoint); + const cloneLink = { ...navLinkItem }; + if (cloneLink.menuItems) { + cloneLink.menuItems = [...cloneLink.menuItems].map((menuItem) => + mapSelectedMatches(menuItem, breakpoint) + ); + for (const { selectedPatterns = [] } of cloneLink.menuItems) { + selectedPatterns.forEach((pattern) => setOfPatterns.add(pattern)); + } + } + cloneLink.selectedPatterns = [...setOfPatterns]; + return cloneLink; +} + /** * Returns the processed navigation link item. * Flattens menu items, and removes items that are not visible for the current breakpoint. diff --git a/src/components/Layout/components/Header/components/Content/components/Actions/components/Menu/menu.tsx b/src/components/Layout/components/Header/components/Content/components/Actions/components/Menu/menu.tsx index ac74ca6b..8ec90e99 100644 --- a/src/components/Layout/components/Header/components/Content/components/Actions/components/Menu/menu.tsx +++ b/src/components/Layout/components/Header/components/Content/components/Actions/components/Menu/menu.tsx @@ -26,7 +26,7 @@ export const Menu = forwardRef( ref ): JSX.Element | null { const { navigation, slogan, socialMedia } = headerProps; - const { breakpoint, smDown } = useBreakpoint(); + const { smDown } = useBreakpoint(); // Set drawer open state to false on change of media breakpoint from small desktop "md" and up. useEffect(() => { @@ -61,7 +61,7 @@ export const Menu = forwardRef( {socialMedia && ( diff --git a/src/components/Layout/components/Header/components/Content/components/Navigation/common/utils.ts b/src/components/Layout/components/Header/components/Content/components/Navigation/common/utils.ts index fbbb4a48..f8a88d62 100644 --- a/src/components/Layout/components/Header/components/Content/components/Navigation/common/utils.ts +++ b/src/components/Layout/components/Header/components/Content/components/Navigation/common/utils.ts @@ -1,27 +1,19 @@ -import { SELECTED_MATCH } from "../../../../../common/entities"; - /** * Returns true if the navigation link is selected. - * @param url - The URL of the navigation link. + * The pathname is matched against the selected patterns. * @param pathname - The current pathname. - * @param selectedMatch - The selected match type. + * @param selectedPatterns - Selected match patterns. * @returns true if the navigation link is selected. */ export function isNavigationLinkSelected( - url: string, pathname?: string, - selectedMatch: SELECTED_MATCH = SELECTED_MATCH.STARTS_WITH + selectedPatterns?: string[] ): boolean { if (!pathname) return false; - if (isSelectedMatchEqual(selectedMatch)) return url === pathname; - return pathname.startsWith(url); -} - -/** - * Returns true if the selected match type is "EQUAL". - * @param selectedMatch - The selected match type. - * @returns True if the selected match type is "EQUAL". - */ -export function isSelectedMatchEqual(selectedMatch: SELECTED_MATCH): boolean { - return selectedMatch === SELECTED_MATCH.EQUALS; + for (const selectedPattern of selectedPatterns ?? []) { + if (new RegExp(selectedPattern).test(pathname)) { + return true; + } + } + return false; } diff --git a/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationDrawer/navigationDrawer.tsx b/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationDrawer/navigationDrawer.tsx index d9a1c8bc..4f9637a7 100644 --- a/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationDrawer/navigationDrawer.tsx +++ b/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationDrawer/navigationDrawer.tsx @@ -1,7 +1,8 @@ import ArrowDropDownRoundedIcon from "@mui/icons-material/ArrowDropDownRounded"; -import React, { ReactNode, useState } from "react"; +import React, { ReactNode, useCallback } from "react"; import { Button } from "../../../../../../../../../common/Button/button"; import { BackArrowIcon } from "../../../../../../../../../common/CustomIcon/components/BackArrowIcon/backArrowIcon"; +import { useDialog } from "../../../../../../../../../common/Dialog/hooks/useDialog"; import { HeaderProps } from "../../../../../../header"; import { AppBar } from "../../../../../../header.styles"; import { DrawerNavigation as Navigation } from "../../../Actions/components/Menu/components/Content/components/Navigation/navigation.styles"; @@ -17,6 +18,7 @@ import { export interface NavigationDrawerProps { closeAncestor?: () => void; headerProps?: HeaderProps; + isSelected?: boolean; menuItems: MenuItem[]; menuLabel: ReactNode; pathname?: string; @@ -25,27 +27,22 @@ export interface NavigationDrawerProps { export const NavigationDrawer = ({ closeAncestor, headerProps, + isSelected = false, menuItems, menuLabel, pathname, }: NavigationDrawerProps): JSX.Element => { - const [drawerOpen, setDrawerOpen] = useState(false); - const openDrawer = (): void => { - setDrawerOpen(true); - }; - const closeDrawer = (): void => { - setDrawerOpen(false); - }; - const closeDrawers = (): void => { - setDrawerOpen(false); + const { onClose, onOpen, open } = useDialog(); + const closeDrawers = useCallback((): void => { + onClose(); closeAncestor?.(); - }; + }, [closeAncestor, onClose]); return ( <> @@ -55,7 +52,7 @@ export const NavigationDrawer = ({ hideBackdrop keepMounted={false} onClose={closeDrawers} - open={drawerOpen} + open={open} PaperProps={{ elevation: 0 }} TransitionComponent={Slide} transitionDuration={300} @@ -66,7 +63,7 @@ export const NavigationDrawer = ({ diff --git a/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenu/navigationMenu.tsx b/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenu/navigationMenu.tsx index 67b1c9ac..6142cbb7 100644 --- a/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenu/navigationMenu.tsx +++ b/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenu/navigationMenu.tsx @@ -2,7 +2,7 @@ import ArrowDropDownRoundedIcon from "@mui/icons-material/ArrowDropDownRounded"; import { MenuProps as MMenuProps } from "@mui/material"; import React, { Fragment, ReactNode } from "react"; import { useBreakpoint } from "../../../../../../../../../../hooks/useBreakpoint"; -import { useMenuWithPosition } from "../../../../../../../../../../hooks/useMenuWithPosition"; +import { useMenu } from "../../../../../../../../../common/Menu/hooks/useMenu"; import { NavigationButtonLabel } from "../NavigationButtonLabel/navigationButtonLabel"; import { MenuItem, @@ -15,6 +15,7 @@ export interface NavLinkMenuProps { anchorOrigin?: MMenuProps["anchorOrigin"]; closeAncestor?: () => void; disablePortal?: boolean; + isSelected?: boolean; menuItems: MenuItem[]; menuLabel: ReactNode; pathname?: string; @@ -24,12 +25,13 @@ export const NavigationMenu = ({ anchorOrigin = MENU_ANCHOR_ORIGIN_LEFT_BOTTOM, closeAncestor, disablePortal, + isSelected = false, menuItems, menuLabel, pathname, }: NavLinkMenuProps): JSX.Element => { const { mdUp } = useBreakpoint(); - const { anchorEl, onClose, onToggleOpen, open } = useMenuWithPosition(); + const { anchorEl, onClose, onToggleOpen, open } = useMenu(); const MenuItem = disablePortal ? StyledMenuItem : Fragment; const menuItemProps = disablePortal ? { onMouseLeave: onClose } : {}; return ( @@ -38,7 +40,7 @@ export const NavigationMenu = ({ EndIcon={ArrowDropDownRoundedIcon} isActive={open} onClick={onToggleOpen} - variant="nav" + variant={isSelected ? "activeNav" : "nav"} > diff --git a/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenuItems/navigationMenuItems.tsx b/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenuItems/navigationMenuItems.tsx index eaa9ca55..263b8d51 100644 --- a/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenuItems/navigationMenuItems.tsx +++ b/src/components/Layout/components/Header/components/Content/components/Navigation/components/NavigationMenuItems/navigationMenuItems.tsx @@ -49,7 +49,7 @@ export const NavigationMenuItems = ({ icon, label, menuItems: nestedMenuItems, - selectedMatch, + selectedPatterns, target = ANCHOR_TARGET.SELF, url, }, @@ -79,11 +79,7 @@ export const NavigationMenuItems = ({ REL_ATTRIBUTE.NO_OPENER_NO_REFERRER ); }} - selected={isNavigationLinkSelected( - url, - pathname, - selectedMatch - )} + selected={isNavigationLinkSelected(pathname, selectedPatterns)} > {icon && {icon}} >; label: ReactNode; menuItems?: MenuItem[]; - selectedMatch?: SELECTED_MATCH; + selectedMatch?: SelectedMatch; + selectedPatterns?: string[]; target?: ANCHOR_TARGET; url: string; visible?: Partial>; @@ -59,7 +60,7 @@ export const Navigation = forwardRef( divider, label, menuItems, - selectedMatch, + selectedPatterns, target = ANCHOR_TARGET.SELF, url, }, @@ -70,6 +71,10 @@ export const Navigation = forwardRef( {mdUp ? ( ( ( ); }} variant={ - isNavigationLinkSelected(url, pathname, selectedMatch) + isNavigationLinkSelected(pathname, selectedPatterns) ? "activeNav" : "nav" } diff --git a/src/components/Layout/components/Header/header.stories.tsx b/src/components/Layout/components/Header/header.stories.tsx index d25fa437..dae9c2bf 100644 --- a/src/components/Layout/components/Header/header.stories.tsx +++ b/src/components/Layout/components/Header/header.stories.tsx @@ -71,7 +71,6 @@ export const HeaderStory: Story = { searchEnabled: true, slogan: "Header Slogan", socialMedia: { - label: "Follow Us", socials: [ { Icon: XIcon, diff --git a/src/components/Layout/components/Header/header.tsx b/src/components/Layout/components/Header/header.tsx index b57b0629..593ec7f8 100644 --- a/src/components/Layout/components/Header/header.tsx +++ b/src/components/Layout/components/Header/header.tsx @@ -2,15 +2,12 @@ import { Fade, Toolbar } from "@mui/material"; import { usePathname } from "next/navigation"; import React, { ReactNode } from "react"; import { ComponentsConfig } from "../../../../config/entities"; -import { useBreakpoint } from "../../../../hooks/useBreakpoint"; -import { useMenu } from "../../../../hooks/useMenu"; import { APP_BAR_PROPS, FADE_TRANSITION_PROPS, TOOLBAR_PROPS, } from "./common/constants"; import { Navigation, SocialMedia } from "./common/entities"; -import { getNavigationLinks } from "./common/utils"; import { Announcements } from "./components/Announcements/announcements"; import { Actions } from "./components/Content/components/Actions/actions"; import { Authentication } from "./components/Content/components/Actions/components/Authentication/authentication"; @@ -21,8 +18,10 @@ import { Slogan } from "./components/Content/components/Slogan/slogan"; import { Divider } from "./components/Content/components/Slogan/slogan.styles"; import { Socials } from "./components/Content/components/Socials/socials.styles"; import { AppBar, Center, Left, Right } from "./header.styles"; +import { useHeaderNavigation } from "./hooks/useHeaderNavigation"; import { useHeaderVisibility } from "./hooks/useHeaderVisibility"; import { useMeasureHeader } from "./hooks/useMeasureHeader"; +import { useMenu } from "./hooks/useMenu"; export interface HeaderProps { actions?: ReactNode; @@ -38,8 +37,8 @@ export interface HeaderProps { } export const Header = ({ ...headerProps }: HeaderProps): JSX.Element => { - const { breakpoint } = useBreakpoint(); - const { isIn } = useHeaderVisibility(headerProps); + const { navigation } = useHeaderNavigation(headerProps); + const { isIn } = useHeaderVisibility({ ...headerProps, navigation }); const { headerRef } = useMeasureHeader(); const { onClose, onOpen, open } = useMenu(); const pathname = usePathname() ?? ""; @@ -49,14 +48,16 @@ export const Header = ({ ...headerProps }: HeaderProps): JSX.Element => { authenticationEnabled, className, logo, - navigation: [navItemsL, navItemsC, navItemsR] = [], searchEnabled, searchURL, slogan, socialMedia, } = headerProps; - const navigationProps = { headerProps, pathname }; - + const [navItemsL, navItemsC, navItemsR] = navigation; + const navigationProps = { + headerProps: { ...headerProps, navigation }, + pathname, + }; return ( {/* Announcements */} @@ -74,10 +75,7 @@ export const Header = ({ ...headerProps }: HeaderProps): JSX.Element => { {isIn.isSloganIn && } {/* Left navigation */} {isIn.isLeftNavigationIn && ( - + )} @@ -86,10 +84,7 @@ export const Header = ({ ...headerProps }: HeaderProps): JSX.Element => {
{/* Center navigation */} {isIn.isCenterNavigationIn && ( - + )}
@@ -98,10 +93,7 @@ export const Header = ({ ...headerProps }: HeaderProps): JSX.Element => { {/* Navigation */} {isIn.isRightNavigationIn && ( - + )} {/* Socials */} {isIn.isSocialsIn && ( diff --git a/src/components/Layout/components/Header/hooks/useHeaderNavigation.ts b/src/components/Layout/components/Header/hooks/useHeaderNavigation.ts new file mode 100644 index 00000000..b3e5a041 --- /dev/null +++ b/src/components/Layout/components/Header/hooks/useHeaderNavigation.ts @@ -0,0 +1,19 @@ +import { useBreakpoint } from "../../../../../hooks/useBreakpoint"; +import { getNavigationLinks } from "../common/utils"; +import { NavLinkItem } from "../components/Content/components/Navigation/navigation"; +import { HeaderProps } from "../header"; + +export interface UseHeaderNavigation { + navigation: [NavLinkItem[], NavLinkItem[], NavLinkItem[]]; +} + +export const useHeaderNavigation = ( + headerProps: HeaderProps +): UseHeaderNavigation => { + const { breakpoint } = useBreakpoint(); + const { navigation: [navL, navC, navR] = [] } = headerProps; + const navItemsL = getNavigationLinks(navL, breakpoint); + const navItemsC = getNavigationLinks(navC, breakpoint); + const navItemsR = getNavigationLinks(navR, breakpoint); + return { navigation: [navItemsL, navItemsC, navItemsR] }; +}; diff --git a/src/components/Layout/components/Header/hooks/useHeaderVisibility.ts b/src/components/Layout/components/Header/hooks/useHeaderVisibility.ts index 5a0681c7..87cdb429 100644 --- a/src/components/Layout/components/Header/hooks/useHeaderVisibility.ts +++ b/src/components/Layout/components/Header/hooks/useHeaderVisibility.ts @@ -38,9 +38,9 @@ export const useHeaderVisibility = ( const hasActions = Boolean(actions); const hasLogo = Boolean(logo); const hasMenu = !mdUp; - const hasNavItemsC = Boolean(navItemsC); - const hasNavItemsL = Boolean(navItemsL); - const hasNavItemsR = Boolean(navItemsR); + const hasNavItemsC = Boolean(navItemsC && navItemsC.length > 0); + const hasNavItemsL = Boolean(navItemsL && navItemsL.length > 0); + const hasNavItemsR = Boolean(navItemsR && navItemsR.length > 0); const hasSlogan = Boolean(slogan); const hasSocials = Boolean(socialMedia); // Determines header content visibility. diff --git a/src/hooks/useMenu.ts b/src/components/Layout/components/Header/hooks/useMenu.ts similarity index 91% rename from src/hooks/useMenu.ts rename to src/components/Layout/components/Header/hooks/useMenu.ts index 4d612c3b..489d5e73 100644 --- a/src/hooks/useMenu.ts +++ b/src/components/Layout/components/Header/hooks/useMenu.ts @@ -13,12 +13,12 @@ export interface UseMenu { export const useMenu = (): UseMenu => { const [open, setOpen] = useState(false); - // Closes header menu. + // Closes menu. const onClose = useCallback((): void => { setOpen(false); }, []); - // Opens header menu. + // Opens menu. const onOpen = useCallback((): void => { setOpen(true); }, []); diff --git a/src/components/common/Dialog/hooks/useDialog.ts b/src/components/common/Dialog/hooks/useDialog.ts new file mode 100644 index 00000000..a8292dc1 --- /dev/null +++ b/src/components/common/Dialog/hooks/useDialog.ts @@ -0,0 +1,28 @@ +import { useCallback, useState } from "react"; + +export interface UseDialog { + onClose: () => void; + onOpen: () => void; + open: boolean; +} + +/** + * Dialog functionality. + * @param isOpen - Open state (initial). + * @returns dialog functionality. + */ +export const useDialog = (isOpen = false): UseDialog => { + const [open, setOpen] = useState(isOpen); + + // Closes dialog. + const onClose = useCallback((): void => { + setOpen(false); + }, []); + + // Opens dialog + const onOpen = useCallback((): void => { + setOpen(true); + }, []); + + return { onClose, onOpen, open }; +}; diff --git a/src/hooks/useMenuWithPosition.ts b/src/components/common/Menu/hooks/useMenu.ts similarity index 87% rename from src/hooks/useMenuWithPosition.ts rename to src/components/common/Menu/hooks/useMenu.ts index acfbde7b..04ec5537 100644 --- a/src/hooks/useMenuWithPosition.ts +++ b/src/components/common/Menu/hooks/useMenu.ts @@ -1,7 +1,7 @@ import { MenuProps as MMenuProps } from "@mui/material"; import { MouseEvent, useCallback, useMemo, useState } from "react"; -export interface UseMenuWithPosition { +export interface UseMenu { anchorEl: MMenuProps["anchorEl"]; onClose: () => void; onOpen: (event: MouseEvent) => void; @@ -13,16 +13,16 @@ export interface UseMenuWithPosition { * Menu functionality for menu dropdown, with menu position. * @returns menu functionality. */ -export const useMenuWithPosition = (): UseMenuWithPosition => { +export const useMenu = (): UseMenu => { const [anchorEl, setAnchorEl] = useState(null); const open = useMemo(() => Boolean(anchorEl), [anchorEl]); - // Closes header menu. + // Closes menu. const onClose = useCallback((): void => { setAnchorEl(null); }, []); - // Opens header menu. + // Opens menu. const onOpen = useCallback((event: MouseEvent): void => { setAnchorEl(event.currentTarget); }, []);