uchill/front_material/components/homework/PdfFirstPagePreview.tsx

97 lines
3.0 KiB
TypeScript

'use client';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
const PDF_PREVIEW_WIDTH = 200;
/**
* Превью первой страницы PDF для карточки файла задания.
* Рендерит только на клиенте (react-pdf использует pdf.js).
*/
export function PdfFirstPagePreview({ url, alt }: { url: string; alt?: string }) {
const [PdfComponents, setPdfComponents] = useState<{
Document: React.ComponentType<any>;
Page: React.ComponentType<any>;
pdfjs: { GlobalWorkerOptions: { workerSrc: string }; version: string };
} | null>(null);
const [error, setError] = useState(false);
useEffect(() => {
import('react-pdf').then((mod) => {
const { Document, Page, pdfjs } = mod;
if (pdfjs?.GlobalWorkerOptions) {
pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version || '4.0.379'}/build/pdf.worker.min.mjs`;
}
setPdfComponents({ Document, Page, pdfjs });
}).catch(() => setError(true));
}, []);
const fileOptionsRef = useRef<{ url: string; withCredentials: true } | null>(null);
if (fileOptionsRef.current === null || fileOptionsRef.current.url !== url) {
fileOptionsRef.current = { url, withCredentials: true as const };
}
const fileOptions = fileOptionsRef.current;
const onLoadError = useCallback(() => setError(true), []);
const loadingElement = useMemo(
() => (
<div style={{ padding: 24 }}>
<span className="material-symbols-outlined" style={{ fontSize: 32, color: 'var(--md-sys-color-on-surface-variant)' }}>hourglass_empty</span>
</div>
),
[]
);
const wrapperStyle = useMemo<React.CSSProperties>(
() => ({
width: PDF_PREVIEW_WIDTH,
minHeight: 120,
background: '#f5f5f5',
borderRadius: 10,
overflow: 'hidden' as const,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}),
[]
);
if (error || !PdfComponents) {
return (
<div
style={{
width: PDF_PREVIEW_WIDTH,
height: Math.round(PDF_PREVIEW_WIDTH * 1.41),
background: 'var(--md-sys-color-surface-variant)',
borderRadius: 10,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<span className="material-symbols-outlined" style={{ fontSize: 40, color: 'var(--md-sys-color-primary)' }}>picture_as_pdf</span>
</div>
);
}
const { Document, Page } = PdfComponents;
return (
<div style={wrapperStyle}>
<Document
file={fileOptions}
onLoadError={onLoadError}
loading={loadingElement}
>
<Page
pageNumber={1}
width={PDF_PREVIEW_WIDTH}
renderTextLayer={false}
renderAnnotationLayer={false}
/>
</Document>
</div>
);
}