From 75325472b8aa803699adbd08dbc184ee7989e607 Mon Sep 17 00:00:00 2001 From: Arnie Date: Fri, 2 Jun 2023 13:45:03 +0200 Subject: [PATCH] Add a sticky navigation --- .../components/Navigation/MainNavigation.tsx | 81 ++++++++++++++++++- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/app/frontend/src/components/Navigation/MainNavigation.tsx b/app/frontend/src/components/Navigation/MainNavigation.tsx index 29b48c7..a4af62b 100644 --- a/app/frontend/src/components/Navigation/MainNavigation.tsx +++ b/app/frontend/src/components/Navigation/MainNavigation.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useRef } from "react"; import { defineMessages, useIntl } from "react-intl"; import { CONTACT_EMAIL, CONTACT_PHONE } from "../../config/environment"; @@ -30,10 +30,85 @@ const messages = defineMessages({ }, }); +const getTopOffset = (input: HTMLElement) => { + let el: HTMLElement | Element | null = input; + let o = 0; + do { + if (!(el instanceof HTMLElement)) { + break; + } + + if (!isNaN(el.offsetTop)) { + o += el.offsetTop; + } + } while ((el = el.offsetParent)); + + return o; +}; + const MainNavigation: React.FC = () => { const intl = useIntl(); + + const ref = useRef(null); + + useEffect(() => { + const d = ref.current; + if (!d) { + return; + } + + let { height } = d.getBoundingClientRect(); + + const ro = new ResizeObserver((entries) => { + if (entries[0]) { + height = entries[0].contentRect.height; + } + }); + + ro.observe(d); + + let scrollPrev = window.scrollY; + + const listener = () => { + const down = scrollPrev < window.scrollY; + scrollPrev = window.scrollY; + const top = getTopOffset(d); + + if (window.innerHeight > height) { + d.style.marginTop = `${Math.max(window.scrollY, 0)}px`; + return; + } + + if (down) { + const delta = top + height; + const wDelta = window.scrollY + window.innerHeight; + + const diff = wDelta - delta; + if (diff > 0) { + const sizeDelta = Math.min(window.innerHeight - height, 0); + d.style.marginTop = `${Math.max(top + diff + sizeDelta, 0)}px`; + } + } else { + const delta = top; + const wDelta = window.scrollY; + + const diff = wDelta - delta; + if (diff < 0) { + d.style.marginTop = `${Math.max(top + diff, 0)}px`; + } + } + }; + + window.addEventListener("scroll", listener); + + return () => { + window.removeEventListener("scroll", listener); + ro.disconnect(); + }; + }, []); + return ( - <> +
{intl.formatMessage(messages.contact)} @@ -79,7 +154,7 @@ const MainNavigation: React.FC = () => { {intl.formatMessage(messages.experience)} - +
); };