@yuanliwei/exceljs 4.4.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.
Files changed (185) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +3024 -0
  3. package/README_zh.md +2878 -0
  4. package/excel.js +13 -0
  5. package/index.d.ts +2040 -0
  6. package/index.ts +2 -0
  7. package/lib/csv/csv.js +191 -0
  8. package/lib/csv/line-buffer.js +74 -0
  9. package/lib/csv/stream-converter.js +135 -0
  10. package/lib/doc/anchor.js +91 -0
  11. package/lib/doc/cell.js +1124 -0
  12. package/lib/doc/column.js +320 -0
  13. package/lib/doc/data/theme1.json +234 -0
  14. package/lib/doc/data-validations.js +19 -0
  15. package/lib/doc/defined-names.js +196 -0
  16. package/lib/doc/enums.js +48 -0
  17. package/lib/doc/image.js +59 -0
  18. package/lib/doc/modelcontainer.js +18 -0
  19. package/lib/doc/note.js +65 -0
  20. package/lib/doc/pivot-table.js +132 -0
  21. package/lib/doc/range.js +257 -0
  22. package/lib/doc/row.js +415 -0
  23. package/lib/doc/table.js +465 -0
  24. package/lib/doc/workbook.js +224 -0
  25. package/lib/doc/worksheet.js +949 -0
  26. package/lib/exceljs.bare.js +13 -0
  27. package/lib/exceljs.browser.js +36 -0
  28. package/lib/exceljs.nodejs.js +14 -0
  29. package/lib/stream/xlsx/hyperlink-reader.js +83 -0
  30. package/lib/stream/xlsx/sheet-comments-writer.js +121 -0
  31. package/lib/stream/xlsx/sheet-rels-writer.js +119 -0
  32. package/lib/stream/xlsx/workbook-reader.js +337 -0
  33. package/lib/stream/xlsx/workbook-writer.js +347 -0
  34. package/lib/stream/xlsx/worksheet-reader.js +374 -0
  35. package/lib/stream/xlsx/worksheet-writer.js +717 -0
  36. package/lib/utils/auto-drain.js +15 -0
  37. package/lib/utils/browser-buffer-decode.js +14 -0
  38. package/lib/utils/browser-buffer-encode.js +15 -0
  39. package/lib/utils/cell-matrix.js +165 -0
  40. package/lib/utils/col-cache.js +287 -0
  41. package/lib/utils/copy-style.js +43 -0
  42. package/lib/utils/encryptor.js +55 -0
  43. package/lib/utils/iterate-stream.js +48 -0
  44. package/lib/utils/parse-sax.js +30 -0
  45. package/lib/utils/shared-formula.js +44 -0
  46. package/lib/utils/shared-strings.js +35 -0
  47. package/lib/utils/stream-base64.js +72 -0
  48. package/lib/utils/stream-buf.js +364 -0
  49. package/lib/utils/string-buf.js +82 -0
  50. package/lib/utils/string-builder.js +35 -0
  51. package/lib/utils/stuttered-pipe.js +67 -0
  52. package/lib/utils/typed-stack.js +24 -0
  53. package/lib/utils/under-dash.js +184 -0
  54. package/lib/utils/utils.js +205 -0
  55. package/lib/utils/xml-stream.js +169 -0
  56. package/lib/utils/zip-stream.js +87 -0
  57. package/lib/xlsx/.rels +11 -0
  58. package/lib/xlsx/calcChain.xml +6 -0
  59. package/lib/xlsx/core.xml +7 -0
  60. package/lib/xlsx/defaultnumformats.js +153 -0
  61. package/lib/xlsx/rel-type.js +20 -0
  62. package/lib/xlsx/styles.xml +41 -0
  63. package/lib/xlsx/workbook.xml +16 -0
  64. package/lib/xlsx/xform/base-xform.js +145 -0
  65. package/lib/xlsx/xform/book/defined-name-xform.js +91 -0
  66. package/lib/xlsx/xform/book/sheet-xform.js +34 -0
  67. package/lib/xlsx/xform/book/workbook-calc-properties-xform.js +26 -0
  68. package/lib/xlsx/xform/book/workbook-pivot-cache-xform.js +29 -0
  69. package/lib/xlsx/xform/book/workbook-properties-xform.js +29 -0
  70. package/lib/xlsx/xform/book/workbook-view-xform.js +53 -0
  71. package/lib/xlsx/xform/book/workbook-xform.js +259 -0
  72. package/lib/xlsx/xform/comment/comment-xform.js +105 -0
  73. package/lib/xlsx/xform/comment/comments-xform.js +82 -0
  74. package/lib/xlsx/xform/comment/style/vml-position-xform.js +39 -0
  75. package/lib/xlsx/xform/comment/style/vml-protection-xform.js +36 -0
  76. package/lib/xlsx/xform/comment/vml-anchor-xform.js +60 -0
  77. package/lib/xlsx/xform/comment/vml-client-data-xform.js +95 -0
  78. package/lib/xlsx/xform/comment/vml-notes-xform.js +107 -0
  79. package/lib/xlsx/xform/comment/vml-shape-xform.js +95 -0
  80. package/lib/xlsx/xform/comment/vml-textbox-xform.js +64 -0
  81. package/lib/xlsx/xform/composite-xform.js +56 -0
  82. package/lib/xlsx/xform/core/app-heading-pairs-xform.js +32 -0
  83. package/lib/xlsx/xform/core/app-titles-of-parts-xform.js +28 -0
  84. package/lib/xlsx/xform/core/app-xform.js +100 -0
  85. package/lib/xlsx/xform/core/content-types-xform.js +135 -0
  86. package/lib/xlsx/xform/core/core-xform.js +136 -0
  87. package/lib/xlsx/xform/core/relationship-xform.js +25 -0
  88. package/lib/xlsx/xform/core/relationships-xform.js +73 -0
  89. package/lib/xlsx/xform/drawing/base-cell-anchor-xform.js +48 -0
  90. package/lib/xlsx/xform/drawing/blip-fill-xform.js +71 -0
  91. package/lib/xlsx/xform/drawing/blip-xform.js +42 -0
  92. package/lib/xlsx/xform/drawing/c-nv-pic-pr-xform.js +38 -0
  93. package/lib/xlsx/xform/drawing/c-nv-pr-xform.js +68 -0
  94. package/lib/xlsx/xform/drawing/cell-position-xform.js +77 -0
  95. package/lib/xlsx/xform/drawing/drawing-xform.js +109 -0
  96. package/lib/xlsx/xform/drawing/ext-lst-xform.js +43 -0
  97. package/lib/xlsx/xform/drawing/ext-xform.js +44 -0
  98. package/lib/xlsx/xform/drawing/hlink-click-xform.js +41 -0
  99. package/lib/xlsx/xform/drawing/nv-pic-pr-xform.js +65 -0
  100. package/lib/xlsx/xform/drawing/one-cell-anchor-xform.js +63 -0
  101. package/lib/xlsx/xform/drawing/pic-xform.js +77 -0
  102. package/lib/xlsx/xform/drawing/sp-pr.js +17 -0
  103. package/lib/xlsx/xform/drawing/two-cell-anchor-xform.js +62 -0
  104. package/lib/xlsx/xform/list-xform.js +95 -0
  105. package/lib/xlsx/xform/pivot-table/cache-field.js +43 -0
  106. package/lib/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +77 -0
  107. package/lib/xlsx/xform/pivot-table/pivot-cache-records-xform.js +103 -0
  108. package/lib/xlsx/xform/pivot-table/pivot-table-xform.js +189 -0
  109. package/lib/xlsx/xform/sheet/auto-filter-xform.js +38 -0
  110. package/lib/xlsx/xform/sheet/cell-xform.js +498 -0
  111. package/lib/xlsx/xform/sheet/cf/cf-rule-xform.js +301 -0
  112. package/lib/xlsx/xform/sheet/cf/cfvo-xform.js +27 -0
  113. package/lib/xlsx/xform/sheet/cf/color-scale-xform.js +45 -0
  114. package/lib/xlsx/xform/sheet/cf/conditional-formatting-xform.js +48 -0
  115. package/lib/xlsx/xform/sheet/cf/conditional-formattings-xform.js +92 -0
  116. package/lib/xlsx/xform/sheet/cf/databar-xform.js +49 -0
  117. package/lib/xlsx/xform/sheet/cf/ext-lst-ref-xform.js +87 -0
  118. package/lib/xlsx/xform/sheet/cf/formula-xform.js +25 -0
  119. package/lib/xlsx/xform/sheet/cf/icon-set-xform.js +47 -0
  120. package/lib/xlsx/xform/sheet/cf-ext/cf-icon-ext-xform.js +27 -0
  121. package/lib/xlsx/xform/sheet/cf-ext/cf-rule-ext-xform.js +98 -0
  122. package/lib/xlsx/xform/sheet/cf-ext/cfvo-ext-xform.js +43 -0
  123. package/lib/xlsx/xform/sheet/cf-ext/conditional-formatting-ext-xform.js +62 -0
  124. package/lib/xlsx/xform/sheet/cf-ext/conditional-formattings-ext-xform.js +50 -0
  125. package/lib/xlsx/xform/sheet/cf-ext/databar-ext-xform.js +98 -0
  126. package/lib/xlsx/xform/sheet/cf-ext/f-ext-xform.js +25 -0
  127. package/lib/xlsx/xform/sheet/cf-ext/icon-set-ext-xform.js +73 -0
  128. package/lib/xlsx/xform/sheet/cf-ext/sqref-ext-xform.js +25 -0
  129. package/lib/xlsx/xform/sheet/col-xform.js +86 -0
  130. package/lib/xlsx/xform/sheet/data-validations-xform.js +257 -0
  131. package/lib/xlsx/xform/sheet/dimension-xform.js +29 -0
  132. package/lib/xlsx/xform/sheet/drawing-xform.js +33 -0
  133. package/lib/xlsx/xform/sheet/ext-lst-xform.js +86 -0
  134. package/lib/xlsx/xform/sheet/header-footer-xform.js +146 -0
  135. package/lib/xlsx/xform/sheet/hyperlink-xform.js +54 -0
  136. package/lib/xlsx/xform/sheet/merge-cell-xform.js +27 -0
  137. package/lib/xlsx/xform/sheet/merges.js +56 -0
  138. package/lib/xlsx/xform/sheet/outline-properties-xform.js +43 -0
  139. package/lib/xlsx/xform/sheet/page-breaks-xform.js +27 -0
  140. package/lib/xlsx/xform/sheet/page-margins-xform.js +49 -0
  141. package/lib/xlsx/xform/sheet/page-setup-properties-xform.js +35 -0
  142. package/lib/xlsx/xform/sheet/page-setup-xform.js +103 -0
  143. package/lib/xlsx/xform/sheet/picture-xform.js +33 -0
  144. package/lib/xlsx/xform/sheet/print-options-xform.js +49 -0
  145. package/lib/xlsx/xform/sheet/row-breaks-xform.js +39 -0
  146. package/lib/xlsx/xform/sheet/row-xform.js +142 -0
  147. package/lib/xlsx/xform/sheet/sheet-format-properties-xform.js +55 -0
  148. package/lib/xlsx/xform/sheet/sheet-properties-xform.js +90 -0
  149. package/lib/xlsx/xform/sheet/sheet-protection-xform.js +89 -0
  150. package/lib/xlsx/xform/sheet/sheet-view-xform.js +202 -0
  151. package/lib/xlsx/xform/sheet/table-part-xform.js +33 -0
  152. package/lib/xlsx/xform/sheet/worksheet-xform.js +548 -0
  153. package/lib/xlsx/xform/simple/boolean-xform.js +31 -0
  154. package/lib/xlsx/xform/simple/date-xform.js +66 -0
  155. package/lib/xlsx/xform/simple/float-xform.js +51 -0
  156. package/lib/xlsx/xform/simple/integer-xform.js +57 -0
  157. package/lib/xlsx/xform/simple/string-xform.js +51 -0
  158. package/lib/xlsx/xform/static-xform.js +64 -0
  159. package/lib/xlsx/xform/strings/phonetic-text-xform.js +98 -0
  160. package/lib/xlsx/xform/strings/rich-text-xform.js +101 -0
  161. package/lib/xlsx/xform/strings/shared-string-xform.js +102 -0
  162. package/lib/xlsx/xform/strings/shared-strings-xform.js +127 -0
  163. package/lib/xlsx/xform/strings/text-xform.js +44 -0
  164. package/lib/xlsx/xform/style/alignment-xform.js +172 -0
  165. package/lib/xlsx/xform/style/border-xform.js +207 -0
  166. package/lib/xlsx/xform/style/color-xform.js +63 -0
  167. package/lib/xlsx/xform/style/dxf-xform.js +111 -0
  168. package/lib/xlsx/xform/style/fill-xform.js +364 -0
  169. package/lib/xlsx/xform/style/font-xform.js +102 -0
  170. package/lib/xlsx/xform/style/numfmt-xform.js +63 -0
  171. package/lib/xlsx/xform/style/protection-xform.js +60 -0
  172. package/lib/xlsx/xform/style/style-xform.js +125 -0
  173. package/lib/xlsx/xform/style/styles-xform.js +527 -0
  174. package/lib/xlsx/xform/style/underline-xform.js +47 -0
  175. package/lib/xlsx/xform/table/auto-filter-xform.js +81 -0
  176. package/lib/xlsx/xform/table/custom-filter-xform.js +33 -0
  177. package/lib/xlsx/xform/table/filter-column-xform.js +96 -0
  178. package/lib/xlsx/xform/table/filter-xform.js +31 -0
  179. package/lib/xlsx/xform/table/table-column-xform.js +44 -0
  180. package/lib/xlsx/xform/table/table-style-info-xform.js +41 -0
  181. package/lib/xlsx/xform/table/table-xform.js +131 -0
  182. package/lib/xlsx/xlsx.js +774 -0
  183. package/lib/xlsx/xml/theme1.js +3 -0
  184. package/lib/xlsx/xml/theme1.xml +318 -0
  185. package/package.json +149 -0
