@odoo/o-spreadsheet 19.1.0-alpha.13 → 19.1.0-alpha.14

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.
@@ -3,8 +3,8 @@
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
5
  * @version 19.1.0-alpha.3
6
- * @date 2025-11-24T07:52:34.809Z
7
- * @hash e232982
6
+ * @date 2025-12-02T05:34:07.213Z
7
+ * @hash 04cf666
8
8
  */
9
9
 
10
10
  (function (exports) {
@@ -3109,8 +3109,56 @@
3109
3109
  return isMatrix(arg) && !isSingleElementMatrix(arg);
3110
3110
  }
3111
3111
 
3112
+ function stackHorizontally(ranges, options) {
3113
+ const matrices = ranges.map(toMatrix);
3114
+ const nbRowsArr = matrices.map((m) => m?.[0]?.length ?? 0);
3115
+ const nbRows = Math.max(...nbRowsArr);
3116
+ if (options?.requireSameRowCount) {
3117
+ const firstLength = nbRowsArr[0];
3118
+ if (nbRowsArr.some((len) => len !== firstLength)) {
3119
+ return new EvaluationError(_t("All ranges in [[FUNCTION_NAME]] must have the same number of columns (got %s).", nbRowsArr.join(", ")));
3120
+ }
3121
+ }
3122
+ const result = [];
3123
+ for (const matrix of matrices) {
3124
+ for (let col = 0; col < matrix.length; col++) {
3125
+ // Fill with nulls if needed
3126
+ const array = Array(nbRows).fill({ value: null });
3127
+ for (let row = 0; row < matrix[col].length; row++) {
3128
+ array[row] = matrix[col][row];
3129
+ }
3130
+ result.push(array);
3131
+ }
3132
+ }
3133
+ return result;
3134
+ }
3135
+ function stackVertically(ranges, options) {
3136
+ const matrices = ranges.map(toMatrix);
3137
+ const nbColsArr = matrices.map((m) => m?.length ?? 0);
3138
+ const nbCols = Math.max(...nbColsArr);
3139
+ if (options?.requireSameColCount) {
3140
+ const firstLength = nbColsArr[0];
3141
+ if (nbColsArr.some((len) => len !== firstLength)) {
3142
+ return new EvaluationError(_t("All ranges in [[FUNCTION_NAME]] must have the same number of columns (got %s).", nbColsArr.join(", ")));
3143
+ }
3144
+ }
3145
+ const nbRows = matrices.reduce((acc, m) => acc + (m?.[0]?.length ?? 0), 0);
3146
+ const result = generateMatrix(nbCols, nbRows, () => ({
3147
+ value: null,
3148
+ }));
3149
+ let currentRow = 0;
3150
+ for (const matrix of matrices) {
3151
+ for (let col = 0; col < matrix.length; col++) {
3152
+ for (let row = 0; row < matrix[col].length; row++) {
3153
+ result[col][currentRow + row] = matrix[col][row];
3154
+ }
3155
+ }
3156
+ currentRow += matrix[0]?.length ?? 0;
3157
+ }
3158
+ return result;
3159
+ }
3112
3160
  // -----------------------------------------------------------------------------
3113
- // ARRAY_CONSTRAIN
3161
+ // ARRAY.CONSTRAIN
3114
3162
  // -----------------------------------------------------------------------------
3115
3163
  const ARRAY_CONSTRAIN = {
3116
3164
  description: _t("Returns a result array constrained to a specific width and height."),
@@ -3136,6 +3184,30 @@
3136
3184
  isExported: false,
3137
3185
  };
3138
3186
  // -----------------------------------------------------------------------------
3187
+ // ARRAY.LITERAL
3188
+ // -----------------------------------------------------------------------------
3189
+ const ARRAY_LITERAL = {
3190
+ description: _t("Appends ranges vertically and in sequence to return a larger array. All ranges must have the same number of columns."),
3191
+ args: [arg("range (any, range<any>, repeating)", _t("The range to be appended."))],
3192
+ compute: function (...ranges) {
3193
+ return stackVertically(ranges, { requireSameColCount: true });
3194
+ },
3195
+ isExported: false,
3196
+ hidden: true,
3197
+ };
3198
+ // -----------------------------------------------------------------------------
3199
+ // ARRAY.ROW
3200
+ // -----------------------------------------------------------------------------
3201
+ const ARRAY_ROW = {
3202
+ description: _t("Appends ranges horizontally and in sequence to return a larger array. All ranges must have the same number of rows."),
3203
+ args: [arg("range (any, range<any>, repeating)", _t("The range to be appended."))],
3204
+ compute: function (...ranges) {
3205
+ return stackHorizontally(ranges, { requireSameRowCount: true });
3206
+ },
3207
+ isExported: false,
3208
+ hidden: true,
3209
+ };
3210
+ // -----------------------------------------------------------------------------
3139
3211
  // CHOOSECOLS
3140
3212
  // -----------------------------------------------------------------------------
3141
3213
  const CHOOSECOLS = {
@@ -3281,20 +3353,7 @@
3281
3353
  description: _t("Appends ranges horizontally and in sequence to return a larger array."),
3282
3354
  args: [arg("range (any, range<any>, repeating)", _t("The range to be appended."))],
3283
3355
  compute: function (...ranges) {
3284
- const nbRows = Math.max(...ranges.map((r) => r?.[0]?.length ?? 0));
3285
- const result = [];
3286
- for (const range of ranges) {
3287
- const _range = toMatrix(range);
3288
- for (let col = 0; col < _range.length; col++) {
3289
- //TODO: fill with #N/A for unavailable values instead of zeroes
3290
- const array = Array(nbRows).fill({ value: null });
3291
- for (let row = 0; row < _range[col].length; row++) {
3292
- array[row] = _range[col][row];
3293
- }
3294
- result.push(array);
3295
- }
3296
- }
3297
- return result;
3356
+ return stackHorizontally(ranges);
3298
3357
  },
3299
3358
  isExported: true,
3300
3359
  };
@@ -3553,22 +3612,7 @@
3553
3612
  description: _t("Appends ranges vertically and in sequence to return a larger array."),
3554
3613
  args: [arg("range (any, range<any>, repeating)", _t("The range to be appended."))],
3555
3614
  compute: function (...ranges) {
3556
- const nbColumns = Math.max(...ranges.map((range) => toMatrix(range).length));
3557
- const nbRows = ranges.reduce((acc, range) => acc + toMatrix(range)[0].length, 0);
3558
- const result = Array(nbColumns)
3559
- .fill([])
3560
- .map(() => Array(nbRows).fill({ value: 0 })); // TODO fill with #N/A
3561
- let currentRow = 0;
3562
- for (const range of ranges) {
3563
- const _array = toMatrix(range);
3564
- for (let col = 0; col < _array.length; col++) {
3565
- for (let row = 0; row < _array[col].length; row++) {
3566
- result[col][currentRow + row] = _array[col][row];
3567
- }
3568
- }
3569
- currentRow += _array[0].length;
3570
- }
3571
- return result;
3615
+ return stackVertically(ranges);
3572
3616
  },
3573
3617
  isExported: true,
3574
3618
  };
