* Highlight /about in nav * Refine full frame icon * Add timestamp to /about * Add /about to cmdk menu * Enrich /about content * Make /about categories responsive * Enlarge app nav buttons * Add /about richer categories * Widen main nav buttons * Add more /about category content * Catch db errors in /about * Update key /about image * Add /about avatar * Add jest TextEncoder polyfill * Refactor sidebar text configuration * Show /about hero photo meta * Hoist about content to server page * Hide admin email on small screens * Add basic about page form * Finalize basic /about upsert functionality * Make /about/edit safe for blank templates * Add configuration to hide /about page * Add default /about title text * Add interactive photos to /about edit form * Apply final /about i18n * Ensure /about static optimization * Add CTA for admins to add /about descriptions * Add convenience for accepting full photo urls * Add photo placeholder icon * Show /about empty state when there are no photos * Hide sort control when in app empty state
124 lines
3.4 KiB
TypeScript
124 lines
3.4 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,
|
|
isPathAbout,
|
|
isPathAdmin,
|
|
isPathFull,
|
|
isPathGrid,
|
|
isPathProtected,
|
|
isPathSignIn,
|
|
} from '@/app/path';
|
|
import AnimateItems from '../components/AnimateItems';
|
|
import {
|
|
GRID_HOMEPAGE_ENABLED,
|
|
NAV_CAPTION,
|
|
} from './config';
|
|
import { useRef } from 'react';
|
|
import useStickyNav from './useStickyNav';
|
|
import { useAppState } from '@/app/AppState';
|
|
|
|
const NAV_HEIGHT_CLASS = NAV_CAPTION
|
|
? 'min-h-[4rem] sm:min-h-[5rem]'
|
|
: 'min-h-[4rem]';
|
|
|
|
export default function NavClient({
|
|
navTitle,
|
|
navCaption,
|
|
isInEmptyState,
|
|
}: {
|
|
navTitle: string
|
|
navCaption?: string
|
|
isInEmptyState: boolean
|
|
}) {
|
|
const ref = useRef<HTMLElement>(null);
|
|
|
|
const pathname = usePathname();
|
|
const showNav = !isPathSignIn(pathname);
|
|
|
|
const {
|
|
hasLoadedWithAnimations,
|
|
} = useAppState();
|
|
|
|
const {
|
|
classNameStickyContainer,
|
|
classNameStickyNav,
|
|
isNavVisible,
|
|
} = useStickyNav(ref, !isPathAdmin(pathname));
|
|
|
|
const renderLink = (
|
|
text: string,
|
|
linkOrAction: string | (() => void),
|
|
) =>
|
|
typeof linkOrAction === 'string'
|
|
? <Link href={linkOrAction}>{text}</Link>
|
|
: <button onClick={linkOrAction} type="button">{text}</button>;
|
|
|
|
const switcherSelectionForPath = (): SwitcherSelection | undefined => {
|
|
if (pathname === PATH_ROOT) {
|
|
return GRID_HOMEPAGE_ENABLED ? 'grid' : 'full';
|
|
} else if (isPathGrid(pathname)) {
|
|
return 'grid';
|
|
} else if (isPathFull(pathname)) {
|
|
return 'full';
|
|
} else if (isPathAbout(pathname)) {
|
|
return 'about';
|
|
} else if (isPathProtected(pathname)) {
|
|
return 'admin';
|
|
}
|
|
};
|
|
|
|
return (
|
|
<AppGrid
|
|
className={classNameStickyContainer}
|
|
classNameMain='pointer-events-auto'
|
|
contentMain={
|
|
<AnimateItems
|
|
animateOnFirstLoadOnly
|
|
type={!isInEmptyState && !isPathAdmin(pathname) ? 'bottom' : 'none'}
|
|
distanceOffset={10}
|
|
items={showNav
|
|
? [<nav
|
|
key="nav"
|
|
ref={ref}
|
|
className={clsx(
|
|
'w-full flex items-center gap-2 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}
|
|
hideSortControl={isInEmptyState}
|
|
/>
|
|
<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>]
|
|
: []}
|
|
/>
|
|
}
|
|
/>
|
|
);
|
|
};
|