Tech4biz-channel/Channel-Frontend/src/features/assets/components/SpreadsheetThumbnail.tsx

111 lines
3.3 KiB
TypeScript

import React, { useEffect, useState, useRef } from 'react';
import { axiosInstance } from '../../../services/axios';
import * as XLSX from 'xlsx';
interface SpreadsheetThumbnailProps {
url: string;
fallback: React.ReactNode;
}
export const SpreadsheetThumbnail: React.FC<SpreadsheetThumbnailProps> = ({ url, fallback }) => {
const parentRef = useRef<HTMLDivElement>(null);
const [html, setHtml] = useState<string>('');
const [scale, setScale] = useState(0.2);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(false);
useEffect(() => {
let isMounted = true;
axiosInstance.get(url, { responseType: 'arraybuffer' })
.then(res => {
if (!isMounted) return;
try {
const workbook = XLSX.read(res.data, { type: 'array' });
const sheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[sheetName];
const sheetHtml = XLSX.utils.sheet_to_html(worksheet, { header: '', footer: '' });
if (isMounted) {
setHtml(sheetHtml);
setLoading(false);
}
} catch (err) {
console.error('[SpreadsheetThumbnail] Render error:', err);
if (isMounted) {
setError(true);
setLoading(false);
}
}
})
.catch(err => {
console.error('[SpreadsheetThumbnail] Fetch error:', err);
if (isMounted) {
setError(true);
setLoading(false);
}
});
return () => {
isMounted = false;
};
}, [url]);
useEffect(() => {
if (!parentRef.current) return;
const updateScale = () => {
if (parentRef.current) {
const width = parentRef.current.offsetWidth || 280;
setScale(width / 900);
}
};
updateScale();
const observer = new ResizeObserver(updateScale);
observer.observe(parentRef.current);
return () => observer.disconnect();
}, []);
if (error) return <>{fallback}</>;
return (
<div ref={parentRef} className="w-full h-full relative overflow-hidden bg-white select-none pointer-events-none p-1">
{loading && (
<div className="absolute inset-0 flex items-center justify-center bg-slate-50 z-10">
<div className="w-5 h-5 rounded-full border-2 border-emerald-600 border-t-transparent animate-spin" />
</div>
)}
<div
className="excel-thumbnail-container absolute top-0 left-0 origin-top-left"
style={{
width: '900px',
height: '600px',
transform: `scale(${scale})`,
overflow: 'hidden',
}}
>
<style>{`
.excel-thumbnail-container table {
border-collapse: collapse;
width: 100%;
font-size: 10px;
font-family: monospace;
color: #0f172a;
}
.excel-thumbnail-container td {
border: 1px solid #cbd5e1;
padding: 4px 6px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #0f172a;
}
.excel-thumbnail-container tr:nth-child(even) {
background-color: #f8fafc;
}
`}</style>
<div dangerouslySetInnerHTML={{ __html: html }} />
</div>
</div>
);
};