UNPKG

gatsby-theme-guitar-book

Version:

A Gatsby theme for building guitar book websites

264 lines (244 loc) 6.35 kB
import PropTypes from 'prop-types'; import React, {Fragment, useEffect, useMemo, useRef} from 'react'; import styled from '@emotion/styled'; import useKey from 'react-use/lib/useKey'; import useWindowSize from 'react-use/lib/useWindowSize'; import {IconTwitter} from '@apollo/space-kit/icons/IconTwitter'; import {IconYoutube} from '@apollo/space-kit/icons/IconYoutube'; import {boxShadow} from './search'; import {colors} from '../utils/colors'; import {smallCaps} from '../utils/typography'; import breakpoints from '../utils/breakpoints'; import {size, transparentize} from 'polished'; const Wrapper = styled.div({ width: '100%', height: '100%', backgroundColor: transparentize(0.5, colors.text2), overflow: 'auto', position: 'fixed', top: 0, left: 0, zIndex: 3, perspective: '1000px', transitionProperty: 'opacity, visibility', transitionDuration: '150ms', transitionTimingFunction: 'ease-in-out' }); const transitionDuration = 150; // in ms const Menu = styled.div({ width: 700, marginBottom: 24, borderRadius: 4, boxShadow, backgroundColor: 'white', overflow: 'hidden', position: 'absolute', transformOrigin: '25% 25%', transition: `transform ${transitionDuration}ms ease-in-out`, outline: 'none', [breakpoints.md]: { width: 450 }, [breakpoints.sm]: { width: 'calc(100vw - 48px)' } }); const MenuTitle = styled.h6(smallCaps, { margin: 24, marginBottom: 0, fontSize: 13, fontWeight: 600, color: colors.text3 }); const StyledNav = styled.nav({ display: 'flex', flexWrap: 'wrap', margin: 12 }); const NavItem = styled.div({ display: 'block', width: '50%', [breakpoints.md]: { width: '100%' } }); const NavItemInner = styled.a({ display: 'block', height: '100%', padding: 12, borderRadius: 4, color: colors.text1, textDecoration: 'none', backgroundColor: 'transparent', transitionProperty: 'color, background-color', transitionDuration: '150ms', transitionTimingFunction: 'ease-in-out', '@media (hover: hover)': { ':hover': { color: 'white', backgroundColor: colors.primary, p: { color: colors.primaryLight } } } }); export const NavItemTitle = styled.h4({ marginBottom: 8, fontWeight: 600, color: 'inherit' }); export const NavItemDescription = styled.p({ marginBottom: 0, fontSize: 14, lineHeight: 1.5, color: colors.text3, transition: 'color 150ms ease-in-out' }); const FooterNav = styled.nav({ display: 'flex', alignItems: 'center', padding: '16px 24px', backgroundColor: colors.background, [breakpoints.md]: { display: 'block' } }); const FooterNavItem = styled.a({ color: colors.text2, textDecoration: 'none', ':hover': { color: colors.text3 }, ':not(:last-child)': { marginRight: 24 } }); const SocialLinks = styled.div({ display: 'flex', marginLeft: 'auto', [breakpoints.md]: { marginTop: 8 } }); const SocialLink = styled.a({ color: colors.text2, ':hover': { color: colors.text3 }, ':not(:last-child)': { marginRight: 24 }, svg: { ...size(24), display: 'block', fill: 'currentColor' } }); export default function DocsetSwitcher(props) { const menuRef = useRef(null); const {width} = useWindowSize(); useKey('Escape', props.onClose); useEffect(() => { if (props.open) { // focus the menu after it has been transitioned in setTimeout(() => { menuRef.current.focus(); }, transitionDuration); } }, [props.open]); const {current} = props.buttonRef; const menuStyles = useMemo(() => { if (!current) { return null; } const {top, left, height} = current.getBoundingClientRect(); return { top: top + height + 2, left }; }, [current, width, props.open]); function handleWrapperClick(event) { if (event.target === event.currentTarget) { props.onClose(); } } const hasSocialUrls = Boolean( props.twitterUrl || props.youtubeUrl ); return ( <Wrapper onClick={handleWrapperClick} style={{ opacity: props.open ? 1 : 0, visibility: props.open ? 'visible' : 'hidden' }} > <Menu ref={menuRef} tabIndex={-1} style={{ ...menuStyles, transform: !props.open && 'translate3d(0,-24px,-16px) rotate3d(1,0,0.1,8deg)' }} > <MenuTitle>{props.siteName}</MenuTitle> <StyledNav> {props.navItems.map(navItem => ( <NavItem key={navItem.url}> <NavItemInner href={navItem.url}> <NavItemTitle>{navItem.title}</NavItemTitle> <NavItemDescription>{navItem.description}</NavItemDescription> </NavItemInner> </NavItem> ))} </StyledNav> {(props.footerNavConfig || hasSocialUrls) && ( <FooterNav> <Fragment> {props.footerNavConfig && Object.entries(props.footerNavConfig).map(([text, props]) => ( <FooterNavItem key={text} {...props}> {text} </FooterNavItem> ))} {hasSocialUrls && ( <SocialLinks> {props.twitterUrl && ( <SocialLink href={props.twitterUrl} title="Twitter" target="_blank" > <IconTwitter/> </SocialLink> )} {props.youtubeUrl && ( <SocialLink href={props.youtubeUrl} title="YouTube" target="_blank" > <IconYoutube/> </SocialLink> )} </SocialLinks> )} </Fragment> </FooterNav> )} </Menu> </Wrapper> ); } DocsetSwitcher.propTypes = { open: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, buttonRef: PropTypes.object.isRequired, siteName: PropTypes.string.isRequired, navItems: PropTypes.array.isRequired, footerNavConfig: PropTypes.object.isRequired, twitterUrl: PropTypes.string, youtubeUrl: PropTypes.string };