@cj-tech-master/excelts 5.1.11 → 5.1.12
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/browser/modules/excel/stream/worksheet-writer.js +13 -5
- package/dist/browser/modules/excel/utils/cell-format.js +4 -37
- package/dist/browser/modules/excel/utils/merge-borders.d.ts +44 -0
- package/dist/browser/modules/excel/utils/merge-borders.js +105 -0
- package/dist/browser/modules/excel/worksheet.js +15 -5
- package/dist/browser/utils/utils.base.d.ts +8 -0
- package/dist/browser/utils/utils.base.js +51 -7
- package/dist/browser/utils/utils.browser.d.ts +1 -1
- package/dist/browser/utils/utils.browser.js +1 -1
- package/dist/browser/utils/utils.d.ts +1 -1
- package/dist/browser/utils/utils.js +1 -1
- package/dist/cjs/modules/excel/stream/worksheet-writer.js +13 -5
- package/dist/cjs/modules/excel/utils/cell-format.js +3 -36
- package/dist/cjs/modules/excel/utils/merge-borders.js +109 -0
- package/dist/cjs/modules/excel/worksheet.js +15 -5
- package/dist/cjs/utils/utils.base.js +52 -7
- package/dist/cjs/utils/utils.browser.js +2 -1
- package/dist/cjs/utils/utils.js +2 -1
- package/dist/esm/modules/excel/stream/worksheet-writer.js +13 -5
- package/dist/esm/modules/excel/utils/cell-format.js +4 -37
- package/dist/esm/modules/excel/utils/merge-borders.js +105 -0
- package/dist/esm/modules/excel/worksheet.js +15 -5
- package/dist/esm/utils/utils.base.js +51 -7
- package/dist/esm/utils/utils.browser.js +1 -1
- package/dist/esm/utils/utils.js +1 -1
- package/dist/iife/excelts.iife.js +140 -38
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +23 -23
- package/dist/types/modules/excel/utils/merge-borders.d.ts +44 -0
- package/dist/types/utils/utils.base.d.ts +8 -0
- package/dist/types/utils/utils.browser.d.ts +1 -1
- package/dist/types/utils/utils.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @cj-tech-master/excelts v5.1.
|
|
2
|
+
* @cj-tech-master/excelts v5.1.12
|
|
3
3
|
* TypeScript Excel Workbook Manager - Read and Write xlsx and csv Files.
|
|
4
4
|
* (c) 2026 cjnoname
|
|
5
5
|
* Released under the MIT License
|
|
@@ -3110,12 +3110,46 @@ var ExcelTS = (function(exports) {
|
|
|
3110
3110
|
const i = typeof value === "number" ? value : parseInt(value, 10);
|
|
3111
3111
|
return Number.isNaN(i) ? 0 : i;
|
|
3112
3112
|
}
|
|
3113
|
+
/**
|
|
3114
|
+
* Split an Excel numFmt string by semicolons, respecting quoted strings and brackets.
|
|
3115
|
+
*
|
|
3116
|
+
* Excel numFmt can have up to 4 sections: `positive ; negative ; zero ; text`.
|
|
3117
|
+
* Semicolons inside `"..."` (literal text) or `[...]` (locale/color tags) must NOT
|
|
3118
|
+
* be treated as section separators.
|
|
3119
|
+
*/
|
|
3120
|
+
function splitFormatSections(fmt) {
|
|
3121
|
+
const sections = [];
|
|
3122
|
+
let current = "";
|
|
3123
|
+
let inQuote = false;
|
|
3124
|
+
let inBracket = false;
|
|
3125
|
+
for (let i = 0; i < fmt.length; i++) {
|
|
3126
|
+
const char = fmt[i];
|
|
3127
|
+
if (char === "\"" && !inBracket) {
|
|
3128
|
+
inQuote = !inQuote;
|
|
3129
|
+
current += char;
|
|
3130
|
+
} else if (char === "[" && !inQuote) {
|
|
3131
|
+
inBracket = true;
|
|
3132
|
+
current += char;
|
|
3133
|
+
} else if (char === "]" && !inQuote) {
|
|
3134
|
+
inBracket = false;
|
|
3135
|
+
current += char;
|
|
3136
|
+
} else if (char === ";" && !inQuote && !inBracket) {
|
|
3137
|
+
sections.push(current);
|
|
3138
|
+
current = "";
|
|
3139
|
+
} else current += char;
|
|
3140
|
+
}
|
|
3141
|
+
sections.push(current);
|
|
3142
|
+
return sections;
|
|
3143
|
+
}
|
|
3144
|
+
/** Reusable regex — no capture groups, so safe for `test()`. */
|
|
3145
|
+
const DATE_FMT_RE = /[ymdhMsb]/;
|
|
3146
|
+
/** Strips bracket expressions `[...]` and quoted literals `"..."` from a format string. */
|
|
3147
|
+
const STRIP_BRACKETS_QUOTES_RE = /\[[^\]]*\]|"[^"]*"/g;
|
|
3113
3148
|
function isDateFmt(fmt) {
|
|
3114
3149
|
if (!fmt) return false;
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
return cleanFmt.match(/[ymdhMsb]+/) !== null;
|
|
3150
|
+
const clean = splitFormatSections(fmt)[0].replace(STRIP_BRACKETS_QUOTES_RE, "");
|
|
3151
|
+
if (clean.indexOf("@") > -1) return false;
|
|
3152
|
+
return DATE_FMT_RE.test(clean);
|
|
3119
3153
|
}
|
|
3120
3154
|
function parseBoolean(value) {
|
|
3121
3155
|
return value === true || value === "true" || value === 1 || value === "1";
|
|
@@ -5757,6 +5791,95 @@ var ExcelTS = (function(exports) {
|
|
|
5757
5791
|
return result;
|
|
5758
5792
|
}
|
|
5759
5793
|
|
|
5794
|
+
//#endregion
|
|
5795
|
+
//#region src/modules/excel/utils/merge-borders.ts
|
|
5796
|
+
/**
|
|
5797
|
+
* Collect perimeter borders from cells before a merge is applied.
|
|
5798
|
+
* Must be called BEFORE cell.merge() overwrites slave styles.
|
|
5799
|
+
*
|
|
5800
|
+
* Only iterates the four edges of the range, not the full rectangle.
|
|
5801
|
+
* For perimeter edges where the cell has no border, falls back to the master's border.
|
|
5802
|
+
*/
|
|
5803
|
+
function collectMergeBorders(top, left, bottom, right, findCell) {
|
|
5804
|
+
const masterBorder = findCell(top, left)?.style?.border;
|
|
5805
|
+
const width = right - left + 1;
|
|
5806
|
+
const height = bottom - top + 1;
|
|
5807
|
+
const topEdges = new Array(width);
|
|
5808
|
+
const bottomEdges = new Array(width);
|
|
5809
|
+
const leftEdges = new Array(height);
|
|
5810
|
+
const rightEdges = new Array(height);
|
|
5811
|
+
let hasAny = false;
|
|
5812
|
+
for (let j = left; j <= right; j++) {
|
|
5813
|
+
const idx = j - left;
|
|
5814
|
+
const topBorder = findCell(top, j)?.style?.border;
|
|
5815
|
+
topEdges[idx] = topBorder?.top || masterBorder?.top;
|
|
5816
|
+
if (bottom !== top) bottomEdges[idx] = (findCell(bottom, j)?.style?.border)?.bottom || masterBorder?.bottom;
|
|
5817
|
+
else bottomEdges[idx] = topBorder?.bottom || masterBorder?.bottom;
|
|
5818
|
+
if (topEdges[idx] || bottomEdges[idx]) hasAny = true;
|
|
5819
|
+
}
|
|
5820
|
+
for (let i = top; i <= bottom; i++) {
|
|
5821
|
+
const idx = i - top;
|
|
5822
|
+
const leftBorder = findCell(i, left)?.style?.border;
|
|
5823
|
+
leftEdges[idx] = leftBorder?.left || masterBorder?.left;
|
|
5824
|
+
if (right !== left) rightEdges[idx] = (findCell(i, right)?.style?.border)?.right || masterBorder?.right;
|
|
5825
|
+
else rightEdges[idx] = leftBorder?.right || masterBorder?.right;
|
|
5826
|
+
if (leftEdges[idx] || rightEdges[idx]) hasAny = true;
|
|
5827
|
+
}
|
|
5828
|
+
const diagonal = masterBorder?.diagonal;
|
|
5829
|
+
const color = masterBorder?.color;
|
|
5830
|
+
if (!hasAny && !diagonal) return;
|
|
5831
|
+
return {
|
|
5832
|
+
topEdges,
|
|
5833
|
+
bottomEdges,
|
|
5834
|
+
leftEdges,
|
|
5835
|
+
rightEdges,
|
|
5836
|
+
diagonal,
|
|
5837
|
+
color
|
|
5838
|
+
};
|
|
5839
|
+
}
|
|
5840
|
+
/**
|
|
5841
|
+
* Apply position-aware borders to a merged cell range.
|
|
5842
|
+
* Must be called AFTER cell.merge() so that the master style is available.
|
|
5843
|
+
*
|
|
5844
|
+
* Each cell receives a deep-copied style from the master so that
|
|
5845
|
+
* later mutations to one cell do not leak to others.
|
|
5846
|
+
*/
|
|
5847
|
+
function applyMergeBorders(top, left, bottom, right, collected, getCell) {
|
|
5848
|
+
const { topEdges, bottomEdges, leftEdges, rightEdges, diagonal, color } = collected;
|
|
5849
|
+
const masterStyle = getCell(top, left).style;
|
|
5850
|
+
for (let i = top; i <= bottom; i++) for (let j = left; j <= right; j++) {
|
|
5851
|
+
const cell = getCell(i, j);
|
|
5852
|
+
const style = copyStyle(masterStyle) || {};
|
|
5853
|
+
const newBorder = {};
|
|
5854
|
+
let hasBorder = false;
|
|
5855
|
+
if (i === top && topEdges[j - left]) {
|
|
5856
|
+
newBorder.top = topEdges[j - left];
|
|
5857
|
+
hasBorder = true;
|
|
5858
|
+
}
|
|
5859
|
+
if (i === bottom && bottomEdges[j - left]) {
|
|
5860
|
+
newBorder.bottom = bottomEdges[j - left];
|
|
5861
|
+
hasBorder = true;
|
|
5862
|
+
}
|
|
5863
|
+
if (j === left && leftEdges[i - top]) {
|
|
5864
|
+
newBorder.left = leftEdges[i - top];
|
|
5865
|
+
hasBorder = true;
|
|
5866
|
+
}
|
|
5867
|
+
if (j === right && rightEdges[i - top]) {
|
|
5868
|
+
newBorder.right = rightEdges[i - top];
|
|
5869
|
+
hasBorder = true;
|
|
5870
|
+
}
|
|
5871
|
+
if (diagonal) {
|
|
5872
|
+
newBorder.diagonal = diagonal;
|
|
5873
|
+
hasBorder = true;
|
|
5874
|
+
}
|
|
5875
|
+
if (hasBorder) {
|
|
5876
|
+
if (color) newBorder.color = color;
|
|
5877
|
+
style.border = newBorder;
|
|
5878
|
+
} else delete style.border;
|
|
5879
|
+
cell.style = style;
|
|
5880
|
+
}
|
|
5881
|
+
}
|
|
5882
|
+
|
|
5760
5883
|
//#endregion
|
|
5761
5884
|
//#region src/modules/excel/worksheet.ts
|
|
5762
5885
|
var Worksheet = class {
|
|
@@ -6273,8 +6396,11 @@ var ExcelTS = (function(exports) {
|
|
|
6273
6396
|
Object.values(this._merges).forEach((merge) => {
|
|
6274
6397
|
if (merge.intersects(dimensions)) throw new Error("Cannot merge already merged cells");
|
|
6275
6398
|
});
|
|
6399
|
+
const { top, left, bottom, right } = dimensions;
|
|
6400
|
+
const collected = ignoreStyle ? void 0 : collectMergeBorders(top, left, bottom, right, (r, c) => this.findCell(r, c));
|
|
6276
6401
|
const master = this.getCell(dimensions.top, dimensions.left);
|
|
6277
|
-
for (let i =
|
|
6402
|
+
for (let i = top; i <= bottom; i++) for (let j = left; j <= right; j++) if (i > top || j > left) this.getCell(i, j).merge(master, ignoreStyle);
|
|
6403
|
+
if (collected) applyMergeBorders(top, left, bottom, right, collected, (r, c) => this.getCell(r, c));
|
|
6278
6404
|
this._merges[master.address] = dimensions;
|
|
6279
6405
|
}
|
|
6280
6406
|
_unMergeMaster(master) {
|
|
@@ -24301,8 +24427,11 @@ var ExcelTS = (function(exports) {
|
|
|
24301
24427
|
this._merges.forEach((merge) => {
|
|
24302
24428
|
if (merge.intersects(dimensions)) throw new Error("Cannot merge already merged cells");
|
|
24303
24429
|
});
|
|
24304
|
-
const
|
|
24305
|
-
|
|
24430
|
+
const { top, left, bottom, right } = dimensions;
|
|
24431
|
+
const collected = collectMergeBorders(top, left, bottom, right, (r, c) => this.findCell(r, c));
|
|
24432
|
+
const master = this.getCell(top, left);
|
|
24433
|
+
for (let i = top; i <= bottom; i++) for (let j = left; j <= right; j++) if (i > top || j > left) this.getCell(i, j).merge(master);
|
|
24434
|
+
if (collected) applyMergeBorders(top, left, bottom, right, collected, (r, c) => this.getCell(r, c));
|
|
24306
24435
|
this._merges.push(dimensions);
|
|
24307
24436
|
}
|
|
24308
24437
|
addConditionalFormatting(cf) {
|
|
@@ -27474,12 +27603,12 @@ onmessage = async (ev) => {
|
|
|
27474
27603
|
*/
|
|
27475
27604
|
function chooseFormat(fmt, val) {
|
|
27476
27605
|
if (typeof val === "string") {
|
|
27477
|
-
const sections =
|
|
27606
|
+
const sections = splitFormatSections(fmt);
|
|
27478
27607
|
if (sections.length >= 4 && sections[3]) return processQuotedText(sections[3]).replace(/@/g, val);
|
|
27479
27608
|
return val;
|
|
27480
27609
|
}
|
|
27481
27610
|
if (typeof val === "boolean") return val ? "TRUE" : "FALSE";
|
|
27482
|
-
const sections =
|
|
27611
|
+
const sections = splitFormatSections(fmt);
|
|
27483
27612
|
const condRegex = /\[(=|>|<|>=|<=|<>)-?\d+(?:\.\d*)?\]/;
|
|
27484
27613
|
if ((sections[0] && condRegex.test(sections[0]) || sections[1] && condRegex.test(sections[1])) && sections.length >= 2) {
|
|
27485
27614
|
for (let i = 0; i < Math.min(sections.length, 2); i++) {
|
|
@@ -27498,37 +27627,10 @@ onmessage = async (ev) => {
|
|
|
27498
27627
|
* Check if format section is for negative values (2nd section in multi-section format)
|
|
27499
27628
|
*/
|
|
27500
27629
|
function isNegativeSection(fmt, selectedFmt) {
|
|
27501
|
-
const sections =
|
|
27630
|
+
const sections = splitFormatSections(fmt);
|
|
27502
27631
|
return sections.length >= 2 && sections[1] === selectedFmt;
|
|
27503
27632
|
}
|
|
27504
27633
|
/**
|
|
27505
|
-
* Split format string by semicolons, respecting quoted strings and brackets
|
|
27506
|
-
*/
|
|
27507
|
-
function splitFormat(fmt) {
|
|
27508
|
-
const sections = [];
|
|
27509
|
-
let current = "";
|
|
27510
|
-
let inQuote = false;
|
|
27511
|
-
let inBracket = false;
|
|
27512
|
-
for (let i = 0; i < fmt.length; i++) {
|
|
27513
|
-
const char = fmt[i];
|
|
27514
|
-
if (char === "\"" && !inBracket) {
|
|
27515
|
-
inQuote = !inQuote;
|
|
27516
|
-
current += char;
|
|
27517
|
-
} else if (char === "[" && !inQuote) {
|
|
27518
|
-
inBracket = true;
|
|
27519
|
-
current += char;
|
|
27520
|
-
} else if (char === "]" && !inQuote) {
|
|
27521
|
-
inBracket = false;
|
|
27522
|
-
current += char;
|
|
27523
|
-
} else if (char === ";" && !inQuote && !inBracket) {
|
|
27524
|
-
sections.push(current);
|
|
27525
|
-
current = "";
|
|
27526
|
-
} else current += char;
|
|
27527
|
-
}
|
|
27528
|
-
sections.push(current);
|
|
27529
|
-
return sections;
|
|
27530
|
-
}
|
|
27531
|
-
/**
|
|
27532
27634
|
* Main format function - formats a value according to Excel numFmt
|
|
27533
27635
|
* @param fmt The Excel number format string (e.g., "0.00%", "#,##0", "yyyy-mm-dd")
|
|
27534
27636
|
* @param val The value to format
|