@odoo/o-spreadsheet 17.0.51 → 17.0.52

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.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 17.0.51
6
- * @date 2025-02-25T05:56:10.869Z
7
- * @hash b433912
5
+ * @version 17.0.52
6
+ * @date 2025-03-07T10:30:04.783Z
7
+ * @hash c19cd9a
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -6213,7 +6213,7 @@
6213
6213
  condition: (cell) => !cell.isFormula &&
6214
6214
  evaluateLiteral(cell.content, { locale: DEFAULT_LOCALE }).type === CellValueType.text &&
6215
6215
  alphaNumericValueRegExp.test(cell.content),
6216
- generateRule: (cell, cells) => {
6216
+ generateRule: (cell, cells, direction) => {
6217
6217
  const numberPostfix = parseInt(cell.content.match(numberPostfixRegExp)[0]);
6218
6218
  const prefix = cell.content.match(stringPrefixRegExp)[0];
6219
6219
  const numberPostfixLength = cell.content.length - prefix.length;
@@ -6221,7 +6221,10 @@
6221
6221
  alphaNumericValueRegExp.test(evaluatedCell.value)) // get consecutive alphanumeric cells, no matter what the prefix is
6222
6222
  .filter((cell) => prefix === cell.value.toString().match(stringPrefixRegExp)[0])
6223
6223
  .map((cell) => parseInt(cell.value.toString().match(numberPostfixRegExp)[0]));
6224
- const increment = calculateIncrementBasedOnGroup(group);
6224
+ let increment = calculateIncrementBasedOnGroup(group);
6225
+ if (["up", "left"].includes(direction) && group.length === 1) {
6226
+ increment = -increment;
6227
+ }
6225
6228
  return {
6226
6229
  type: "ALPHANUMERIC_INCREMENT_MODIFIER",
6227
6230
  prefix,
@@ -6250,9 +6253,12 @@
6250
6253
  .add("increment_number", {
6251
6254
  condition: (cell) => !cell.isFormula &&
6252
6255
  evaluateLiteral(cell.content, { locale: DEFAULT_LOCALE }).type === CellValueType.number,
6253
- generateRule: (cell, cells) => {
6256
+ generateRule: (cell, cells, direction) => {
6254
6257
  const group = getGroup(cell, cells, (evaluatedCell) => evaluatedCell.type === CellValueType.number).map((cell) => Number(cell.value));
6255
- const increment = calculateIncrementBasedOnGroup(group);
6258
+ let increment = calculateIncrementBasedOnGroup(group);
6259
+ if (["up", "left"].includes(direction) && group.length === 1) {
6260
+ increment = -increment;
6261
+ }
6256
6262
  const evaluation = evaluateLiteral(cell.content, { locale: DEFAULT_LOCALE });
6257
6263
  return {
6258
6264
  type: "INCREMENT_MODIFIER",
@@ -28962,6 +28968,9 @@
28962
28968
  this.MAX_SIZE_MARGIN = 90;
28963
28969
  this.MIN_ELEMENT_SIZE = MIN_COL_WIDTH;
28964
28970
  }
28971
+ get sheetId() {
28972
+ return this.env.model.getters.getActiveSheetId();
28973
+ }
28965
28974
  _getEvOffset(ev) {
28966
28975
  return ev.offsetX;
28967
28976
  }
@@ -28984,10 +28993,10 @@
28984
28993
  return this.env.model.getters.getEdgeScrollCol(position, position, position);
28985
28994
  }
28986
28995
  _getDimensionsInViewport(index) {
28987
- return this.env.model.getters.getColDimensionsInViewport(this.env.model.getters.getActiveSheetId(), index);
28996
+ return this.env.model.getters.getColDimensionsInViewport(this.sheetId, index);
28988
28997
  }
28989
28998
  _getElementSize(index) {
28990
- return this.env.model.getters.getColSize(this.env.model.getters.getActiveSheetId(), index);
28999
+ return this.env.model.getters.getColSize(this.sheetId, index);
28991
29000
  }
28992
29001
  _getMaxSize() {
28993
29002
  return this.colResizerRef.el.clientWidth;
@@ -28998,7 +29007,7 @@
28998
29007
  const cols = this.env.model.getters.getActiveCols();
28999
29008
  this.env.model.dispatch("RESIZE_COLUMNS_ROWS", {
29000
29009
  dimension: "COL",
29001
- sheetId: this.env.model.getters.getActiveSheetId(),
29010
+ sheetId: this.sheetId,
29002
29011
  elements: cols.has(index) ? [...cols] : [index],
29003
29012
  size,
29004
29013
  });
@@ -29011,7 +29020,7 @@
29011
29020
  elements.push(colIndex);
29012
29021
  }
29013
29022
  const result = this.env.model.dispatch("MOVE_COLUMNS_ROWS", {
29014
- sheetId: this.env.model.getters.getActiveSheetId(),
29023
+ sheetId: this.sheetId,
29015
29024
  dimension: "COL",
29016
29025
  base: this.state.base,
29017
29026
  elements,
@@ -29030,7 +29039,7 @@
29030
29039
  _fitElementSize(index) {
29031
29040
  const cols = this.env.model.getters.getActiveCols();
29032
29041
  this.env.model.dispatch("AUTORESIZE_COLUMNS", {
29033
- sheetId: this.env.model.getters.getActiveSheetId(),
29042
+ sheetId: this.sheetId,
29034
29043
  cols: cols.has(index) ? [...cols] : [index],
29035
29044
  });
29036
29045
  }
@@ -29041,7 +29050,7 @@
29041
29050
  return this.env.model.getters.getActiveCols();
29042
29051
  }
29043
29052
  _getPreviousVisibleElement(index) {
29044
- const sheetId = this.env.model.getters.getActiveSheetId();
29053
+ const sheetId = this.sheetId;
29045
29054
  let row;
29046
29055
  for (row = index - 1; row >= 0; row--) {
29047
29056
  if (!this.env.model.getters.isColHidden(sheetId, row)) {
@@ -29052,7 +29061,7 @@
29052
29061
  }
29053
29062
  unhide(hiddenElements) {
29054
29063
  this.env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
29055
- sheetId: this.env.model.getters.getActiveSheetId(),
29064
+ sheetId: this.sheetId,
29056
29065
  elements: hiddenElements,
29057
29066
  dimension: "COL",
29058
29067
  });
@@ -29068,7 +29077,7 @@
29068
29077
  left: 0;
29069
29078
  right: 0;
29070
29079
  width: ${HEADER_WIDTH}px;
29071
- height: 100%;
29080
+ height: calc(100% - ${HEADER_HEIGHT + SCROLLBAR_WIDTH}px);
29072
29081
  &.o-dragging {
29073
29082
  cursor: grabbing;
29074
29083
  }
@@ -29136,6 +29145,9 @@
29136
29145
  this.MIN_ELEMENT_SIZE = MIN_ROW_HEIGHT;
29137
29146
  }
29138
29147
  rowResizerRef;
29148
+ get sheetId() {
29149
+ return this.env.model.getters.getActiveSheetId();
29150
+ }
29139
29151
  _getEvOffset(ev) {
29140
29152
  return ev.offsetY;
29141
29153
  }
@@ -29158,10 +29170,10 @@
29158
29170
  return this.env.model.getters.getEdgeScrollRow(position, position, position);
29159
29171
  }
29160
29172
  _getDimensionsInViewport(index) {
29161
- return this.env.model.getters.getRowDimensionsInViewport(this.env.model.getters.getActiveSheetId(), index);
29173
+ return this.env.model.getters.getRowDimensionsInViewport(this.sheetId, index);
29162
29174
  }
29163
29175
  _getElementSize(index) {
29164
- return this.env.model.getters.getRowSize(this.env.model.getters.getActiveSheetId(), index);
29176
+ return this.env.model.getters.getRowSize(this.sheetId, index);
29165
29177
  }
29166
29178
  _getMaxSize() {
29167
29179
  return this.rowResizerRef.el.clientHeight;
@@ -29172,7 +29184,7 @@
29172
29184
  const rows = this.env.model.getters.getActiveRows();
29173
29185
  this.env.model.dispatch("RESIZE_COLUMNS_ROWS", {
29174
29186
  dimension: "ROW",
29175
- sheetId: this.env.model.getters.getActiveSheetId(),
29187
+ sheetId: this.sheetId,
29176
29188
  elements: rows.has(index) ? [...rows] : [index],
29177
29189
  size,
29178
29190
  });
@@ -29185,7 +29197,7 @@
29185
29197
  elements.push(rowIndex);
29186
29198
  }
29187
29199
  const result = this.env.model.dispatch("MOVE_COLUMNS_ROWS", {
29188
- sheetId: this.env.model.getters.getActiveSheetId(),
29200
+ sheetId: this.sheetId,
29189
29201
  dimension: "ROW",
29190
29202
  base: this.state.base,
29191
29203
  elements,
@@ -29204,7 +29216,7 @@
29204
29216
  _fitElementSize(index) {
29205
29217
  const rows = this.env.model.getters.getActiveRows();
29206
29218
  this.env.model.dispatch("AUTORESIZE_ROWS", {
29207
- sheetId: this.env.model.getters.getActiveSheetId(),
29219
+ sheetId: this.sheetId,
29208
29220
  rows: rows.has(index) ? [...rows] : [index],
29209
29221
  });
29210
29222
  }
@@ -29215,7 +29227,7 @@
29215
29227
  return this.env.model.getters.getActiveRows();
29216
29228
  }
29217
29229
  _getPreviousVisibleElement(index) {
29218
- const sheetId = this.env.model.getters.getActiveSheetId();
29230
+ const sheetId = this.sheetId;
29219
29231
  let row;
29220
29232
  for (row = index - 1; row >= 0; row--) {
29221
29233
  if (!this.env.model.getters.isRowHidden(sheetId, row)) {
@@ -29226,7 +29238,7 @@
29226
29238
  }
29227
29239
  unhide(hiddenElements) {
29228
29240
  this.env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
29229
- sheetId: this.env.model.getters.getActiveSheetId(),
29241
+ sheetId: this.sheetId,
29230
29242
  dimension: "ROW",
29231
29243
  elements: hiddenElements,
29232
29244
  });
@@ -30371,6 +30383,7 @@
30371
30383
  const CONTENT_TYPES = {
30372
30384
  workbook: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
30373
30385
  sheet: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
30386
+ metadata: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml",
30374
30387
  sharedStrings: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
30375
30388
  styles: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
30376
30389
  drawing: "application/vnd.openxmlformats-officedocument.drawing+xml",
@@ -30383,6 +30396,7 @@
30383
30396
  const XLSX_RELATION_TYPE = {
30384
30397
  document: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
30385
30398
  sheet: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet",
30399
+ metadata: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata",
30386
30400
  sharedStrings: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings",
30387
30401
  styles: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
30388
30402
  drawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
@@ -30392,6 +30406,7 @@
30392
30406
  hyperlink: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
30393
30407
  image: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
30394
30408
  };
30409
+ const ARRAY_FORMULA_URI = "bdbb8cdc-fa1e-496e-a857-3c3f30c029c3";
30395
30410
  const RELATIONSHIP_NSR = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
30396
30411
  const HEIGHT_FACTOR = 0.75; // 100px => 75 u
30397
30412
  /**
@@ -32221,29 +32236,33 @@
32221
32236
  * In all the sheets, replace the table-only references in the formula cells with standard references.
32222
32237
  */
32223
32238
  function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
32224
- for (let sheet of convertedSheets) {
32225
- const tables = xlsxSheets.find((s) => s.sheetName === sheet.name).tables;
32239
+ for (let tableSheet of convertedSheets) {
32240
+ const tables = xlsxSheets.find((s) => s.sheetName === tableSheet.name).tables;
32226
32241
  for (let table of tables) {
32227
32242
  const tabRef = table.name + "[";
32228
- for (let position of positions(toZone(table.ref))) {
32229
- const xc = toXC(position.col, position.row);
32230
- const cell = sheet.cells[xc];
32231
- if (cell && cell.content && cell.content.startsWith("=")) {
32232
- let refIndex;
32233
- while ((refIndex = cell.content.indexOf(tabRef)) !== -1) {
32234
- let reference = cell.content.slice(refIndex + tabRef.length);
32235
- // Expression can either be tableName[colName] or tableName[[#This Row], [colName]]
32236
- let endIndex = reference.indexOf("]");
32237
- if (reference.startsWith(`[`)) {
32238
- endIndex = reference.indexOf("]", endIndex + 1);
32239
- endIndex = reference.indexOf("]", endIndex + 1);
32243
+ for (let sheet of convertedSheets) {
32244
+ for (let xc in sheet.cells) {
32245
+ const cell = sheet.cells[xc];
32246
+ if (cell && cell.content && cell.content.startsWith("=")) {
32247
+ let refIndex;
32248
+ while ((refIndex = cell.content.indexOf(tabRef)) !== -1) {
32249
+ let endIndex = refIndex + tabRef.length;
32250
+ let openBrackets = 1;
32251
+ while (openBrackets > 0 && endIndex < cell.content.length) {
32252
+ if (cell.content[endIndex] === "[") {
32253
+ openBrackets++;
32254
+ }
32255
+ else if (cell.content[endIndex] === "]") {
32256
+ openBrackets--;
32257
+ }
32258
+ endIndex++;
32259
+ }
32260
+ let reference = cell.content.slice(refIndex + tabRef.length, endIndex - 1);
32261
+ const sheetPrefix = tableSheet.id === sheet.id ? "" : tableSheet.name + "!";
32262
+ const convertedRef = convertTableReference(sheetPrefix, reference, table, xc);
32263
+ cell.content =
32264
+ cell.content.slice(0, refIndex) + convertedRef + cell.content.slice(endIndex);
32240
32265
  }
32241
- reference = reference.slice(0, endIndex);
32242
- const convertedRef = convertTableReference(reference, table, xc);
32243
- cell.content =
32244
- cell.content.slice(0, refIndex) +
32245
- convertedRef +
32246
- cell.content.slice(tabRef.length + refIndex + endIndex + 1);
32247
32266
  }
32248
32267
  }
32249
32268
  }
@@ -32251,11 +32270,17 @@
32251
32270
  }
32252
32271
  }
32253
32272
  /**
32254
- * Convert table-specific references in formulas into standard references.
32273
+ * Convert table-specific references in formulas into standard references. A table reference is composed of columns names,
32274
+ * and of keywords determining the rows of the table to reference.
32255
32275
  *
32256
32276
  * A reference in a table can have the form (only the part between brackets should be given to this function):
32257
32277
  * - tableName[colName] : reference to the whole column "colName"
32278
+ * - tableName[#keyword] : reference to the whatever row the keyword refers to
32258
32279
  * - tableName[[#keyword], [colName]] : reference to some of the element(s) of the column colName
32280
+ * - tableName[[#keyword], [colName]:[col2Name]] : reference to some of the element(s) of the columns colName to col2Name
32281
+ * - tableName[[#keyword1], [#keyword2], [colName]] : reference to all the rows referenced by the keywords in the column colName
32282
+ * - tableName[[#keyword1], [colName], [#keyword2]]: the keywords and colName can be in any order
32283
+ *
32259
32284
  *
32260
32285
  * The available keywords are :
32261
32286
  * - #All : all the column (including totals)
@@ -32263,58 +32288,109 @@
32263
32288
  * - #Headers : only the header of the column
32264
32289
  * - #Totals : only the totals of the column
32265
32290
  * - #This Row : only the element in the same row as the cell
32291
+ *
32292
+ * Note that the only valid combination of multiple keywords are #Data + #Totals and #Headers + #Data.
32266
32293
  */
32267
- function convertTableReference(expr, table, cellXc) {
32268
- const refElements = expr.split(",");
32294
+ function convertTableReference(sheetPrefix, expr, table, cellXc) {
32295
+ // TODO: Ideally we'd want to make a real tokenizer, this simple approach won't work if for example the column name
32296
+ // contain # or , characters. But that's probably an edge case that we can ignore for now.
32297
+ const parts = expr.split(",").map((part) => part.trim());
32269
32298
  const tableZone = toZone(table.ref);
32270
- const refZone = { ...tableZone };
32271
- let isReferencedZoneValid = true;
32272
- // Single column reference
32273
- if (refElements.length === 1) {
32274
- const colRelativeIndex = table.cols.findIndex((col) => col.name === refElements[0]);
32275
- refZone.left = refZone.right = colRelativeIndex + tableZone.left;
32276
- if (table.headerRowCount) {
32277
- refZone.top += table.headerRowCount;
32278
- }
32279
- if (table.totalsRowCount) {
32280
- refZone.bottom -= 1;
32299
+ const colIndexes = [];
32300
+ const rowIndexes = [];
32301
+ const foundKeywords = [];
32302
+ for (const part of parts) {
32303
+ if (removeBrackets(part).startsWith("#")) {
32304
+ const keyWord = removeBrackets(part);
32305
+ foundKeywords.push(keyWord);
32306
+ switch (keyWord) {
32307
+ case "#All":
32308
+ rowIndexes.push(tableZone.top, tableZone.bottom);
32309
+ break;
32310
+ case "#Data":
32311
+ const top = table.headerRowCount ? tableZone.top + table.headerRowCount : tableZone.top;
32312
+ const bottom = table.totalsRowCount
32313
+ ? tableZone.bottom - table.totalsRowCount
32314
+ : tableZone.bottom;
32315
+ rowIndexes.push(top, bottom);
32316
+ break;
32317
+ case "#This Row":
32318
+ rowIndexes.push(toCartesian(cellXc).row);
32319
+ break;
32320
+ case "#Headers":
32321
+ if (!table.headerRowCount) {
32322
+ return INCORRECT_RANGE_STRING;
32323
+ }
32324
+ rowIndexes.push(tableZone.top);
32325
+ break;
32326
+ case "#Totals":
32327
+ if (!table.totalsRowCount) {
32328
+ return INCORRECT_RANGE_STRING;
32329
+ }
32330
+ rowIndexes.push(tableZone.bottom);
32331
+ break;
32332
+ }
32281
32333
  }
32282
- }
32283
- // Other references
32284
- else {
32285
- switch (refElements[0].slice(1, refElements[0].length - 1)) {
32286
- case "#All":
32287
- refZone.top = table.headerRowCount ? tableZone.top + table.headerRowCount : tableZone.top;
32288
- refZone.bottom = tableZone.bottom;
32289
- break;
32290
- case "#Data":
32291
- refZone.top = table.headerRowCount ? tableZone.top + table.headerRowCount : tableZone.top;
32292
- refZone.bottom = table.totalsRowCount ? tableZone.bottom + 1 : tableZone.bottom;
32293
- break;
32294
- case "#This Row":
32295
- refZone.top = refZone.bottom = toCartesian(cellXc).row;
32296
- break;
32297
- case "#Headers":
32298
- refZone.top = refZone.bottom = tableZone.top;
32299
- if (!table.headerRowCount) {
32300
- isReferencedZoneValid = false;
32301
- }
32302
- break;
32303
- case "#Totals":
32304
- refZone.top = refZone.bottom = tableZone.bottom;
32305
- if (!table.totalsRowCount) {
32306
- isReferencedZoneValid = false;
32334
+ else {
32335
+ const columns = part
32336
+ .split(":")
32337
+ .map((part) => part.trim())
32338
+ .map(removeBrackets);
32339
+ if (colIndexes.length) {
32340
+ return INCORRECT_RANGE_STRING;
32341
+ }
32342
+ const colRelativeIndex = table.cols.findIndex((col) => col.name === columns[0]);
32343
+ if (colRelativeIndex === -1) {
32344
+ return INCORRECT_RANGE_STRING;
32345
+ }
32346
+ colIndexes.push(colRelativeIndex + tableZone.left);
32347
+ if (columns[1]) {
32348
+ const colRelativeIndex2 = table.cols.findIndex((col) => col.name === columns[1]);
32349
+ if (colRelativeIndex2 === -1) {
32350
+ return INCORRECT_RANGE_STRING;
32307
32351
  }
32308
- break;
32352
+ colIndexes.push(colRelativeIndex2 + tableZone.left);
32353
+ }
32309
32354
  }
32310
- const colRef = refElements[1].slice(1, refElements[1].length - 1);
32311
- const colRelativeIndex = table.cols.findIndex((col) => col.name === colRef);
32312
- refZone.left = refZone.right = colRelativeIndex + tableZone.left;
32313
32355
  }
32314
- if (!isReferencedZoneValid) {
32356
+ if (!areKeywordsCompatible(foundKeywords)) {
32315
32357
  return INCORRECT_RANGE_STRING;
32316
32358
  }
32317
- return refZone.top !== refZone.bottom ? zoneToXc(refZone) : toXC(refZone.left, refZone.top);
32359
+ if (rowIndexes.length === 0) {
32360
+ const top = table.headerRowCount ? tableZone.top + table.headerRowCount : tableZone.top;
32361
+ const bottom = table.totalsRowCount
32362
+ ? tableZone.bottom - table.totalsRowCount
32363
+ : tableZone.bottom;
32364
+ rowIndexes.push(top, bottom);
32365
+ }
32366
+ if (colIndexes.length === 0) {
32367
+ colIndexes.push(tableZone.left, tableZone.right);
32368
+ }
32369
+ const refZone = {
32370
+ top: Math.min(...rowIndexes),
32371
+ left: Math.min(...colIndexes),
32372
+ bottom: Math.max(...rowIndexes),
32373
+ right: Math.max(...colIndexes),
32374
+ };
32375
+ return sheetPrefix + zoneToXc(refZone);
32376
+ }
32377
+ function removeBrackets(str) {
32378
+ return str.startsWith("[") && str.endsWith("]") ? str.slice(1, str.length - 1) : str;
32379
+ }
32380
+ function areKeywordsCompatible(keywords) {
32381
+ if (keywords.length < 2) {
32382
+ return true;
32383
+ }
32384
+ else if (keywords.length > 2) {
32385
+ return false;
32386
+ }
32387
+ else if (keywords.includes("#Data") && keywords.includes("#Totals")) {
32388
+ return true;
32389
+ }
32390
+ else if (keywords.includes("#Headers") && keywords.includes("#Data")) {
32391
+ return true;
32392
+ }
32393
+ return false;
32318
32394
  }
32319
32395
 
32320
32396
  // -------------------------------------
@@ -42376,6 +42452,9 @@
42376
42452
  // Export
42377
42453
  // ---------------------------------------------------------------------------
42378
42454
  exportForExcel(data) {
42455
+ for (const sheet of data.sheets) {
42456
+ sheet.formulaSpillRanges = {};
42457
+ }
42379
42458
  for (const position of this.evaluator.getEvaluatedPositions()) {
42380
42459
  const evaluatedCell = this.evaluator.getEvaluatedCell(position);
42381
42460
  const xc = toXC(position.col, position.row);
@@ -42387,8 +42466,9 @@
42387
42466
  const exportedSheetData = data.sheets.find((sheet) => sheet.id === position.sheetId);
42388
42467
  const formulaCell = this.getCorrespondingFormulaCell(position);
42389
42468
  if (formulaCell) {
42469
+ const cell = this.getters.getCell(position);
42390
42470
  isExported = isExportableToExcel(formulaCell.compiledFormula.tokens);
42391
- isFormula = isExported;
42471
+ isFormula = isExported && cell?.content === formulaCell.content;
42392
42472
  if (!isExported) {
42393
42473
  // If the cell contains a non-exported formula and that is evaluates to
42394
42474
  // nothing* ,we don't export it.
@@ -42412,6 +42492,14 @@
42412
42492
  content = !isExported ? newContent : exportedCellData.content;
42413
42493
  }
42414
42494
  exportedSheetData.cells[xc] = { ...exportedCellData, value, isFormula, content, format };
42495
+ const spillCells = this.getSpreadPositionsOf(position);
42496
+ spillCells.push(position);
42497
+ const spillZones = spillCells.map((cell) => positionToZone(cell));
42498
+ const spillZone = union(...spillZones);
42499
+ const spillZoneXc = this.getters.getRangeString(this.getters.getRangeFromZone(position.sheetId, spillZone), position.sheetId);
42500
+ if (spillZoneXc && spillZoneXc !== "#REF") {
42501
+ exportedSheetData.formulaSpillRanges[xc] = spillZoneXc;
42502
+ }
42415
42503
  }
42416
42504
  }
42417
42505
  /**
@@ -43612,7 +43700,7 @@
43612
43700
  getRule(cell, cells) {
43613
43701
  const rules = autofillRulesRegistry.getAll().sort((a, b) => a.sequence - b.sequence);
43614
43702
  const rule = rules.find((rule) => rule.condition(cell, cells));
43615
- return rule && rule.generateRule(cell, cells);
43703
+ return rule && this.direction && rule.generateRule(cell, cells, this.direction);
43616
43704
  }
43617
43705
  /**
43618
43706
  * Create the generator to be able to autofill the next cells.
@@ -51282,7 +51370,8 @@
51282
51370
  ? this.getters.getSheetViewVisibleCols()
51283
51371
  : this.getters.getSheetViewVisibleRows();
51284
51372
  const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header);
51285
- const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
51373
+ let endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
51374
+ endIndex = endIndex === -1 ? visibleHeaders.length : endIndex;
51286
51375
  const relevantIndexes = visibleHeaders.slice(startIndex, endIndex);
51287
51376
  let offset = 0;
51288
51377
  for (const i of relevantIndexes) {
@@ -56081,7 +56170,7 @@
56081
56170
  `;
56082
56171
  }
56083
56172
 
56084
- function addFormula(cell) {
56173
+ function addFormula(cell, formulaSpillRange) {
56085
56174
  const formula = cell.content;
56086
56175
  if (!formula) {
56087
56176
  return { attrs: [], node: escapeXml `` };
@@ -56090,10 +56179,17 @@
56090
56179
  if (type === undefined) {
56091
56180
  return { attrs: [], node: escapeXml `` };
56092
56181
  }
56093
- const attrs = [["t", type]];
56182
+ const attrs = [
56183
+ ["cm", "1"],
56184
+ ["t", type],
56185
+ ];
56094
56186
  const XlsxFormula = adaptFormulaToExcel(formula);
56095
56187
  const exportedValue = adaptFormulaValueToExcel(cell.value);
56096
- const node = escapeXml /*xml*/ `<f>${XlsxFormula}</f><v>${exportedValue}</v>`;
56188
+ // We treat all formulas as array formulas (a simple formula
56189
+ // is an array formula that spills on only one cell) to avoid
56190
+ // trying to detect spilling sub-formulas which is not a trivial task.
56191
+ let node;
56192
+ node = escapeXml /*xml*/ `<f t="array" ref="${formulaSpillRange}">${XlsxFormula}</f><v>${exportedValue}</v>`;
56097
56193
  return { attrs, node };
56098
56194
  }
56099
56195
  function addContent(content, sharedStrings, forceString = false) {
@@ -56905,7 +57001,7 @@
56905
57001
  let cellNode = escapeXml ``;
56906
57002
  // Either formula or static value inside the cell
56907
57003
  if (cell.isFormula) {
56908
- const res = addFormula(cell);
57004
+ const res = addFormula(cell, sheet.formulaSpillRanges[xc] ?? xc);
56909
57005
  if (!res) {
56910
57006
  continue;
56911
57007
  }
@@ -57162,6 +57258,30 @@
57162
57258
  `;
57163
57259
  files.push(createXMLFile(parseXML(sheetXml), `xl/worksheets/sheet${sheetIndex}.xml`, "sheet"));
57164
57260
  }
57261
+ const sheetMetadataXml = escapeXml /*xml*/ `
57262
+ <metadata xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:xda="http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray">
57263
+ <metadataTypes count="1">
57264
+ <metadataType name="XLDAPR" minSupportedVersion="120000" copy="1" pasteAll="1"
57265
+ pasteValues="1" merge="1" splitFirst="1" rowColShift="1" clearFormats="1"
57266
+ clearComments="1" assign="1" coerce="1" cellMeta="1" />
57267
+ </metadataTypes>
57268
+ <futureMetadata name="XLDAPR" count="1">
57269
+ <bk>
57270
+ <extLst>
57271
+ <ext uri="{${ARRAY_FORMULA_URI}}">
57272
+ <xda:dynamicArrayProperties fDynamic="1" fCollapsed="0" />
57273
+ </ext>
57274
+ </extLst>
57275
+ </bk>
57276
+ </futureMetadata>
57277
+ <cellMetadata count="1">
57278
+ <bk>
57279
+ <rc t="1" v="0" />
57280
+ </bk>
57281
+ </cellMetadata>
57282
+ </metadata>
57283
+ `;
57284
+ files.push(createXMLFile(parseXML(sheetMetadataXml), "xl/metadata.xml", "metadata"));
57165
57285
  addRelsToFile(construct.relsFiles, "xl/_rels/workbook.xml.rels", {
57166
57286
  type: XLSX_RELATION_TYPE.sharedStrings,
57167
57287
  target: "sharedStrings.xml",
@@ -57170,6 +57290,10 @@
57170
57290
  type: XLSX_RELATION_TYPE.styles,
57171
57291
  target: "styles.xml",
57172
57292
  });
57293
+ addRelsToFile(construct.relsFiles, "xl/_rels/workbook.xml.rels", {
57294
+ type: XLSX_RELATION_TYPE.metadata,
57295
+ target: "metadata.xml",
57296
+ });
57173
57297
  return files;
57174
57298
  }
57175
57299
  /**
@@ -57944,9 +58068,9 @@
57944
58068
  exports.tokenize = tokenize;
57945
58069
 
57946
58070
 
57947
- __info__.version = "17.0.51";
57948
- __info__.date = "2025-02-25T05:56:10.869Z";
57949
- __info__.hash = "b433912";
58071
+ __info__.version = "17.0.52";
58072
+ __info__.date = "2025-03-07T10:30:04.783Z";
58073
+ __info__.hash = "c19cd9a";
57950
58074
 
57951
58075
 
57952
58076
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);