'use client'; import { useEffect, useState } from 'react'; import dynamic from 'next/dynamic'; import { ExternalLink, ZoomIn } from 'lucide-react'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { apiFetch } from '@/lib/api/client'; // yet-another-react-lightbox is ~50kb, lazy-load it. const Lightbox = dynamic(() => import('yet-another-react-lightbox'), { ssr: false }); import 'yet-another-react-lightbox/styles.css'; // pdfjs-dist is ~150kb gzip — lazy-load so routes that never preview // PDFs don't ship it. ssr:false because the worker setup needs window. const PdfViewer = dynamic(() => import('./pdf-viewer').then((m) => ({ default: m.PdfViewer })), { ssr: false, loading: () => (
Loading PDF viewer…
), }); interface FilePreviewDialogProps { open: boolean; onOpenChange: (open: boolean) => void; fileId?: string; fileName?: string; mimeType?: string; } export function FilePreviewDialog({ open, onOpenChange, fileId, fileName, mimeType, }: FilePreviewDialogProps) { const [previewUrl, setPreviewUrl] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [lightboxOpen, setLightboxOpen] = useState(false); useEffect(() => { if (!open || !fileId) { setPreviewUrl(null); setError(null); return; } setLoading(true); setError(null); apiFetch<{ data: { url: string } }>(`/api/v1/files/${fileId}/preview`) .then((res) => { setPreviewUrl(res.data.url); }) .catch(() => { setError('Failed to load preview'); }) .finally(() => { setLoading(false); }); }, [open, fileId]); const isImage = mimeType?.startsWith('image/'); const isPdf = mimeType === 'application/pdf'; return ( {fileName ?? 'Preview'} {previewUrl && ( )}
{loading && (
Loading preview...
)} {error && (
{error}
)} {!loading && !error && previewUrl && isImage && ( )} {!loading && !error && previewUrl && isPdf && ( )}
{/* Lightbox renders OUTSIDE the parent Dialog so the dialog's own * bounds don't clip the fullscreen overlay. yet-another-react- * lightbox handles zoom/pan/keyboard nav out of the box. */} {previewUrl && isImage && ( setLightboxOpen(false)} slides={[{ src: previewUrl, alt: fileName ?? 'Preview' }]} controller={{ closeOnBackdropClick: true }} carousel={{ finite: true }} /> )}
); }