Skip to content

Commit

Permalink
feat: APP-374 header navigation toggle (#2581)
Browse files Browse the repository at this point in the history
  • Loading branch information
r41ph authored Feb 11, 2025
1 parent 8c5378b commit 77cf40a
Show file tree
Hide file tree
Showing 17 changed files with 180 additions and 119 deletions.
30 changes: 30 additions & 0 deletions tailwind.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,36 @@ module.exports = {
},
},
},
keyframes: {
menuOpen: {
'0%': {
opacity: '0',
transform: 'scale(0.60)',
transformOrigin: 'top right',
},
'100%': {
opacity: '1',
transform: 'scale(1)',
transformOrigin: 'top right',
},
},
menuClose: {
'0%': {
opacity: '1',
transform: 'scale(1)',
transformOrigin: 'top right',
},
'100%': {
opacity: '0',
transform: 'scale(0.60)',
transformOrigin: 'top right',
},
},
},
animation: {
menuOpen: 'menuOpen 300ms ease-in-out',
menuClose: 'menuClose 225ms ease-in-out',
},
},
},
plugins: [],
Expand Down
8 changes: 0 additions & 8 deletions web-components/src/components/header/Header.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,6 @@ export const useHeaderStyles = makeStyles<StyleProps>()(
// BEGIN HACK setting jss styles (duplicated from mui components built-in emotion styles)
// so it's initially rendered on gatsby build
// Remove once migrations from mui jss to emotion and to latest gatsby done
desktop: {
[theme.breakpoints.down('md')]: {
display: 'none',
},
[theme.breakpoints.up('md')]: {
display: 'block',
},
},
mobile: {
[theme.breakpoints.down('md')]: {
display: 'block',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const HeaderDropdownItem: React.FC<
alignItems="center"
className={cn(styles.item, className)}
onMouseEnter={onHover}
component="li"
>
{SVG && (
<Box className="mr-[18px] flex items-center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,32 @@ const HeaderDropdown: React.FC<
items: HeaderDropdownItemProps[];
linkComponent: React.FC<React.PropsWithChildren<NavLinkProps>>;
title?: string;
isUserMenu?: boolean;
}>
> = props => {
const { classes: styles } = useStyles();
return (
<Box display="flex" flexDirection="column">
<Box
display="flex"
flexDirection="column"
className={`${props.isUserMenu ? 'pt-10' : 'p-25'}`}
>
{props.title && (
<Box mb={2}>
<Title variant="h4" className={styles.label}>
{ReactHtmlParser(props.title)}
</Title>
</Box>
)}
{props.items.map((link, i) => (
<HeaderDropdownItem
key={i}
{...link}
linkComponent={props.linkComponent}
/>
))}
<Box component="ul" className="p-0">
{props.items.map((link, i) => (
<HeaderDropdownItem
key={i}
{...link}
linkComponent={props.linkComponent}
/>
))}
</Box>
</Box>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ type Props = {
classes?: {
paper?: string;
};
isUserMenu?: boolean;
};

export const HeaderMenuItemContent = ({
item,
linkComponent: LinkComponent,
pathname,
classes,
isUserMenu,
}: Props): JSX.Element => {
const theme = useTheme();
const { classes: styles } = useHeaderMenuHoverStyles();
Expand All @@ -41,12 +43,14 @@ export const HeaderMenuItemContent = ({
title={item.title}
renderTitle={item.renderTitle}
classes={{ title: styles.title, paper: classes?.paper }}
isUserMenu={isUserMenu}
>
{/* `render` overrides default dropdown */}
{item.dropdownItems && !item.renderDropdownItems && (
<HeaderDropdown
items={item.dropdownItems}
linkComponent={LinkComponent}
isUserMenu={isUserMenu}
/>
)}
{item.renderDropdownItems && item.renderDropdownItems()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,11 @@ import { Theme } from '@mui/material/styles';
import { makeStyles } from 'tss-react/mui';

export const useMenuHoverStyles = makeStyles()((theme: Theme) => ({
popover: {
pointerEvents: 'none',
},
popoverContent: {
pointerEvents: 'auto',
marginTop: theme.spacing(4),
},
text: {
'& li.MuiMenuItem-root:hover': {
backgroundColor: 'transparent',
},
'& li > a': {
'& ul > li > a': {
fontFamily: 'lato',
color: '#000',
textDecoration: 'none',
Expand All @@ -29,11 +22,6 @@ export const useMenuHoverStyles = makeStyles()((theme: Theme) => ({
outline: 'none',
},
},
paper: {
borderRadius: '2px',
border: `1px solid ${theme.palette.grey[400]}`,
padding: theme.spacing(6.25),
},
icon: {
marginLeft: theme.spacing(1),
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React, { useRef } from 'react';
import { MenuList, Paper, Popover } from '@mui/material';
import cx from 'clsx';
import { MenuList } from '@mui/material';

import DropdownIcon from '../../../icons/DropdownIcon';
import { useMenuHoverStyles } from './HeaderMenuItem.Hover.styles';
import { useMenuState } from './hooks/useMenuState';

export interface MenuTitle {
title?: string;
Expand All @@ -18,6 +17,7 @@ interface Props extends MenuTitle {
children: React.ReactNode;
textColor?: string;
dropdownColor?: string;
isUserMenu?: boolean;
}

/**
Expand All @@ -30,31 +30,30 @@ const HeaderMenuItemHover = ({
classes,
dropdownColor,
children,
isUserMenu,
}: Props): JSX.Element => {
const { classes: styles } = useMenuHoverStyles();

const popoverAnchor = useRef(null);
const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);

const handlePopoverOpen = () => {
setAnchorEl(popoverAnchor.current);
};

const handlePopoverClose = () => {
setAnchorEl(null);
};

const open = Boolean(anchorEl);
const {
isMenuOpen,
hasInteracted,
isTouchScreen,
openMenu,
closeMenu,
toggleMenu,
} = useMenuState();

return (
<div>
<div
className="relative p-10"
onMouseEnter={isTouchScreen ? undefined : openMenu}
onMouseLeave={closeMenu}
>
<span
ref={popoverAnchor}
aria-owns={open ? 'mouse-over-popover' : undefined}
className="relative"
aria-owns={isMenuOpen ? 'mouse-over-menu' : undefined}
aria-haspopup="true"
onMouseEnter={handlePopoverOpen}
onMouseLeave={handlePopoverClose}
onClick={handlePopoverOpen}
onClick={isTouchScreen ? toggleMenu : undefined}
>
{title && (
<span className={classes?.title}>
Expand All @@ -64,40 +63,29 @@ const HeaderMenuItemHover = ({
)}
{renderTitle && renderTitle()}
</span>
<Popover
disableRestoreFocus
id="mouse-over-popover"
className={styles.popover}
classes={{
paper: styles.popoverContent,
}}
open={open}
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
PaperProps={{
onMouseEnter: handlePopoverOpen,
onMouseLeave: handlePopoverClose,
}}
onClose={handlePopoverClose}
disableScrollLock={true}
sx={{ position: 'absolute' }}
<nav
id="mouse-over-menu"
className={`absolute top-15 ${
isUserMenu ? 'pt-35' : 'top-full'
} right-0 z-50 ${
isMenuOpen
? 'block opacity-100' + (hasInteracted ? ' animate-menuOpen' : '')
: 'opacity-0' +
(hasInteracted
? ' animate-menuClose pointer-events-none'
: ' hidden')
}`}
onClick={isTouchScreen ? closeMenu : undefined}
>
<Paper className={cx(classes?.paper, styles.paper)} elevation={5}>
<MenuList
classes={{ root: styles.text, padding: styles.noOutline }}
disablePadding
>
{children}
</MenuList>
</Paper>
</Popover>
<MenuList
className="bg-grey-0 rounded-[4px] border border-solid border-grey-300 shadow-[0px_5px_5px_-3px_rgba(0,0,0,0.2),_0px_8px_10px_1px_rgba(0,0,0,0.14),_0px_3px_14px_2px_rgba(0,0,0,0.12)]"
classes={{ root: styles.text, padding: styles.noOutline }}
disablePadding
component="div"
>
{children}
</MenuList>
</nav>
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ export const useHeaderMenuHoverStyles = makeStyles()(theme => ({
boxSizing: 'border-box',
height: '100%',
lineHeight: theme.spacing(6),
paddingRight: theme.spacing(7.375),
paddingLeft: theme.spacing(7.375),
backgroundColor: 'inherit',
'& > a': {
borderBottom: '2px solid transparent',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import React from 'react';
import { BoxProps, MenuItem, SxProps } from '@mui/material';
import {
BoxProps,
MenuItem,
SxProps,
useMediaQuery,
useTheme,
} from '@mui/material';
import cx from 'clsx';

import { Theme } from '../../../../theme/muiTheme';
Expand Down Expand Up @@ -30,6 +35,7 @@ export interface HeaderMenuItemBase {

export interface MenuItemProps extends HeaderMenuItemBase {
item: Item;
isUserMenu?: boolean;
}

const HeaderMenuItem: React.FC<MenuItemProps> = ({
Expand All @@ -39,15 +45,18 @@ const HeaderMenuItem: React.FC<MenuItemProps> = ({
classes,
sx,
component = 'li',
isUserMenu,
}) => {
const { classes: styles } = useHeaderMenuHoverStyles();

const theme = useTheme();
const isTablet = useMediaQuery(theme.breakpoints.down('md'));
return (
<MenuItem
className={cx(
classes?.root,
styles.menuItem,
pathname === item.href && styles.currentMenuItem,
isTablet && !isUserMenu ? '!hidden' : '',
)}
sx={[...sxToArray(sx)]}
component={component}
Expand All @@ -57,6 +66,7 @@ const HeaderMenuItem: React.FC<MenuItemProps> = ({
linkComponent={linkComponent}
pathname={pathname}
classes={{ paper: classes?.paper }}
isUserMenu={isUserMenu}
/>
</MenuItem>
);
Expand Down
Loading

0 comments on commit 77cf40a

Please sign in to comment.