1const e=`import { useEffect, useState } from "react"; 2import { Loader2 } from "lucide-react"; 3import DOMPurify from "dompurify"; 4 5interface Props { 6 data: ArrayBuffer | null; 7 ext: string; 8 className?: string; 9 fallback?: React.ReactNode; 10} 11 12type Sheet = { name: string; rows: string[][] }; 13 14export default function OfficeDocViewer({ data, ext, className, fallback }: Props) { 15 const [status, setStatus] = useState<"loading" | "ready" | "error">("loading"); 16 const [sheets, setSheets] = useState<Sheet[]>([]); 17 const [html, setHtml] = useState<string | null>(null); 18 const [text, setText] = useState<string | null>(null); 19 const [active, setActive] = useState(0); 20 21 useEffect(() => { 22 if (!data) return; 23 let cancelled = false; 24 // Reset all content state first so a new document never renders stale 25 // sheets/html/text from the previously viewed document. 26 setSheets([]); 27 setHtml(null); 28 setText(null); 29 setActive(0); 30 setStatus("loading"); 31 (async () => { 32 try { 33 if (ext === "xlsx" || ext === "xls" || ext === "csv") { 34 const XLSX = await import("xlsx"); 35 const wb = XLSX.read(new Uint8Array(data), { type: "array" }); 36 const out = wb.SheetNames.map((name) => ({ 37 name, 38 rows: (XLSX.utils.sheet_to_json(wb.Sheets[name], { header: 1, blankrows: false, raw: false }) as unknown[][]) 39 .map((r) => r.map((c) => (c == null ? "" : String(c)))), 40 })); 41 if (!cancelled) setSheets(out); 42 } else if (ext === "docx") { 43 const mammoth = await import("mammoth"); 44 const res = await mammoth.convertToHtml({ arrayBuffer: data.slice(0) }); 45 if (!cancelled) setHtml(DOMPurify.sanitize(res.value)); 46 } else { 47 const raw = new TextDecoder(ext === "doc" ? "latin1" : "utf-8").decode(data); 48 const t = ext === "doc" ? (raw.match(/[\\x20-\\x7E\\u00A0-\\u024F]{4,}/g) || []).join("\\n") : raw; 49 if (!cancelled) setText(t); 50 } 51 if (!cancelled) setStatus("ready"); 52 } catch (e) { 53 console.error("OfficeDocViewer failed", e); 54 if (!cancelled) setStatus("error"); 55 } 56 })(); 57 return () => { cancelled = true; }; 58 }, [data, ext]); 59 60 if (status === "loading") { 61 return <div className={className}><div className="h-full flex items-center justify-center"><Loader2 className="h-6 w-6 animate-spin text-muted-foreground" /></div></div>; 62 } 63 if (status === "error") { 64 return <div className={className}><div className="h-full flex items-center justify-center p-4 text-center text-sm text-muted-foreground">{fallback ?? "This document could not be shown here."}</div></div>; 65 } 66 67 if (sheets.length) { 68 const sheet = sheets[Math.min(active, sheets.length - 1)]; 69 const width = Math.max(0, ...sheet.rows.map((r) => r.length)); 70 return ( 71 <div className={className}> 72 {sheets.length > 1 && ( 73 <div className="flex gap-1 mb-2 flex-wrap"> 74 {sheets.map((s, i) => ( 75 <button key={s.name} type="button" onClick={() => setActive(i)} 76 className={\`px-2 py-1 text-xs rounded border \${i === active ? "bg-primary text-primary-foreground" : "bg-background"}\`}> 77 {s.name} 78 </button> 79 ))} 80 </div> 81 )} 82 <table className="text-xs border-collapse w-max min-w-full"> 83 <tbody> 84 {sheet.rows.map((r, i) => ( 85 <tr key={i} className={i === 0 ? "font-semibold bg-muted" : ""}> 86 {Array.from({ length: width }).map((_, j) => ( 87 <td key={j} className="border px-2 py-1 whitespace-nowrap">{r[j] ?? ""}</td> 88 ))} 89 </tr> 90 ))} 91 </tbody> 92 </table> 93 </div> 94 ); 95 } 96 97 if (html !== null) { 98 return ( 99 <div className={className}> 100 <div className="prose prose-sm max-w-none text-foreground [&_table]:border-collapse [&_td]:border [&_td]:px-2 [&_td]:py-1" 101 dangerouslySetInnerHTML={{ __html: html }} /> 102 </div> 103 ); 104 } 105 106 return <div className={className}><pre className="text-xs whitespace-pre-wrap">{text}</pre></div>; 107} 108`;export{e as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.