@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.
@@ -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 19.1.0-alpha.13
6
- * @date 2025-11-24T07:53:14.109Z
7
- * @hash e232982
5
+ * @version 19.1.0-alpha.14
6
+ * @date 2025-12-02T05:34:46.718Z
7
+ * @hash 04cf666
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -2148,6 +2148,47 @@
2148
2148
  return newZones;
2149
2149
  }
2150
2150
  }
2151
+ /**
2152
+ * Splits zone z2 by removing the overlapping zone z1 (fully inside z2).
2153
+ * Returns the remaining parts of z2 that don't overlap with z1.
2154
+ *
2155
+ * Diagram:
2156
+ * ┌──────────── z2 ─────────────┐
2157
+ * │ 1 │
2158
+ * │--------─────────────--------│
2159
+ * │ 2 | z1 | 3 │
2160
+ * │--------─────────────--------│
2161
+ * │ 4 │
2162
+ * └─────────────────────────────┘
2163
+ *
2164
+ * Input:
2165
+ * z1 = { top: 2, bottom: 3, left: 2, right: 3 }
2166
+ * z2 = { top: 1, bottom: 4, left: 1, right: 4 }
2167
+ *
2168
+ * Output:
2169
+ * [
2170
+ * { top: 4, bottom: 4, left: 1, right: 4 }, // bottom
2171
+ * { top: 2, bottom: 3, left: 4, right: 4 }, // right
2172
+ * { top: 2, bottom: 3, left: 1, right: 1 }, // left
2173
+ * { top: 1, bottom: 1, left: 1, right: 4 } // top
2174
+ * ]
2175
+ */
2176
+ function splitZone(z1, z2) {
2177
+ const zones = [];
2178
+ if (z1.bottom < z2.bottom) {
2179
+ zones.push({ ...z2, top: z1.bottom + 1 });
2180
+ }
2181
+ if (z1.right < z2.right) {
2182
+ zones.push({ ...z2, left: z1.right + 1, top: z1.top, bottom: z1.bottom });
2183
+ }
2184
+ if (z1.left > z2.left) {
2185
+ zones.push({ ...z2, right: z1.left - 1, top: z1.top, bottom: z1.bottom });
2186
+ }
2187
+ if (z1.top > z2.top) {
2188
+ zones.push({ ...z2, bottom: z1.top - 1 });
2189
+ }
2190
+ return zones;
2191
+ }
2151
2192
 
2152
2193
  function transformZone(zone, executed) {
2153
2194
  if (executed.type === "REMOVE_COLUMNS_ROWS") {
@@ -4385,8 +4426,56 @@
4385
4426
  return isMatrix(arg) && !isSingleElementMatrix(arg);
4386
4427
  }
4387
4428
 
4429
+ function stackHorizontally(ranges, options) {
4430
+ const matrices = ranges.map(toMatrix);
4431
+ const nbRowsArr = matrices.map((m) => m?.[0]?.length ?? 0);
4432
+ const nbRows = Math.max(...nbRowsArr);
4433
+ if (options?.requireSameRowCount) {
4434
+ const firstLength = nbRowsArr[0];
4435
+ if (nbRowsArr.some((len) => len !== firstLength)) {
4436
+ return new EvaluationError(_t$1("All ranges in [[FUNCTION_NAME]] must have the same number of columns (got %s).", nbRowsArr.join(", ")));
4437
+ }
4438
+ }
4439
+ const result = [];
4440
+ for (const matrix of matrices) {
4441
+ for (let col = 0; col < matrix.length; col++) {
4442
+ // Fill with nulls if needed
4443
+ const array = Array(nbRows).fill({ value: null });
4444
+ for (let row = 0; row < matrix[col].length; row++) {
4445
+ array[row] = matrix[col][row];
4446
+ }
4447
+ result.push(array);
4448
+ }
4449
+ }
4450
+ return result;
4451
+ }
4452
+ function stackVertically(ranges, options) {
4453
+ const matrices = ranges.map(toMatrix);
4454
+ const nbColsArr = matrices.map((m) => m?.length ?? 0);
4455
+ const nbCols = Math.max(...nbColsArr);
4456
+ if (options?.requireSameColCount) {
4457
+ const firstLength = nbColsArr[0];
4458
+ if (nbColsArr.some((len) => len !== firstLength)) {
4459
+ return new EvaluationError(_t$1("All ranges in [[FUNCTION_NAME]] must have the same number of columns (got %s).", nbColsArr.join(", ")));
4460
+ }
4461
+ }
4462
+ const nbRows = matrices.reduce((acc, m) => acc + (m?.[0]?.length ?? 0), 0);
4463
+ const result = generateMatrix(nbCols, nbRows, () => ({
4464
+ value: null,
4465
+ }));
4466
+ let currentRow = 0;
4467
+ for (const matrix of matrices) {
4468
+ for (let col = 0; col < matrix.length; col++) {
4469
+ for (let row = 0; row < matrix[col].length; row++) {
4470
+ result[col][currentRow + row] = matrix[col][row];
4471
+ }
4472
+ }
4473
+ currentRow += matrix[0]?.length ?? 0;
4474
+ }
4475
+ return result;
4476
+ }
4388
4477
  // -----------------------------------------------------------------------------
4389
- // ARRAY_CONSTRAIN
4478
+ // ARRAY.CONSTRAIN
4390
4479
  // -----------------------------------------------------------------------------
4391
4480
  const ARRAY_CONSTRAIN = {
4392
4481
  description: _t$1("Returns a result array constrained to a specific width and height."),
@@ -4412,6 +4501,30 @@
4412
4501
  isExported: false,
4413
4502
  };
4414
4503
  // -----------------------------------------------------------------------------
4504
+ // ARRAY.LITERAL
4505
+ // -----------------------------------------------------------------------------
4506
+ const ARRAY_LITERAL = {
4507
+ description: _t$1("Appends ranges vertically and in sequence to return a larger array. All ranges must have the same number of columns."),
4508
+ args: [arg("range (any, range<any>, repeating)", _t$1("The range to be appended."))],
4509
+ compute: function (...ranges) {
4510
+ return stackVertically(ranges, { requireSameColCount: true });
4511
+ },
4512
+ isExported: false,
4513
+ hidden: true,
4514
+ };
4515
+ // -----------------------------------------------------------------------------
4516
+ // ARRAY.ROW
4517
+ // -----------------------------------------------------------------------------
4518
+ const ARRAY_ROW = {
4519
+ description: _t$1("Appends ranges horizontally and in sequence to return a larger array. All ranges must have the same number of rows."),
4520
+ args: [arg("range (any, range<any>, repeating)", _t$1("The range to be appended."))],
4521
+ compute: function (...ranges) {
4522
+ return stackHorizontally(ranges, { requireSameRowCount: true });
4523
+ },
4524
+ isExported: false,
4525
+ hidden: true,
4526
+ };
4527
+ // -----------------------------------------------------------------------------
4415
4528
  // CHOOSECOLS
4416
4529
  // -----------------------------------------------------------------------------
4417
4530
  const CHOOSECOLS = {
@@ -4557,20 +4670,7 @@
4557
4670
  description: _t$1("Appends ranges horizontally and in sequence to return a larger array."),
4558
4671
  args: [arg("range (any, range<any>, repeating)", _t$1("The range to be appended."))],
4559
4672
  compute: function (...ranges) {
4560
- const nbRows = Math.max(...ranges.map((r) => r?.[0]?.length ?? 0));
4561
- const result = [];
4562
- for (const range of ranges) {
4563
- const _range = toMatrix(range);
4564
- for (let col = 0; col < _range.length; col++) {
4565
- //TODO: fill with #N/A for unavailable values instead of zeroes
4566
- const array = Array(nbRows).fill({ value: null });
4567
- for (let row = 0; row < _range[col].length; row++) {
4568
- array[row] = _range[col][row];
4569
- }
4570
- result.push(array);
4571
- }
4572
- }
4573
- return result;
4673
+ return stackHorizontally(ranges);
4574
4674
  },
4575
4675
  isExported: true,
4576
4676
  };
@@ -4829,22 +4929,7 @@
4829
4929
  description: _t$1("Appends ranges vertically and in sequence to return a larger array."),
4830
4930
  args: [arg("range (any, range<any>, repeating)", _t$1("The range to be appended."))],
4831
4931
  compute: function (...ranges) {
4832
- const nbColumns = Math.max(...ranges.map((range) => toMatrix(range).length));
4833
- const nbRows = ranges.reduce((acc, range) => acc + toMatrix(range)[0].length, 0);
4834
- const result = Array(nbColumns)
4835
- .fill([])
4836
- .map(() => Array(nbRows).fill({ value: 0 })); // TODO fill with #N/A
4837
- let currentRow = 0;
4838
- for (const range of ranges) {
4839
- const _array = toMatrix(range);
4840
- for (let col = 0; col < _array.length; col++) {
4841
- for (let row = 0; row < _array[col].length; row++) {
4842
- result[col][currentRow + row] = _array[col][row];
4843
- }
4844
- }
4845
- currentRow += _array[0].length;
4846
- }
4847
- return result;
4932
+ return stackVertically(ranges);
4848
4933
  },
4849
4934
  isExported: true,
4850
4935
  };
