PageSourceSearch

https://book.minimozart.com/assets/OfficeDocViewer-zcu2kfRD.js

js minimozart.com collected 2026-10-09 22:05:55 UTC 4,227 bytes, 108 lines download raw bytes

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.