Make nav sticky for top-level pages
This commit is contained in:
parent
1060a5d061
commit
805a4193a7
@ -10,6 +10,7 @@ import {
|
||||
isPathAdmin,
|
||||
isPathFeed,
|
||||
isPathGrid,
|
||||
isPathTopLevel,
|
||||
isPathProtected,
|
||||
isPathSignIn,
|
||||
} from '@/app/paths';
|
||||
@ -19,6 +20,8 @@ import {
|
||||
HAS_DEFINED_SITE_DESCRIPTION,
|
||||
SITE_DESCRIPTION,
|
||||
} from './config';
|
||||
import { useRef } from 'react';
|
||||
import useStickyNav from './useStickyNav';
|
||||
|
||||
const NAV_HEIGHT_CLASS = HAS_DEFINED_SITE_DESCRIPTION
|
||||
? 'min-h-[4rem] sm:min-h-[5rem]'
|
||||
@ -29,9 +32,17 @@ export default function Nav({
|
||||
}: {
|
||||
siteDomainOrTitle: string;
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
const ref = useRef<HTMLElement>(null);
|
||||
|
||||
const pathname = usePathname();
|
||||
const showNav = !isPathSignIn(pathname);
|
||||
const isHome = isPathTopLevel(pathname);
|
||||
|
||||
const {
|
||||
isNavSticky,
|
||||
shouldHideStickyNav,
|
||||
shouldAnimateStickyNav,
|
||||
} = useStickyNav(ref, isHome);
|
||||
|
||||
const renderLink = (
|
||||
text: string,
|
||||
@ -55,6 +66,9 @@ export default function Nav({
|
||||
|
||||
return (
|
||||
<AppGrid
|
||||
className={clsx(
|
||||
isNavSticky && 'sticky top-0 z-10',
|
||||
)}
|
||||
contentMain={
|
||||
<AnimateItems
|
||||
animateOnFirstLoadOnly
|
||||
@ -63,8 +77,14 @@ export default function Nav({
|
||||
items={showNav
|
||||
? [<nav
|
||||
key="nav"
|
||||
ref={ref}
|
||||
className={clsx(
|
||||
'flex items-center w-full',
|
||||
'bg-main',
|
||||
shouldAnimateStickyNav && 'transition-transform duration-200',
|
||||
shouldHideStickyNav
|
||||
? 'translate-y-[-100%]'
|
||||
: 'translate-y-0',
|
||||
NAV_HEIGHT_CLASS,
|
||||
)}>
|
||||
<ViewSwitcher
|
||||
|
||||
@ -249,12 +249,20 @@ export const isPathFocalLengthPhoto = (pathname = '') =>
|
||||
export const checkPathPrefix = (pathname = '', prefix: string) =>
|
||||
pathname.toLowerCase().startsWith(prefix);
|
||||
|
||||
export const isPathRoot = (pathname?: string) =>
|
||||
pathname === PATH_ROOT;
|
||||
|
||||
export const isPathGrid = (pathname?: string) =>
|
||||
checkPathPrefix(pathname, PATH_GRID);
|
||||
|
||||
export const isPathFeed = (pathname?: string) =>
|
||||
checkPathPrefix(pathname, PATH_FEED);
|
||||
|
||||
export const isPathTopLevel = (pathname?: string) =>
|
||||
isPathRoot(pathname)||
|
||||
isPathGrid(pathname) ||
|
||||
isPathFeed(pathname);
|
||||
|
||||
export const isPathSignIn = (pathname?: string) =>
|
||||
checkPathPrefix(pathname, PATH_SIGN_IN);
|
||||
|
||||
|
||||
33
src/app/useStickyNav.ts
Normal file
33
src/app/useStickyNav.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import useScrollDirection from '@/utility/useScrollDirection';
|
||||
import { RefObject } from 'react';
|
||||
|
||||
export default function useStickyNav(
|
||||
ref: RefObject<HTMLElement | null>,
|
||||
isEnabled = true,
|
||||
) {
|
||||
const { scrollDirection, lastScrollY } = useScrollDirection();
|
||||
|
||||
const navHeight = ref.current?.clientHeight ?? 0;
|
||||
|
||||
const hasScrolledPastNav = lastScrollY > navHeight;
|
||||
|
||||
const isNavSticky = isEnabled && (
|
||||
hasScrolledPastNav ||
|
||||
scrollDirection === 'up'
|
||||
);
|
||||
|
||||
const shouldHideStickyNav =
|
||||
isNavSticky &&
|
||||
scrollDirection === 'down';
|
||||
|
||||
const shouldAnimateStickyNav =
|
||||
isNavSticky &&
|
||||
lastScrollY > navHeight * 2 ||
|
||||
scrollDirection === 'up';
|
||||
|
||||
return {
|
||||
isNavSticky,
|
||||
shouldHideStickyNav,
|
||||
shouldAnimateStickyNav,
|
||||
};
|
||||
};
|
||||
21
src/utility/useScrollDirection.ts
Normal file
21
src/utility/useScrollDirection.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function useScrollDirection() {
|
||||
const [lastScrollY, setLastScrollY] = useState(0);
|
||||
const [scrollDirection, setScrollDirection] = useState<'up' | 'down'>('up');
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setScrollDirection(window.scrollY > lastScrollY ? 'down' : 'up');
|
||||
setLastScrollY(window.scrollY);
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, [lastScrollY]);
|
||||
|
||||
return {
|
||||
scrollDirection,
|
||||
lastScrollY,
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user