Files
pdf/frontend/src/components/Thumbnail.tsx
T

139 lines
5.4 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { gatewayService } from '../lib/gatewayService';
interface ThumbnailProps {
documentId: string;
pageIndex: number;
onClick: () => void;
onDelete?: () => void;
onMoveUp?: () => void;
onMoveDown?: () => void;
totalPages?: number;
}
export const Thumbnail: React.FC<ThumbnailProps> = ({
documentId,
pageIndex,
onClick,
onDelete,
onMoveUp,
onMoveDown,
totalPages,
}) => {
const [imageUrl, setImageUrl] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let active = true;
const fetchThumbnail = async () => {
try {
setLoading(true);
// Request a lower resolution/zoom image for the thumbnail
const url = await gatewayService.renderPage({
documentId,
pageIndex,
zoom: 0.2, // Small zoom for thumbnail size
rotation: 0,
});
if (active) {
setImageUrl(url);
}
} catch (err) {
console.error(`Failed to load thumbnail for page ${pageIndex}`, err);
} finally {
if (active) setLoading(false);
}
};
fetchThumbnail();
return () => {
active = false;
};
}, [documentId, pageIndex]);
return (
<div className="thumbnail-card" onClick={onClick}>
<div className="thumbnail-preview relative group overflow-hidden bg-white hover:border-indigo-500 transition-colors">
{loading ? (
<div className="flex flex-col items-center justify-center h-full w-full bg-slate-900">
<div className="w-5 h-5 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin mb-2" />
<span className="thumbnail-label">Loading...</span>
</div>
) : imageUrl ? (
<>
<img
src={imageUrl}
alt={`Page ${pageIndex + 1}`}
className="w-full h-full object-cover"
/>
{/* Group Hover Overlay Controls */}
<div className="absolute inset-0 bg-slate-950/60 opacity-0 group-hover:opacity-100 transition-opacity flex flex-col justify-between p-2 pointer-events-auto">
<div className="flex justify-end">
{onDelete && totalPages && totalPages > 1 && (
<button
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
className="p-1 bg-rose-500 hover:bg-rose-600 text-white rounded shadow transition-colors cursor-pointer"
title="Delete Page"
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
)}
</div>
<div className="flex justify-between w-full">
{onMoveUp ? (
<button
onClick={(e) => {
e.stopPropagation();
onMoveUp();
}}
className="p-1 bg-indigo-600 hover:bg-indigo-500 text-white rounded shadow transition-colors cursor-pointer"
title="Move Page Up"
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M5 10l7-7m0 0l7 7m-7-7v18" />
</svg>
</button>
) : (
<div />
)}
{onMoveDown ? (
<button
onClick={(e) => {
e.stopPropagation();
onMoveDown();
}}
className="p-1 bg-indigo-600 hover:bg-indigo-500 text-white rounded shadow transition-colors cursor-pointer"
title="Move Page Down"
>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M19 14l-7 7m0 0l-7-7m7 7V3" />
</svg>
</button>
) : (
<div />
)}
</div>
</div>
</>
) : (
<div className="flex flex-col items-center justify-center h-full w-full">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" className="thumbnail-svg-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<span className="thumbnail-label text-rose-400">Error</span>
</div>
)}
</div>
<span className="thumbnail-label text-center mt-1">Page {pageIndex + 1}</span>
</div>
);
};