import * as React from 'react' import * as PropTypes from 'prop-types' import { Icon } from '../../../../react-common/src' import pkg from '../../../../../package.json' import styled from 'styled-components' import Link from 'next/link' import Nav from '../Nav/Nav' import { MouseEventHandler } from 'react' import ThemeToggle from '../ThemeToggle/ThemeToggle' const StyledLink = styled('a')({ display: 'block', textDecoration: 'none', marginTop: '4rem', marginBottom: '3rem', }) const Container = styled('span')({ display: 'flex', alignItems: 'center', padding: '0 1rem', height: '2rem', margin: '0 0 0 auto', boxSizing: 'border-box', '@media (min-width: 720px)': { maxWidth: 'var(--max-width)', }, }) const Base = styled('aside')({ '--max-width': 240, display: 'none', '@media only screen': { display: 'block', position: 'fixed', top: 0, width: '100%', height: '100%', backgroundColor: 'var(--color-bg)', zIndex: 4, transitionProperty: 'color, background-color, left', transitionTimingFunction: 'ease', transitionDuration: '350ms', overflow: 'auto', '@media (min-width: 720px)': { left: '0 !important', width: `${100 / 4}%`, maxWidth: 'none', height: '100%', '+ *': { paddingLeft: `${100 / 4}%`, boxSizing: 'border-box', }, }, }, }) const SidebarToggle = styled('a')({ display: 'none', '@media only screen': { width: '4rem', height: '4rem', position: 'fixed', top: 0, left: 0, display: 'grid', placeContent: 'center', zIndex: 5, '@media (min-width: 720px)': { display: 'none', }, '::before': { backgroundColor: 'var(--color-bg)', content: "''", position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', opacity: 0.75, zIndex: -1, }, }, }) const Actions = styled('div')({ marginBottom: '4rem', display: 'grid', gap: '1rem', alignItems: 'center', gridTemplateColumns: 'auto auto', }) const NavWrapper = styled('nav')({ '--size-link': '3rem', marginBottom: '4rem', }) const RepoLink = styled('a')({ display: 'inline-grid', width: '1.5rem', height: '1.5rem', placeContent: 'center', }) const propTypes = { data: PropTypes.shape({ nav: PropTypes.array, }), brand: PropTypes.elementType, initialTheme: PropTypes.string, } type Props = PropTypes.InferProps<typeof propTypes> const Sidebar: React.FC<Props> = ({ data, brand: BrandRaw, initialTheme, }) => { const [sidebar, setSidebar] = React.useState(false) const navRef = React.useRef<HTMLDivElement>(null) const Brand = BrandRaw! const toggleSidebar = (b: boolean): MouseEventHandler => (e) => { e.preventDefault() setSidebar(b) } const handleSidebarClose = (e: MouseEvent) => { let currentElement = e.target as HTMLElement while (currentElement !== e.currentTarget) { if (currentElement.tagName === 'A') { setSidebar(false) break } const { parentElement } = currentElement if (parentElement as HTMLElement === null) { break } currentElement = parentElement as HTMLElement } } React.useEffect(() => { navRef.current.addEventListener('click', handleSidebarClose, { capture: true }) return () => { navRef.current.removeEventListener('click', handleSidebarClose, { capture: true }) } }, []) return ( <React.Fragment> <SidebarToggle onClick={toggleSidebar(!sidebar)} > { !sidebar && ( <Icon name="menu" /> ) } { sidebar && ( <Icon name="x" /> ) } </SidebarToggle> <Base ref={navRef} style={{ left: sidebar ? 0 : '-100%', }} > <NavWrapper> <Link href="/" passHref > <StyledLink> <Container> <Brand /> </Container> </StyledLink> </Link> <Container> <Actions> <ThemeToggle initialTheme={initialTheme} /> <RepoLink href={pkg.repository} target="_blank" rel="noopener noreferrer" > <Icon name="code" label="Visit Repository" /> </RepoLink> </Actions> </Container> <Nav data={data!.nav} /> </NavWrapper> </Base> </React.Fragment> ) } Sidebar.propTypes = propTypes export default Sidebar