sheetra 1.0.2 → 1.0.4

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.
package/dist/index.js CHANGED
@@ -526,22 +526,129 @@ class ExcelWriter {
526
526
  const zip = new ZipWriter();
527
527
  const sheets = workbook['sheets'];
528
528
  const sheetNames = sheets.map((sheet, index) => sheet.getName() || `Sheet${index + 1}`);
529
+ // Collect all styles from sheets
530
+ const styleRegistry = this.collectStyles(sheets);
529
531
  // Add required XLSX files
530
532
  zip.addFile('[Content_Types].xml', this.generateContentTypes(sheetNames));
531
533
  zip.addFile('_rels/.rels', this.generateRels());
532
534
  zip.addFile('xl/workbook.xml', this.generateWorkbook(sheetNames));
533
535
  zip.addFile('xl/_rels/workbook.xml.rels', this.generateWorkbookRels(sheetNames));
534
- zip.addFile('xl/styles.xml', this.generateStyles());
536
+ zip.addFile('xl/styles.xml', this.generateStyles(styleRegistry));
535
537
  zip.addFile('xl/sharedStrings.xml', this.generateSharedStrings(sheets));
536
538
  // Add each worksheet
537
539
  sheets.forEach((sheet, index) => {
538
- zip.addFile(`xl/worksheets/sheet${index + 1}.xml`, this.generateWorksheet(sheet));
540
+ zip.addFile(`xl/worksheets/sheet${index + 1}.xml`, this.generateWorksheet(sheet, styleRegistry));
539
541
  });
540
542
  const buffer = zip.generate();
541
543
  return new Blob([buffer.buffer], {
542
544
  type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
543
545
  });
544
546
  }
