194 lines
5.0 KiB
TypeScript
194 lines
5.0 KiB
TypeScript
import { useEffect } from 'react';
|
|
import type { JSX } from 'react';
|
|
import { ArrowLeftIcon, ArrowRightIcon } from '../ui/icons';
|
|
import { useAssetNavigator } from '../hooks/useAssetNavigator';
|
|
import { preventAll } from './events';
|
|
|
|
type Identifier = string | number;
|
|
|
|
interface DocumentLike {
|
|
id?: Identifier;
|
|
title?: string;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
interface AssetLike {
|
|
id?: Identifier;
|
|
cardinality?: number;
|
|
url?: string | null;
|
|
metadata?: Record<string, unknown> | null;
|
|
objects?: Array<Record<string, unknown>>;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
type EnsureAssetUrl = (
|
|
documentId: Identifier,
|
|
asset: AssetLike,
|
|
options?: { start?: number; limit?: number; [key: string]: unknown },
|
|
) => Promise<unknown>;
|
|
|
|
type GetDocumentAsset = (document: DocumentLike | null, assetType: string) => AssetLike | null;
|
|
|
|
interface NavigatorSnapshot {
|
|
url: string | null;
|
|
alt?: string;
|
|
canGoPrev: boolean;
|
|
canGoNext: boolean;
|
|
goPrev?: () => void;
|
|
goNext?: () => void;
|
|
ordinal: number;
|
|
width: number | null;
|
|
height: number | null;
|
|
}
|
|
|
|
interface DesktopPreviewCardProps {
|
|
doc: DocumentLike | null;
|
|
title?: string;
|
|
ensureAssetUrl?: EnsureAssetUrl | null;
|
|
getDocumentAsset: GetDocumentAsset;
|
|
prefetch?: number;
|
|
onNavigatorSnapshot?: (docId: Identifier, snapshot: NavigatorSnapshot | null) => void;
|
|
shouldLoad?: boolean;
|
|
}
|
|
|
|
const DesktopPreviewCard = ({
|
|
doc,
|
|
title,
|
|
ensureAssetUrl,
|
|
getDocumentAsset,
|
|
prefetch = 3,
|
|
onNavigatorSnapshot,
|
|
shouldLoad = true,
|
|
}: DesktopPreviewCardProps): JSX.Element => {
|
|
const navigator = useAssetNavigator({
|
|
document: doc,
|
|
assetType: 'thumbnail',
|
|
ensureAssetUrl: shouldLoad ? ensureAssetUrl : null,
|
|
getAsset: getDocumentAsset,
|
|
prefetch,
|
|
});
|
|
|
|
const { currentUrl, cardinality, canGoPrev, canGoNext, currentMetadata, ordinal } = navigator;
|
|
const docId = doc?.id ?? null;
|
|
|
|
const metadataWidth = Number((currentMetadata as { width?: number } | null)?.width);
|
|
const metadataHeight = Number((currentMetadata as { height?: number } | null)?.height);
|
|
|
|
useEffect(() => {
|
|
if (!onNavigatorSnapshot || !docId) {
|
|
return undefined;
|
|
}
|
|
const snapshot = {
|
|
url: currentUrl || null,
|
|
alt: title,
|
|
canGoPrev,
|
|
canGoNext,
|
|
goPrev: navigator.goPrev,
|
|
goNext: navigator.goNext,
|
|
ordinal,
|
|
width: Number.isFinite(metadataWidth) && metadataWidth > 0 ? metadataWidth : null,
|
|
height: Number.isFinite(metadataHeight) && metadataHeight > 0 ? metadataHeight : null,
|
|
};
|
|
onNavigatorSnapshot(docId, snapshot);
|
|
return () => onNavigatorSnapshot(docId, null);
|
|
}, [
|
|
docId,
|
|
currentUrl,
|
|
title,
|
|
canGoPrev,
|
|
canGoNext,
|
|
ordinal,
|
|
metadataWidth,
|
|
metadataHeight,
|
|
navigator.goPrev,
|
|
navigator.goNext,
|
|
onNavigatorSnapshot,
|
|
]);
|
|
|
|
const hasPreview = Boolean(currentUrl);
|
|
const cardClasses = ['desk-item__card'];
|
|
if (!hasPreview) cardClasses.push('desk-item__card--empty');
|
|
const showNav = hasPreview && (cardinality > 1 || canGoPrev || canGoNext);
|
|
|
|
return (
|
|
<div
|
|
className={cardClasses.join(' ')}
|
|
onDragStart={(event) => {
|
|
if (event instanceof DragEvent) {
|
|
event.preventDefault();
|
|
}
|
|
}}
|
|
>
|
|
{hasPreview ? (
|
|
<img
|
|
src={currentUrl}
|
|
alt={title}
|
|
draggable={false}
|
|
onDragStart={(event) => event.preventDefault()}
|
|
/>
|
|
) : (
|
|
<div className="desk-item__empty">
|
|
<div className="desk-item__placeholder">DOC</div>
|
|
<div className="desk-item__title" title={title}>
|
|
{title}
|
|
</div>
|
|
</div>
|
|
)}
|
|
{showNav ? (
|
|
<div className="desk-card__nav">
|
|
<button
|
|
type="button"
|
|
className="desk-card__nav-button"
|
|
onClick={(event) => {
|
|
preventAll(event);
|
|
navigator.goPrev();
|
|
}}
|
|
onPointerDown={(event) => {
|
|
preventAll(event);
|
|
}}
|
|
onPointerUp={(event) => {
|
|
preventAll(event);
|
|
}}
|
|
onMouseDown={(event) => {
|
|
preventAll(event);
|
|
}}
|
|
onMouseUp={(event) => {
|
|
preventAll(event);
|
|
}}
|
|
disabled={!canGoPrev}
|
|
aria-label="Previous preview"
|
|
>
|
|
<ArrowLeftIcon />
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="desk-card__nav-button"
|
|
onClick={(event) => {
|
|
preventAll(event);
|
|
navigator.goNext();
|
|
}}
|
|
onPointerDown={(event) => {
|
|
preventAll(event);
|
|
}}
|
|
onPointerUp={(event) => {
|
|
preventAll(event);
|
|
}}
|
|
onMouseDown={(event) => {
|
|
preventAll(event);
|
|
}}
|
|
onMouseUp={(event) => {
|
|
preventAll(event);
|
|
}}
|
|
disabled={!canGoNext}
|
|
aria-label="Next preview"
|
|
>
|
|
<ArrowRightIcon />
|
|
</button>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DesktopPreviewCard;
|