office-viewer-react 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +390 -0
  2. package/dist/client/assets/DocxViewer-NgAiZAEg.css +1 -0
  3. package/dist/client/assets/DocxViewer-gwdjm0mw.js +60 -0
  4. package/dist/client/assets/LogoIcon-BcnkueZW.js +1 -0
  5. package/dist/client/assets/PptxViewer-CLNaZa_4.js +59 -0
  6. package/dist/client/assets/PptxViewer-CYMXzyIj.css +1 -0
  7. package/dist/client/assets/XlsxViewer-BNso6L-X.css +1 -0
  8. package/dist/client/assets/XlsxViewer-C2ErMokS.js +64 -0
  9. package/dist/client/assets/_commonjs-dynamic-modules-DaXrHM_S.js +1 -0
  10. package/dist/client/assets/form-C1byQJR4.js +1 -0
  11. package/dist/client/assets/index-BDMLGHcR.js +2 -0
  12. package/dist/client/assets/index-CKjGwz9R.js +12 -0
  13. package/dist/client/assets/jszip.min-BwIaN_vk.js +2 -0
  14. package/dist/client/assets/login-DEy3R1iD.js +1 -0
  15. package/dist/client/assets/register-CUUVGLJE.js +1 -0
  16. package/dist/client/assets/styles-3a3CPFIV.css +1 -0
  17. package/dist/client/robots.txt +2 -0
  18. package/dist/index.cjs +1806 -0
  19. package/dist/index.d.cts +16 -0
  20. package/dist/index.d.ts +16 -0
  21. package/dist/index.js +1769 -0
  22. package/dist/server/assets/DocxViewer-Bm8UJY-7.js +469 -0
  23. package/dist/server/assets/LogoIcon-Dx0LU3or.js +26 -0
  24. package/dist/server/assets/PptxViewer-DS7Atucw.js +213 -0
  25. package/dist/server/assets/XlsxViewer-jzIgKmN2.js +841 -0
  26. package/dist/server/assets/_tanstack-start-manifest_v-CpFqMvFH.js +4 -0
  27. package/dist/server/assets/empty-plugin-adapters-BFgPZ6_d.js +6 -0
  28. package/dist/server/assets/form-CD9otjw-.js +236 -0
  29. package/dist/server/assets/index-gQHSGxNv.js +365 -0
  30. package/dist/server/assets/login-DvbAXNSQ.js +81 -0
  31. package/dist/server/assets/register-C2G9K9kP.js +102 -0
  32. package/dist/server/assets/router-F5YKPXkV.js +229 -0
  33. package/dist/server/assets/server-6Sfy37dh.js +1523 -0
  34. package/dist/server/assets/start-dMGD6DUy.js +56 -0
  35. package/dist/server/server.js +94 -0
  36. package/package.json +120 -0
