@virtualansoftware/vs-office-viewer 1.0.1 → 1.0.2

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