@@ -3628,6 +3672,8 @@
3628
3672
  var array = /*#__PURE__*/Object.freeze({
3629
3673
  __proto__: null,
3630
3674
  ARRAY_CONSTRAIN: ARRAY_CONSTRAIN,
3675
+ ARRAY_LITERAL: ARRAY_LITERAL,
3676
+ ARRAY_ROW: ARRAY_ROW,
3631
3677
  CHOOSECOLS: CHOOSECOLS,
3632
3678
  CHOOSEROWS: CHOOSEROWS,
3633
3679
  EXPAND: EXPAND,
@@ -6083,6 +6129,47 @@
6083
6129
  return newZones;
6084
6130
  }
6085
6131
  }
6132
+ /**
6133
+ * Splits zone z2 by removing the overlapping zone z1 (fully inside z2).
6134
+ * Returns the remaining parts of z2 that don't overlap with z1.
6135
+ *
6136
+ * Diagram:
6137
+ * ┌──────────── z2 ─────────────┐
6138
+ * │ 1 │
6139
+ * │--------─────────────--------│
6140
+ * │ 2 | z1 | 3 │
6141
+ * │--------─────────────--------│
6142
+ * │ 4 │
6143
+ * └─────────────────────────────┘
6144
+ *
6145
+ * Input:
6146
+ * z1 = { top: 2, bottom: 3, left: 2, right: 3 }
6147
+ * z2 = { top: 1, bottom: 4, left: 1, right: 4 }
6148
+ *
6149
+ * Output:
6150
+ * [
6151
+ * { top: 4, bottom: 4, left: 1, right: 4 }, // bottom
6152
+ * { top: 2, bottom: 3, left: 4, right: 4 }, // right
6153
+ * { top: 2, bottom: 3, left: 1, right: 1 }, // left
6154
+ * { top: 1, bottom: 1, left: 1, right: 4 } // top
6155
+ * ]
6156
+ */
6157
+ function splitZone(z1, z2) {
6158
+ const zones = [];
6159
+ if (z1.bottom < z2.bottom) {
6160
+ zones.push({ ...z2, top: z1.bottom + 1 });
6161
+ }
6162
+ if (z1.right < z2.right) {
6163
+ zones.push({ ...z2, left: z1.right + 1, top: z1.top, bottom: z1.bottom });
6164
+ }
6165
+ if (z1.left > z2.left) {
6166
+ zones.push({ ...z2, right: z1.left - 1, top: z1.top, bottom: z1.bottom });
6167
+ }
6168
+ if (z1.top > z2.top) {
6169
+ zones.push({ ...z2, bottom: z1.top - 1 });
6170
+ }
6171
+ return zones;
6172
+ }
6086
6173
 