@@ -4904,6 +4989,8 @@
4904
4989
  var array = /*#__PURE__*/Object.freeze({
4905
4990
  __proto__: null,
4906
4991
  ARRAY_CONSTRAIN: ARRAY_CONSTRAIN,
4992
+ ARRAY_LITERAL: ARRAY_LITERAL,
4993
+ ARRAY_ROW: ARRAY_ROW,
4907
4994
  CHOOSECOLS: CHOOSECOLS,
4908
4995
  CHOOSEROWS: CHOOSEROWS,
4909
4996
  EXPAND: EXPAND,
@@ -6370,7 +6457,6 @@
6370
6457
  "REDO",
6371
6458
  "ADD_MERGE",
6372
6459
  "REMOVE_MERGE",
6373
- "DUPLICATE_SHEET",
6374
6460
  "UPDATE_LOCALE",
6375
6461
  "ADD_PIVOT",
6376
6462
  "UPDATE_PIVOT",
@@ -9875,7 +9961,9 @@
9875
9961
  while (!chars.isOver()) {
9876
9962
  let token = tokenizeNewLine(chars) ||
9877
9963
  tokenizeSpace(chars) ||
9964
+ tokenizeArrayRowSeparator(chars, locale) ||
9878
9965
  tokenizeArgsSeparator(chars, locale) ||
9966
+ tokenizeBraces(chars) ||
9879
9967
  tokenizeParenthesis(chars) ||
9880
9968
  tokenizeOperator(chars) ||
9881
9969
  tokenizeString$1(chars) ||
@@ -9908,6 +9996,17 @@
9908
9996
  }
9909
9997
  return null;
9910
9998
  }
9999
+ const braces = {
10000
+ "{": { type: "LEFT_BRACE", value: "{" },
10001
+ "}": { type: "RIGHT_BRACE", value: "}" },
10002
+ };
10003
+ function tokenizeBraces(chars) {
10004
+ if (chars.current === "{" || chars.current === "}") {
10005
+ const value = chars.shift();
10006
+ return braces[value];
10007
+ }
10008
+ return null;
10009
+ }
9911
10010
  function tokenizeArgsSeparator(chars, locale) {
9912
10011
  if (chars.current === locale.formulaArgSeparator) {
9913
10012
  const value = chars.shift();
@@ -9916,6 +10015,20 @@
9916
10015
  }
9917
10016
  return null;
9918
10017
  }
10018
+ function tokenizeArrayRowSeparator(chars, locale) {
10019
+ // The array row separator is used in array literals to separate rows.
10020
+ // It is not explicitly defined in locales, but depends on the formulaArgSeparator.
10021
+ // Example: {1,2,3;4,5,6} — here, ';' separates rows and ',' separates columns.
10022
+ const rowSeparator = locale.formulaArgSeparator === ";" ? "\\" : ";";
10023
+ if (!rowSeparator) {
10024
+ return null;
10025
+ }
10026
+ if (chars.current === rowSeparator) {
10027
+ chars.shift();
10028
+ return { type: "ARRAY_ROW_SEPARATOR", value: rowSeparator };
10029
+ }
10030
+ return null;
10031
+ }
9919
10032
  function tokenizeOperator(chars) {
9920
10033
  for (const op of OPERATORS) {
9921
10034
  if (chars.currentStartsWith(op)) {
@@ -10157,6 +10270,9 @@
10157
10270
  else if (token.type === "ARG_SEPARATOR") {
10158
10271
  localizedFormula += toLocale.formulaArgSeparator;
10159
10272
  }
10273
+ else if (token.type === "ARRAY_ROW_SEPARATOR") {
10274
+ localizedFormula += toLocale.formulaArgSeparator === ";" ? "\\" : ";";
10275
+ }
10160
10276
  else {
10161
10277
  localizedFormula += token.value;
10162
10278
  }
@@ -11400,7 +11516,6 @@
11400
11516
  // -----------------------------------------------------------------------------
11401
11517
  const FILTER = {
11402
11518
  description: _t$1("Returns a filtered version of the source range, returning only rows or columns that meet the specified conditions."),
11403
- // TODO modify args description when vectorization on formulas is available
11404
11519
  args: [
11405
11520
  arg("range (any, range<any>)", _t$1("The data to be filtered.")),
11406
11521
  arg("condition (boolean, range<boolean>, repeating)", _t$1("Column or row containing true or false values corresponding to the range.")),
@@ -17638,6 +17753,8 @@
17638
17753
  tokenStartIndex: current.tokenIndex,
17639
17754
  tokenEndIndex: rightParen.tokenIndex,
17640
17755
  };
17756
+ case "LEFT_BRACE":
17757
+ return parseArrayLiteral(tokens, current);
17641
17758
  case "OPERATOR":
17642
17759
  const operator = current.value;
17643
17760
  if (UNARY_OPERATORS_PREFIX.includes(operator)) {
@@ -17691,6 +17808,34 @@
17691
17808
  }
17692
17809
  return token;
17693
17810
  }
17811
+ function parseArrayLiteral(tokens, leftBrace) {
17812
+ const rows = [];
17813
+ let currentRow = [parseExpression(tokens)]; // there must be at least one element
17814
+ while (tokens.current?.type !== "RIGHT_BRACE") {
17815
+ const nextToken = tokens.shift();
17816
+ if (!nextToken) {
17817
+ throw new BadExpressionError(_t$1("Missing closing brace"));
17818
+ }
17819
+ else if (nextToken.type === "ARG_SEPARATOR") {
17820
+ currentRow.push(parseExpression(tokens));
17821
+ }
17822
+ else if (nextToken.type === "ARRAY_ROW_SEPARATOR") {
17823
+ rows.push(currentRow);
17824
+ currentRow = [parseExpression(tokens)];
17825
+ }
17826
+ else {
17827
+ throw new BadExpressionError(_t$1("Unexpected token: %s", nextToken.value));
17828
+ }
17829
+ }
17830
+ const rightBrace = consumeOrThrow(tokens, "RIGHT_BRACE", _t$1("Missing closing brace"));
17831
+ rows.push(currentRow);
17832
+ return {
17833
+ type: "ARRAY",
17834
+ value: rows,
17835
+ tokenStartIndex: leftBrace.tokenIndex,
17836
+ tokenEndIndex: rightBrace.tokenIndex,
17837
+ };
17838
+ }
17694
17839
  function parseExpression(tokens, parent_priority = 0) {
17695
17840
  if (tokens.length === 0) {
17696
17841
  throw new BadExpressionError();
@@ -17781,6 +17926,13 @@
17781
17926
  yield* astIterator(arg);
17782
17927
  }
17783
17928
  break;
17929
+ case "ARRAY":
17930
+ for (const row of ast.value) {
17931
+ for (const cell of row) {
17932
+ yield* astIterator(cell);
17933
+ }
17934
+ }
17935
+ break;
17784
17936
  case "UNARY_OPERATION":
17785
17937
  yield* astIterator(ast.operand);
17786
17938
  break;
@@ -17798,6 +17950,11 @@
17798
17950
  ...ast,
17799
17951
  args: ast.args.map((child) => mapAst(child, fn)),
17800
17952
  };
17953
+ case "ARRAY":
17954
+ return {
17955
+ ...ast,
17956
+ value: ast.value.map((row) => row.map((cell) => mapAst(cell, fn))),
17957
+ };
17801
17958
  case "UNARY_OPERATION":
17802
17959
  return {
17803
17960
  ...ast,
@@ -20253,6 +20410,30 @@ stores.inject(MyMetaStore, storeInstance);
20253
20410
  }
20254
20411
  }
20255
20412
 
20413
+ function getCalendarChartController() {
20414
+ return class CalendarChartController extends window.Chart.BarController {
20415
+ static id = "calendar";
20416
+ static defaults = {
20417
+ ...window.Chart?.BarController.defaults,
20418
+ dataElementType: "bar",
20419
+ animations: {
20420
+ numbers: { type: "number", properties: [] }, // Disable number animations (width, height, ...)
20421
+ },
20422
+ };
20423
+ updateElements(rects, start, count, mode) {
20424
+ super.updateElements(rects, start, count, mode);
20425
+ // Remove the element background at the start of an animation
20426
+ const chartBackground = this.chart.config.options?.chartBackground;
20427
+ const backgroundColor = chartBackground || "#ffffff";
20428
+ for (let i = start; i < start + count; i++) {
20429
+ if (mode === "reset") {
20430
+ this.updateElement(rects[i], i, { options: { backgroundColor } }, mode);
20431
+ }
20432
+ }
20433
+ }
20434
+ };
20435
+ }
20436
+
20256
20437
  /** This is a chartJS plugin that will draw the heatmap colorscale at the chart legend position */
20257
20438
  const chartColorScalePlugin = {
20258
20439
  id: "chartColorScalePlugin",
@@ -31588,6 +31769,9 @@ stores.inject(MyMetaStore, storeInstance);
31588
31769
  }));
31589
31770
  }
