diff --git a/src/admin/AddButton.tsx b/src/admin/AddButton.tsx index b1bdede6..f776b6d7 100644 --- a/src/admin/AddButton.tsx +++ b/src/admin/AddButton.tsx @@ -1,23 +1,17 @@ -import Link from 'next/link'; import { BiImageAdd } from 'react-icons/bi'; +import PathLoaderButton from '@/components/PathLoaderButton'; -export default function AddButton ({ - href, - label = 'Add', +export default function AddButton({ + path, }: { - href: string, - label?: string, + path: string, }) { return ( - } > - - - {label} - - + Add + ); } diff --git a/src/admin/AdminPhotosTable.tsx b/src/admin/AdminPhotosTable.tsx index a0802161..e501c963 100644 --- a/src/admin/AdminPhotosTable.tsx +++ b/src/admin/AdminPhotosTable.tsx @@ -91,7 +91,9 @@ export default function AdminPhotosTable({ > } + icon={} onFormSubmitToastMessage={` "${titleForPhoto(photo)}" EXIF data synced `} diff --git a/src/admin/AdminUploadsTable.tsx b/src/admin/AdminUploadsTable.tsx index 316f7222..1427f594 100644 --- a/src/admin/AdminUploadsTable.tsx +++ b/src/admin/AdminUploadsTable.tsx @@ -49,7 +49,7 @@ export default function AdminUploadsTable({ 'flex flex-nowrap', 'gap-2 sm:gap-3 items-center', )}> - + } + icon={} onFormSubmit={invalidateSwr} > Clear Cache diff --git a/src/admin/DeleteButton.tsx b/src/admin/DeleteButton.tsx index 7c570903..7c048beb 100644 --- a/src/admin/DeleteButton.tsx +++ b/src/admin/DeleteButton.tsx @@ -14,6 +14,7 @@ export default function DeleteButton ( const { onFormSubmit: onFormSubmitProps, clearLocalState, + className, ...rest } = props; @@ -30,11 +31,13 @@ export default function DeleteButton ( return } + icon={} spinnerColor="text" className={clsx( - 'text-red-500 dark:text-red-600', + className, + '!text-red-500 dark:!text-red-600', 'active:!bg-red-100/50 active:dark:!bg-red-950/50', + 'disabled:!bg-red-100/50 disabled:dark:!bg-red-950/50', '!border-red-200 hover:!border-red-300', 'dark:!border-red-900/75 dark:hover:!border-red-900', )} diff --git a/src/admin/LoaderButton.tsx b/src/admin/LoaderButton.tsx new file mode 100644 index 00000000..b6c55329 --- /dev/null +++ b/src/admin/LoaderButton.tsx @@ -0,0 +1,54 @@ +import Spinner, { SpinnerColor } from '@/components/Spinner'; +import { clsx } from 'clsx/lite'; +import { ButtonHTMLAttributes, ReactNode } from 'react'; + +export default function LoaderButton(props: { + children?: ReactNode + isLoading?: boolean + icon?: JSX.Element + spinnerColor?: SpinnerColor + styleAsLink?: boolean +} & ButtonHTMLAttributes) { + const { + children, + isLoading, + icon, + spinnerColor, + styleAsLink, + type = 'button', + disabled, + className, + ...rest + } = props; + return ( + + ); +} diff --git a/src/components/PathLoaderButton.tsx b/src/components/PathLoaderButton.tsx new file mode 100644 index 00000000..ba121f4d --- /dev/null +++ b/src/components/PathLoaderButton.tsx @@ -0,0 +1,66 @@ +'use client'; + +import { useRouter } from 'next/navigation'; +import { ReactNode, useEffect, useState, useTransition } from 'react'; +import { SpinnerColor } from './Spinner'; +import LoaderButton from '@/admin/LoaderButton'; + +export default function PathLoaderButton({ + path, + icon, + prefetch, + loaderDelay = 100, + shouldScroll = true, + shouldReplace, + spinnerColor, + children, +}: { + path: string + icon?: JSX.Element + prefetch?: boolean + loaderDelay?: number + shouldScroll?: boolean + shouldReplace?: boolean + spinnerColor?: SpinnerColor + children?: ReactNode +}) { + const router = useRouter(); + + const [isPending, startTransition] = useTransition(); + + const [shouldShowLoader, setShouldShowLoader] = useState(false); + + useEffect(() => { + if (isPending) { + const timeout = setTimeout(() => { + setShouldShowLoader(true); + }, loaderDelay); + return () => clearTimeout(timeout); + } else { + setShouldShowLoader(false); + } + }, [isPending, loaderDelay]); + + useEffect(() => { + if (prefetch) { + router.prefetch(path); + } + }, [prefetch, router, path]); + + return ( + startTransition(() => { + if (shouldReplace) { + router.replace(path, { scroll: shouldScroll }); + } else { + router.push(path, { scroll: shouldScroll }); + } + })} + isLoading={shouldShowLoader} + spinnerColor={spinnerColor} + > + {children} + + ); +} diff --git a/src/components/SubmitButtonWithStatus.tsx b/src/components/SubmitButtonWithStatus.tsx index 514b138d..3cd6a248 100644 --- a/src/components/SubmitButtonWithStatus.tsx +++ b/src/components/SubmitButtonWithStatus.tsx @@ -2,9 +2,10 @@ import { HTMLProps, useEffect, useRef } from 'react'; import { useFormStatus } from 'react-dom'; -import Spinner, { SpinnerColor } from './Spinner'; +import { SpinnerColor } from './Spinner'; import { clsx } from 'clsx/lite'; import { toastSuccess } from '@/toast'; +import LoaderButton from '@/admin/LoaderButton'; interface Props extends HTMLProps { icon?: JSX.Element @@ -49,34 +50,21 @@ export default function SubmitButtonWithStatus({ }, [onFormStatusChange, pending]); return ( - + {children} + ); }; diff --git a/src/site/globals.css b/src/site/globals.css index 5052b267..f0fdf1a1 100644 --- a/src/site/globals.css +++ b/src/site/globals.css @@ -77,12 +77,13 @@ cursor-pointer hover:no-underline inline-flex gap-2 items-center - px-4 + px-3 text-base shadow-sm active:bg-gray-100 dark:active:bg-gray-900 hover:border-gray-300 dark:hover:border-gray-600 disabled:cursor-not-allowed + disabled:text-dim disabled:bg-gray-100 dark:disabled:bg-gray-900 disabled:border-gray-200 disabled:dark:border-gray-700 }