Add a sticky navigation

This commit is contained in:
Arnie 2023-06-02 13:45:03 +02:00
parent e7f6b2be79
commit 75325472b8

View File

@ -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<HTMLDivElement>(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 (
<>
<div ref={ref}>
<NavigationHeadline>
{intl.formatMessage(messages.contact)}
</NavigationHeadline>
@ -79,7 +154,7 @@ const MainNavigation: React.FC = () => {
<NavigationHeadline href="#experience">
{intl.formatMessage(messages.experience)}
</NavigationHeadline>
</>
</div>
);
};