Refine uploads layout
This commit is contained in:
parent
3bd6b20e76
commit
6ac797d5ac
@ -2,9 +2,10 @@ import { BiImageAdd } from 'react-icons/bi';
|
||||
import PathLoaderButton from '@/components/primitives/PathLoaderButton';
|
||||
import { ComponentProps } from 'react';
|
||||
|
||||
export default function AddButton(
|
||||
props: ComponentProps<typeof PathLoaderButton>,
|
||||
) {
|
||||
export default function AddButton({
|
||||
children,
|
||||
...props
|
||||
}: ComponentProps<typeof PathLoaderButton>) {
|
||||
return (
|
||||
<PathLoaderButton
|
||||
{...props}
|
||||
@ -13,7 +14,7 @@ export default function AddButton(
|
||||
className="translate-x-[1px] translate-y-[1px]"
|
||||
/>}
|
||||
>
|
||||
Add
|
||||
{children || 'Add'}
|
||||
</PathLoaderButton>
|
||||
);
|
||||
}
|
||||
|
||||
@ -149,6 +149,7 @@ export default function AdminBatchUploadActions({
|
||||
onChange={setTags}
|
||||
onError={setTagErrorMessage}
|
||||
readOnly={isAdding}
|
||||
className="relative z-10"
|
||||
/>
|
||||
<div className="flex gap-8">
|
||||
<FieldsetFavs
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { UrlAddStatus } from './AdminUploadsClient';
|
||||
import AdminUploadsTableRow from './AdminUploadsTableRow';
|
||||
|
||||
@ -12,23 +13,24 @@ export default function AdminUploadsTable({
|
||||
}: {
|
||||
isAdding?: boolean
|
||||
urlAddStatuses: UrlAddStatus[]
|
||||
setUrlAddStatuses?: (urlAddStatuses: UrlAddStatus[]) => void
|
||||
setUrlAddStatuses?: Dispatch<SetStateAction<UrlAddStatus[]>>
|
||||
isDeleting?: boolean
|
||||
setIsDeleting?: (isDeleting: boolean) => void
|
||||
setIsDeleting?: Dispatch<SetStateAction<boolean>>
|
||||
}) {
|
||||
const isComplete = urlAddStatuses.every(({ status }) => status === 'added');
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{urlAddStatuses.map(status =>
|
||||
{urlAddStatuses.map((status, index) =>
|
||||
<AdminUploadsTableRow
|
||||
key={status.url}
|
||||
{...{
|
||||
...status,
|
||||
tabIndex: index + 1,
|
||||
shouldRedirectToAdminPhotosOnDelete: urlAddStatuses.length <= 1,
|
||||
isAdding,
|
||||
isDeleting,
|
||||
isComplete,
|
||||
setIsDeleting,
|
||||
urlAddStatuses,
|
||||
setUrlAddStatuses,
|
||||
}}
|
||||
/>,
|
||||
|
||||
@ -11,9 +11,10 @@ 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 { Dispatch, SetStateAction, useEffect, useRef } from 'react';
|
||||
import { isElementEntirelyInViewport } from '@/utility/dom';
|
||||
import FieldSetWithStatus from '@/components/FieldSetWithStatus';
|
||||
import EditButton from './EditButton';
|
||||
|
||||
export default function AdminUploadsTableRow({
|
||||
url,
|
||||
@ -22,19 +23,21 @@ export default function AdminUploadsTableRow({
|
||||
draftTitle = '',
|
||||
uploadedAt,
|
||||
size,
|
||||
tabIndex,
|
||||
shouldRedirectToAdminPhotosOnDelete,
|
||||
isAdding,
|
||||
isDeleting,
|
||||
isComplete,
|
||||
setIsDeleting,
|
||||
urlAddStatuses,
|
||||
setUrlAddStatuses,
|
||||
}: UrlAddStatus & {
|
||||
tabIndex: number
|
||||
shouldRedirectToAdminPhotosOnDelete: boolean
|
||||
isAdding?: boolean
|
||||
isDeleting?: boolean
|
||||
isComplete?: boolean
|
||||
setIsDeleting?: (isDeleting: boolean) => void
|
||||
urlAddStatuses: UrlAddStatus[]
|
||||
setUrlAddStatuses?: (urlAddStatuses: UrlAddStatus[]) => void
|
||||
setIsDeleting?: Dispatch<SetStateAction<boolean>>
|
||||
setUrlAddStatuses?: Dispatch<SetStateAction<UrlAddStatus[]>>
|
||||
}) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -75,38 +78,17 @@ export default function AdminUploadsTableRow({
|
||||
/>
|
||||
</div>
|
||||
<div className={clsx(
|
||||
'flex flex-col w-full self-start min-w-0',
|
||||
'flex self-stretch w-full min-w-0',
|
||||
'gap-2 sm:gap-3',
|
||||
'p-2 sm:p-3',
|
||||
)}>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className={clsx(
|
||||
'flex gap-2 sm:gap-3',
|
||||
'ml-0.5',
|
||||
)}>
|
||||
{isAdding || isComplete
|
||||
? status === 'added'
|
||||
? 'Added'
|
||||
: status === 'adding'
|
||||
? statusMessage ?? 'Adding ...'
|
||||
: 'Waiting'
|
||||
: <>
|
||||
{uploadedAt
|
||||
? <ResponsiveDate date={uploadedAt} length="medium" />
|
||||
: '—'}
|
||||
<div className="max-sm:hidden text-dim truncate">
|
||||
{size
|
||||
? `${size} ${extension}`
|
||||
: extension}
|
||||
</div>
|
||||
</>}
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 w-full">
|
||||
<div className="flex flex-col grow gap-3">
|
||||
<FieldSetWithStatus
|
||||
label="Title"
|
||||
className="[&_input]:min-h-9 [&_input]:px-2 [&_input]:py-0"
|
||||
value={draftTitle}
|
||||
onChange={titleUpdated => {
|
||||
setUrlAddStatuses?.(urlAddStatuses.map(status => ({
|
||||
setUrlAddStatuses?.(statuses => statuses.map(status => ({
|
||||
...status,
|
||||
draftTitle: status.url === url
|
||||
? titleUpdated
|
||||
@ -114,12 +96,11 @@ export default function AdminUploadsTableRow({
|
||||
})));
|
||||
}}
|
||||
placeholder="Title (optional)"
|
||||
tabIndex={urlAddStatuses
|
||||
.findIndex(status => status.url === url) + 1}
|
||||
tabIndex={tabIndex}
|
||||
readOnly={isAdding || isDeleting || isComplete || Boolean(status)}
|
||||
hideLabel
|
||||
/>
|
||||
</div>
|
||||
<span className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
{isAdding || isComplete
|
||||
? <>
|
||||
{status === 'added'
|
||||
@ -135,21 +116,54 @@ export default function AdminUploadsTableRow({
|
||||
path={pathForAdminUploadUrl(url)}
|
||||
disabled={isDeleting}
|
||||
hideTextOnMobile={false}
|
||||
tooltip="Add directly"
|
||||
/>
|
||||
<EditButton
|
||||
path={pathForAdminUploadUrl(url)}
|
||||
tooltip="Review photo details"
|
||||
hideText
|
||||
/>
|
||||
<DeleteBlobButton
|
||||
urls={[url]}
|
||||
shouldRedirectToAdminPhotos={urlAddStatuses.length <= 1}
|
||||
shouldRedirectToAdminPhotos={
|
||||
shouldRedirectToAdminPhotosOnDelete}
|
||||
onDeleteStart={() => setIsDeleting?.(true)}
|
||||
onDelete={() => {
|
||||
setIsDeleting?.(false);
|
||||
setUrlAddStatuses?.(urlAddStatuses
|
||||
.filter(({ url: urlToRemove }) =>
|
||||
urlToRemove !== url));
|
||||
setUrlAddStatuses?.(statuses => statuses
|
||||
.filter(({ url: urlToRemove }) => urlToRemove !== url));
|
||||
}}
|
||||
isLoading={isDeleting}
|
||||
tooltip="Delete upload"
|
||||
/>
|
||||
</>}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={clsx(
|
||||
'flex gap-2 sm:gap-3',
|
||||
'ml-0.5',
|
||||
)}>
|
||||
{isAdding || isComplete
|
||||
? status === 'added'
|
||||
? 'Added'
|
||||
: status === 'adding'
|
||||
? statusMessage ?? 'Adding ...'
|
||||
: 'Waiting'
|
||||
: <>
|
||||
{uploadedAt
|
||||
? <ResponsiveDate
|
||||
date={uploadedAt}
|
||||
titleLabel="UPLOADED AT"
|
||||
/>
|
||||
: '—'}
|
||||
<div className="max-sm:hidden text-dim truncate">
|
||||
{size
|
||||
? `${size} ${extension}`
|
||||
: extension}
|
||||
</div>
|
||||
</>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -12,7 +12,6 @@ export default function DeleteUploadButton({
|
||||
shouldRedirectToAdminPhotos,
|
||||
onDeleteStart,
|
||||
onDelete,
|
||||
hideTextOnMobile,
|
||||
children,
|
||||
isLoading,
|
||||
...props
|
||||
@ -50,7 +49,6 @@ export default function DeleteUploadButton({
|
||||
});
|
||||
}}
|
||||
isLoading={isLoading ?? isDeleting}
|
||||
hideTextOnMobile={hideTextOnMobile}
|
||||
>
|
||||
{children}
|
||||
</DeleteButton>
|
||||
|
||||
@ -1,17 +1,19 @@
|
||||
import IconEdit from '@/components/icons/IconEdit';
|
||||
import PathLoaderButton from '@/components/primitives/PathLoaderButton';
|
||||
import { ComponentProps } from 'react';
|
||||
|
||||
export default function EditButton ({
|
||||
path,
|
||||
children,
|
||||
...props
|
||||
}: {
|
||||
path: string,
|
||||
}) {
|
||||
hideText?: boolean
|
||||
} & ComponentProps<typeof PathLoaderButton>) {
|
||||
return (
|
||||
<PathLoaderButton
|
||||
path={path}
|
||||
{...props}
|
||||
icon={<IconEdit size={15} className="translate-y-[0.5px]" />}
|
||||
>
|
||||
Edit
|
||||
{children || 'Edit'}
|
||||
</PathLoaderButton>
|
||||
);
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ export default function LoaderButton({
|
||||
),
|
||||
styleAs === 'link' && 'hover:text-dim',
|
||||
styleAs === 'link-without-hover' && 'hover:text-main',
|
||||
'inline-flex items-center gap-2 self-start whitespace-nowrap',
|
||||
'inline-flex items-center gap-1.5 self-start whitespace-nowrap',
|
||||
primary && 'primary',
|
||||
hideFocusOutline && 'focus:outline-hidden',
|
||||
className,
|
||||
@ -88,7 +88,7 @@ export default function LoaderButton({
|
||||
size={14}
|
||||
color={spinnerColor}
|
||||
className={clsx(
|
||||
'translate-y-[1px]',
|
||||
'translate-y-[0.5px]',
|
||||
spinnerClassName,
|
||||
)}
|
||||
/>
|
||||
@ -96,7 +96,7 @@ export default function LoaderButton({
|
||||
</span>}
|
||||
{children && <span className={clsx(
|
||||
styleAs !== 'button' && isLoading && 'text-dim',
|
||||
hideTextOnMobile && icon !== undefined && 'hidden sm:inline-block',
|
||||
hideTextOnMobile && icon !== undefined && 'max-sm:hidden',
|
||||
)}>
|
||||
{children}
|
||||
</span>}
|
||||
|
||||
@ -11,6 +11,7 @@ export default function PathLoaderButton({
|
||||
shouldScroll = true,
|
||||
shouldReplace,
|
||||
isLoading,
|
||||
onClick,
|
||||
children,
|
||||
...props
|
||||
}: {
|
||||
@ -46,7 +47,8 @@ export default function PathLoaderButton({
|
||||
return (
|
||||
<LoaderButton
|
||||
{...props}
|
||||
onClick={() => {
|
||||
onClick={e => {
|
||||
onClick?.(e);
|
||||
startTransition(() => {
|
||||
if (shouldReplace) {
|
||||
router.replace(path, { scroll: shouldScroll });
|
||||
|
||||
Loading…
Reference in New Issue
Block a user