@justybase/spreadsheet-tasks 1.1.1 → 1.3.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.
package/README.md CHANGED
@@ -30,6 +30,42 @@ A high-performance TypeScript library for reading and writing Excel files in XLS
30
30
  npm install @justybase/spreadsheet-tasks
31
31
  ```
32
32
 
33
+ ## 🌐 Browser Support & CDN (Zero Dependencies)
34
+
35
+ This library includes browser-compatible modules to generate XLSB and XLSX files directly on the client side without relying on Node.js APIs or external dependencies.
36
+
37
+ To bundle the browser version into a single minified file (e.g. for deployment to a CDN), run the following command in the project root:
38
+
39
+ ```bash
40
+ npx esbuild browser/browser-spreadsheet.js --bundle --minify --format=esm --outfile=browser/justybase-spreadsheet.min.js
41
+ ```
42
+
43
+ You can then import this single file via a script tag in any web application:
44
+
45
+ ```html
46
+ <script type="module">
47
+ import { downloadXlsb, downloadXlsx } from 'https://your-cdn.com/justybase-spreadsheet.min.js';
48
+
49
+ const headers = ["ID", "Name", "Score"];
50
+ const rows = [
51
+ [1, "Alice", 99.5],
52
+ [2, "Bob", 88.0]
53
+ ];
54
+
55
+ // Generates the file and triggers the browser download automatically
56
+ document.getElementById('downloadBtn').addEventListener('click', () => {
57
+ downloadXlsb('report.xlsb', rows, headers, { headerStyle: 'bold+fill' });
58
+ });
59
+ </script>
60
+ ```
61
+
62
+ **Features included in the browser build:**
63
+ - Built-in Zip archiving (no external libraries needed)
64
+ - Memory-efficient `Uint8Array` buffer handling
65
+ - Auto-fitted column widths
66
+ - Frozen and styled headers (styles: `bold`, `fill`, `bold+fill`)
67
+ - Auto-filters enabled by default
68
+
33
69
  ## 🔧 Quick Start
34
70
 
35
71
  ### Writing Excel Files
@@ -0,0 +1,34 @@
1
+ export interface FormattedCell {
2
+ value: any;
3
+ format: string;
4
+ }
5
+ export declare const F: {
6
+ THOUSANDS_SEP: string;
7
+ CURRENCY_PLN: string;
8
+ CURRENCY_EUR: string;
9
+ PERCENTAGE: string;
10
+ SCIENTIFIC: string;
11
+ TWO_DECIMALS: string;
12
+ TEXT: string;
13
+ LEADING_ZEROS: string;
14
+ DATE_SHORT: string;
15
+ DATE_LONG: string;
16
+ DATE_DAY_MONTH_YEAR: string;
17
+ DATE_ISO: string;
18
+ DATE_MONTH_YEAR: string;
19
+ DATE_WEEKDAY: string;
20
+ DATE_DAY_MONTH: string;
21
+ DATE_YEAR_ONLY: string;
22
+ DATETIME_SHORT: string;
23
+ DATETIME_LONG: string;
24
+ TIME_HH_MM: string;
25
+ TIME_HH_MM_SS: string;
26
+ TIME_12H: string;
27
+ DATETIME_24H: string;
28
+ DATETIME_ISO: string;
29
+ TIME_MS: string;
30
+ };
31
+ export declare function isFormattedCell(val: any): val is FormattedCell;
32
+ export declare function unwrapCell(val: any): any;
33
+ export declare function getFormat(val: any): string | null;
34
+ //# sourceMappingURL=Formats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Formats.d.ts","sourceRoot":"","sources":["../src/Formats.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,GAAG,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;CA2Bb,CAAC;AAEF,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,aAAa,CAE9D;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CAKxC;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,IAAI,CAKjD"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.F = void 0;
4
+ exports.isFormattedCell = isFormattedCell;
5
+ exports.unwrapCell = unwrapCell;
6
+ exports.getFormat = getFormat;
7
+ exports.F = {
8
+ THOUSANDS_SEP: '#,##0',
9
+ CURRENCY_PLN: '#,##0.00 "z\u0142"',
10
+ CURRENCY_EUR: '#,##0.00 \u20AC',
11
+ PERCENTAGE: '0%',
12
+ SCIENTIFIC: '0.00E+00',
13
+ TWO_DECIMALS: '#,##0.00',
14
+ TEXT: '@',
15
+ LEADING_ZEROS: '000000000',
16
+ DATE_SHORT: 'dd.mm.yyyy',
17
+ DATE_LONG: 'd mmmm yyyy',
18
+ DATE_DAY_MONTH_YEAR: 'dd-mm-yyyy',
19
+ DATE_ISO: 'yyyy-mm-dd',
20
+ DATE_MONTH_YEAR: 'mmmm yyyy',
21
+ DATE_WEEKDAY: 'dddd, d mmmm yyyy',
22
+ DATE_DAY_MONTH: 'd mmmm',
23
+ DATE_YEAR_ONLY: 'yyyy',
24
+ DATETIME_SHORT: 'dd.mm.yyyy hh:mm',
25
+ DATETIME_LONG: 'd mmmm yyyy hh:mm:ss',
26
+ TIME_HH_MM: 'hh:mm',
27
+ TIME_HH_MM_SS: 'hh:mm:ss',
28
+ TIME_12H: 'h:mm AM/PM',
29
+ DATETIME_24H: 'dd.mm.yyyy hh:mm:ss',
30
+ DATETIME_ISO: 'yyyy-mm-dd"T"hh:mm:ss',
31
+ TIME_MS: 'hh:mm:ss.000',
32
+ };
33
+ function isFormattedCell(val) {
34
+ return val !== null && val !== undefined && typeof val === 'object' && 'format' in val && typeof val.format === 'string';
35
+ }
36
+ function unwrapCell(val) {
37
+ if (isFormattedCell(val)) {
38
+ return val.value;
39
+ }
40
+ return val;
41
+ }
42
+ function getFormat(val) {
43
+ if (isFormattedCell(val)) {
44
+ return val.format;
45
+ }
46
+ return null;
47
+ }
48
+ //# sourceMappingURL=Formats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Formats.js","sourceRoot":"","sources":["../src/Formats.ts"],"names":[],"mappings":";;;AAkCA,0CAEC;AAED,gCAKC;AAED,8BAKC;AA7CY,QAAA,CAAC,GAAG;IACb,aAAa,EAAE,OAAO;IACtB,YAAY,EAAE,oBAAoB;IAClC,YAAY,EAAE,iBAAiB;IAC/B,UAAU,EAAE,IAAI;IAChB,UAAU,EAAE,UAAU;IACtB,YAAY,EAAE,UAAU;IACxB,IAAI,EAAE,GAAG;IACT,aAAa,EAAE,WAAW;IAE1B,UAAU,EAAE,YAAY;IACxB,SAAS,EAAE,aAAa;IACxB,mBAAmB,EAAE,YAAY;IACjC,QAAQ,EAAE,YAAY;IACtB,eAAe,EAAE,WAAW;IAC5B,YAAY,EAAE,mBAAmB;IACjC,cAAc,EAAE,QAAQ;IACxB,cAAc,EAAE,MAAM;IAEtB,cAAc,EAAE,kBAAkB;IAClC,aAAa,EAAE,sBAAsB;IACrC,UAAU,EAAE,OAAO;IACnB,aAAa,EAAE,UAAU;IACzB,QAAQ,EAAE,YAAY;IACtB,YAAY,EAAE,qBAAqB;IACnC,YAAY,EAAE,uBAAuB;IACrC,OAAO,EAAE,cAAc;CAC1B,CAAC;AAEF,SAAgB,eAAe,CAAC,GAAQ;IACpC,OAAO,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,QAAQ,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC;AAC7H,CAAC;AAED,SAAgB,UAAU,CAAC,GAAQ;IAC/B,IAAI,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,KAAK,CAAC;IACrB,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,SAAgB,SAAS,CAAC,GAAQ;IAC9B,IAAI,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,MAAM,CAAC;IACtB,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC"}
@@ -10,6 +10,9 @@ export declare class XlsbWriter {
10
10
  private colWidths;
11
11
  private _autofilterIsOn;
12
12
  private _oaEpoch;
13
+ private _nextFmtId;
14
+ private _nextXfIdx;
15
+ private _stFmtRecords;
13
16
  private _sheet1Bytes;
14
17
  private _workbookBinStart;
15
18
  private _workbookBinMiddle;
@@ -32,6 +35,12 @@ export declare class XlsbWriter {
32
35
  private isStreaming;
33
36
  constructor(filePath: string);
34
37
  private _sanitizeSheetName;
38
+ private _registerFormat;
39
+ private static _buildBrtRecord;
40
+ private static _encodeVlq;
41
+ private static _buildStFmtRecord;
42
+ private static _buildXfRecord;
43
+ private _buildStylesBin;
35
44
  addSheet(sheetName: string, hidden?: boolean): void;
36
45
  /**
37
46
  * Start a new sheet in streaming mode. Call writeRow() for each row, then endSheet() when done.
@@ -60,7 +69,7 @@ export declare class XlsbWriter {
60
69
  writeDouble(bigBuf: BigBuffer, val: number, colNum: number, styleNum?: number): void;
61
70
  writeBool(bigBuf: BigBuffer, val: boolean, colNum: number): void;
62
71
  writeDateTime(bigBuf: BigBuffer, date: Date, colNum: number): void;
63
- writeString(bigBuf: BigBuffer, val: string, colNum: number, bolded?: boolean): void;
72
+ writeString(bigBuf: BigBuffer, val: string, colNum: number, bolded?: boolean, styleOverride?: number): void;
64
73
  private saveSst;
65
74
  private _writeFilterDefinedName;
66
75
  finalize(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"XlsbWriter.d.ts","sourceRoot":"","sources":["../src/XlsbWriter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAmBxC,qBAAa,UAAU;IACnB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,OAAO,CAA8B;IAE7C,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,eAAe,CAAkB;IAEzC,OAAO,CAAC,QAAQ,CAAS;IAEzB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,yBAAyB,CAAS;IAC1C,OAAO,CAAC,yBAAyB,CAAS;IAC1C,OAAO,CAAC,yBAAyB,CAAS;IAG1C,OAAO,CAAC,kBAAkB,CAA0B;IACpD,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,oBAAoB,CAAa;IACzC,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,wBAAwB,CAAkB;IAClD,OAAO,CAAC,WAAW,CAAkB;gBAGzB,QAAQ,EAAE,MAAM;IA4K5B,OAAO,CAAC,kBAAkB;IAc1B,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,OAAe,GAAG,IAAI;IAa1D;;;;;;OAMG;IACH,UAAU,CACN,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,EAAE,EAClB,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAO,GAC3D,IAAI;IA8FP;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;IA6C1B;;;OAGG;IACH,QAAQ,IAAI,IAAI;IA0DhB,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,GAAE,MAAM,EAAE,GAAG,IAAW,EAAE,YAAY,GAAE,OAAc,GAAG,IAAI;IAgK9F,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAmB7F,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,GAAG,IAAI;IAahG,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,GAAG,IAAI;IAYvF,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAShE,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAMlE,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,OAAe,GAAG,IAAI;IAsB1F,OAAO,CAAC,OAAO;IA6Cf,OAAO,CAAC,uBAAuB;IA2B/B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAyH5B;AAED,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"XlsbWriter.d.ts","sourceRoot":"","sources":["../src/XlsbWriter.ts"],"names":[],"mappings":"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"}
@@ -41,6 +41,7 @@ const fs = __importStar(require("fs"));
41
41
  const archiver_1 = __importDefault(require("archiver"));
42
42
  const stream_1 = require("stream");
43
43
  const BigBuffer_1 = require("./BigBuffer");
44
+ const Formats_1 = require("./Formats");
44
45
  const INVALID_SHEET_NAME_CHARS = /[\\/*?[\]:]/g;
45
46
  class XlsbWriter {
46
47
  constructor(filePath) {
@@ -51,6 +52,9 @@ class XlsbWriter {
51
52
  this.sstCntAll = 0;
52
53
  this.colWidths = [];
53
54
  this._autofilterIsOn = false;
55
+ this._nextFmtId = 167;
56
+ this._nextXfIdx = 4;
57
+ this._stFmtRecords = new Map();
54
58
  // Streaming state
55
59
  this.currentSheetBuffer = null;
56
60
  this.currentSheetRowNum = 0;
@@ -229,6 +233,128 @@ class XlsbWriter {
229
233
  }
230
234
  return sanitized;
231
235
  }
236
+ _registerFormat(fmtString) {
237
+ if (this._stFmtRecords.has(fmtString)) {
238
+ return this._stFmtRecords.get(fmtString).xfIndex;
239
+ }
240
+ const numfmtId = this._nextFmtId++;
241
+ const xfIndex = this._nextXfIdx++;
242
+ this._stFmtRecords.set(fmtString, { numfmtId, xfIndex });
243
+ return xfIndex;
244
+ }
245
+ static _buildBrtRecord(type, data) {
246
+ const header = XlsbWriter._encodeVlq(type);
247
+ header.push(...XlsbWriter._encodeVlq(data.length));
248
+ return Buffer.concat([Buffer.from(header), data]);
249
+ }
250
+ static _encodeVlq(value) {
251
+ const result = [];
252
+ while (value >= 0x80) {
253
+ result.push((value & 0x7F) | 0x80);
254
+ value >>>= 7;
255
+ }
256
+ result.push(value & 0x7F);
257
+ return result;
258
+ }
259
+ static _buildStFmtRecord(ifmt, fmtString) {
260
+ const cch = fmtString.length;
261
+ const remaining = 2 + 4 + cch * 2;
262
+ const data = Buffer.alloc(2 + remaining);
263
+ data[0] = 0x2C;
264
+ data[1] = remaining;
265
+ data.writeUInt16LE(ifmt, 2);
266
+ data.writeUInt32LE(cch, 4);
267
+ data.write(fmtString, 8, cch * 2, 'utf16le');
268
+ return data;
269
+ }
270
+ static _buildXfRecord(fontId, ifmt, flags, byte6Extra = 0) {
271
+ const data = Buffer.alloc(18);
272
+ data[0] = 0x2F;
273
+ data[1] = 16;
274
+ data.writeUInt16LE(fontId, 2);
275
+ data.writeUInt16LE(ifmt, 4);
276
+ data[6] = byte6Extra;
277
+ data[14] = 0x10;
278
+ data[15] = 0x10;
279
+ data.writeUInt16LE(flags, 16);
280
+ return data;
281
+ }
282
+ _buildStylesBin() {
283
+ if (this._stFmtRecords.size === 0) {
284
+ return this._stylesBin;
285
+ }
286
+ const base = this._stylesBin;
287
+ // Find BrtEndFonts (0xE8, 0x04, 0x00)
288
+ let fontEndOff = -1;
289
+ for (let i = 3; i < base.length - 3; i++) {
290
+ if (base[i] === 0xE8 && base[i + 1] === 0x04 && base[i + 2] === 0x00) {
291
+ fontEndOff = i;
292
+ break;
293
+ }
294
+ }
295
+ // Find BrtBeginCellXFs (0xE9, 0x04)
296
+ let xfBeginOff = -1;
297
+ for (let i = fontEndOff + 3; i < base.length - 2; i++) {
298
+ if (base[i] === 0xE9 && base[i + 1] === 0x04) {
299
+ xfBeginOff = i;
300
+ break;
301
+ }
302
+ }
303
+ // Find BrtEndCellXFs (0xEA, 0x04, 0x00)
304
+ let xfEndOff = -1;
305
+ for (let i = xfBeginOff; i < base.length - 3; i++) {
306
+ if (base[i] === 0xEA && base[i + 1] === 0x04 && base[i + 2] === 0x00) {
307
+ xfEndOff = i;
308
+ break;
309
+ }
310
+ }
311
+ // Find BrtBeginFills (0xE3, 0x04) — start after BrtEndFonts
312
+ let fillBeginOff = -1;
313
+ for (let i = fontEndOff + 3; i < base.length - 2; i++) {
314
+ if (base[i] === 0xE3 && base[i + 1] === 0x04) {
315
+ fillBeginOff = i;
316
+ break;
317
+ }
318
+ }
319
+ const totalFmt = 2 + this._stFmtRecords.size;
320
+ const totalXf = 4 + this._stFmtRecords.size;
321
+ const parts = [];
322
+ // 1. BrtBeginStyleSheet
323
+ parts.push(base.subarray(0, 3));
324
+ // 2. NEW BrtFmt: VLQ(0x0267) + VLQ(4) + count(2 LE) + pad(2)
325
+ const fmtHeader = Buffer.alloc(4);
326
+ fmtHeader.writeUInt16LE(totalFmt, 0);
327
+ parts.push(XlsbWriter._buildBrtRecord(0x0267, fmtHeader));
328
+ // 3. BrtNumFmt records: 2 base + custom
329
+ parts.push(XlsbWriter._buildStFmtRecord(164, 'yyyy\\-mm\\-dd\\ hh:mm:ss'));
330
+ parts.push(XlsbWriter._buildStFmtRecord(166, 'yyyy\\-mm\\-dd'));
331
+ for (const [fmtString, st] of this._stFmtRecords) {
332
+ parts.push(XlsbWriter._buildStFmtRecord(st.numfmtId, fmtString));
333
+ }
334
+ // 4. BrtEndFonts
335
+ parts.push(XlsbWriter._buildBrtRecord(0x0268, Buffer.alloc(0)));
336
+ // 5. Copy fills+borders+BrtFont+cellStyleXFs from template
337
+ parts.push(base.subarray(fillBeginOff, xfBeginOff));
338
+ // 7. NEW BrtBeginCellXFs
339
+ const xfHeader = Buffer.alloc(4);
340
+ xfHeader.writeUInt16LE(totalXf, 0);
341
+ parts.push(XlsbWriter._buildBrtRecord(0x0269, xfHeader));
342
+ // 8. Base XF records
343
+ parts.push(XlsbWriter._buildXfRecord(0, 0, 0x0000));
344
+ parts.push(XlsbWriter._buildXfRecord(0, 164, 0x0001));
345
+ parts.push(XlsbWriter._buildXfRecord(0, 166, 0x0001));
346
+ parts.push(XlsbWriter._buildXfRecord(1, 0, 0x0000, 1));
347
+ // 9. Custom XF records
348
+ const sortedXf = [...this._stFmtRecords.entries()].sort((a, b) => a[1].xfIndex - b[1].xfIndex);
349
+ for (const [, st] of sortedXf) {
350
+ parts.push(XlsbWriter._buildXfRecord(0, st.numfmtId, 0x0001));
351
+ }
352
+ // 10. BrtEndCellXFs
353
+ parts.push(XlsbWriter._buildBrtRecord(0x026A, Buffer.alloc(0)));
354
+ // 11. Copy remaining template
355
+ parts.push(base.subarray(xfEndOff + 3));
356
+ return Buffer.concat(parts);
357
+ }
232
358
  addSheet(sheetName, hidden = false) {
233
359
  const sanitizedName = this._sanitizeSheetName(sheetName);
234
360
  this.sheetCount++;
@@ -339,20 +465,23 @@ class XlsbWriter {
339
465
  this.createRowHeader(bigBuf, this.currentSheetRowNum, this.currentSheetStartCol, this.currentSheetEndCol);
340
466
  // Write each cell
341
467
  for (let c = 0; c < row.length; c++) {
342
- const val = row[c];
343
- if (val === null || val === undefined)
468
+ const raw = row[c];
469
+ if (raw === null || raw === undefined)
344
470
  continue;
471
+ const fmtString = (0, Formats_1.getFormat)(raw);
472
+ const val = fmtString !== null ? (0, Formats_1.unwrapCell)(raw) : raw;
473
+ const styleNum = fmtString !== null ? this._registerFormat(fmtString) : 0;
345
474
  if (typeof val === 'number') {
346
475
  if (Number.isInteger(val)) {
347
476
  if (val >= this._rRkIntegerLowerLimit && val <= this._rRkIntegerUpperLimit) {
348
- this.writeRkNumberInteger(bigBuf, val, c);
477
+ this.writeRkNumberInteger(bigBuf, val, c, styleNum);
349
478
  }
350
479
  else {
351
- this.writeDouble(bigBuf, val, c);
480
+ this.writeDouble(bigBuf, val, c, styleNum);
352
481
  }
353
482
  }
354
483
  else {
355
- this.writeDouble(bigBuf, val, c);
484
+ this.writeDouble(bigBuf, val, c, styleNum);
356
485
  }
357
486
  }
358
487
  else if (typeof val === 'bigint') {
@@ -362,10 +491,11 @@ class XlsbWriter {
362
491
  this.writeBool(bigBuf, val, c);
363
492
  }
364
493
  else if (val instanceof Date) {
365
- this.writeDateTime(bigBuf, val, c);
494
+ const oaDate = (val.getTime() - this._oaEpoch) / 86400000;
495
+ this.writeDouble(bigBuf, oaDate, c, fmtString !== null ? styleNum : 1);
366
496
  }
367
497
  else {
368
- this.writeString(bigBuf, val.toString(), c);
498
+ this.writeString(bigBuf, val.toString(), c, false, fmtString !== null ? styleNum : undefined);
369
499
  }
370
500
  }
371
501
  this.currentSheetRowNum++;
@@ -442,8 +572,11 @@ class XlsbWriter {
442
572
  for (let r = 0; r < Math.min(rows.length, 100); r++) {
443
573
  const row = rows[r];
444
574
  for (let c = 0; c < row.length; c++) {
445
- const val = row[c];
446
- const len = val ? val.toString().length : 0;
575
+ const raw = row[c];
576
+ if (raw === null || raw === undefined)
577
+ continue;
578
+ const val = (0, Formats_1.isFormattedCell)(raw) ? raw.value : raw;
579
+ const len = val instanceof Date ? 10 : val.toString().length;
447
580
  let width = 1.25 * len + 2;
448
581
  if (width > 80)
449
582
  width = 80;
@@ -501,20 +634,23 @@ class XlsbWriter {
501
634
  this.createRowHeader(bigBuf, rowNum, startCol, endCol);
502
635
  const row = rows[r];
503
636
  for (let c = 0; c < row.length; c++) {
504
- const val = row[c];
505
- if (val === null || val === undefined)
637
+ const raw = row[c];
638
+ if (raw === null || raw === undefined)
506
639
  continue;
640
+ const fmtString = (0, Formats_1.getFormat)(raw);
641
+ const val = fmtString !== null ? (0, Formats_1.unwrapCell)(raw) : raw;
642
+ const styleNum = fmtString !== null ? this._registerFormat(fmtString) : 0;
507
643
  if (typeof val === 'number') {
508
644
  if (Number.isInteger(val)) {
509
645
  if (val >= this._rRkIntegerLowerLimit && val <= this._rRkIntegerUpperLimit) {
510
- this.writeRkNumberInteger(bigBuf, val, c);
646
+ this.writeRkNumberInteger(bigBuf, val, c, styleNum);
511
647
  }
512
648
  else {
513
- this.writeDouble(bigBuf, val, c);
649
+ this.writeDouble(bigBuf, val, c, styleNum);
514
650
  }
515
651
  }
516
652
  else {
517
- this.writeDouble(bigBuf, val, c);
653
+ this.writeDouble(bigBuf, val, c, styleNum);
518
654
  }
519
655
  }
520
656
  else if (typeof val === 'bigint') {
@@ -524,10 +660,11 @@ class XlsbWriter {
524
660
  this.writeBool(bigBuf, val, c);
525
661
  }
526
662
  else if (val instanceof Date) {
527
- this.writeDateTime(bigBuf, val, c);
663
+ const oaDate = (val.getTime() - this._oaEpoch) / 86400000;
664
+ this.writeDouble(bigBuf, oaDate, c, fmtString !== null ? styleNum : 1);
528
665
  }
529
666
  else {
530
- this.writeString(bigBuf, val.toString(), c);
667
+ this.writeString(bigBuf, val.toString(), c, false, fmtString !== null ? styleNum : undefined);
531
668
  }
532
669
  }
533
670
  rowNum++;
@@ -611,7 +748,7 @@ class XlsbWriter {
611
748
  const oaDate = (date.getTime() - this._oaEpoch) / 86400000;
612
749
  this.writeDouble(bigBuf, oaDate, colNum, 1);
613
750
  }
614
- writeString(bigBuf, val, colNum, bolded = false) {
751
+ writeString(bigBuf, val, colNum, bolded = false, styleOverride) {
615
752
  let index;
616
753
  if (this.sstDic.has(val)) {
617
754
  index = this.sstDic.get(val);
@@ -622,11 +759,12 @@ class XlsbWriter {
622
759
  this.sstCntUnique++;
623
760
  }
624
761
  this.sstCntAll++;
762
+ const styleNum = styleOverride !== undefined ? styleOverride : (bolded ? 3 : 0);
625
763
  bigBuf.ensureCapacity(17);
626
764
  bigBuf.writeUnsafeByte(7);
627
765
  bigBuf.writeUnsafeByte(12);
628
766
  bigBuf.writeUnsafeInt32LE(colNum);
629
- bigBuf.writeUnsafeByte(bolded ? 3 : 0);
767
+ bigBuf.writeUnsafeByte(styleNum);
630
768
  bigBuf.writeUnsafeByte(0);
631
769
  bigBuf.writeUnsafeByte(0);
632
770
  bigBuf.writeUnsafeByte(0);
@@ -692,7 +830,7 @@ class XlsbWriter {
692
830
  return new Promise((resolve, reject) => {
693
831
  try {
694
832
  this.saveSst();
695
- this.archive.append(this._stylesBin, { name: 'xl/styles.bin' });
833
+ this.archive.append(this._buildStylesBin(), { name: 'xl/styles.bin' });
696
834
  const wbBuffers = [];
697
835
  wbBuffers.push(this._workbookBinStart);
698
836
  for (const sheet of this.sheetList) {