table.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. "use strict";
  2. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  3. function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
  4. function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
  5. /* eslint-disable max-classes-per-file */
  6. var colCache = require('../utils/col-cache');
  7. var Column = /*#__PURE__*/function () {
  8. // wrapper around column model, allowing access and manipulation
  9. function Column(table, column, index) {
  10. _classCallCheck(this, Column);
  11. this.table = table;
  12. this.column = column;
  13. this.index = index;
  14. }
  15. _createClass(Column, [{
  16. key: "_set",
  17. value: function _set(name, value) {
  18. this.table.cacheState();
  19. this.column[name] = value;
  20. }
  21. /* eslint-disable lines-between-class-members */
  22. }, {
  23. key: "name",
  24. get: function get() {
  25. return this.column.name;
  26. },
  27. set: function set(value) {
  28. this._set('name', value);
  29. }
  30. }, {
  31. key: "filterButton",
  32. get: function get() {
  33. return this.column.filterButton;
  34. },
  35. set: function set(value) {
  36. this.column.filterButton = value;
  37. }
  38. }, {
  39. key: "style",
  40. get: function get() {
  41. return this.column.style;
  42. },
  43. set: function set(value) {
  44. this.column.style = value;
  45. }
  46. }, {
  47. key: "totalsRowLabel",
  48. get: function get() {
  49. return this.column.totalsRowLabel;
  50. },
  51. set: function set(value) {
  52. this._set('totalsRowLabel', value);
  53. }
  54. }, {
  55. key: "totalsRowFunction",
  56. get: function get() {
  57. return this.column.totalsRowFunction;
  58. },
  59. set: function set(value) {
  60. this._set('totalsRowFunction', value);
  61. }
  62. }, {
  63. key: "totalsRowResult",
  64. get: function get() {
  65. return this.column.totalsRowResult;
  66. },
  67. set: function set(value) {
  68. this._set('totalsRowResult', value);
  69. }
  70. }, {
  71. key: "totalsRowFormula",
  72. get: function get() {
  73. return this.column.totalsRowFormula;
  74. },
  75. set: function set(value) {
  76. this._set('totalsRowFormula', value);
  77. }
  78. /* eslint-enable lines-between-class-members */
  79. }]);
  80. return Column;
  81. }();
  82. var Table = /*#__PURE__*/function () {
  83. function Table(worksheet, table) {
  84. _classCallCheck(this, Table);
  85. this.worksheet = worksheet;
  86. if (table) {
  87. this.table = table; // check things are ok first
  88. this.validate();
  89. this.store();
  90. }
  91. }
  92. _createClass(Table, [{
  93. key: "getFormula",
  94. value: function getFormula(column) {
  95. // get the correct formula to apply to the totals row
  96. switch (column.totalsRowFunction) {
  97. case 'none':
  98. return null;
  99. case 'average':
  100. return "SUBTOTAL(101,".concat(this.table.name, "[").concat(column.name, "])");
  101. case 'countNums':
  102. return "SUBTOTAL(102,".concat(this.table.name, "[").concat(column.name, "])");
  103. case 'count':
  104. return "SUBTOTAL(103,".concat(this.table.name, "[").concat(column.name, "])");
  105. case 'max':
  106. return "SUBTOTAL(104,".concat(this.table.name, "[").concat(column.name, "])");
  107. case 'min':
  108. return "SUBTOTAL(105,".concat(this.table.name, "[").concat(column.name, "])");
  109. case 'stdDev':
  110. return "SUBTOTAL(106,".concat(this.table.name, "[").concat(column.name, "])");
  111. case 'var':
  112. return "SUBTOTAL(107,".concat(this.table.name, "[").concat(column.name, "])");
  113. case 'sum':
  114. return "SUBTOTAL(109,".concat(this.table.name, "[").concat(column.name, "])");
  115. case 'custom':
  116. return column.totalsRowFormula;
  117. default:
  118. throw new Error("Invalid Totals Row Function: ".concat(column.totalsRowFunction));
  119. }
  120. }
  121. }, {
  122. key: "validate",
  123. value: function validate() {
  124. var _this = this;
  125. var table = this.table; // set defaults and check is valid
  126. var assign = function assign(o, name, dflt) {
  127. if (o[name] === undefined) {
  128. o[name] = dflt;
  129. }
  130. };
  131. assign(table, 'headerRow', true);
  132. assign(table, 'totalsRow', false);
  133. assign(table, 'style', {});
  134. assign(table.style, 'theme', 'TableStyleMedium2');
  135. assign(table.style, 'showFirstColumn', false);
  136. assign(table.style, 'showLastColumn', false);
  137. assign(table.style, 'showRowStripes', false);
  138. assign(table.style, 'showColumnStripes', false);
  139. var assert = function assert(test, message) {
  140. if (!test) {
  141. throw new Error(message);
  142. }
  143. };
  144. assert(table.ref, 'Table must have ref');
  145. assert(table.columns, 'Table must have column definitions');
  146. assert(table.rows, 'Table must have row definitions');
  147. table.tl = colCache.decodeAddress(table.ref);
  148. var _table$tl = table.tl,
  149. row = _table$tl.row,
  150. col = _table$tl.col;
  151. assert(row > 0, 'Table must be on valid row');
  152. assert(col > 0, 'Table must be on valid col');
  153. var width = this.width,
  154. filterHeight = this.filterHeight,
  155. tableHeight = this.tableHeight; // autoFilterRef is a range that includes optional headers only
  156. table.autoFilterRef = colCache.encode(row, col, row + filterHeight - 1, col + width - 1); // tableRef is a range that includes optional headers and totals
  157. table.tableRef = colCache.encode(row, col, row + tableHeight - 1, col + width - 1);
  158. table.columns.forEach(function (column, i) {
  159. assert(column.name, "Column ".concat(i, " must have a name"));
  160. if (i === 0) {
  161. assign(column, 'totalsRowLabel', 'Total');
  162. } else {
  163. assign(column, 'totalsRowFunction', 'none');
  164. column.totalsRowFormula = _this.getFormula(column);
  165. }
  166. });
  167. }
  168. }, {
  169. key: "store",
  170. value: function store() {
  171. var _this2 = this;
  172. // where the table needs to store table data, headers, footers in
  173. // the sheet...
  174. var assignStyle = function assignStyle(cell, style) {
  175. if (style) {
  176. Object.keys(style).forEach(function (key) {
  177. cell[key] = style[key];
  178. });
  179. }
  180. };
  181. var worksheet = this.worksheet,
  182. table = this.table;
  183. var _table$tl2 = table.tl,
  184. row = _table$tl2.row,
  185. col = _table$tl2.col;
  186. var count = 0;
  187. if (table.headerRow) {
  188. var r = worksheet.getRow(row + count++);
  189. table.columns.forEach(function (column, j) {
  190. var style = column.style,
  191. name = column.name;
  192. var cell = r.getCell(col + j);
  193. cell.value = name;
  194. assignStyle(cell, style);
  195. });
  196. }
  197. table.rows.forEach(function (data) {
  198. var r = worksheet.getRow(row + count++);
  199. data.forEach(function (value, j) {
  200. var cell = r.getCell(col + j);
  201. cell.value = value;
  202. assignStyle(cell, table.columns[j].style);
  203. });
  204. });
  205. if (table.totalsRow) {
  206. var _r = worksheet.getRow(row + count++);
  207. table.columns.forEach(function (column, j) {
  208. var cell = _r.getCell(col + j);
  209. if (j === 0) {
  210. cell.value = column.totalsRowLabel;
  211. } else {
  212. var formula = _this2.getFormula(column);
  213. if (formula) {
  214. cell.value = {
  215. formula: column.totalsRowFormula,
  216. result: column.totalsRowResult
  217. };
  218. } else {
  219. cell.value = null;
  220. }
  221. }
  222. assignStyle(cell, column.style);
  223. });
  224. }
  225. }
  226. }, {
  227. key: "load",
  228. value: function load(worksheet) {
  229. var _this3 = this;
  230. // where the table will read necessary features from a loaded sheet
  231. var table = this.table;
  232. var _table$tl3 = table.tl,
  233. row = _table$tl3.row,
  234. col = _table$tl3.col;
  235. var count = 0;
  236. if (table.headerRow) {
  237. var r = worksheet.getRow(row + count++);
  238. table.columns.forEach(function (column, j) {
  239. var cell = r.getCell(col + j);
  240. cell.value = column.name;
  241. });
  242. }
  243. table.rows.forEach(function (data) {
  244. var r = worksheet.getRow(row + count++);
  245. data.forEach(function (value, j) {
  246. var cell = r.getCell(col + j);
  247. cell.value = value;
  248. });
  249. });
  250. if (table.totalsRow) {
  251. var _r2 = worksheet.getRow(row + count++);
  252. table.columns.forEach(function (column, j) {
  253. var cell = _r2.getCell(col + j);
  254. if (j === 0) {
  255. cell.value = column.totalsRowLabel;
  256. } else {
  257. var formula = _this3.getFormula(column);
  258. if (formula) {
  259. cell.value = {
  260. formula: column.totalsRowFormula,
  261. result: column.totalsRowResult
  262. };
  263. }
  264. }
  265. });
  266. }
  267. }
  268. }, {
  269. key: "cacheState",
  270. // ================================================================
  271. // TODO: Mutating methods
  272. value: function cacheState() {
  273. if (!this._cache) {
  274. this._cache = {
  275. ref: this.ref,
  276. width: this.width,
  277. tableHeight: this.tableHeight
  278. };
  279. }
  280. }
  281. }, {
  282. key: "commit",
  283. value: function commit() {
  284. // changes may have been made that might have on-sheet effects
  285. if (!this._cache) {
  286. return;
  287. } // check things are ok first
  288. this.validate();
  289. var ref = colCache.decodeAddress(this._cache.ref);
  290. if (this.ref !== this._cache.ref) {
  291. // wipe out whole table footprint at previous location
  292. for (var i = 0; i < this._cache.tableHeight; i++) {
  293. var row = this.worksheet.getRow(ref.row + i);
  294. for (var j = 0; j < this._cache.width; j++) {
  295. var cell = row.getCell(ref.col + j);
  296. cell.value = null;
  297. }
  298. }
  299. } else {
  300. // clear out below table if it has shrunk
  301. for (var _i = this.tableHeight; _i < this._cache.tableHeight; _i++) {
  302. var _row = this.worksheet.getRow(ref.row + _i);
  303. for (var _j = 0; _j < this._cache.width; _j++) {
  304. var _cell = _row.getCell(ref.col + _j);
  305. _cell.value = null;
  306. }
  307. } // clear out to right of table if it has lost columns
  308. for (var _i2 = 0; _i2 < this.tableHeight; _i2++) {
  309. var _row2 = this.worksheet.getRow(ref.row + _i2);
  310. for (var _j2 = this.width; _j2 < this._cache.width; _j2++) {
  311. var _cell2 = _row2.getCell(ref.col + _j2);
  312. _cell2.value = null;
  313. }
  314. }
  315. }
  316. this.store();
  317. }
  318. }, {
  319. key: "addRow",
  320. value: function addRow(values, rowNumber) {
  321. // Add a row of data, either insert at rowNumber or append
  322. this.cacheState();
  323. if (rowNumber === undefined) {
  324. this.table.rows.push(values);
  325. } else {
  326. this.table.rows.splice(rowNumber, 0, values);
  327. }
  328. }
  329. }, {
  330. key: "removeRows",
  331. value: function removeRows(rowIndex) {
  332. var count = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
  333. // Remove a rows of data
  334. this.cacheState();
  335. this.table.rows.splice(rowIndex, count);
  336. }
  337. }, {
  338. key: "getColumn",
  339. value: function getColumn(colIndex) {
  340. var column = this.table.columns[colIndex];
  341. return new Column(this, column, colIndex);
  342. }
  343. }, {
  344. key: "addColumn",
  345. value: function addColumn(column, values, colIndex) {
  346. // Add a new column, including column defn and values
  347. // Inserts at colNumber or adds to the right
  348. this.cacheState();
  349. if (colIndex === undefined) {
  350. this.table.columns.push(column);
  351. this.table.rows.forEach(function (row, i) {
  352. row.push(values[i]);
  353. });
  354. } else {
  355. this.table.columns.splice(colIndex, 0, column);
  356. this.table.rows.forEach(function (row, i) {
  357. row.splice(colIndex, 0, values[i]);
  358. });
  359. }
  360. }
  361. }, {
  362. key: "removeColumns",
  363. value: function removeColumns(colIndex) {
  364. var count = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
  365. // Remove a column with data
  366. this.cacheState();
  367. this.table.columns.splice(colIndex, count);
  368. this.table.rows.forEach(function (row) {
  369. row.splice(colIndex, count);
  370. });
  371. }
  372. }, {
  373. key: "_assign",
  374. value: function _assign(target, prop, value) {
  375. this.cacheState();
  376. target[prop] = value;
  377. }
  378. /* eslint-disable lines-between-class-members */
  379. }, {
  380. key: "width",
  381. get: function get() {
  382. // width of the table
  383. return this.table.columns.length;
  384. }
  385. }, {
  386. key: "height",
  387. get: function get() {
  388. // height of the table data
  389. return this.table.rows.length;
  390. }
  391. }, {
  392. key: "filterHeight",
  393. get: function get() {
  394. // height of the table data plus optional header row
  395. return this.height + (this.table.headerRow ? 1 : 0);
  396. }
  397. }, {
  398. key: "tableHeight",
  399. get: function get() {
  400. // full height of the table on the sheet
  401. return this.filterHeight + (this.table.totalsRow ? 1 : 0);
  402. }
  403. }, {
  404. key: "model",
  405. get: function get() {
  406. return this.table;
  407. },
  408. set: function set(value) {
  409. this.table = value;
  410. }
  411. }, {
  412. key: "ref",
  413. get: function get() {
  414. return this.table.ref;
  415. },
  416. set: function set(value) {
  417. this._assign(this.table, 'ref', value);
  418. }
  419. }, {
  420. key: "name",
  421. get: function get() {
  422. return this.table.name;
  423. },
  424. set: function set(value) {
  425. this.table.name = value;
  426. }
  427. }, {
  428. key: "displayName",
  429. get: function get() {
  430. return this.table.displyName || this.table.name;
  431. }
  432. }, {
  433. key: "displayNamename",
  434. set: function set(value) {
  435. this.table.displayName = value;
  436. }
  437. }, {
  438. key: "headerRow",
  439. get: function get() {
  440. return this.table.headerRow;
  441. },
  442. set: function set(value) {
  443. this._assign(this.table, 'headerRow', value);
  444. }
  445. }, {
  446. key: "totalsRow",
  447. get: function get() {
  448. return this.table.totalsRow;
  449. },
  450. set: function set(value) {
  451. this._assign(this.table, 'totalsRow', value);
  452. }
  453. }, {
  454. key: "theme",
  455. get: function get() {
  456. return this.table.style.name;
  457. },
  458. set: function set(value) {
  459. this.table.style.name = value;
  460. }
  461. }, {
  462. key: "showFirstColumn",
  463. get: function get() {
  464. return this.table.style.showFirstColumn;
  465. },
  466. set: function set(value) {
  467. this.table.style.showFirstColumn = value;
  468. }
  469. }, {
  470. key: "showLastColumn",
  471. get: function get() {
  472. return this.table.style.showLastColumn;
  473. },
  474. set: function set(value) {
  475. this.table.style.showLastColumn = value;
  476. }
  477. }, {
  478. key: "showRowStripes",
  479. get: function get() {
  480. return this.table.style.showRowStripes;
  481. },
  482. set: function set(value) {
  483. this.table.style.showRowStripes = value;
  484. }
  485. }, {
  486. key: "showColumnStripes",
  487. get: function get() {
  488. return this.table.style.showColumnStripes;
  489. },
  490. set: function set(value) {
  491. this.table.style.showColumnStripes = value;
  492. }
  493. /* eslint-enable lines-between-class-members */
  494. }]);
  495. return Table;
  496. }();
  497. module.exports = Table;
  498. //# sourceMappingURL=table.js.map