exceljs-lts 4.5.2 → 4.5.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/index.d.ts CHANGED
@@ -865,6 +865,10 @@ export interface HeaderFooter {
865
865
  export type AutoFilter = string | {
866
866
  from: string | { row: number; column: number };
867
867
  to: string | { row: number; column: number };
868
+ /**
869
+ * Columns (0 = leftmost column of the range) whose filter button is hidden
870
+ */
871
+ exclude?: number[];
868
872
  };
869
873
 
870
874
  export interface WorksheetProtection {
package/lib/doc/image.js CHANGED
@@ -56,4 +56,36 @@ class Image {
56
56
  }
57
57
  }
58
58
 
59
+ function isFiniteNumber(value) {
60
+ return typeof value === 'number' && Number.isFinite(value);
61
+ }
62
+
63
+ Image.validateRange = function validateRange(range) {
64
+ if (typeof range === 'string') {
65
+ let decoded;
66
+ try {
67
+ decoded = colCache.decode(range);
68
+ } catch (e) {
69
+ decoded = {};
70
+ }
71
+ const isRange = ['top', 'left', 'bottom', 'right'].every(key => isFiniteNumber(decoded[key]));
72
+ const isAddress = isFiniteNumber(decoded.row) && isFiniteNumber(decoded.col);
73
+ if (!isRange && !isAddress) {
74
+ throw new Error(`Invalid image range "${range}": expected a cell range such as "B2:D6"`);
75
+ }
76
+ return;
77
+ }
78
+ if (!range || typeof range !== 'object') {
79
+ throw new Error(
80
+ 'Invalid image range: expected a cell range string or an object with tl and br or ext'
81
+ );
82
+ }
83
+ if (!range.br && !range.ext) {
84
+ throw new Error('Invalid image range: an object range needs either br or ext');
85
+ }
86
+ if (range.ext && typeof range.ext !== 'object') {
87
+ throw new Error('Invalid image range: ext must be an object with width and height');
88
+ }
89
+ };
90
+
59
91
  module.exports = Image;
@@ -781,7 +781,15 @@ class Worksheet {
781
781
 
782
782
  // =========================================================================
783
783
  // Images
784
+ _validateImageId(imageId) {
785
+ if (imageId === undefined || imageId === null || !this._workbook.getImage(imageId)) {
786
+ throw new Error(`Image with id ${imageId} not found. Add it with workbook.addImage() first`);
787
+ }
788
+ }
789
+
784
790
  addImage(imageId, range) {
791
+ this._validateImageId(imageId);
792
+ Image.validateRange(range);
785
793
  const model = {
786
794
  type: 'image',
787
795
  imageId,
@@ -795,6 +803,7 @@ class Worksheet {
795
803
  }
796
804
 
797
805
  addBackgroundImage(imageId) {
806
+ this._validateImageId(imageId);
798
807
  const model = {
799
808
  type: 'background',
800
809
  imageId,
@@ -473,6 +473,9 @@ class WorksheetWriter {
473
473
  // =========================================================================
474
474
 
475
475
  addBackgroundImage(imageId) {
476
+ if (imageId === undefined || imageId === null || !this._workbook.getImage(imageId)) {
477
+ throw new Error(`Image with id ${imageId} not found. Add it with workbook.addImage() first`);
478
+ }
476
479
  this._background = {
477
480
  imageId,
478
481
  };
@@ -22,16 +22,55 @@ class AutoFilterXform extends BaseXform {
22
22
  const firstAddress = getAddress(model.from);
23
23
  const secondAddress = getAddress(model.to);
24
24
  if (firstAddress && secondAddress) {
25
- xmlStream.leafNode('autoFilter', {ref: `${firstAddress}:${secondAddress}`});
25
+ const ref = `${firstAddress}:${secondAddress}`;
26
+ if (model.exclude && model.exclude.length) {
27
+ // hide the filter button on the excluded columns (0 = leftmost column of the range)
28
+ xmlStream.openNode('autoFilter', {ref});
29
+ model.exclude.forEach(colId => {
30
+ xmlStream.leafNode('filterColumn', {colId, hiddenButton: '1'});
31
+ });
32
+ xmlStream.closeNode();
33
+ } else {
34
+ xmlStream.leafNode('autoFilter', {ref});
35
+ }
26
36
  }
27
37
  }
28
38
  }
29
39
  }
30
40
 
31
41
  parseOpen(node) {
32
- if (node.name === 'autoFilter') {
33
- this.model = node.attributes.ref;
42
+ switch (node.name) {
43
+ case 'autoFilter':
44
+ this.ref = node.attributes.ref;
45
+ this.exclude = [];
46
+ this.model = this.ref;
47
+ return true;
48
+ case 'filterColumn': {
49
+ const {colId, hiddenButton, showButton} = node.attributes;
50
+ const hidden =
51
+ hiddenButton === '1' ||
52
+ hiddenButton === 'true' ||
53
+ showButton === '0' ||
54
+ showButton === 'false';
55
+ if (hidden && colId !== undefined) {
56
+ this.exclude.push(parseInt(colId, 10));
57
+ }
58
+ return true;
59
+ }
60
+ default:
61
+ return true;
62
+ }
63
+ }
64
+
65
+ parseClose(name) {
66
+ if (name === 'autoFilter') {
67
+ if (this.exclude.length && this.ref) {
68
+ const [from, to] = this.ref.split(':');
69
+ this.model = {from, to: to || from, exclude: this.exclude};
70
+ }
71
+ return false;
34
72
  }
73
+ return true;
35
74
  }
36
75
  }
37
76
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exceljs-lts",
3
- "version": "4.5.2",
3
+ "version": "4.5.4",
4
4
  "description": "Maintained fork of exceljs — drop-in replacement, fixes Excel \"We found a problem with some content\" repair prompts, the TypeError crash on rich-text shared strings, and lost formula results (0, false, ''). Read and write xlsx and csv files.",
5
5
  "private": false,
6
6
  "license": "MIT",