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