@odoo/o-spreadsheet 18.1.0-alpha.1 → 18.1.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 18.1.0-alpha.1
6
- * @date 2024-10-14T07:53:17.717Z
7
- * @hash e9ce3aa
5
+ * @version 18.1.0-alpha.2
6
+ * @date 2024-10-24T08:53:21.828Z
7
+ * @hash 2a01250
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -330,6 +330,7 @@
330
330
  const DEFAULT_SCORECARD_BASELINE_COLOR_UP = "#43C5B1";
331
331
  const DEFAULT_SCORECARD_BASELINE_COLOR_DOWN = "#EA6175";
332
332
  const LINE_FILL_TRANSPARENCY = 0.4;
333
+ const DEFAULT_WINDOW_SIZE = 2;
333
334
  // session
334
335
  const DEBOUNCE_TIME = 200;
335
336
  const MESSAGE_VERSION = 1;
@@ -375,7 +376,7 @@
375
376
  bandedRows: true,
376
377
  bandedColumns: false,
377
378
  styleId: "TableStyleMedium5",
378
- automaticAutofill: true,
379
+ automaticAutofill: false,
379
380
  };
380
381
  const DEFAULT_CURRENCY = {
381
382
  symbol: "$",
@@ -591,7 +592,7 @@
591
592
  */
592
593
  function parseSheetUrl(sheetLink) {
593
594
  if (sheetLink.startsWith(O_SPREADSHEET_LINK_PREFIX)) {
594
- return sheetLink.substr(O_SPREADSHEET_LINK_PREFIX.length);
595
+ return sheetLink.slice(O_SPREADSHEET_LINK_PREFIX.length);
595
596
  }
596
597
  throw new Error(`${sheetLink} is not a valid sheet link`);
597
598
  }
@@ -3156,8 +3157,7 @@
3156
3157
  const p2 = pMinus + pNumber + pCurrencyFormat;
3157
3158
  const p3 = pCurrencyFormat + pMinus + pNumber;
3158
3159
  const pNumberExp = "^(?:(?:" + [p1, p2, p3].join(")|(?:") + "))$";
3159
- const numberRegexp = new RegExp(pNumberExp, "i");
3160
- return numberRegexp;
3160
+ return new RegExp(pNumberExp, "i");
3161
3161
  });
3162
3162
  /**
3163
3163
  * Return true if the argument is a "number string".
@@ -5831,8 +5831,7 @@
5831
5831
  textWidthCache[font] = {};
5832
5832
  }
5833
5833
  if (textWidthCache[font][text] === undefined) {
5834
- const textWidth = context.measureText(text).width;
5835
- textWidthCache[font][text] = textWidth;
5834
+ textWidthCache[font][text] = context.measureText(text).width;
5836
5835
  }
5837
5836
  return textWidthCache[font][text];
5838
5837
  }
@@ -6084,7 +6083,7 @@
6084
6083
  else {
6085
6084
  // mainly for jest and other browsers that do not have the crypto functionality
6086
6085
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
6087
- var r = (Math.random() * 16) | 0, v = c === "x" ? r : (r & 0x3) | 0x8;
6086
+ const r = (Math.random() * 16) | 0, v = c === "x" ? r : (r & 0x3) | 0x8;
6088
6087
  return v.toString(16);
6089
6088
  });
6090
6089
  }
@@ -6581,10 +6580,9 @@
6581
6580
  return literal;
6582
6581
  }
6583
6582
  const decimalNumberRegex = getDecimalNumberRegex(DEFAULT_LOCALE);
6584
- const localized = literal.replace(decimalNumberRegex, (match) => {
6583
+ return literal.replace(decimalNumberRegex, (match) => {
6585
6584
  return match.replace(".", locale.decimalSeparator);
6586
6585
  });
6587
- return localized;
6588
6586
  }
6589
6587
  /**
6590
6588
  * Change a literal string from its canonical form (en_US locale) to the given locale. Also convert date string.
@@ -7069,6 +7067,21 @@
7069
7067
  });
7070
7068
  return newY.length === newX.length ? newY : transposeMatrix(newY);
7071
7069
  }
7070
+ function getMovingAverageValues(dataset, windowSize = DEFAULT_WINDOW_SIZE) {
7071
+ const values = [];
7072
+ // Fill the starting values with null until we have a full window
7073
+ for (let i = 0; i < windowSize - 1; i++) {
7074
+ values.push(null);
7075
+ }
7076
+ for (let i = 0; i <= dataset.length - windowSize; i++) {
7077
+ let sum = 0;
7078
+ for (let j = i; j < i + windowSize; j++) {
7079
+ sum += dataset[j];
7080
+ }
7081
+ values.push(sum / windowSize);
7082
+ }
7083
+ return values;
7084
+ }
7072
7085
 
7073
7086
  const PREVIOUS_VALUE = "(previous)";
7074
7087
  const NEXT_VALUE = "(next)";
@@ -7604,8 +7617,7 @@
7604
7617
  return 0;
7605
7618
  }
7606
7619
  const nums = keys.map((id) => parseInt(id, 10));
7607
- const max = Math.max(...nums);
7608
- return max;
7620
+ return Math.max(...nums);
7609
7621
  }
7610
7622
  const ALL_PERIODS = {
7611
7623
  year: _t("Year"),
@@ -8458,10 +8470,6 @@
8458
8470
  tableZone &&
8459
8471
  zones.some((z) => isZoneInside(tableZone, z))) {
8460
8472
  copiedTablesIds.add(table.id);
8461
- const values = [];
8462
- for (const col of range(tableZone.left, tableZone.right + 1)) {
8463
- values.push(this.getters.getFilterHiddenValues({ sheetId, col, row: tableZone.top }));
8464
- }
8465
8473
  copiedTable = {
8466
8474
  range: coreTable.range.rangeData,
8467
8475
  config: coreTable.config,
@@ -9242,11 +9250,10 @@ stores.inject(MyMetaStore, storeInstance);
9242
9250
  const { x, y } = getters.getMainViewportCoordinates();
9243
9251
  const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
9244
9252
  const { width, height } = getters.getVisibleRect(getters.getActiveMainViewport());
9245
- const position = {
9253
+ return {
9246
9254
  x: x + scrollX + Math.max(0, (width - chartSize.width) / 2),
9247
9255
  y: y + scrollY + Math.max(0, (height - chartSize.height) / 2),
9248
9256
  }; // Position at the center of the scrollable viewport
9249
- return position;
9250
9257
  }
9251
9258
  function getChartAxisTitleRuntime(design) {
9252
9259
  if (design?.title?.text) {
@@ -9363,7 +9370,7 @@ stores.inject(MyMetaStore, storeInstance);
9363
9370
  return {
9364
9371
  ...dataset,
9365
9372
  type: "line",
9366
- xAxisID: TREND_LINE_XAXIS_ID,
9373
+ xAxisID: config.type !== "trailingMovingAverage" ? TREND_LINE_XAXIS_ID : "x",
9367
9374
  label: dataset.label ? _t("Trend line for %s", dataset.label) : "",
9368
9375
  data,
9369
9376
  order: -1,
@@ -9405,6 +9412,9 @@ stores.inject(MyMetaStore, storeInstance);
9405
9412
  case "logarithmic": {
9406
9413
  return predictLinearValues([values], logM([labels]), logM([newLabels]), true)[0];
9407
9414
  }
9415
+ case "trailingMovingAverage": {
9416
+ return getMovingAverageValues(values, config.window);
9417
+ }
9408
9418
  default:
9409
9419
  return [];
9410
9420
  }
@@ -9450,72 +9460,115 @@ stores.inject(MyMetaStore, storeInstance);
9450
9460
  ctx.save();
9451
9461
  ctx.textAlign = "center";
9452
9462
  ctx.textBaseline = "middle";
9453
- ctx.fillStyle = chartFontColor(options.background);
9454
- ctx.strokeStyle = chartFontColor(ctx.fillStyle);
9455
- chart._metasets.forEach(function (dataset) {
9456
- if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
9457
- return; // ignore trend lines
9458
- }
9459
- switch (dataset.type) {
9460
- case "doughnut":
9461
- case "pie": {
9462
- for (let i = 0; i < dataset._parsed.length; i++) {
9463
- const bar = dataset.data[i];
9464
- const { startAngle, endAngle, innerRadius, outerRadius } = bar;
9465
- const midAngle = (startAngle + endAngle) / 2;
9466
- const midRadius = (innerRadius + outerRadius) / 2;
9467
- const x = bar.x + midRadius * Math.cos(midAngle);
9468
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
9469
- ctx.fillStyle = chartFontColor(bar.options.backgroundColor);
9470
- ctx.strokeStyle = chartFontColor(ctx.fillStyle);
9471
- const value = options.callback(dataset._parsed[i]);
9472
- ctx.strokeText(value, x, y);
9473
- ctx.fillText(value, x, y);
9474
- }
9475
- break;
9476
- }
9477
- case "bar":
9478
- case "line": {
9479
- const yOffset = dataset.type === "bar" && !options.horizontal ? 0 : 3;
9480
- const horizontalChart = dataset.type === "bar" && options.horizontal;
9481
- const axisId = horizontalChart ? dataset.xAxisID : dataset.yAxisID;
9482
- for (let i = 0; i < dataset._parsed.length; i++) {
9483
- const point = dataset.data[i];
9484
- const value = options.horizontal ? dataset._parsed[i].x : dataset._parsed[i].y;
9485
- const displayedValue = options.callback(value - 0, axisId);
9486
- let xPosition = 0, yPosition = 0;
9487
- if (options.horizontal) {
9488
- yPosition = point.y;
9489
- if (value < 0) {
9490
- ctx.textAlign = "right";
9491
- xPosition = point.x - yOffset;
9492
- }
9493
- else {
9494
- ctx.textAlign = "left";
9495
- xPosition = point.x + yOffset;
9496
- }
9497
- }
9498
- else {
9499
- xPosition = point.x;
9500
- if (value < 0) {
9501
- ctx.textBaseline = "top";
9502
- yPosition = point.y + yOffset;
9503
- }
9504
- else {
9505
- ctx.textBaseline = "bottom";
9506
- yPosition = point.y - yOffset;
9507
- }
9508
- }
9509
- ctx.strokeText(displayedValue, xPosition, yPosition);
9510
- ctx.fillText(displayedValue, xPosition, yPosition);
9511
- }
9512
- break;
9513
- }
9514
- }
9515
- });
9463
+ ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
9464
+ switch (chart.config.type) {
9465
+ case "pie":
9466
+ case "doughnut":
9467
+ drawPieChartValues(chart, options, ctx);
9468
+ break;
9469
+ case "bar":
9470
+ case "line":
9471
+ options.horizontal
9472
+ ? drawHorizontalBarChartValues(chart, options, ctx)
9473
+ : drawLineOrBarChartValues(chart, options, ctx);
9474
+ break;
9475
+ }
9516
9476
  ctx.restore();
9517
9477
  },
9518
9478
  };
9479
+ function drawTextWithBackground(text, x, y, ctx) {
9480
+ ctx.lineWidth = 3; // Stroke the text with a big lineWidth width to have some kind of background
9481
+ ctx.strokeText(text, x, y);
9482
+ ctx.lineWidth = 1;
9483
+ ctx.fillText(text, x, y);
9484
+ }
9485
+ function drawLineOrBarChartValues(chart, options, ctx) {
9486
+ const yMax = chart.chartArea.bottom;
9487
+ const yMin = chart.chartArea.top;
9488
+ const textsPositions = {};
9489
+ for (const dataset of chart._metasets) {
9490
+ if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
9491
+ return; // ignore trend lines
9492
+ }
9493
+ for (let i = 0; i < dataset._parsed.length; i++) {
9494
+ const value = dataset._parsed[i].y;
9495
+ const displayValue = options.callback(value - 0, dataset.yAxisID);
9496
+ const point = dataset.data[i];
9497
+ const xPosition = point.x;
9498
+ let yPosition = 0;
9499
+ if (chart.config.type === "line") {
9500
+ yPosition = point.y - 10;
9501
+ }
9502
+ else {
9503
+ yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
9504
+ }
9505
+ yPosition = Math.min(yPosition, yMax);
9506
+ yPosition = Math.max(yPosition, yMin);
9507
+ // Avoid overlapping texts with same X
9508
+ if (!textsPositions[xPosition]) {
9509
+ textsPositions[xPosition] = [];
9510
+ }
9511
+ for (const otherPosition of textsPositions[xPosition] || []) {
9512
+ if (Math.abs(otherPosition - yPosition) < 13) {
9513
+ yPosition = otherPosition - 13;
9514
+ }
9515
+ }
9516
+ textsPositions[xPosition].push(yPosition);
9517
+ ctx.fillStyle = point.options.backgroundColor;
9518
+ ctx.strokeStyle = options.background || "#ffffff";
9519
+ drawTextWithBackground(displayValue, xPosition, yPosition, ctx);
9520
+ }
9521
+ }
9522
+ }
9523
+ function drawHorizontalBarChartValues(chart, options, ctx) {
9524
+ const xMax = chart.chartArea.right;
9525
+ const xMin = chart.chartArea.left;
9526
+ const textsPositions = {};
9527
+ for (const dataset of chart._metasets) {
9528
+ if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
9529
+ return; // ignore trend lines
9530
+ }
9531
+ for (let i = 0; i < dataset._parsed.length; i++) {
9532
+ const value = dataset._parsed[i].x;
9533
+ const displayValue = options.callback(value - 0, dataset.xAxisID);
9534
+ const point = dataset.data[i];
9535
+ const yPosition = point.y;
9536
+ let xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
9537
+ xPosition = Math.min(xPosition, xMax);
9538
+ xPosition = Math.max(xPosition, xMin);
9539
+ // Avoid overlapping texts with same Y
9540
+ if (!textsPositions[yPosition]) {
9541
+ textsPositions[yPosition] = [];
9542
+ }
9543
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
9544
+ for (const otherPosition of textsPositions[yPosition]) {
9545
+ if (Math.abs(otherPosition - xPosition) < textWidth) {
9546
+ xPosition = otherPosition + textWidth + 3;
9547
+ }
9548
+ }
9549
+ textsPositions[yPosition].push(xPosition);
9550
+ ctx.fillStyle = point.options.backgroundColor;
9551
+ ctx.strokeStyle = options.background || "#ffffff";
9552
+ drawTextWithBackground(displayValue, xPosition, yPosition, ctx);
9553
+ }
9554
+ }
9555
+ }
9556
+ function drawPieChartValues(chart, options, ctx) {
9557
+ for (const dataset of chart._metasets) {
9558
+ for (let i = 0; i < dataset._parsed.length; i++) {
9559
+ const bar = dataset.data[i];
9560
+ const { startAngle, endAngle, innerRadius, outerRadius } = bar;
9561
+ const midAngle = (startAngle + endAngle) / 2;
9562
+ const midRadius = (innerRadius + outerRadius) / 2;
9563
+ const x = bar.x + midRadius * Math.cos(midAngle);
9564
+ const y = bar.y + midRadius * Math.sin(midAngle) + 7;
9565
+ ctx.fillStyle = chartFontColor(options.background);
9566
+ ctx.strokeStyle = options.background || "#ffffff";
9567
+ const value = options.callback(dataset._parsed[i]);
9568
+ drawTextWithBackground(value, x, y, ctx);
9569
+ }
9570
+ }
9571
+ }
9519
9572
 
9520
9573
  /** This is a chartJS plugin that will draw connector lines between the bars of a Waterfall chart */
9521
9574
  const waterfallLinesPlugin = {
@@ -10293,8 +10346,7 @@ stores.inject(MyMetaStore, storeInstance);
10293
10346
  value = value.slice(index + 1);
10294
10347
  }
10295
10348
  pendingHtmlContent.push({ value });
10296
- const htmlContent = pendingHtmlContent.filter((content) => content.value);
10297
- return htmlContent;
10349
+ return pendingHtmlContent.filter((content) => content.value);
10298
10350
  }
10299
10351
 
10300
10352
  //------------------------------------------------------------------------------
@@ -10698,7 +10750,7 @@ stores.inject(MyMetaStore, storeInstance);
10698
10750
  assertSquareMatrix(_t("The argument square_matrix must have the same number of columns and rows."), _matrix);
10699
10751
  const { inverted } = invertMatrix(_matrix);
10700
10752
  if (!inverted) {
10701
- throw new EvaluationError(_t("The matrix is not invertible."));
10753
+ return new EvaluationError(_t("The matrix is not invertible."));
10702
10754
  }
10703
10755
  return inverted;
10704
10756
  },
@@ -10777,7 +10829,7 @@ stores.inject(MyMetaStore, storeInstance);
10777
10829
  }
10778
10830
  }
10779
10831
  if (!validPairFound) {
10780
- throw new EvaluationError(_t("The arguments array_x and array_y must contain at least one pair of numbers."));
10832
+ return new EvaluationError(_t("The arguments array_x and array_y must contain at least one pair of numbers."));
10781
10833
  }