6087
6174
  function isSheetDependent(cmd) {
6088
6175
  return "sheetId" in cmd;
@@ -6113,7 +6200,6 @@
6113
6200
  "REDO",
6114
6201
  "ADD_MERGE",
6115
6202
  "REMOVE_MERGE",
6116
- "DUPLICATE_SHEET",
6117
6203
  "UPDATE_LOCALE",
6118
6204
  "ADD_PIVOT",
6119
6205
  "UPDATE_PIVOT",
@@ -9603,7 +9689,9 @@
9603
9689
  while (!chars.isOver()) {
9604
9690
  let token = tokenizeNewLine(chars) ||
9605
9691
  tokenizeSpace(chars) ||
9692
+ tokenizeArrayRowSeparator(chars, locale) ||
9606
9693
  tokenizeArgsSeparator(chars, locale) ||
9694
+ tokenizeBraces(chars) ||
9607
9695
  tokenizeParenthesis(chars) ||
9608
9696
  tokenizeOperator(chars) ||
9609
9697
  tokenizeString(chars) ||
@@ -9636,6 +9724,17 @@
9636
9724
  }
9637
9725
  return null;
9638
9726
  }
9727
+ const braces = {
9728
+ "{": { type: "LEFT_BRACE", value: "{" },
9729
+ "}": { type: "RIGHT_BRACE", value: "}" },
9730
+ };
9731
+ function tokenizeBraces(chars) {
9732
+ if (chars.current === "{" || chars.current === "}") {
9733
+ const value = chars.shift();
9734
+ return braces[value];
9735
+ }
9736
+ return null;
9737
+ }
9639
9738
  function tokenizeArgsSeparator(chars, locale) {
9640
9739
  if (chars.current === locale.formulaArgSeparator) {
9641
9740
  const value = chars.shift();
@@ -9644,6 +9743,20 @@
9644
9743
  }
9645
9744
  return null;
9646
9745
  }
