@frontity/twentytwenty-theme
Version:
The WordPress Twenty Twenty starter theme for Frontity
181 lines (159 loc) • 4.34 kB
JavaScript
import { useRef } from "react";
import { styled, connect, Global } from "frontity";
import Link from "../link";
import { CloseNavToggle } from "../navigation/nav-toggle";
import { CloseIcon } from "../icons";
import useFocusTrap from "../hooks/use-trap-focus";
import useFocusEffect from "../hooks/use-focus-effect";
import SectionContainer from "../styles/section-container";
const MobileMenuModal = ({ state, actions }) => {
// Get the menu state and action
const { menu, isMobileMenuOpen } = state.theme;
const { closeMobileMenu } = actions.theme;
// Check if there are links in the state
const isThereLinks = menu != null && menu.length > 0;
/**
* Keep a reference to the close button so we can focus on it when
* the modal opens
*/
const closeButtonRef = useRef();
// Keep a reference to the menu so we can trap focus within it
const menuRef = useRef();
// Focus on the close button when the mobile menu is open
useFocusEffect(closeButtonRef, isMobileMenuOpen);
// Trap focus within the menu when the mobile menu is open
useFocusTrap(menuRef, isMobileMenuOpen);
return (
<Modal data-open={isMobileMenuOpen} role="dialog" aria-modal="true">
{/* Global styles to prevent body scroll when the menu is open */}
{isMobileMenuOpen && (
<Global styles={{ body: { overflowY: "hidden" } }} />
)}
<ModalInner>
<MenuWrapper ref={menuRef}>
<div style={{ flexShrink: 0 }}>
<CloseNavToggle
ref={closeButtonRef}
aria-expanded={isMobileMenuOpen}
onClick={closeMobileMenu}
>
<ToggleText> Close Menu</ToggleText>
<CloseIcon />
</CloseNavToggle>
<MenuContent
as="nav"
role="navigation"
aria-label="Mobile menu links"
>
<MenuList>
{isThereLinks &&
menu.map(([name, link]) => (
<MenuListItem key={name}>
<MenuLinkWrapper>
<MenuLink
link={link}
aria-current={
state.router.link === link ? "page" : undefined
}
>
{name}
</MenuLink>
</MenuLinkWrapper>
</MenuListItem>
))}
</MenuList>
</MenuContent>
</div>
</MenuWrapper>
</ModalInner>
</Modal>
);
};
const Modal = styled.div`
background: #fff;
display: none;
opacity: 0;
overflow-y: auto;
overflow-x: hidden;
position: fixed;
bottom: 0;
right: 99999rem;
top: 0;
z-index: 99;
&[data-open="true"] {
display: flex;
left: 0;
opacity: 1;
right: 0;
transition: opacity 0.25s ease-out;
}
`;
const ModalInner = styled.div`
background: #fff;
display: flex;
justify-content: stretch;
overflow: auto;
width: 100%;
`;
const MenuWrapper = styled(SectionContainer)`
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
width: 100%;
`;
const MenuContent = styled.nav`
display: block;
`;
const MenuList = styled.ul`
position: relative;
list-style: none;
margin: 0;
/* left: calc(50% - 50vw);
width: 100vw; */
`;
const ToggleText = styled.span`
margin-right: 1.6rem;
* {
fill: currentColor;
}
`;
const MenuListItem = styled.li`
position: relative;
border-style: solid;
border-width: 0.1rem 0 0 0;
border-color: #dcd7ca;
display: flex;
flex-wrap: wrap;
line-height: 1;
justify-content: flex-start;
margin: 0;
`;
const MenuLinkWrapper = styled.div`
display: flex;
justify-content: space-between;
width: 100%;
`;
const MenuLink = styled(Link)`
font-size: 2rem;
font-weight: 700;
letter-spacing: -0.0375em;
display: block;
padding: 2rem 2.5rem;
text-decoration: none;
width: 100%;
text-align: left;
@media (min-width: 700px) {
font-size: 2.4rem;
padding: 2.5rem 0;
}
&:hover,
&:focus {
text-decoration: underline;
}
/* styles for active link */
&[aria-current="page"] {
text-decoration: underline;
}
`;
export default connect(MobileMenuModal);