10782
10834
  return result;
10783
10835
  }
@@ -10858,7 +10910,7 @@ stores.inject(MyMetaStore, storeInstance);
10858
10910
  .flat()
10859
10911
  .filter(shouldKeepValue(_ignore));
10860
10912
  if (result.length === 0) {
10861
- throw new NotAvailableError(_t("No results for the given arguments of TOCOL."));
10913
+ return new NotAvailableError(_t("No results for the given arguments of TOCOL."));
10862
10914
  }
10863
10915
  return [result];
10864
10916
  },
@@ -10879,7 +10931,7 @@ stores.inject(MyMetaStore, storeInstance);
10879
10931
  .filter(shouldKeepValue(_ignore))
10880
10932
  .map((item) => [item]);
10881
10933
  if (result.length === 0 || result[0].length === 0) {
10882
- throw new NotAvailableError(_t("No results for the given arguments of TOROW."));
10934
+ return new NotAvailableError(_t("No results for the given arguments of TOROW."));
10883
10935
  }
10884
10936
  return result;
10885
10937
  },
@@ -11439,7 +11491,7 @@ stores.inject(MyMetaStore, storeInstance);
11439
11491
  * Return error if 'value' is positive.
11440
11492
  * Remove '-?' in the next regex to catch this error.
11441
11493
  */
11442
- assert(() => !!DECIMAL_REPRESENTATION.test(_value), _t("The value (%s) must be a valid base %s representation.", _value, _base.toString()));
11494
+ assert(() => DECIMAL_REPRESENTATION.test(_value), _t("The value (%s) must be a valid base %s representation.", _value, _base.toString()));
11443
11495
  const deci = parseInt(_value, _base);
11444
11496
  assert(() => !isNaN(deci), _t("The value (%s) must be a valid base %s representation.", _value, _base.toString()));
11445
11497
  return deci;
@@ -11707,7 +11759,7 @@ stores.inject(MyMetaStore, storeInstance);
11707
11759
  count += 1;
11708
11760
  }
11709
11761
  if (isEvaluationError(f)) {
11710
- throw j;
11762
+ return j;
11711
11763
  }
11712
11764
  }
11713
11765
  }
@@ -12210,9 +12262,8 @@ stores.inject(MyMetaStore, storeInstance);
12210
12262
  }
12211
12263
  function variance(args, isSample, textAs0, locale) {
12212
12264
  let count = 0;
12213
- let sum = 0;
12214
12265
  const reduceFunction = textAs0 ? reduceNumbersTextAs0 : reduceNumbers;
12215
- sum = reduceFunction(args, (acc, a) => {
12266
+ const sum = reduceFunction(args, (acc, a) => {
12216
12267
  count += 1;
12217
12268
  return acc + a;
12218
12269
  }, 0, locale);
@@ -12609,7 +12660,7 @@ stores.inject(MyMetaStore, storeInstance);
12609
12660
  const flatY = dataY.flat();
12610
12661
  assertSameNumberOfElements(flatX, flatY);
12611
12662
  if (flatX.length === 0) {
12612
- throw new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
12663
+ return new EvaluationError(_t("[[FUNCTION_NAME]] expects non-empty ranges for both parameters."));
12613
12664
  }
12614
12665
  const n = flatX.length;
12615
12666
  let trueN = 0, trueP = 0, falseP = 0, falseN = 0;
@@ -13409,9 +13460,8 @@ stores.inject(MyMetaStore, storeInstance);
13409
13460
  // 4 - return for each database row corresponding, the cells corresponding to the field parameter
13410
13461
  const fieldCol = database[index];
13411
13462
  // Example continuation:: fieldCol = ["C", "j", "k", 7]
13412
- const matchingCells = [...matchingRows].map((x) => fieldCol[x + 1]);
13413
13463
  // Example continuation:: matchingCells = ["j", 7]
13414
- return matchingCells;
13464
+ return [...matchingRows].map((x) => fieldCol[x + 1]);
13415
13465
  }
13416
13466
  const databaseArgs = [
13417
13467
  arg("database (range)", _t("The array or range containing the data to consider, structured in such a way that the first row contains the labels for each column's values.")),
@@ -14596,7 +14646,7 @@ stores.inject(MyMetaStore, storeInstance);
14596
14646
  }
14597
14647
  }
14598
14648
  if (!result.length) {
14599
- throw new NotAvailableError(_t("No match found in FILTER evaluation"));
14649
+ return new NotAvailableError(_t("No match found in FILTER evaluation"));
14600
14650
  }
14601
14651
  return mode === "row" ? transposeMatrix(result) : result;
14602
14652
  },
@@ -17294,7 +17344,7 @@ stores.inject(MyMetaStore, storeInstance);
17294
17344
  argsTokens[argPosition].push({ value: token.value, type: token.type });
17295
17345
  }
17296
17346
  }
17297
- const res = tokens.map((token, i) => {
17347
+ return tokens.map((token, i) => {
17298
17348
  if (!["SPACE", "LEFT_PAREN"].includes(token.type)) {
17299
17349
  functionStarted = "";
17300
17350
  }
@@ -17332,7 +17382,6 @@ stores.inject(MyMetaStore, storeInstance);
17332
17382
  }
17333
17383
  return token;
17334
17384
  });
17335
- return res;
17336
17385
  }
17337
17386
  /**
17338
17387
  * Parse the list of tokens that compose the arguments of a function to
@@ -17788,7 +17837,7 @@ stores.inject(MyMetaStore, storeInstance);
17788
17837
  return result;
17789
17838
  }
17790
17839
  }
17791
- throw new EvaluationError(_t("No match."));
17840
+ return new EvaluationError(_t("No match."));
17792
17841
  },
17793
17842
  isExported: true,
17794
17843
  };
@@ -17952,8 +18001,8 @@ stores.inject(MyMetaStore, storeInstance);
17952
18001
  let cellReference;
17953
18002
  if (_useA1Notation) {
17954
18003
  const rangePart = {
17955
- rowFixed: [1, 2].includes(_absoluteRelativeMode) ? true : false,
17956
- colFixed: [1, 3].includes(_absoluteRelativeMode) ? true : false,
18004
+ rowFixed: [1, 2].includes(_absoluteRelativeMode),
18005
+ colFixed: [1, 3].includes(_absoluteRelativeMode),
17957
18006
  };
17958
18007
  cellReference = toXC(colNumber - 1, rowNumber - 1, rangePart);
17959
18008
  }
@@ -17979,7 +18028,7 @@ stores.inject(MyMetaStore, storeInstance);
17979
18028
  ],
17980
18029
  compute: function (cellReference) {
17981
18030
  if (isEvaluationError(cellReference?.value)) {
17982
- throw cellReference;
18031
+ return cellReference;
17983
18032
  }
17984
18033
  const column = cellReference === undefined
17985
18034
  ? this.__originCellPosition?.col
@@ -17997,7 +18046,7 @@ stores.inject(MyMetaStore, storeInstance);
17997
18046
  args: [arg("range (meta)", _t("The range whose column count will be returned."))],
17998
18047
  compute: function (range) {
17999
18048
  if (isEvaluationError(range?.value)) {
18000
- throw range;
18049
+ return range;
18001
18050
  }
18002
18051
  const zone = toZone(range.value);
18003
18052
  return zone.right - zone.left + 1;
@@ -18077,11 +18126,11 @@ stores.inject(MyMetaStore, storeInstance);
18077
18126
  compute: function (reference, useA1Notation = { value: true }) {
18078
18127
  let _reference = reference?.value?.toString();
18079
18128
  if (!_reference) {
18080
- throw new InvalidReferenceError(_t("Reference should be defined."));
18129
+ return new InvalidReferenceError(_t("Reference should be defined."));
18081
18130
  }
18082
18131
  const _useA1Notation = toBoolean(useA1Notation);
18083
18132
  if (!_useA1Notation) {
18084
- throw new EvaluationError(_t("R1C1 notation is not supported."));
18133
+ return new EvaluationError(_t("R1C1 notation is not supported."));
18085
18134
  }
18086
18135
  const sheetId = this.__originSheetId;
18087
18136
  const originPosition = this.__originCellPosition;
@@ -18093,7 +18142,7 @@ stores.inject(MyMetaStore, storeInstance);
18093
18142
  }
18094
18143
  const range = this.getters.getRangeFromSheetXC(sheetId, _reference);
18095
18144
  if (range === undefined || range.invalidXc || range.invalidSheetName) {
18096
- throw new InvalidReferenceError();
18145
+ return new InvalidReferenceError();
18097
18146
  }
18098
18147
  if (originPosition) {
18099
18148
  this.addDependencies?.(originPosition, [range]);
@@ -18201,7 +18250,7 @@ stores.inject(MyMetaStore, storeInstance);
18201
18250
  ],
18202
18251
  compute: function (cellReference) {
18203
18252
  if (isEvaluationError(cellReference?.value)) {
18204
- throw cellReference;
18253
+ return cellReference;
18205
18254
  }
18206
18255
  const row = cellReference === undefined
18207
18256
  ? this.__originCellPosition?.row
@@ -18219,7 +18268,7 @@ stores.inject(MyMetaStore, storeInstance);
18219
18268
  args: [arg("range (meta)", _t("The range whose row count will be returned."))],
18220
18269
  compute: function (range) {
18221
18270
  if (isEvaluationError(range?.value)) {
18222
- throw range;
18271
+ return range;
18223
18272
  }
18224
18273
  const zone = toZone(range.value);
18225
18274
  return zone.bottom - zone.top + 1;
@@ -18406,11 +18455,11 @@ stores.inject(MyMetaStore, storeInstance);
18406
18455
  const _pivotFormulaId = toString(pivotFormulaId);
18407
18456
  const _rowCount = toNumber(rowCount, this.locale);
18408
18457
  if (_rowCount < 0) {
18409
- throw new EvaluationError(_t("The number of rows must be positive."));
18458
+ return new EvaluationError(_t("The number of rows must be positive."));
18410
18459
  }
18411
18460
  const _columnCount = toNumber(columnCount, this.locale);
18412
18461
  if (_columnCount < 0) {
18413
- throw new EvaluationError(_t("The number of columns must be positive."));
18462
+ return new EvaluationError(_t("The number of columns must be positive."));
18414
18463
  }
18415
18464
  const _includeColumnHeaders = toBoolean(includeColumnHeaders);
18416
18465
  const _includedTotal = toBoolean(includeTotal);
@@ -18609,6 +18658,12 @@ stores.inject(MyMetaStore, storeInstance);
18609
18658
  arg("value2 (any)", _t("The value to test against value1 for equality.")),
18610
18659
  ],
18611
18660
  compute: function (value1, value2) {
18661
+ if (isEvaluationError(value1?.value)) {
18662
+ return value1;
18663
+ }
18664
+ if (isEvaluationError(value2?.value)) {
18665
+ return value2;
18666
+ }
18612
18667
  let _value1 = isEmpty(value1) ? getNeutral[typeof value2?.value] : value1?.value;
18613
18668
  let _value2 = isEmpty(value2) ? getNeutral[typeof value1?.value] : value2?.value;
18614
18669
  if (typeof _value1 === "string") {
@@ -18617,27 +18672,21 @@ stores.inject(MyMetaStore, storeInstance);
18617
18672
  if (typeof _value2 === "string") {
18618
18673
  _value2 = _value2.toUpperCase();
18619
18674
  }
18620
- if (isEvaluationError(_value1)) {
18621
- throw value1;
18622
- }
18623
- if (isEvaluationError(_value2)) {
18624
- throw value2;
18625
- }
18626
- return _value1 === _value2;
18675
+ return { value: _value1 === _value2 };
18627
18676
  },
18628
18677
  };
18629
18678
  // -----------------------------------------------------------------------------
18630
18679
  // GT
18631
18680
  // -----------------------------------------------------------------------------
18632
18681
  function applyRelationalOperator(value1, value2, cb) {
18633
- let _value1 = isEmpty(value1) ? getNeutral[typeof value2?.value] : value1?.value;
18634
- let _value2 = isEmpty(value2) ? getNeutral[typeof value1?.value] : value2?.value;
18635
- if (isEvaluationError(_value1)) {
18636
- throw value1;
18682
+ if (isEvaluationError(value1?.value)) {
18683
+ return value1;
18637
18684
  }
18638
- if (isEvaluationError(_value2)) {
18639
- throw value2;
18685
+ if (isEvaluationError(value2?.value)) {
18686
+ return value2;
18640
18687
  }
18688
+ let _value1 = isEmpty(value1) ? getNeutral[typeof value2?.value] : value1?.value;
18689
+ let _value2 = isEmpty(value2) ? getNeutral[typeof value1?.value] : value2?.value;
18641
18690
  if (typeof _value1 !== "number") {
18642
18691
  _value1 = toString(_value1).toUpperCase();
18643
18692
  }
@@ -18647,12 +18696,12 @@ stores.inject(MyMetaStore, storeInstance);
18647
18696
  const tV1 = typeof _value1;
18648
18697
  const tV2 = typeof _value2;
18649
18698
  if (tV1 === "string" && tV2 === "number") {
18650
- return true;
18699
+ return { value: true };
18651
18700
  }
18652
18701
  if (tV2 === "string" && tV1 === "number") {
18653
- return false;
18702
+ return { value: false };
18654
18703
  }
18655
- return cb(_value1, _value2);
18704
+ return { value: cb(_value1, _value2) };
18656
18705
  }
18657
18706
  const GT = {
18658
18707
  description: _t("Strictly greater than."),
@@ -18691,7 +18740,11 @@ stores.inject(MyMetaStore, storeInstance);
18691
18740
  arg("value2 (any)", _t("The second value.")),
18692
18741
  ],
18693
18742
  compute: function (value1, value2) {
18694
- return !GTE.compute.bind(this)(value1, value2);
18743
+ const result = GTE.compute.bind(this)(value1, value2);
18744
+ if (isEvaluationError(result.value)) {
18745
+ return result;
18746
+ }
18747
+ return { value: !result.value };
18695
18748
  },
18696
18749
  };
18697
18750
  // -----------------------------------------------------------------------------
@@ -18704,7 +18757,11 @@ stores.inject(MyMetaStore, storeInstance);
18704
18757
  arg("value2 (any)", _t("The second value.")),
18705
18758
  ],
18706
18759
  compute: function (value1, value2) {
18707
- return !GT.compute.bind(this)(value1, value2);
18760
+ const result = GT.compute.bind(this)(value1, value2);
18761
+ if (isEvaluationError(result.value)) {
18762
+ return result;
18763
+ }
18764
+ return { value: !result.value };
18708
18765
  },
18709
18766
  };
18710
18767
  // -----------------------------------------------------------------------------
@@ -18749,7 +18806,11 @@ stores.inject(MyMetaStore, storeInstance);
18749
18806
  arg("value2 (any)", _t("The value to test against value1 for inequality.")),
18750
18807
  ],
18751
18808
  compute: function (value1, value2) {
18752
- return !EQ.compute.bind(this)(value1, value2);
18809
+ const result = EQ.compute.bind(this)(value1, value2);
18810
+ if (isEvaluationError(result.value)) {
18811
+ return result;
18812
+ }
18813
+ return { value: !result.value };
18753
18814
  },
18754
18815
  };
18755
18816
  // -----------------------------------------------------------------------------
@@ -19908,13 +19969,12 @@ stores.inject(MyMetaStore, storeInstance);
19908
19969
  }
19909
19970
  function getElementMargins(el) {
19910
19971
  const style = window.getComputedStyle(el);
19911
- const margins = {
19972
+ return {
19912
19973
  top: parseInt(style.marginTop, 10) || 0,
19913
19974
  bottom: parseInt(style.marginBottom, 10) || 0,
19914
19975
  left: parseInt(style.marginLeft, 10) || 0,
19915
19976
  right: parseInt(style.marginRight, 10) || 0,
19916
19977
  };
19917
- return margins;
19918
19978
  }
19919
19979
 
19920
19980
  const macRegex = /Mac/i;
@@ -21789,9 +21849,13 @@ stores.inject(MyMetaStore, storeInstance);
21789
21849
  const colFields = columns.map((groupBy) => groupBy.nameWithGranularity);
21790
21850
  const rowFields = rows.map((groupBy) => groupBy.nameWithGranularity);
21791
21851
  const proposals = [];
21792
- const previousGroupBy = ["ARG_SEPARATOR", "SPACE"].includes(tokenAtCursor.type)
21852
+ let previousGroupBy = ["ARG_SEPARATOR", "SPACE"].includes(tokenAtCursor.type)
21793
21853
  ? argGroupBys.at(-1)
21794
21854
  : argGroupBys.at(-2);
21855
+ const isPositionalSupported = supportedPivotPositionalFormulaRegistry.get(pivot.type);
21856
+ if (isPositionalSupported && previousGroupBy?.startsWith("#")) {
21857
+ previousGroupBy = previousGroupBy.slice(1);
21858
+ }
21795
21859
  if (previousGroupBy === undefined) {
21796
21860
  proposals.push(colFields[0]);
21797
21861
  proposals.push(rowFields[0]);
@@ -21813,7 +21877,7 @@ stores.inject(MyMetaStore, storeInstance);
21813
21877
  return field ? makeFieldProposal(field, granularity) : undefined;
21814
21878
  })
21815
21879
  .concat(groupBys.map((groupBy) => {
21816
- if (!supportedPivotPositionalFormulaRegistry.get(pivot.type)) {
21880
+ if (!isPositionalSupported) {
21817
21881
  return undefined;
21818
21882
  }
21819
21883
  const fieldName = groupBy.split(":")[0];
@@ -21822,13 +21886,12 @@ stores.inject(MyMetaStore, storeInstance);
21822
21886
  return undefined;
21823
21887
  }
21824
21888
  const positionalFieldArg = `"#${groupBy}"`;
21825
- const positionalProposal = {
21889
+ return {
21826
21890
  text: positionalFieldArg,
21827
21891
  description: _t("%s (positional)", field.string) + (field.help ? ` (${field.help})` : ""),
21828
21892
  htmlContent: [{ value: positionalFieldArg, color: tokenColors.STRING }],
21829
21893
  fuzzySearchKey: field.string + positionalFieldArg, // search on translated name and on technical name
21830
21894
  };
21831
- return positionalProposal;
21832
21895
  }))
21833
21896
  .filter(isDefined);
21834
21897
  },
@@ -22097,7 +22160,7 @@ stores.inject(MyMetaStore, storeInstance);
22097
22160
  return internalDate.value;
22098
22161
  }
22099
22162
  if (isBoolean(content)) {
22100
- return content.toUpperCase() === "TRUE" ? true : false;
22163
+ return content.toUpperCase() === "TRUE";
22101
22164
  }
22102
22165
  return content;
22103
22166
  }
@@ -22438,8 +22501,7 @@ stores.inject(MyMetaStore, storeInstance);
22438
22501
  return 0;
22439
22502
  }
22440
22503
  const previous = jsDates[index - 1];
22441
- const days = Math.floor(date.getTime()) - Math.floor(previous.getTime());
22442
- return days;
22504
+ return Math.floor(date.getTime()) - Math.floor(previous.getTime());
22443
22505
  })
22444
22506
  .slice(1);
22445
22507
  const equidistantDates = timeIntervals.every((interval) => interval === timeIntervals[0]);
@@ -22998,6 +23060,7 @@ stores.inject(MyMetaStore, storeInstance);
22998
23060
  "surface3DChart",
22999
23061
  "bubbleChart",
23000
23062
  "comboChart",
23063
+ "radarChart",
23001
23064
  ];
23002
23065
 
23003
23066
  /** In XLSX color format (no #) */