@@ -0,0 +1,196 @@
1
+ 'use strict';
2
+
3
+ const _ = require('../utils/under-dash');
4
+ const colCache = require('../utils/col-cache');
5
+ const CellMatrix = require('../utils/cell-matrix');
6
+ const Range = require('./range');
7
+
8
+ const rangeRegexp = /[$](\w+)[$](\d+)(:[$](\w+)[$](\d+))?/;
9
+
10
+ class DefinedNames {
11
+ constructor() {
12
+ this.matrixMap = {};
13
+ }
14
+
15
+ getMatrix(name) {
16
+ const matrix = this.matrixMap[name] || (this.matrixMap[name] = new CellMatrix());
17
+ return matrix;
18
+ }
19
+
20
+ // add a name to a cell. locStr in the form SheetName!$col$row or SheetName!$c1$r1:$c2:$r2
21
+ add(locStr, name) {
22
+ const location = colCache.decodeEx(locStr);
23
+ this.addEx(location, name);
24
+ }
25
+
26
+ addEx(location, name) {
27
+ const matrix = this.getMatrix(name);
28
+ if (location.top) {
29
+ for (let col = location.left; col <= location.right; col++) {
30
+ for (let row = location.top; row <= location.bottom; row++) {
31
+ const address = {
32
+ sheetName: location.sheetName,
33
+ address: colCache.n2l(col) + row,
34
+ row,
35
+ col,
36
+ };
37
+
38
+ matrix.addCellEx(address);
39
+ }
40
+ }
41
+ } else {
42
+ matrix.addCellEx(location);
43
+ }
44
+ }
45
+
46
+ remove(locStr, name) {
47
+ const location = colCache.decodeEx(locStr);
48
+ this.removeEx(location, name);
49
+ }
50
+
51
+ removeEx(location, name) {
52
+ const matrix = this.getMatrix(name);
53
+ matrix.removeCellEx(location);
54
+ }
55
+
56
+ removeAllNames(location) {
57
+ _.each(this.matrixMap, matrix => {
58
+ matrix.removeCellEx(location);
59
+ });
60
+ }
61
+
62
+ forEach(callback) {
63
+ _.each(this.matrixMap, (matrix, name) => {
64
+ matrix.forEach(cell => {
65
+ callback(name, cell);
66
+ });
67
+ });
68
+ }
69
+
70
+ // get all the names of a cell
71
+ getNames(addressStr) {
72
+ return this.getNamesEx(colCache.decodeEx(addressStr));
73
+ }
74
+
75
+ getNamesEx(address) {
76
+ return _.map(this.matrixMap, (matrix, name) => matrix.findCellEx(address) && name).filter(
77
+ Boolean
78
+ );
79
+ }
80
+
81
+ _explore(matrix, cell) {
82
+ cell.mark = false;
83
+ const {sheetName} = cell;
84
+
85
+ const range = new Range(cell.row, cell.col, cell.row, cell.col, sheetName);
86
+ let x;
87
+ let y;
88
+
89
+ // grow vertical - only one col to worry about
90
+ function vGrow(yy, edge) {
91
+ const c = matrix.findCellAt(sheetName, yy, cell.col);
92
+ if (!c || !c.mark) {
93
+ return false;
94
+ }
95
+ range[edge] = yy;
96
+ c.mark = false;
97
+ return true;
98
+ }
99
+ for (y = cell.row - 1; vGrow(y, 'top'); y--);
100
+ for (y = cell.row + 1; vGrow(y, 'bottom'); y++);
101
+
102
+ // grow horizontal - ensure all rows can grow
103
+ function hGrow(xx, edge) {
104
+ const cells = [];
105
+ for (y = range.top; y <= range.bottom; y++) {
106
+ const c = matrix.findCellAt(sheetName, y, xx);
107
+ if (c && c.mark) {
108
+ cells.push(c);
109
+ } else {
110
+ return false;
111
+ }
112
+ }
113
+ range[edge] = xx;
114
+ for (let i = 0; i < cells.length; i++) {
115
+ cells[i].mark = false;
116
+ }
117
+ return true;
118
+ }
119
+ for (x = cell.col - 1; hGrow(x, 'left'); x--);
120
+ for (x = cell.col + 1; hGrow(x, 'right'); x++);
121
+
122
+ return range;
123
+ }
124
+
125
+ getRanges(name, matrix) {
126
+ matrix = matrix || this.matrixMap[name];
127
+
128
+ if (!matrix) {
129
+ return {name, ranges: []};
130
+ }
131
+
132
+ // mark and sweep!
133
+ matrix.forEach(cell => {
134
+ cell.mark = true;
135
+ });
136
+ const ranges = matrix
137
+ .map(cell => cell.mark && this._explore(matrix, cell))
138
+ .filter(Boolean)
139
+ .map(range => range.$shortRange);
140
+
141
+ return {
142
+ name,
143
+ ranges,
144
+ };
145
+ }
146
+
147
+ normaliseMatrix(matrix, sheetName) {
148
+ // some of the cells might have shifted on specified sheet
149
+ // need to reassign rows, cols
150
+ matrix.forEachInSheet(sheetName, (cell, row, col) => {
151
+ if (cell) {
152
+ if (cell.row !== row || cell.col !== col) {
153
+ cell.row = row;
154
+ cell.col = col;
155
+ cell.address = colCache.n2l(col) + row;
156
+ }
157
+ }
158
+ });
159
+ }
160
+
161
+ spliceRows(sheetName, start, numDelete, numInsert) {
162
+ _.each(this.matrixMap, matrix => {
163
+ matrix.spliceRows(sheetName, start, numDelete, numInsert);
164
+ this.normaliseMatrix(matrix, sheetName);
165
+ });
166
+ }
167
+
168
+ spliceColumns(sheetName, start, numDelete, numInsert) {
169
+ _.each(this.matrixMap, matrix => {
170
+ matrix.spliceColumns(sheetName, start, numDelete, numInsert);
171
+ this.normaliseMatrix(matrix, sheetName);
172
+ });
173
+ }
174
+
175
+ get model() {
176
+ // To get names per cell - just iterate over all names finding cells if they exist
177
+ return _.map(this.matrixMap, (matrix, name) => this.getRanges(name, matrix)).filter(
178
+ definedName => definedName.ranges.length
179
+ );
180
+ }
181
+
182
+ set model(value) {
183
+ // value is [ { name, ranges }, ... ]
184
+ const matrixMap = (this.matrixMap = {});
185
+ value.forEach(definedName => {
186
+ const matrix = (matrixMap[definedName.name] = new CellMatrix());
187
+ definedName.ranges.forEach(rangeStr => {
188
+ if (rangeRegexp.test(rangeStr.split('!').pop() || '')) {
189
+ matrix.addCell(rangeStr);
190
+ }
191
+ });
192
+ });
193
+ }
194
+ }
195
+
196
+ module.exports = DefinedNames;
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ ValueType: {
5
+ Null: 0,
6
+ Merge: 1,
7
+ Number: 2,
8
+ String: 3,
9
+ Date: 4,
10
+ Hyperlink: 5,
11
+ Formula: 6,
12
+ SharedString: 7,
13
+ RichText: 8,
14
+ Boolean: 9,
15
+ Error: 10,
16
+ },
17
+ FormulaType: {
18
+ None: 0,
19
+ Master: 1,
20
+ Shared: 2,
21
+ },
22
+ RelationshipType: {
23
+ None: 0,
24
+ OfficeDocument: 1,
25
+ Worksheet: 2,
26
+ CalcChain: 3,
27
+ SharedStrings: 4,
28
+ Styles: 5,
29
+ Theme: 6,
30
+ Hyperlink: 7,
31
+ },
32
+ DocumentType: {
33
+ Xlsx: 1,
34
+ },
35
+ ReadingOrder: {
36
+ LeftToRight: 1,
37
+ RightToLeft: 2,
38
+ },
39
+ ErrorValue: {
40
+ NotApplicable: '#N/A',
41
+ Ref: '#REF!',
42
+ Name: '#NAME?',
43
+ DivZero: '#DIV/0!',
44
+ Null: '#NULL!',
45
+ Value: '#VALUE!',
46
+ Num: '#NUM!',
47
+ },
48
+ };
@@ -0,0 +1,59 @@
1
+ const colCache = require('../utils/col-cache');
2
+ const Anchor = require('./anchor');
3
+
4
+ class Image {
5
+ constructor(worksheet, model) {
6
+ this.worksheet = worksheet;
7
+ this.model = model;
8
+ }
9
+
10
+ get model() {
11
+ switch (this.type) {
12
+ case 'background':
13
+ return {
14
+ type: this.type,
15
+ imageId: this.imageId,
16
+ };
17
+ case 'image':
18
+ return {
19
+ type: this.type,
20
+ imageId: this.imageId,
21
+ hyperlinks: this.range.hyperlinks,
22
+ range: {
23
+ tl: this.range.tl.model,
24
+ br: this.range.br && this.range.br.model,
25
+ ext: this.range.ext,
26
+ editAs: this.range.editAs,
27
+ },
28
+ };
29
+ default:
30
+ throw new Error('Invalid Image Type');
31
+ }
32
+ }
33
+
34
+ set model({type, imageId, range, hyperlinks}) {
35
+ this.type = type;
36
+ this.imageId = imageId;
37
+
38
+ if (type === 'image') {
39
+ if (typeof range === 'string') {
40
+ const decoded = colCache.decode(range);
41
+ this.range = {
42
+ tl: new Anchor(this.worksheet, {col: decoded.left, row: decoded.top}, -1),
43
+ br: new Anchor(this.worksheet, {col: decoded.right, row: decoded.bottom}, 0),
44
+ editAs: 'oneCell',
45
+ };
46
+ } else {
47
+ this.range = {
48
+ tl: new Anchor(this.worksheet, range.tl, 0),
49
+ br: range.br && new Anchor(this.worksheet, range.br, 0),
50
+ ext: range.ext,
51
+ editAs: range.editAs,
52
+ hyperlinks: hyperlinks || range.hyperlinks,
53
+ };
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ module.exports = Image;
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ const XLSX = require('../xlsx/xlsx');
4
+
5
+ class ModelContainer {
6
+ constructor(model) {
7
+ this.model = model;
8
+ }
9
+
10
+ get xlsx() {
11
+ if (!this._xlsx) {
12
+ this._xlsx = new XLSX(this);
13
+ }
14
+ return this._xlsx;
15
+ }
16
+ }
17
+
18
+ module.exports = ModelContainer;
@@ -0,0 +1,65 @@
1
+ const _ = require('../utils/under-dash');
2
+
3
+ class Note {
4
+ constructor(note) {
5
+ this.note = note;
6
+ }
7
+
8
+ get model() {
9
+ let value = null;
10
+ switch (typeof this.note) {
11
+ case 'string':
12
+ value = {
13
+ type: 'note',
14
+ note: {
15
+ texts: [
16
+ {
17
+ text: this.note,
18
+ },
19
+ ],
20
+ },
21
+ };
22
+ break;
23
+ default:
24
+ value = {
25
+ type: 'note',
26
+ note: this.note,
27
+ };
28
+ break;
29
+ }
30
+ // Suitable for all cell comments
31
+ return _.deepMerge({}, Note.DEFAULT_CONFIGS, value);
32
+ }
33
+
34
+ set model(value) {
35
+ const {note} = value;
36
+ const {texts} = note;
37
+ if (texts.length === 1 && Object.keys(texts[0]).length === 1) {
38
+ this.note = texts[0].text;
39
+ } else {
40
+ this.note = note;
41
+ }
42
+ }
43
+
44
+ static fromModel(model) {
45
+ const note = new Note();
46
+ note.model = model;
47
+ return note;
48
+ }
49
+ }
50
+
51
+ Note.DEFAULT_CONFIGS = {
52
+ note: {
53
+ margins: {
54
+ insetmode: 'auto',
55
+ inset: [0.13, 0.13, 0.25, 0.25],
56
+ },
57
+ protection: {
58
+ locked: 'True',
59
+ lockText: 'True',
60
+ },
61
+ editAs: 'absolute',
62
+ },
63
+ };
64
+
65
+ module.exports = Note;
@@ -0,0 +1,132 @@
1
+ const {objectFromProps, range, toSortedArray} = require('../utils/utils');
2
+
3
+ // TK(2023-10-10): turn this into a class constructor.
4
+
5
+ function makePivotTable(worksheet, model) {
6
+ // Example `model`:
7
+ // {
8
+ // // Source of data: the entire sheet range is taken,
9
+ // // akin to `worksheet1.getSheetValues()`.
10
+ // sourceSheet: worksheet1,
11
+ //
12
+ // // Pivot table fields: values indicate field names;
13
+ // // they come from the first row in `worksheet1`.
14
+ // rows: ['A', 'B'],
15
+ // columns: ['C'],
16
+ // values: ['E'], // only 1 item possible for now
17
+ // metric: 'sum', // only 'sum' possible for now
18
+ // }
19
+
20
+ validate(worksheet, model);
21
+
22
+ const {sourceSheet} = model;
23
+ let {rows, columns, values} = model;
24
+
25
+ const cacheFields = makeCacheFields(sourceSheet, [...rows, ...columns]);
26
+
27
+ // let {rows, columns, values} use indices instead of names;
28
+ // names can then be accessed via `pivotTable.cacheFields[index].name`.
29
+ // *Note*: Using `reduce` as `Object.fromEntries` requires Node 12+;
30
+ // ExcelJS is >=8.3.0 (as of 2023-10-08).
31
+ const nameToIndex = cacheFields.reduce((result, cacheField, index) => {
32
+ result[cacheField.name] = index;
33
+ return result;
34
+ }, {});
35
+ rows = rows.map(row => nameToIndex[row]);
36
+ columns = columns.map(column => nameToIndex[column]);
37
+ values = values.map(value => nameToIndex[value]);
38
+
39
+ // form pivot table object
40
+ return {
41
+ sourceSheet,
42
+ rows,
43
+ columns,
44
+ values,
45
+ metric: 'sum',
46
+ cacheFields,
47
+ // defined in <pivotTableDefinition> of xl/pivotTables/pivotTable1.xml;
48
+ // also used in xl/workbook.xml
49
+ cacheId: '10',
50
+ };
51
+ }
52
+
53
+ function validate(worksheet, model) {
54
+ if (worksheet.workbook.pivotTables.length === 1) {
55
+ throw new Error(
56
+ 'A pivot table was already added. At this time, ExcelJS supports at most one pivot table per file.'
57
+ );
58
+ }
59
+
60
+ if (model.metric && model.metric !== 'sum') {
61
+ throw new Error('Only the "sum" metric is supported at this time.');
62
+ }
63
+
64
+ const headerNames = model.sourceSheet.getRow(1).values.slice(1);
65
+ const isInHeaderNames = objectFromProps(headerNames, true);
66
+ for (const name of [...model.rows, ...model.columns, ...model.values]) {
67
+ if (!isInHeaderNames[name]) {
68
+ throw new Error(`The header name "${name}" was not found in ${model.sourceSheet.name}.`);
69
+ }
70
+ }
71
+
72
+ if (!model.rows.length) {
73
+ throw new Error('No pivot table rows specified.');
74
+ }
75
+
76
+ if (!model.columns.length) {
77
+ throw new Error('No pivot table columns specified.');
78
+ }
79
+
80
+ if (model.values.length !== 1) {
81
+ throw new Error('Exactly 1 value needs to be specified at this time.');
82
+ }
83
+ }
84
+
85
+ function makeCacheFields(worksheet, fieldNamesWithSharedItems) {
86
+ // Cache fields are used in pivot tables to reference source data.
87
+ //
88
+ // Example
89
+ // -------
90
+ // Turn
91
+ //
92
+ // `worksheet` sheet values [
93
+ // ['A', 'B', 'C', 'D', 'E'],
94
+ // ['a1', 'b1', 'c1', 4, 5],
95
+ // ['a1', 'b2', 'c1', 4, 5],
96
+ // ['a2', 'b1', 'c2', 14, 24],
97
+ // ['a2', 'b2', 'c2', 24, 35],
98
+ // ['a3', 'b1', 'c3', 34, 45],
99
+ // ['a3', 'b2', 'c3', 44, 45]
100
+ // ];
101
+ // fieldNamesWithSharedItems = ['A', 'B', 'C'];
102
+ //
103
+ // into
104
+ //
105
+ // [
106
+ // { name: 'A', sharedItems: ['a1', 'a2', 'a3'] },
107
+ // { name: 'B', sharedItems: ['b1', 'b2'] },
108
+ // { name: 'C', sharedItems: ['c1', 'c2', 'c3'] },
109
+ // { name: 'D', sharedItems: null },
110
+ // { name: 'E', sharedItems: null }
111
+ // ]
112
+
113
+ const names = worksheet.getRow(1).values;
114
+ const nameToHasSharedItems = objectFromProps(fieldNamesWithSharedItems, true);
115
+
116
+ const aggregate = columnIndex => {
117
+ const columnValues = worksheet.getColumn(columnIndex).values.splice(2);
118
+ const columnValuesAsSet = new Set(columnValues);
119
+ return toSortedArray(columnValuesAsSet);
120
+ };
121
+
122
+ // make result
123
+ const result = [];
124
+ for (const columnIndex of range(1, names.length)) {
125
+ const name = names[columnIndex];
126
+ const sharedItems = nameToHasSharedItems[name] ? aggregate(columnIndex) : null;
127
+ result.push({name, sharedItems});
128
+ }
129
+ return result;
130
+ }
131
+
132
+ module.exports = {makePivotTable};