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