@@ -23538,7 +23601,7 @@ stores.inject(MyMetaStore, storeInstance);
23538
23601
  lineChart: "line",
23539
23602
  line3DChart: undefined,
23540
23603
  stockChart: undefined,
23541
- radarChart: undefined,
23604
+ radarChart: "radar",
23542
23605
  scatterChart: "scatter",
23543
23606
  pieChart: "pie",
23544
23607
  pie3DChart: undefined,
@@ -24951,7 +25014,7 @@ stores.inject(MyMetaStore, storeInstance);
24951
25014
  custom: _t("Custom"),
24952
25015
  };
24953
25016
  const DEFAULT_TABLE_CONFIG = {
24954
- hasFilters: true,
25017
+ hasFilters: false,
24955
25018
  totalRow: false,
24956
25019
  firstColumn: false,
24957
25020
  lastColumn: false,
@@ -25620,12 +25683,11 @@ stores.inject(MyMetaStore, storeInstance);
25620
25683
  * Get the list of all the XLSX files in the XLSX file structure
25621
25684
  */
25622
25685
  getListOfXMLFiles() {
25623
- const XMLFiles = Object.entries(this.xlsxFileStructure)
25686
+ return Object.entries(this.xlsxFileStructure)
25624
25687
  .filter(([key]) => key !== "images")
25625
25688
  .map(([_, value]) => value)
25626
25689
  .flat()
25627
25690
  .filter(isDefined);
25628
- return XMLFiles;
25629
25691
  }
25630
25692
  /**
25631
25693
  * Return an array containing the return value of the given function applied to all the XML elements
@@ -25789,13 +25851,12 @@ stores.inject(MyMetaStore, storeInstance);
25789
25851
  rgb = this.extractAttr(colorElement, "rgb")?.asString();
25790
25852
  rgb = rgb === DEFAULT_SYSTEM_COLOR ? undefined : rgb;
25791
25853
  }
25792
- const color = {
25854
+ return {
25793
25855
  rgb: rgb || defaultColor,
25794
25856
  auto: this.extractAttr(colorElement, "auto")?.asBool(),
25795
25857
  indexed: this.extractAttr(colorElement, "indexed")?.asNum(),
25796
25858
  tint: this.extractAttr(colorElement, "tint")?.asNum(),
25797
25859
  };
25798
- return color;
25799
25860
  }
25800
25861
  /**
25801
25862
  * Returns the xml file targeted by a relationship.
@@ -26387,7 +26448,7 @@ stores.inject(MyMetaStore, storeInstance);
26387
26448
  hyperlinks: this.extractHyperLinks(sheetElement),
26388
26449
  tables: this.extractTables(sheetElement),
26389
26450
  pivotTables: this.extractPivotTables(),
26390
- isVisible: sheetWorkbookInfo.state === "visible" ? true : false,
26451
+ isVisible: sheetWorkbookInfo.state === "visible",
26391
26452
  };
26392
26453
  })[0];
26393
26454
  }
@@ -26458,8 +26519,7 @@ stores.inject(MyMetaStore, storeInstance);
26458
26519
  const figures = this.mapOnElements({ parent: worksheet, query: "drawing" }, (drawingElement) => {
26459
26520
  const drawingId = this.extractAttr(drawingElement, "r:id", { required: true })?.asString();
26460
26521
  const drawingFile = this.getTargetXmlFile(this.relationships[drawingId]);
26461
- const figures = new XlsxFigureExtractor(drawingFile, this.xlsxFileStructure, this.warningManager).extractFigures();
26462
- return figures;
26522
+ return new XlsxFigureExtractor(drawingFile, this.xlsxFileStructure, this.warningManager).extractFigures();
26463
26523
  })[0];
26464
26524
  return figures || [];
26465
26525
  }
@@ -26477,8 +26537,7 @@ stores.inject(MyMetaStore, storeInstance);
26477
26537
  .filter((relationship) => relationship.type.endsWith("pivotTable"))
26478
26538
  .map((pivotRelationship) => {
26479
26539
  const pivotFile = this.getTargetXmlFile(pivotRelationship);
26480
- const pivot = new XlsxPivotExtractor(pivotFile, this.xlsxFileStructure, this.warningManager).getPivotTable();
26481
- return pivot;
26540
+ return new XlsxPivotExtractor(pivotFile, this.xlsxFileStructure, this.warningManager).getPivotTable();
26482
26541
  });
26483
26542
  }
26484
26543
  catch (e) {
@@ -26877,8 +26936,7 @@ stores.inject(MyMetaStore, storeInstance);
26877
26936
  }
26878
26937
  convertXlsx() {
26879
26938
  const xlsxData = this.getXlsxData();
26880
- const convertedData = this.convertImportedData(xlsxData);
26881
- return convertedData;
26939
+ return this.convertImportedData(xlsxData);
26882
26940
  }
26883
26941
  // ---------------------------------------------------------------------------
26884
26942
  // Parsing XMLs
@@ -27469,6 +27527,20 @@ stores.inject(MyMetaStore, storeInstance);
27469
27527
  }
27470
27528
  return data;
27471
27529
  },
27530
+ })
27531
+ .add("migration_22", {
27532
+ // "tables are no longer inserted with filters by default",
27533
+ versionFrom: "22",
27534
+ migrate(data) {
27535
+ for (const sheet of data.sheets || []) {
27536
+ for (const table of sheet.tables || []) {
27537
+ if (!table.config) {
27538
+ table.config = { ...DEFAULT_TABLE_CONFIG, hasFilters: true };
27539
+ }
27540
+ }
27541
+ }
27542
+ return data;
27543
+ },
27472
27544
  });
27473
27545
  function fixOverlappingFilters(data) {
27474
27546
  for (let sheet of data.sheets || []) {
@@ -27496,7 +27568,7 @@ stores.inject(MyMetaStore, storeInstance);
27496
27568
  * a breaking change is made in the way the state is handled, and an upgrade
27497
27569
  * function should be defined
27498
27570
  */
27499
- const CURRENT_VERSION = 22;
27571
+ const CURRENT_VERSION = 23;
27500
27572
  const INITIAL_SHEET_ID = "Sheet1";
27501
27573
  /**
27502
27574
  * This function tries to load anything that could look like a valid
@@ -27509,7 +27581,7 @@ stores.inject(MyMetaStore, storeInstance);
27509
27581
  if (!data) {
27510
27582
  return createEmptyWorkbookData();
27511
27583
  }
27512
- console.group("Loading data");
27584
+ console.debug("### Loading data ###");
27513
27585
  const start = performance.now();
27514
27586
  if (data["[Content_Types].xml"]) {
27515
27587
  const reader = new XlsxReader(data);
@@ -27523,13 +27595,13 @@ stores.inject(MyMetaStore, storeInstance);
27523
27595
  // apply migrations, if needed
27524
27596
  if ("version" in data) {
27525
27597
  if (data.version < CURRENT_VERSION) {
27526
- console.info("Migrating data from version", data.version);
27598
+ console.debug("Migrating data from version", data.version);
27527
27599
  data = migrate(data);
27528
27600
  }
27529
27601
  }
27530
27602
  data = repairData(data);
27531
- console.info("Data loaded in", performance.now() - start, "ms");
27532
- console.groupEnd();
27603
+ console.debug("Data loaded in", performance.now() - start, "ms");
27604
+ console.debug("###");
27533
27605
  return data;
27534
27606
  }
27535
27607
  // -----------------------------------------------------------------------------
@@ -27559,7 +27631,7 @@ stores.inject(MyMetaStore, storeInstance);
27559
27631
  for (let i = index; i < steps.length; i++) {
27560
27632
  data = steps[i].migrate(data);
27561
27633
  }
27562
- console.info("Data migrated in", performance.now() - start, "ms");
27634
+ console.debug("Data migrated in", performance.now() - start, "ms");
27563
27635
  return data;
27564
27636
  }
27565
27637
  /**
@@ -27741,7 +27813,7 @@ stores.inject(MyMetaStore, storeInstance);
27741
27813
  };
27742
27814
  }
27743
27815
  function createEmptyWorkbookData(sheetName = "Sheet1") {
27744
- const data = {
27816
+ return {
27745
27817
  version: CURRENT_VERSION,
27746
27818
  sheets: [createEmptySheet(INITIAL_SHEET_ID, sheetName)],
27747
27819
  styles: {},
@@ -27754,7 +27826,6 @@ stores.inject(MyMetaStore, storeInstance);
27754
27826
  pivotNextId: 1,
27755
27827
  customTableStyles: {},
27756
27828
  };
27757
- return data;
27758
27829
  }
27759
27830
  function createEmptyExcelSheet(sheetId, name) {
27760
27831
  return {
@@ -28138,7 +28209,7 @@ stores.inject(MyMetaStore, storeInstance);
28138
28209
  const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
28139
28210
  // tooltipItem.parsed can be an object or a number for pie charts
28140
28211
  let yLabel = horizontalChart ? tooltipItem.parsed.x : tooltipItem.parsed.y;
28141
- if (!yLabel) {
28212
+ if (yLabel === undefined || yLabel === null) {
28142
28213
  yLabel = tooltipItem.parsed;
28143
28214
  }
28144
28215
  const axisId = horizontalChart
@@ -28532,8 +28603,7 @@ stores.inject(MyMetaStore, storeInstance);
28532
28603
  };
28533
28604
  config.data.datasets.push(dataset);
28534
28605
  if (definition.dataSets?.[index]?.label) {
28535
- const label = definition.dataSets[index].label;
28536
- dataset.label = label;
28606
+ dataset.label = definition.dataSets[index].label;
28537
28607
  }
28538
28608
  dataset.yAxisID = chart.horizontal ? "y" : definition.dataSets[index].yAxisId || "y";
28539
28609
  dataset.xAxisID = "x";
@@ -28561,13 +28631,10 @@ stores.inject(MyMetaStore, storeInstance);
28561
28631
  * datasets to ensure the way we distinguish the originals and trendLine datasets after
28562
28632
  */
28563
28633
  trendDatasets.forEach((x) => config.data.datasets.push(x));
28564
- const originalTooltipTitle = config.options.plugins.tooltip.callbacks.title;
28565
28634
  config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
28566
- if (tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)) {
28567
- // @ts-expect-error
28568
- return originalTooltipTitle?.(tooltipItems);
28569
- }
28570
- return "";
28635
+ return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
28636
+ ? undefined
28637
+ : "";
28571
28638
  };
28572
28639
  }
28573
28640
  return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
@@ -28905,7 +28972,6 @@ stores.inject(MyMetaStore, storeInstance);
28905
28972
  else if (axisType === "linear") {
28906
28973
  config.options.scales.x.type = "linear";
28907
28974
  config.options.scales.x.ticks.callback = (value) => formatValue(value, { format: labelFormat, locale });
28908
- config.options.plugins.tooltip.callbacks.title = () => "";
28909
28975
  config.options.plugins.tooltip.callbacks.label = (tooltipItem) => {
28910
28976
  const dataSetPoint = dataSetsValues[tooltipItem.datasetIndex].data[tooltipItem.dataIndex];
28911
28977
  let label = tooltipItem.label || labelValues.values[tooltipItem.dataIndex];
@@ -28960,8 +29026,7 @@ stores.inject(MyMetaStore, storeInstance);
28960
29026
  const trendDatasets = [];
28961
29027
  for (const [index, dataset] of config.data.datasets.entries()) {
28962
29028
  if (definition.dataSets?.[index]?.label) {
28963
- const label = definition.dataSets[index].label;
28964
- dataset.label = label;
29029
+ dataset.label = definition.dataSets[index].label;
28965
29030
  }
28966
29031
  dataset["yAxisID"] = definition.dataSets[index].yAxisId || "y";
28967
29032
  const trend = definition.dataSets?.[index].trend;
@@ -28990,15 +29055,12 @@ stores.inject(MyMetaStore, storeInstance);
28990
29055
  * distinguish the originals and trendLine datasets after
28991
29056
  */
28992
29057
  trendDatasets.forEach((x) => config.data.datasets.push(x));
28993
- const originalTooltipTitle = config.options.plugins.tooltip.callbacks.title;
28994
- config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
28995
- if (tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)) {
28996
- // @ts-expect-error
28997
- return originalTooltipTitle?.(tooltipItems);
28998
- }
28999
- return "";
29000
- };
29001
29058
  }
29059
+ config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
29060
+ const displayTooltipTitle = axisType !== "linear" &&
29061
+ tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID);
29062
+ return displayTooltipTitle ? undefined : "";
29063
+ };
29002
29064
  return {
29003
29065
  chartJsConfig: config,
29004
29066
  background: chart.background || BACKGROUND_CHART_COLOR,
@@ -29221,13 +29283,10 @@ stores.inject(MyMetaStore, storeInstance);
29221
29283
  * distinguish the originals and trendLine datasets after
29222
29284
  */
29223
29285
  trendDatasets.forEach((x) => config.data.datasets.push(x));
29224
- const originalTooltipTitle = config.options.plugins.tooltip.callbacks.title;
29225
29286
  config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
29226
- if (tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)) {
29227
- // @ts-expect-error
29228
- return originalTooltipTitle?.(tooltipItems);
29229
- }
29230
- return "";
29287
+ return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
29288
+ ? undefined
29289
+ : "";
29231
29290
  };
29232
29291
  }
29233
29292
  return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
@@ -29966,6 +30025,198 @@ stores.inject(MyMetaStore, storeInstance);
29966
30025
  return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
29967
30026
  }
29968
30027
 