31590
31771
  function getTreeMapColorScale(tree, coloringOption) {
31772
+ if (tree.length === 0) {
31773
+ return undefined;
31774
+ }
31591
31775
  const treeNodesByLevel = pyramidizeTree(tree);
31592
31776
  const nodes = treeNodesByLevel[treeNodesByLevel.length - 1];
31593
31777
  const minValue = Math.min(...nodes.map((node) => node.value));
@@ -31843,6 +32027,10 @@ stores.inject(MyMetaStore, storeInstance);
31843
32027
  register: (Chart) => Chart.register(chartColorScalePlugin),
31844
32028
  unregister: (Chart) => Chart.unregister(chartColorScalePlugin),
31845
32029
  });
32030
+ chartJsExtensionRegistry.add("calendarController", {
32031
+ register: (Chart) => Chart.register(getCalendarChartController()),
32032
+ unregister: (Chart) => Chart.unregister(getCalendarChartController()),
32033
+ });
31846
32034
  class ChartJsComponent extends owl.Component {
31847
32035
  static template = "o-spreadsheet-ChartJsComponent";
31848
32036
  static props = {
@@ -33859,6 +34047,23 @@ stores.inject(MyMetaStore, storeInstance);
33859
34047
  code.append(...args);
33860
34048
  const fnName = ast.value.toUpperCase();
33861
34049
  return code.return(`ctx['${fnName}'](${args.map((arg) => arg.returnExpression)})`);
34050
+ case "ARRAY": {
34051
+ // a literal array is compiled into function calls
34052
+ const arrayFunctionCall = {
34053
+ type: "FUNCALL",
34054
+ value: "ARRAY.LITERAL",
34055
+ args: ast.value.map((row) => ({
34056
+ type: "FUNCALL",
34057
+ value: "ARRAY.ROW",
34058
+ args: row,
34059
+ tokenStartIndex: 0,
34060
+ tokenEndIndex: 0,
34061
+ })),
34062
+ tokenStartIndex: 0,
34063
+ tokenEndIndex: 0,
34064
+ };
34065
+ return compileAST(arrayFunctionCall);
34066
+ }
33862
34067
  case "UNARY_OPERATION": {
33863
34068
  const fnName = UNARY_OPERATOR_MAP[ast.value];
33864
34069
  const operand = compileAST(ast.operand, false, false).assignResultToVariable();
@@ -40336,6 +40541,26 @@ stores.inject(MyMetaStore, storeInstance);
40336
40541
  }
40337
40542
  }
40338
40543
 
40544
+ function schemeToColorScale(scheme) {
40545
+ const colors = COLORSCHEMES[scheme];
40546
+ return colors === undefined
40547
+ ? undefined
40548
+ : {
40549
+ minColor: colors[0],
40550
+ midColor: colors.length === 3 ? colors[1] : undefined,
40551
+ maxColor: colors[colors.length - 1],
40552
+ };
40553
+ }
40554
+
40555
+ const SunburstChartDefaults = {
40556
+ showValues: false,
40557
+ showLabels: true,
40558
+ valuesDesign: {
40559
+ align: "center",
40560
+ fontSize: 13,
40561
+ },
40562
+ };
40563
+
40339
40564
  /**
40340
40565
  * parses a formula (as a string) into the same formula,
40341
40566
  * but with the references to other cells extracted
@@ -40980,13 +41205,22 @@ stores.inject(MyMetaStore, storeInstance);
40980
41205
  return data;
40981
41206
  },
40982
41207
  })
40983
- .add("19.2", {
41208
+ .add("19.1.0", {
40984
41209
  migrate(data) {
40985
41210
  for (const sheet of data.sheets || []) {
40986
41211
  for (const figure of sheet.figures || []) {
40987
41212
  if (figure.tag === "chart" && figure.data.type === "geo") {
40988
41213
  if ("colorScale" in figure.data && typeof figure.data.colorScale === "string") {
40989
- figure.data.colorScale = COLORSCHEMES[figure.data.colorScale];
41214
+ figure.data.colorScale = schemeToColorScale(figure.data.colorScale);
41215
+ }
41216
+ }
41217
+ if (figure.tag === "carousel") {
41218
+ for (const definition of Object.values(figure.data.chartDefinitions) || []) {
41219
+ if (definition.type === "geo") {
41220
+ if ("colorScale" in definition && typeof definition.colorScale === "string") {
41221
+ definition.colorScale = schemeToColorScale(definition.colorScale);
41222
+ }
41223
+ }
40990
41224
  }
40991
41225
  }
40992
41226
  }
@@ -41249,18 +41483,22 @@ stores.inject(MyMetaStore, storeInstance);
41249
41483
  return messages;
41250
41484
  }
41251
41485
  function fixChartDefinitions(data, initialMessages) {
41486
+ /**
41487
+ * Revisions created after version 18.5.1 contain the full chart definition in the command
41488
+ * if the data was alreay updated to 18.5.1, then those older revision cannot (by definition) be reaplied
41489
+ * and should not be replayed.
41490
+ * FIXME: every command should be versionned when upgraded to allow finer tuning.
41491
+ */
41492
+ if (!data.version || compareVersions(String(data.version), "18.5.1") >= 0) {
41493
+ return initialMessages;
41494
+ }
41252
41495
  const messages = [];
41253
41496
  const map = {};
41254
41497
  for (const sheet of data.sheets || []) {
41255
41498
  sheet.figures?.forEach((figure) => {
41256
41499
  if (figure.tag === "chart") {
41257
41500
  // chart definition
41258
- if (data.version && compareVersions(String(data.version), "18.5.1") <= 0) {
41259
- map[figure.data.chartId] = figure.data;
41260
- }
41261
- else {
41262
- map[figure.id] = figure.data;
41263
- }
41501
+ map[figure.id] = figure.data;
41264
41502
  }
41265
41503
  });
41266
41504
  }
@@ -44300,7 +44538,8 @@ stores.inject(MyMetaStore, storeInstance);
44300
44538
  }
44301
44539
  break;
