Vercel/src/app/Nav.tsx
2025-06-30 09:28:27 -05:00

119 lines
3.2 KiB
TypeScript

'use client';
import { clsx } from 'clsx/lite';
import { usePathname } from 'next/navigation';
import Link from 'next/link';
import AppGrid from '../components/AppGrid';
import AppViewSwitcher, { SwitcherSelection } from '@/app/AppViewSwitcher';
import {
PATH_ROOT,
isPathAdmin,
isPathFeed,
isPathGrid,
isPathProtected,
isPathSignIn,
} from '@/app/paths';
import AnimateItems from '../components/AnimateItems';
import {
GRID_HOMEPAGE_ENABLED,
NAV_CAPTION,
} from './config';
import { useRef } from 'react';
import useStickyNav from './useStickyNav';
import { useAppState } from '@/state/AppState';
const NAV_HEIGHT_CLASS = NAV_CAPTION
? 'min-h-[4rem] sm:min-h-[5rem]'
: 'min-h-[4rem]';
export default function Nav({
navTitle,
navCaption,
}: {
navTitle: string
navCaption?: string
}) {
const ref = useRef<HTMLElement>(null);
const pathname = usePathname();
const showNav = !isPathSignIn(pathname);
const {
hasLoadedWithAnimations,
} = useAppState();
const {
classNameStickyContainer,
classNameStickyNav,
isNavVisible,
} = useStickyNav(ref);
const renderLink = (
text: string,
linkOrAction: string | (() => void),
) =>
typeof linkOrAction === 'string'
? <Link href={linkOrAction}>{text}</Link>
: <button onClick={linkOrAction}>{text}</button>;
const switcherSelectionForPath = (): SwitcherSelection | undefined => {
if (pathname === PATH_ROOT) {
return GRID_HOMEPAGE_ENABLED ? 'grid' : 'feed';
} else if (isPathGrid(pathname)) {
return 'grid';
} else if (isPathFeed(pathname)) {
return 'feed';
} else if (isPathProtected(pathname)) {
return 'admin';
}
};
return (
<AppGrid
className={classNameStickyContainer}
classNameMain='pointer-events-auto'
contentMain={
<AnimateItems
animateOnFirstLoadOnly
type={!isPathAdmin(pathname) ? 'bottom' : 'none'}
distanceOffset={10}
items={showNav
? [<nav
key="nav"
ref={ref}
className={clsx(
'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]',
classNameStickyNav,
)}>
<AppViewSwitcher
currentSelection={switcherSelectionForPath()}
className="translate-x-[-1px]"
animate={hasLoadedWithAnimations && isNavVisible}
/>
<div className={clsx(
'grow text-right min-w-0',
'translate-y-[-1px]',
)}>
<div className="truncate overflow-hidden select-none">
{renderLink(navTitle, PATH_ROOT)}
</div>
{navCaption &&
<div className={clsx(
'hidden sm:block truncate overflow-hidden',
'leading-tight text-dim',
)}>
{navCaption}
</div>}
</div>
</nav>]
: []}
/>
}
sideHiddenOnMobile
/>
);
};