547
+ /**
548
+ * Collect all unique styles from sheets
549
+ */
550
+ static collectStyles(sheets) {
551
+ const registry = {
552
+ fonts: new Map(),
553
+ fills: new Map(),
554
+ borders: new Map(),
555
+ cellXfs: new Map()
556
+ };
557
+ // Add default font (Calibri 11)
558
+ const defaultFont = this.serializeFont({});
559
+ registry.fonts.set(defaultFont, 0);
560
+ // Add required fills (none and gray125)
561
+ registry.fills.set('none', 0);
562
+ registry.fills.set('gray125', 1);
563
+ // Add default border
564
+ registry.borders.set('none', 0);
565
+ // Add default cellXf (no style)
566
+ registry.cellXfs.set('default', 0);
567
+ // Collect styles from all cells
568
+ sheets.forEach((sheet) => {
569
+ const rows = sheet.getRows();
570
+ rows.forEach((row) => {
571
+ row.getCells().forEach((cell) => {
572
+ const cellData = cell.toData();
573
+ if (cellData.style) {
574
+ this.registerStyle(cellData.style, registry);
575
+ }
576
+ });
577
+ });
578
+ });
579
+ return registry;
580
+ }
581
+ /**
582
+ * Register a style and get its cellXf index
583
+ */
584
+ static registerStyle(style, registry) {
585
+ var _a, _b, _c, _d;
586
+ // Serialize and register font
587
+ const fontKey = this.serializeFont(style);
588
+ if (!registry.fonts.has(fontKey)) {
589
+ registry.fonts.set(fontKey, registry.fonts.size);
590
+ }
591
+ // Serialize and register fill
592
+ const fillKey = this.serializeFill(style);
593
+ if (fillKey !== 'none' && !registry.fills.has(fillKey)) {
594
+ registry.fills.set(fillKey, registry.fills.size);
595
+ }
596
+ // Serialize and register border
597
+ const borderKey = this.serializeBorder(style);
598
+ if (borderKey !== 'none' && !registry.borders.has(borderKey)) {
599
+ registry.borders.set(borderKey, registry.borders.size);
600
+ }
601
+ // Create cellXf key
602
+ const fontIndex = (_a = registry.fonts.get(fontKey)) !== null && _a !== void 0 ? _a : 0;
603
+ const fillIndex = (_b = registry.fills.get(fillKey)) !== null && _b !== void 0 ? _b : 0;
604
+ const borderIndex = (_c = registry.borders.get(borderKey)) !== null && _c !== void 0 ? _c : 0;
605
+ let alignment = this.serializeAlignment(style);
606
+ if (!alignment)
607
+ alignment = '{}';
608
+ const xfKey = `f${fontIndex}:l${fillIndex}:b${borderIndex}:a${alignment}`;
609
+ if (!registry.cellXfs.has(xfKey)) {
610
+ registry.cellXfs.set(xfKey, registry.cellXfs.size);
611
+ }
612
+ return (_d = registry.cellXfs.get(xfKey)) !== null && _d !== void 0 ? _d : 0;
613
+ }
614
+ /**
615
+ * Get style index for a cell style
616
+ */
617
+ static getStyleIndex(style, registry) {
618
+ if (!style)
619
+ return 0;
620
+ return this.registerStyle(style, registry);
621
+ }
622
+ static serializeFont(style) {
623
+ var _a, _b, _c, _d, _e, _f;
624
+ const bold = style.bold || ((_a = style.font) === null || _a === void 0 ? void 0 : _a.bold);
625
+ const italic = style.italic || ((_b = style.font) === null || _b === void 0 ? void 0 : _b.italic);
626
+ const underline = style.underline || ((_c = style.font) === null || _c === void 0 ? void 0 : _c.underline);
627
+ const color = style.color || ((_d = style.font) === null || _d === void 0 ? void 0 : _d.color);
628
+ const size = style.fontSize || ((_e = style.font) === null || _e === void 0 ? void 0 : _e.size) || 11;
629
+ const name = style.fontFamily || ((_f = style.font) === null || _f === void 0 ? void 0 : _f.name) || 'Calibri';
630
+ return JSON.stringify({ bold, italic, underline, color, size, name });
631
+ }
632
+ static serializeFill(style) {
633
+ var _a;
634
+ const bgColor = style.backgroundColor || ((_a = style.fill) === null || _a === void 0 ? void 0 : _a.fgColor);
635
+ if (!bgColor)
636
+ return 'none';
637
+ return JSON.stringify({ bgColor });
638
+ }
639
+ static serializeBorder(style) {
640
+ if (!style.border && !style.borderAll)
641
+ return 'none';
642
+ return JSON.stringify({ border: style.border, borderAll: style.borderAll });
643
+ }
644
+ static serializeAlignment(style) {
645
+ const h = style.alignment;
646
+ const v = style.verticalAlignment;
647
+ const wrap = style.wrapText;
648
+ if (!h && !v && !wrap)
649
+ return '';
650
+ return JSON.stringify({ h, v, wrap });
651
+ }
545
652
  static escapeXml(str) {
546
653
  if (typeof str !== 'string')
547
654
  return String(str !== null && str !== void 0 ? str : '');
@@ -595,17 +702,150 @@ class ExcelWriter {
595
702
  xml += '</Relationships>';
596
703
  return xml;
597
704
  }
598
- static generateStyles() {
705
+ static generateStyles(registry) {
599
706
  let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
600
707
  xml += '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">';
601
- xml += '<fonts count="1"><font><sz val="11"/><name val="Calibri"/></font></fonts>';
602
- xml += '<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>';
603
- xml += '<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>';
708
+ // Generate fonts
709
+ xml += `<fonts count="${registry.fonts.size}">`;
710
+ const fontEntries = Array.from(registry.fonts.entries()).sort((a, b) => a[1] - b[1]);
711
+ for (const [fontKey] of fontEntries) {
712
+ const font = JSON.parse(fontKey);
713
+ xml += '<font>';
714
+ if (font.bold)
715
+ xml += '<b/>';
716
+ if (font.italic)
717
+ xml += '<i/>';
718
+ if (font.underline)
719
+ xml += '<u/>';
720
+ xml += `<sz val="${font.size || 11}"/>`;
721
+ if (font.color) {
722
+ const colorHex = this.colorToARGB(font.color);
723
+ xml += `<color rgb="${colorHex}"/>`;
724
+ }
725
+ xml += `<name val="${font.name || 'Calibri'}"/>`;
726
+ xml += '</font>';
727
+ }
728
+ xml += '</fonts>';
729
+ // Generate fills
730
+ xml += `<fills count="${registry.fills.size}">`;
731
+ const fillEntries = Array.from(registry.fills.entries()).sort((a, b) => a[1] - b[1]);
732
+ for (const [fillKey] of fillEntries) {
733
+ if (fillKey === 'none') {
734
+ xml += '<fill><patternFill patternType="none"/></fill>';
735
+ }
736
+ else if (fillKey === 'gray125') {
737
+ xml += '<fill><patternFill patternType="gray125"/></fill>';
738
+ }
739
+ else {
740
+ const fill = JSON.parse(fillKey);
741
+ const colorHex = this.colorToARGB(fill.bgColor);
742
+ xml += `<fill><patternFill patternType="solid"><fgColor rgb="${colorHex}"/><bgColor indexed="64"/></patternFill></fill>`;
743
+ }
744
+ }
745
+ xml += '</fills>';
746
+ // Generate borders
747
+ xml += `<borders count="${registry.borders.size}">`;
748
+ const borderEntries = Array.from(registry.borders.entries()).sort((a, b) => a[1] - b[1]);
749
+ for (const [borderKey] of borderEntries) {
750
+ if (borderKey === 'none') {
751
+ xml += '<border><left/><right/><top/><bottom/><diagonal/></border>';
752
+ }
753
+ else {
754
+ const borderData = JSON.parse(borderKey);
755
+ xml += '<border>';
756
+ if (borderData.borderAll) {
757
+ const style = borderData.borderAll.style || 'thin';
758
+ const color = borderData.borderAll.color ? this.colorToARGB(borderData.borderAll.color) : 'FF000000';
759
+ xml += `<left style="${style}"><color rgb="${color}"/></left>`;
760
+ xml += `<right style="${style}"><color rgb="${color}"/></right>`;
761
+ xml += `<top style="${style}"><color rgb="${color}"/></top>`;
762
+ xml += `<bottom style="${style}"><color rgb="${color}"/></bottom>`;
763
+ }
764
+ else if (borderData.border) {
765
+ const b = borderData.border;
766
+ xml += this.generateBorderEdge('left', b.left);
767
+ xml += this.generateBorderEdge('right', b.right);
768
+ xml += this.generateBorderEdge('top', b.top);
769
+ xml += this.generateBorderEdge('bottom', b.bottom);
770
+ }
771
+ xml += '<diagonal/></border>';
772
+ }
773
+ }
774
+ xml += '</borders>';
775
+ // Generate cellStyleXfs (base styles)
604
776
  xml += '<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>';
605
- xml += '<cellXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/></cellXfs>';
777
+ // Generate cellXfs (cell formats)
778
+ xml += `<cellXfs count="${registry.cellXfs.size}">`;
779
+ const xfEntries = Array.from(registry.cellXfs.entries()).sort((a, b) => a[1] - b[1]);
780
+ for (const [xfKey] of xfEntries) {
781
+ if (xfKey === 'default') {
782
+ xml += '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>';
783
+ }
784
+ else {
785
+ // Parse xf key: f{fontIndex}:l{fillIndex}:b{borderIndex}:a{alignment}
786
+ const parts = xfKey.split(':');
787
+ const fontId = parseInt(parts[0].substring(1)) || 0;
788
+ const fillId = parseInt(parts[1].substring(1)) || 0;
789
+ const borderId = parseInt(parts[2].substring(1)) || 0;
790
+ const alignmentJson = parts[3].substring(1);
791
+ let xf = `<xf numFmtId="0" fontId="${fontId}" fillId="${fillId}" borderId="${borderId}" xfId="0"`;
792
+ if (fontId > 0)
793
+ xf += ' applyFont="1"';
794
+ if (fillId > 0)
795
+ xf += ' applyFill="1"';
796
+ if (borderId > 0)
797
+ xf += ' applyBorder="1"';
798
+ let align = {};
799
+ if (alignmentJson && alignmentJson.trim().startsWith('{')) {
800
+ try {
801
+ align = JSON.parse(alignmentJson);
802
+ }
803
+ catch (e) {
804
+ align = {};
805
+ }
806
+ }
807
+ if (Object.keys(align).length > 0) {
808
+ xf += ' applyAlignment="1">';
809
+ xf += '<alignment';
810
+ if (align.h)
811
+ xf += ` horizontal="${align.h}"`;
812
+ if (align.v)
813
+ xf += ` vertical="${align.v === 'middle' ? 'center' : align.v}"`;
814
+ if (align.wrap)
815
+ xf += ' wrapText="1"';
816
+ xf += '/></xf>';
817
+ }
818
+ else {
819
+ xf += '/>';
820
+ }
821
+ xml += xf;
822
+ }
823
+ }
824
+ xml += '</cellXfs>';
606
825
  xml += '</styleSheet>';
607
826
  return xml;
608
827
  }
828
+ static generateBorderEdge(side, edge) {
829
+ if (!edge || !edge.style)
830
+ return `<${side}/>`;
831
+ const color = edge.color ? this.colorToARGB(edge.color) : 'FF000000';
832
+ return `<${side} style="${edge.style}"><color rgb="${color}"/></${side}>`;
833
+ }
834
+ static colorToARGB(color) {
835
+ if (!color)
836
+ return 'FF000000';
837
+ // Remove # if present
838
+ let hex = color.replace('#', '').toUpperCase();
839
+ // Add alpha if not present (6 chars -> 8 chars)
840
+ if (hex.length === 6) {
841
+ hex = 'FF' + hex;
842
+ }
843
+ // Handle 3 char hex
844
+ if (hex.length === 3) {
845
+ hex = 'FF' + hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
846
+ }
847
+ return hex;
848
+ }
609
849
  static generateSharedStrings(sheets) {
610
850
  const strings = [];
611
851
  sheets.forEach((sheet) => {
@@ -629,14 +869,32 @@ class ExcelWriter {
629
869
  xml += '</sst>';
630
870
  return xml;
631
871
  }
632
- static generateWorksheet(sheet) {
872
+ static generateWorksheet(sheet, styleRegistry) {
633
873
  const rows = sheet.getRows();
874
+ const columns = sheet.getColumns();
634
875
  const allStrings = this.collectAllStrings(sheet);
635
876
  let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
636
877
  xml += '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">';
637
878
  // Add sheet dimension
638
879
  const dimension = this.getSheetDimension(rows);
639
880
  xml += `<dimension ref="${dimension}"/>`;
881
+ // Add column widths if defined
882
+ if (columns.length > 0) {
883
+ xml += '<cols>';
884
+ columns.forEach((col, index) => {
885
+ const colData = col.toData();
886
+ if (colData.width !== undefined && colData.width > 0) {
887
+ // Excel width is in character units, convert from pixels: width / 7
888
+ const excelWidth = colData.width / 7;
889
+ xml += `<col min="${index + 1}" max="${index + 1}" width="${excelWidth}" customWidth="1"`;
890
+ if (colData.hidden) {
891
+ xml += ' hidden="1"';
892
+ }
893
+ xml += '/>';
894
+ }
895
+ });
896
+ xml += '</cols>';
897
+ }
640
898
  xml += '<sheetData>';
641
899
  rows.forEach((row, rowIndex) => {
642
900
  const cells = row.getCells();
@@ -645,37 +903,54 @@ class ExcelWriter {
645
903
  cells.forEach((cell, colIndex) => {
646
904
  const cellData = cell.toData();
647
905
  const cellRef = this.columnToLetter(colIndex + 1) + (rowIndex + 1);
906
+ const styleIndex = this.getStyleIndex(cellData.style, styleRegistry);
907
+ const styleAttr = styleIndex > 0 ? ` s="${styleIndex}"` : '';
648
908
  if (cellData.formula) {
649
- xml += `<c r="${cellRef}"><f>${this.escapeXml(cellData.formula)}</f></c>`;
909
+ xml += `<c r="${cellRef}"${styleAttr}><f>${this.escapeXml(cellData.formula)}</f></c>`;
650
910
  }
651
911
  else if (cellData.value !== null && cellData.value !== undefined && cellData.value !== '') {
652
912
  if (typeof cellData.value === 'number') {
653
- xml += `<c r="${cellRef}"><v>${cellData.value}</v></c>`;
913
+ xml += `<c r="${cellRef}"${styleAttr}><v>${cellData.value}</v></c>`;
654
914
  }
655
915
  else if (typeof cellData.value === 'boolean') {
656
- xml += `<c r="${cellRef}" t="b"><v>${cellData.value ? 1 : 0}</v></c>`;
916
+ xml += `<c r="${cellRef}"${styleAttr} t="b"><v>${cellData.value ? 1 : 0}</v></c>`;
657
917
  }
658
918
  else if (cellData.type === 'date') {
659
919
  const excelDate = DateFormatter.toExcelDate(cellData.value);
660
- xml += `<c r="${cellRef}"><v>${excelDate}</v></c>`;
920
+ xml += `<c r="${cellRef}"${styleAttr}><v>${excelDate}</v></c>`;
661
921
  }
662
922
  else {
663
923
  // String value - use shared string index
664
924
  const stringIndex = allStrings.indexOf(String(cellData.value));
665
925
  if (stringIndex >= 0) {
666
- xml += `<c r="${cellRef}" t="s"><v>${stringIndex}</v></c>`;
926
+ xml += `<c r="${cellRef}"${styleAttr} t="s"><v>${stringIndex}</v></c>`;
667
927
  }
668
928
  else {
669
929
  // Inline string as fallback
670
- xml += `<c r="${cellRef}" t="inlineStr"><is><t>${this.escapeXml(String(cellData.value))}</t></is></c>`;
930
+ xml += `<c r="${cellRef}"${styleAttr} t="inlineStr"><is><t>${this.escapeXml(String(cellData.value))}</t></is></c>`;
671
931
  }
672
932
  }
673
933
  }
934
+ else if (styleIndex > 0) {
935
+ // Empty cell with style
936
+ xml += `<c r="${cellRef}"${styleAttr}/>`;
937
+ }
674
938
  });
675
939
  xml += '</row>';
676
940
  }
677
941
  });
678
942
  xml += '</sheetData>';
943
+ // Add merged cells if any
944
+ const sheetData = sheet.toData();
945
+ if (sheetData.mergeCells && sheetData.mergeCells.length > 0) {
946
+ xml += '<mergeCells>';
947
+ sheetData.mergeCells.forEach(merge => {
948
+ const startRef = this.columnToLetter(merge.startCol + 1) + (merge.startRow + 1);
949
+ const endRef = this.columnToLetter(merge.endCol + 1) + (merge.endRow + 1);
950
+ xml += `<mergeCell ref="${startRef}:${endRef}"/>`;
951
+ });
952
+ xml += '</mergeCells>';
953
+ }
679
954
  xml += '</worksheet>';
680
955
  return xml;
681
956
  }
@@ -969,23 +1244,46 @@ class ExportBuilder {
969
1244
  });
970
1245
  return this;
971
1246
  }
972
- addDataRows(data, fields) {
973
- data.forEach(item => {
1247
+ /**
1248
+ * Add multiple data rows to the sheet
1249
+ * @param data Array of row data
1250
+ * @param fields Optional array of field names (for object data)
1251
+ * @param styles Optional array of styles per row or per cell
1252
+ */
1253
+ addDataRows(data, fields, styles) {
1254
+ data.forEach((item, rowIdx) => {
974
1255
  const row = this.currentSheet.createRow();
1256
+ let rowStyle = undefined;
1257
+ let cellStyles = undefined;
1258
+ if (styles && styles[rowIdx]) {
1259
+ if (Array.isArray(styles[rowIdx])) {
1260
+ cellStyles = styles[rowIdx];
1261
+ }
1262
+ else {
1263
+ rowStyle = styles[rowIdx];
1264
+ }
1265
+ }
975
1266
  if (fields && fields.length > 0) {
976
- fields.forEach(field => {
1267
+ fields.forEach((field, colIdx) => {
977
1268
  const value = this.getNestedValue(item, field);
978
- row.createCell(value);
1269
+ const style = cellStyles ? cellStyles[colIdx] : rowStyle;
1270
+ row.createCell(value, style);
979
1271
  });
980
1272
  }
981
1273
  else if (Array.isArray(item)) {
982
- item.forEach(value => row.createCell(value));
1274
+ item.forEach((value, colIdx) => {
1275
+ const style = cellStyles ? cellStyles[colIdx] : rowStyle;
1276
+ row.createCell(value, style);
1277
+ });
983
1278
  }
984
1279
  else if (typeof item === 'object') {
985
- Object.values(item).forEach(value => row.createCell(value));
1280
+ Object.values(item).forEach((value, colIdx) => {
1281
+ const style = cellStyles ? cellStyles[colIdx] : rowStyle;
1282
+ row.createCell(value, style);
1283
+ });
986
1284
  }
987
1285
  else {
988
- row.createCell(item);
1286
+ row.createCell(item, rowStyle);
989
1287
  }
990
1288
  });
991
1289
  return this;
@@ -1044,6 +1342,55 @@ class ExportBuilder {
1044
1342
  });
1045
1343
  return this;
1046
1344
  }
1345
+ /**
1346
+ * Merge cells in the current worksheet
1347
+ * @param startRow Start row index (0-based)
1348
+ * @param startCol Start column index (0-based)
1349
+ * @param endRow End row index (0-based)
1350
+ * @param endCol End column index (0-based)
1351
+ */
1352
+ mergeCells(startRow, startCol, endRow, endCol) {
1353
+ this.currentSheet.mergeCells(startRow, startCol, endRow, endCol);
1354
+ return this;
1355
+ }
1356
+ /**
1357
+ * Set alignment for a specific cell
1358
+ * @param row Row index (0-based)
1359
+ * @param col Column index (0-based)
1360
+ * @param horizontal Horizontal alignment
1361
+ * @param vertical Vertical alignment (optional)
1362
+ */
1363
+ setAlignment(row, col, horizontal, vertical) {
1364
+ const rowObj = this.currentSheet.getRow(row);
1365
+ if (rowObj) {
1366
+ const cells = rowObj.getCells();
1367
+ if (cells[col]) {
1368
+ const style = StyleBuilder.create().align(horizontal);
1369
+ if (vertical) {
1370
+ style.verticalAlign(vertical);
1371
+ }
1372
+ cells[col].setStyle(style.build());
1373
+ }
1374
+ }
1375
+ return this;
1376
+ }
1377
+ /**
1378
+ * Set alignment for a range of cells
1379
+ * @param startRow Start row index (0-based)
1380
+ * @param startCol Start column index (0-based)
1381
+ * @param endRow End row index (0-based)
1382
+ * @param endCol End column index (0-based)
1383
+ * @param horizontal Horizontal alignment
1384
+ * @param vertical Vertical alignment (optional)
1385
+ */
1386
+ setRangeAlignment(startRow, startCol, endRow, endCol, horizontal, vertical) {
1387
+ for (let r = startRow; r <= endRow; r++) {
1388
+ for (let c = startCol; c <= endCol; c++) {
1389
+ this.setAlignment(r, c, horizontal, vertical);
1390
+ }
1391
+ }
1392
+ return this;
1393
+ }
1047
1394
  autoSizeColumns() {
1048
1395
  const rows = this.currentSheet.getRows();
1049
1396
  const maxLengths = [];
@@ -1168,6 +1515,28 @@ class SheetBuilder {
1168
1515
  });
1169
1516
  return this;
1170
1517
  }
1518
+ /**
1519
+ * Set the width of a specific column
1520
+ * @param colIndex Column index
1521
+ * @param width Width to set
1522
+ */
1523
+ setColumnWidth(colIndex, width) {
1524
+ const column = this.getOrCreateColumn(colIndex);
1525
+ column.setWidth(width);
1526
+ return this;
1527
+ }
1528
+ /**
1529
+ * Set the height of a specific row
1530
+ * @param rowIndex Row index
1531
+ * @param height Height to set
1532
+ */
1533
+ setRowHeight(rowIndex, height) {
1534
+ const row = this.worksheet.getRow(rowIndex);
1535
+ if (row) {
1536
+ row.setHeight(height);
1537
+ }
1538
+ return this;
1539
+ }
1171
1540
  /**
1172
1541
  * Add a title row
1173
1542
  * @param title Title text