44302
44540
  case "DUPLICATE_SHEET": {
44303
- for (const figureId in this.figures[cmd.sheetId]) {
44541
+ for (const figure of this.getFigures(cmd.sheetId)) {
44542
+ const figureId = figure.id;
44304
44543
  const fig = this.figures[cmd.sheetId]?.[figureId];
44305
44544
  if (!fig) {
44306
44545
  continue;
@@ -46633,10 +46872,17 @@ stores.inject(MyMetaStore, storeInstance);
46633
46872
  if (!pivot) {
46634
46873
  continue;
46635
46874
  }
46636
- for (const measure of pivot.definition.measures) {
46875
+ const def = deepCopy$1(pivot.definition);
46876
+ for (const measure of def.measures) {
46637
46877
  if (measure.computedBy?.formula === formulaString) {
46638
- const measureIndex = pivot.definition.measures.indexOf(measure);
46639
- this.history.update("pivots", pivotId, "definition", "measures", measureIndex, "computedBy", { formula: newFormulaString, sheetId });
46878
+ const measureIndex = def.measures.indexOf(measure);
46879
+ if (measureIndex !== -1) {
46880
+ def.measures[measureIndex].computedBy = {
46881
+ formula: newFormulaString,
46882
+ sheetId,
46883
+ };
46884
+ }
46885
+ this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
46640
46886
  }
46641
46887
  }
46642
46888
  }
@@ -47668,6 +47914,9 @@ stores.inject(MyMetaStore, storeInstance);
47668
47914
  const { sheetId, zone } = definition.dataSet;
47669
47915
  const range = this.getters.getRangeFromZone(sheetId, zone);
47670
47916
  const adaptedRange = adaptPivotRange(range, applyChange);
47917
+ if (adaptedRange === range) {
47918
+ return;
47919
+ }
47671
47920
  const dataSet = adaptedRange && {
47672
47921
  sheetId: adaptedRange.sheetId,
47673
47922
  zone: adaptedRange.zone,
@@ -52473,6 +52722,11 @@ stores.inject(MyMetaStore, storeInstance);
52473
52722
  }
52474
52723
  }
52475
52724
  return wrapInParentheses(concat(splitArgsWithCommas(docs)), ast.value);
52725
+ case "ARRAY": {
52726
+ const rowDocs = ast.value.map((row) => concat(row.map((value, index) => index === 0 ? astToDoc(value) : concat([", ", line(), astToDoc(value)]))));
52727
+ const body = concat(rowDocs.map((doc, index) => (index === 0 ? group(doc) : concat(["; ", line(), group(doc)]))));
52728
+ return wrapInBraces(body);
52729
+ }
52476
52730
  case "UNARY_OPERATION":
52477
52731
  const operandDoc = astToDoc(ast.operand);
52478
52732
  const needParenthesis = ast.postfix
@@ -52516,6 +52770,9 @@ stores.inject(MyMetaStore, storeInstance);
52516
52770
  }
52517
52771
  return group(concat(docToConcat));
52518
52772
  }
52773
+ function wrapInBraces(doc) {
52774
+ return group(concat(["{", nest(1, concat([line(), doc])), line(), "}"]));
52775
+ }
52519
52776
  /**
52520
52777
  * Converts an ast formula to the corresponding string
52521
52778
  */
@@ -52543,6 +52800,10 @@ stores.inject(MyMetaStore, storeInstance);
52543
52800
  ? `(${astToFormula(ast.operand)})`
52544
52801
  : astToFormula(ast.operand);
52545
52802
  return ast.value + rightOperand;
52803
+ case "ARRAY":
52804
+ return ("{" +
52805
+ ast.value.map((row) => row.map((cell) => astToFormula(cell)).join(",")).join(";") +
52806
+ "}");
52546
52807
  case "BIN_OPERATION":
52547
52808
  const leftOperation = leftOperandNeedsParenthesis(ast)
52548
52809
  ? `(${astToFormula(ast.left)})`
@@ -54107,7 +54368,6 @@ stores.inject(MyMetaStore, storeInstance);
54107
54368
  pivotRegistry.add("SPREADSHEET", {
54108
54369
  ui: SpreadsheetPivot,
54109
54370
  definition: SpreadsheetPivotRuntimeDefinition,
54110
- externalData: false,
54111
54371
  dateGranularities: [...dateGranularities],
54112
54372
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
54113
54373
  isMeasureCandidate: (field) => field.type !== "boolean",
@@ -54149,9 +54409,7 @@ stores.inject(MyMetaStore, storeInstance);
54149
54409
  handle(cmd) {
54150
54410
  if (invalidateEvaluationCommands.has(cmd.type)) {
54151
54411
  for (const pivotId of this.getters.getPivotIds()) {
54152
- if (!pivotRegistry.get(this.getters.getPivotCoreDefinition(pivotId).type).externalData) {
54153
- this.setupPivot(pivotId, { recreate: true });
54154
- }
54412
+ this.setupPivot(pivotId, { recreate: true });
54155
54413
  }
54156
54414
  }
54157
54415
  switch (cmd.type) {
@@ -54365,7 +54623,7 @@ stores.inject(MyMetaStore, storeInstance);
54365
54623
  pivot.init({ reload: true });
54366
54624
  }
54367
54625
  setupPivot(pivotId, { recreate } = { recreate: false }) {
54368
- const definition = this.getters.getPivotCoreDefinition(pivotId);
54626
+ const definition = deepCopy$1(this.getters.getPivotCoreDefinition(pivotId));
54369
54627
  if (!(pivotId in this.pivots)) {
54370
54628
  const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
54371
54629
  this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
@@ -57459,6 +57717,15 @@ stores.inject(MyMetaStore, storeInstance);
57459
57717
  return "InvalidFigureId" /* CommandResult.InvalidFigureId */;
57460
57718
  }
57461
57719
  return "Success" /* CommandResult.Success */;
57720
+ case "DUPLICATE_CAROUSEL_CHART":
57721
+ if (!this.getters.doesCarouselExist(cmd.carouselId) ||
57722
+ !this.getters
57723
+ .getCarousel(cmd.carouselId)
57724
+ .items.some((item) => item.type === "chart" && item.chartId === cmd.chartId) ||
57725
+ this.getters.getChart(cmd.duplicatedChartId)) {
57726
+ return "InvalidFigureId" /* CommandResult.InvalidFigureId */;
57727
+ }
57728
+ return "Success" /* CommandResult.Success */;
57462
57729
  case "ADD_NEW_CHART_TO_CAROUSEL":
57463
57730
  if (!this.getters.doesCarouselExist(cmd.figureId)) {
57464
57731
  return "InvalidFigureId" /* CommandResult.InvalidFigureId */;
@@ -57483,6 +57750,9 @@ stores.inject(MyMetaStore, storeInstance);
57483
57750
  case "ADD_FIGURE_CHART_TO_CAROUSEL":
57484
57751
  this.addFigureChartToCarousel(cmd.carouselFigureId, cmd.chartFigureId, cmd.sheetId);
57485
57752
  break;
57753
+ case "DUPLICATE_CAROUSEL_CHART":
57754
+ this.duplicateCarouselChart(cmd);
57755
+ break;
57486
57756
  case "UPDATE_CAROUSEL_ACTIVE_ITEM":
57487
57757
  this.carouselStates[cmd.figureId] = this.getCarouselItemId(cmd.item);
57488
57758
  break;
@@ -57621,6 +57891,29 @@ stores.inject(MyMetaStore, storeInstance);
57621
57891
  });
57622
57892
  this.dispatch("DELETE_FIGURE", { sheetId, figureId: chartFigureId });
57623
57893
  }
57894
+ duplicateCarouselChart({ carouselId, chartId, sheetId, duplicatedChartId, }) {
57895
+ const chart = this.getters.getChart(chartId);
57896
+ if (!chart) {
57897
+ return;
57898
+ }
57899
+ const carousel = this.getters.getCarousel(carouselId);
57900
+ const duplicatedItemIndex = carousel.items.findIndex((item) => item.type === "chart" && item.chartId === chartId);
57901
+ if (duplicatedItemIndex === -1) {
57902
+ return;
57903
+ }
57904
+ this.dispatch("CREATE_CHART", {
57905
+ chartId: duplicatedChartId,
57906
+ figureId: carouselId,
57907
+ sheetId,
57908
+ definition: chart.getDefinition(),
57909
+ });
57910
+ const carouselItems = insertItemsAtIndex(carousel.items, [{ type: "chart", chartId: duplicatedChartId }], duplicatedItemIndex + 1);
57911
+ this.dispatch("UPDATE_CAROUSEL", {
57912
+ sheetId,
57913
+ figureId: carouselId,
57914
+ definition: { ...carousel, items: carouselItems },
57915
+ });
57916
+ }
57624
57917
  getCarouselItemId(item) {
57625
57918
  return item.type === "chart" ? item.chartId : "carouselDataView";
57626
57919
  }
@@ -58833,25 +59126,43 @@ stores.inject(MyMetaStore, storeInstance);
58833
59126
  return "Success" /* CommandResult.Success */;
58834
59127
  }
58835
59128
  handleEvent(event) {
58836
- const anchor = event.anchor;
58837
- let zones = [];
59129
+ let anchor = event.anchor;
59130
+ let zones = [...this.gridSelection.zones];
58838
59131
  this.isUnbounded = event.options?.unbounded || false;
58839
59132
  switch (event.mode) {
58840
59133
  case "overrideSelection":
58841
59134
  zones = [anchor.zone];
58842
59135
  break;
58843
59136
  case "updateAnchor":
58844
- zones = [...this.gridSelection.zones];
58845
59137
  const index = zones.findIndex((z) => isEqual(z, event.previousAnchor.zone));
58846
59138
  if (index >= 0) {
58847
59139
  zones[index] = anchor.zone;
58848
59140
  }
58849
59141
  break;
58850
59142
  case "newAnchor":
58851
- zones = [...this.gridSelection.zones, anchor.zone];
59143
+ zones.push(anchor.zone);
59144
+ break;
59145
+ case "commitSelection":
59146
+ const zoneToSplit = zones.find((zone) => isZoneInside(anchor.zone, zone) && !isEqual(anchor.zone, zone));
59147
+ const removeFullAnchor = zones.filter((zone) => isEqual(anchor.zone, zone)).length > 1;
59148
+ if (removeFullAnchor && zones.length > 2) {
59149
+ zones = zones.filter((zone) => !isEqual(zone, anchor.zone));
59150
+ }
59151
+ else if (zoneToSplit) {
59152
+ const splittedZones = splitZone(anchor.zone, zoneToSplit);
59153
+ zones = zones
59154
+ .filter((z) => !isEqual(z, anchor.zone) && !isEqual(z, zoneToSplit))
59155
+ .concat(splittedZones);
59156
+ }
59157
+ zones = uniqueZones(zones);
59158
+ const lastZone = zones[zones.length - 1];
59159
+ anchor = {
59160
+ cell: { col: lastZone.left, row: lastZone.top },
59161
+ zone: lastZone,
59162
+ };
58852
59163
  break;
58853
59164
  }
58854
- this.setSelectionMixin(event.anchor, zones);
59165
+ this.setSelectionMixin(anchor, zones);
58855
59166
  /** Any change to the selection has to be reflected in the selection processor. */
58856
59167
  this.selection.resetDefaultAnchor(this, deepCopy$1(this.gridSelection.anchor));
58857
59168
  const { col, row } = this.gridSelection.anchor.cell;
@@ -59131,7 +59442,7 @@ stores.inject(MyMetaStore, storeInstance);
59131
59442
  setSelectionMixin(anchor, zones) {
59132
59443
  const { anchor: clippedAnchor, zones: clippedZones } = this.clipSelection(this.getters.getActiveSheetId(), { anchor, zones });
59133
59444
  this.gridSelection.anchor = clippedAnchor;
59134
- this.gridSelection.zones = uniqueZones(clippedZones);
59445
+ this.gridSelection.zones = clippedZones;
59135
59446
  }
59136
59447
  /**
59137
59448
  * Change the anchor of the selection active cell to an absolute col and row index.
@@ -61089,6 +61400,9 @@ stores.inject(MyMetaStore, storeInstance);
61089
61400
  bottom: Math.max(anchorRow, row),
61090
61401
  };
61091
61402
  const expandedZone = this.getters.expandZone(sheetId, zone);
61403
+ if (isEqual(this.anchor.zone, expandedZone)) {
61404
+ return new DispatchResult("NoChanges" /* CommandResult.NoChanges */);
61405
+ }
61092
61406
  const anchor = { zone: expandedZone, cell: { col: anchorCol, row: anchorRow } };
61093
61407
  return this.processEvent({
61094
61408
  mode: "updateAnchor",
@@ -61109,6 +61423,22 @@ stores.inject(MyMetaStore, storeInstance);
61109
61423
  mode: "newAnchor",
61110
61424
  });
61111
61425
  }
