@odoo/o-spreadsheet 17.0.50 → 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.50
6
- * @date 2025-02-14T08:37:34.289Z
7
- * @hash cc1037d
5
+ * @version 17.0.52
6
+ * @date 2025-03-07T10:30:04.783Z
7
+ * @hash c19cd9a
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';
@@ -4731,11 +4731,12 @@ class UuidGenerator {
4731
4731
  if (this.isFastIdStrategy) {
4732
4732
  this.fastIdStart++;
4733
4733
  return String(this.fastIdStart);
4734
- //@ts-ignore
4735
4734
  }
4736
- else if (window.crypto && window.crypto.getRandomValues) {
4737
- //@ts-ignore
4738
- return ([1e7] + -1e3).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
4735
+ else if (window.crypto) {
4736
+ return "10000000-1000".replace(/[01]/g, (c) => {
4737
+ const n = Number(c);
4738
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
4739
+ });
4739
4740
  }
4740
4741
  else {
4741
4742
  // mainly for jest and other browsers that do not have the crypto functionality
@@ -4753,11 +4754,12 @@ class UuidGenerator {
4753
4754
  if (this.isFastIdStrategy) {
4754
4755
  this.fastIdStart++;
4755
4756
  return String(this.fastIdStart);
4756
- //@ts-ignore
4757
4757
  }
4758
- else if (window.crypto && window.crypto.getRandomValues) {
4759
- //@ts-ignore
4760
- return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
4758
+ else if (window.crypto) {
4759
+ return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => {
4760
+ const n = Number(c);
4761
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
4762
+ });
4761
4763
  }
4762
4764
  else {
4763
4765
  // mainly for jest and other browsers that do not have the crypto functionality
@@ -6210,7 +6212,7 @@ autofillRulesRegistry
6210
6212
  condition: (cell) => !cell.isFormula &&
6211
6213
  evaluateLiteral(cell.content, { locale: DEFAULT_LOCALE }).type === CellValueType.text &&
6212
6214
  alphaNumericValueRegExp.test(cell.content),
6213
- generateRule: (cell, cells) => {
6215
+ generateRule: (cell, cells, direction) => {
6214
6216
  const numberPostfix = parseInt(cell.content.match(numberPostfixRegExp)[0]);
6215
6217
  const prefix = cell.content.match(stringPrefixRegExp)[0];
6216
6218
  const numberPostfixLength = cell.content.length - prefix.length;
@@ -6218,7 +6220,10 @@ autofillRulesRegistry
6218
6220
  alphaNumericValueRegExp.test(evaluatedCell.value)) // get consecutive alphanumeric cells, no matter what the prefix is
6219
6221
  .filter((cell) => prefix === cell.value.toString().match(stringPrefixRegExp)[0])
6220
6222
  .map((cell) => parseInt(cell.value.toString().match(numberPostfixRegExp)[0]));
6221
- const increment = calculateIncrementBasedOnGroup(group);
6223
+ let increment = calculateIncrementBasedOnGroup(group);
6224
+ if (["up", "left"].includes(direction) && group.length === 1) {
6225
+ increment = -increment;
6226
+ }
6222
6227
  return {
6223
6228
  type: "ALPHANUMERIC_INCREMENT_MODIFIER",
6224
6229
  prefix,
@@ -6247,9 +6252,12 @@ autofillRulesRegistry
6247
6252
  .add("increment_number", {
6248
6253
  condition: (cell) => !cell.isFormula &&
6249
6254
  evaluateLiteral(cell.content, { locale: DEFAULT_LOCALE }).type === CellValueType.number,
6250
- generateRule: (cell, cells) => {
6255
+ generateRule: (cell, cells, direction) => {
6251
6256
  const group = getGroup(cell, cells, (evaluatedCell) => evaluatedCell.type === CellValueType.number).map((cell) => Number(cell.value));
6252
- const increment = calculateIncrementBasedOnGroup(group);
6257
+ let increment = calculateIncrementBasedOnGroup(group);
6258
+ if (["up", "left"].includes(direction) && group.length === 1) {
6259
+ increment = -increment;
6260
+ }
6253
6261
  const evaluation = evaluateLiteral(cell.content, { locale: DEFAULT_LOCALE });
6254
6262
  return {
6255
6263
  type: "INCREMENT_MODIFIER",
@@ -6983,10 +6991,6 @@ class Popover extends Component {
6983
6991
  this.currentDisplayValue = newDisplay;
6984
6992
  if (!anchor)
6985
6993
  return;
6986
- el.style.top = "";
6987
- el.style.left = "";
6988
- el.style["max-height"] = "";
6989
- el.style["max-width"] = "";
6990
6994
  const propsMaxSize = { width: this.props.maxWidth, height: this.props.maxHeight };
6991
6995
  let elDims = {
6992
6996
  width: el.getBoundingClientRect().width,
@@ -9728,7 +9732,7 @@ function createPieChartRuntime(chart, getters) {
9728
9732
  const dataset = {
9729
9733
  label,
9730
9734
  data,
9731
- borderColor: "#FFFFFF",
9735
+ borderColor: chart.background || "#FFFFFF",
9732
9736
  backgroundColor,
9733
9737
  };
9734
9738
  config.data.datasets.push(dataset);
@@ -26760,25 +26764,21 @@ css /* scss */ `
26760
26764
  }
26761
26765
  }
26762
26766
  }
26767
+ }
26768
+ .o-spreadsheet .o-composer-assistant {
26769
+ pointer-events: none;
26763
26770
 
26764
- .o-composer-assistant {
26765
- position: absolute;
26766
- margin: 1px 4px;
26767
- pointer-events: none;
26768
- overflow: auto;
26769
-
26770
- .o-semi-bold {
26771
- /** FIXME: to remove in favor of Bootstrap
26772
- * 'fw-semibold' when we upgrade to Bootstrap 5.2
26773
- */
26774
- font-weight: 600 !important;
26775
- }
26771
+ .o-semi-bold {
26772
+ /** FIXME: to remove in favor of Bootstrap
26773
+ * 'fw-semibold' when we upgrade to Bootstrap 5.2
26774
+ */
26775
+ font-weight: 600 !important;
26776
26776
  }
26777
26777
  }
26778
26778
  `;
26779
26779
  class Composer extends Component {
26780
26780
  static template = "o-spreadsheet-Composer";
26781
- static components = { TextValueProvider, FunctionDescriptionProvider };
26781
+ static components = { TextValueProvider, FunctionDescriptionProvider, Popover };
26782
26782
  static defaultProps = {
26783
26783
  inputStyle: "",
26784
26784
  isDefaultFocus: false,
@@ -26803,35 +26803,13 @@ class Composer extends Component {
26803
26803
  argToFocus: 0,
26804
26804
  });
26805
26805
  compositionActive = false;
26806
- spreadsheetRect = useSpreadsheetRect();
26807
26806
  get assistantStyle() {
26808
- const composerRect = this.composerRef.el.getBoundingClientRect();
26809
26807
  const assistantStyle = {};
26810
- assistantStyle["min-width"] = `${this.props.rect?.width || ASSISTANT_WIDTH}px`;
26808
+ const minWidth = Math.min(getBoundingRectAsPOJO(this.composerRef.el).width || Infinity, ASSISTANT_WIDTH);
26809
+ assistantStyle["min-width"] = `${minWidth}px`;
26811
26810
  if (this.autoCompleteState.type === "function") {
26812
26811
  assistantStyle.width = `${ASSISTANT_WIDTH}px`;
26813
26812
  }
26814
- if (this.props.delimitation && this.props.rect) {
26815
- const { x: cellX, y: cellY, height: cellHeight } = this.props.rect;
26816
- const remainingHeight = this.props.delimitation.height - (cellY + cellHeight);
26817
- assistantStyle["max-height"] = `${remainingHeight}px`;
26818
- if (cellY > remainingHeight) {
26819
- const availableSpaceAbove = cellY;
26820
- assistantStyle["max-height"] = `${availableSpaceAbove}px`;
26821
- // render top
26822
- // We compensate 2 px of margin on the assistant style + 1px for design reasons
26823
- assistantStyle.top = `-3px`;
26824
- assistantStyle.transform = `translate(0, -100%)`;
26825
- }
26826
- if (cellX + ASSISTANT_WIDTH > this.props.delimitation.width) {
26827
- // render left
26828
- assistantStyle.right = `0px`;
26829
- }
26830
- }
26831
- if (this.props.focus === "contentFocus" &&
26832
- composerRect.left + ASSISTANT_WIDTH > this.spreadsheetRect.width) {
26833
- assistantStyle.right = "0px";
26834
- }
26835
26813
  return cssPropertiesToCss(assistantStyle);
26836
26814
  }
26837
26815
  // we can't allow input events to be triggered while we remove and add back the content of the composer in processContent
@@ -27080,7 +27058,7 @@ class Composer extends Component {
27080
27058
  this.autoCompleteState.selectedIndex = 0;
27081
27059
  }
27082
27060
  updateAutoCompleteIndex(index) {
27083
- this.autoCompleteState.selectedIndex = clip(0, index, 10);
27061
+ this.autoCompleteState.selectedIndex = index;
27084
27062
  }
27085
27063
  /**
27086
27064
  * This is required to ensure the content helper selection is
@@ -27368,11 +27346,16 @@ class Composer extends Component {
27368
27346
  this.autoCompleteState.values = values.map((value) => ({ text: value, description: "" }));
27369
27347
  this.autoCompleteState.getHtmlContent = (value) => [{ value }];
27370
27348
  }
27349
+ get popoverProps() {
27350
+ return {
27351
+ anchorRect: getBoundingRectAsPOJO(this.composerRef.el),
27352
+ positioning: "BottomLeft",
27353
+ verticalOffset: 0,
27354
+ };
27355
+ }
27371
27356
  }
27372
27357
  Composer.props = {
27373
27358
  inputStyle: { type: String, optional: true },
27374
- rect: { type: Object, optional: true },
27375
- delimitation: { type: Object, optional: true },
27376
27359
  focus: { validate: (value) => ["inactive", "cellFocus", "contentFocus"].includes(value) },
27377
27360
  onComposerCellFocused: { type: Function, optional: true },
27378
27361
  onComposerContentFocused: Function,
@@ -27442,13 +27425,7 @@ class GridComposer extends Component {
27442
27425
  });
27443
27426
  }
27444
27427
  get composerProps() {
27445
- const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
27446
27428
  return {
27447
- rect: { ...this.rect },
27448
- delimitation: {
27449
- width,
27450
- height,
27451
- },
27452
27429
  focus: this.props.focus,
27453
27430
  isDefaultFocus: true,
27454
27431
  onComposerContentFocused: this.props.onComposerContentFocused,
@@ -28990,6 +28967,9 @@ class ColResizer extends AbstractResizer {
28990
28967
  this.MAX_SIZE_MARGIN = 90;
28991
28968
  this.MIN_ELEMENT_SIZE = MIN_COL_WIDTH;
28992
28969
  }
28970
+ get sheetId() {
28971
+ return this.env.model.getters.getActiveSheetId();
28972
+ }
28993
28973
  _getEvOffset(ev) {
28994
28974
  return ev.offsetX;
28995
28975
  }
@@ -29012,10 +28992,10 @@ class ColResizer extends AbstractResizer {
29012
28992
  return this.env.model.getters.getEdgeScrollCol(position, position, position);
29013
28993
  }
29014
28994
  _getDimensionsInViewport(index) {
29015
- return this.env.model.getters.getColDimensionsInViewport(this.env.model.getters.getActiveSheetId(), index);
28995
+ return this.env.model.getters.getColDimensionsInViewport(this.sheetId, index);
29016
28996
  }
29017
28997
  _getElementSize(index) {
29018
- return this.env.model.getters.getColSize(this.env.model.getters.getActiveSheetId(), index);
28998
+ return this.env.model.getters.getColSize(this.sheetId, index);
29019
28999
  }
29020
29000
  _getMaxSize() {
29021
29001
  return this.colResizerRef.el.clientWidth;
@@ -29026,7 +29006,7 @@ class ColResizer extends AbstractResizer {
29026
29006
  const cols = this.env.model.getters.getActiveCols();
29027
29007
  this.env.model.dispatch("RESIZE_COLUMNS_ROWS", {
29028
29008
  dimension: "COL",
29029
- sheetId: this.env.model.getters.getActiveSheetId(),
29009
+ sheetId: this.sheetId,
29030
29010
  elements: cols.has(index) ? [...cols] : [index],
29031
29011
  size,
29032
29012
  });
@@ -29039,7 +29019,7 @@ class ColResizer extends AbstractResizer {
29039
29019
  elements.push(colIndex);
29040
29020
  }
29041
29021
  const result = this.env.model.dispatch("MOVE_COLUMNS_ROWS", {
29042
- sheetId: this.env.model.getters.getActiveSheetId(),
29022
+ sheetId: this.sheetId,
29043
29023
  dimension: "COL",
29044
29024
  base: this.state.base,
29045
29025
  elements,
@@ -29058,7 +29038,7 @@ class ColResizer extends AbstractResizer {
29058
29038
  _fitElementSize(index) {
29059
29039
  const cols = this.env.model.getters.getActiveCols();
29060
29040
  this.env.model.dispatch("AUTORESIZE_COLUMNS", {
29061
- sheetId: this.env.model.getters.getActiveSheetId(),
29041
+ sheetId: this.sheetId,
29062
29042
  cols: cols.has(index) ? [...cols] : [index],
29063
29043
  });
29064
29044
  }
@@ -29069,7 +29049,7 @@ class ColResizer extends AbstractResizer {
29069
29049
  return this.env.model.getters.getActiveCols();
29070
29050
  }
29071
29051
  _getPreviousVisibleElement(index) {
29072
- const sheetId = this.env.model.getters.getActiveSheetId();
29052
+ const sheetId = this.sheetId;
29073
29053
  let row;
29074
29054
  for (row = index - 1; row >= 0; row--) {
29075
29055
  if (!this.env.model.getters.isColHidden(sheetId, row)) {
@@ -29080,7 +29060,7 @@ class ColResizer extends AbstractResizer {
29080
29060
  }
29081
29061
  unhide(hiddenElements) {
29082
29062
  this.env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
29083
- sheetId: this.env.model.getters.getActiveSheetId(),
29063
+ sheetId: this.sheetId,
29084
29064
  elements: hiddenElements,
29085
29065
  dimension: "COL",
29086
29066
  });
@@ -29096,7 +29076,7 @@ css /* scss */ `
29096
29076
  left: 0;
29097
29077
  right: 0;
29098
29078
  width: ${HEADER_WIDTH}px;
29099
- height: 100%;
29079
+ height: calc(100% - ${HEADER_HEIGHT + SCROLLBAR_WIDTH}px);
29100
29080
  &.o-dragging {
29101
29081
  cursor: grabbing;
29102
29082
  }
@@ -29164,6 +29144,9 @@ class RowResizer extends AbstractResizer {
29164
29144
  this.MIN_ELEMENT_SIZE = MIN_ROW_HEIGHT;
29165
29145
  }
29166
29146
  rowResizerRef;
29147
+ get sheetId() {
29148
+ return this.env.model.getters.getActiveSheetId();
29149
+ }
29167
29150
  _getEvOffset(ev) {
29168
29151
  return ev.offsetY;
29169
29152
  }
@@ -29186,10 +29169,10 @@ class RowResizer extends AbstractResizer {
29186
29169
  return this.env.model.getters.getEdgeScrollRow(position, position, position);
29187
29170
  }
29188
29171
  _getDimensionsInViewport(index) {
29189
- return this.env.model.getters.getRowDimensionsInViewport(this.env.model.getters.getActiveSheetId(), index);
29172
+ return this.env.model.getters.getRowDimensionsInViewport(this.sheetId, index);
29190
29173
  }
29191
29174
  _getElementSize(index) {
29192
- return this.env.model.getters.getRowSize(this.env.model.getters.getActiveSheetId(), index);
29175
+ return this.env.model.getters.getRowSize(this.sheetId, index);
29193
29176
  }
29194
29177
  _getMaxSize() {
29195
29178
  return this.rowResizerRef.el.clientHeight;
@@ -29200,7 +29183,7 @@ class RowResizer extends AbstractResizer {
29200
29183
  const rows = this.env.model.getters.getActiveRows();
29201
29184
  this.env.model.dispatch("RESIZE_COLUMNS_ROWS", {
29202
29185
  dimension: "ROW",
29203
- sheetId: this.env.model.getters.getActiveSheetId(),
29186
+ sheetId: this.sheetId,
29204
29187
  elements: rows.has(index) ? [...rows] : [index],
29205
29188
  size,
29206
29189
  });
@@ -29213,7 +29196,7 @@ class RowResizer extends AbstractResizer {
29213
29196
  elements.push(rowIndex);
29214
29197
  }
29215
29198
  const result = this.env.model.dispatch("MOVE_COLUMNS_ROWS", {
29216
- sheetId: this.env.model.getters.getActiveSheetId(),
29199
+ sheetId: this.sheetId,
29217
29200
  dimension: "ROW",
29218
29201
  base: this.state.base,
29219
29202
  elements,
@@ -29232,7 +29215,7 @@ class RowResizer extends AbstractResizer {
29232
29215
  _fitElementSize(index) {
29233
29216
  const rows = this.env.model.getters.getActiveRows();
29234
29217
  this.env.model.dispatch("AUTORESIZE_ROWS", {
29235
- sheetId: this.env.model.getters.getActiveSheetId(),
29218
+ sheetId: this.sheetId,
29236
29219
  rows: rows.has(index) ? [...rows] : [index],
29237
29220
  });
29238
29221
  }
@@ -29243,7 +29226,7 @@ class RowResizer extends AbstractResizer {
29243
29226
  return this.env.model.getters.getActiveRows();
29244
29227
  }
29245
29228
  _getPreviousVisibleElement(index) {
29246
- const sheetId = this.env.model.getters.getActiveSheetId();
29229
+ const sheetId = this.sheetId;
29247
29230
  let row;
29248
29231
  for (row = index - 1; row >= 0; row--) {
29249
29232
  if (!this.env.model.getters.isRowHidden(sheetId, row)) {
@@ -29254,7 +29237,7 @@ class RowResizer extends AbstractResizer {
29254
29237
  }
29255
29238
  unhide(hiddenElements) {
29256
29239
  this.env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
29257
- sheetId: this.env.model.getters.getActiveSheetId(),
29240
+ sheetId: this.sheetId,
29258
29241
  dimension: "ROW",
29259
29242
  elements: hiddenElements,
29260
29243
  });
@@ -30399,6 +30382,7 @@ const DRAWING_NS_C = "http://schemas.openxmlformats.org/drawingml/2006/chart";
30399
30382
  const CONTENT_TYPES = {
30400
30383
  workbook: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
30401
30384
  sheet: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
30385
+ metadata: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml",
30402
30386
  sharedStrings: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
30403
30387
  styles: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
30404
30388
  drawing: "application/vnd.openxmlformats-officedocument.drawing+xml",
@@ -30411,6 +30395,7 @@ const CONTENT_TYPES = {
30411
30395
  const XLSX_RELATION_TYPE = {
30412
30396
  document: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
30413
30397
  sheet: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet",
30398
+ metadata: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata",
30414
30399
  sharedStrings: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings",
30415
30400
  styles: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
30416
30401
  drawing: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
@@ -30420,6 +30405,7 @@ const XLSX_RELATION_TYPE = {
30420
30405
  hyperlink: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
30421
30406
  image: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
30422
30407
  };
30408
+ const ARRAY_FORMULA_URI = "bdbb8cdc-fa1e-496e-a857-3c3f30c029c3";
30423
30409
  const RELATIONSHIP_NSR = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
30424
30410
  const HEIGHT_FACTOR = 0.75; // 100px => 75 u
30425
30411
  /**
@@ -32249,29 +32235,33 @@ function applyStyleToZone(appliedStyle, zone, cells, styles) {
32249
32235
  * In all the sheets, replace the table-only references in the formula cells with standard references.
32250
32236
  */
32251
32237
  function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
32252
- for (let sheet of convertedSheets) {
32253
- 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;
32254
32240
  for (let table of tables) {
32255
32241
  const tabRef = table.name + "[";
32256
- for (let position of positions(toZone(table.ref))) {
32257
- const xc = toXC(position.col, position.row);
32258
- const cell = sheet.cells[xc];
32259
- if (cell && cell.content && cell.content.startsWith("=")) {
32260
- let refIndex;
32261
- while ((refIndex = cell.content.indexOf(tabRef)) !== -1) {
32262
- let reference = cell.content.slice(refIndex + tabRef.length);
32263
- // Expression can either be tableName[colName] or tableName[[#This Row], [colName]]
32264
- let endIndex = reference.indexOf("]");
32265
- if (reference.startsWith(`[`)) {
32266
- endIndex = reference.indexOf("]", endIndex + 1);
32267
- 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);
32268
32264
  }
32269
- reference = reference.slice(0, endIndex);
32270
- const convertedRef = convertTableReference(reference, table, xc);
32271
- cell.content =
32272
- cell.content.slice(0, refIndex) +
32273
- convertedRef +
32274
- cell.content.slice(tabRef.length + refIndex + endIndex + 1);
32275
32265
  }
32276
32266
  }
32277
32267
  }
@@ -32279,11 +32269,17 @@ function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
32279
32269
  }
32280
32270
  }
32281
32271
  /**
32282
- * 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.
32283
32274
  *
32284
32275
  * A reference in a table can have the form (only the part between brackets should be given to this function):
32285
32276
  * - tableName[colName] : reference to the whole column "colName"
32277
+ * - tableName[#keyword] : reference to the whatever row the keyword refers to
32286
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
+ *
32287
32283
  *
32288
32284
  * The available keywords are :
32289
32285
  * - #All : all the column (including totals)
@@ -32291,58 +32287,109 @@ function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
32291
32287
  * - #Headers : only the header of the column
32292
32288
  * - #Totals : only the totals of the column
32293
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.
32294
32292
  */
32295
- function convertTableReference(expr, table, cellXc) {
32296
- 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());
32297
32297
  const tableZone = toZone(table.ref);
32298
- const refZone = { ...tableZone };
32299
- let isReferencedZoneValid = true;
32300
- // Single column reference
32301
- if (refElements.length === 1) {
32302
- const colRelativeIndex = table.cols.findIndex((col) => col.name === refElements[0]);
32303
- refZone.left = refZone.right = colRelativeIndex + tableZone.left;
32304
- if (table.headerRowCount) {
32305
- refZone.top += table.headerRowCount;
32306
- }
32307
- if (table.totalsRowCount) {
32308
- 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
+ }
32309
32332
  }
32310
- }
32311
- // Other references
32312
- else {
32313
- switch (refElements[0].slice(1, refElements[0].length - 1)) {
32314
- case "#All":
32315
- refZone.top = table.headerRowCount ? tableZone.top + table.headerRowCount : tableZone.top;
32316
- refZone.bottom = tableZone.bottom;
32317
- break;
32318
- case "#Data":
32319
- refZone.top = table.headerRowCount ? tableZone.top + table.headerRowCount : tableZone.top;
32320
- refZone.bottom = table.totalsRowCount ? tableZone.bottom + 1 : tableZone.bottom;
32321
- break;
32322
- case "#This Row":
32323
- refZone.top = refZone.bottom = toCartesian(cellXc).row;
32324
- break;
32325
- case "#Headers":
32326
- refZone.top = refZone.bottom = tableZone.top;
32327
- if (!table.headerRowCount) {
32328
- isReferencedZoneValid = false;
32329
- }
32330
- break;
32331
- case "#Totals":
32332
- refZone.top = refZone.bottom = tableZone.bottom;
32333
- if (!table.totalsRowCount) {
32334
- 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;
32335
32350
  }
32336
- break;
32351
+ colIndexes.push(colRelativeIndex2 + tableZone.left);
32352
+ }
32337
32353
  }
32338
- const colRef = refElements[1].slice(1, refElements[1].length - 1);
32339
- const colRelativeIndex = table.cols.findIndex((col) => col.name === colRef);
32340
- refZone.left = refZone.right = colRelativeIndex + tableZone.left;
32341
32354
  }
32342
- if (!isReferencedZoneValid) {
32355
+ if (!areKeywordsCompatible(foundKeywords)) {
32343
32356
  return INCORRECT_RANGE_STRING;
32344
32357
  }
32345
- 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;
32346
32393
  }
32347
32394
 
32348
32395
  // -------------------------------------
@@ -33895,7 +33942,7 @@ function normalizeV9(formula) {
33895
33942
  * a breaking change is made in the way the state is handled, and an upgrade
33896
33943
  * function should be defined
33897
33944
  */
33898
- const CURRENT_VERSION = 14;
33945
+ const CURRENT_VERSION = 14.5;
33899
33946
  const INITIAL_SHEET_ID = "Sheet1";
33900
33947
  /**
33901
33948
  * This function tries to load anything that could look like a valid
@@ -42404,6 +42451,9 @@ class EvaluationPlugin extends UIPlugin {
42404
42451
  // Export
42405
42452
  // ---------------------------------------------------------------------------
42406
42453
  exportForExcel(data) {
42454
+ for (const sheet of data.sheets) {
42455
+ sheet.formulaSpillRanges = {};
42456
+ }
42407
42457
  for (const position of this.evaluator.getEvaluatedPositions()) {
42408
42458
  const evaluatedCell = this.evaluator.getEvaluatedCell(position);
42409
42459
  const xc = toXC(position.col, position.row);
@@ -42415,8 +42465,9 @@ class EvaluationPlugin extends UIPlugin {
42415
42465
  const exportedSheetData = data.sheets.find((sheet) => sheet.id === position.sheetId);
42416
42466
  const formulaCell = this.getCorrespondingFormulaCell(position);
42417
42467
  if (formulaCell) {
42468
+ const cell = this.getters.getCell(position);
42418
42469
  isExported = isExportableToExcel(formulaCell.compiledFormula.tokens);
42419
- isFormula = isExported;
42470
+ isFormula = isExported && cell?.content === formulaCell.content;
42420
42471
  if (!isExported) {
42421
42472
  // If the cell contains a non-exported formula and that is evaluates to
42422
42473
  // nothing* ,we don't export it.
@@ -42440,6 +42491,14 @@ class EvaluationPlugin extends UIPlugin {
42440
42491
  content = !isExported ? newContent : exportedCellData.content;
42441
42492
  }
42442
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
+ }
42443
42502
  }
42444
42503
  }
42445
42504
  /**
@@ -43640,7 +43699,7 @@ class AutofillPlugin extends UIPlugin {
43640
43699
  getRule(cell, cells) {
43641
43700
  const rules = autofillRulesRegistry.getAll().sort((a, b) => a.sequence - b.sequence);
43642
43701
  const rule = rules.find((rule) => rule.condition(cell, cells));
43643
- return rule && rule.generateRule(cell, cells);
43702
+ return rule && this.direction && rule.generateRule(cell, cells, this.direction);
43644
43703
  }
43645
43704
  /**
43646
43705
  * Create the generator to be able to autofill the next cells.
@@ -51310,7 +51369,8 @@ class SheetViewPlugin extends UIPlugin {
51310
51369
  ? this.getters.getSheetViewVisibleCols()
51311
51370
  : this.getters.getSheetViewVisibleRows();
51312
51371
  const startIndex = visibleHeaders.findIndex((header) => referenceHeaderIndex >= header);
51313
- const endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
51372
+ let endIndex = visibleHeaders.findIndex((header) => targetHeaderIndex <= header);
51373
+ endIndex = endIndex === -1 ? visibleHeaders.length : endIndex;
51314
51374
  const relevantIndexes = visibleHeaders.slice(startIndex, endIndex);
51315
51375
  let offset = 0;
51316
51376
  for (const i of relevantIndexes) {
@@ -56109,7 +56169,7 @@ function numberRef(reference) {
56109
56169
  `;
56110
56170
  }
56111
56171
 
56112
- function addFormula(cell) {
56172
+ function addFormula(cell, formulaSpillRange) {
56113
56173
  const formula = cell.content;
56114
56174
  if (!formula) {
56115
56175
  return { attrs: [], node: escapeXml `` };
@@ -56118,10 +56178,17 @@ function addFormula(cell) {
56118
56178
  if (type === undefined) {
56119
56179
  return { attrs: [], node: escapeXml `` };
56120
56180
  }
56121
- const attrs = [["t", type]];
56181
+ const attrs = [
56182
+ ["cm", "1"],
56183
+ ["t", type],
56184
+ ];
56122
56185
  const XlsxFormula = adaptFormulaToExcel(formula);
56123
56186
  const exportedValue = adaptFormulaValueToExcel(cell.value);
56124
- 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>`;
56125
56192
  return { attrs, node };
56126
56193
  }
56127
56194
  function addContent(content, sharedStrings, forceString = false) {
@@ -56933,7 +57000,7 @@ function addRows(construct, data, sheet) {
56933
57000
  let cellNode = escapeXml ``;
56934
57001
  // Either formula or static value inside the cell
56935
57002
  if (cell.isFormula) {
56936
- const res = addFormula(cell);
57003
+ const res = addFormula(cell, sheet.formulaSpillRanges[xc] ?? xc);
56937
57004
  if (!res) {
56938
57005
  continue;
56939
57006
  }
@@ -57190,6 +57257,30 @@ function createWorksheets(data, construct) {
57190
57257
  `;
57191
57258
  files.push(createXMLFile(parseXML(sheetXml), `xl/worksheets/sheet${sheetIndex}.xml`, "sheet"));
57192
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"));
57193
57284
  addRelsToFile(construct.relsFiles, "xl/_rels/workbook.xml.rels", {
57194
57285
  type: XLSX_RELATION_TYPE.sharedStrings,
57195
57286
  target: "sharedStrings.xml",
@@ -57198,6 +57289,10 @@ function createWorksheets(data, construct) {
57198
57289
  type: XLSX_RELATION_TYPE.styles,
57199
57290
  target: "styles.xml",
57200
57291
  });
57292
+ addRelsToFile(construct.relsFiles, "xl/_rels/workbook.xml.rels", {
57293
+ type: XLSX_RELATION_TYPE.metadata,
57294
+ target: "metadata.xml",
57295
+ });
57201
57296
  return files;
57202
57297
  }
57203
57298
  /**
@@ -57937,6 +58032,6 @@ const constants = {
57937
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 };
57938
58033
 
57939
58034
 
57940
- __info__.version = "17.0.50";
57941
- __info__.date = "2025-02-14T08:37:34.289Z";
57942
- __info__.hash = "cc1037d";
58035
+ __info__.version = "17.0.52";
58036
+ __info__.date = "2025-03-07T10:30:04.783Z";
58037
+ __info__.hash = "c19cd9a";