import React, { useEffect } from 'react'; import styled from 'styled-components'; import v from '../../styles/Variables'; import trapFocus from '../../utils/TrapFocus'; import Close from '../NavButton/Close'; interface Props { /** * the color of the mask */ backgroundColor?: string; /** * any valid jsx */ children: React.ReactNode; /** * function to happen after the back button is clicked */ backCb: () => void; /** * function to happen after the close button is clicked */ closeCb: () => void; /** * if the drawer is currently open or not */ drawerOpen: boolean; /** * id added to the main wrapper */ id?: string; /** * class applied to the drawer */ theme?: string; /** * docked on the right if true */ onRight?: boolean; /** * the page behind will not scroll when enabled */ preventScroll?: boolean; /** * show the back button */ showBack?: boolean; /** * show the close button */ showClose?: boolean; /** * allow swiping to close */ swipeToClose?: boolean; /** * title of the drawer */ title?: string; } interface Styles { backgroundColor: string; } const Drawer = ({ backgroundColor = v.colors.light, children, backCb, closeCb, drawerOpen = false, id = 'my-drawer-id', theme, onRight = false, preventScroll = true, showBack = false, showClose = true, swipeToClose, title, }: Props) => { // change docked position let positionStyle = {}; if (onRight === true) positionStyle = { left: 'inherit', right: 0 }; useEffect(() => { document.addEventListener('keydown', (e) => { trapFocus(e, id, drawerOpen); if (e.key === 'Escape') { window.document.getElementsByTagName('html')[0].classList.remove('nwc--noscroll'); closeCb(); } }); }, []); // stop the body from being scrollable useEffect(() => { const elClassList = window.document.getElementsByTagName('html')[0].classList; setTimeout(() => { if (window.document && preventScroll === true && drawerOpen === true) { if (elClassList.contains('nwc--noscroll')) { elClassList.remove('nwc--noscroll'); } else { elClassList.add('nwc--noscroll'); } } // if open, focus the close button for accessibility const closeBtn = document.querySelector('.nwc--drawer .nwc--hamburger'); if (drawerOpen === true) closeBtn!.focus(); }, 300); }, [drawerOpen, preventScroll]); // swipe detection :: https://stackoverflow.com/questions/2264072/detect-a-finger-swipe-through-javascript-on-the-iphone-and-android if (swipeToClose === true && drawerOpen) { document.addEventListener('touchstart', handleTouchStart, false); document.addEventListener('touchmove', handleTouchMove, false); } let xDown: number | null = null; let yDown: number | null = null; function getTouches(e: TouchEvent) { return e.touches; } function handleTouchStart(e: TouchEvent) { const firstTouch = getTouches(e)[0]; xDown = firstTouch.clientX; yDown = firstTouch.clientY; console.log('handleTouchStart'); } function handleTouchMove(evt: TouchEvent) { if (!xDown || !yDown || drawerOpen !== true) return; let xUp = evt.touches[0].clientX; let yUp = evt.touches[0].clientY; var xDiff = xDown - xUp; var yDiff = yDown - yUp; if (Math.abs(xDiff) > Math.abs(yDiff)) { if (xDiff > 0) { // left swipe // console.log('left'); if (onRight !== true) { window.document.getElementsByTagName('html')[0].classList.remove('nwc--noscroll'); closeCb(); } } else { // right swipe // console.log('right'); if (onRight === true) { window.document.getElementsByTagName('html')[0].classList.remove('nwc--noscroll'); closeCb(); } } } else { if (yDiff > 0) { console.log('up'); // up swipe } else { console.log('down'); // down swipe } } // reset values xDown = null; yDown = null; } return ( closeCb()} /> ); }; export default Drawer; /* styles */ const DrawerStyles = styled.div` opacity: 1; position: fixed; height: 100%; width: 90%; max-width: 380px; top: 0; left: 0; transition: all 0.2s linear; z-index: 9999; &.closed { left: -1000px; } .drawer--wrapper { position: relative; height: 100%; width: 100%; box-shadow: 0 0 8px rgba(0, 0, 0, 0.3); background: ${({ backgroundColor }) => backgroundColor}; overflow-y: scroll; z-index: 999; } .nwc--hamburger { position: absolute; right: 5px; top: 5px; } `; const Mask = styled.aside` position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 99; background: rgba(0, 0, 0, 0.66); `; const Title = styled.div` line-height: 60px; font-size: 21px; padding-left: 10px; color: ${v.colors.dark}; `; const BackStyles = styled.span` position: absolute; left: 5px; top: 5px; width: 40px; height: 40px; line-height: 57px; `;