Scroll photo into view when bulk adding
This commit is contained in:
parent
d971a71c5d
commit
8caa2b9245
@ -1,18 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import Spinner from '@/components/Spinner';
|
||||
import {
|
||||
getIdFromStorageUrl,
|
||||
getExtensionFromStorageUrl,
|
||||
} from '@/platforms/storage';
|
||||
import { clsx } from 'clsx/lite';
|
||||
import { FaRegCircleCheck } from 'react-icons/fa6';
|
||||
import { pathForAdminUploadUrl } from '@/app/paths';
|
||||
import AddButton from './AddButton';
|
||||
import { UrlAddStatus } from './AdminUploadsClient';
|
||||
import ResponsiveDate from '@/components/ResponsiveDate';
|
||||
import DeleteBlobButton from './DeleteUploadButton';
|
||||
import ImageMedium from '@/components/image/ImageMedium';
|
||||
import AdminUploadsTableRow from './AdminUploadsTableRow';
|
||||
|
||||
export default function AdminUploadsTable({
|
||||
isAdding,
|
||||
@ -28,89 +17,22 @@ export default function AdminUploadsTable({
|
||||
setIsDeleting?: (isDeleting: boolean) => void
|
||||
}) {
|
||||
const isComplete = urlAddStatuses.every(({ status }) => status === 'added');
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{urlAddStatuses.map(({ url, status, statusMessage, uploadedAt, size }) =>
|
||||
<div
|
||||
key={url}
|
||||
className={clsx(
|
||||
'flex items-center grow',
|
||||
'transition-opacity',
|
||||
'rounded-md overflow-hidden',
|
||||
'border-medium bg-extra-dim',
|
||||
isAdding && !isComplete && status !== 'adding' && 'opacity-30',
|
||||
)}
|
||||
>
|
||||
<div className={clsx(
|
||||
'w-[50%] sm:w-auto shrink-0 bg-dim',
|
||||
'transition-transform',
|
||||
)}>
|
||||
<ImageMedium
|
||||
title={getIdFromStorageUrl(url)}
|
||||
src={url}
|
||||
alt={url}
|
||||
aspectRatio={3.0 / 2.0}
|
||||
/>
|
||||
</div>
|
||||
<div className={clsx(
|
||||
'flex flex-col w-full self-start',
|
||||
'gap-2 sm:gap-4',
|
||||
'p-2.5 pl-3',
|
||||
'sm:p-4 sm:pl-6',
|
||||
)}>
|
||||
<div className="flex flex-col gap-0.5 h-full">
|
||||
<div className="truncate font-medium">
|
||||
{uploadedAt
|
||||
? <ResponsiveDate date={uploadedAt} />
|
||||
: '—'}
|
||||
</div>
|
||||
<div className="text-dim overflow-hidden text-ellipsis">
|
||||
{isAdding || isComplete
|
||||
? status === 'added'
|
||||
? 'Added'
|
||||
: status === 'adding'
|
||||
? statusMessage ?? 'Adding ...'
|
||||
: 'Waiting'
|
||||
: size
|
||||
// eslint-disable-next-line max-len
|
||||
? `${size} ${getExtensionFromStorageUrl(url)?.toUpperCase()}`
|
||||
: getExtensionFromStorageUrl(url)?.toUpperCase()}
|
||||
</div>
|
||||
</div>
|
||||
<span className="flex items-center gap-2">
|
||||
{isAdding || isComplete
|
||||
? <>
|
||||
{status === 'added'
|
||||
? <FaRegCircleCheck size={18} />
|
||||
: status === 'adding' &&
|
||||
<Spinner
|
||||
size={19}
|
||||
className="translate-y-[2px]"
|
||||
/>}
|
||||
</>
|
||||
: <>
|
||||
<AddButton
|
||||
path={pathForAdminUploadUrl(url)}
|
||||
disabled={isDeleting}
|
||||
hideTextOnMobile={false}
|
||||
/>
|
||||
<DeleteBlobButton
|
||||
urls={[url]}
|
||||
shouldRedirectToAdminPhotos={urlAddStatuses.length <= 1}
|
||||
onDeleteStart={() => setIsDeleting?.(true)}
|
||||
onDelete={() => {
|
||||
setIsDeleting?.(false);
|
||||
setUrlAddStatuses?.(urlAddStatuses
|
||||
.filter(({ url: urlToRemove }) =>
|
||||
urlToRemove !== url));
|
||||
}}
|
||||
isLoading={isDeleting}
|
||||
/>
|
||||
</>}
|
||||
</span>
|
||||
</div>
|
||||
</div>)}
|
||||
{urlAddStatuses.map(status =>
|
||||
<AdminUploadsTableRow
|
||||
key={status.url}
|
||||
{...{
|
||||
...status,
|
||||
isAdding,
|
||||
isDeleting,
|
||||
isComplete,
|
||||
setIsDeleting,
|
||||
urlAddStatuses,
|
||||
setUrlAddStatuses,
|
||||
}}
|
||||
/>,
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
131
src/admin/AdminUploadsTableRow.tsx
Normal file
131
src/admin/AdminUploadsTableRow.tsx
Normal file
@ -0,0 +1,131 @@
|
||||
import ImageMedium from '@/components/image/ImageMedium';
|
||||
import { UrlAddStatus } from './AdminUploadsClient';
|
||||
import {
|
||||
getExtensionFromStorageUrl,
|
||||
getIdFromStorageUrl,
|
||||
} from '@/platforms/storage';
|
||||
import clsx from 'clsx/lite';
|
||||
import ResponsiveDate from '@/components/ResponsiveDate';
|
||||
import Spinner from '@/components/Spinner';
|
||||
import { FaRegCircleCheck } from 'react-icons/fa6';
|
||||
import AddButton from './AddButton';
|
||||
import { pathForAdminUploadUrl } from '@/app/paths';
|
||||
import DeleteBlobButton from './DeleteUploadButton';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { isElementEntirelyInViewport } from '@/utility/dom';
|
||||
|
||||
export default function AdminUploadsTableRow({
|
||||
url,
|
||||
status,
|
||||
statusMessage,
|
||||
uploadedAt,
|
||||
size,
|
||||
isAdding,
|
||||
isDeleting,
|
||||
isComplete,
|
||||
setIsDeleting,
|
||||
urlAddStatuses,
|
||||
setUrlAddStatuses,
|
||||
}: UrlAddStatus & {
|
||||
isAdding?: boolean
|
||||
isDeleting?: boolean
|
||||
isComplete?: boolean
|
||||
setIsDeleting?: (isDeleting: boolean) => void
|
||||
urlAddStatuses: UrlAddStatus[]
|
||||
setUrlAddStatuses?: (urlAddStatuses: UrlAddStatus[]) => void
|
||||
}) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
status === 'adding' &&
|
||||
!isElementEntirelyInViewport(ref.current)
|
||||
) {
|
||||
window.scrollTo({
|
||||
top: (ref.current?.offsetTop ?? 0) - 16,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={clsx(
|
||||
'flex items-center grow',
|
||||
'transition-opacity',
|
||||
'rounded-md overflow-hidden',
|
||||
'border-medium bg-extra-dim',
|
||||
isAdding && !isComplete && status !== 'adding' && 'opacity-30',
|
||||
)}
|
||||
>
|
||||
<div className={clsx(
|
||||
'w-[50%] sm:w-auto shrink-0 bg-dim',
|
||||
'transition-transform',
|
||||
)}>
|
||||
<ImageMedium
|
||||
title={getIdFromStorageUrl(url)}
|
||||
src={url}
|
||||
alt={url}
|
||||
aspectRatio={3.0 / 2.0}
|
||||
/>
|
||||
</div>
|
||||
<div className={clsx(
|
||||
'flex flex-col w-full self-start',
|
||||
'gap-2 sm:gap-4',
|
||||
'p-2.5 pl-3',
|
||||
'sm:p-4 sm:pl-6',
|
||||
)}>
|
||||
<div className="flex flex-col gap-0.5 h-full">
|
||||
<div className="truncate font-medium">
|
||||
{uploadedAt
|
||||
? <ResponsiveDate date={uploadedAt} />
|
||||
: '—'}
|
||||
</div>
|
||||
<div className="text-dim overflow-hidden text-ellipsis">
|
||||
{isAdding || isComplete
|
||||
? status === 'added'
|
||||
? 'Added'
|
||||
: status === 'adding'
|
||||
? statusMessage ?? 'Adding ...'
|
||||
: 'Waiting'
|
||||
: size
|
||||
? `${size} ${getExtensionFromStorageUrl(url)?.toUpperCase()}`
|
||||
: getExtensionFromStorageUrl(url)?.toUpperCase()}
|
||||
</div>
|
||||
</div>
|
||||
<span className="flex items-center gap-2">
|
||||
{isAdding || isComplete
|
||||
? <>
|
||||
{status === 'added'
|
||||
? <FaRegCircleCheck size={18} />
|
||||
: status === 'adding' &&
|
||||
<Spinner
|
||||
size={19}
|
||||
className="translate-y-[2px]"
|
||||
/>}
|
||||
</>
|
||||
: <>
|
||||
<AddButton
|
||||
path={pathForAdminUploadUrl(url)}
|
||||
disabled={isDeleting}
|
||||
hideTextOnMobile={false}
|
||||
/>
|
||||
<DeleteBlobButton
|
||||
urls={[url]}
|
||||
shouldRedirectToAdminPhotos={urlAddStatuses.length <= 1}
|
||||
onDeleteStart={() => setIsDeleting?.(true)}
|
||||
onDelete={() => {
|
||||
setIsDeleting?.(false);
|
||||
setUrlAddStatuses?.(urlAddStatuses
|
||||
.filter(({ url: urlToRemove }) =>
|
||||
urlToRemove !== url));
|
||||
}}
|
||||
isLoading={isDeleting}
|
||||
/>
|
||||
</>}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user