@odoo/o-spreadsheet 17.0.51 → 17.0.53

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