61426
+ /**
61427
+ * Triggered on mouse up to finalize the selection.
61428
+ * - If the current anchor zone already exists in the selection, it will be removed.
61429
+ * - If the anchor zone overlaps with existing zones, it will be split into
61430
+ * multiple non-overlapping parts.
61431
+ */
61432
+ commitSelection() {
61433
+ return this.processEvent({
61434
+ options: {
61435
+ scrollIntoView: false,
61436
+ unbounded: true,
61437
+ },
61438
+ anchor: this.anchor,
61439
+ mode: "commitSelection",
61440
+ });
61441
+ }
61112
61442
  /**
61113
61443
  * Increase or decrease the size of the current anchor zone.
61114
61444
  * The anchor cell remains where it is. It's the opposite side
@@ -64473,17 +64803,6 @@ stores.inject(MyMetaStore, storeInstance);
64473
64803
  return command;
64474
64804
  }
64475
64805
 
64476
- function schemeToColorScale(scheme) {
64477
- const colors = COLORSCHEMES[scheme];
64478
- return colors === undefined
64479
- ? undefined
64480
- : {
64481
- minColor: colors[0],
64482
- midColor: colors.length === 3 ? colors[1] : undefined,
64483
- maxColor: colors[colors.length - 1],
64484
- };
64485
- }
64486
-
64487
64806
  const availableConditionalFormatOperators = new Set([
64488
64807
  "containsText",
64489
64808
  "notContainsText",
@@ -65634,15 +65953,6 @@ stores.inject(MyMetaStore, storeInstance);
65634
65953
  };
65635
65954
  }
65636
65955
 
65637
- const SunburstChartDefaults = {
65638
- showValues: false,
65639
- showLabels: true,
65640
- valuesDesign: {
65641
- align: "center",
65642
- fontSize: 13,
65643
- },
65644
- };
65645
-
65646
65956
  function getChartShowValues(definition, args) {
65647
65957
  const { axisFormats, locale } = args;
65648
65958
  return {
@@ -68646,10 +68956,16 @@ stores.inject(MyMetaStore, storeInstance);
68646
68956
  return menu.children(this.env).some((child) => child.isVisible(this.env));
68647
68957
  }
68648
68958
  isEnabled(menu) {
68649
- if (menu.isEnabled(this.env)) {
68650
- return this.env.model.getters.isReadonly() ? menu.isReadonlyAllowed : true;
68959
+ const children = menu.children?.(this.env);
68960
+ if (children.length) {
68961
+ return children.some((child) => this.isEnabled(child));
68962
+ }
68963
+ else {
68964
+ if (menu.isEnabled(this.env)) {
68965
+ return this.env.model.getters.isReadonly() ? menu.isReadonlyAllowed : true;
68966
+ }
68967
+ return false;
68651
68968
  }
68652
- return false;
68653
68969
  }
68654
68970
  get menuStyle() {
68655
68971
  return this.props.width ? cssPropertiesToCss({ width: this.props.width + "px" }) : "";
@@ -69154,17 +69470,15 @@ stores.inject(MyMetaStore, storeInstance);
69154
69470
  }
69155
69471
  }
69156
69472
  onMouseOver(menu, ev) {
69157
- if (this.isEnabled(menu)) {
69158
- if (this.isParentMenu(this.subMenu, menu)) {
69159
- this.openingTimeOut.clear();
69160
- return;
69161
- }
69162
- const currentTarget = ev.currentTarget;
69163
- if (this.isRoot(menu)) {
69164
- this.openingTimeOut.schedule(() => {
69165
- this.openSubMenu(menu, currentTarget);
69166
- }, TIMEOUT_DELAY);
69167
- }
69473
+ if (this.isParentMenu(this.subMenu, menu)) {
69474
+ this.openingTimeOut.clear();
69475
+ return;
69476
+ }
69477
+ const currentTarget = ev.currentTarget;
69478
+ if (this.isRoot(menu)) {
69479
+ this.openingTimeOut.schedule(() => {
69480
+ this.openSubMenu(menu, currentTarget);
69481
+ }, TIMEOUT_DELAY);
69168
69482
  }
69169
69483
  }
69170
69484
  onMouseOverMainMenu() {
@@ -71591,16 +71905,19 @@ stores.inject(MyMetaStore, storeInstance);
71591
71905
  if (displayBrackets) {
71592
71906
  result.push({ content: "]" });
71593
71907
  }
71594
- result.push({ content: argSeparator + "[" });
71595
- for (let idx = 0; idx < repeatingArgNames.length; idx++) {
71596
- const name = repeatingArgNames[idx];
71597
- result.push({ content: name + ((repeatingArgGroupIndex ?? 0) + 2) });
71598
- // Add separator after each element except the last
71599
- if (idx < repeatingArgNames.length - 1) {
71600
- result.push({ content: argSeparator });
71908
+ if (functionDescription.nbrArgRepeating <= 1) {
71909
+ result.push({ content: argSeparator + "[" });
71910
+ for (let idx = 0; idx < repeatingArgNames.length; idx++) {
71911
+ const name = repeatingArgNames[idx];
71912
+ result.push({ content: name + ((repeatingArgGroupIndex ?? 0) + 2) });
71913
+ // Add separator after each element except the last
71914
+ if (idx < repeatingArgNames.length - 1) {
71915
+ result.push({ content: argSeparator });
71916
+ }
71601
71917
  }
71918
+ result.push({ content: "]" });
71602
71919
  }
71603
- result.push({ content: "]" + argSeparator + "... " });
71920
+ result.push({ content: argSeparator + "... " });
71604
71921
  // Skip the processed repeating args
71605
71922
  i += functionDescription.nbrArgRepeating - 1;
71606
71923
  }
@@ -71677,6 +71994,7 @@ stores.inject(MyMetaStore, storeInstance);
71677
71994
  };
71678
71995
  DOMFocusableElementStore;
71679
71996
  composerRef = owl.useRef("o_composer");
71997
+ containerRef = owl.useRef("composerContainer");
71680
71998
  contentHelper = new ContentEditableHelper(this.composerRef.el);
71681
71999
  composerState = owl.useState({
71682
72000
  positionStart: 0,
@@ -71725,7 +72043,8 @@ stores.inject(MyMetaStore, storeInstance);
71725
72043
  assistantStyle["max-height"] = `${availableSpaceAbove - CLOSE_ICON_RADIUS}px`;
71726
72044
  // render top
71727
72045
  // We compensate 2 px of margin on the assistant style + 1px for design reasons
71728
- assistantStyle.transform = `translate(0, calc(-100% - ${cellHeight + 3}px))`;
72046
+ assistantStyle.top = `-3px`;
72047
+ assistantStyle.transform = `translate(0, -100%)`;
71729
72048
  }
71730
72049
  if (cellX + ASSISTANT_WIDTH > this.props.delimitation.width) {
71731
72050
  // render left
@@ -71984,6 +72303,9 @@ stores.inject(MyMetaStore, storeInstance);
71984
72303
  this.props.composerStore.stopEdition();
71985
72304
  return;
71986
72305
  }
72306
+ if (this.containerRef.el?.contains(ev.relatedTarget)) {
72307
+ return;
72308
+ }
71987
72309
  if (target.attributes.getNamedItem("composerFocusableElement")) {
71988
72310
  this.contentHelper.el.focus();
71989
72311
  return;
@@ -72730,7 +73052,7 @@ stores.inject(MyMetaStore, storeInstance);
72730
73052
  }
72731
73053
  this.selectionStart = start;
72732
73054
  this.selectionEnd = end;
72733
- this.editionMode = "editing";
73055
+ this.stopComposerRangeSelection();
72734
73056
  this.computeFormulaCursorContext();
72735
73057
  this.computeParenthesisRelatedToCursor();
72736
73058
  this.updateAutoCompleteProvider();
@@ -73443,7 +73765,7 @@ stores.inject(MyMetaStore, storeInstance);
73443
73765
  let count = tokenIdex;
73444
73766
  let currentToken = tokenAtCursor;
73445
73767
  // check previous token
73446
- while (!["ARG_SEPARATOR", "LEFT_PAREN", "OPERATOR"].includes(currentToken.type) ||
73768
+ while (!["ARG_SEPARATOR", "ARRAY_ROW_SEPARATOR", "LEFT_PAREN", "LEFT_BRACE", "OPERATOR"].includes(currentToken.type) ||
73447
73769
  POSTFIX_UNARY_OPERATORS.includes(currentToken.value)) {
73448
73770
  if (currentToken.type !== "SPACE" || count < 1) {
73449
73771
  return false;
@@ -73455,7 +73777,13 @@ stores.inject(MyMetaStore, storeInstance);
73455
73777
  currentToken = this.currentTokens[count];
73456
73778
  // check next token
73457
73779
  while (currentToken &&
73458
- !["ARG_SEPARATOR", "RIGHT_PAREN", "OPERATOR"].includes(currentToken.type)) {
73780
+ ![
73781
+ "ARG_SEPARATOR",
73782
+ "ARRAY_ROW_SEPARATOR",
73783
+ "RIGHT_PAREN",
73784
+ "RIGHT_BRACE",
73785
+ "OPERATOR",
73786
+ ].includes(currentToken.type)) {
73459
73787
  if (currentToken.type !== "SPACE") {
73460
73788
  return false;
73461
73789
  }
@@ -77094,18 +77422,15 @@ stores.inject(MyMetaStore, storeInstance);
77094
77422
  execute: (env) => env.model.dispatch("UNFREEZE_ROWS", {
77095
77423
  sheetId: env.model.getters.getActiveSheetId(),
77096
77424
  }),
77097
- isReadonlyAllowed: true,
77098
77425
  isVisible: (env) => !!env.model.getters.getPaneDivisions(env.model.getters.getActiveSheetId()).ySplit,
77099
77426
  };
77100
77427
  const freezeFirstRow = {
77101
77428
  name: _t$1("1 row"),
77102
77429
  execute: (env) => interactiveFreezeColumnsRows(env, "ROW", 1),
77103
- isReadonlyAllowed: true,
77104
77430
  };
77105
77431
  const freezeSecondRow = {
77106
77432
  name: _t$1("2 rows"),
77107
77433
  execute: (env) => interactiveFreezeColumnsRows(env, "ROW", 2),
77108
- isReadonlyAllowed: true,
77109
77434
  };
77110
77435
  const freezeCurrentRow = {
77111
77436
  name: _t$1("Up to current row"),
@@ -77113,25 +77438,21 @@ stores.inject(MyMetaStore, storeInstance);
77113
77438
  const { bottom } = env.model.getters.getSelectedZone();
77114
77439
  interactiveFreezeColumnsRows(env, "ROW", bottom + 1);
77115
77440
  },
77116
- isReadonlyAllowed: true,
77117
77441
  };
77118
77442
  const unFreezeCols = {
77119
77443
  name: _t$1("No columns"),
77120
77444
  execute: (env) => env.model.dispatch("UNFREEZE_COLUMNS", {
77121
77445
  sheetId: env.model.getters.getActiveSheetId(),
77122
77446
  }),
77123
- isReadonlyAllowed: true,
77124
77447
  isVisible: (env) => !!env.model.getters.getPaneDivisions(env.model.getters.getActiveSheetId()).xSplit,
77125
77448
  };
77126
77449
  const freezeFirstCol = {
77127
77450
  name: _t$1("1 column"),
77128
77451
  execute: (env) => interactiveFreezeColumnsRows(env, "COL", 1),
77129
- isReadonlyAllowed: true,
77130
77452
  };
77131
77453
  const freezeSecondCol = {
77132
77454
  name: _t$1("2 columns"),
77133
77455
  execute: (env) => interactiveFreezeColumnsRows(env, "COL", 2),
77134
- isReadonlyAllowed: true,
77135
77456
  };
77136
77457
  const freezeCurrentCol = {
77137
77458
  name: _t$1("Up to current column"),
@@ -77139,7 +77460,6 @@ stores.inject(MyMetaStore, storeInstance);
77139
77460
  const { right } = env.model.getters.getSelectedZone();
77140
77461
  interactiveFreezeColumnsRows(env, "COL", right + 1);
77141
77462
  },
77142
- isReadonlyAllowed: true,
77143
77463
  };
77144
77464
  const viewGridlines = {
77145
77465
  name: _t$1("Gridlines"),
@@ -79299,6 +79619,8 @@ stores.inject(MyMetaStore, storeInstance);
79299
79619
  let startingY;
79300
79620
  let scrollDirection = "all";
79301
79621
  const getters = env.model.getters;
79622
+ const blockKeyboard = (ev) => ev.preventDefault();
79623
+ const cleanUpBlockKeyboard = () => removeEventListener("keydown", blockKeyboard, { capture: true });
79302
79624
  let cleanUpFns = [];
79303
79625
  const cleanUp = () => {
79304
79626
  clearTimeout(timeOutId);
@@ -79411,7 +79733,9 @@ stores.inject(MyMetaStore, storeInstance);
79411
79733
  };
79412
79734
  pointerMoveCallback = onPointerMove;
79413
79735
  pointerUpCallback = onPointerUp;
79414
- cleanUpFns.push(startDnd(pointerMoveHandler, pointerUpHandler));
79736
+ // block keyboard events during pointer interaction to avoid conflicts
79737
+ addEventListener("keydown", blockKeyboard, { capture: true });
79738
+ cleanUpFns.push(startDnd(pointerMoveHandler, pointerUpHandler), cleanUpBlockKeyboard);
79415
79739
  };
79416
79740
  owl.onWillUnmount(() => {
79417
79741
  cleanUp();
@@ -81292,6 +81616,10 @@ stores.inject(MyMetaStore, storeInstance);
81292
81616
  }
81293
81617
  }
81294
81618
  _computeGrabDisplay(zoomedMouseEvent) {
81619
+ if (isCtrlKey(zoomedMouseEvent.ev)) {
81620
+ this.state.waitingForMove = false;
81621
+ return;
81622
+ }
81295
81623
  const index = this._getElementIndex(this._getEvOffset(zoomedMouseEvent));
81296
81624
  const activeElements = this._getActiveElements();
81297
81625
  const selectedZoneStart = this._getSelectedZoneStart();
@@ -81385,7 +81713,7 @@ stores.inject(MyMetaStore, storeInstance);
81385
81713
  this._selectElement(index, false);
81386
81714
  return;
81387
81715
  }
81388
- if (this.state.waitingForMove) {
81716
+ if (!isCtrlKey(ev) && this.state.waitingForMove) {
81389
81717
  if (!this.env.model.getters.isGridSelectionActive()) {
81390
81718
  this._selectElement(index, false);
81391
81719
  }
@@ -81467,15 +81795,13 @@ stores.inject(MyMetaStore, storeInstance);
81467
81795
  }
81468
81796
  };
81469
81797
  const mouseUpSelect = () => {
81798
+ this.env.model.selection.commitSelection();
81470
81799
  this.state.isSelecting = false;
81471
81800
  this.lastSelectedElementIndex = null;
81472
81801
  this._computeGrabDisplay(zoomedMouseEvent);
81473
81802
  };
81474
81803
  this.dragNDropGrid.start(zoomedMouseEvent, mouseMoveSelect, mouseUpSelect);
81475
81804
  }
81476
- onMouseUp(ev) {
81477
- this.lastSelectedElementIndex = null;
81478
- }
81479
81805
  onContextMenu(ev) {
81480
81806
  ev.preventDefault();
81481
81807
  const index = this._getElementIndex(this._getEvOffset(withZoom(this.env, ev)));
@@ -83211,14 +83537,14 @@ stores.inject(MyMetaStore, storeInstance);
83211
83537
  }
83212
83538
  onMouseDown(ev) {
83213
83539
  // Stop the event if the input is not focused, we handle everything in onMouseUp
83214
- if (ev.target !== document.activeElement) {
83540
+ if (ev.target !== document.activeElement && this.props.selectContentOnFocus) {
83215
83541
  ev.preventDefault();
83216
83542
  ev.stopPropagation();
83217
83543
  }
83218
83544
  }
83219
83545
  onMouseUp(ev) {
83220
83546
  const target = ev.target;
83221
- if (target !== document.activeElement) {
83547
+ if (target !== document.activeElement && this.props.selectContentOnFocus) {
83222
83548
  target.focus();
83223
83549
  if (this.props.selectContentOnFocus) {
83224
83550
  target.select();
@@ -83354,7 +83680,9 @@ stores.inject(MyMetaStore, storeInstance);
83354
83680
  inputRef = owl.useRef("inputNumber");
83355
83681
  rootEditorRef = owl.useRef("NumberEditor");
83356
83682
  valueListRef = owl.useRef("numberList");
83683
+ DOMFocusableElementStore;
83357
83684
  setup() {
83685
+ this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
83358
83686
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
83359
83687
  owl.onWillUpdateProps((nextProps) => {
83360
83688
  if (this.inputRef.el && document.activeElement !== this.inputRef.el) {
@@ -83421,6 +83749,13 @@ stores.inject(MyMetaStore, storeInstance);
83421
83749
  }
83422
83750
  this.props.onToggle?.();
83423
83751
  }
83752
+ if (ev.key === "Tab") {
83753
+ ev.preventDefault();
83754
+ ev.stopPropagation();
83755
+ this.closeList();
83756
+ this.DOMFocusableElementStore.focus();
83757
+ return;
83758
+ }
83424
83759
  }
83425
83760
  }
83426
83761
 
@@ -83710,6 +84045,16 @@ stores.inject(MyMetaStore, storeInstance);
83710
84045
  chartId: item.chartId,
83711
84046
  });
83712
84047
  }
84048
+ duplicateCarouselChart(item) {
84049
+ if (item.type !== "chart")
84050
+ return;
84051
+ this.env.model.dispatch("DUPLICATE_CAROUSEL_CHART", {
84052
+ sheetId: this.carouselSheetId,
84053
+ carouselId: this.props.figureId,
84054
+ chartId: item.chartId,
84055
+ duplicatedChartId: this.env.model.uuidGenerator.smallUuid(),
84056
+ });
84057
+ }
83713
84058
  onDragHandleMouseDown(item, event) {
83714
84059
  if (event.button !== 0)
83715
84060
  return;
@@ -83795,6 +84140,11 @@ stores.inject(MyMetaStore, storeInstance);
83795
84140
  execute: () => this.popOutCarouselItem(item),
83796
84141
  icon: "o-spreadsheet-Icon.EXTERNAL",
83797
84142
  });
84143
+ actions.push({
84144
+ name: _t$1("Duplicate chart"),
84145
+ execute: () => this.duplicateCarouselChart(item),
84146
+ icon: "o-spreadsheet-Icon.COPY",
84147
+ });
83798
84148
  }
83799
84149
  actions.push({
83800
84150
  name: _t$1("Delete item"),
@@ -86206,8 +86556,9 @@ stores.inject(MyMetaStore, storeInstance);
86206
86556
  : baseString + minColor + ", " + maxColor + ")";
86207
86557
  }
86208
86558
  else if (rule.type === "DataBarRule") {
86209
- const color = colorNumberToHex(rule.color);
86210
- return `background-image: linear-gradient(to right, ${color} 50%, white 50%)`;
86559
+ const barColor = colorNumberToHex(rule.color);
86560
+ const gradient = `background-image: linear-gradient(to right, ${barColor} 50%, white 50%)`;
86561
+ return `${gradient}; color: ${TEXT_BODY};`;
86211
86562
  }
86212
86563
  return "";
86213
86564
  }
@@ -90657,6 +91008,7 @@ stores.inject(MyMetaStore, storeInstance);
90657
91008
  }
90658
91009
  };
90659
91010
  const onMouseUp = () => {
91011
+ this.env.model.selection.commitSelection();
90660
91012
  if (this.paintFormatStore.isActive) {
90661
91013
  this.paintFormatStore.pasteFormat(this.env.model.getters.getSelectedZones());
90662
91014
  }
@@ -92513,7 +92865,7 @@ stores.inject(MyMetaStore, storeInstance);
92513
92865
  if (customFormats.length > 0) {
92514
92866
  customFormats[customFormats.length - 1].separator = true;
92515
92867
  }
92516
- return createActions([...numberFormatMenuRegistry.getAll(), ...customFormats]);
92868
+ return [...numberFormatMenuRegistry.getAll(), ...customFormats];
92517
92869
  },
92518
92870
  ],
92519
92871
  };
@@ -92562,7 +92914,6 @@ stores.inject(MyMetaStore, storeInstance);
92562
92914
  .add("file", {
92563
92915
  name: _t$1("File"),
92564
92916
  sequence: 10,
92565
- isReadonlyAllowed: true,
92566
92917
  })
92567
92918
  .addChild("settings", ["file"], {
92568
92919
  name: _t$1("Settings"),
@@ -92577,7 +92928,6 @@ stores.inject(MyMetaStore, storeInstance);
92577
92928
  .add("edit", {
92578
92929
  name: _t$1("Edit"),
92579
92930
  sequence: 20,
92580
- isReadonlyAllowed: true,
92581
92931
  })
92582
92932
  .addChild("undo", ["edit"], {
92583
92933
  ...undo,
@@ -92662,7 +93012,6 @@ stores.inject(MyMetaStore, storeInstance);
92662
93012
  .add("view", {
92663
93013
  name: _t$1("View"),
92664
93014
  sequence: 30,
92665
- isReadonlyAllowed: true,
92666
93015
  })
92667
93016
  .addChild("unfreeze_panes", ["view"], {
92668
93017
  ...unFreezePane,
@@ -92759,7 +93108,6 @@ stores.inject(MyMetaStore, storeInstance);
92759
93108
  .add("insert", {
92760
93109
  name: _t$1("Insert"),
92761
93110
  sequence: 40,
92762
- isReadonlyAllowed: true,
92763
93111
  })
92764
93112
  .addChild("insert_row", ["insert"], {
92765
93113
  ...insertRow,
@@ -92874,7 +93222,6 @@ stores.inject(MyMetaStore, storeInstance);
92874
93222
  .add("format", {
92875
93223
  name: _t$1("Format"),
92876
93224
  sequence: 50,
92877
- isReadonlyAllowed: true,
92878
93225
  })
92879
93226
  .addChild("format_number", ["format"], {
92880
93227
  ...formatNumberMenuItemSpec,
@@ -92967,7 +93314,6 @@ stores.inject(MyMetaStore, storeInstance);
92967
93314
  .add("data", {
92968
93315
  name: _t$1("Data"),
92969
93316
  sequence: 60,
92970
- isReadonlyAllowed: true,
92971
93317
  })
92972
93318
  .addChild("sort_range", ["data"], {
92973
93319
  ...sortRange,
@@ -94037,16 +94383,18 @@ stores.inject(MyMetaStore, storeInstance);
94037
94383
  get clickableCells() {
94038
94384
  const cells = [];
94039
94385
  const getters = this.getters;
94040
- const sheetId = getters.getActiveSheetId();
94041
94386
  for (const position of this.getters.getVisibleCellPositions()) {
94042
94387
  const item = this.getClickableItem(position);
94043
94388
  if (!item) {
94044
94389
  continue;
94045
94390
  }
94046
94391
  const title = typeof item.title === "function" ? item.title(position, getters) : item.title;
94047
- const zone = getters.expandZone(sheetId, positionToZone(position));
94392
+ const rect = this.getClickableCellRect(position);
94393
+ if (!rect) {
94394
+ continue;
94395
+ }
94048
94396
  cells.push({
94049
- coordinates: getters.getVisibleRect(zone),
94397
+ coordinates: rect,
94050
94398
  position,
94051
94399
  action: item.execute,
94052
94400
  title: title || "",
@@ -94056,6 +94404,31 @@ stores.inject(MyMetaStore, storeInstance);
94056
94404
  }
94057
94405
  return cells;
94058
94406
  }
94407
+ getClickableCellRect(position) {
94408
+ const zone = this.getters.expandZone(position.sheetId, positionToZone(position));
94409
+ const clickableRect = this.getters.getVisibleRect(zone);
94410
+ const icons = this.getters.getCellIcons(position);
94411
+ const iconsAtPosition = {
94412
+ center: icons.find((icon) => icon.horizontalAlign === "center"),
94413
+ left: icons.find((icon) => icon.horizontalAlign === "left"),
94414
+ right: icons.find((icon) => icon.horizontalAlign === "right"),
94415
+ };
94416
+ if (iconsAtPosition.center?.onClick) {
94417
+ return undefined;
94418
+ }
94419
+ if (iconsAtPosition.right?.onClick) {
94420
+ const cellRect = this.getters.getRect(zone);
94421
+ const iconRect = this.getters.getCellIconRect(iconsAtPosition.right, cellRect);
94422
+ clickableRect.width -= iconRect.width + iconsAtPosition.right.margin;
94423
+ }
94424
+ if (iconsAtPosition.left?.onClick) {
94425
+ const cellRect = this.getters.getRect(zone);
94426
+ const iconRect = this.getters.getCellIconRect(iconsAtPosition.left, cellRect);
94427
+ clickableRect.x += iconRect.width + iconsAtPosition.left.margin;
94428
+ clickableRect.width -= iconRect.width + iconsAtPosition.left.margin;
94429
+ }
94430
+ return clickableRect;
94431
+ }
94059
94432
  }
94060
94433
 
94061
94434
  class SpreadsheetDashboard extends owl.Component {
@@ -94647,7 +95020,7 @@ stores.inject(MyMetaStore, storeInstance);
94647
95020
  height: this.focus === "inactive" ? "26px" : "fit-content",
94648
95021
  "max-height": `130px`,
94649
95022
  }),
94650
- showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
95023
+ showAssistant: false, // Hide assistant in small composer as it gets cropped ATM
94651
95024
  placeholder: this.composerStore.placeholder,
94652
95025
  };
94653
95026
  }
@@ -95887,12 +96260,10 @@ stores.inject(MyMetaStore, storeInstance);
95887
96260
  }
95888
96261
 
95889
96262
  function checkDateGranularity(definition) {
95890
- if (definition.horizontalGroupBy &&
95891
- !CALENDAR_CHART_GRANULARITIES.includes(definition.horizontalGroupBy)) {
96263
+ if (!CALENDAR_CHART_GRANULARITIES.includes(definition.horizontalGroupBy)) {
95892
96264
  return "InvalidChartDefinition" /* CommandResult.InvalidChartDefinition */;