@@ -0,0 +1,841 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect, useMemo } from "react";
3
+ import ExcelJS from "exceljs";
4
+ import * as XLSX from "xlsx";
5
+ import { Loader2, ChevronLeft, ChevronRight } from "lucide-react";
6
+ function colLetter(n) {
7
+ let s = "";
8
+ while (n > 0) {
9
+ const m = (n - 1) % 26;
10
+ s = String.fromCharCode(65 + m) + s;
11
+ n = Math.floor((n - 1) / 26);
12
+ }
13
+ return s;
14
+ }
15
+ function letterToNum(letters) {
16
+ let n = 0;
17
+ for (const ch of letters) n = n * 26 + (ch.charCodeAt(0) - 64);
18
+ return n;
19
+ }
20
+ const THEME_COLORS = [
21
+ "#FFFFFF",
22
+ // 0: lt1 (background)
23
+ "#000000",
24
+ // 1: dk1 (text / borders)
25
+ "#E7E6E6",
26
+ // 2: lt2
27
+ "#44546A",
28
+ // 3: dk2
29
+ "#4472C4",
30
+ // 4: accent1
31
+ "#ED7D31",
32
+ // 5: accent2
33
+ "#A5A5A5",
34
+ // 6: accent3
35
+ "#FFC000",
36
+ // 7: accent4
37
+ "#5B9BD5",
38
+ // 8: accent5
39
+ "#70AD47"
40
+ // 9: accent6
41
+ ];
42
+ const INDEXED_COLORS = [
43
+ "#000000",
44
+ "#FFFFFF",
45
+ "#FF0000",
46
+ "#00FF00",
47
+ "#0000FF",
48
+ "#FFFF00",
49
+ "#FF00FF",
50
+ "#00FFFF",
51
+ "#000000",
52
+ "#FFFFFF",
53
+ "#FF0000",
54
+ "#00FF00",
55
+ "#0000FF",
56
+ "#FFFF00",
57
+ "#FF00FF",
58
+ "#00FFFF",
59
+ "#800000",
60
+ "#008000",
61
+ "#000080",
62
+ "#808000",
63
+ "#800080",
64
+ "#008080",
65
+ "#C0C0C0",
66
+ "#808080",
67
+ "#9999FF",
68
+ "#993366",
69
+ "#FFFFCC",
70
+ "#CCFFFF",
71
+ "#660066",
72
+ "#FF8080",
73
+ "#0066CC",
74
+ "#CCCCFF",
75
+ "#000080",
76
+ "#FF00FF",
77
+ "#FFFF00",
78
+ "#00FFFF",
79
+ "#800080",
80
+ "#800000",
81
+ "#008080",
82
+ "#0000FF",
83
+ "#00CCFF",
84
+ "#CCFFFF",
85
+ "#CCFFCC",
86
+ "#FFFF99",
87
+ "#99CCFF",
88
+ "#FF99CC",
89
+ "#CC99FF",
90
+ "#FFCC99",
91
+ "#3366FF",
92
+ "#33CCCC",
93
+ "#99CC00",
94
+ "#FFCC00",
95
+ "#FF9900",
96
+ "#FF6600",
97
+ "#666699",
98
+ "#969696",
99
+ "#003366",
100
+ "#339966",
101
+ "#003300",
102
+ "#333300",
103
+ "#993300",
104
+ "#993366",
105
+ "#333399",
106
+ "#333333"
107
+ ];
108
+ function argbToCss(color) {
109
+ if (!color) return void 0;
110
+ if (color.argb) {
111
+ const argb = color.argb;
112
+ const hex = argb.length === 8 ? argb.slice(2) : argb;
113
+ if (!/^[0-9a-fA-F]{6}$/.test(hex)) return void 0;
114
+ return `#${hex}`;
115
+ }
116
+ if (color.theme !== void 0) return THEME_COLORS[color.theme];
117
+ if (color.indexed !== void 0) return color.indexed === 64 ? "#000000" : INDEXED_COLORS[color.indexed];
118
+ return void 0;
119
+ }
120
+ const SCALE = 1.5;
121
+ const JS_DATE_TOSTRING_RE = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s/;
122
+ function excelSerialToDate(serial) {
123
+ return new Date(Math.round((serial - 25569) * 864e5));
124
+ }
125
+ function fmtDate(d) {
126
+ return `${d.getUTCMonth() + 1}/${d.getUTCDate()}/${d.getUTCFullYear()}`;
127
+ }
128
+ const MONTH_LONG = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
129
+ const MONTH_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
130
+ const DAY_LONG = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
131
+ const DAY_SHORT = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
132
+ function serialToDateParts(serial) {
133
+ const frac = serial - Math.floor(serial);
134
+ const totalSec = Math.round(frac * 86400);
135
+ const h = Math.floor(totalSec / 3600);
136
+ const mi = Math.floor(totalSec % 3600 / 60);
137
+ const s = totalSec % 60;
138
+ const ds = Math.floor(serial);
139
+ if (ds <= 0) return { y: 1900, mo: 1, d: ds, dow: 6, h, mi, s };
140
+ if (ds === 60) return { y: 1900, mo: 2, d: 29, dow: 3, h, mi, s };
141
+ const adj = ds < 60 ? ds + 1 : ds;
142
+ const js = new Date(Math.round((adj - 25569) * 864e5));
143
+ return { y: js.getUTCFullYear(), mo: js.getUTCMonth() + 1, d: js.getUTCDate(), dow: js.getUTCDay(), h, mi, s };
144
+ }
145
+ function applyDateFmt(fmt, p) {
146
+ const section = fmt.split(";")[0];
147
+ const unquoted = section.replace(/"[^"]*"/g, "").replace(/\[.*?\]/g, "");
148
+ const hasHour = /h/i.test(unquoted);
149
+ const hasAmPm = /am\/pm|a\/p/i.test(unquoted);
150
+ let out = "", i = 0;
151
+ let prevHour = false;
152
+ while (i < section.length) {
153
+ if (section[i] === "[") {
154
+ const end = section.indexOf("]", i);
155
+ if (end >= 0) {
156
+ i = end + 1;
157
+ continue;
158
+ }
159
+ }
160
+ if (section[i] === "_") {
161
+ i += 2;
162
+ continue;
163
+ }
164
+ if (section[i] === "*") {
165
+ i += 2;
166
+ continue;
167
+ }
168
+ if (section[i] === '"') {
169
+ const end = section.indexOf('"', i + 1);
170
+ if (end < 0) {
171
+ out += section.slice(i + 1);
172
+ break;
173
+ }
174
+ out += section.slice(i + 1, end);
175
+ i = end + 1;
176
+ continue;
177
+ }
178
+ if (section[i] === "\\") {
179
+ out += section[i + 1] ?? "";
180
+ i += 2;
181
+ continue;
182
+ }
183
+ const rest = section.slice(i);
184
+ if (/^AM\/PM/i.test(rest)) {
185
+ out += p.h >= 12 ? "PM" : "AM";
186
+ i += 5;
187
+ prevHour = false;
188
+ continue;
189
+ }
190
+ if (/^A\/P/i.test(rest)) {
191
+ out += p.h >= 12 ? "P" : "A";
192
+ i += 3;
193
+ prevHour = false;
194
+ continue;
195
+ }
196
+ if (/^hh/i.test(rest)) {
197
+ const hr = hasAmPm ? p.h % 12 || 12 : p.h;
198
+ out += String(hr).padStart(2, "0");
199
+ i += 2;
200
+ prevHour = true;
201
+ continue;
202
+ }
203
+ if (/^h/i.test(rest)) {
204
+ const hr = hasAmPm ? p.h % 12 || 12 : p.h;
205
+ out += String(hr);
206
+ i += 1;
207
+ prevHour = true;
208
+ continue;
209
+ }
210
+ if (/^ss/i.test(rest)) {
211
+ out += String(p.s).padStart(2, "0");
212
+ i += 2;
213
+ prevHour = false;
214
+ continue;
215
+ }
216
+ if (/^s/i.test(rest)) {
217
+ out += String(p.s);
218
+ i += 1;
219
+ prevHour = false;
220
+ continue;
221
+ }
222
+ if (/^yyyy/i.test(rest)) {
223
+ out += String(p.y).padStart(4, "0");
224
+ i += 4;
225
+ prevHour = false;
226
+ continue;
227
+ }
228
+ if (/^yy/i.test(rest)) {
229
+ out += String(p.y % 100).padStart(2, "0");
230
+ i += 2;
231
+ prevHour = false;
232
+ continue;
233
+ }
234
+ if (/^mmmm/i.test(rest)) {
235
+ out += MONTH_LONG[p.mo - 1] ?? "";
236
+ i += 4;
237
+ prevHour = false;
238
+ continue;
239
+ }
240
+ if (/^mmm/i.test(rest)) {
241
+ out += MONTH_SHORT[p.mo - 1] ?? "";
242
+ i += 3;
243
+ prevHour = false;
244
+ continue;
245
+ }
246
+ if (/^mm/i.test(rest)) {
247
+ if (hasHour) {
248
+ out += String(p.mi).padStart(2, "0");
249
+ } else {
250
+ out += String(p.mo).padStart(2, "0");
251
+ }
252
+ i += 2;
253
+ prevHour = false;
254
+ continue;
255
+ }
256
+ if (/^m/i.test(rest)) {
257
+ if (hasHour && prevHour) {
258
+ out += String(p.mi);
259
+ } else {
260
+ out += String(p.mo);
261
+ }
262
+ i += 1;
263
+ prevHour = false;
264
+ continue;
265
+ }
266
+ if (/^dddd/i.test(rest)) {
267
+ out += DAY_LONG[p.dow] ?? "";
268
+ i += 4;
269
+ prevHour = false;
270
+ continue;
271
+ }
272
+ if (/^ddd/i.test(rest)) {
273
+ out += DAY_SHORT[p.dow] ?? "";
274
+ i += 3;
275
+ prevHour = false;
276
+ continue;
277
+ }
278
+ if (/^dd/i.test(rest)) {
279
+ out += String(p.d).padStart(2, "0");
280
+ i += 2;
281
+ prevHour = false;
282
+ continue;
283
+ }
284
+ if (/^d/i.test(rest)) {
285
+ out += String(p.d);
286
+ i += 1;
287
+ prevHour = false;
288
+ continue;
289
+ }
290
+ out += section[i];
291
+ i++;
292
+ prevHour = false;
293
+ }
294
+ return out;
295
+ }
296
+ function fmtExcelDate(d, numFmt) {
297
+ const serial = d.getTime() / 864e5 + 25569;
298
+ const parts = serialToDateParts(serial);
299
+ return numFmt && /[yYmMdDhHsS]/.test(numFmt) ? applyDateFmt(numFmt, parts) : fmtDate(d);
300
+ }
301
+ function fmtNumber(value, numFmt) {
302
+ if (!numFmt || numFmt === "General" || numFmt === "@") return null;
303
+ if (/[yY]/.test(numFmt) || /mmmm?/i.test(numFmt) || /[hH]/.test(numFmt)) return null;
304
+ const sections = numFmt.split(";");
305
+ let si = 0;
306
+ if (value < 0 && sections.length >= 2) si = 1;
307
+ else if (value === 0 && sections.length >= 3) si = 2;
308
+ const sec = sections[si];
309
+ let isPercent = false;
310
+ {
311
+ let inQ = false;
312
+ for (const ch of sec) {
313
+ if (ch === '"') inQ = !inQ;
314
+ else if (!inQ && ch === "%") {
315
+ isPercent = true;
316
+ break;
317
+ }
318
+ }
319
+ }
320
+ let prefix = "", suffix = "", numStr = "";
321
+ let inNum = false;
322
+ let i = 0;
323
+ while (i < sec.length) {
324
+ if (sec[i] === "[") {
325
+ const e = sec.indexOf("]", i);
326
+ i = e >= 0 ? e + 1 : i + 1;
327
+ continue;
328
+ }
329
+ if (sec[i] === "_") {
330
+ i += 2;
331
+ continue;
332
+ }
333
+ if (sec[i] === "*") {
334
+ i += 2;
335
+ continue;
336
+ }
337
+ if (sec[i] === '"') {
338
+ const e = sec.indexOf('"', i + 1);
339
+ const lit = e >= 0 ? sec.slice(i + 1, e) : sec.slice(i + 1);
340
+ inNum ? suffix += lit : prefix += lit;
341
+ i = e >= 0 ? e + 1 : sec.length;
342
+ continue;
343
+ }
344
+ if (sec[i] === "\\") {
345
+ inNum ? suffix += sec[i + 1] ?? "" : prefix += sec[i + 1] ?? "";
346
+ i += 2;
347
+ continue;
348
+ }
349
+ const ch = sec[i];
350
+ if ("$€£¥".includes(ch)) {
351
+ inNum ? suffix += ch : prefix += ch;
352
+ i++;
353
+ continue;
354
+ }
355
+ if (ch === "(" || ch === ")") {
356
+ i++;
357
+ continue;
358
+ }
359
+ if ("#0?.".includes(ch) || ch === "," && inNum) {
360
+ inNum = true;
361
+ numStr += ch;
362
+ i++;
363
+ continue;
364
+ }
365
+ inNum ? suffix += ch : prefix += ch;
366
+ i++;
367
+ }
368
+ if (!/[#0]/.test(numStr)) {
369
+ const lit = (prefix + suffix).replace(/[$€£¥]/g, "").trim();
370
+ return lit || null;
371
+ }
372
+ const dotIdx = numStr.indexOf(".");
373
+ const decPlaces = dotIdx >= 0 ? (numStr.slice(dotIdx + 1).match(/^[#0?]+/) ?? [""])[0].length : 0;
374
+ const hasGroup = /[#0],[#0]/.test(numStr);
375
+ const absVal = Math.abs(isPercent ? value * 100 : value);
376
+ const formatted = absVal.toLocaleString("en-US", {
377
+ minimumFractionDigits: decPlaces,
378
+ maximumFractionDigits: decPlaces,
379
+ useGrouping: hasGroup
380
+ });
381
+ let result = `${prefix}${formatted}${suffix}`;
382
+ if (value < 0 && si === 0) result = `-${result}`;
383
+ else if (value < 0 && si === 1 && (sections[1].includes("(") || sections[1].includes(")"))) {
384
+ result = `(${result})`;
385
+ }
386
+ return result;
387
+ }
388
+ function getAccountingCurrency(numFmt) {
389
+ if (!numFmt) return null;
390
+ if (!/_\([$€£¥]\*/.test(numFmt) && !/_\(\*[$€£¥]/.test(numFmt)) return null;
391
+ const m = numFmt.match(/[$€£¥]/);
392
+ return m ? m[0] : null;
393
+ }
394
+ function effectiveValue(cell) {
395
+ const v = cell.value;
396
+ if (v !== null && typeof v === "object" && !(v instanceof Date) && ("formula" in v || "sharedFormula" in v)) {
397
+ return v.result ?? null;
398
+ }
399
+ return v;
400
+ }
401
+ function getCellText(cell) {
402
+ const val = effectiveValue(cell);
403
+ if (val !== null && typeof val === "object" && !(val instanceof Date) && "error" in val) {
404
+ return val.error ?? "";
405
+ }
406
+ if (typeof val === "string" && /^#[A-Z0-9\/]+[!?]?$/.test(val)) {
407
+ return val;
408
+ }
409
+ const text = cell.text ?? "";
410
+ if (val instanceof Date) {
411
+ if (isNaN(val.getTime())) return "";
412
+ return fmtExcelDate(val, cell.numFmt ?? "");
413
+ }
414
+ if (JS_DATE_TOSTRING_RE.test(text) || text === "Invalid Date") return "";
415
+ if (typeof val === "number") {
416
+ const numFmt = cell.numFmt ?? "";
417
+ const isDateTimeFmt = numFmt && (/[yY]/.test(numFmt) || /mmmm?/i.test(numFmt) || /[hH]/.test(numFmt) || /[dD]/.test(numFmt));
418
+ if (isDateTimeFmt) {
419
+ if (val >= 0) {
420
+ const d = excelSerialToDate(val);
421
+ if (!isNaN(d.getTime())) return fmtExcelDate(d, numFmt);
422
+ }
423
+ return text;
424
+ }
425
+ if (numFmt && numFmt !== "General" && numFmt !== "@") {
426
+ const formatted = fmtNumber(val, numFmt);
427
+ if (formatted !== null) return formatted;
428
+ }
429
+ if (text && !/^-?\d+\.?\d*$/.test(text)) return text;
430
+ if (!text) return Number.isInteger(val) ? String(val) : val.toLocaleString("en-US");
431
+ }
432
+ return text;
433
+ }
434
+ function borderStyle(b) {
435
+ if (!b?.style) return void 0;
436
+ const color = argbToCss(b.color) || "#000000";
437
+ const weight = b.style.includes("thick") || b.style.includes("medium") ? "2px" : "1px";
438
+ const kind = b.style.includes("dash") ? "dashed" : b.style.includes("dot") ? "dotted" : b.style.includes("double") ? "double" : "solid";
439
+ return `${weight} ${kind} ${color}`;
440
+ }
441
+ function buildRawResultMap(ws) {
442
+ const map = /* @__PURE__ */ new Map();
443
+ const sheetModel = ws.model;
444
+ for (const rawRow of sheetModel?.rows ?? []) {
445
+ const rn = rawRow?.number;
446
+ if (!rn || !rawRow.cells) continue;
447
+ for (const rc of rawRow.cells) {
448
+ if (!rc?.col) continue;
449
+ if ((rc.type === 6 || rc.formula !== void 0 || rc.sharedFormula !== void 0) && rc.result !== void 0) {
450
+ map.set(`${rn}:${rc.col}`, rc.result);
451
+ }
452
+ }
453
+ }
454
+ return map;
455
+ }
456
+ function formatRawResult(result, numFmt) {
457
+ if (result === null || result === void 0) return "";
458
+ if (typeof result === "object" && !Array.isArray(result) && !(result instanceof Date)) {
459
+ if ("error" in result) return result.error ?? "";
460
+ }
461
+ if (result instanceof Date) {
462
+ if (isNaN(result.getTime())) return "";
463
+ return fmtExcelDate(result, numFmt);
464
+ }
465
+ if (typeof result === "number") {
466
+ if (numFmt && numFmt !== "General" && numFmt !== "@") {
467
+ const f = fmtNumber(result, numFmt);
468
+ if (f !== null) return f;
469
+ }
470
+ return Number.isInteger(result) ? String(result) : result.toLocaleString("en-US");
471
+ }
472
+ if (typeof result === "string") return result;
473
+ if (typeof result === "boolean") return result ? "TRUE" : "FALSE";
474
+ return String(result);
475
+ }
476
+ function buildSheet(ws, sjSheet) {
477
+ const sjCells = /* @__PURE__ */ new Map();
478
+ let sjMaxRow = 0;
479
+ let sjMaxCol = 0;
480
+ if (sjSheet) {
481
+ for (const addr of Object.keys(sjSheet)) {
482
+ if (addr.startsWith("!")) continue;
483
+ const sjc = sjSheet[addr];
484
+ if (!sjc || !sjc.t || sjc.t === "z") continue;
485
+ const m = addr.match(/^([A-Z]+)(\d+)$/);
486
+ if (!m) continue;
487
+ const rowNum = parseInt(m[2], 10);
488
+ const colNum = letterToNum(m[1]);
489
+ if (rowNum > sjMaxRow) sjMaxRow = rowNum;
490
+ if (colNum > sjMaxCol) sjMaxCol = colNum;
491
+ sjCells.set(`${rowNum}:${colNum}`, sjc);
492
+ }
493
+ }
494
+ let originalCols = Math.max(ws.columnCount, sjMaxCol);
495
+ let cols = Math.max(originalCols, 26);
496
+ let rowsN = Math.max(ws.rowCount, 50);
497
+ const exRawModel = ws.model;
498
+ if (exRawModel?.rows) {
499
+ for (const rawRow of exRawModel.rows) {
500
+ if (rawRow?.number) rowsN = Math.max(rowsN, rawRow.number);
501
+ }
502
+ }
503
+ if (sjMaxRow > 0) rowsN = Math.max(rowsN, sjMaxRow);
504
+ if (sjMaxCol > 0) {
505
+ originalCols = Math.max(originalCols, sjMaxCol);
506
+ cols = Math.max(cols, sjMaxCol);
507
+ }
508
+ const rawResults = buildRawResultMap(ws);
509
+ const merges = {};
510
+ const covered = /* @__PURE__ */ new Set();
511
+ const mergeList = ws.model.merges || [];
512
+ for (const range of mergeList) {
513
+ const [a, b] = range.split(":");
514
+ const m1 = a.match(/([A-Z]+)(\d+)/);
515
+ const m2 = b.match(/([A-Z]+)(\d+)/);
516
+ if (!m1 || !m2) continue;
517
+ const c1 = letterToNum(m1[1]);
518
+ const r1 = parseInt(m1[2], 10);
519
+ const c2 = letterToNum(m2[1]);
520
+ const r2 = parseInt(m2[2], 10);
521
+ merges[`${r1}:${c1}`] = { rowSpan: r2 - r1 + 1, colSpan: c2 - c1 + 1 };
522
+ for (let r = r1; r <= r2; r++)
523
+ for (let c = c1; c <= c2; c++) if (!(r === r1 && c === c1)) covered.add(`${r}:${c}`);
524
+ }
525
+ const colWidths = [];
526
+ for (let c = 1; c <= cols; c++) {
527
+ const col = ws.getColumn(c);
528
+ if (col.hidden) {
529
+ colWidths[c - 1] = 0;
530
+ continue;
531
+ }
532
+ const w = col.width;
533
+ colWidths[c - 1] = w ? Math.max(Math.round((w * 7 + 5) * SCALE), 10) : Math.round(64 * SCALE);
534
+ }
535
+ const rowHeights = [];
536
+ const rows = [];
537
+ for (let r = 1; r <= rowsN; r++) {
538
+ const row = ws.getRow(r);
539
+ rowHeights[r - 1] = row.height ? Math.max(Math.round(row.height * 4 / 3 * SCALE), 6) : Math.round(20 * SCALE);
540
+ const cells = [];
541
+ const ccIndices = [];
542
+ for (let c = 1; c <= cols; c++) {
543
+ if (covered.has(`${r}:${c}`)) {
544
+ cells.push(null);
545
+ continue;
546
+ }
547
+ const cell = row.getCell(c);
548
+ const s = cell.style || {};
549
+ const style = {};
550
+ let isCenterContinuous = false;
551
+ if (s.font) {
552
+ if (s.font.bold) style.fontWeight = 600;
553
+ if (s.font.italic) style.fontStyle = "italic";
554
+ {
555
+ const dec = [];
556
+ if (s.font.underline) dec.push("underline");
557
+ if (s.font.strike) dec.push("line-through");
558
+ if (dec.length) style.textDecoration = dec.join(" ");
559
+ }
560
+ if (s.font.size) style.fontSize = `${Math.round(s.font.size * 4 / 3 * SCALE)}px`;
561
+ if (s.font.name) style.fontFamily = s.font.name;
562
+ const fc = argbToCss(s.font.color);
563
+ if (fc) style.color = fc;
564
+ }
565
+ if (s.fill && s.fill.type === "pattern" && s.fill.pattern === "solid") {
566
+ const bg = argbToCss(s.fill.fgColor);
567
+ if (bg) style.backgroundColor = bg;
568
+ }
569
+ if (s.alignment) {
570
+ const h = s.alignment.horizontal;
571
+ if (h) {
572
+ isCenterContinuous = h === "centerContinuous";
573
+ style.textAlign = isCenterContinuous || h === "distributed" ? "center" : h === "fill" ? "left" : h;
574
+ }
575
+ const v = s.alignment.vertical;
576
+ if (v) {
577
+ style.verticalAlign = v === "center" || v === "middle" ? "middle" : v === "top" ? "top" : "bottom";
578
+ }
579
+ if (s.alignment.wrapText) style.whiteSpace = "normal";
580
+ if (s.alignment.indent) style.paddingLeft = `${s.alignment.indent * 16 + 6}px`;
581
+ }
582
+ if (s.border) {
583
+ const t = borderStyle(s.border.top);
584
+ const l = borderStyle(s.border.left);
585
+ const bo = borderStyle(s.border.bottom);
586
+ const ri = borderStyle(s.border.right);
587
+ if (t) style.borderTop = t;
588
+ if (l) style.borderLeft = l;
589
+ if (bo) style.borderBottom = bo;
590
+ if (ri) style.borderRight = ri;
591
+ }
592
+ if (style.textAlign === void 0) {
593
+ const ev = effectiveValue(cell);
594
+ if (typeof ev === "number" || ev instanceof Date) style.textAlign = "right";
595
+ }
596
+ const merge = merges[`${r}:${c}`];
597
+ const sj = sjCells.get(`${r}:${c}`);
598
+ const numFmt = cell.numFmt || sj?.z || "";
599
+ const accountingCurrency = getAccountingCurrency(numFmt);
600
+ let text = "";
601
+ if (sj) {
602
+ if (sj.t === "e") {
603
+ text = sj.w ?? (typeof sj.v === "string" ? sj.v : "");
604
+ } else if (sj.t === "n" && typeof sj.v === "number") {
605
+ const isDateFmt = numFmt && (/[yY]/.test(numFmt) || /mmmm?/i.test(numFmt) || /[hH]/.test(numFmt) || /[dD]/.test(numFmt));
606
+ if (isDateFmt && sj.v >= 0) {
607
+ const d = excelSerialToDate(sj.v);
608
+ if (!isNaN(d.getTime())) text = fmtExcelDate(d, numFmt);
609
+ } else if (numFmt && numFmt !== "General" && numFmt !== "@") {
610
+ const formatted = fmtNumber(sj.v, numFmt);
611
+ if (formatted !== null) text = formatted;
612
+ }
613
+ if (!text) text = sj.w ?? String(sj.v);
614
+ if (style.textAlign === void 0) style.textAlign = "right";
615
+ } else if (sj.t === "s") {
616
+ text = sj.w ?? "";
617
+ } else if (sj.t === "str") {
618
+ text = sj.w ?? String(sj.v ?? "");
619
+ } else if (sj.t === "b") {
620
+ text = sj.v ? "TRUE" : "FALSE";
621
+ } else {
622
+ text = sj.w ?? String(sj.v ?? "");
623
+ }
624
+ }
625
+ if (!text) text = getCellText(cell);
626
+ if (!text) {
627
+ const raw = rawResults.get(`${r}:${c}`);
628
+ if (raw !== void 0) {
629
+ text = formatRawResult(raw, numFmt);
630
+ if (style.textAlign === void 0) {
631
+ if (typeof raw === "number" || raw instanceof Date) style.textAlign = "right";
632
+ }
633
+ }
634
+ }
635
+ if (accountingCurrency && text) {
636
+ text = text.replace(/[$€£¥]/g, "").trim() || text;
637
+ }
638
+ cells.push({
639
+ key: `${r}:${c}`,
640
+ text,
641
+ accountingPrefix: accountingCurrency && text ? accountingCurrency : void 0,
642
+ style,
643
+ rowSpan: merge?.rowSpan ?? 1,
644
+ colSpan: merge?.colSpan ?? 1
645
+ });
646
+ if (isCenterContinuous && text) ccIndices.push(cells.length - 1);
647
+ }
648
+ for (const idx of ccIndices) {
649
+ const cc = cells[idx];
650
+ for (let cj = idx + 1; cj < originalCols; cj++) {
651
+ const adj = cells[cj];
652
+ if (adj === null || adj.text !== "") break;
653
+ cc.colSpan++;
654
+ cells[cj] = null;
655
+ }
656
+ }
657
+ for (let idx = 0; idx < cells.length; idx++) {
658
+ const cell = cells[idx];
659
+ if (!cell || !cell.text || cell.colSpan > 1 || cell.style.whiteSpace === "normal") continue;
660
+ const align = cell.style.textAlign;
661
+ if (align === "right" || align === "center") continue;
662
+ if (cell.style.borderRight) continue;
663
+ for (let cj = idx + 1; cj < originalCols; cj++) {
664
+ const adj = cells[cj];
665
+ if (!adj || adj.text !== "") break;
666
+ if (adj.style.borderLeft) break;
667
+ if (adj.style.borderRight) {
668
+ cell.style.borderRight = adj.style.borderRight;
669
+ cells[cj] = null;
670
+ cell.colSpan++;
671
+ break;
672
+ }
673
+ cells[cj] = null;
674
+ cell.colSpan++;
675
+ }
676
+ }
677
+ rows.push(cells);
678
+ }
679
+ return { name: ws.name, rows, colWidths, rowHeights, cols };
680
+ }
681
+ function XlsxViewer({
682
+ data,
683
+ className,
684
+ onError
685
+ }) {
686
+ const [sheets, setSheets] = useState([]);
687
+ const [active, setActive] = useState(0);
688
+ const [loading, setLoading] = useState(true);
689
+ const [error, setError] = useState(null);
690
+ const tabBarRef = useRef(null);
691
+ const scrollTabs = (dir) => {
692
+ tabBarRef.current?.scrollBy({ left: dir === "left" ? -160 : 160, behavior: "smooth" });
693
+ };
694
+ useEffect(() => {
695
+ let cancelled = false;
696
+ setLoading(true);
697
+ setError(null);
698
+ (async () => {
699
+ try {
700
+ const wb = new ExcelJS.Workbook();
701
+ await wb.xlsx.load(data.slice(0));
702
+ let sjSheets = [];
703
+ try {
704
+ const sjWb = XLSX.read(new Uint8Array(data), {
705
+ type: "array",
706
+ cellFormula: true,
707
+ cellText: true,
708
+ cellNF: true,
709
+ cellDates: false,
710
+ sheetRows: 0
711
+ });
712
+ const visibleWs = wb.worksheets.filter(
713
+ (ws) => ws.state !== "hidden" && ws.state !== "veryHidden"
714
+ );
715
+ sjSheets = visibleWs.map((ws, idx) => {
716
+ if (sjWb.Sheets[ws.name]) return sjWb.Sheets[ws.name];
717
+ const lc = ws.name.toLowerCase();
718
+ const found = sjWb.SheetNames.find((n) => n.toLowerCase() === lc);
719
+ if (found && sjWb.Sheets[found]) return sjWb.Sheets[found];
720
+ const sjName = sjWb.SheetNames[idx];
721
+ return sjName && sjWb.Sheets[sjName] ? sjWb.Sheets[sjName] : void 0;
722
+ });
723
+ } catch (sjErr) {
724
+ console.warn("[XlsxViewer] SheetJS failed, falling back to ExcelJS only:", sjErr);
725
+ }
726
+ const visibleWorksheets = wb.worksheets.filter(
727
+ (ws) => ws.state !== "hidden" && ws.state !== "veryHidden"
728
+ );
729
+ const built = visibleWorksheets.map(
730
+ (ws, idx) => buildSheet(ws, sjSheets[idx])
731
+ );
732
+ if (cancelled) return;
733
+ setSheets(built);
734
+ setActive(0);
735
+ } catch (e) {
736
+ const err = e instanceof Error ? e : new Error("Failed to read spreadsheet");
737
+ if (!cancelled) {
738
+ setError(err.message);
739
+ onError?.(err);
740
+ }
741
+ } finally {
742
+ if (!cancelled) setLoading(false);
743
+ }
744
+ })();
745
+ return () => {
746
+ cancelled = true;
747
+ };
748
+ }, [data, onError]);
749
+ const sheet = sheets[active];
750
+ const headerCols = useMemo(() => sheet ? Array.from({ length: sheet.cols }, (_, i) => i + 1) : [], [sheet]);
751
+ if (loading)
752
+ return /* @__PURE__ */ jsxs("div", { className: `ov-xlsx__loading${className ? ` ${className}` : ""}`, children: [
753
+ /* @__PURE__ */ jsx(Loader2, { className: "ov-xlsx__loading-icon" }),
754
+ /* @__PURE__ */ jsx("span", { className: "ov-xlsx__loading-text", children: "Reading spreadsheet…" })
755
+ ] });
756
+ if (error)
757
+ return /* @__PURE__ */ jsx("div", { className: `ov-xlsx__error${className ? ` ${className}` : ""}`, children: error });
758
+ if (!sheet) return null;
759
+ return /* @__PURE__ */ jsxs("div", { className: `ov-xlsx${className ? ` ${className}` : ""}`, children: [
760
+ /* @__PURE__ */ jsx("div", { className: "ov-xlsx__grid-area", children: /* @__PURE__ */ jsxs("table", { className: "ov-xlsx__table", style: { tableLayout: "fixed", fontFamily: "Calibri, 'Segoe UI', Arial, sans-serif" }, children: [
761
+ /* @__PURE__ */ jsxs("colgroup", { children: [
762
+ /* @__PURE__ */ jsx("col", { style: { width: 44, minWidth: 44 } }),
763
+ headerCols.map((c) => /* @__PURE__ */ jsx("col", { style: { width: sheet.colWidths[c - 1], minWidth: sheet.colWidths[c - 1] } }, c))
764
+ ] }),
765
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
766
+ /* @__PURE__ */ jsx("th", { className: "ov-xlsx__corner" }),
767
+ headerCols.map((c) => {
768
+ const w = sheet.colWidths[c - 1];
769
+ if (w === 0) return /* @__PURE__ */ jsx("th", { style: { width: 0, padding: 0, border: "none" } }, c);
770
+ return /* @__PURE__ */ jsx("th", { className: "ov-xlsx__col-header", children: colLetter(c) }, c);
771
+ })
772
+ ] }) }),
773
+ /* @__PURE__ */ jsx("tbody", { children: sheet.rows.map((row, ri) => {
774
+ const h = sheet.rowHeights[ri];
775
+ if (h === 0) return null;
776
+ return /* @__PURE__ */ jsxs("tr", { style: { height: h }, children: [
777
+ /* @__PURE__ */ jsx("td", { className: "ov-xlsx__row-header", children: ri + 1 }),
778
+ row.map(
779
+ (cell) => cell === null ? null : /* @__PURE__ */ jsx(
780
+ "td",
781
+ {
782
+ rowSpan: cell.rowSpan,
783
+ colSpan: cell.colSpan,
784
+ className: "ov-xlsx__cell",
785
+ style: cell.style,
786
+ "data-ov-bold": cell.style.fontWeight === 600 ? "" : void 0,
787
+ children: cell.accountingPrefix ? /* @__PURE__ */ jsxs("span", { className: "ov-xlsx__cell-accounting", style: { whiteSpace: "nowrap" }, children: [
788
+ /* @__PURE__ */ jsx("span", { children: cell.accountingPrefix }),
789
+ /* @__PURE__ */ jsx("span", { style: { flex: 1, textAlign: "right" }, children: cell.text })
790
+ ] }) : /* @__PURE__ */ jsx(
791
+ "span",
792
+ {
793
+ className: "ov-xlsx__cell-text",
794
+ style: { whiteSpace: cell.style.whiteSpace ?? "nowrap" },
795
+ children: cell.text
796
+ }
797
+ )
798
+ },
799
+ cell.key
800
+ )
801
+ )
802
+ ] }, ri);
803
+ }) })
804
+ ] }) }),
805
+ /* @__PURE__ */ jsxs("div", { className: "ov-xlsx__tabbar", children: [
806
+ sheets.length > 5 && /* @__PURE__ */ jsxs("div", { className: "ov-xlsx__tabnav", children: [
807
+ /* @__PURE__ */ jsx(
808
+ "button",
809
+ {
810
+ onClick: () => scrollTabs("left"),
811
+ "aria-label": "Scroll sheet tabs left",
812
+ className: "ov-xlsx__tabnav-btn",
813
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "ov-xlsx__tabnav-icon" })
814
+ }
815
+ ),
816
+ /* @__PURE__ */ jsx(
817
+ "button",
818
+ {
819
+ onClick: () => scrollTabs("right"),
820
+ "aria-label": "Scroll sheet tabs right",
821
+ className: "ov-xlsx__tabnav-btn",
822
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "ov-xlsx__tabnav-icon" })
823
+ }
824
+ )
825
+ ] }),
826
+ /* @__PURE__ */ jsx("div", { ref: tabBarRef, className: "ov-xlsx__tabs", children: sheets.map((s, i) => /* @__PURE__ */ jsx(
827
+ "button",
828
+ {
829
+ onClick: () => setActive(i),
830
+ title: s.name,
831
+ className: `ov-xlsx__tab${i === active ? " ov-xlsx__tab--active" : ""}`,
832
+ children: /* @__PURE__ */ jsx("span", { className: "ov-xlsx__tab-name", children: s.name })
833
+ },
834
+ i
835
+ )) })
836
+ ] })
837
+ ] });
838
+ }
839
+ export {
840
+ XlsxViewer
841
+ };