Refactor sticky nav hook/components

This commit is contained in:
Sam Becker 2025-04-03 18:10:43 -05:00
parent c959e88ebb
commit ffde3cedaf
3 changed files with 42 additions and 39 deletions

View File

@ -36,9 +36,8 @@ export default function Nav({
const showNav = !isPathSignIn(pathname);
const {
isNavSticky,
shouldHideStickyNav,
shouldAnimateStickyNav,
classNameStickyContainer,
classNameStickyNav,
} = useStickyNav(ref);
const renderLink = (
@ -63,9 +62,7 @@ export default function Nav({
return (
<AppGrid
className={clsx(
isNavSticky && 'sticky top-0 z-10 pointer-events-none',
)}
className={classNameStickyContainer}
classNameMain='pointer-events-auto'
contentMain={
<AnimateItems
@ -77,15 +74,11 @@ export default function Nav({
key="nav"
ref={ref}
className={clsx(
'w-full',
'w-full flex items-center bg-main',
NAV_HEIGHT_CLASS,
// Enlarge nav to ensure it fully masks underlying content
'md:w-[calc(100%+8px)] md:translate-x-[-4px] md:px-[4px]',
'flex items-center bg-main',
shouldAnimateStickyNav && 'transition-transform duration-200',
shouldHideStickyNav
? 'translate-y-[-100%]'
: 'translate-y-0',
NAV_HEIGHT_CLASS,
classNameStickyNav,
)}>
<ViewSwitcher
currentSelection={switcherSelectionForPath()}

View File

@ -1,15 +1,16 @@
import useScrollDirection from '@/utility/useScrollDirection';
import { RefObject } from 'react';
import clsx from 'clsx/lite';
import { RefObject, useMemo } from 'react';
export default function useStickyNav(
ref: RefObject<HTMLElement | null>,
isEnabled = true,
) {
const { scrollDirection, lastScrollY } = useScrollDirection();
const { scrollDirection, scrollY } = useScrollDirection();
const navHeight = ref.current?.clientHeight ?? 0;
const hasScrolledPastNav = lastScrollY > navHeight;
const hasScrolledPastNav = scrollY > navHeight;
const isNavSticky = isEnabled && (
hasScrolledPastNav ||
@ -21,13 +22,20 @@ export default function useStickyNav(
scrollDirection === 'down';
const shouldAnimateStickyNav =
isNavSticky &&
lastScrollY > navHeight * 2 ||
scrollDirection === 'up';
isNavSticky && (
scrollY > navHeight * 2 ||
scrollDirection === 'up'
);
return {
isNavSticky,
shouldHideStickyNav,
shouldAnimateStickyNav,
};
const classNames = useMemo(() => ({
classNameStickyContainer: clsx(
isNavSticky && 'sticky top-0 z-10 pointer-events-none',
),
classNameStickyNav: clsx(
shouldHideStickyNav ? 'translate-y-[-100%]' : 'translate-y-0',
shouldAnimateStickyNav && 'transition-transform duration-200',
),
}), [isNavSticky, shouldAnimateStickyNav, shouldHideStickyNav]);
return classNames;
};

View File

@ -1,26 +1,28 @@
import { useEffect, useState, useRef } from 'react';
import { useEffect, useState } from 'react';
export default function useScrollDirection() {
const [scrollDirection, setScrollDirection] = useState<'up' | 'down'>('down');
const lastScrollY = useRef(0);
const [scrollInfo, setScrollInfo] = useState({
scrollDirection: 'down' as 'up' | 'down',
scrollY: 0,
});
useEffect(() => {
const handleScroll = () => {
const pageHeight =
document.documentElement.scrollHeight - window.innerHeight;
setScrollDirection((
window.scrollY >= lastScrollY.current ||
lastScrollY.current > pageHeight
) ? 'down' : 'up');
lastScrollY.current = window.scrollY;
const pageHeight = (
document.documentElement.scrollHeight -
window.innerHeight
);
setScrollInfo(prev => ({
scrollDirection: (
window.scrollY >= prev.scrollY ||
prev.scrollY > pageHeight
) ? 'down' : 'up',
scrollY: window.scrollY,
}));
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return {
scrollDirection,
lastScrollY: lastScrollY.current,
};
return scrollInfo;
}