@canvas-components/utils 0.2.4 → 0.2.5

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.
@@ -33,7 +33,10 @@ var __export = (target, all) => {
33
33
  };
34
34
 
35
35
  // src/export/xlsx-runtime.ts
36
- function createXlsxWriter(xlsx, header, sheetName, maxRows = 1048576) {
36
+ function createXlsxWriter(xlsx, header, sheetName, maxRows = 1048576, merges = []) {
37
+ const headerRows = Array.isArray(header[0]) ? header : [header];
38
+ if (headerRows.length >= maxRows)
39
+ throw new Error("XLSX \u8868\u5934\u884C\u6570\u8D85\u8FC7\u5DE5\u4F5C\u8868\u5BB9\u91CF");
37
40
  const workbook = xlsx.utils.book_new();
38
41
  let worksheet;
39
42
  let rowCount = 0;
@@ -41,7 +44,7 @@ function createXlsxWriter(xlsx, header, sheetName, maxRows = 1048576) {
41
44
  const baseName = sheetName.replace(/[\\/?*[\]:]/g, "_").replace(/^'+|'+$/g, "") || "Sheet1";
42
45
  const addSheet = () => {
43
46
  const suffix = sheetIndex === 0 ? "" : `_${sheetIndex + 1}`;
44
- worksheet = xlsx.utils.aoa_to_sheet([header], {
47
+ worksheet = xlsx.utils.aoa_to_sheet(headerRows, {
45
48
  dense: true
46
49
  });
47
50
  xlsx.utils.book_append_sheet(
@@ -49,7 +52,8 @@ function createXlsxWriter(xlsx, header, sheetName, maxRows = 1048576) {
49
52
  worksheet,
50
53
  baseName.slice(0, 31 - suffix.length) + suffix
51
54
  );
52
- rowCount = 1;
55
+ if (merges.length && sheetIndex === 0) worksheet["!merges"] = merges;
56
+ rowCount = headerRows.length;
53
57
  sheetIndex += 1;
54
58
  };
55
59
  addSheet();
@@ -125,7 +129,7 @@ let writer;
125
129
  self.onmessage = function(event) {
126
130
  try {
127
131
  const message = event.data;
128
- if (message.type === 'init') writer = createWriter(XLSX, message.header, message.sheetName);
132
+ if (message.type === 'init') writer = createWriter(XLSX, message.header, message.sheetName, undefined, message.merges);
129
133
  else if (message.type === 'append') writer.append(message.rows);
130
134
  else if (message.type === 'finish') {
131
135
  const buffer = writer.finish();
@@ -173,8 +177,22 @@ async function buildXlsxPart(params, rangeStart, rangeEnd) {
173
177
  throw new Error(
174
178
  "\u5355\u4EFD XLSX \u6570\u636E\u8D85\u8FC7\u5B89\u5168\u9650\u5236\uFF0C\u8BF7\u4F7F\u7528 exportTableToXlsx \u81EA\u52A8\u5206\u6587\u4EF6\u5BFC\u51FA"
175
179
  );
176
- const header = columns.map((column) => column.title);
177
- let textSize = header.reduce((size, title) => size + title.length, 0);
180
+ const header = params.headerRows?.length ? params.headerRows : [columns.map((column) => column.title)];
181
+ const headerRowCount = header.length;
182
+ const merges = (params.merges ?? []).flatMap((merge) => {
183
+ if (merge.e.r < headerRowCount) return [merge];
184
+ const start = Math.max(merge.s.r, headerRowCount + rangeStart);
185
+ const end = Math.min(merge.e.r, headerRowCount + rangeEnd - 1);
186
+ return end < start || end === start && merge.s.c === merge.e.c ? [] : [
187
+ {
188
+ s: { r: start - rangeStart, c: merge.s.c },
189
+ e: { r: end - rangeStart, c: merge.e.c }
190
+ }
191
+ ];
192
+ });
193
+ if (header.some((row) => row.length > columns.length))
194
+ throw new Error("XLSX \u8868\u5934\u5355\u5143\u683C\u6570\u4E0D\u80FD\u8D85\u8FC7\u5BFC\u51FA\u5217\u6570");
195
+ let textSize = header.flat().reduce((size, title) => size + title.length, 0);
178
196
  const normalizeExportCell = (value) => {
179
197
  const cell = normalizeCell(value);
180
198
  if (typeof cell === "string") {
@@ -188,7 +206,7 @@ async function buildXlsxPart(params, rangeStart, rangeEnd) {
188
206
  }
189
207
  return cell;
190
208
  };
191
- header.forEach((title) => {
209
+ header.flat().forEach((title) => {
192
210
  if (title.length > 32767) throw new Error("XLSX \u8868\u5934\u8D85\u8FC7 32767 \u5B57\u7B26");
193
211
  });
194
212
  if (textSize > XLSX_MAX_TEXT_CHARS_PER_FILE)
@@ -240,7 +258,7 @@ async function buildXlsxPart(params, rangeStart, rangeEnd) {
240
258
  () => fail(new Error("XLSX Worker \u542F\u52A8\u8D85\u65F6")),
241
259
  1e4
242
260
  );
243
- await request({ type: "init", header, sheetName });
261
+ await request({ type: "init", header, sheetName, merges });
244
262
  } catch {
245
263
  worker?.terminate();
246
264
  worker = void 0;
@@ -250,7 +268,7 @@ async function buildXlsxPart(params, rangeStart, rangeEnd) {
250
268
  }
251
269
  }
252
270
  checkAbort();
253
- const writer = worker ? void 0 : createXlsxWriter(XLSX__namespace, header, sheetName);
271
+ const writer = worker ? void 0 : createXlsxWriter(XLSX__namespace, header, sheetName, void 0, merges);
254
272
  const totalRows = rangeEnd - rangeStart;
255
273
  const report = (phase, completedRows) => params.onProgress?.({
256
274
  phase,
@@ -381,7 +399,7 @@ function getXlsxRowsPerFile(params) {
381
399
  throw new Error("XLSX \u5206\u6587\u4EF6\u4E0A\u9650\u5FC5\u987B\u4E3A\u6B63\u6574\u6570");
382
400
  const byCells = Math.floor(
383
401
  Math.min(cellLimit, XLSX_MAX_CELLS_PER_FILE) / Math.max(1, params.columns.length)
384
- ) - 1;
402
+ ) - (params.headerRows?.length || 1);
385
403
  if (byCells < 1) throw new Error("XLSX \u5355\u5143\u683C\u9884\u7B97\u4E0D\u8DB3\u4EE5\u5BB9\u7EB3\u8868\u5934\u548C\u4E00\u884C\u6570\u636E");
386
404
  return Math.min(rowLimit, XLSX_MAX_ROWS_PER_FILE, byCells);
387
405
  }
@@ -403,7 +421,8 @@ function buildCsvContent(params) {
403
421
  columns.map((column) => column.title),
404
422
  ...rows.map(
405
423
  (row, rowIndex) => columns.map((column) => column.value(row, rowIndex))
406
- )
424
+ ),
425
+ ...params.footerRows ?? []
407
426
  ]);
408
427
  }
409
428
  function serializeCsvRows(rows, lineBreak = "\n") {
@@ -415,7 +434,13 @@ function buildTxtContent(params) {
415
434
  const body = rows.map(
416
435
  (row, rowIndex) => columns.map((column) => normalizeTextCell(column.value(row, rowIndex))).join(" ")
417
436
  );
418
- return [header, ...body].join("\n");
437
+ return [
438
+ header,
439
+ ...body,
440
+ ...(params.footerRows ?? []).map(
441
+ (row) => row.map(normalizeTextCell).join(" ")
442
+ )
443
+ ].join("\n");
419
444
  }
420
445
  async function downloadJsonFile(params) {
421
446
  const { data, fileName, space = 2 } = params;
@@ -427,8 +452,8 @@ async function downloadJsonFile(params) {
427
452
  });
428
453
  }
429
454
  async function exportTableToCsv(params) {
430
- const { fileName, rows, columns } = params;
431
- const content = await buildCsvContentAsync({ rows, columns });
455
+ const { fileName, rows, columns, footerRows } = params;
456
+ const content = await buildCsvContentAsync({ rows, columns, footerRows });
432
457
  downloadTextFile({
433
458
  fileName: ensureExtension(fileName, "csv"),
434
459
  content,
@@ -436,8 +461,8 @@ async function exportTableToCsv(params) {
436
461
  });
437
462
  }
438
463
  async function exportTableToTxt(params) {
439
- const { fileName, rows, columns } = params;
440
- const content = await buildTxtContentAsync({ rows, columns });
464
+ const { fileName, rows, columns, footerRows } = params;
465
+ const content = await buildTxtContentAsync({ rows, columns, footerRows });
441
466
  downloadTextFile({
442
467
  fileName: ensureExtension(fileName, "txt"),
443
468
  content,
@@ -481,6 +506,9 @@ async function buildCsvContentAsync(params) {
481
506
  }
482
507
  await yieldToMainThread();
483
508
  }
509
+ for (const footer of params.footerRows ?? []) {
510
+ parts.push("\n", footer.map(escapeCsvCell).join(","));
511
+ }
484
512
  return parts.join("");
485
513
  }
486
514
  async function buildTxtContentAsync(params) {
@@ -496,6 +524,9 @@ async function buildTxtContentAsync(params) {
496
524
  }
497
525
  await yieldToMainThread();
498
526
  }
527
+ for (const footer of params.footerRows ?? []) {
528
+ parts.push("\n", footer.map(normalizeTextCell).join(" "));
529
+ }
499
530
  return parts.join("");
500
531
  }
501
532
  function escapeCsvCell(value) {