30028
+ class RadarChart extends AbstractChart {
30029
+ dataSets;
30030
+ labelRange;
30031
+ background;
30032
+ legendPosition;
30033
+ stacked;
30034
+ aggregated;
30035
+ type = "radar";
30036
+ dataSetsHaveTitle;
30037
+ dataSetDesign;
30038
+ fillArea;
30039
+ constructor(definition, sheetId, getters) {
30040
+ super(definition, sheetId, getters);
30041
+ this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
30042
+ this.labelRange = createValidRange(getters, sheetId, definition.labelRange);
30043
+ this.background = definition.background;
30044
+ this.legendPosition = definition.legendPosition;
30045
+ this.stacked = definition.stacked;
30046
+ this.aggregated = definition.aggregated;
30047
+ this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
30048
+ this.dataSetDesign = definition.dataSets;
30049
+ this.fillArea = definition.fillArea;
30050
+ }
30051
+ static transformDefinition(definition, executed) {
30052
+ return transformChartDefinitionWithDataSetsWithZone(definition, executed);
30053
+ }
30054
+ static validateChartDefinition(validator, definition) {
30055
+ return validator.checkValidations(definition, checkDataset, checkLabelRange);
30056
+ }
30057
+ static getDefinitionFromContextCreation(context) {
30058
+ return {
30059
+ background: context.background,
30060
+ dataSets: context.range ?? [],
30061
+ dataSetsHaveTitle: context.dataSetsHaveTitle ?? false,
30062
+ stacked: context.stacked ?? false,
30063
+ aggregated: context.aggregated ?? false,
30064
+ legendPosition: context.legendPosition ?? "top",
30065
+ title: context.title || { text: "" },
30066
+ type: "radar",
30067
+ labelRange: context.auxiliaryRange || undefined,
30068
+ fillArea: context.fillArea ?? false,
30069
+ };
30070
+ }
30071
+ getContextCreation() {
30072
+ const range = [];
30073
+ for (const [i, dataSet] of this.dataSets.entries()) {
30074
+ range.push({
30075
+ ...this.dataSetDesign?.[i],
30076
+ dataRange: this.getters.getRangeString(dataSet.dataRange, this.sheetId),
30077
+ });
30078
+ }
30079
+ return {
30080
+ ...this,
30081
+ range,
30082
+ auxiliaryRange: this.labelRange
30083
+ ? this.getters.getRangeString(this.labelRange, this.sheetId)
30084
+ : undefined,
30085
+ };
30086
+ }
30087
+ copyForSheetId(sheetId) {
30088
+ const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
30089
+ const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
30090
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
30091
+ return new RadarChart(definition, sheetId, this.getters);
30092
+ }
30093
+ copyInSheetId(sheetId) {
30094
+ const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
30095
+ return new RadarChart(definition, sheetId, this.getters);
30096
+ }
30097
+ getDefinition() {
30098
+ return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
30099
+ }
30100
+ getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
30101
+ const ranges = [];
30102
+ for (const [i, dataSet] of dataSets.entries()) {
30103
+ ranges.push({
30104
+ ...this.dataSetDesign?.[i],
30105
+ dataRange: this.getters.getRangeString(dataSet.dataRange, targetSheetId || this.sheetId),
30106
+ });
30107
+ }
30108
+ return {
30109
+ type: "radar",
30110
+ dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
30111
+ background: this.background,
30112
+ dataSets: ranges,
30113
+ legendPosition: this.legendPosition,
30114
+ labelRange: labelRange
30115
+ ? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
30116
+ : undefined,
30117
+ title: this.title,
30118
+ stacked: this.stacked,
30119
+ aggregated: this.aggregated,
30120
+ fillArea: this.fillArea,
30121
+ };
30122
+ }
30123
+ getDefinitionForExcel() {
30124
+ if (this.aggregated) {
30125
+ return undefined;
30126
+ }
30127
+ const dataSets = this.dataSets
30128
+ .map((ds) => toExcelDataset(this.getters, ds))
30129
+ .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
30130
+ const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
30131
+ const definition = this.getDefinition();
30132
+ return {
30133
+ ...definition,
30134
+ backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
30135
+ fontColor: toXlsxHexColor(chartFontColor(this.background)),
30136
+ dataSets,
30137
+ labelRange,
30138
+ };
30139
+ }
30140
+ updateRanges(applyChange) {
30141
+ const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
30142
+ if (!isStale) {
30143
+ return this;
30144
+ }
30145
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
30146
+ return new RadarChart(definition, this.sheetId, this.getters);
30147
+ }
30148
+ }
30149
+ function createRadarChartRuntime(chart, getters) {
30150
+ const definition = chart.getDefinition();
30151
+ const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
30152
+ let labels = labelValues.formattedValues;
30153
+ let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
30154
+ if (chart.dataSetsHaveTitle &&
30155
+ dataSetsValues[0] &&
30156
+ labels.length > dataSetsValues[0].data.length) {
30157
+ labels.shift();
30158
+ }
30159
+ ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
30160
+ if (chart.aggregated) {
30161
+ ({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
30162
+ }
30163
+ const leftAxisFormat = getChartDatasetFormat(getters, chart.dataSets, "left");
30164
+ const rightAxisFormat = getChartDatasetFormat(getters, chart.dataSets, "right");
30165
+ const axisFormats = { y: leftAxisFormat, y1: rightAxisFormat };
30166
+ const locale = getters.getLocale();
30167
+ const fontColor = chartFontColor(chart.background);
30168
+ const config = getDefaultChartJsRuntime(chart, labels, fontColor, {
30169
+ axisFormats,
30170
+ locale,
30171
+ });
30172
+ const legend = {
30173
+ labels: { color: fontColor },
30174
+ };
30175
+ if ((!chart.labelRange && chart.dataSets.length === 1) || chart.legendPosition === "none") {
30176
+ legend.display = false;
30177
+ }
30178
+ else {
30179
+ legend.position = chart.legendPosition;
30180
+ }
30181
+ const fill = definition.fillArea ?? false;
30182
+ if (!fill) {
30183
+ legend.labels["boxHeight"] = 0;
30184
+ }
30185
+ config.options.plugins.legend = { ...config.options.plugins?.legend, ...legend };
30186
+ config.options.plugins.tooltip = {
30187
+ ...config.options.plugins?.tooltip,
30188
+ callbacks: {
30189
+ label: function (tooltipItem) {
30190
+ const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
30191
+ const yLabel = tooltipItem.parsed.r;
30192
+ return xLabel ? `${xLabel}: ${yLabel}` : yLabel.toString();
30193
+ },
30194
+ },
30195
+ };
30196
+ config.options.layout = {
30197
+ padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
30198
+ };
30199
+ const colorGenerator = getChartColorsGenerator(definition, dataSetsValues.length);
30200
+ for (let i = 0; i < dataSetsValues.length; i++) {
30201
+ let { label, data } = dataSetsValues[i];
30202
+ if (definition.dataSets?.[i]?.label) {
30203
+ label = definition.dataSets[i].label;
30204
+ }
30205
+ const borderColor = colorGenerator.next();
30206
+ const dataset = {
30207
+ label,
30208
+ data,
30209
+ borderColor,
30210
+ };
30211
+ if (fill) {
30212
+ dataset.backgroundColor = setColorAlpha(borderColor, 0.3);
30213
+ dataset["fill"] = true;
30214
+ }
30215
+ config.data.datasets.push(dataset);
30216
+ }
30217
+ return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
30218
+ }
30219
+
29969
30220
  class ScatterChart extends AbstractChart {
29970
30221
  dataSets;
29971
30222
  labelRange;
@@ -30488,6 +30739,15 @@ stores.inject(MyMetaStore, storeInstance);
30488
30739
  getChartDefinitionFromContextCreation: PyramidChart.getDefinitionFromContextCreation,
30489
30740
  sequence: 80,
30490
30741
  });
30742
+ chartRegistry.add("radar", {
30743
+ match: (type) => type === "radar",
30744
+ createChart: (definition, sheetId, getters) => new RadarChart(definition, sheetId, getters),
30745
+ getChartRuntime: createRadarChartRuntime,
30746
+ validateChartDefinition: RadarChart.validateChartDefinition,
30747
+ transformDefinition: RadarChart.transformDefinition,
30748
+ getChartDefinitionFromContextCreation: RadarChart.getDefinitionFromContextCreation,
30749
+ sequence: 80,
30750
+ });
30491
30751
  const chartComponentRegistry = new Registry();
30492
30752
  chartComponentRegistry.add("line", ChartJsComponent);
30493
30753
  chartComponentRegistry.add("bar", ChartJsComponent);
@@ -30498,6 +30758,7 @@ stores.inject(MyMetaStore, storeInstance);
30498
30758
  chartComponentRegistry.add("scorecard", ScorecardChart);
30499
30759
  chartComponentRegistry.add("waterfall", ChartJsComponent);
30500
30760
  chartComponentRegistry.add("pyramid", ChartJsComponent);
30761
+ chartComponentRegistry.add("radar", ChartJsComponent);
30501
30762
  const chartCategories = {
30502
30763
  line: _t("Line"),
30503
30764
  column: _t("Column"),
@@ -30639,6 +30900,24 @@ stores.inject(MyMetaStore, storeInstance);
30639
30900
  chartType: "pyramid",
30640
30901
  category: "misc",
30641
30902
  preview: "o-spreadsheet-ChartPreview.POPULATION_PYRAMID_CHART",
30903
+ })
30904
+ .add("radar", {
30905
+ matcher: (definition) => definition.type === "radar" && !definition.fillArea,
30906
+ displayName: _t("Radar"),
30907
+ chartSubtype: "radar",
30908
+ chartType: "radar",
30909
+ subtypeDefinition: { fillArea: false },
30910
+ category: "misc",
30911
+ preview: "o-spreadsheet-ChartPreview.RADAR_CHART",
30912
+ })
30913
+ .add("filled_radar", {
30914
+ matcher: (definition) => definition.type === "radar" && !!definition.fillArea,
30915
+ displayName: _t("Filled Radar"),
30916
+ chartType: "radar",
30917
+ chartSubtype: "filled_radar",
30918
+ subtypeDefinition: { fillArea: true },
30919
+ category: "misc",
30920
+ preview: "o-spreadsheet-ChartPreview.FILLED_RADAR_CHART",
30642
30921
  });
30643
30922
 
30644
30923
  /**
@@ -30706,11 +30985,10 @@ stores.inject(MyMetaStore, storeInstance);
30706
30985
  const rect = getters.getVisibleRect(getters.getActiveMainViewport());
30707
30986
  const scrollableViewportWidth = Math.min(rect.width, dim.width - offsetCorrectionX);
30708
30987
  const scrollableViewportHeight = Math.min(rect.height, dim.height - offsetCorrectionY);
30709
- const position = {
30988
+ return {
30710
30989
  x: offsetCorrectionX + scrollX + Math.max(0, (scrollableViewportWidth - size.width) / 2),
30711
30990
  y: offsetCorrectionY + scrollY + Math.max(0, (scrollableViewportHeight - size.height) / 2),
30712
30991
  }; // Position at the center of the scrollable viewport
30713
- return position;
30714
30992
  }
30715
30993
  function getMaxFigureSize(getters, figureSize) {
30716
30994
  const size = deepCopy(figureSize);
@@ -31283,14 +31561,13 @@ stores.inject(MyMetaStore, storeInstance);
31283
31561
  const shouldRenderAtBottom = this.shouldRenderAtBottom(elDims.height);
31284
31562
  const shouldRenderAtRight = this.shouldRenderAtRight(elDims.width);
31285
31563
  verticalOffset = shouldRenderAtBottom ? verticalOffset : -verticalOffset;
31286
- const cssProperties = {
31564
+ return {
31287
31565
  top: this.getTopCoordinate(actualHeight, shouldRenderAtBottom) -
31288
31566
  this.spreadsheetOffset.y -
31289
31567
  verticalOffset +
31290
31568
  "px",
31291
31569
  left: this.getLeftCoordinate(actualWidth, shouldRenderAtRight) - this.spreadsheetOffset.x + "px",
31292
31570
  };
31293
- return cssProperties;
31294
31571
  }
31295
31572
  getCurrentPosition(elDims) {
31296
31573
  const shouldRenderAtBottom = this.shouldRenderAtBottom(elDims.height);
@@ -32518,7 +32795,7 @@ stores.inject(MyMetaStore, storeInstance);
32518
32795
  * Create a table on the selected zone, with UI warnings to the user if the creation fails.
32519
32796
  * If a single cell is selected, expand the selection to non-empty adjacent cells to create a table.
32520
32797
  */
32521
- function interactiveCreateTable(env, sheetId, tableConfig) {
32798
+ function interactiveCreateTable(env, sheetId, tableConfig = DEFAULT_TABLE_CONFIG) {
32522
32799
  let target = env.model.getters.getSelectedZones();
32523
32800
  let isDynamic = env.model.getters.canCreateDynamicTableOnZones(sheetId, target);
32524
32801
  if (target.length === 1 && !isDynamic && getZoneArea(target[0]) === 1) {
@@ -33577,7 +33854,7 @@ stores.inject(MyMetaStore, storeInstance);
33577
33854
  }
33578
33855
 
33579
33856
  const pivotProperties = {
33580
- name: _t("Edit Pivot"),
33857
+ name: _t("See pivot properties"),
33581
33858
  execute(env) {
33582
33859
  const position = env.model.getters.getActivePosition();
33583
33860
  const pivotId = env.model.getters.getPivotIdFromPosition(position);
@@ -33731,8 +34008,7 @@ stores.inject(MyMetaStore, storeInstance);
33731
34008
  })
33732
34009
  .add("pivot_properties", {
33733
34010
  ...pivotProperties,
33734
- sequence: 160,
33735
- separator: true,
34011
+ sequence: 170,
33736
34012
  });
33737
34013
 
33738
34014
  const sortRange = {
@@ -35479,6 +35755,7 @@ stores.inject(MyMetaStore, storeInstance);
35479
35755
  static template = "o_spreadsheet.Section";
35480
35756
  static props = {
35481
35757
  class: { type: String, optional: true },
35758
+ title: { type: String, optional: true },
35482
35759
  slots: Object,
35483
35760
  };
35484
35761
  }
@@ -36208,17 +36485,11 @@ stores.inject(MyMetaStore, storeInstance);
36208
36485
  stacked,
36209
36486
  });
36210
36487
  }
36211
- onUpdateAggregated(aggregated) {
36212
- this.props.updateChart(this.props.figureId, {
36213
- aggregated,
36214
- });
36215
- }
36216
36488
  }
36217
36489
 
36218
36490
  css /* scss */ `
36219
36491
  .o_side_panel_collapsible_title {
36220
36492
  font-size: 16px;
36221
- font-weight: bold;
36222
36493
  cursor: pointer;
36223
36494
  padding: 6px 0px 6px 6px !important;
36224
36495
 
@@ -36255,49 +36526,40 @@ stores.inject(MyMetaStore, storeInstance);
36255
36526
  static template = "o-spreadsheet-SidePanelCollapsible";
36256
36527
  static props = {
36257
36528
  slots: Object,
36529
+ title: { type: String, optional: true },
36258
36530
  collapsedAtInit: { type: Boolean, optional: true },
36259
36531
  class: { type: String, optional: true },
36260
36532
  };
36261
36533
  currentId = (CURRENT_COLLAPSIBLE_ID++).toString();
36262
36534
  }
36263
36535
 
36264
- const CIRCLE_SVG = /*xml*/ `
36265
- <svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'>
36266
- <circle r="2" fill="#FFF"/>
36267
- </svg>
36268
- `;
36269
36536
  css /* scss */ `
