Add loading status to admin sub-menu

This commit is contained in:
Sam Becker 2025-01-19 15:09:24 -06:00
parent a96abdb6f0
commit 00b058c812
3 changed files with 35 additions and 20 deletions

View File

@ -1,7 +1,9 @@
'use client';
import LinkWithStatus from '@/components/LinkWithStatus';
import Note from '@/components/Note';
import SiteGrid from '@/components/SiteGrid';
import Spinner from '@/components/Spinner';
import {
PATH_ADMIN_CONFIGURATION,
checkPathPrefix,
@ -11,7 +13,6 @@ import {
import { useAppState } from '@/state/AppState';
import { clsx } from 'clsx/lite';
import { differenceInMinutes } from 'date-fns';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';
import { BiCog } from 'react-icons/bi';
@ -60,40 +61,43 @@ export default function AdminNavClient({
contentMain={
<div className="space-y-5">
<div className={clsx(
'flex gap-2 md:gap-4',
'border-b border-gray-200 dark:border-gray-800 pb-3',
'flex gap-2 pb-3',
'border-b border-gray-200 dark:border-gray-800',
)}>
<div className={clsx(
'flex gap-2 md:gap-4',
'flex gap-0.5 -mx-1',
'flex-grow overflow-x-auto',
)}>
{items.map(({ label, href, count }) =>
<Link
<LinkWithStatus
key={label}
href={href}
className={clsx(
'flex gap-0.5',
checkPathPrefix(pathname, href) ? 'font-bold' : 'text-dim',
'px-1 py-0.5 rounded-md',
)}
loadingClassName="bg-dim"
contentClassName="flex gap-1"
prefetch={false}
>
<span>{label}</span>
{count > 0 &&
<span>({count})</span>}
</Link>)}
</LinkWithStatus>)}
</div>
<Link
<LinkWithStatus
href={PATH_ADMIN_CONFIGURATION}
className={isPathAdminConfiguration(pathname)
? 'font-bold'
: 'text-dim'}
loadingElement={<Spinner />}
>
<BiCog
size={18}
className="inline-block"
className="inline-flex translate-y-0.5"
aria-label="App Configuration"
/>
</Link>
</LinkWithStatus>
</div>
{shouldShowBanner &&
<Note icon={<FaRegClock className="flex-shrink-0" />}>

View File

@ -14,15 +14,19 @@ import clsx from 'clsx/lite';
// Avoid showing spinner for too short a time
const FLICKER_THRESHOLD = 400;
// Clear loading status after 10 seconds of inactivity
const MAX_LOADING_DURATION = 10_000;
// Clear loading status after long duration of inactivity
const MAX_LOADING_DURATION = 15_000;
export type LinkWithStatusProps = ComponentProps<typeof Link> & {
loader?: ReactNode
loadingElement?: ReactNode
loadingClassName?: string
contentClassName?: string
}
export default function LinkWithStatus({
loader,
loadingElement,
loadingClassName,
contentClassName,
href,
className,
onClick,
@ -71,7 +75,11 @@ export default function LinkWithStatus({
return <Link
{...props }
href={href}
className={clsx('relative', className)}
className={clsx(
'relative transition-colors',
className,
isLoading && loadingClassName,
)}
onClick={e => {
const isOpeningNewTab = e.metaKey || e.ctrlKey;
if (!isVisitingLinkHref && !isOpeningNewTab) {
@ -92,17 +100,20 @@ export default function LinkWithStatus({
>
<span className={clsx(
'flex transition-opacity',
loader
contentClassName,
loadingElement
? isLoading ? 'opacity-0' : 'opacity-100'
: isLoading ? 'opacity-50' : 'opacity-100',
: loadingClassName
? 'opacity-100'
: isLoading ? 'opacity-50' : 'opacity-100',
)}>
{children}
</span>
{isLoading && loader && <span className={clsx(
{isLoading && loadingElement && <span className={clsx(
'absolute inset-0',
'flex items-center justify-center',
)}>
{loader}
{loadingElement}
</span>}
</Link>;
}

View File

@ -50,7 +50,7 @@ export default function SwitcherItem({
href,
className,
prefetch,
loader: <Spinner />,
loadingElement: <Spinner />,
}}>
{renderContent(icon)}
</LinkWithStatus>