95893
96265
  }
95894
- if (definition.verticalGroupBy &&
95895
- !CALENDAR_CHART_GRANULARITIES.includes(definition.verticalGroupBy)) {
96266
+ if (!CALENDAR_CHART_GRANULARITIES.includes(definition.verticalGroupBy)) {
95896
96267
  return "InvalidChartDefinition" /* CommandResult.InvalidChartDefinition */;
95897
96268
  }
95898
96269
  return "Success" /* CommandResult.Success */;
@@ -95917,8 +96288,8 @@ stores.inject(MyMetaStore, storeInstance);
95917
96288
  this.showValues = definition.showValues;
95918
96289
  this.colorScale = definition.colorScale;
95919
96290
  this.axesDesign = definition.axesDesign;
95920
- this.horizontalGroupBy = definition.horizontalGroupBy ?? "day_of_week";
95921
- this.verticalGroupBy = definition.verticalGroupBy ?? "month_number";
96291
+ this.horizontalGroupBy = definition.horizontalGroupBy;
96292
+ this.verticalGroupBy = definition.verticalGroupBy;
95922
96293
  this.legendPosition = definition.legendPosition;
95923
96294
  this.missingValueColor = definition.missingValueColor;
95924
96295
  }
@@ -95943,6 +96314,8 @@ stores.inject(MyMetaStore, storeInstance);
95943
96314
  showValues: context.showValues,
