Split link status/loader into two components
This commit is contained in:
parent
b65136faa3
commit
ad11ce32b0
@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import LinkWithLoader from '@/components/LinkWithLoader';
|
||||
import LinkWithStatus from '@/components/LinkWithStatus';
|
||||
import Note from '@/components/Note';
|
||||
import SiteGrid from '@/components/SiteGrid';
|
||||
@ -73,11 +74,11 @@ export default function AdminNavClient({
|
||||
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-0.5"
|
||||
prefetch={false}
|
||||
>
|
||||
<span>{label}</span>
|
||||
@ -85,19 +86,19 @@ export default function AdminNavClient({
|
||||
<span>({count})</span>}
|
||||
</LinkWithStatus>)}
|
||||
</div>
|
||||
<LinkWithStatus
|
||||
<LinkWithLoader
|
||||
href={PATH_ADMIN_CONFIGURATION}
|
||||
className={isPathAdminConfiguration(pathname)
|
||||
? 'font-bold'
|
||||
: 'text-dim'}
|
||||
loadingElement={<Spinner />}
|
||||
loader={<Spinner />}
|
||||
>
|
||||
<BiCog
|
||||
size={18}
|
||||
className="inline-flex translate-y-0.5"
|
||||
aria-label="App Configuration"
|
||||
/>
|
||||
</LinkWithStatus>
|
||||
</LinkWithLoader>
|
||||
</div>
|
||||
{shouldShowBanner &&
|
||||
<Note icon={<FaRegClock className="flex-shrink-0" />}>
|
||||
|
||||
31
src/components/LinkWithLoader.tsx
Normal file
31
src/components/LinkWithLoader.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import { ComponentProps, ReactNode } from 'react';
|
||||
import LinkWithStatus from './LinkWithStatus';
|
||||
import clsx from 'clsx/lite';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function LinkWithLoader({
|
||||
loader,
|
||||
children,
|
||||
...props
|
||||
}: ComponentProps<typeof Link> & {
|
||||
loader: ReactNode
|
||||
}) {
|
||||
return (
|
||||
<LinkWithStatus {...props}>
|
||||
{({ isLoading }) => <>
|
||||
<span className={clsx(
|
||||
'flex transition-opacity',
|
||||
isLoading ? 'opacity-0' : 'opacity-100',
|
||||
)}>
|
||||
{children}
|
||||
</span>
|
||||
{isLoading && <span className={clsx(
|
||||
'absolute inset-0',
|
||||
'flex items-center justify-center',
|
||||
)}>
|
||||
{loader}
|
||||
</span>}
|
||||
</>}
|
||||
</LinkWithStatus>
|
||||
);
|
||||
}
|
||||
@ -20,18 +20,14 @@ const MAX_LOADING_DURATION = 15_000;
|
||||
export type LinkWithStatusProps = Omit<
|
||||
ComponentProps<typeof Link>, 'children'
|
||||
> & {
|
||||
loadingElement?: ReactNode
|
||||
loadingClassName?: string
|
||||
contentClassName?: string
|
||||
children: ReactNode | ((props: {
|
||||
isLoading: boolean
|
||||
}) => ReactNode)
|
||||
}
|
||||
|
||||
export default function LinkWithStatus({
|
||||
loadingElement,
|
||||
loadingClassName,
|
||||
contentClassName,
|
||||
href,
|
||||
className,
|
||||
onClick,
|
||||
@ -93,7 +89,10 @@ export default function LinkWithStatus({
|
||||
{...props }
|
||||
href={href}
|
||||
className={clsx(
|
||||
'relative transition-colors',
|
||||
'relative flex transition-[colors,opacity]',
|
||||
(loadingClassName || isControlled)
|
||||
? 'opacity-100'
|
||||
: isLoading ? 'opacity-50' : 'opacity-100',
|
||||
className,
|
||||
isLoading && loadingClassName,
|
||||
)}
|
||||
@ -116,24 +115,8 @@ export default function LinkWithStatus({
|
||||
onClick?.(e);
|
||||
}}
|
||||
>
|
||||
<span className={clsx(
|
||||
'flex transition-opacity',
|
||||
contentClassName,
|
||||
loadingElement
|
||||
? isLoading ? 'opacity-0' : 'opacity-100'
|
||||
: (loadingClassName || isControlled)
|
||||
? 'opacity-100'
|
||||
: isLoading ? 'opacity-50' : 'opacity-100',
|
||||
)}>
|
||||
{typeof children === 'function'
|
||||
? children({ isLoading })
|
||||
: children}
|
||||
</span>
|
||||
{isLoading && loadingElement && <span className={clsx(
|
||||
'absolute inset-0',
|
||||
'flex items-center justify-center',
|
||||
)}>
|
||||
{loadingElement}
|
||||
</span>}
|
||||
</Link>;
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { clsx } from 'clsx/lite';
|
||||
import { SHOULD_PREFETCH_ALL_LINKS } from '@/site/config';
|
||||
import { JSX, ReactNode } from 'react';
|
||||
import LinkWithStatus from './LinkWithStatus';
|
||||
import { JSX } from 'react';
|
||||
import Spinner from './Spinner';
|
||||
import LinkWithLoader from './LinkWithLoader';
|
||||
|
||||
export default function SwitcherItem({
|
||||
icon,
|
||||
@ -37,23 +37,23 @@ export default function SwitcherItem({
|
||||
: 'hover:text-gray-700 dark:hover:text-gray-400',
|
||||
);
|
||||
|
||||
const renderContent = (content: ReactNode) => noPadding
|
||||
? content
|
||||
const renderIcon = () => noPadding
|
||||
? icon
|
||||
: <div className="w-[28px] h-[24px] flex items-center justify-center">
|
||||
{content}
|
||||
{icon}
|
||||
</div>;
|
||||
|
||||
return (
|
||||
href
|
||||
? <LinkWithStatus {...{
|
||||
? <LinkWithLoader {...{
|
||||
title,
|
||||
href,
|
||||
className,
|
||||
prefetch,
|
||||
loadingElement: <Spinner />,
|
||||
loader: <Spinner />,
|
||||
}}>
|
||||
{renderContent(icon)}
|
||||
</LinkWithStatus>
|
||||
: <div {...{ title, onClick, className }}>{renderContent(icon)}</div>
|
||||
{renderIcon()}
|
||||
</LinkWithLoader>
|
||||
: <div {...{ title, onClick, className }}>{renderIcon()}</div>
|
||||
);
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user