diff --git a/components/OrganizedContent.module.css b/components/OrganizedContent.module.css index 28c6fd00..699e654e 100644 --- a/components/OrganizedContent.module.css +++ b/components/OrganizedContent.module.css @@ -111,78 +111,80 @@ } .burger { - position: fixed; display: none; - bottom: calc(32rem / 16); - left: calc(16rem / 16); - width: calc(62rem / 16); - height: calc(57rem / 16); - cursor: pointer; - z-index: 10; - background: var(--light-blue-2); - border-radius: calc(5rem / 16); - transition: bottom 0.6s; -} - -.hiddenBurger { - bottom: calc(-94rem / 16); -} - -.burger > div { - width: 100%; - display: flex; - flex-direction: column; - justify-content: space-between; - padding: calc(11rem / 16) calc(9rem / 16); -} - -.burger > div > div { - border-radius: calc(5rem / 16); - border: calc(2.5rem / 16) solid var(--blue-2); - position: relative; - background: var(--blue-2); -} - -.mobileNav { - position: fixed; - display: flex; - flex-direction: column; - background: var(--light-blue-1); - width: 90%; - height: 100%; - text-align: left; - top: 0; - left: 0; - transition: transform 0.3s ease-in-out; - transform: translateX(0); - z-index: 9; -} - -.mobileNavClosed { - transform: translateX(-100%); } .mobileNavTitle { - font-size: calc(24rem / 16); - font-weight: 700; - margin: calc(14rem / 16); - margin-top: calc(39rem / 16); + display: none; } @media only screen and (max-width: calc(768rem / 16)) { .burger { display: flex; } - .wrapper > .nav { - display: none; + + .burger { + position: fixed; + border: none; + bottom: calc(32rem / 16); + left: calc(16rem / 16); + width: calc(62rem / 16); + height: calc(57rem / 16); + cursor: pointer; + z-index: 10; + background: var(--light-blue-2); + border-radius: calc(5rem / 16); + transition: transform 0.6s ease-in-out; + transform: translateY(calc(94rem / 16)); + padding: calc(11rem / 16) calc(9rem / 16); + } + + .burgerVisible { + transform: translateY(0); } + + .burger > svg { + width: 100%; + height: 100%; + stroke: var(--blue-2); + } + .navItem { width: auto; padding: 0 calc(16rem / 16); } + .nav { margin: 0; } + + .navWrapper { + position: fixed; + display: flex; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 9; + transition: transform 0.3s ease-in-out; + transform: translateX(-100%); + } + + .mobileNav { + background: var(--light-blue-1); + width: 90%; + } + .mobileNavOpen { + transform: translateX(0); + } + + .mobileNavTitle { + display: flex; + font-size: calc(24rem / 16); + font-weight: 700; + margin: calc(14rem / 16); + margin-top: calc(39rem / 16); + } } diff --git a/components/OrganizedContent.tsx b/components/OrganizedContent.tsx index a558b141..dfda7a3d 100644 --- a/components/OrganizedContent.tsx +++ b/components/OrganizedContent.tsx @@ -35,7 +35,7 @@ interface Props { export function OrganizedContent(props: Props) { const sections = createSections(props.sections); const currentIndex = sections.findIndex(({ id }) => id === props.currentId); - const [open, setOpen] = useState(false); + const [mobileNavOpen, setMobileNavOpen] = useState(false); if (currentIndex < 0) { throw new Error(`Section with ID ${props.currentId} was not found`); @@ -43,19 +43,37 @@ export function OrganizedContent(props: Props) { const section = sections[currentIndex]; const isReadAll = section.id === READ_ALL_ID; + const ref = useRef(null); + const isVisible = useOnScreen(ref.current); + const burgerVisible = useBurger(isVisible); + const navWrapperStyles = mobileNavOpen + ? [styles.navWrapper, styles.mobileNavOpen] + : [styles.navWrapper]; useEffect(() => { - open + mobileNavOpen ? (document.body.style.overflow = "hidden") : (document.body.style.overflow = "visible"); - }, [open]); - - const ref = useRef(null); - const isVisible = useOnScreen(ref); + }, [mobileNavOpen]); return (
-
); } @@ -90,13 +142,13 @@ interface NavProps { sections: Section[]; currentIndex: number; link: Link; - pageTitle?: string; + pageTitle: string; } function Nav({ sections, currentIndex, link: Link, pageTitle }: NavProps) { return (
- {pageTitle &&

{pageTitle}

} +

{pageTitle}

{sections.map((section, index) => { const classNames = [styles.navItem]; @@ -164,116 +216,6 @@ function Footer({ sections, currentIndex, link: Link }: FooterProps) { ); } -interface MobileProps { - open: boolean; - setOpen: React.Dispatch>; - sections: Section[]; - currentIndex: number; - link: Link; - pageTitle: string; - componentIsVisible: boolean; -} - -function MobileWrapper(mobileProps: MobileProps) { - const wrapperRef = useRef(null); - useOutsideAlerter(wrapperRef, mobileProps.setOpen); - - return ( -
- - -
- ); -} - -interface BurgerProps { - open: boolean; - setOpen: React.Dispatch>; - componentIsVisible: boolean; -} - -const Burger = ({ open, setOpen, componentIsVisible }: BurgerProps) => { - const [prevScrollPos, setPrevScrollPos] = useState(0); - const [burgerVisible, setBurgerVisible] = useState(true); - - const handleScroll = useDebounce(() => { - // find current scroll position - const currentScrollPos = window.pageYOffset; - setBurgerVisible( - componentIsVisible && - ((prevScrollPos > currentScrollPos && - prevScrollPos - currentScrollPos > 70) || - currentScrollPos < 10) - ); - - // set state to new scroll position - setPrevScrollPos(currentScrollPos); - }); - - useEffect(() => { - window.addEventListener("scroll", handleScroll); - - return () => window.removeEventListener("scroll", handleScroll); - }, [handleScroll]); - - return ( -
setOpen(!open)} - > -
-
-
-
-
-
- ); -}; - -interface MenuProps { - open: boolean; - sections: Section[]; - currentIndex: number; - link: Link; - pageTitle: string; -} - -const Menu = ({ open, sections, currentIndex, link, pageTitle }: MenuProps) => { - const mobileNav = open - ? styles.mobileNav - : styles.mobileNav + " " + styles.mobileNavClosed; - return ( -
-
- ); -}; - -function useOutsideAlerter( - ref: React.RefObject, - setOpen: React.Dispatch> -) { - useEffect(() => { - const handleClickOutside = (event: Event) => { - if (ref.current && !ref.current.contains(event.target as Node)) { - setOpen(false); - } - }; - - document.addEventListener("mousedown", handleClickOutside); - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }, [ref, setOpen]); -} - function useDebounce(func: () => void, delay = 300) { const timerRef = useRef(undefined); return useCallback(() => { @@ -326,7 +268,7 @@ function Arrow({ direction }: { direction: "left" | "right" }) { ); } -function useOnScreen(ref: React.RefObject) { +function useOnScreen(element: HTMLDivElement | null) { const [isIntersecting, setIntersecting] = useState(false); useEffect(() => { @@ -334,14 +276,41 @@ function useOnScreen(ref: React.RefObject) { setIntersecting(entry.isIntersecting) ); - if (ref.current) { - observer.observe(ref.current); + if (element) { + observer.observe(element); } // Remove the observer as soon as the component is unmounted return () => { observer.disconnect(); }; - }, [ref]); + }, [element]); return isIntersecting; } + +function useBurger(componentIsVisible: boolean): boolean { + const [prevScrollPos, setPrevScrollPos] = useState(0); + const [burgerVisible, setBurgerVisible] = useState(true); + + const handleScroll = useDebounce(() => { + // find current scroll position + const currentScrollPos = window.pageYOffset; + setBurgerVisible( + componentIsVisible && + ((prevScrollPos > currentScrollPos && + prevScrollPos - currentScrollPos > 70) || + currentScrollPos < 10) + ); + + // set state to new scroll position + setPrevScrollPos(currentScrollPos); + }); + + useEffect(() => { + window.addEventListener("scroll", handleScroll); + + return () => window.removeEventListener("scroll", handleScroll); + }, [handleScroll]); + + return burgerVisible; +}