diff --git a/docs/src/components/QuickNav/QuickNav.tsx b/docs/src/components/QuickNav/QuickNav.tsx index d93a6e231c..e05efe1940 100644 --- a/docs/src/components/QuickNav/QuickNav.tsx +++ b/docs/src/components/QuickNav/QuickNav.tsx @@ -65,16 +65,17 @@ function onMounted(ref: React.RefObject) { ref.current.style.top = '0px'; ref.current.style.bottom = ''; - ref.current.style.marginTop = '0px'; + ref.current.style.marginTop = ''; ref.current.style.marginBottom = ''; // Get the nav top Y coordinate from the start of the document // if it was `position: static` and `position: absolute` // relative to the start of the document ref.current.style.position = 'static'; - const staticTop = window.scrollY + ref.current.getBoundingClientRect().y; + const staticTop = window.scrollY + Math.round(ref.current.getBoundingClientRect().y); + ref.current.style.marginTop = '0px'; ref.current.style.position = 'absolute'; - const absoluteTop = window.scrollY + ref.current.getBoundingClientRect().y; + const absoluteTop = window.scrollY + Math.round(ref.current.getBoundingClientRect().y); // Get the nav bottom Y coordinate when it's at its maximum possible bottom position // relative to the start of the document @@ -82,7 +83,7 @@ function onMounted(ref: React.RefObject) { ref.current.style.top = 'auto'; ref.current.style.bottom = '0'; const rect = ref.current.getBoundingClientRect(); - const absoluteBottom = window.scrollY + rect.bottom; + const absoluteBottom = window.scrollY + Math.round(rect.bottom); ref.current.style.position = ''; ref.current.style.top = initialStyles.top; @@ -159,14 +160,12 @@ function onMounted(ref: React.RefObject) { if (!ref.current || isScrollLocked) { return; } - const delta = window.scrollY - prevScrollY; prevScrollY = window.scrollY; - // We may get into <0.1px rounding issues in Safari and Firefox const rect = ref.current.getBoundingClientRect(); - top = Math.round(rect.top); - bottom = Math.round(rect.bottom); + top = rect.top; + bottom = rect.bottom; // Skip when scrolling in the direction that matches the sticky position if ((delta > 0 && state === 'StickyBottom') || (delta < 0 && state === 'StickyTop')) { @@ -184,11 +183,12 @@ function onMounted(ref: React.RefObject) { if (state === 'StickyTop') { const clippedAtBottom = bottom - window.innerHeight; - if (clippedAtBottom - top >= stickyTopThreshold) { + if (clippedAtBottom - top > stickyTopThreshold) { if (delta >= clippedAtBottom) { stickToBottom(); - } else { - unstick(top - delta, bottom - delta); + // Unstick if we are scrolling down (and not recovering from overscroll) + } else if (delta > 0 && !isOverscrolling()) { + unstick(Math.round(top) - delta, Math.round(bottom) - delta); } } return; @@ -197,13 +197,14 @@ function onMounted(ref: React.RefObject) { if (state === 'StickyBottom') { if (delta <= top) { stickToTop(); - } else { - unstick(top - delta, bottom - delta); + // Unstick if we are scrolling up (and not recovering from overscroll) + } else if (delta < 0 && !isOverscrolling()) { + unstick(Math.round(top) - delta, Math.round(bottom) - delta); } return; } - if (state === 'Scrollable' && delta <= 0 && top - delta >= cssTop) { + if (state === 'Scrollable' && delta < 0 && top - delta >= cssTop) { stickToTop(); return; } @@ -259,6 +260,13 @@ function onMounted(ref: React.RefObject) { }; } +function isOverscrolling() { + return ( + window.scrollY < 0 || + window.scrollY + window.innerHeight > document.documentElement.scrollHeight + ); +} + export function Title({ className, ...props }: React.ComponentProps<'header'>) { return
; }