95944
96315
  axesDesign: context.axesDesign,
95945
96316
  legendPosition,
96317
+ horizontalGroupBy: "day_of_week",
96318
+ verticalGroupBy: "month_number",
95946
96319
  };
95947
96320
  }
95948
96321
  getContextCreation() {
@@ -96009,7 +96382,7 @@ stores.inject(MyMetaStore, storeInstance);
96009
96382
  const chartData = getCalendarChartData(definition, chart.dataSets, chart.labelRange, getters);
96010
96383
  const { labels, datasets } = getCalendarChartDatasetAndLabels(definition, chartData);
96011
96384
  const config = {
96012
- type: "bar",
96385
+ type: "calendar",
96013
96386
  data: {
96014
96387
  labels,
96015
96388
  datasets,
@@ -96026,6 +96399,7 @@ stores.inject(MyMetaStore, storeInstance);
96026
96399
  chartShowValuesPlugin: getCalendarChartShowValues(definition, chartData),
96027
96400
  chartColorScalePlugin: getCalendarColorScale(definition, chartData),
96028
96401
  },
96402
+ chartBackground: definition.background || BACKGROUND_CHART_COLOR,
96029
96403
  },
96030
96404
  };
96031
96405
  return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
@@ -98430,6 +98804,7 @@ stores.inject(MyMetaStore, storeInstance);
98430
98804
  isNumber: isNumber$1,
98431
98805
  isDateTime,
98432
98806
  createCustomFields,
98807
+ schemeToColorScale,
98433
98808
  };
98434
98809
  const links = {
98435
98810
  isMarkdownLink,
@@ -98599,9 +98974,9 @@ stores.inject(MyMetaStore, storeInstance);
98599
98974
  exports.tokenize = tokenize;
98600
98975
 
98601
98976
 
98602
- __info__.version = "19.1.0-alpha.13";
98603
- __info__.date = "2025-11-24T07:53:14.109Z";
98604
- __info__.hash = "e232982";
98977
+ __info__.version = "19.1.0-alpha.14";
98978
+ __info__.date = "2025-12-02T05:34:46.718Z";
98979
+ __info__.hash = "04cf666";
98605
98980
 
98606
98981
 
98607
98982
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);