@odoo/o-spreadsheet 18.2.27 → 18.2.29

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 18.2.27
6
- * @date 2025-08-26T10:07:14.516Z
7
- * @hash fbf9445
5
+ * @version 18.2.29
6
+ * @date 2025-09-11T08:44:31.801Z
7
+ * @hash 665bc43
8
8
  */
9
9
 
10
10
  'use strict';
@@ -3478,6 +3478,7 @@ const invalidateBordersCommands = new Set([
3478
3478
  "AUTOFILL_CELL",
3479
3479
  "SET_BORDER",
3480
3480
  "SET_ZONE_BORDERS",
3481
+ "SET_BORDERS_ON_TARGET",
3481
3482
  ]);
3482
3483
  const readonlyAllowedCommands = new Set([
3483
3484
  "START",
@@ -6904,7 +6905,7 @@ class ClipboardHandler {
6904
6905
  this.getters = getters;
6905
6906
  this.dispatch = dispatch;
6906
6907
  }
6907
- copy(data) {
6908
+ copy(data, mode = "copyPaste") {
6908
6909
  return;
6909
6910
  }
6910
6911
  paste(target, clippedContent, options) { }
@@ -6923,7 +6924,7 @@ class ClipboardHandler {
6923
6924
  }
6924
6925
 
6925
6926
  class AbstractCellClipboardHandler extends ClipboardHandler {
6926
- copy(data) {
6927
+ copy(data, mode = "copyPaste") {
6927
6928
  return;
6928
6929
  }
6929
6930
  pasteFromCopy(sheetId, target, content, options) {
@@ -7528,8 +7529,11 @@ function invertMatrix(M) {
7528
7529
  // (a) Swap 2 rows. This multiply the determinant by -1.
7529
7530
  // (b) Multiply a row by a scalar. This multiply the determinant by that scalar.
7530
7531
  // (c) Add to a row a multiple of another row. This does not change the determinant.
7532
+ if (M.length < 1 || M[0].length < 1) {
7533
+ throw new Error("invertMatrix: an empty matrix cannot be inverted.");
7534
+ }
7531
7535
  if (M.length !== M[0].length) {
7532
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] invert matrix error, only square matrices are invertible"));
7536
+ throw new Error("invertMatrix: only square matrices are invertible");
7533
7537
  }
7534
7538
  let determinant = 1;
7535
7539
  const dim = M.length;
@@ -7598,8 +7602,11 @@ function swapMatrixRows(matrix, row1, row2) {
7598
7602
  * Note: we use indexing [col][row] instead of the standard mathematical notation [row][col]
7599
7603
  */
7600
7604
  function multiplyMatrices(matrix1, matrix2) {
7605
+ if (matrix1.length < 1 || matrix2.length < 1) {
7606
+ throw new Error("multiplyMatrices: empty matrices cannot be multiplied.");
7607
+ }
7601
7608
  if (matrix1.length !== matrix2[0].length) {
7602
- throw new EvaluationError(_t("Cannot multiply matrices : incompatible matrices size."));
7609
+ throw new Error("multiplyMatrices: incompatible matrices size.");
7603
7610
  }
7604
7611
  const rowsM1 = matrix1[0].length;
7605
7612
  const colsM2 = matrix2.length;
@@ -7625,7 +7632,7 @@ function toScalar(arg) {
7625
7632
  return arg;
7626
7633
  }
7627
7634
  if (!isSingleElementMatrix(arg)) {
7628
- throw new EvaluationError(_t("The value should be a scalar or a 1x1 matrix"));
7635
+ throw new Error("The value should be a scalar or a 1x1 matrix");
7629
7636
  }
7630
7637
  return arg[0][0];
7631
7638
  }
@@ -7862,6 +7869,16 @@ function getMovingAverageValues(dataset, labels, windowSize = DEFAULT_WINDOW_SIZ
7862
7869
  }
7863
7870
  return values;
7864
7871
  }
7872
+ function assertNonEmptyMatrix(matrix, argName) {
7873
+ assert(() => matrix.length > 0 && matrix[0].length > 0, _t("[[FUNCTION_NAME]] expects the provided values of %(argName)s to be a non-empty matrix.", {
7874
+ argName,
7875
+ }));
7876
+ }
7877
+ function assertNonEmpty(...data) {
7878
+ if (data.length === 0 || data.some((arg) => arg.length === 0)) {
7879
+ throw new NotAvailableError(_t("[[FUNCTION_NAME]] has no valid input data."));
7880
+ }
7881
+ }
7865
7882
 
7866
7883
  const PREVIOUS_VALUE = "(previous)";
7867
7884
  const NEXT_VALUE = "(next)";
@@ -8624,7 +8641,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
8624
8641
  }
8625
8642
  return "Success" /* CommandResult.Success */;
8626
8643
  }
8627
- copy(data) {
8644
+ copy(data, mode = "copyPaste") {
8628
8645
  const sheetId = data.sheetId;
8629
8646
  const { clippedZones, rowsIndexes, columnsIndexes } = data;
8630
8647
  const clippedCells = [];
@@ -8637,7 +8654,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
8637
8654
  const evaluatedCell = this.getters.getEvaluatedCell(position);
8638
8655
  const pivotId = this.getters.getPivotIdFromPosition(position);
8639
8656
  const spreader = this.getters.getArrayFormulaSpreadingOn(position);
8640
- if (pivotId && spreader) {
8657
+ if (mode !== "shiftCells" && pivotId && spreader) {
8641
8658
  const pivotZone = this.getters.getSpreadZone(spreader);
8642
8659
  if ((!deepEquals(spreader, position) || !isCopyingOneCell) &&
8643
8660
  pivotZone &&
@@ -8655,7 +8672,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
8655
8672
  };
8656
8673
  }
8657
8674
  }
8658
- else {
8675
+ else if (mode !== "shiftCells") {
8659
8676
  if (spreader && !deepEquals(spreader, position)) {
8660
8677
  const isSpreaderCopied = rowsIndexes.includes(spreader.row) && columnsIndexes.includes(spreader.col);
8661
8678
  const content = isSpreaderCopied
@@ -9348,7 +9365,7 @@ class SheetClipboardHandler extends AbstractCellClipboardHandler {
9348
9365
  }
9349
9366
 
9350
9367
  class TableClipboardHandler extends AbstractCellClipboardHandler {
9351
- copy(data) {
9368
+ copy(data, mode = "copyPaste") {
9352
9369
  const sheetId = data.sheetId;
9353
9370
  const { rowsIndexes, columnsIndexes, zones } = data;
9354
9371
  const copiedTablesIds = new Set();
@@ -9378,11 +9395,13 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
9378
9395
  type: coreTable.type,
9379
9396
  };
9380
9397
  }
9381
- tableCellsInRow.push({
9382
- table: copiedTable,
9383
- style: this.getTableStyleToCopy(position),
9384
- isWholeTableCopied: copiedTablesIds.has(table.id),
9385
- });
9398
+ if (mode !== "shiftCells") {
9399
+ tableCellsInRow.push({
9400
+ table: copiedTable,
9401
+ style: this.getTableStyleToCopy(position),
9402
+ isWholeTableCopied: copiedTablesIds.has(table.id),
9403
+ });
9404
+ }
9386
9405
  }
9387
9406
  }
9388
9407
  return {
@@ -11799,6 +11818,7 @@ const MMULT = {
11799
11818
  compute: function (matrix1, matrix2) {
11800
11819
  const _matrix1 = toNumberMatrix(matrix1, "matrix1");
11801
11820
  const _matrix2 = toNumberMatrix(matrix2, "matrix2");
11821
+ assert(() => _matrix1.length > 0 && _matrix2.length > 0, _t("The first and second arguments of [[FUNCTION_NAME]] must be non-empty matrices."));
11802
11822
  assert(() => _matrix1.length === _matrix2[0].length, _t("In [[FUNCTION_NAME]], the number of columns of the first matrix (%s) must be equal to the \
11803
11823
  number of rows of the second matrix (%s).", _matrix1.length.toString(), _matrix2[0].length.toString()));
11804
11824
  return multiplyMatrices(_matrix1, _matrix2);
@@ -13594,6 +13614,7 @@ const FORECAST = {
13594
13614
  ],
13595
13615
  compute: function (x, dataY, dataX) {
13596
13616
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13617
+ assertNonEmpty(flatDataX, flatDataY);
13597
13618
  return predictLinearValues([flatDataY], [flatDataX], matrixMap(toMatrix(x), (value) => toNumber(value, this.locale)), true);
13598
13619
  },
13599
13620
  isExported: true,
@@ -13610,6 +13631,7 @@ const GROWTH = {
13610
13631
  arg("b (boolean, default=TRUE)", _t("Given a general exponential form of y = b*m^x for a curve fit, calculates b if TRUE or forces b to be 1 and only calculates the m values if FALSE.")),
13611
13632
  ],
13612
13633
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
13634
+ assertNonEmptyMatrix(knownDataY, "known_data_y");
13613
13635
  return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "the first argument (known_data_y)")), toNumberMatrix(knownDataX, "the second argument (known_data_x)"), toNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b)));
13614
13636
  },
13615
13637
  };
@@ -13624,6 +13646,7 @@ const INTERCEPT = {
13624
13646
  ],
13625
13647
  compute: function (dataY, dataX) {
13626
13648
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13649
+ assertNonEmpty(flatDataX, flatDataY);
13627
13650
  const [[], [intercept]] = fullLinearRegression([flatDataX], [flatDataY]);
13628
13651
  return intercept;
13629
13652
  },
@@ -13673,6 +13696,7 @@ const LINEST = {
13673
13696
  arg("verbose (boolean, default=FALSE)", _t("A flag specifying whether to return additional regression statistics or only the linear coefficients and the y-intercept")),
13674
13697
  ],
13675
13698
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13699
+ assertNonEmptyMatrix(dataY, "data_y");
13676
13700
  return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13677
13701
  },
13678
13702
  isExported: true,
@@ -13689,6 +13713,7 @@ const LOGEST = {
13689
13713
  arg("verbose (boolean, default=FALSE)", _t("A flag specifying whether to return additional regression statistics or only the linear coefficients and the y-intercept")),
13690
13714
  ],
13691
13715
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13716
+ assertNonEmptyMatrix(dataY, "data_y");
13692
13717
  const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
13693
13718
  for (let i = 0; i < coeffs.length; i++) {
13694
13719
  coeffs[i][0] = Math.exp(coeffs[i][0]);
@@ -13710,9 +13735,7 @@ const MATTHEWS = {
13710
13735
  const flatX = dataX.flat();
13711
13736
  const flatY = dataY.flat();
13712
13737
  assertSameNumberOfElements(flatX, flatY);
13713
- if (flatX.length === 0) {
13714
- return new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13715
- }
13738
+ assertNonEmpty(flatX, flatY);
13716
13739
  const n = flatX.length;
13717
13740
  let trueN = 0, trueP = 0, falseP = 0, falseN = 0;
13718
13741
  for (let i = 0; i < n; ++i) {
@@ -13876,12 +13899,7 @@ const MINIFS = {
13876
13899
  // -----------------------------------------------------------------------------
13877
13900
  function pearson(dataY, dataX) {
13878
13901
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13879
- if (flatDataX.length === 0) {
13880
- throw new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
13881
- }
13882
- if (flatDataX.length < 2) {
13883
- throw new EvaluationError(_t("[[FUNCTION_NAME]] needs at least two values for both parameters."));
13884
- }
13902
+ assertNonEmpty(flatDataX, flatDataY);
13885
13903
  const n = flatDataX.length;
13886
13904
  let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0, sumYY = 0;
13887
13905
  for (let i = 0; i < n; i++) {
@@ -13970,6 +13988,7 @@ const POLYFIT_COEFFS = {
13970
13988
  ],
13971
13989
  compute: function (dataY, dataX, order, intercept = { value: true }) {
13972
13990
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
13991
+ assertNonEmpty(flatDataX, flatDataY);
13973
13992
  return polynomialRegression(flatDataY, flatDataX, toNumber(order, this.locale), toBoolean(intercept));
13974
13993
  },
13975
13994
  isExported: false,
@@ -13989,6 +14008,7 @@ const POLYFIT_FORECAST = {
13989
14008
  compute: function (x, dataY, dataX, order, intercept = { value: true }) {
13990
14009
  const _order = toNumber(order, this.locale);
13991
14010
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14011
+ assertNonEmpty(flatDataX, flatDataY);
13992
14012
  const coeffs = polynomialRegression(flatDataY, flatDataX, _order, toBoolean(intercept)).flat();
13993
14013
  return matrixMap(toMatrix(x), (xij) => evaluatePolynomial(coeffs, toNumber(xij, this.locale), _order));
13994
14014
  },
@@ -14105,6 +14125,7 @@ const SLOPE = {
14105
14125
  ],
14106
14126
  compute: function (dataY, dataX) {
14107
14127
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14128
+ assertNonEmpty(flatDataX, flatDataY);
14108
14129
  const [[slope]] = fullLinearRegression([flatDataX], [flatDataY]);
14109
14130
  return slope;
14110
14131
  },
@@ -14153,6 +14174,7 @@ const SPEARMAN = {
14153
14174
  ],
14154
14175
  compute: function (dataX, dataY) {
14155
14176
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14177
+ assertNonEmpty(flatDataX, flatDataY);
14156
14178
  const n = flatDataX.length;
14157
14179
  const order = flatDataX.map((e, i) => [e, flatDataY[i]]);
14158
14180
  order.sort((a, b) => a[0] - b[0]);
@@ -14263,6 +14285,7 @@ const STEYX = {
14263
14285
  ],
14264
14286
  compute: function (dataY, dataX) {
14265
14287
  const { flatDataX, flatDataY } = filterAndFlatData(dataY, dataX);
14288
+ assertNonEmpty(flatDataX, flatDataY);
14266
14289
  const data = fullLinearRegression([flatDataX], [flatDataY], true, true);
14267
14290
  return data[1][2];
14268
14291
  },
@@ -14280,6 +14303,7 @@ const TREND = {
14280
14303
  arg("b (boolean, optional, default=TRUE)", _t("Given a general linear form of y = m*x+b for a curve fit, calculates b if TRUE or forces b to be 0 and only calculates the m values if FALSE, i.e. forces the curve fit to pass through the origin.")),
14281
14304
  ],
14282
14305
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
14306
+ assertNonEmptyMatrix(knownDataY, "known_data_y");
14283
14307
  return predictLinearValues(toNumberMatrix(knownDataY, "the first argument (known_data_y)"), toNumberMatrix(knownDataX, "the second argument (known_data_x)"), toNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b));
14284
14308
  },
14285
14309
  };
@@ -23773,7 +23797,7 @@ const SUPPORTED_HORIZONTAL_ALIGNMENTS = [
23773
23797
  ];
23774
23798
  const SUPPORTED_VERTICAL_ALIGNMENTS = ["top", "center", "bottom"];
23775
23799
  const SUPPORTED_FONTS = ["Arial"];
23776
- const SUPPORTED_FILL_PATTERNS = ["solid"];
23800
+ const SUPPORTED_FILL_PATTERNS = ["solid", "none"];
23777
23801
  const SUPPORTED_CF_TYPES = [
23778
23802
  "expression",
23779
23803
  "cellIs",
@@ -23958,7 +23982,7 @@ const SUBTOTAL_FUNCTION_CONVERSION_MAP = {
23958
23982
  };
23959
23983
  /** Mapping between Excel format indexes (see XLSX_FORMAT_MAP) and some supported formats */
23960
23984
  const XLSX_FORMATS_CONVERSION_MAP = {
23961
- 0: "",
23985
+ 0: "General",
23962
23986
  1: "0",
23963
23987
  2: "0.00",
23964
23988
  3: "#,#00",
@@ -24284,11 +24308,11 @@ const XLSX_DATE_FORMAT_REGEX = /^(yy|yyyy|m{1,5}|d{1,4}|h{1,2}|s{1,2}|am\/pm|a\/
24284
24308
  * Excel format are defined in openXML §18.8.31
24285
24309
  */
24286
24310
  function convertXlsxFormat(numFmtId, formats, warningManager) {
24287
- if (numFmtId === 0) {
24288
- return undefined;
24289
- }
24290
24311
  // Format is either defined in the imported data, or the formatId is defined in openXML §18.8.30
24291
24312
  let format = XLSX_FORMATS_CONVERSION_MAP[numFmtId] || formats.find((f) => f.id === numFmtId)?.format;
24313
+ if (format === "General") {
24314
+ return undefined;
24315
+ }
24292
24316
  if (format) {
24293
24317
  try {
24294
24318
  let convertedFormat = format.replace(/\[(.*)-[A-Z0-9]{3}\]/g, "[$1]"); // remove currency and locale/date system/number system info (ECMA §18.8.31)
@@ -24486,9 +24510,9 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
24486
24510
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
24487
24511
  continue;
24488
24512
  operator = convertCFCellIsOperator(rule.operator);
24489
- values.push(rule.formula[0]);
24513
+ values.push(prefixFormula(rule.formula[0]));
24490
24514
  if (rule.formula.length === 2) {
24491
- values.push(rule.formula[1]);
24515
+ values.push(prefixFormula(rule.formula[1]));
24492
24516
  }
24493
24517
  break;
24494
24518
  }
@@ -24646,6 +24670,11 @@ function convertIcons(xlsxIconSet, index) {
24646
24670
  ? ICON_SETS[iconSet].neutral
24647
24671
  : ICON_SETS[iconSet].good;
24648
24672
  }
24673
+ /** Prefix the string by "=" if the string looks like a formula */
24674
+ function prefixFormula(formula) {
24675
+ const tokens = tokenize(formula);
24676
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
24677
+ }
24649
24678
  // ---------------------------------------------------------------------------
24650
24679
  // Warnings
24651
24680
  // ---------------------------------------------------------------------------
@@ -27244,10 +27273,11 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
27244
27273
  });
27245
27274
  }
27246
27275
  extractRows(worksheet) {
27276
+ const spilledCells = new Set();
27247
27277
  return this.mapOnElements({ parent: worksheet, query: "sheetData row" }, (rowElement) => {
27248
27278
  return {
27249
27279
  index: this.extractAttr(rowElement, "r", { required: true })?.asNum(),
27250
- cells: this.extractCells(rowElement),
27280
+ cells: this.extractCells(rowElement, spilledCells),
27251
27281
  height: this.extractAttr(rowElement, "ht")?.asNum(),
27252
27282
  customHeight: this.extractAttr(rowElement, "customHeight")?.asBool(),
27253
27283
  hidden: this.extractAttr(rowElement, "hidden")?.asBool(),
@@ -27257,14 +27287,26 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
27257
27287
  };
27258
27288
  });
27259
27289
  }
27260
- extractCells(row) {
27290
+ extractCells(row, spilledCells) {
27261
27291
  return this.mapOnElements({ parent: row, query: "c" }, (cellElement) => {
27292
+ const xc = this.extractAttr(cellElement, "r", { required: true })?.asString();
27293
+ const formula = this.extractCellFormula(cellElement);
27294
+ if (formula?.ref && formula.sharedIndex === undefined) {
27295
+ const zone = toZone(formula.ref);
27296
+ for (const { col, row } of positions(zone)) {
27297
+ const followerXc = toXC(col, row);
27298
+ if (followerXc !== xc) {
27299
+ spilledCells.add(followerXc);
27300
+ }
27301
+ }
27302
+ }
27303
+ const isSpilled = spilledCells.has(xc);
27262
27304
  return {
27263
- xc: this.extractAttr(cellElement, "r", { required: true })?.asString(),
27305
+ xc,
27264
27306
  styleIndex: this.extractAttr(cellElement, "s")?.asNum(),
27265
27307
  type: CELL_TYPE_CONVERSION_MAP[this.extractAttr(cellElement, "t", { default: "n" })?.asString()],
27266
- value: this.extractChildTextContent(cellElement, "v"),
27267
- formula: this.extractCellFormula(cellElement),
27308
+ value: isSpilled ? undefined : this.extractChildTextContent(cellElement, "v") ?? undefined,
27309
+ formula: isSpilled ? undefined : formula,
27268
27310
  };
27269
27311
  });
27270
27312
  }
@@ -27272,11 +27314,14 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
27272
27314
  const formulaElement = this.querySelector(cellElement, "f");
27273
27315
  if (!formulaElement)
27274
27316
  return undefined;
27275
- return {
27276
- content: this.extractTextContent(formulaElement),
27277
- sharedIndex: this.extractAttr(formulaElement, "si")?.asNum(),
27278
- ref: this.extractAttr(formulaElement, "ref")?.asString(),
27279
- };
27317
+ const content = this.extractTextContent(formulaElement);
27318
+ const sharedIndex = this.extractAttr(formulaElement, "si")?.asNum();
27319
+ const ref = this.extractAttr(formulaElement, "ref")?.asString();
27320
+ // This is the case of spilled cells of array formulas where <f> is empty
27321
+ if ((content === undefined || content.trim() === "") && sharedIndex === undefined) {
27322
+ return undefined;
27323
+ }
27324
+ return { content, sharedIndex, ref };
27280
27325
  }
27281
27326
  extractHyperLinks(worksheet) {
27282
27327
  return this.mapOnElements({ parent: worksheet, query: "hyperlink" }, (linkElement) => {
@@ -36170,7 +36215,7 @@ const splitToColumns = {
36170
36215
  const reinsertDynamicPivotMenu = {
36171
36216
  id: "reinsert_dynamic_pivot",
36172
36217
  name: _t("Re-insert dynamic pivot"),
36173
- sequence: 1020,
36218
+ sequence: 60,
36174
36219
  icon: "o-spreadsheet-Icon.INSERT_PIVOT",
36175
36220
  children: [REINSERT_DYNAMIC_PIVOT_CHILDREN],
36176
36221
  isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid()),
@@ -36178,7 +36223,7 @@ const reinsertDynamicPivotMenu = {
36178
36223
  const reinsertStaticPivotMenu = {
36179
36224
  id: "reinsert_static_pivot",
36180
36225
  name: _t("Re-insert static pivot"),
36181
- sequence: 1020,
36226
+ sequence: 70,
36182
36227
  icon: "o-spreadsheet-Icon.INSERT_PIVOT",
36183
36228
  children: [REINSERT_STATIC_PIVOT_CHILDREN],
36184
36229
  isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid()),
@@ -37916,8 +37961,9 @@ topbarMenuRegistry
37916
37961
  sequence: 40,
37917
37962
  separator: true,
37918
37963
  })
37919
- .addChild("data_sources_data", ["data"], (env) => {
37964
+ .addChild("pivot_data_sources", ["data"], (env) => {
37920
37965
  const sequence = 50;
37966
+ const numberOfPivots = env.model.getters.getPivotIds().length;
37921
37967
  return env.model.getters.getPivotIds().map((pivotId, index) => {
37922
37968
  const highlightProvider = {
37923
37969
  get highlights() {
@@ -37927,7 +37973,7 @@ topbarMenuRegistry
37927
37973
  return {
37928
37974
  id: `item_pivot_${env.model.getters.getPivotFormulaId(pivotId)}`,
37929
37975
  name: env.model.getters.getPivotDisplayName(pivotId),
37930
- sequence: sequence + index,
37976
+ sequence: sequence + index / numberOfPivots,
37931
37977
  isReadonlyAllowed: true,
37932
37978
  execute: (env) => env.openSidePanel("PivotSidePanel", { pivotId }),
37933
37979
  onStartHover: (env) => env.getStore(HighlightStore).register(highlightProvider),
@@ -63899,7 +63945,7 @@ class PivotUIPlugin extends CoreViewPlugin {
63899
63945
  if (!result) {
63900
63946
  return EMPTY_PIVOT_CELL;
63901
63947
  }
63902
- const { functionName, args } = result;
63948
+ let { functionName, args } = result;
63903
63949
  const formulaId = args[0];
63904
63950
  if (!formulaId) {
63905
63951
  return EMPTY_PIVOT_CELL;
@@ -63929,6 +63975,9 @@ class PivotUIPlugin extends CoreViewPlugin {
63929
63975
  return pivotCells[pivotCol][pivotRow];
63930
63976
  }
63931
63977
  try {
63978
+ const offsetRow = position.row - mainPosition.row;
63979
+ const offsetCol = position.col - mainPosition.col;
63980
+ args = args.map((arg) => (isMatrix(arg) ? arg[offsetCol][offsetRow] : arg));
63932
63981
  if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
63933
63982
  const domain = pivot.parseArgsToPivotDomain(args.slice(1, -2).map((value) => ({ value })));
63934
63983
  return {
@@ -67777,12 +67826,12 @@ class ClipboardPlugin extends UIPlugin {
67777
67826
  }
67778
67827
  case "INSERT_CELL": {
67779
67828
  const { cut, paste } = this.getInsertCellsTargets(cmd.zone, cmd.shiftDimension);
67780
- const copiedData = this.copy(cut);
67829
+ const copiedData = this.copy(cut, "shiftCells");
67781
67830
  return this.isPasteAllowed(paste, copiedData, { isCutOperation: true });
67782
67831
  }
67783
67832
  case "DELETE_CELL": {
67784
67833
  const { cut, paste } = this.getDeleteCellsTargets(cmd.zone, cmd.shiftDimension);
67785
- const copiedData = this.copy(cut);
67834
+ const copiedData = this.copy(cut, "shiftCells");
67786
67835
  return this.isPasteAllowed(paste, copiedData, { isCutOperation: true });
67787
67836
  }
67788
67837
  }
@@ -67872,13 +67921,13 @@ class ClipboardPlugin extends UIPlugin {
67872
67921
  });
67873
67922
  break;
67874
67923
  }
67875
- const copiedData = this.copy(cut);
67924
+ const copiedData = this.copy(cut, "shiftCells");
67876
67925
  this.paste(paste, copiedData, { isCutOperation: true });
67877
67926
  break;
67878
67927
  }
67879
67928
  case "INSERT_CELL": {
67880
67929
  const { cut, paste } = this.getInsertCellsTargets(cmd.zone, cmd.shiftDimension);
67881
- const copiedData = this.copy(cut);
67930
+ const copiedData = this.copy(cut, "shiftCells");
67882
67931
  this.paste(paste, copiedData, { isCutOperation: true });
67883
67932
  break;
67884
67933
  }
@@ -67993,11 +68042,11 @@ class ClipboardPlugin extends UIPlugin {
67993
68042
  }
67994
68043
  return false;
67995
68044
  }
67996
- copy(zones) {
68045
+ copy(zones, mode = "copyPaste") {
67997
68046
  let copiedData = {};
67998
68047
  const clipboardData = this.getClipboardData(zones);
67999
68048
  for (const { handlerName, handler } of this.selectClipboardHandlers(clipboardData)) {
68000
- const data = handler.copy(clipboardData);
68049
+ const data = handler.copy(clipboardData, mode);
68001
68050
  copiedData[handlerName] = data;
68002
68051
  const minimalKeys = ["sheetId", "cells", "zones", "figureId"];
68003
68052
  for (const key of minimalKeys) {
@@ -68861,7 +68910,7 @@ class GridSelectionPlugin extends UIPlugin {
68861
68910
  ];
68862
68911
  for (const Handler of clipboardHandlersRegistries.cellHandlers.getAll()) {
68863
68912
  const handler = new Handler(this.getters, this.dispatch);
68864
- const data = handler.copy(getClipboardDataPositions(sheetId, target));
68913
+ const data = handler.copy(getClipboardDataPositions(sheetId, target), "shiftCells");
68865
68914
  if (!data) {
68866
68915
  continue;
68867
68916
  }
@@ -77285,6 +77334,6 @@ exports.tokenColors = tokenColors;
77285
77334
  exports.tokenize = tokenize;
77286
77335
 
77287
77336
 
77288
- __info__.version = "18.2.27";
77289
- __info__.date = "2025-08-26T10:07:14.516Z";
77290
- __info__.hash = "fbf9445";
77337
+ __info__.version = "18.2.29";
77338
+ __info__.date = "2025-09-11T08:44:31.801Z";
77339
+ __info__.hash = "665bc43";