@cj-tech-master/excelts 5.1.0 → 5.1.1
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/worksheet.d.ts +4 -0
- package/dist/browser/modules/excel/worksheet.js +155 -7
- package/dist/cjs/modules/excel/worksheet.js +155 -7
- package/dist/esm/modules/excel/worksheet.js +155 -7
- package/dist/iife/excelts.iife.js +72 -7
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +2 -2
- package/dist/types/modules/excel/worksheet.d.ts +4 -0
- package/package.json +1 -1
|
@@ -283,6 +283,10 @@ declare class Worksheet {
|
|
|
283
283
|
mergeCellsWithoutStyle(...cells: RangeInput[]): void;
|
|
284
284
|
_mergeCellsInternal(dimensions: Range, ignoreStyle?: boolean): void;
|
|
285
285
|
_unMergeMaster(master: Cell): void;
|
|
286
|
+
/**
|
|
287
|
+
* Update _merges dictionary and cell-level merge references after a row or column splice.
|
|
288
|
+
*/
|
|
289
|
+
private _spliceMerges;
|
|
286
290
|
get hasMerges(): boolean;
|
|
287
291
|
/**
|
|
288
292
|
* Scan the range and if any cell is part of a merge, un-merge the group.
|
|
@@ -245,6 +245,19 @@ class Worksheet {
|
|
|
245
245
|
* the rows will still be shifted as if the values existed
|
|
246
246
|
*/
|
|
247
247
|
spliceColumns(start, count, ...inserts) {
|
|
248
|
+
// Before splicing cells, release all cell-level merge references so that
|
|
249
|
+
// row.splice copies plain values instead of merge proxies.
|
|
250
|
+
// _spliceMerges (called later) will rebuild cell-level refs at new coordinates.
|
|
251
|
+
for (const merge of Object.values(this._merges)) {
|
|
252
|
+
for (let r = merge.top; r <= merge.bottom; r++) {
|
|
253
|
+
for (let c = merge.left; c <= merge.right; c++) {
|
|
254
|
+
const cell = this.findCell(r, c);
|
|
255
|
+
if (cell && cell.type === Enums.ValueType.Merge) {
|
|
256
|
+
cell.unmerge();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
248
261
|
const rows = this._rows;
|
|
249
262
|
const nRows = rows.length;
|
|
250
263
|
if (inserts.length > 0) {
|
|
@@ -296,6 +309,8 @@ class Worksheet {
|
|
|
296
309
|
}
|
|
297
310
|
}
|
|
298
311
|
}
|
|
312
|
+
// account for merges
|
|
313
|
+
this._spliceMerges("col", start, count, inserts.length);
|
|
299
314
|
}
|
|
300
315
|
/**
|
|
301
316
|
* Get the last column in a worksheet
|
|
@@ -469,6 +484,14 @@ class Worksheet {
|
|
|
469
484
|
// either inserting new or overwriting existing rows
|
|
470
485
|
const rSrc = this._rows[rowNum - 1];
|
|
471
486
|
const inserts = Array.from({ length: count }).fill(rSrc.values);
|
|
487
|
+
// Collect single-row merges from the source row before splicing
|
|
488
|
+
// (only merges where top == bottom == rowNum, i.e. horizontal merges within one row)
|
|
489
|
+
const srcMerges = [];
|
|
490
|
+
for (const merge of Object.values(this._merges)) {
|
|
491
|
+
if (merge.top === rowNum && merge.bottom === rowNum) {
|
|
492
|
+
srcMerges.push(merge);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
472
495
|
this.spliceRows(rowNum + 1, insert ? 0 : count, ...inserts);
|
|
473
496
|
// now copy styles...
|
|
474
497
|
for (let i = 0; i < count; i++) {
|
|
@@ -479,6 +502,27 @@ class Worksheet {
|
|
|
479
502
|
rDst.getCell(colNumber).style = cell.style;
|
|
480
503
|
});
|
|
481
504
|
}
|
|
505
|
+
// Duplicate single-row merges from source row into each new row
|
|
506
|
+
if (srcMerges.length > 0) {
|
|
507
|
+
for (let i = 0; i < count; i++) {
|
|
508
|
+
const dstRow = rowNum + 1 + i;
|
|
509
|
+
// In overwrite mode, clear any existing merges in the target row
|
|
510
|
+
if (!insert) {
|
|
511
|
+
const toRemove = [];
|
|
512
|
+
for (const [key, merge] of Object.entries(this._merges)) {
|
|
513
|
+
if (merge.top <= dstRow && merge.bottom >= dstRow) {
|
|
514
|
+
toRemove.push(key);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
for (const key of toRemove) {
|
|
518
|
+
this._unMergeMaster(this.getCell(key));
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
for (const srcMerge of srcMerges) {
|
|
522
|
+
this.mergeCellsWithoutStyle(dstRow, srcMerge.left, dstRow, srcMerge.right);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
482
526
|
}
|
|
483
527
|
/**
|
|
484
528
|
* Cut one or more rows (rows below are shifted up)
|
|
@@ -488,6 +532,19 @@ class Worksheet {
|
|
|
488
532
|
*/
|
|
489
533
|
spliceRows(start, count, ...inserts) {
|
|
490
534
|
// same problem as row.splice, except worse.
|
|
535
|
+
// Before splicing rows, release all cell-level merge references so that
|
|
536
|
+
// row value copies work on plain values instead of merge proxies.
|
|
537
|
+
// _spliceMerges (called later) will rebuild cell-level refs at new coordinates.
|
|
538
|
+
for (const merge of Object.values(this._merges)) {
|
|
539
|
+
for (let r = merge.top; r <= merge.bottom; r++) {
|
|
540
|
+
for (let c = merge.left; c <= merge.right; c++) {
|
|
541
|
+
const cell = this.findCell(r, c);
|
|
542
|
+
if (cell && cell.type === Enums.ValueType.Merge) {
|
|
543
|
+
cell.unmerge();
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
491
548
|
const nKeep = start + count;
|
|
492
549
|
const nInserts = inserts.length;
|
|
493
550
|
const nExpand = nInserts - count;
|
|
@@ -527,13 +584,6 @@ class Worksheet {
|
|
|
527
584
|
rDst.height = rSrc.height;
|
|
528
585
|
rSrc.eachCell({ includeEmpty: true }, (cell, colNumber) => {
|
|
529
586
|
rDst.getCell(colNumber).style = cell.style;
|
|
530
|
-
// remerge cells accounting for insert offset
|
|
531
|
-
if (cell.type === Enums.ValueType.Merge) {
|
|
532
|
-
const cellToBeMerged = this.getRow(cell.row + nInserts).getCell(colNumber);
|
|
533
|
-
const prevMaster = cell.master;
|
|
534
|
-
const newMaster = this.getRow(prevMaster.row + nInserts).getCell(prevMaster.col);
|
|
535
|
-
cellToBeMerged.merge(newMaster);
|
|
536
|
-
}
|
|
537
587
|
});
|
|
538
588
|
}
|
|
539
589
|
else {
|
|
@@ -563,6 +613,8 @@ class Worksheet {
|
|
|
563
613
|
}
|
|
564
614
|
}
|
|
565
615
|
}
|
|
616
|
+
// account for merges
|
|
617
|
+
this._spliceMerges("row", start, count, nInserts);
|
|
566
618
|
}
|
|
567
619
|
eachRow(optOrCallback, maybeCallback) {
|
|
568
620
|
let options;
|
|
@@ -669,6 +721,102 @@ class Worksheet {
|
|
|
669
721
|
delete this._merges[master.address];
|
|
670
722
|
}
|
|
671
723
|
}
|
|
724
|
+
/**
|
|
725
|
+
* Update _merges dictionary and cell-level merge references after a row or column splice.
|
|
726
|
+
*/
|
|
727
|
+
_spliceMerges(axis, start, count, nInserts) {
|
|
728
|
+
const nExpand = nInserts - count;
|
|
729
|
+
if (nExpand === 0 && count === 0) {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
const nKeep = start + count;
|
|
733
|
+
const isRow = axis === "row";
|
|
734
|
+
const newMerges = {};
|
|
735
|
+
for (const merge of Object.values(this._merges)) {
|
|
736
|
+
const { top, left, bottom, right } = merge.model;
|
|
737
|
+
// For row axis: lo=top, hi=bottom. For col axis: lo=left, hi=right.
|
|
738
|
+
const lo = isRow ? top : left;
|
|
739
|
+
const hi = isRow ? bottom : right;
|
|
740
|
+
if (nExpand <= 0 && count > 0) {
|
|
741
|
+
// Deleting rows/columns
|
|
742
|
+
const deleteEnd = nKeep - 1;
|
|
743
|
+
if (lo > deleteEnd) {
|
|
744
|
+
// Entirely after deleted range — shift
|
|
745
|
+
const newRange = isRow
|
|
746
|
+
? new Range(top + nExpand, left, bottom + nExpand, right)
|
|
747
|
+
: new Range(top, left + nExpand, bottom, right + nExpand);
|
|
748
|
+
newMerges[colCache.encodeAddress(newRange.top, newRange.left)] = newRange;
|
|
749
|
+
}
|
|
750
|
+
else if (hi < start) {
|
|
751
|
+
// Entirely before deleted range — unchanged
|
|
752
|
+
newMerges[colCache.encodeAddress(top, left)] = merge;
|
|
753
|
+
}
|
|
754
|
+
else if (lo >= start && hi <= deleteEnd) {
|
|
755
|
+
// Entirely within deleted range — remove
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
// Spans splice boundary — shrink
|
|
759
|
+
let newTop = top;
|
|
760
|
+
let newLeft = left;
|
|
761
|
+
let newBottom = bottom;
|
|
762
|
+
let newRight = right;
|
|
763
|
+
if (isRow) {
|
|
764
|
+
newTop = top < start ? top : start;
|
|
765
|
+
newBottom = Math.max(newTop, bottom + nExpand);
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
newLeft = left < start ? left : start;
|
|
769
|
+
newRight = Math.max(newLeft, right + nExpand);
|
|
770
|
+
}
|
|
771
|
+
const newRange = new Range(newTop, newLeft, newBottom, newRight);
|
|
772
|
+
if (newTop === newBottom && newLeft === newRight) {
|
|
773
|
+
// Degenerate 1x1 merge — remove instead of keeping
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
newMerges[colCache.encodeAddress(newRange.top, newRange.left)] = newRange;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
// Inserting rows/columns: shift items at/after nKeep
|
|
782
|
+
if (lo >= nKeep) {
|
|
783
|
+
// Entirely at or after splice — shift
|
|
784
|
+
const newRange = isRow
|
|
785
|
+
? new Range(top + nExpand, left, bottom + nExpand, right)
|
|
786
|
+
: new Range(top, left + nExpand, bottom, right + nExpand);
|
|
787
|
+
newMerges[colCache.encodeAddress(newRange.top, newRange.left)] = newRange;
|
|
788
|
+
}
|
|
789
|
+
else if (hi < nKeep) {
|
|
790
|
+
// Entirely before splice — unchanged
|
|
791
|
+
newMerges[colCache.encodeAddress(top, left)] = merge;
|
|
792
|
+
}
|
|
793
|
+
else {
|
|
794
|
+
// Spans splice boundary — stretch
|
|
795
|
+
if (isRow) {
|
|
796
|
+
merge.model.bottom = bottom + nExpand;
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
merge.model.right = right + nExpand;
|
|
800
|
+
}
|
|
801
|
+
newMerges[colCache.encodeAddress(top, left)] = merge;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
this._merges = newMerges;
|
|
806
|
+
// Rebuild cell-level merge references for all merges.
|
|
807
|
+
// Pre-unmerge in spliceRows/spliceColumns clears all cell refs,
|
|
808
|
+
// so we must rebuild every merge, not just moved/resized ones.
|
|
809
|
+
for (const m of Object.values(newMerges)) {
|
|
810
|
+
const master = this.getCell(m.top, m.left);
|
|
811
|
+
for (let r = m.top; r <= m.bottom; r++) {
|
|
812
|
+
for (let c = m.left; c <= m.right; c++) {
|
|
813
|
+
if (r > m.top || c > m.left) {
|
|
814
|
+
this.getCell(r, c).merge(master, true);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
672
820
|
get hasMerges() {
|
|
673
821
|
// return true if this._merges has a merge object
|
|
674
822
|
return Object.values(this._merges).some(Boolean);
|
|
@@ -248,6 +248,19 @@ class Worksheet {
|
|
|
248
248
|
* the rows will still be shifted as if the values existed
|
|
249
249
|
*/
|
|
250
250
|
spliceColumns(start, count, ...inserts) {
|
|
251
|
+
// Before splicing cells, release all cell-level merge references so that
|
|
252
|
+
// row.splice copies plain values instead of merge proxies.
|
|
253
|
+
// _spliceMerges (called later) will rebuild cell-level refs at new coordinates.
|
|
254
|
+
for (const merge of Object.values(this._merges)) {
|
|
255
|
+
for (let r = merge.top; r <= merge.bottom; r++) {
|
|
256
|
+
for (let c = merge.left; c <= merge.right; c++) {
|
|
257
|
+
const cell = this.findCell(r, c);
|
|
258
|
+
if (cell && cell.type === enums_1.Enums.ValueType.Merge) {
|
|
259
|
+
cell.unmerge();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
251
264
|
const rows = this._rows;
|
|
252
265
|
const nRows = rows.length;
|
|
253
266
|
if (inserts.length > 0) {
|
|
@@ -299,6 +312,8 @@ class Worksheet {
|
|
|
299
312
|
}
|
|
300
313
|
}
|
|
301
314
|
}
|
|
315
|
+
// account for merges
|
|
316
|
+
this._spliceMerges("col", start, count, inserts.length);
|
|
302
317
|
}
|
|
303
318
|
/**
|
|
304
319
|
* Get the last column in a worksheet
|
|
@@ -472,6 +487,14 @@ class Worksheet {
|
|
|
472
487
|
// either inserting new or overwriting existing rows
|
|
473
488
|
const rSrc = this._rows[rowNum - 1];
|
|
474
489
|
const inserts = Array.from({ length: count }).fill(rSrc.values);
|
|
490
|
+
// Collect single-row merges from the source row before splicing
|
|
491
|
+
// (only merges where top == bottom == rowNum, i.e. horizontal merges within one row)
|
|
492
|
+
const srcMerges = [];
|
|
493
|
+
for (const merge of Object.values(this._merges)) {
|
|
494
|
+
if (merge.top === rowNum && merge.bottom === rowNum) {
|
|
495
|
+
srcMerges.push(merge);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
475
498
|
this.spliceRows(rowNum + 1, insert ? 0 : count, ...inserts);
|
|
476
499
|
// now copy styles...
|
|
477
500
|
for (let i = 0; i < count; i++) {
|
|
@@ -482,6 +505,27 @@ class Worksheet {
|
|
|
482
505
|
rDst.getCell(colNumber).style = cell.style;
|
|
483
506
|
});
|
|
484
507
|
}
|
|
508
|
+
// Duplicate single-row merges from source row into each new row
|
|
509
|
+
if (srcMerges.length > 0) {
|
|
510
|
+
for (let i = 0; i < count; i++) {
|
|
511
|
+
const dstRow = rowNum + 1 + i;
|
|
512
|
+
// In overwrite mode, clear any existing merges in the target row
|
|
513
|
+
if (!insert) {
|
|
514
|
+
const toRemove = [];
|
|
515
|
+
for (const [key, merge] of Object.entries(this._merges)) {
|
|
516
|
+
if (merge.top <= dstRow && merge.bottom >= dstRow) {
|
|
517
|
+
toRemove.push(key);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
for (const key of toRemove) {
|
|
521
|
+
this._unMergeMaster(this.getCell(key));
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
for (const srcMerge of srcMerges) {
|
|
525
|
+
this.mergeCellsWithoutStyle(dstRow, srcMerge.left, dstRow, srcMerge.right);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
485
529
|
}
|
|
486
530
|
/**
|
|
487
531
|
* Cut one or more rows (rows below are shifted up)
|
|
@@ -491,6 +535,19 @@ class Worksheet {
|
|
|
491
535
|
*/
|
|
492
536
|
spliceRows(start, count, ...inserts) {
|
|
493
537
|
// same problem as row.splice, except worse.
|
|
538
|
+
// Before splicing rows, release all cell-level merge references so that
|
|
539
|
+
// row value copies work on plain values instead of merge proxies.
|
|
540
|
+
// _spliceMerges (called later) will rebuild cell-level refs at new coordinates.
|
|
541
|
+
for (const merge of Object.values(this._merges)) {
|
|
542
|
+
for (let r = merge.top; r <= merge.bottom; r++) {
|
|
543
|
+
for (let c = merge.left; c <= merge.right; c++) {
|
|
544
|
+
const cell = this.findCell(r, c);
|
|
545
|
+
if (cell && cell.type === enums_1.Enums.ValueType.Merge) {
|
|
546
|
+
cell.unmerge();
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
494
551
|
const nKeep = start + count;
|
|
495
552
|
const nInserts = inserts.length;
|
|
496
553
|
const nExpand = nInserts - count;
|
|
@@ -530,13 +587,6 @@ class Worksheet {
|
|
|
530
587
|
rDst.height = rSrc.height;
|
|
531
588
|
rSrc.eachCell({ includeEmpty: true }, (cell, colNumber) => {
|
|
532
589
|
rDst.getCell(colNumber).style = cell.style;
|
|
533
|
-
// remerge cells accounting for insert offset
|
|
534
|
-
if (cell.type === enums_1.Enums.ValueType.Merge) {
|
|
535
|
-
const cellToBeMerged = this.getRow(cell.row + nInserts).getCell(colNumber);
|
|
536
|
-
const prevMaster = cell.master;
|
|
537
|
-
const newMaster = this.getRow(prevMaster.row + nInserts).getCell(prevMaster.col);
|
|
538
|
-
cellToBeMerged.merge(newMaster);
|
|
539
|
-
}
|
|
540
590
|
});
|
|
541
591
|
}
|
|
542
592
|
else {
|
|
@@ -566,6 +616,8 @@ class Worksheet {
|
|
|
566
616
|
}
|
|
567
617
|
}
|
|
568
618
|
}
|
|
619
|
+
// account for merges
|
|
620
|
+
this._spliceMerges("row", start, count, nInserts);
|
|
569
621
|
}
|
|
570
622
|
eachRow(optOrCallback, maybeCallback) {
|
|
571
623
|
let options;
|
|
@@ -672,6 +724,102 @@ class Worksheet {
|
|
|
672
724
|
delete this._merges[master.address];
|
|
673
725
|
}
|
|
674
726
|
}
|
|
727
|
+
/**
|
|
728
|
+
* Update _merges dictionary and cell-level merge references after a row or column splice.
|
|
729
|
+
*/
|
|
730
|
+
_spliceMerges(axis, start, count, nInserts) {
|
|
731
|
+
const nExpand = nInserts - count;
|
|
732
|
+
if (nExpand === 0 && count === 0) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
const nKeep = start + count;
|
|
736
|
+
const isRow = axis === "row";
|
|
737
|
+
const newMerges = {};
|
|
738
|
+
for (const merge of Object.values(this._merges)) {
|
|
739
|
+
const { top, left, bottom, right } = merge.model;
|
|
740
|
+
// For row axis: lo=top, hi=bottom. For col axis: lo=left, hi=right.
|
|
741
|
+
const lo = isRow ? top : left;
|
|
742
|
+
const hi = isRow ? bottom : right;
|
|
743
|
+
if (nExpand <= 0 && count > 0) {
|
|
744
|
+
// Deleting rows/columns
|
|
745
|
+
const deleteEnd = nKeep - 1;
|
|
746
|
+
if (lo > deleteEnd) {
|
|
747
|
+
// Entirely after deleted range — shift
|
|
748
|
+
const newRange = isRow
|
|
749
|
+
? new range_1.Range(top + nExpand, left, bottom + nExpand, right)
|
|
750
|
+
: new range_1.Range(top, left + nExpand, bottom, right + nExpand);
|
|
751
|
+
newMerges[col_cache_1.colCache.encodeAddress(newRange.top, newRange.left)] = newRange;
|
|
752
|
+
}
|
|
753
|
+
else if (hi < start) {
|
|
754
|
+
// Entirely before deleted range — unchanged
|
|
755
|
+
newMerges[col_cache_1.colCache.encodeAddress(top, left)] = merge;
|
|
756
|
+
}
|
|
757
|
+
else if (lo >= start && hi <= deleteEnd) {
|
|
758
|
+
// Entirely within deleted range — remove
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
// Spans splice boundary — shrink
|
|
762
|
+
let newTop = top;
|
|
763
|
+
let newLeft = left;
|
|
764
|
+
let newBottom = bottom;
|
|
765
|
+
let newRight = right;
|
|
766
|
+
if (isRow) {
|
|
767
|
+
newTop = top < start ? top : start;
|
|
768
|
+
newBottom = Math.max(newTop, bottom + nExpand);
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
newLeft = left < start ? left : start;
|
|
772
|
+
newRight = Math.max(newLeft, right + nExpand);
|
|
773
|
+
}
|
|
774
|
+
const newRange = new range_1.Range(newTop, newLeft, newBottom, newRight);
|
|
775
|
+
if (newTop === newBottom && newLeft === newRight) {
|
|
776
|
+
// Degenerate 1x1 merge — remove instead of keeping
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
newMerges[col_cache_1.colCache.encodeAddress(newRange.top, newRange.left)] = newRange;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
// Inserting rows/columns: shift items at/after nKeep
|
|
785
|
+
if (lo >= nKeep) {
|
|
786
|
+
// Entirely at or after splice — shift
|
|
787
|
+
const newRange = isRow
|
|
788
|
+
? new range_1.Range(top + nExpand, left, bottom + nExpand, right)
|
|
789
|
+
: new range_1.Range(top, left + nExpand, bottom, right + nExpand);
|
|
790
|
+
newMerges[col_cache_1.colCache.encodeAddress(newRange.top, newRange.left)] = newRange;
|
|
791
|
+
}
|
|
792
|
+
else if (hi < nKeep) {
|
|
793
|
+
// Entirely before splice — unchanged
|
|
794
|
+
newMerges[col_cache_1.colCache.encodeAddress(top, left)] = merge;
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
// Spans splice boundary — stretch
|
|
798
|
+
if (isRow) {
|
|
799
|
+
merge.model.bottom = bottom + nExpand;
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
merge.model.right = right + nExpand;
|
|
803
|
+
}
|
|
804
|
+
newMerges[col_cache_1.colCache.encodeAddress(top, left)] = merge;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
this._merges = newMerges;
|
|
809
|
+
// Rebuild cell-level merge references for all merges.
|
|
810
|
+
// Pre-unmerge in spliceRows/spliceColumns clears all cell refs,
|
|
811
|
+
// so we must rebuild every merge, not just moved/resized ones.
|
|
812
|
+
for (const m of Object.values(newMerges)) {
|
|
813
|
+
const master = this.getCell(m.top, m.left);
|
|
814
|
+
for (let r = m.top; r <= m.bottom; r++) {
|
|
815
|
+
for (let c = m.left; c <= m.right; c++) {
|
|
816
|
+
if (r > m.top || c > m.left) {
|
|
817
|
+
this.getCell(r, c).merge(master, true);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
675
823
|
get hasMerges() {
|
|
676
824
|
// return true if this._merges has a merge object
|
|
677
825
|
return Object.values(this._merges).some(Boolean);
|
|
@@ -245,6 +245,19 @@ class Worksheet {
|
|
|
245
245
|
* the rows will still be shifted as if the values existed
|
|
246
246
|
*/
|
|
247
247
|
spliceColumns(start, count, ...inserts) {
|
|
248
|
+
// Before splicing cells, release all cell-level merge references so that
|
|
249
|
+
// row.splice copies plain values instead of merge proxies.
|
|
250
|
+
// _spliceMerges (called later) will rebuild cell-level refs at new coordinates.
|
|
251
|
+
for (const merge of Object.values(this._merges)) {
|
|
252
|
+
for (let r = merge.top; r <= merge.bottom; r++) {
|
|
253
|
+
for (let c = merge.left; c <= merge.right; c++) {
|
|
254
|
+
const cell = this.findCell(r, c);
|
|
255
|
+
if (cell && cell.type === Enums.ValueType.Merge) {
|
|
256
|
+
cell.unmerge();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
248
261
|
const rows = this._rows;
|
|
249
262
|
const nRows = rows.length;
|
|
250
263
|
if (inserts.length > 0) {
|
|
@@ -296,6 +309,8 @@ class Worksheet {
|
|
|
296
309
|
}
|
|
297
310
|
}
|
|
298
311
|
}
|
|
312
|
+
// account for merges
|
|
313
|
+
this._spliceMerges("col", start, count, inserts.length);
|
|
299
314
|
}
|
|
300
315
|
/**
|
|
301
316
|
* Get the last column in a worksheet
|
|
@@ -469,6 +484,14 @@ class Worksheet {
|
|
|
469
484
|
// either inserting new or overwriting existing rows
|
|
470
485
|
const rSrc = this._rows[rowNum - 1];
|
|
471
486
|
const inserts = Array.from({ length: count }).fill(rSrc.values);
|
|
487
|
+
// Collect single-row merges from the source row before splicing
|
|
488
|
+
// (only merges where top == bottom == rowNum, i.e. horizontal merges within one row)
|
|
489
|
+
const srcMerges = [];
|
|
490
|
+
for (const merge of Object.values(this._merges)) {
|
|
491
|
+
if (merge.top === rowNum && merge.bottom === rowNum) {
|
|
492
|
+
srcMerges.push(merge);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
472
495
|
this.spliceRows(rowNum + 1, insert ? 0 : count, ...inserts);
|
|
473
496
|
// now copy styles...
|
|
474
497
|
for (let i = 0; i < count; i++) {
|
|
@@ -479,6 +502,27 @@ class Worksheet {
|
|
|
479
502
|
rDst.getCell(colNumber).style = cell.style;
|
|
480
503
|
});
|
|
481
504
|
}
|
|
505
|
+
// Duplicate single-row merges from source row into each new row
|
|
506
|
+
if (srcMerges.length > 0) {
|
|
507
|
+
for (let i = 0; i < count; i++) {
|
|
508
|
+
const dstRow = rowNum + 1 + i;
|
|
509
|
+
// In overwrite mode, clear any existing merges in the target row
|
|
510
|
+
if (!insert) {
|
|
511
|
+
const toRemove = [];
|
|
512
|
+
for (const [key, merge] of Object.entries(this._merges)) {
|
|
513
|
+
if (merge.top <= dstRow && merge.bottom >= dstRow) {
|
|
514
|
+
toRemove.push(key);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
for (const key of toRemove) {
|
|
518
|
+
this._unMergeMaster(this.getCell(key));
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
for (const srcMerge of srcMerges) {
|
|
522
|
+
this.mergeCellsWithoutStyle(dstRow, srcMerge.left, dstRow, srcMerge.right);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
482
526
|
}
|
|
483
527
|
/**
|
|
484
528
|
* Cut one or more rows (rows below are shifted up)
|
|
@@ -488,6 +532,19 @@ class Worksheet {
|
|
|
488
532
|
*/
|
|
489
533
|
spliceRows(start, count, ...inserts) {
|
|
490
534
|
// same problem as row.splice, except worse.
|
|
535
|
+
// Before splicing rows, release all cell-level merge references so that
|
|
536
|
+
// row value copies work on plain values instead of merge proxies.
|
|
537
|
+
// _spliceMerges (called later) will rebuild cell-level refs at new coordinates.
|
|
538
|
+
for (const merge of Object.values(this._merges)) {
|
|
539
|
+
for (let r = merge.top; r <= merge.bottom; r++) {
|
|
540
|
+
for (let c = merge.left; c <= merge.right; c++) {
|
|
541
|
+
const cell = this.findCell(r, c);
|
|
542
|
+
if (cell && cell.type === Enums.ValueType.Merge) {
|
|
543
|
+
cell.unmerge();
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
491
548
|
const nKeep = start + count;
|
|
492
549
|
const nInserts = inserts.length;
|
|
493
550
|
const nExpand = nInserts - count;
|
|
@@ -527,13 +584,6 @@ class Worksheet {
|
|
|
527
584
|
rDst.height = rSrc.height;
|
|
528
585
|
rSrc.eachCell({ includeEmpty: true }, (cell, colNumber) => {
|
|
529
586
|
rDst.getCell(colNumber).style = cell.style;
|
|
530
|
-
// remerge cells accounting for insert offset
|
|
531
|
-
if (cell.type === Enums.ValueType.Merge) {
|
|
532
|
-
const cellToBeMerged = this.getRow(cell.row + nInserts).getCell(colNumber);
|
|
533
|
-
const prevMaster = cell.master;
|
|
534
|
-
const newMaster = this.getRow(prevMaster.row + nInserts).getCell(prevMaster.col);
|
|
535
|
-
cellToBeMerged.merge(newMaster);
|
|
536
|
-
}
|
|
537
587
|
});
|
|
538
588
|
}
|
|
539
589
|
else {
|
|
@@ -563,6 +613,8 @@ class Worksheet {
|
|
|
563
613
|
}
|
|
564
614
|
}
|
|
565
615
|
}
|
|
616
|
+
// account for merges
|
|
617
|
+
this._spliceMerges("row", start, count, nInserts);
|
|
566
618
|
}
|
|
567
619
|
eachRow(optOrCallback, maybeCallback) {
|
|
568
620
|
let options;
|
|
@@ -669,6 +721,102 @@ class Worksheet {
|
|
|
669
721
|
delete this._merges[master.address];
|
|
670
722
|
}
|
|
671
723
|
}
|
|
724
|
+
/**
|
|
725
|
+
* Update _merges dictionary and cell-level merge references after a row or column splice.
|
|
726
|
+
*/
|
|
727
|
+
_spliceMerges(axis, start, count, nInserts) {
|
|
728
|
+
const nExpand = nInserts - count;
|
|
729
|
+
if (nExpand === 0 && count === 0) {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
const nKeep = start + count;
|
|
733
|
+
const isRow = axis === "row";
|
|
734
|
+
const newMerges = {};
|
|
735
|
+
for (const merge of Object.values(this._merges)) {
|
|
736
|
+
const { top, left, bottom, right } = merge.model;
|
|
737
|
+
// For row axis: lo=top, hi=bottom. For col axis: lo=left, hi=right.
|
|
738
|
+
const lo = isRow ? top : left;
|
|
739
|
+
const hi = isRow ? bottom : right;
|
|
740
|
+
if (nExpand <= 0 && count > 0) {
|
|
741
|
+
// Deleting rows/columns
|
|
742
|
+
const deleteEnd = nKeep - 1;
|
|
743
|
+
if (lo > deleteEnd) {
|
|
744
|
+
// Entirely after deleted range — shift
|
|
745
|
+
const newRange = isRow
|
|
746
|
+
? new Range(top + nExpand, left, bottom + nExpand, right)
|
|
747
|
+
: new Range(top, left + nExpand, bottom, right + nExpand);
|
|
748
|
+
newMerges[colCache.encodeAddress(newRange.top, newRange.left)] = newRange;
|
|
749
|
+
}
|
|
750
|
+
else if (hi < start) {
|
|
751
|
+
// Entirely before deleted range — unchanged
|
|
752
|
+
newMerges[colCache.encodeAddress(top, left)] = merge;
|
|
753
|
+
}
|
|
754
|
+
else if (lo >= start && hi <= deleteEnd) {
|
|
755
|
+
// Entirely within deleted range — remove
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
// Spans splice boundary — shrink
|
|
759
|
+
let newTop = top;
|
|
760
|
+
let newLeft = left;
|
|
761
|
+
let newBottom = bottom;
|
|
762
|
+
let newRight = right;
|
|
763
|
+
if (isRow) {
|
|
764
|
+
newTop = top < start ? top : start;
|
|
765
|
+
newBottom = Math.max(newTop, bottom + nExpand);
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
newLeft = left < start ? left : start;
|
|
769
|
+
newRight = Math.max(newLeft, right + nExpand);
|
|
770
|
+
}
|
|
771
|
+
const newRange = new Range(newTop, newLeft, newBottom, newRight);
|
|
772
|
+
if (newTop === newBottom && newLeft === newRight) {
|
|
773
|
+
// Degenerate 1x1 merge — remove instead of keeping
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
newMerges[colCache.encodeAddress(newRange.top, newRange.left)] = newRange;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
// Inserting rows/columns: shift items at/after nKeep
|
|
782
|
+
if (lo >= nKeep) {
|
|
783
|
+
// Entirely at or after splice — shift
|
|
784
|
+
const newRange = isRow
|
|
785
|
+
? new Range(top + nExpand, left, bottom + nExpand, right)
|
|
786
|
+
: new Range(top, left + nExpand, bottom, right + nExpand);
|
|
787
|
+
newMerges[colCache.encodeAddress(newRange.top, newRange.left)] = newRange;
|
|
788
|
+
}
|
|
789
|
+
else if (hi < nKeep) {
|
|
790
|
+
// Entirely before splice — unchanged
|
|
791
|
+
newMerges[colCache.encodeAddress(top, left)] = merge;
|
|
792
|
+
}
|
|
793
|
+
else {
|
|
794
|
+
// Spans splice boundary — stretch
|
|
795
|
+
if (isRow) {
|
|
796
|
+
merge.model.bottom = bottom + nExpand;
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
merge.model.right = right + nExpand;
|
|
800
|
+
}
|
|
801
|
+
newMerges[colCache.encodeAddress(top, left)] = merge;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
this._merges = newMerges;
|
|
806
|
+
// Rebuild cell-level merge references for all merges.
|
|
807
|
+
// Pre-unmerge in spliceRows/spliceColumns clears all cell refs,
|
|
808
|
+
// so we must rebuild every merge, not just moved/resized ones.
|
|
809
|
+
for (const m of Object.values(newMerges)) {
|
|
810
|
+
const master = this.getCell(m.top, m.left);
|
|
811
|
+
for (let r = m.top; r <= m.bottom; r++) {
|
|
812
|
+
for (let c = m.left; c <= m.right; c++) {
|
|
813
|
+
if (r > m.top || c > m.left) {
|
|
814
|
+
this.getCell(r, c).merge(master, true);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
672
820
|
get hasMerges() {
|
|
673
821
|
// return true if this._merges has a merge object
|
|
674
822
|
return Object.values(this._merges).some(Boolean);
|