9746
+ function tokenizeArrayRowSeparator(chars, locale) {
9747
+ // The array row separator is used in array literals to separate rows.
9748
+ // It is not explicitly defined in locales, but depends on the formulaArgSeparator.
9749
+ // Example: {1,2,3;4,5,6} — here, ';' separates rows and ',' separates columns.
9750
+ const rowSeparator = locale.formulaArgSeparator === ";" ? "\\" : ";";
9751
+ if (!rowSeparator) {
9752
+ return null;
9753
+ }
9754
+ if (chars.current === rowSeparator) {
9755
+ chars.shift();
9756
+ return { type: "ARRAY_ROW_SEPARATOR", value: rowSeparator };
9757
+ }
9758
+ return null;
9759
+ }
9647
9760
  function tokenizeOperator(chars) {
9648
9761
  for (const op of OPERATORS) {
9649
9762
  if (chars.currentStartsWith(op)) {
@@ -9864,6 +9977,9 @@
9864
9977
  else if (token.type === "ARG_SEPARATOR") {
9865
9978
  localizedFormula += toLocale.formulaArgSeparator;
9866
9979
  }
9980
+ else if (token.type === "ARRAY_ROW_SEPARATOR") {
9981
+ localizedFormula += toLocale.formulaArgSeparator === ";" ? "\\" : ";";
9982
+ }
9867
9983
  else {
9868
9984
  localizedFormula += token.value;
9869
9985
  }
@@ -11027,7 +11143,6 @@
11027
11143
  // -----------------------------------------------------------------------------
11028
11144
  const FILTER = {
11029
11145
  description: _t("Returns a filtered version of the source range, returning only rows or columns that meet the specified conditions."),
11030
- // TODO modify args description when vectorization on formulas is available
11031
11146
  args: [
11032
11147
  arg("range (any, range<any>)", _t("The data to be filtered.")),
11033
11148
  arg("condition (boolean, range<boolean>, repeating)", _t("Column or row containing true or false values corresponding to the range.")),
@@ -17227,6 +17342,8 @@
17227
17342
  tokenStartIndex: current.tokenIndex,
17228
17343
  tokenEndIndex: rightParen.tokenIndex,
17229
17344
  };
17345
+ case "LEFT_BRACE":
17346
+ return parseArrayLiteral(tokens, current);
17230
17347
  case "OPERATOR":
17231
17348
  const operator = current.value;
17232
17349
  if (UNARY_OPERATORS_PREFIX.includes(operator)) {
@@ -17280,6 +17397,34 @@
17280
17397
  }
17281
17398
  return token;
17282
17399
  }
17400
+ function parseArrayLiteral(tokens, leftBrace) {
17401
+ const rows = [];
17402
+ let currentRow = [parseExpression(tokens)]; // there must be at least one element
17403
+ while (tokens.current?.type !== "RIGHT_BRACE") {
17404
+ const nextToken = tokens.shift();
17405
+ if (!nextToken) {
17406
+ throw new BadExpressionError(_t("Missing closing brace"));
17407
+ }
17408
+ else if (nextToken.type === "ARG_SEPARATOR") {
17409
+ currentRow.push(parseExpression(tokens));
17410
+ }
17411
+ else if (nextToken.type === "ARRAY_ROW_SEPARATOR") {
17412
+ rows.push(currentRow);
17413
+ currentRow = [parseExpression(tokens)];
17414
+ }
17415
+ else {
17416
+ throw new BadExpressionError(_t("Unexpected token: %s", nextToken.value));
17417
+ }
17418
+ }
17419
+ const rightBrace = consumeOrThrow(tokens, "RIGHT_BRACE", _t("Missing closing brace"));
17420
+ rows.push(currentRow);
17421
+ return {
17422
+ type: "ARRAY",
17423
+ value: rows,
17424
+ tokenStartIndex: leftBrace.tokenIndex,
17425
+ tokenEndIndex: rightBrace.tokenIndex,
17426
+ };
17427
+ }
17283
17428
  function parseExpression(tokens, parent_priority = 0) {
17284
17429
  if (tokens.length === 0) {
17285
17430
  throw new BadExpressionError();
@@ -17370,6 +17515,13 @@
17370
17515
  yield* astIterator(arg);
17371
17516
  }
17372
17517
  break;
17518
+ case "ARRAY":
17519
+ for (const row of ast.value) {
17520
+ for (const cell of row) {
17521
+ yield* astIterator(cell);
17522
+ }
17523
+ }
17524
+ break;
17373
17525
  case "UNARY_OPERATION":
17374
17526
  yield* astIterator(ast.operand);
17375
17527
  break;
@@ -17387,6 +17539,11 @@
17387
17539
  ...ast,
17388
17540
  args: ast.args.map((child) => mapAst(child, fn)),
17389
17541
  };
17542
+ case "ARRAY":
17543
+ return {
17544
+ ...ast,
17545
+ value: ast.value.map((row) => row.map((cell) => mapAst(cell, fn))),
17546
+ };
17390
17547
  case "UNARY_OPERATION":
17391
17548
  return {
17392
17549
  ...ast,
@@ -17545,6 +17702,23 @@
17545
17702
  code.append(...args);
17546
17703
  const fnName = ast.value.toUpperCase();
17547
17704
  return code.return(`ctx['${fnName}'](${args.map((arg) => arg.returnExpression)})`);
17705
+ case "ARRAY": {
17706
+ // a literal array is compiled into function calls
17707
+ const arrayFunctionCall = {
17708
+ type: "FUNCALL",
17709
+ value: "ARRAY.LITERAL",
17710
+ args: ast.value.map((row) => ({
17711
+ type: "FUNCALL",
17712
+ value: "ARRAY.ROW",
17713
+ args: row,
17714
+ tokenStartIndex: 0,
17715
+ tokenEndIndex: 0,
17716
+ })),
17717
+ tokenStartIndex: 0,
17718
+ tokenEndIndex: 0,
17719
+ };
17720
+ return compileAST(arrayFunctionCall);
17721
+ }
17548
17722
  case "UNARY_OPERATION": {
17549
17723
  const fnName = UNARY_OPERATOR_MAP[ast.value];
17550
17724
  const operand = compileAST(ast.operand, false, false).assignResultToVariable();
@@ -25887,6 +26061,17 @@
25887
26061
  }
25888
26062
  }
25889
26063
 
26064
+ function schemeToColorScale(scheme) {
26065
+ const colors = COLORSCHEMES[scheme];
26066
+ return colors === undefined
26067
+ ? undefined
26068
+ : {
26069
+ minColor: colors[0],
26070
+ midColor: colors.length === 3 ? colors[1] : undefined,
26071
+ maxColor: colors[colors.length - 1],
26072
+ };
26073
+ }
26074
+
25890
26075
  /**
25891
26076
  * parses a formula (as a string) into the same formula,
25892
26077
  * but with the references to other cells extracted
@@ -26531,13 +26716,22 @@
26531
26716
  return data;
26532
26717
  },
26533
26718
  })
26534
- .add("19.2", {
26719
+ .add("19.1.0", {
26535
26720
  migrate(data) {
26536
26721
  for (const sheet of data.sheets || []) {
26537
26722
  for (const figure of sheet.figures || []) {
26538
26723
  if (figure.tag === "chart" && figure.data.type === "geo") {
26539
26724
  if ("colorScale" in figure.data && typeof figure.data.colorScale === "string") {
26540
- figure.data.colorScale = COLORSCHEMES[figure.data.colorScale];
26725
+ figure.data.colorScale = schemeToColorScale(figure.data.colorScale);
26726
+ }
26727
+ }
26728
+ if (figure.tag === "carousel") {
26729
+ for (const definition of Object.values(figure.data.chartDefinitions) || []) {
26730
+ if (definition.type === "geo") {
26731
+ if ("colorScale" in definition && typeof definition.colorScale === "string") {
26732
+ definition.colorScale = schemeToColorScale(definition.colorScale);
26733
+ }
26734
+ }
26541
26735
  }
26542
26736
  }
26543
26737
  }
@@ -26800,18 +26994,22 @@
26800
26994
  return messages;
26801
26995
  }
26802
26996
  function fixChartDefinitions(data, initialMessages) {
26997
+ /**
26998
+ * Revisions created after version 18.5.1 contain the full chart definition in the command
26999
+ * if the data was alreay updated to 18.5.1, then those older revision cannot (by definition) be reaplied
27000
+ * and should not be replayed.
27001
+ * FIXME: every command should be versionned when upgraded to allow finer tuning.
27002
+ */
27003
+ if (!data.version || compareVersions(String(data.version), "18.5.1") >= 0) {
27004
+ return initialMessages;
27005
+ }
26803
27006
  const messages = [];
26804
27007
  const map = {};
26805
27008
  for (const sheet of data.sheets || []) {
26806
27009
  sheet.figures?.forEach((figure) => {
26807
27010
  if (figure.tag === "chart") {
26808
27011
  // chart definition
26809
- if (data.version && compareVersions(String(data.version), "18.5.1") <= 0) {
26810
- map[figure.data.chartId] = figure.data;
26811
- }
26812
- else {
26813
- map[figure.id] = figure.data;
26814
- }
27012
+ map[figure.id] = figure.data;
26815
27013
  }
26816
27014
  });
26817
27015
  }
@@ -29851,7 +30049,8 @@
29851
30049
  }
29852
30050
  break;
29853
30051
  case "DUPLICATE_SHEET": {
29854
- for (const figureId in this.figures[cmd.sheetId]) {
30052
+ for (const figure of this.getFigures(cmd.sheetId)) {
30053
+ const figureId = figure.id;
29855
30054
  const fig = this.figures[cmd.sheetId]?.[figureId];
29856
30055
  if (!fig) {
29857
30056
  continue;
@@ -32184,10 +32383,17 @@
32184
32383
  if (!pivot) {
32185
32384
  continue;
32186
32385
  }
32187
- for (const measure of pivot.definition.measures) {
32386
+ const def = deepCopy(pivot.definition);
32387
+ for (const measure of def.measures) {
32188
32388
  if (measure.computedBy?.formula === formulaString) {
32189
- const measureIndex = pivot.definition.measures.indexOf(measure);
32190
- this.history.update("pivots", pivotId, "definition", "measures", measureIndex, "computedBy", { formula: newFormulaString, sheetId });
32389
+ const measureIndex = def.measures.indexOf(measure);
32390
+ if (measureIndex !== -1) {
32391
+ def.measures[measureIndex].computedBy = {
32392
+ formula: newFormulaString,
32393
+ sheetId,
32394
+ };
32395
+ }
32396
+ this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
32191
32397
  }
32192
32398
  }
32193
32399
  }
@@ -33219,6 +33425,9 @@
33219
33425
  const { sheetId, zone } = definition.dataSet;
33220
33426
  const range = this.getters.getRangeFromZone(sheetId, zone);
33221
33427
  const adaptedRange = adaptPivotRange(range, applyChange);
33428
+ if (adaptedRange === range) {
33429
+ return;
33430
+ }
33222
33431
  const dataSet = adaptedRange && {
33223
33432
  sheetId: adaptedRange.sheetId,
33224
33433
  zone: adaptedRange.zone,
@@ -38950,6 +39159,10 @@
38950
39159
  ? `(${astToFormula(ast.operand)})`
38951
39160
  : astToFormula(ast.operand);
38952
39161
  return ast.value + rightOperand;
39162
+ case "ARRAY":
39163
+ return ("{" +
39164
+ ast.value.map((row) => row.map((cell) => astToFormula(cell)).join(",")).join(";") +
39165
+ "}");
38953
39166
  case "BIN_OPERATION":
38954
39167
  const leftOperation = leftOperandNeedsParenthesis(ast)
38955
39168
  ? `(${astToFormula(ast.left)})`
@@ -40514,7 +40727,6 @@
40514
40727
  pivotRegistry.add("SPREADSHEET", {
40515
40728
  ui: SpreadsheetPivot,
40516
40729
  definition: SpreadsheetPivotRuntimeDefinition,
40517
- externalData: false,
40518
40730
  dateGranularities: [...dateGranularities],
40519
40731
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
40520
40732
  isMeasureCandidate: (field) => field.type !== "boolean",
@@ -40556,9 +40768,7 @@
40556
40768
  handle(cmd) {
40557
40769
  if (invalidateEvaluationCommands.has(cmd.type)) {
40558
40770
  for (const pivotId of this.getters.getPivotIds()) {
40559
- if (!pivotRegistry.get(this.getters.getPivotCoreDefinition(pivotId).type).externalData) {
40560
- this.setupPivot(pivotId, { recreate: true });
40561
- }
40771
+ this.setupPivot(pivotId, { recreate: true });
40562
40772
  }
40563
40773
  }
40564
40774
  switch (cmd.type) {
@@ -40772,7 +40982,7 @@
40772
40982
  pivot.init({ reload: true });
40773
40983
  }
40774
40984
  setupPivot(pivotId, { recreate } = { recreate: false }) {
40775
- const definition = this.getters.getPivotCoreDefinition(pivotId);
40985
+ const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
40776
40986
  if (!(pivotId in this.pivots)) {
40777
40987
  const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
40778
40988
  this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
@@ -43843,6 +44053,15 @@
43843
44053
  return "InvalidFigureId" /* CommandResult.InvalidFigureId */;
43844
44054
  }
43845
44055
  return "Success" /* CommandResult.Success */;
44056
+ case "DUPLICATE_CAROUSEL_CHART":
44057
+ if (!this.getters.doesCarouselExist(cmd.carouselId) ||
44058
+ !this.getters
44059
+ .getCarousel(cmd.carouselId)
44060
+ .items.some((item) => item.type === "chart" && item.chartId === cmd.chartId) ||
44061
+ this.getters.getChart(cmd.duplicatedChartId)) {
44062
+ return "InvalidFigureId" /* CommandResult.InvalidFigureId */;
44063
+ }
44064
+ return "Success" /* CommandResult.Success */;
43846
44065
  case "ADD_NEW_CHART_TO_CAROUSEL":
43847
44066
  if (!this.getters.doesCarouselExist(cmd.figureId)) {
43848
44067
  return "InvalidFigureId" /* CommandResult.InvalidFigureId */;
@@ -43867,6 +44086,9 @@
43867
44086
  case "ADD_FIGURE_CHART_TO_CAROUSEL":
43868
44087
  this.addFigureChartToCarousel(cmd.carouselFigureId, cmd.chartFigureId, cmd.sheetId);
43869
44088
  break;
44089
+ case "DUPLICATE_CAROUSEL_CHART":
44090
+ this.duplicateCarouselChart(cmd);
44091
+ break;
43870
44092
  case "UPDATE_CAROUSEL_ACTIVE_ITEM":
43871
44093
  this.carouselStates[cmd.figureId] = this.getCarouselItemId(cmd.item);
43872
44094
  break;
@@ -44005,6 +44227,29 @@
44005
44227
  });
44006
44228
  this.dispatch("DELETE_FIGURE", { sheetId, figureId: chartFigureId });
44007
44229
  }
44230
+ duplicateCarouselChart({ carouselId, chartId, sheetId, duplicatedChartId, }) {
44231
+ const chart = this.getters.getChart(chartId);
44232
+ if (!chart) {
44233
+ return;
44234
+ }
44235
+ const carousel = this.getters.getCarousel(carouselId);
44236
+ const duplicatedItemIndex = carousel.items.findIndex((item) => item.type === "chart" && item.chartId === chartId);
44237
+ if (duplicatedItemIndex === -1) {
44238
+ return;
44239
+ }
44240
+ this.dispatch("CREATE_CHART", {
44241
+ chartId: duplicatedChartId,
44242
+ figureId: carouselId,
44243
+ sheetId,
44244
+ definition: chart.getDefinition(),
44245
+ });
44246
+ const carouselItems = insertItemsAtIndex(carousel.items, [{ type: "chart", chartId: duplicatedChartId }], duplicatedItemIndex + 1);
44247
+ this.dispatch("UPDATE_CAROUSEL", {
44248
+ sheetId,
44249
+ figureId: carouselId,
44250
+ definition: { ...carousel, items: carouselItems },
44251
+ });
44252
+ }
44008
44253
  getCarouselItemId(item) {
44009
44254
  return item.type === "chart" ? item.chartId : "carouselDataView";
44010
44255
  }
@@ -45220,25 +45465,43 @@
45220
45465
  return "Success" /* CommandResult.Success */;
45221
45466
  }
45222
45467
  handleEvent(event) {
45223
- const anchor = event.anchor;
45224
- let zones = [];
45468
+ let anchor = event.anchor;
45469
+ let zones = [...this.gridSelection.zones];
45225
45470
  this.isUnbounded = event.options?.unbounded || false;
45226
45471
  switch (event.mode) {
45227
45472
  case "overrideSelection":
45228
45473
  zones = [anchor.zone];
45229
45474
  break;
45230
45475
  case "updateAnchor":
45231
- zones = [...this.gridSelection.zones];
45232
45476
  const index = zones.findIndex((z) => isEqual(z, event.previousAnchor.zone));
45233
45477
  if (index >= 0) {
45234
45478
  zones[index] = anchor.zone;
45235
45479
  }
45236
45480
  break;
45237
45481
  case "newAnchor":
45238
- zones = [...this.gridSelection.zones, anchor.zone];
45482
+ zones.push(anchor.zone);
45483
+ break;
45484
+ case "commitSelection":
45485
+ const zoneToSplit = zones.find((zone) => isZoneInside(anchor.zone, zone) && !isEqual(anchor.zone, zone));
45486
+ const removeFullAnchor = zones.filter((zone) => isEqual(anchor.zone, zone)).length > 1;
45487
+ if (removeFullAnchor && zones.length > 2) {
45488
+ zones = zones.filter((zone) => !isEqual(zone, anchor.zone));
45489
+ }
45490
+ else if (zoneToSplit) {
45491
+ const splittedZones = splitZone(anchor.zone, zoneToSplit);
45492
+ zones = zones
45493
+ .filter((z) => !isEqual(z, anchor.zone) && !isEqual(z, zoneToSplit))
45494
+ .concat(splittedZones);
45495
+ }
45496
+ zones = uniqueZones(zones);
45497
+ const lastZone = zones[zones.length - 1];
45498
+ anchor = {
45499
+ cell: { col: lastZone.left, row: lastZone.top },
45500
+ zone: lastZone,
45501
+ };
45239
45502
  break;
45240
45503
  }
45241
- this.setSelectionMixin(event.anchor, zones);
45504
+ this.setSelectionMixin(anchor, zones);
45242
45505
  /** Any change to the selection has to be reflected in the selection processor. */
45243
45506
  this.selection.resetDefaultAnchor(this, deepCopy(this.gridSelection.anchor));
45244
45507
  const { col, row } = this.gridSelection.anchor.cell;
@@ -45518,7 +45781,7 @@
45518
45781
  setSelectionMixin(anchor, zones) {
45519
45782
  const { anchor: clippedAnchor, zones: clippedZones } = this.clipSelection(this.getters.getActiveSheetId(), { anchor, zones });
45520
45783
  this.gridSelection.anchor = clippedAnchor;
45521
- this.gridSelection.zones = uniqueZones(clippedZones);
45784
+ this.gridSelection.zones = clippedZones;
45522
45785
  }
45523
45786
  /**
45524
45787
  * Change the anchor of the selection active cell to an absolute col and row index.
@@ -47490,6 +47753,9 @@
47490
47753
  bottom: Math.max(anchorRow, row),
47491
47754
  };
47492
47755
  const expandedZone = this.getters.expandZone(sheetId, zone);
47756
+ if (isEqual(this.anchor.zone, expandedZone)) {
47757
+ return new DispatchResult("NoChanges" /* CommandResult.NoChanges */);
47758
+ }
47493
47759
  const anchor = { zone: expandedZone, cell: { col: anchorCol, row: anchorRow } };
47494
47760
  return this.processEvent({
47495
47761
  mode: "updateAnchor",
@@ -47510,6 +47776,22 @@
47510
47776
  mode: "newAnchor",
47511
47777
  });
47512
47778
  }
47779
+ /**
47780
+ * Triggered on mouse up to finalize the selection.
47781
+ * - If the current anchor zone already exists in the selection, it will be removed.
47782
+ * - If the anchor zone overlaps with existing zones, it will be split into
47783
+ * multiple non-overlapping parts.
47784
+ */
47785
+ commitSelection() {
47786
+ return this.processEvent({
47787
+ options: {
47788
+ scrollIntoView: false,
47789
+ unbounded: true,
47790
+ },
47791
+ anchor: this.anchor,
47792
+ mode: "commitSelection",
47793
+ });
47794
+ }
47513
47795
  /**
47514
47796
  * Increase or decrease the size of the current anchor zone.
47515
47797
  * The anchor cell remains where it is. It's the opposite side
@@ -51024,8 +51306,8 @@
51024
51306
 
51025
51307
 
51026
51308
  __info__.version = "19.1.0-alpha.3";
51027
- __info__.date = "2025-11-24T07:52:34.809Z";
51028
- __info__.hash = "e232982";
51309
+ __info__.date = "2025-12-02T05:34:07.213Z";
51310
+ __info__.hash = "04cf666";
51029
51311
 
51030
51312
 
51031
51313
  })(this.o_spreadsheet_engine = this.o_spreadsheet_engine || {});