36270
- .o-radio {
36271
- input {
36272
- appearance: none;
36273
- -webkit-appearance: none;
36274
- -moz-appearance: none;
36275
- width: 14px;
36276
- height: 14px;
36277
- border: 1px solid ${GRAY_300};
36278
- box-sizing: border-box;
36279
- outline: none;
36280
- border-radius: 8px;
36281
-
36282
- &:checked {
36283
- background: url("data:image/svg+xml,${encodeURIComponent(CIRCLE_SVG)}");
36284
- background-color: ${ACTION_COLOR};
36537
+ .o-badge-selection {
36538
+ gap: 1px;
36539
+ button.o-button {
36540
+ border-radius: 0;
36541
+ &.selected {
36542
+ color: ${GRAY_900};
36285
36543
  border-color: ${ACTION_COLOR};
36544
+ background: ${BADGE_SELECTED_COLOR};
36545
+ font-weight: 600;
36546
+ }
36547
+
36548
+ &:first-child {
36549
+ border-radius: 4px 0 0 4px;
36550
+ }
36551
+ &:last-child {
36552
+ border-radius: 0 4px 4px 0;
36286
36553
  }
36287
36554
  }
36288
36555
  }
36289
36556
  `;
36290
- class RadioSelection extends owl.Component {
36291
- static template = "o-spreadsheet.RadioSelection";
36557
+ class BadgeSelection extends owl.Component {
36558
+ static template = "o-spreadsheet.BadgeSelection";
36292
36559
  static props = {
36293
36560
  choices: Array,
36294
36561
  onChange: Function,
36295
- selectedValue: { optional: false },
36296
- name: String,
36297
- direction: { type: String, optional: true },
36298
- };
36299
- static defaultProps = {
36300
- direction: "horizontal",
36562
+ selectedValue: String,
36301
36563
  };
36302
36564
  }
36303
36565
 
@@ -36842,86 +37104,6 @@ stores.inject(MyMetaStore, storeInstance);
36842
37104
  }
36843
37105
  }
36844
37106
 
36845
- const TRANSPARENT_BACKGROUND_SVG = /*xml*/ `
36846
- <svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
36847
- <path fill="#d9d9d9" d="M5 5h5v5H5zH0V0h5"/>
36848
- </svg>
36849
- `;
36850
- css /* scss */ `
36851
- .o-round-color-picker-button {
36852
- width: 18px;
36853
- height: 18px;
36854
- cursor: pointer;
36855
- border: 1px solid ${GRAY_300};
36856
- background-position: 1px 1px;
36857
- background-image: url("data:image/svg+xml,${encodeURIComponent(TRANSPARENT_BACKGROUND_SVG)}");
36858
- }
36859
- `;
36860
- class RoundColorPicker extends owl.Component {
36861
- static template = "o-spreadsheet.RoundColorPicker";
36862
- static components = { ColorPickerWidget, Section, ColorPicker };
36863
- static props = {
36864
- currentColor: { type: String, optional: true },
36865
- title: { type: String, optional: true },
36866
- onColorPicked: Function,
36867
- };
36868
- colorPickerButtonRef = owl.useRef("colorPickerButton");
36869
- state;
36870
- setup() {
36871
- this.state = owl.useState({ pickerOpened: false });
36872
- owl.useExternalListener(window, "click", this.closePicker);
36873
- }
36874
- closePicker() {
36875
- this.state.pickerOpened = false;
36876
- }
36877
- togglePicker() {
36878
- this.state.pickerOpened = !this.state.pickerOpened;
36879
- }
36880
- onColorPicked(color) {
36881
- this.props.onColorPicked(color);
36882
- this.state.pickerOpened = false;
36883
- }
36884
- get colorPickerAnchorRect() {
36885
- const button = this.colorPickerButtonRef.el;
36886
- return getBoundingRectAsPOJO(button);
36887
- }
36888
- get buttonStyle() {
36889
- return cssPropertiesToCss({
36890
- background: this.props.currentColor,
36891
- });
36892
- }
36893
- }
36894
-
36895
- css /* scss */ `
36896
- .o-badge-selection {
36897
- gap: 1px;
36898
- button.o-button {
36899
- border-radius: 0;
36900
- &.selected {
36901
- color: ${GRAY_900};
36902
- border-color: ${ACTION_COLOR};
36903
- background: ${BADGE_SELECTED_COLOR};
36904
- font-weight: 600;
36905
- }
36906
-
36907
- &:first-child {
36908
- border-radius: 4px 0 0 4px;
36909
- }
36910
- &:last-child {
36911
- border-radius: 0 4px 4px 0;
36912
- }
36913
- }
36914
- }
36915
- `;
36916
- class BadgeSelection extends owl.Component {
36917
- static template = "o-spreadsheet.BadgeSelection";
36918
- static props = {
36919
- choices: Array,
36920
- onChange: Function,
36921
- selectedValue: String,
36922
- };
36923
- }
36924
-
36925
37107
  css /* scss */ `
36926
37108
  .o-chart-title-designer {
36927
37109
  > span {
@@ -37076,8 +37258,7 @@ stores.inject(MyMetaStore, storeInstance);
37076
37258
  this.props.updateChart(this.props.figureId, { axesDesign });
37077
37259
  }
37078
37260
  updateAxisEditor(ev) {
37079
- const axis = ev.target.value;
37080
- this.state.currentAxis = axis;
37261
+ this.state.currentAxis = ev.target.value;
37081
37262
  }
37082
37263
  getAxisTitle() {
37083
37264
  const axesDesign = this.props.definition.axesDesign ?? {};
@@ -37096,6 +37277,56 @@ stores.inject(MyMetaStore, storeInstance);
37096
37277
  }
37097
37278
  }
37098
37279
 
37280
+ const TRANSPARENT_BACKGROUND_SVG = /*xml*/ `
37281
+ <svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
37282
+ <path fill="#d9d9d9" d="M5 5h5v5H5zH0V0h5"/>
37283
+ </svg>
37284
+ `;
37285
+ css /* scss */ `
37286
+ .o-round-color-picker-button {
37287
+ width: 18px;
37288
+ height: 18px;
37289
+ cursor: pointer;
37290
+ border: 1px solid ${GRAY_300};
37291
+ background-position: 1px 1px;
37292
+ background-image: url("data:image/svg+xml,${encodeURIComponent(TRANSPARENT_BACKGROUND_SVG)}");
37293
+ }
37294
+ `;
37295
+ class RoundColorPicker extends owl.Component {
37296
+ static template = "o-spreadsheet.RoundColorPicker";
37297
+ static components = { Section, ColorPicker };
37298
+ static props = {
37299
+ currentColor: { type: String, optional: true },
37300
+ title: { type: String, optional: true },
37301
+ onColorPicked: Function,
37302
+ };
37303
+ colorPickerButtonRef = owl.useRef("colorPickerButton");
37304
+ state;
37305
+ setup() {
37306
+ this.state = owl.useState({ pickerOpened: false });
37307
+ owl.useExternalListener(window, "click", this.closePicker);
37308
+ }
37309
+ closePicker() {
37310
+ this.state.pickerOpened = false;
37311
+ }
37312
+ togglePicker() {
37313
+ this.state.pickerOpened = !this.state.pickerOpened;
37314
+ }
37315
+ onColorPicked(color) {
37316
+ this.props.onColorPicked(color);
37317
+ this.state.pickerOpened = false;
37318
+ }
37319
+ get colorPickerAnchorRect() {
37320
+ const button = this.colorPickerButtonRef.el;
37321
+ return getBoundingRectAsPOJO(button);
37322
+ }
37323
+ get buttonStyle() {
37324
+ return cssPropertiesToCss({
37325
+ background: this.props.currentColor,
37326
+ });
37327
+ }
37328
+ }
37329
+
37099
37330
  class GeneralDesignEditor extends owl.Component {
37100
37331
  static template = "o-spreadsheet-GeneralDesignEditor";
37101
37332
  static components = {
@@ -37160,58 +37391,75 @@ stores.inject(MyMetaStore, storeInstance);
37160
37391
  }
37161
37392
  }
37162
37393
 
37163
- class ChartWithAxisDesignPanel extends owl.Component {
37164
- static template = "o-spreadsheet-ChartWithAxisDesignPanel";
37394
+ const CIRCLE_SVG = /*xml*/ `
37395
+ <svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'>
37396
+ <circle r="2" fill="#FFF"/>
37397
+ </svg>
37398
+ `;
37399
+ css /* scss */ `
37400
+ .o-radio {
37401
+ input {
37402
+ appearance: none;
37403
+ -webkit-appearance: none;
37404
+ -moz-appearance: none;
37405
+ width: 14px;
37406
+ height: 14px;
37407
+ border: 1px solid ${GRAY_300};
37408
+ box-sizing: border-box;
37409
+ outline: none;
37410
+ border-radius: 8px;
37411
+
37412
+ &:checked {
37413
+ background: url("data:image/svg+xml,${encodeURIComponent(CIRCLE_SVG)}");
37414
+ background-color: ${ACTION_COLOR};
37415
+ border-color: ${ACTION_COLOR};
37416
+ }
37417
+ }
37418
+ }
37419
+ `;
37420
+ class RadioSelection extends owl.Component {
37421
+ static template = "o-spreadsheet.RadioSelection";
37422
+ static props = {
37423
+ choices: Array,
37424
+ onChange: Function,
37425
+ selectedValue: { optional: false },
37426
+ name: String,
37427
+ direction: { type: String, optional: true },
37428
+ };
37429
+ static defaultProps = {
37430
+ direction: "horizontal",
37431
+ };
37432
+ }
37433
+
37434
+ class SeriesDesignEditor extends owl.Component {
37435
+ static template = "o-spreadsheet-SeriesDesignEditor";
37165
37436
  static components = {
37166
- GeneralDesignEditor,
37167
37437
  SidePanelCollapsible,
37168
37438
  Section,
37169
- AxisDesignEditor,
37170
37439
  RoundColorPicker,
37171
- Checkbox,
37172
- RadioSelection,
37173
37440
  };
37174
37441
  static props = {
37175
37442
  figureId: String,
37176
37443
  definition: Object,
37177
- canUpdateChart: Function,
37178
37444
  updateChart: Function,
37445
+ canUpdateChart: Function,
37446
+ slots: { type: Object, optional: true },
37179
37447
  };
37180
- axisChoices = CHART_AXIS_CHOICES;
37181
37448
  state = owl.useState({ index: 0 });
37182
- get axesList() {
37183
- const { useLeftAxis, useRightAxis } = getDefinedAxis(this.props.definition);
37184
- let axes = [{ id: "x", name: _t("Horizontal axis") }];
37185
- if (useLeftAxis) {
37186
- axes.push({ id: "y", name: useRightAxis ? _t("Left axis") : _t("Vertical axis") });
37187
- }
37188
- if (useRightAxis) {
37189
- axes.push({ id: "y1", name: useLeftAxis ? _t("Right axis") : _t("Vertical axis") });
37190
- }
37191
- return axes;
37192
- }
37193
- updateLegendPosition(ev) {
37194
- this.props.updateChart(this.props.figureId, {
37195
- legendPosition: ev.target.value,
37196
- });
37197
- }
37198
37449
  getDataSeries() {
37199
- return this.props.definition.dataSets.map((d, i) => d.label ?? `${ChartTerms.Series} ${i + 1}`);
37450
+ const runtime = this.env.model.getters.getChartRuntime(this.props.figureId);
37451
+ if (!runtime || !("chartJsConfig" in runtime)) {
37452
+ return [];
37453
+ }
37454
+ return runtime.chartJsConfig.data.datasets.map((d) => d.label);
37200
37455
  }
37201
37456
  updateSerieEditor(ev) {
37202
- const chartId = this.props.figureId;
37203
- const selectedIndex = ev.target.selectedIndex;
37204
- const runtime = this.env.model.getters.getChartRuntime(chartId);
37205
- if (!runtime) {
37206
- return;
37207
- }
37208
- this.state.index = selectedIndex;
37457
+ this.state.index = ev.target.selectedIndex;
37209
37458
  }
37210
37459
  updateDataSeriesColor(color) {
37211
- const dataSets = [...this.props.definition.dataSets];
37212
- if (!dataSets?.[this.state.index]) {
37460
+ const dataSets = this.props.definition.dataSets;
37461
+ if (!dataSets?.[this.state.index])
37213
37462
  return;
37214
- }
37215
37463
  dataSets[this.state.index] = {
37216
37464
  ...dataSets[this.state.index],
37217
37465
  backgroundColor: color,
@@ -37220,71 +37468,87 @@ stores.inject(MyMetaStore, storeInstance);
37220
37468
  }
37221
37469
  getDataSerieColor() {
37222
37470
  const dataSets = this.props.definition.dataSets;
37223
- if (!dataSets?.[this.state.index]) {
37471
+ if (!dataSets?.[this.state.index])
37224
37472
  return "";
37225
- }
37226
37473
  const color = dataSets[this.state.index].backgroundColor;
37227
- return color ? toHex(color) : getNthColor(this.state.index, getColorsPalette(dataSets.length));
37474
+ return color
37475
+ ? toHex(color)
37476
+ : getNthColor(this.state.index, getColorsPalette(this.props.definition.dataSets.length));
37228
37477
  }
37229
- updateDataSeriesAxis(axis) {
37230
- const dataSets = [...this.props.definition.dataSets];
37231
- if (!dataSets?.[this.state.index]) {
37478
+ updateDataSeriesLabel(ev) {
37479
+ const label = ev.target.value;
37480
+ const dataSets = this.props.definition.dataSets;
37481
+ if (!dataSets?.[this.state.index])
37232
37482
  return;
37233
- }
37234
37483
  dataSets[this.state.index] = {
37235
37484
  ...dataSets[this.state.index],
37236
- yAxisId: axis === "left" ? "y" : "y1",
37485
+ label,
37237
37486
  };
37238
37487
  this.props.updateChart(this.props.figureId, { dataSets });
37239
37488
  }
37240
- getDataSerieAxis() {
37489
+ getDataSerieLabel() {
37241
37490
  const dataSets = this.props.definition.dataSets;
37242
- if (!dataSets?.[this.state.index]) {
37243
- return "left";
37244
- }
37245
- return dataSets[this.state.index].yAxisId === "y1" ? "right" : "left";
37246
- }
37247
- get canHaveTwoVerticalAxis() {
37248
- return "horizontal" in this.props.definition ? !this.props.definition.horizontal : true;
37491
+ return dataSets[this.state.index]?.label || this.getDataSeries()[this.state.index];
37249
37492
  }
37250
- updateDataSeriesLabel(ev) {
37251
- const label = ev.target.value;
37493
+ }
37494
+
37495
+ class SeriesWithAxisDesignEditor extends owl.Component {
37496
+ static template = "o-spreadsheet-SeriesWithAxisDesignEditor";
37497
+ static components = {
37498
+ SeriesDesignEditor,
37499
+ Checkbox,
37500
+ RadioSelection,
37501
+ Section,
37502
+ RoundColorPicker,
37503
+ };
37504
+ static props = {
37505
+ figureId: String,
37506
+ definition: Object,
37507
+ canUpdateChart: Function,
37508
+ updateChart: Function,
37509
+ slots: { type: Object, optional: true },
37510
+ };
37511
+ axisChoices = CHART_AXIS_CHOICES;
37512
+ updateDataSeriesAxis(index, axis) {
37252
37513
  const dataSets = [...this.props.definition.dataSets];
37253
- if (!dataSets?.[this.state.index]) {
37514
+ if (!dataSets?.[index]) {
37254
37515
  return;
37255
37516
  }
37256
- dataSets[this.state.index] = {
37257
- ...dataSets[this.state.index],
37258
- label,
37517
+ dataSets[index] = {
37518
+ ...dataSets[index],
37519
+ yAxisId: axis === "left" ? "y" : "y1",
37259
37520
  };
37260
37521
  this.props.updateChart(this.props.figureId, { dataSets });
37261
37522
  }
37262
- getDataSerieLabel() {
37523
+ getDataSerieAxis(index) {
37263
37524
  const dataSets = this.props.definition.dataSets;
37264
- return dataSets[this.state.index]?.label || this.getDataSeries()[this.state.index];
37525
+ if (!dataSets?.[index]) {
37526
+ return "left";
37527
+ }
37528
+ return dataSets[index].yAxisId === "y1" ? "right" : "left";
37265
37529
  }
37266
- updateShowValues(showValues) {
37267
- this.props.updateChart(this.props.figureId, { showValues });
37530
+ get canHaveTwoVerticalAxis() {
37531
+ return !("horizontal" in this.props.definition && this.props.definition.horizontal);
37268
37532
  }
37269
- toggleDataTrend(display) {
37533
+ toggleDataTrend(index, display) {
37270
37534
  const dataSets = [...this.props.definition.dataSets];
37271
- if (!dataSets?.[this.state.index]) {
37535
+ if (!dataSets?.[index]) {
37272
37536
  return;
37273
37537
  }
37274
- dataSets[this.state.index] = {
37275
- ...dataSets[this.state.index],
37538
+ dataSets[index] = {
37539
+ ...dataSets[index],
37276
37540
  trend: {
37277
37541
  type: "polynomial",
37278
37542
  order: 1,
37279
- ...dataSets[this.state.index].trend,
37543
+ ...dataSets[index].trend,
37280
37544
  display,
37281
37545
  },
37282
37546
  };
37283
37547
  this.props.updateChart(this.props.figureId, { dataSets });
37284
37548
  }
37285
- getTrendLineConfiguration() {
37549
+ getTrendLineConfiguration(index) {
37286
37550
  const dataSets = this.props.definition.dataSets;
37287
- return dataSets?.[this.state.index]?.trend;
37551
+ return dataSets?.[index]?.trend;
37288
37552
  }
37289
37553
  getTrendType(config) {
37290
37554
  if (!config) {
@@ -37292,7 +37556,7 @@ stores.inject(MyMetaStore, storeInstance);
37292
37556
  }
37293
37557
  return config.type === "polynomial" && config.order === 1 ? "linear" : config.type;
37294
37558
  }
37295
- onChangeTrendType(ev) {
37559
+ onChangeTrendType(index, ev) {
37296
37560
  const type = ev.target.value;
37297
37561
  let config;
37298
37562
  switch (type) {
@@ -37305,37 +37569,59 @@ stores.inject(MyMetaStore, storeInstance);
37305
37569
  break;
37306
37570
  case "exponential":
37307
37571
  case "logarithmic":
37572
+ case "trailingMovingAverage":
37308
37573
  config = { type };
37309
37574
  break;
37310
37575
  default:
37311
37576
  return;
37312
37577
  }
37313
- this.updateTrendLineValue(config);
37578
+ this.updateTrendLineValue(index, config);
37314
37579
  }
37315
- onChangePolynomialDegree(ev) {
37580
+ onChangePolynomialDegree(index, ev) {
37316
37581
  const element = ev.target;
37317
37582
  const order = parseInt(element.value || "1");
37318
37583
  if (order < 2) {
37319
- element.value = `${this.getTrendLineConfiguration()?.order ?? 2}`;
37584
+ element.value = `${this.getTrendLineConfiguration(index)?.order ?? 2}`;
37320
37585
  return;
37321
37586
  }
37322
- this.updateTrendLineValue({ order });
37587
+ this.updateTrendLineValue(index, { order });
37323
37588
  }
37324
- getTrendLineColor() {
37325
- return this.getTrendLineConfiguration()?.color ?? setColorAlpha(this.getDataSerieColor(), 0.5);
37589
+ get defaultWindowSize() {
37590
+ return DEFAULT_WINDOW_SIZE;
37326
37591
  }
37327
- updateTrendLineColor(color) {
37328
- this.updateTrendLineValue({ color });
37592
+ onChangeMovingAverageWindow(index, ev) {
37593
+ const element = ev.target;
37594
+ let window = parseInt(element.value) || DEFAULT_WINDOW_SIZE;
37595
+ if (window <= 1) {
37596
+ window = DEFAULT_WINDOW_SIZE;
37597
+ }
37598
+ this.updateTrendLineValue(index, { window });
37329
37599
  }
37330
- updateTrendLineValue(config) {
37600
+ getDataSerieColor(index) {
37601
+ const dataSets = this.props.definition.dataSets;
37602
+ if (!dataSets?.[index])
37603
+ return "";
37604
+ const color = dataSets[index].backgroundColor;
37605
+ return color
37606
+ ? toHex(color)
37607
+ : getNthColor(index, getColorsPalette(this.props.definition.dataSets.length));
37608
+ }
37609
+ getTrendLineColor(index) {
37610
+ return (this.getTrendLineConfiguration(index)?.color ??
37611
+ setColorAlpha(this.getDataSerieColor(index), 0.5));
37612
+ }
37613
+ updateTrendLineColor(index, color) {
37614
+ this.updateTrendLineValue(index, { color });
37615
+ }
37616
+ updateTrendLineValue(index, config) {
37331
37617
  const dataSets = [...this.props.definition.dataSets];
37332
- if (!dataSets?.[this.state.index]) {
37618
+ if (!dataSets?.[index]) {
37333
37619
  return;
37334
37620
  }
37335
- dataSets[this.state.index] = {
37336
- ...dataSets[this.state.index],
37621
+ dataSets[index] = {
37622
+ ...dataSets[index],
37337
37623
  trend: {
37338
- ...dataSets[this.state.index].trend,
37624
+ ...dataSets[index].trend,
37339
37625
  ...config,
37340
37626
  },
37341
37627
  };
@@ -37343,29 +37629,67 @@ stores.inject(MyMetaStore, storeInstance);
37343
37629
  }
37344
37630
  }
37345
37631
 
37632
+ class ChartWithAxisDesignPanel extends owl.Component {
37633
+ static template = "o-spreadsheet-ChartWithAxisDesignPanel";
37634
+ static components = {
37635
+ GeneralDesignEditor,
37636
+ SidePanelCollapsible,
37637
+ Section,
37638
+ AxisDesignEditor,
37639
+ Checkbox,
37640
+ SeriesWithAxisDesignEditor,
37641
+ };
37642
+ static props = {
37643
+ figureId: String,
37644
+ definition: Object,
37645
+ canUpdateChart: Function,
37646
+ updateChart: Function,
37647
+ };
37648
+ get axesList() {
37649
+ const { useLeftAxis, useRightAxis } = getDefinedAxis(this.props.definition);
37650
+ let axes = [{ id: "x", name: _t("Horizontal axis") }];
37651
+ if (useLeftAxis) {
37652
+ axes.push({ id: "y", name: useRightAxis ? _t("Left axis") : _t("Vertical axis") });
37653
+ }
37654
+ if (useRightAxis) {
37655
+ axes.push({ id: "y1", name: useLeftAxis ? _t("Right axis") : _t("Vertical axis") });
37656
+ }
37657
+ return axes;
37658
+ }
37659
+ updateLegendPosition(ev) {
37660
+ this.props.updateChart(this.props.figureId, {
37661
+ legendPosition: ev.target.value,
37662
+ });
37663
+ }
37664
+ }
37665
+
37346
37666
  class ComboChartDesignPanel extends ChartWithAxisDesignPanel {
37347
37667
  static template = "o-spreadsheet-ComboChartDesignPanel";
37668
+ static components = {
37669
+ ...ChartWithAxisDesignPanel.components,
37670
+ RadioSelection,
37671
+ };
37348
37672
  seriesTypeChoices = [
37349
37673
  { value: "bar", label: _t("Bar") },
37350
37674
  { value: "line", label: _t("Line") },
37351
37675
  ];
37352
- updateDataSeriesType(type) {
37676
+ updateDataSeriesType(index, type) {
37353
37677
  const dataSets = [...this.props.definition.dataSets];
37354
- if (!dataSets?.[this.state.index]) {
37678
+ if (!dataSets?.[index]) {
37355
37679
  return;
37356
37680
  }
37357
- dataSets[this.state.index] = {
37358
- ...dataSets[this.state.index],
37681
+ dataSets[index] = {
37682
+ ...dataSets[index],
37359
37683
  type,
37360
37684
  };
37361
37685
  this.props.updateChart(this.props.figureId, { dataSets });
37362
37686
  }
37363
- getDataSeriesType() {
37687
+ getDataSeriesType(index) {
37364
37688
  const dataSets = this.props.definition.dataSets;
37365
- if (!dataSets?.[this.state.index]) {
37689
+ if (!dataSets?.[index]) {
37366
37690
  return "bar";
37367
37691
  }
37368
- return dataSets[this.state.index].type ?? "line";
37692
+ return dataSets[index].type ?? "line";
37369
37693
  }
37370
37694
  }
37371
37695
 
@@ -37548,11 +37872,6 @@ stores.inject(MyMetaStore, storeInstance);
37548
37872
  stacked,
37549
37873
  });
37550
37874
  }
37551
- onUpdateAggregated(aggregated) {
37552
- this.props.updateChart(this.props.figureId, {
37553
- aggregated,
37554
- });
37555
- }
37556
37875
  onUpdateCumulative(cumulative) {
37557
37876
  this.props.updateChart(this.props.figureId, {
37558
37877
  cumulative,
@@ -37580,6 +37899,27 @@ stores.inject(MyMetaStore, storeInstance);
37580
37899
  }
37581
37900
  }
37582
37901
 
37902
+ class RadarChartDesignPanel extends owl.Component {
37903
+ static template = "o-spreadsheet-RadarChartDesignPanel";
37904
+ static components = {
37905
+ GeneralDesignEditor,
37906
+ SeriesDesignEditor,
37907
+ Section,
37908
+ Checkbox,
37909
+ };
37910
+ static props = {
37911
+ figureId: String,
37912
+ definition: Object,
37913
+ canUpdateChart: Function,
37914
+ updateChart: Function,
37915
+ };
37916
+ updateLegendPosition(ev) {
37917
+ this.props.updateChart(this.props.figureId, {
37918
+ legendPosition: ev.target.value,
37919
+ });
37920
+ }
37921
+ }
37922
+
37583
37923
  class ScatterConfigPanel extends GenericChartConfigPanel {
37584
37924
  static template = "o-spreadsheet-ScatterConfigPanel";
37585
37925
  get canTreatLabelsAsText() {
@@ -37774,9 +38114,6 @@ stores.inject(MyMetaStore, storeInstance);
37774
38114
  verticalAxisPosition: value,
37775
38115
  });
37776
38116
  }
37777
- updateShowValues(showValues) {
37778
- this.props.updateChart(this.props.figureId, { showValues });
37779
- }
37780
38117
  }
37781
38118
 
37782
38119
  const chartSidePanelComponentRegistry = new Registry();
@@ -37816,6 +38153,10 @@ stores.inject(MyMetaStore, storeInstance);
37816
38153
  .add("pyramid", {
37817
38154
  configuration: GenericChartConfigPanel,
37818
38155
  design: ChartWithAxisDesignPanel,
38156
+ })
38157
+ .add("radar", {
38158
+ configuration: GenericChartConfigPanel,
38159
+ design: RadarChartDesignPanel,
37819
38160
  });
37820
38161
 
37821
38162
  css /* scss */ `
@@ -39018,13 +39359,6 @@ stores.inject(MyMetaStore, storeInstance);
39018
39359
  return;
39019
39360
  this.edgeScrollIntervalId = window.setInterval(() => {
39020
39361
  const offset = direction * 3;
39021
- let newPosition = this.currentMousePosition + offset;
39022
- if (newPosition < Math.min(this.container.start, this.minPosition)) {
39023
- newPosition = Math.min(this.container.start, this.minPosition);
39024
- }
39025
- else if (newPosition > Math.max(this.container.end, this.maxPosition)) {
39026
- newPosition = Math.max(this.container.end, this.maxPosition);
39027
- }
39028
39362
  this.container.scroll += offset;
39029
39363
  }, 5);
39030
39364
  }
@@ -39201,7 +39535,6 @@ stores.inject(MyMetaStore, storeInstance);
39201
39535
  width: 142px;
39202
39536
  .o-cf-preview-description-rule {
39203
39537
  margin-bottom: 4px;
39204
- font-weight: 600;
39205
39538
  max-height: 2.8em;
39206
39539
  line-height: 1.4em;
39207
39540
  }
@@ -39673,7 +40006,7 @@ stores.inject(MyMetaStore, storeInstance);
39673
40006
  setColorScaleColor(target, color) {
39674
40007
  const point = this.state.rules.colorScale[target];
39675
40008
  if (point) {
39676
- point.color = Number.parseInt(color.substr(1), 16);
40009
+ point.color = Number.parseInt(color.slice(1), 16);
39677
40010
  }
39678
40011
  this.closeMenus();
39679
40012
  }
@@ -39784,7 +40117,7 @@ stores.inject(MyMetaStore, storeInstance);
39784
40117
  return [this.state.rules.dataBar.rangeValues || ""];
39785
40118
  }
39786
40119
  updateDataBarColor(color) {
39787
- this.state.rules.dataBar.color = Number.parseInt(color.substr(1), 16);
40120
+ this.state.rules.dataBar.color = Number.parseInt(color.slice(1), 16);
39788
40121
  }
39789
40122
  onDataBarRangeUpdate(ranges) {
39790
40123
  this.state.rules.dataBar.rangeValues = ranges[0];
@@ -41197,10 +41530,11 @@ stores.inject(MyMetaStore, storeInstance);
41197
41530
  activeSheetMatches = [];
41198
41531
  specificRangeMatches = [];
41199
41532
  currentSearchRegex = null;
41200
- isSearchDirty = false;
41201
41533
  initialShowFormulaState;
41202
41534
  preserveSelectedMatchIndex = false;
41203
41535
  irreplaceableMatchCount = 0;
41536
+ isSearchDirty = false;
41537
+ shouldFinalizeUpdateSelection = false;
41204
41538
  notificationStore = this.get(NotificationStore);
41205
41539
  // fixme: why do we make selectedMatchIndex on top of a selected
41206
41540
  // property in the matches?
@@ -41246,10 +41580,13 @@ stores.inject(MyMetaStore, storeInstance);
41246
41580
  this.updateSearchOptions({ searchFormulas: showFormula });
41247
41581
  }
41248
41582
  selectPreviousMatch() {
41249
- this.selectNextCell(Direction.previous);
41583
+ this.selectNextCell(Direction.previous, {
41584
+ jumpToMatchSheet: true,
41585
+ updateSelection: true,
41586
+ });
41250
41587
  }
41251
41588
  selectNextMatch() {
41252
- this.selectNextCell(Direction.next);
41589
+ this.selectNextCell(Direction.next, { jumpToMatchSheet: true, updateSelection: true });
41253
41590
  }
41254
41591
  handle(cmd) {
41255
41592
  switch (cmd.type) {
@@ -41266,8 +41603,11 @@ stores.inject(MyMetaStore, storeInstance);
41266
41603
  case "ADD_COLUMNS_ROWS":
41267
41604
  case "EVALUATE_CELLS":
41268
41605
  case "UPDATE_CELL":
41606
+ this.isSearchDirty = true;
41607
+ break;
41269
41608
  case "ACTIVATE_SHEET":
41270
41609
  this.isSearchDirty = true;
41610
+ this.shouldFinalizeUpdateSelection = true;
41271
41611
  break;
41272
41612
  case "REPLACE_SEARCH":
41273
41613
  for (const match of cmd.matches) {
@@ -41282,7 +41622,11 @@ stores.inject(MyMetaStore, storeInstance);
41282
41622
  }
41283
41623
  finalize() {
41284
41624
  if (this.isSearchDirty) {
41285
- this.refreshSearch(false);
41625
+ this.refreshSearch({
41626
+ jumpToMatchSheet: false,
41627
+ updateSelection: this.shouldFinalizeUpdateSelection,
41628
+ });
41629
+ this.shouldFinalizeUpdateSelection = false;
41286
41630
  this.isSearchDirty = false;
41287
41631
  }
41288
41632
  }
@@ -41309,17 +41653,17 @@ stores.inject(MyMetaStore, storeInstance);
41309
41653
  }
41310
41654
  this.toSearch = toSearch;
41311
41655
  this.currentSearchRegex = getSearchRegex(this.toSearch, this.searchOptions);
41312
- this.refreshSearch();
41656
+ this.refreshSearch({ jumpToMatchSheet: true, updateSelection: true });
41313
41657
  }
41314
41658
  /**
41315
41659
  * refresh the matches according to the current search options
41316
41660
  */
41317
- refreshSearch(jumpToMatchSheet = true) {
41661
+ refreshSearch(options) {
41318
41662
  if (!this.preserveSelectedMatchIndex) {
41319
41663
  this.selectedMatchIndex = null;
41320
41664
  }
41321
41665
  this.findMatches();
41322
- this.selectNextCell(Direction.current, jumpToMatchSheet);
41666
+ this.selectNextCell(Direction.current, options);
41323
41667
  }
41324
41668
  getSheetsInSearchOrder() {
41325
41669
  switch (this.searchOptions.searchScope) {
@@ -41389,7 +41733,7 @@ stores.inject(MyMetaStore, storeInstance);
41389
41733
  * It is also used to keep coherence between the selected searchMatch
41390
41734
  * and selectedMatchIndex.
41391
41735
  */
41392
- selectNextCell(indexChange, jumpToMatchSheet = true) {
41736
+ selectNextCell(indexChange, options) {
41393
41737
  const matches = this.searchMatches;
41394
41738
  if (!matches.length) {
41395
41739
  this.selectedMatchIndex = null;
@@ -41415,7 +41759,7 @@ stores.inject(MyMetaStore, storeInstance);
41415
41759
  this.selectedMatchIndex = nextIndex;
41416
41760
  const selectedMatch = matches[nextIndex];
41417
41761
  // Switch to the sheet where the match is located
41418
- if (jumpToMatchSheet && this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
41762
+ if (options.jumpToMatchSheet && this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
41419
41763
  // We set `preserveSelectedMatchIndex` to true to avoid resetting the selected search
41420
41764
  // index in the `refreshSearch` function when a new sheet is activated. The reason being
41421
41765
  // that, when we automatically go back to previous sheet while performing a search, the
@@ -41431,7 +41775,9 @@ stores.inject(MyMetaStore, storeInstance);
41431
41775
  }
41432
41776
  // we want grid selection to capture the selection stream
41433
41777
  this.model.selection.getBackToDefault();
41434
- this.model.selection.selectCell(selectedMatch.col, selectedMatch.row);
41778
+ if (options.updateSelection) {
41779
+ this.model.selection.selectCell(selectedMatch.col, selectedMatch.row);
41780
+ }
41435
41781
  }
41436
41782
  /**
41437
41783
  * Replace the value of the currently selected match
@@ -41446,7 +41792,7 @@ stores.inject(MyMetaStore, storeInstance);
41446
41792
  matches: [this.searchMatches[this.selectedMatchIndex]],
41447
41793
  searchOptions: this.searchOptions,
41448
41794
  });
41449
- this.selectNextCell(Direction.next);
41795
+ this.selectNextCell(Direction.next, { jumpToMatchSheet: true, updateSelection: true });
41450
41796
  }
41451
41797
  /**
41452
41798
  * Apply the replace function to all the matches one time.
@@ -41628,6 +41974,14 @@ stores.inject(MyMetaStore, storeInstance);
41628
41974
  owl.onMounted(() => this.searchInput.el?.focus());
41629
41975
  owl.onWillUnmount(() => this.updateSearchContent.stopDebounce());
41630
41976
  this.updateSearchContent = debounce(this.store.updateSearchContent, 200);
41977
+ owl.useExternalListener(window, "keydown", (ev) => {
41978
+ const code = keyboardEventToShortcutString(ev);
41979
+ if (code === "Ctrl+F" || code === "Ctrl+H") {
41980
+ this.searchInput.el?.focus();
41981
+ ev.preventDefault();
41982
+ ev.stopPropagation();
41983
+ }
41984
+ }, { capture: true });
41631
41985
  }
41632
41986
  onFocusSearch() {
41633
41987
  this.updateDataRange();
@@ -42203,7 +42557,6 @@ stores.inject(MyMetaStore, storeInstance);
42203
42557
  sequence: 0,
42204
42558
  autoSelectFirstProposal: true,
42205
42559
  getProposals(tokenAtCursor) {
42206
- // return []
42207
42560
  const measureProposals = pivot.measures
42208
42561
  .filter((m) => m !== forComputedMeasure)
42209
42562
  .map((measure) => {
@@ -43784,13 +44137,15 @@ stores.inject(MyMetaStore, storeInstance);
43784
44137
  class PivotSidePanelStore extends SpreadsheetStore {
43785
44138
  pivotId;
43786
44139
  mutators = ["reset", "deferUpdates", "applyUpdate", "discardPendingUpdate", "update"];
43787
- updatesAreDeferred = false;
44140
+ updatesAreDeferred;
43788
44141
  draft = null;
43789
44142
  notification = this.get(NotificationStore);
43790
44143
  alreadyNotified = false;
43791
44144
  constructor(get, pivotId) {
43792
44145
  super(get);
43793
44146
  this.pivotId = pivotId;
44147
+ this.updatesAreDeferred =
44148
+ this.getters.getPivotCoreDefinition(this.pivotId).deferUpdates ?? false;
43794
44149
  }
43795
44150
  handle(cmd) {
43796
44151
  switch (cmd.type) {
@@ -43878,10 +44233,14 @@ stores.inject(MyMetaStore, storeInstance);
43878
44233
  this.draft = null;
43879
44234
  }
43880
44235
  deferUpdates(shouldDefer) {
43881
- this.updatesAreDeferred = shouldDefer;
43882
44236
  if (shouldDefer === false && this.draft) {
44237
+ this.draft.deferUpdates = false;
43883
44238
  this.applyUpdate();
43884
44239
  }
44240
+ else {
44241
+ this.update({ deferUpdates: shouldDefer });
44242
+ }
44243
+ this.updatesAreDeferred = shouldDefer;
43885
44244
  }
43886
44245
  applyUpdate() {
43887
44246
  if (this.draft) {
@@ -44139,7 +44498,7 @@ stores.inject(MyMetaStore, storeInstance);
44139
44498
  return colLabel;
44140
44499
  }
44141
44500
  get isEveryColumnSelected() {
44142
- return Object.values(this.state.columns).every((value) => value === true);
44501
+ return Object.values(this.state.columns).every((value) => value);
44143
44502
  }
44144
44503
  get errorMessages() {
44145
44504
  const cancelledReasons = this.env.model.canDispatch("REMOVE_DUPLICATES", {
@@ -44239,8 +44598,7 @@ stores.inject(MyMetaStore, storeInstance);
44239
44598
  const currentLocale = this.currentLocale;
44240
44599
  const localeInLoadedLocales = this.loadedLocales.find((l) => l.code === currentLocale.code);
44241
44600
  if (!localeInLoadedLocales) {
44242
- const locales = [...this.loadedLocales, currentLocale].sort((a, b) => a.name.localeCompare(b.name));
44243
- return locales;
44601
+ return [...this.loadedLocales, currentLocale].sort((a, b) => a.name.localeCompare(b.name));
44244
44602
  }
44245
44603
  else if (!deepEquals(currentLocale, localeInLoadedLocales)) {
44246
44604
  const index = this.loadedLocales.indexOf(localeInLoadedLocales);
@@ -46134,10 +46492,9 @@ stores.inject(MyMetaStore, storeInstance);
46134
46492
  });
46135
46493
  }
46136
46494
  get focus() {
46137
- const focus = this.composerFocusStore.activeComposer === this.composerInterface
46495
+ return this.composerFocusStore.activeComposer === this.composerInterface
46138
46496
  ? this.composerFocusStore.focusMode
46139
46497
  : "inactive";
46140
- return focus;
46141
46498
  }
46142
46499
  get composerProps() {
46143
46500
  const { width, height } = this.env.model.getters.getSheetViewDimensionWithHeaders();
@@ -47144,6 +47501,7 @@ stores.inject(MyMetaStore, storeInstance);
47144
47501
  new CellClipboardHandler(this.getters, this.model.dispatch),
47145
47502
  new BorderClipboardHandler(this.getters, this.model.dispatch),
47146
47503
  new TableClipboardHandler(this.getters, this.model.dispatch),
47504
+ new ConditionalFormatClipboardHandler(this.getters, this.model.dispatch),
47147
47505
  ];
47148
47506
  status = "inactive";
47149
47507
  copiedData;
@@ -47154,6 +47512,13 @@ stores.inject(MyMetaStore, storeInstance);
47154
47512
  this.highlightStore.unRegister(this);
47155
47513
  });
47156
47514
  }
47515
+ handle(cmd) {
47516
+ switch (cmd.type) {
47517
+ case "PAINT_FORMAT":
47518
+ this.paintFormat(cmd.sheetId, cmd.target);
47519
+ break;
47520
+ }
47521
+ }
47157
47522
  activate(args) {
47158
47523
  this.copiedData = this.copyFormats();
47159
47524
  this.status = args.persistent ? "persistent" : "oneOff";
@@ -47163,18 +47528,7 @@ stores.inject(MyMetaStore, storeInstance);
47163
47528
  this.copiedData = undefined;
47164
47529
  }
47165
47530
  pasteFormat(target) {
47166
- if (this.copiedData) {
47167
- const sheetId = this.getters.getActiveSheetId();
47168
- for (const handler of this.clipboardHandlers) {
47169
- handler.paste({ zones: target, sheetId }, this.copiedData, {
47170
- isCutOperation: false,
47171
- pasteOption: "onlyFormat",
47172
- });
47173
- }
47174
- }
47175
- if (this.status === "oneOff") {
47176
- this.cancel();
47177
- }
47531
+ this.model.dispatch("PAINT_FORMAT", { target, sheetId: this.getters.getActiveSheetId() });
47178
47532
  }
47179
47533
  get isActive() {
47180
47534
  return this.status !== "inactive";
@@ -47188,6 +47542,19 @@ stores.inject(MyMetaStore, storeInstance);
47188
47542
  }
47189
47543
  return copiedData;
47190
47544
  }
47545
+ paintFormat(sheetId, target) {
47546
+ if (this.copiedData) {
47547
+ for (const handler of this.clipboardHandlers) {
47548
+ handler.paste({ zones: target, sheetId }, this.copiedData, {
47549
+ isCutOperation: false,
47550
+ pasteOption: "onlyFormat",
47551
+ });
47552
+ }
47553
+ }
47554
+ if (this.status === "oneOff") {
47555
+ this.cancel();
47556
+ }
47557
+ }
47191
47558
  get highlights() {
47192
47559
  const data = this.copiedData;
47193
47560
  if (!data) {
@@ -47564,7 +47931,7 @@ stores.inject(MyMetaStore, storeInstance);
47564
47931
  if (index < 0) {
47565
47932
  return;
47566
47933
  }
47567
- if (this.state.waitingForMove === true) {
47934
+ if (this.state.waitingForMove) {
47568
47935
  if (!this.env.model.getters.isGridSelectionActive()) {
47569
47936
  this._selectElement(index, false);
47570
47937
  }
@@ -49123,7 +49490,7 @@ stores.inject(MyMetaStore, storeInstance);
49123
49490
  }
49124
49491
  open(componentTag, panelProps = {}) {
49125
49492
  const state = this.computeState(componentTag, panelProps);
49126
- if (state.isOpen === false) {
49493
+ if (!state.isOpen) {
49127
49494
  return;
49128
49495
  }
49129
49496
  if (this.isOpen && componentTag !== this.componentTag) {
@@ -49462,6 +49829,8 @@ stores.inject(MyMetaStore, storeInstance);
49462
49829
  },
49463
49830
  "Ctrl+D": async () => this.env.model.dispatch("COPY_PASTE_CELLS_ABOVE"),
49464
49831
  "Ctrl+R": async () => this.env.model.dispatch("COPY_PASTE_CELLS_ON_LEFT"),
49832
+ "Ctrl+H": () => this.sidePanel.open("FindAndReplace", {}),
49833
+ "Ctrl+F": () => this.sidePanel.open("FindAndReplace", {}),
49465
49834
  "Ctrl+Shift+E": () => this.setHorizontalAlign("center"),
49466
49835
  "Ctrl+Shift+L": () => this.setHorizontalAlign("left"),
49467
49836
  "Ctrl+Shift+R": () => this.setHorizontalAlign("right"),
@@ -49869,31 +50238,6 @@ stores.inject(MyMetaStore, storeInstance);
49869
50238
  }
49870
50239
  }
49871
50240
 
49872
- /** @odoo-module */
49873
- class EditableName extends owl.Component {
49874
- static template = "o-spreadsheet-EditableName";
49875
- static props = {
49876
- name: String,
49877
- displayName: String,
49878
- onChanged: Function,
49879
- };
49880
- state;
49881
- setup() {
49882
- this.state = owl.useState({
49883
- isEditing: false,
49884
- name: "",
49885
- });
49886
- }
49887
- rename() {
49888
- this.state.isEditing = true;
49889
- this.state.name = this.props.name;
49890
- }
49891
- save() {
49892
- this.props.onChanged(this.state.name.trim());
49893
- this.state.isEditing = false;
49894
- }
49895
- }
49896
-
49897
50241
  /**
49898
50242
  * BasePlugin
49899
50243
  *
@@ -53811,7 +54155,7 @@ stores.inject(MyMetaStore, storeInstance);
53811
54155
  this.sheetIdsMapName[sheet.name] = sheet.id;
53812
54156
  }
53813
54157
  for (let sheetData of data.sheets) {
53814
- const name = sheetData.name || _t("Sheet") + (Object.keys(this.sheets).length + 1);
54158
+ const name = sheetData.name || "Sheet" + (Object.keys(this.sheets).length + 1);
53815
54159
  const { colNumber, rowNumber } = this.getImportedSheetSize(sheetData);
53816
54160
  const sheet = {
53817
54161
  id: sheetData.id,
@@ -55430,7 +55774,7 @@ stores.inject(MyMetaStore, storeInstance);
55430
55774
  case "DUPLICATE_PIVOT": {
55431
55775
  const { pivotId, newPivotId } = cmd;
55432
55776
  const pivot = deepCopy(this.getPivotCore(pivotId).definition);
55433
- pivot.name = _t("%s (copy)", pivot.name);
55777
+ pivot.name = cmd.duplicatedPivotName ?? pivot.name + " (copy)";
55434
55778
  this.addPivot(newPivotId, pivot);
55435
55779
  break;
55436
55780
  }
@@ -55470,7 +55814,7 @@ stores.inject(MyMetaStore, storeInstance);
55470
55814
  return `(#${formulaId}) ${this.getPivotName(pivotId)}`;
55471
55815
  }
55472
55816
  getPivotName(pivotId) {
55473
- return _t(this.getPivotCore(pivotId).definition.name);
55817
+ return this.getPivotCore(pivotId).definition.name;
55474
55818
  }
55475
55819
  /**
55476
55820
  * Returns the pivot core definition of the pivot with the given id.
@@ -57112,7 +57456,7 @@ stores.inject(MyMetaStore, storeInstance);
57112
57456
  cellsToCompute.addMany(arrayFormulasPositions);
57113
57457
  cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositions));
57114
57458
  this.evaluate(cellsToCompute);
57115
- console.info("evaluate Cells", performance.now() - start, "ms");
57459
+ console.debug("evaluate Cells", performance.now() - start, "ms");
57116
57460
  }
57117
57461
  getArrayFormulasImpactedByChangesOf(positions) {
57118
57462
  const impactedPositions = this.createEmptyPositionSet();
@@ -57156,7 +57500,7 @@ stores.inject(MyMetaStore, storeInstance);
57156
57500
  const start = performance.now();
57157
57501
  this.evaluatedCells = new PositionMap();
57158
57502
  this.evaluate(this.getAllCells());
57159
- console.info("evaluate all cells", performance.now() - start, "ms");
57503
+ console.debug("evaluate all cells", performance.now() - start, "ms");
57160
57504
  }
57161
57505
  evaluateFormulaResult(sheetId, formulaString) {
57162
57506
  const compiledFormula = compile(formulaString);
@@ -58162,8 +58506,7 @@ stores.inject(MyMetaStore, storeInstance);
58162
58506
  .map((cell) => cell.value);
58163
58507
  switch (threshold.type) {
58164
58508
  case "value":
58165
- const result = functionName === "max" ? largeMax(rangeValues) : largeMin(rangeValues);
58166
- return result;
58509
+ return functionName === "max" ? largeMax(rangeValues) : largeMin(rangeValues);
58167
58510
  case "number":
58168
58511
  return Number(threshold.value);
58169
58512
  case "percentage":
@@ -59367,8 +59710,7 @@ stores.inject(MyMetaStore, storeInstance);
59367
59710
  throw new NotAvailableError();
59368
59711
  }
59369
59712
  const comparedValue = this._getPivotCellValueAndFormat(measure.id, comparedDomain);
59370
- const comparedValueNumber = this.strictMeasureValueToNumber(comparedValue);
59371
- return comparedValueNumber;
59713
+ return this.strictMeasureValueToNumber(comparedValue);
59372
59714
  }
59373
59715
  getPivotValueCells(measureId) {
59374
59716
  return this.getTableStructure()
@@ -60902,7 +61244,7 @@ stores.inject(MyMetaStore, storeInstance);
60902
61244
  this.onMessageReceived(message);
60903
61245
  }
60904
61246
  this.isReplayingInitialRevisions = false;
60905
- console.info("Replayed", numberOfCommands, "commands in", performance.now() - start, "ms");
61247
+ console.debug("Replayed", numberOfCommands, "commands in", performance.now() - start, "ms");
60906
61248
  }
60907
61249
  /**
60908
61250
  * Notify the server that the user client left the collaborative session
@@ -61074,7 +61416,6 @@ stores.inject(MyMetaStore, storeInstance);
61074
61416
  if (this.waitingAck) {
61075
61417
  return;
61076
61418
  }
61077
- this.waitingAck = true;
61078
61419
  this.sendPendingMessage();
61079
61420
  }
61080
61421
  /**
@@ -61111,6 +61452,7 @@ stores.inject(MyMetaStore, storeInstance);
61111
61452
  throw new Error(`Trying to send a new revision while replaying initial revision. This can lead to endless dispatches every time the spreadsheet is open.
61112
61453
  ${JSON.stringify(message)}`);
61113
61454
  }
61455
+ this.waitingAck = true;
61114
61456
  this.transportService.sendMessage({
61115
61457
  ...message,
61116
61458
  serverRevisionId: this.serverRevisionId,
@@ -61270,8 +61612,7 @@ stores.inject(MyMetaStore, storeInstance);
61270
61612
  }
61271
61613
  const color = client.color;
61272
61614
  /* Cell background */
61273
- const cellBackgroundColor = `${color}10`;
61274
- ctx.fillStyle = cellBackgroundColor;
61615
+ ctx.fillStyle = `${color}10`;
61275
61616
  ctx.lineWidth = 4 * thinLineWidth;
61276
61617
  ctx.strokeStyle = color;
61277
61618
  ctx.globalCompositeOperation = "multiply";
@@ -61638,8 +61979,7 @@ stores.inject(MyMetaStore, storeInstance);
61638
61979
  exportForExcel(data) {
61639
61980
  for (const sheetData of data.sheets) {
61640
61981
  for (const [row, rowData] of Object.entries(sheetData.rows)) {
61641
- const isHidden = this.isRowHidden(sheetData.id, Number(row));
61642
- rowData.isHidden = isHidden;
61982
+ rowData.isHidden = this.isRowHidden(sheetData.id, Number(row));
61643
61983
  }
61644
61984
  }
61645
61985
  }
@@ -61699,6 +62039,7 @@ stores.inject(MyMetaStore, storeInstance);
61699
62039
  this.dispatch("DUPLICATE_PIVOT", {
61700
62040
  pivotId,
61701
62041
  newPivotId,
62042
+ duplicatedPivotName: _t("%s (copy)", this.getters.getPivotCoreDefinition(pivotId).name),
61702
62043
  });
61703
62044
  const activeSheetId = this.getters.getActiveSheetId();
61704
62045
  const position = this.getters.getSheetIds().indexOf(activeSheetId) + 1;
@@ -62163,7 +62504,6 @@ stores.inject(MyMetaStore, storeInstance);
62163
62504
  if (!isEqual(zone, newZone)) {
62164
62505
  hasExpanded = true;
62165
62506
  zone = newZone;
62166
- continue;
62167
62507
  }
62168
62508
  } while (hasExpanded);
62169
62509
  return zone;
@@ -63256,7 +63596,7 @@ stores.inject(MyMetaStore, storeInstance);
63256
63596
  case "ADD_COLUMNS_ROWS": {
63257
63597
  this.status = "invisible";
63258
63598
  // If we add a col/row inside or before the cut area, we invalidate the clipboard
63259
- if (this._isCutOperation !== true || cmd.sheetId !== this.copiedData?.sheetId) {
63599
+ if (!this._isCutOperation || cmd.sheetId !== this.copiedData?.sheetId) {
63260
63600
  return;
63261
63601
  }
63262
63602
  const isClipboardDirty = this.isColRowDirtyingClipboard(cmd.position === "before" ? cmd.base : cmd.base + 1, cmd.dimension);
@@ -63268,7 +63608,7 @@ stores.inject(MyMetaStore, storeInstance);
63268
63608
  case "REMOVE_COLUMNS_ROWS": {
63269
63609
  this.status = "invisible";
63270
63610
  // If we remove a col/row inside or before the cut area, we invalidate the clipboard
63271
- if (this._isCutOperation !== true || cmd.sheetId !== this.copiedData?.sheetId) {
63611
+ if (!this._isCutOperation || cmd.sheetId !== this.copiedData?.sheetId) {
63272
63612
  return;
63273
63613
  }
63274
63614
  for (let el of cmd.elements) {
@@ -63290,7 +63630,7 @@ stores.inject(MyMetaStore, storeInstance);
63290
63630
  break;
63291
63631
  }
63292
63632
  case "DELETE_SHEET":
63293
- if (this._isCutOperation !== true) {
63633
+ if (!this._isCutOperation) {
63294
63634
  return;
63295
63635
  }
63296
63636
  if (this.originSheetId === cmd.sheetId) {
@@ -64183,8 +64523,7 @@ stores.inject(MyMetaStore, storeInstance);
64183
64523
  this.setSelectionMixin({ zone, cell: { col, row } }, [zone]);
64184
64524
  }
64185
64525
  setActiveSheet(id) {
64186
- const sheet = this.getters.getSheet(id);
64187
- this.activeSheet = sheet;
64526
+ this.activeSheet = this.getters.getSheet(id);
64188
64527
  }
64189
64528
  activateNextSheet(direction) {
64190
64529
  const sheetIds = this.getters.getSheetIds();
@@ -64878,9 +65217,6 @@ stores.inject(MyMetaStore, storeInstance);
64878
65217
  case "UNFREEZE_COLUMNS_ROWS":
64879
65218
  this.resetViewports(this.getters.getActiveSheetId());
64880
65219
  break;
64881
- case "DELETE_SHEET":
64882
- this.sheetsWithDirtyViewports.delete(cmd.sheetId);
64883
- break;
64884
65220
  case "SCROLL_TO_CELL":
64885
65221
  this.refreshViewport(this.getters.getActiveSheetId(), { col: cmd.col, row: cmd.row });
64886
65222
  break;
@@ -66832,10 +67168,9 @@ stores.inject(MyMetaStore, storeInstance);
66832
67168
  user-select: none;
66833
67169
  color: ${TEXT_BODY};
66834
67170
 
66835
- .o-heading-3 {
67171
+ .o-sidePanelTitle {
66836
67172
  line-height: 20px;
66837
67173
  font-size: 16px;
66838
- font-weight: 600;
66839
67174
  }
66840
67175
 
66841
67176
  .o-sidePanelHeader {
@@ -66920,6 +67255,10 @@ stores.inject(MyMetaStore, storeInstance);
66920
67255
  }
66921
67256
  }
66922
67257
  }
67258
+
67259
+ .o-fw-bold {
67260
+ font-weight: 500;
67261
+ }
66923
67262
  `;
66924
67263
  class SidePanel extends owl.Component {
66925
67264
  static template = "o-spreadsheet-SidePanel";
@@ -67751,8 +68090,7 @@ stores.inject(MyMetaStore, storeInstance);
67751
68090
  for (const item of clipboardItems) {
67752
68091
  for (const type of item.types) {
67753
68092
  const blob = await item.getType(type);
67754
- const text = await blob.text();
67755
- clipboardContent[type] = text;
68093
+ clipboardContent[type] = await blob.text();
67756
68094
  }
67757
68095
  }
67758
68096
  return { status: "ok", content: clipboardContent };
@@ -68064,7 +68402,6 @@ stores.inject(MyMetaStore, storeInstance);
68064
68402
  spreadsheetRef = owl.useRef("spreadsheet");
68065
68403
  spreadsheetRect = useSpreadsheetRect();
68066
68404
  _focusGrid;
68067
- keyDownMapping;
68068
68405
  isViewportTooSmall = false;
68069
68406
  notificationStore;
68070
68407
  composerFocusStore;
@@ -68088,10 +68425,6 @@ stores.inject(MyMetaStore, storeInstance);
68088
68425
  this.notificationStore = useStore(NotificationStore);
68089
68426
  this.composerFocusStore = useStore(ComposerFocusStore);
68090
68427
  this.sidePanel = useStore(SidePanelStore);
68091
- this.keyDownMapping = {
68092
- "CTRL+H": () => this.sidePanel.toggle("FindAndReplace", {}),
68093
- "CTRL+F": () => this.sidePanel.toggle("FindAndReplace", {}),
68094
- };
68095
68428
  const fileStore = this.model.config.external.fileStore;
68096
68429
  owl.useSubEnv({
68097
68430
  model: this.model,
@@ -68191,20 +68524,6 @@ stores.inject(MyMetaStore, storeInstance);
68191
68524
  }
68192
68525
  this._focusGrid();
68193
68526
  }
68194
- onKeydown(ev) {
68195
- let keyDownString = "";
68196
- if (isCtrlKey(ev)) {
68197
- keyDownString += "CTRL+";
68198
- }
68199
- keyDownString += ev.key.toUpperCase();
68200
- let handler = this.keyDownMapping[keyDownString];
68201
- if (handler) {
68202
- ev.preventDefault();
68203
- ev.stopPropagation();
68204
- handler();
68205
- return;
68206
- }
68207
- }
68208
68527
  get gridHeight() {
68209
68528
  const { height } = this.env.model.getters.getSheetViewDimension();
68210
68529
  return height;
@@ -69619,10 +69938,10 @@ stores.inject(MyMetaStore, storeInstance);
69619
69938
  getNextCellPosition(currentPosition, dimension, direction) {
69620
69939
  const dimOfInterest = dimension === "cols" ? "col" : "row";
69621
69940
  const startingPosition = { ...currentPosition };
69622
- const nextCoord = dimension === "cols"
69623
- ? this.getNextAvailableCol(direction, startingPosition.col, startingPosition.row)
69624
- : this.getNextAvailableRow(direction, startingPosition.col, startingPosition.row);
69625
- startingPosition[dimOfInterest] = nextCoord;
69941
+ startingPosition[dimOfInterest] =
69942
+ dimension === "cols"
69943
+ ? this.getNextAvailableCol(direction, startingPosition.col, startingPosition.row)
69944
+ : this.getNextAvailableRow(direction, startingPosition.col, startingPosition.row);
69626
69945
  return { col: startingPosition.col, row: startingPosition.row };
69627
69946
  }
69628
69947
  getPosition() {
@@ -69746,6 +70065,8 @@ stores.inject(MyMetaStore, storeInstance);
69746
70065
  case "pie":
69747
70066
  plot = addDoughnutChart(chart.data, chartSheetIndex, data, { holeSize: 0 });
69748
70067
  break;
70068
+ case "radar":
70069
+ plot = addRadarChart(chart.data);
69749
70070
  }
69750
70071
  let position = "t";
69751
70072
  switch (chart.data.legendPosition) {
@@ -70204,6 +70525,53 @@ stores.inject(MyMetaStore, storeInstance);
70204
70525
  `
70205
70526
  : ""}`;
70206
70527
  }
70528
+ function addRadarChart(chart) {
70529
+ const dataSetsColors = chart.dataSets.map((ds) => ds.backgroundColor ?? "");
70530
+ const colors = new ColorGenerator(chart.dataSets.length, dataSetsColors);
70531
+ const dataSetsNodes = [];
70532
+ for (const [dsIndex, dataset] of Object.entries(chart.dataSets)) {
70533
+ const color = toXlsxHexColor(colors.next());
70534
+ const dataShapeProperty = shapeProperty({
70535
+ line: {
70536
+ width: 2.5,
70537
+ style: "solid",
70538
+ color,
70539
+ },
70540
+ });
70541
+ const dataSetNode = escapeXml /*xml*/ `
70542
+ <c:ser>
70543
+ <c:idx val="${dsIndex}"/>
70544
+ <c:order val="${dsIndex}"/>
70545
+ <c:smooth val="0"/>
70546
+ <c:marker>
70547
+ <c:symbol val="circle" />
70548
+ <c:size val="5"/>
70549
+ ${shapeProperty({ backgroundColor: color, line: { color } })}
70550
+ </c:marker>
70551
+ ${extractDataSetLabel(dataset.label)}
70552
+ ${dataShapeProperty}
70553
+ ${chart.labelRange ? escapeXml `<c:cat>${stringRef(chart.labelRange)}</c:cat>` : ""} <!-- x-coordinate values -->
70554
+ <c:val> <!-- x-coordinate values -->
70555
+ ${numberRef(dataset.range)}
70556
+ </c:val>
70557
+ </c:ser>
70558
+ `;
70559
+ dataSetsNodes.push(dataSetNode);
70560
+ }
70561
+ return escapeXml /*xml*/ `
70562
+ ${escapeXml /*xml*/ `
70563
+ <c:radarChart>
70564
+ <c:radarStyle val="marker"/>
70565
+ <c:varyColors val="0"/>
70566
+ ${joinXmlNodes(dataSetsNodes)}
70567
+ <c:axId val="${catAxId}" />
70568
+ <c:axId val="${valAxId}" />
70569
+ </c:radarChart>
70570
+ ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor)}
70571
+ ${addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)}
70572
+ `}
70573
+ `;
70574
+ }
70207
70575
  function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
70208
70576
  const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
70209
70577
  const colors = new ColorGenerator(maxLength);
@@ -71311,25 +71679,23 @@ stores.inject(MyMetaStore, storeInstance);
71311
71679
  ["showGridLines", sheet.areGridLinesVisible ? 1 : 0],
71312
71680
  ["workbookViewId", 0],
71313
71681
  ];
71314
- let sheetView = escapeXml /*xml*/ `
71682
+ return escapeXml /*xml*/ `
71315
71683
  <sheetViews>
71316
71684
  <sheetView ${formatAttributes(sheetViewAttrs)}>
71317
71685
  ${splitPanes}
71318
71686
  </sheetView>
71319
71687
  </sheetViews>
71320
71688
  `;
71321
- return sheetView;
71322
71689
  }
71323
71690
  function addSheetProperties(sheet) {
71324
71691
  if (!sheet.color) {
71325
71692
  return "";
71326
71693
  }
71327
- let sheetView = escapeXml /*xml*/ `
71694
+ return escapeXml /*xml*/ `
71328
71695
  <sheetPr>
71329
71696
  <tabColor ${formatAttributes([["rgb", toXlsxHexColor(sheet.color)]])} />
71330
71697
  </sheetPr>
71331
71698
  `;
71332
- return sheetView;
71333
71699
  }
71334
71700
 
71335
71701
  /**
@@ -71642,9 +72008,7 @@ stores.inject(MyMetaStore, storeInstance);
71642
72008
  })(Status || (Status = {}));
71643
72009
  class Model extends EventBus {
71644
72010
  corePlugins = [];
71645
- featurePlugins = [];
71646
72011
  statefulUIPlugins = [];
71647
- coreViewsPlugins = [];
71648
72012
  range;
71649
72013
  session;
71650
72014
  /**
@@ -71689,7 +72053,7 @@ stores.inject(MyMetaStore, storeInstance);
71689
72053
  coreHandlers = [];
71690
72054
  constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = false) {
71691
72055
  const start = performance.now();
71692
- console.group("Model creation");
72056
+ console.debug("##### Model creation #####");
71693
72057
  super();
71694
72058
  setDefaultTranslationMethod();
71695
72059
  stateUpdateMessages = repairInitialMessages(data, stateUpdateMessages);
@@ -71730,7 +72094,6 @@ stores.inject(MyMetaStore, storeInstance);
71730
72094
  this.session.loadInitialMessages(stateUpdateMessages);
71731
72095
  for (let Plugin of coreViewsPluginRegistry.getAll()) {
71732
72096
  const plugin = this.setupUiPlugin(Plugin);
71733
- this.coreViewsPlugins.push(plugin);
71734
72097
  this.handlers.push(plugin);
71735
72098
  this.uiHandlers.push(plugin);
71736
72099
  this.coreHandlers.push(plugin);
@@ -71743,7 +72106,6 @@ stores.inject(MyMetaStore, storeInstance);
71743
72106
  }
71744
72107
  for (let Plugin of featurePluginRegistry.getAll()) {
71745
72108
  const plugin = this.setupUiPlugin(Plugin);
71746
- this.featurePlugins.push(plugin);
71747
72109
  this.handlers.push(plugin);
71748
72110
  this.uiHandlers.push(plugin);
71749
72111
  }
@@ -71760,16 +72122,16 @@ stores.inject(MyMetaStore, storeInstance);
71760
72122
  this.joinSession();
71761
72123
  if (config.snapshotRequested) {
71762
72124
  const startSnapshot = performance.now();
71763
- console.info("Snapshot requested");
72125
+ console.debug("Snapshot requested");
71764
72126
  this.session.snapshot(this.exportData());
71765
72127
  this.garbageCollectExternalResources();
71766
- console.info("Snapshot taken in", performance.now() - startSnapshot, "ms");
72128
+ console.debug("Snapshot taken in", performance.now() - startSnapshot, "ms");
71767
72129
  }
71768
72130
  // mark all models as "raw", so they will not be turned into reactive objects
71769
72131
  // by owl, since we do not rely on reactivity
71770
72132
  owl.markRaw(this);
71771
- console.info("Model created in", performance.now() - start, "ms");
71772
- console.groupEnd();
72133
+ console.debug("Model created in", performance.now() - start, "ms");
72134
+ console.debug("######");
71773
72135
  }
71774
72136
  joinSession() {
71775
72137
  this.session.join(this.config.client);
@@ -71828,7 +72190,7 @@ stores.inject(MyMetaStore, storeInstance);
71828
72190
  this.finalize();
71829
72191
  }
71830
72192
  setupSession(revisionId) {
71831
- const session = new Session(buildRevisionLog({
72193
+ return new Session(buildRevisionLog({
71832
72194
  initialRevisionId: revisionId,
71833
72195
  recordChanges: this.state.recordChanges.bind(this.state),
71834
72196
  dispatch: (command) => {
@@ -71841,7 +72203,6 @@ stores.inject(MyMetaStore, storeInstance);
71841
72203
  this.isReplayingCommand = false;
71842
72204
  },
71843
72205
  }), this.config.transportService, revisionId);
71844
- return session;
71845
72206
  }
71846
72207
  setupSessionEvents() {
71847
72208
  this.session.on("remote-revision-received", this, this.onRemoteRevisionReceived);
@@ -71934,8 +72295,7 @@ stores.inject(MyMetaStore, storeInstance);
71934
72295
  return results;
71935
72296
  }
71936
72297
  checkDispatchAllowedLocalCommand(command) {
71937
- const results = this.uiHandlers.map((handler) => handler.allowDispatch(command));
71938
- return results;
72298
+ return this.uiHandlers.map((handler) => handler.allowDispatch(command));
71939
72299
  }
71940
72300
  finalize() {
71941
72301
  this.status = 3 /* Status.Finalizing */;
@@ -71992,7 +72352,7 @@ stores.inject(MyMetaStore, storeInstance);
71992
72352
  this.finalize();
71993
72353
  const time = performance.now() - start;
71994
72354
  if (time > 5) {
71995
- console.info(type, time, "ms");
72355
+ console.debug(type, time, "ms");
71996
72356
  }
71997
72357
  });
71998
72358
  this.session.save(command, commands, changes);
@@ -72295,7 +72655,6 @@ stores.inject(MyMetaStore, storeInstance);
72295
72655
  PivotDimensionOrder,
72296
72656
  PivotDimension,
72297
72657
  PivotLayoutConfigurator,
72298
- EditableName,
72299
72658
  PivotDeferUpdate,
72300
72659
  PivotTitleSection,
72301
72660
  CogWheelMenu,
@@ -72387,9 +72746,9 @@ stores.inject(MyMetaStore, storeInstance);
72387
72746
  exports.tokenize = tokenize;
72388
72747
 
72389
72748
 
72390
- __info__.version = "18.1.0-alpha.1";
72391
- __info__.date = "2024-10-14T07:53:17.717Z";
72392
- __info__.hash = "e9ce3aa";
72749
+ __info__.version = "18.1.0-alpha.2";
72750
+ __info__.date = "2024-10-24T08:53:21.828Z";
72751
+ __info__.hash = "2a01250";
72393
72752
 
72394
72753
 
72395
72754
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);