@odoo/o-spreadsheet 18.0.1 → 18.0.3

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.0.1
6
- * @date 2024-10-14T07:54:24.768Z
7
- * @hash 1771f68
5
+ * @version 18.0.3
6
+ * @date 2024-11-08T12:17:49.992Z
7
+ * @hash 5fa5fbb
8
8
  */
9
9
 
10
10
  'use strict';
@@ -376,7 +376,7 @@ const PIVOT_TABLE_CONFIG = {
376
376
  bandedRows: true,
377
377
  bandedColumns: false,
378
378
  styleId: "TableStyleMedium5",
379
- automaticAutofill: true,
379
+ automaticAutofill: false,
380
380
  };
381
381
  const DEFAULT_CURRENCY = {
382
382
  symbol: "$",
@@ -9346,14 +9346,19 @@ function interpolateData(config, values, labels, newLabels) {
9346
9346
  if (values.length < 2 || labels.length < 2 || newLabels.length === 0) {
9347
9347
  return [];
9348
9348
  }
9349
+ const labelMin = Math.min(...labels);
9350
+ const labelMax = Math.max(...labels);
9351
+ const labelRange = labelMax - labelMin;
9352
+ const normalizedLabels = labels.map((v) => (v - labelMin) / labelRange);
9353
+ const normalizedNewLabels = newLabels.map((v) => (v - labelMin) / labelRange);
9349
9354
  switch (config.type) {
9350
9355
  case "polynomial": {
9351
9356
  const order = config.order ?? 2;
9352
9357
  if (order === 1) {
9353
- return predictLinearValues([values], [labels], [newLabels], true)[0];
9358
+ return predictLinearValues([values], [normalizedLabels], [normalizedNewLabels], true)[0];
9354
9359
  }
9355
- const coeffs = polynomialRegression(values, labels, order, true).flat();
9356
- return newLabels.map((v) => evaluatePolynomial(coeffs, v, order));
9360
+ const coeffs = polynomialRegression(values, normalizedLabels, order, true).flat();
9361
+ return normalizedNewLabels.map((v) => evaluatePolynomial(coeffs, v, order));
9357
9362
  }
9358
9363
  case "exponential": {
9359
9364
  const positiveLogValues = [];
@@ -9361,16 +9366,16 @@ function interpolateData(config, values, labels, newLabels) {
9361
9366
  for (let i = 0; i < values.length; i++) {
9362
9367
  if (values[i] > 0) {
9363
9368
  positiveLogValues.push(Math.log(values[i]));
9364
- filteredLabels.push(labels[i]);
9369
+ filteredLabels.push(normalizedLabels[i]);
9365
9370
  }
9366
9371
  }
9367
9372
  if (!filteredLabels.length) {
9368
9373
  return [];
9369
9374
  }
9370
- return expM(predictLinearValues([positiveLogValues], [filteredLabels], [newLabels], true))[0];
9375
+ return expM(predictLinearValues([positiveLogValues], [filteredLabels], [normalizedNewLabels], true))[0];
9371
9376
  }
9372
9377
  case "logarithmic": {
9373
- return predictLinearValues([values], logM([labels]), logM([newLabels]), true)[0];
9378
+ return predictLinearValues([values], logM([normalizedLabels]), logM([normalizedNewLabels]), true)[0];
9374
9379
  }
9375
9380
  default:
9376
9381
  return [];
@@ -9411,70 +9416,118 @@ const chartShowValuesPlugin = {
9411
9416
  ctx.save();
9412
9417
  ctx.textAlign = "center";
9413
9418
  ctx.textBaseline = "middle";
9414
- ctx.fillStyle = chartFontColor(options.background);
9415
- ctx.strokeStyle = chartFontColor(ctx.fillStyle);
9416
- chart._metasets.forEach(function (dataset) {
9417
- if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
9418
- return; // ignore trend lines
9419
- }
9420
- switch (dataset.type) {
9421
- case "doughnut":
9422
- case "pie": {
9423
- for (let i = 0; i < dataset._parsed.length; i++) {
9424
- const bar = dataset.data[i];
9425
- const { startAngle, endAngle, innerRadius, outerRadius } = bar;
9426
- const midAngle = (startAngle + endAngle) / 2;
9427
- const midRadius = (innerRadius + outerRadius) / 2;
9428
- const x = bar.x + midRadius * Math.cos(midAngle);
9429
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
9430
- ctx.fillStyle = chartFontColor(bar.options.backgroundColor);
9431
- ctx.strokeStyle = chartFontColor(ctx.fillStyle);
9432
- const value = options.callback(dataset._parsed[i]);
9433
- ctx.strokeText(value, x, y);
9434
- ctx.fillText(value, x, y);
9435
- }
9436
- break;
9437
- }
9438
- case "bar":
9439
- case "line": {
9440
- const yOffset = dataset.type === "bar" && !options.horizontal ? 0 : 3;
9441
- for (let i = 0; i < dataset._parsed.length; i++) {
9442
- const point = dataset.data[i];
9443
- const value = options.horizontal ? dataset._parsed[i].x : dataset._parsed[i].y;
9444
- const displayedValue = options.callback(value - 0);
9445
- let xPosition = 0, yPosition = 0;
9446
- if (options.horizontal) {
9447
- yPosition = point.y;
9448
- if (value < 0) {
9449
- ctx.textAlign = "right";
9450
- xPosition = point.x - yOffset;
9451
- }
9452
- else {
9453
- ctx.textAlign = "left";
9454
- xPosition = point.x + yOffset;
9455
- }
9456
- }
9457
- else {
9458
- xPosition = point.x;
9459
- if (value < 0) {
9460
- ctx.textBaseline = "top";
9461
- yPosition = point.y + yOffset;
9462
- }
9463
- else {
9464
- ctx.textBaseline = "bottom";
9465
- yPosition = point.y - yOffset;
9466
- }
9467
- }
9468
- ctx.strokeText(displayedValue, xPosition, yPosition);
9469
- ctx.fillText(displayedValue, xPosition, yPosition);
9470
- }
9471
- break;
9472
- }
9473
- }
9474
- });
9419
+ ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
9420
+ switch (chart.config.type) {
9421
+ case "pie":
9422
+ case "doughnut":
9423
+ drawPieChartValues(chart, options, ctx);
9424
+ break;
9425
+ case "bar":
9426
+ case "line":
9427
+ options.horizontal
9428
+ ? drawHorizontalBarChartValues(chart, options, ctx)
9429
+ : drawLineOrBarChartValues(chart, options, ctx);
9430
+ break;
9431
+ }
9475
9432
  ctx.restore();
9476
9433
  },
9477
9434
  };
9435
+ function drawTextWithBackground(text, x, y, ctx) {
9436
+ ctx.lineWidth = 3; // Stroke the text with a big lineWidth width to have some kind of background
9437
+ ctx.strokeText(text, x, y);
9438
+ ctx.lineWidth = 1;
9439
+ ctx.fillText(text, x, y);
9440
+ }
9441
+ function drawLineOrBarChartValues(chart, options, ctx) {
9442
+ const yMax = chart.chartArea.bottom;
9443
+ const yMin = chart.chartArea.top;
9444
+ const textsPositions = {};
9445
+ for (const dataset of chart._metasets) {
9446
+ if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
9447
+ return; // ignore trend lines
9448
+ }
9449
+ for (let i = 0; i < dataset._parsed.length; i++) {
9450
+ const value = dataset._parsed[i].y;
9451
+ const point = dataset.data[i];
9452
+ const xPosition = point.x;
9453
+ let yPosition = 0;
9454
+ if (chart.config.type === "line") {
9455
+ yPosition = point.y - 10;
9456
+ }
9457
+ else {
9458
+ yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
9459
+ }
9460
+ yPosition = Math.min(yPosition, yMax);
9461
+ yPosition = Math.max(yPosition, yMin);
9462
+ // Avoid overlapping texts with same X
9463
+ if (!textsPositions[xPosition]) {
9464
+ textsPositions[xPosition] = [];
9465
+ }
9466
+ for (const otherPosition of textsPositions[xPosition] || []) {
9467
+ if (Math.abs(otherPosition - yPosition) < 13) {
9468
+ yPosition = otherPosition - 13;
9469
+ }
9470
+ }
9471
+ textsPositions[xPosition].push(yPosition);
9472
+ ctx.fillStyle = point.options.backgroundColor;
9473
+ ctx.strokeStyle = options.background || "#ffffff";
9474
+ drawTextWithBackground(options.callback(value - 0), xPosition, yPosition, ctx);
9475
+ }
9476
+ }
9477
+ }
9478
+ function drawHorizontalBarChartValues(chart, options, ctx) {
9479
+ const xMax = chart.chartArea.right;
9480
+ const xMin = chart.chartArea.left;
9481
+ const textsPositions = {};
9482
+ for (const dataset of chart._metasets) {
9483
+ if (dataset.xAxisID === TREND_LINE_XAXIS_ID) {
9484
+ return; // ignore trend lines
9485
+ }
9486
+ for (let i = 0; i < dataset._parsed.length; i++) {
9487
+ const value = dataset._parsed[i].x;
9488
+ const displayValue = options.callback(value - 0);
9489
+ const point = dataset.data[i];
9490
+ const yPosition = point.y;
9491
+ let xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
9492
+ xPosition = Math.min(xPosition, xMax);
9493
+ xPosition = Math.max(xPosition, xMin);
9494
+ // Avoid overlapping texts with same Y
9495
+ if (!textsPositions[yPosition]) {
9496
+ textsPositions[yPosition] = [];
9497
+ }
9498
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
9499
+ for (const otherPosition of textsPositions[yPosition]) {
9500
+ if (Math.abs(otherPosition - xPosition) < textWidth) {
9501
+ xPosition = otherPosition + textWidth + 3;
9502
+ }
9503
+ }
9504
+ textsPositions[yPosition].push(xPosition);
9505
+ ctx.fillStyle = point.options.backgroundColor;
9506
+ ctx.strokeStyle = options.background || "#ffffff";
9507
+ drawTextWithBackground(displayValue, xPosition, yPosition, ctx);
9508
+ }
9509
+ }
9510
+ }
9511
+ function drawPieChartValues(chart, options, ctx) {
9512
+ for (const dataset of chart._metasets) {
9513
+ for (let i = 0; i < dataset._parsed.length; i++) {
9514
+ const value = Number(dataset._parsed[i]);
9515
+ if (isNaN(value) || value === 0) {
9516
+ continue;
9517
+ }
9518
+ const bar = dataset.data[i];
9519
+ const { startAngle, endAngle, innerRadius, outerRadius } = bar;
9520
+ const midAngle = (startAngle + endAngle) / 2;
9521
+ const midRadius = (innerRadius + outerRadius) / 2;
9522
+ const x = bar.x + midRadius * Math.cos(midAngle);
9523
+ const y = bar.y + midRadius * Math.sin(midAngle) + 7;
9524
+ ctx.fillStyle = chartFontColor(options.background);
9525
+ ctx.strokeStyle = options.background || "#ffffff";
9526
+ const displayValue = options.callback(value);
9527
+ drawTextWithBackground(displayValue, x, y, ctx);
9528
+ }
9529
+ }
9530
+ }
9478
9531
 
9479
9532
  /** This is a chartJS plugin that will draw connector lines between the bars of a Waterfall chart */
9480
9533
  const waterfallLinesPlugin = {
@@ -14550,7 +14603,7 @@ const FILTER = {
14550
14603
  const result = [];
14551
14604
  for (let i = 0; i < _array.length; i++) {
14552
14605
  const row = _array[i];
14553
- if (_conditions.every((c) => c[i])) {
14606
+ if (_conditions.every((c) => (typeof c[i] === "boolean" || typeof c[i] === "number") && c[i])) {
14554
14607
  result.push(row);
14555
14608
  }
14556
14609
  }
@@ -16945,7 +16998,10 @@ function parseOperand(tokens) {
16945
16998
  case "STRING":
16946
16999
  return { type: "STRING", value: removeStringQuotes(current.value) };
16947
17000
  case "INVALID_REFERENCE":
16948
- throw new InvalidReferenceError();
17001
+ return {
17002
+ type: "REFERENCE",
17003
+ value: CellErrorType.InvalidReference,
17004
+ };
16949
17005
  case "REFERENCE":
16950
17006
  if (tokens[0]?.value === ":" && tokens[1]?.type === "REFERENCE") {
16951
17007
  tokens.shift();
@@ -21737,9 +21793,13 @@ autoCompleteProviders.add("pivot_group_fields", {
21737
21793
  const colFields = columns.map((groupBy) => groupBy.nameWithGranularity);
21738
21794
  const rowFields = rows.map((groupBy) => groupBy.nameWithGranularity);
21739
21795
  const proposals = [];
21740
- const previousGroupBy = ["ARG_SEPARATOR", "SPACE"].includes(tokenAtCursor.type)
21796
+ let previousGroupBy = ["ARG_SEPARATOR", "SPACE"].includes(tokenAtCursor.type)
21741
21797
  ? argGroupBys.at(-1)
21742
21798
  : argGroupBys.at(-2);
21799
+ const isPositionalSupported = supportedPivotPositionalFormulaRegistry.get(pivot.type);
21800
+ if (isPositionalSupported && previousGroupBy?.startsWith("#")) {
21801
+ previousGroupBy = previousGroupBy.slice(1);
21802
+ }
21743
21803
  if (previousGroupBy === undefined) {
21744
21804
  proposals.push(colFields[0]);
21745
21805
  proposals.push(rowFields[0]);
@@ -21761,7 +21821,7 @@ autoCompleteProviders.add("pivot_group_fields", {
21761
21821
  return field ? makeFieldProposal(field, granularity) : undefined;
21762
21822
  })
21763
21823
  .concat(groupBys.map((groupBy) => {
21764
- if (!supportedPivotPositionalFormulaRegistry.get(pivot.type)) {
21824
+ if (!isPositionalSupported) {
21765
21825
  return undefined;
21766
21826
  }
21767
21827
  const fieldName = groupBy.split(":")[0];
@@ -27457,7 +27517,7 @@ function load(data, verboseImport) {
27457
27517
  if (!data) {
27458
27518
  return createEmptyWorkbookData();
27459
27519
  }
27460
- console.group("Loading data");
27520
+ console.debug("### Loading data ###");
27461
27521
  const start = performance.now();
27462
27522
  if (data["[Content_Types].xml"]) {
27463
27523
  const reader = new XlsxReader(data);
@@ -27471,13 +27531,13 @@ function load(data, verboseImport) {
27471
27531
  // apply migrations, if needed
27472
27532
  if ("version" in data) {
27473
27533
  if (data.version < CURRENT_VERSION) {
27474
- console.info("Migrating data from version", data.version);
27534
+ console.debug("Migrating data from version", data.version);
27475
27535
  data = migrate(data);
27476
27536
  }
27477
27537
  }
27478
27538
  data = repairData(data);
27479
- console.info("Data loaded in", performance.now() - start, "ms");
27480
- console.groupEnd();
27539
+ console.debug("Data loaded in", performance.now() - start, "ms");
27540
+ console.debug("###");
27481
27541
  return data;
27482
27542
  }
27483
27543
  // -----------------------------------------------------------------------------
@@ -27507,7 +27567,7 @@ function migrate(data) {
27507
27567
  for (let i = index; i < steps.length; i++) {
27508
27568
  data = steps[i].migrate(data);
27509
27569
  }
27510
- console.info("Data migrated in", performance.now() - start, "ms");
27570
+ console.debug("Data migrated in", performance.now() - start, "ms");
27511
27571
  return data;
27512
27572
  }
27513
27573
  /**
@@ -27825,6 +27885,7 @@ const ChartTerms = {
27825
27885
  StackedBarChart: _t("Stacked bar chart"),
27826
27886
  StackedLineChart: _t("Stacked line chart"),
27827
27887
  StackedAreaChart: _t("Stacked area chart"),
27888
+ StackedColumnChart: _t("Stacked column chart"),
27828
27889
  CumulativeData: _t("Cumulative data"),
27829
27890
  TreatLabelsAsText: _t("Treat labels as text"),
27830
27891
  AggregatedChart: _t("Aggregate"),
@@ -28086,7 +28147,7 @@ function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale, tr
28086
28147
  const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
28087
28148
  // tooltipItem.parsed can be an object or a number for pie charts
28088
28149
  let yLabel = horizontalChart ? tooltipItem.parsed.x : tooltipItem.parsed.y;
28089
- if (!yLabel) {
28150
+ if (yLabel === undefined || yLabel === null) {
28090
28151
  yLabel = tooltipItem.parsed;
28091
28152
  }
28092
28153
  const toolTipFormat = !format && Math.abs(yLabel) >= 1000 ? "#,##" : format;
@@ -28527,13 +28588,10 @@ function createBarChartRuntime(chart, getters) {
28527
28588
  * datasets to ensure the way we distinguish the originals and trendLine datasets after
28528
28589
  */
28529
28590
  trendDatasets.forEach((x) => config.data.datasets.push(x));
28530
- const originalTooltipTitle = config.options.plugins.tooltip.callbacks.title;
28531
28591
  config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
28532
- if (tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)) {
28533
- // @ts-expect-error
28534
- return originalTooltipTitle?.(tooltipItems);
28535
- }
28536
- return "";
28592
+ return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
28593
+ ? undefined
28594
+ : "";
28537
28595
  };
28538
28596
  }
28539
28597
  return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
@@ -28766,8 +28824,8 @@ function getTrendDatasetForLineChart(config, dataset, axisType, locale) {
28766
28824
  break;
28767
28825
  case "time":
28768
28826
  for (const point of dataset.data) {
28769
- const date = toJsDate({ value: point.x }, locale).getTime();
28770
- if (typeof point.y === "number") {
28827
+ const date = toNumber({ value: point.x }, locale);
28828
+ if (point.y !== null) {
28771
28829
  filteredValues.push(point.y);
28772
28830
  filteredLabels.push(date);
28773
28831
  }
@@ -28903,7 +28961,6 @@ function createLineOrScatterChartRuntime(chart, getters) {
28903
28961
  else if (axisType === "linear") {
28904
28962
  config.options.scales.x.type = "linear";
28905
28963
  config.options.scales.x.ticks.callback = (value) => formatValue(value, { format: labelFormat, locale });
28906
- config.options.plugins.tooltip.callbacks.title = () => "";
28907
28964
  config.options.plugins.tooltip.callbacks.label = (tooltipItem) => {
28908
28965
  const dataSetPoint = dataSetsValues[tooltipItem.datasetIndex].data[tooltipItem.dataIndex];
28909
28966
  let label = tooltipItem.label || labelValues.values[tooltipItem.dataIndex];
@@ -28991,22 +29048,15 @@ function createLineOrScatterChartRuntime(chart, getters) {
28991
29048
  * distinguish the originals and trendLine datasets after
28992
29049
  */
28993
29050
  trendDatasets.forEach((x) => config.data.datasets.push(x));
28994
- const originalTooltipTitle = config.options.plugins.tooltip.callbacks.title;
28995
- config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
28996
- if (tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)) {
28997
- // @ts-expect-error
28998
- return originalTooltipTitle?.(tooltipItems);
28999
- }
29000
- return "";
29001
- };
29002
29051
  }
29052
+ config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
29053
+ const displayTooltipTitle = axisType !== "linear" &&
29054
+ tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID);
29055
+ return displayTooltipTitle ? undefined : "";
29056
+ };
29003
29057
  return {
29004
29058
  chartJsConfig: config,
29005
29059
  background: chart.background || BACKGROUND_CHART_COLOR,
29006
- dataSetsValues,
29007
- labelValues,
29008
- dataSetFormat,
29009
- labelFormat,
29010
29060
  };
29011
29061
  }
29012
29062
 
@@ -29263,13 +29313,10 @@ function createComboChartRuntime(chart, getters) {
29263
29313
  * distinguish the originals and trendLine datasets after
29264
29314
  */
29265
29315
  trendDatasets.forEach((x) => config.data.datasets.push(x));
29266
- const originalTooltipTitle = config.options.plugins.tooltip.callbacks.title;
29267
29316
  config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
29268
- if (tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)) {
29269
- // @ts-expect-error
29270
- return originalTooltipTitle?.(tooltipItems);
29271
- }
29272
- return "";
29317
+ return tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID)
29318
+ ? undefined
29319
+ : "";
29273
29320
  };
29274
29321
  }
29275
29322
  return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
@@ -36244,6 +36291,12 @@ class GenericChartConfigPanel extends owl.Component {
36244
36291
 
36245
36292
  class BarConfigPanel extends GenericChartConfigPanel {
36246
36293
  static template = "o-spreadsheet-BarConfigPanel";
36294
+ get stackedLabel() {
36295
+ const definition = this.props.definition;
36296
+ return definition.horizontal
36297
+ ? this.chartTerms.StackedBarChart
36298
+ : this.chartTerms.StackedColumnChart;
36299
+ }
36247
36300
  onUpdateStacked(stacked) {
36248
36301
  this.props.updateChart(this.props.figureId, {
36249
36302
  stacked,
@@ -36259,7 +36312,6 @@ class BarConfigPanel extends GenericChartConfigPanel {
36259
36312
  css /* scss */ `
36260
36313
  .o_side_panel_collapsible_title {
36261
36314
  font-size: 16px;
36262
- font-weight: bold;
36263
36315
  cursor: pointer;
36264
36316
  padding: 6px 0px 6px 6px !important;
36265
36317
 
@@ -37239,6 +37291,9 @@ class ChartWithAxisDesignPanel extends owl.Component {
37239
37291
  getDataSeries() {
37240
37292
  return this.props.definition.dataSets.map((d, i) => d.label ?? `${ChartTerms.Series} ${i + 1}`);
37241
37293
  }
37294
+ getPolynomialDegrees() {
37295
+ return range(1, this.getMaxPolynomialDegree() + 1);
37296
+ }
37242
37297
  updateSerieEditor(ev) {
37243
37298
  const chartId = this.props.figureId;
37244
37299
  const selectedIndex = ev.target.selectedIndex;
@@ -37355,12 +37410,7 @@ class ChartWithAxisDesignPanel extends owl.Component {
37355
37410
  }
37356
37411
  onChangePolynomialDegree(ev) {
37357
37412
  const element = ev.target;
37358
- const order = parseInt(element.value || "1");
37359
- if (order < 2) {
37360
- element.value = `${this.getTrendLineConfiguration()?.order ?? 2}`;
37361
- return;
37362
- }
37363
- this.updateTrendLineValue({ order });
37413
+ this.updateTrendLineValue({ order: parseInt(element.value) });
37364
37414
  }
37365
37415
  getTrendLineColor() {
37366
37416
  return this.getTrendLineConfiguration()?.color ?? setColorAlpha(this.getDataSerieColor(), 0.5);
@@ -37382,6 +37432,10 @@ class ChartWithAxisDesignPanel extends owl.Component {
37382
37432
  };
37383
37433
  this.props.updateChart(this.props.figureId, { dataSets });
37384
37434
  }
37435
+ getMaxPolynomialDegree() {
37436
+ const runtime = this.env.model.getters.getChartRuntime(this.props.figureId);
37437
+ return Math.min(10, runtime.chartJsConfig.data.datasets[this.state.index].data.length - 1);
37438
+ }
37385
37439
  }
37386
37440
 
37387
37441
  class ComboChartDesignPanel extends ChartWithAxisDesignPanel {
@@ -37565,7 +37619,9 @@ class LineConfigPanel extends GenericChartConfigPanel {
37565
37619
  }
37566
37620
  get stackedLabel() {
37567
37621
  const definition = this.props.definition;
37568
- return definition.fillArea ? this.chartTerms.StackedAreaChart : this.chartTerms.StackedBarChart;
37622
+ return definition.fillArea
37623
+ ? this.chartTerms.StackedAreaChart
37624
+ : this.chartTerms.StackedLineChart;
37569
37625
  }
37570
37626
  getLabelRangeOptions() {
37571
37627
  const options = super.getLabelRangeOptions();
@@ -39210,7 +39266,6 @@ css /* scss */ `
39210
39266
  width: 142px;
39211
39267
  .o-cf-preview-description-rule {
39212
39268
  margin-bottom: 4px;
39213
- font-weight: 600;
39214
39269
  max-height: 2.8em;
39215
39270
  line-height: 1.4em;
39216
39271
  }
@@ -42212,7 +42267,6 @@ function createMeasureAutoComplete(pivot, forComputedMeasure) {
42212
42267
  sequence: 0,
42213
42268
  autoSelectFirstProposal: true,
42214
42269
  getProposals(tokenAtCursor) {
42215
- // return []
42216
42270
  const measureProposals = pivot.measures
42217
42271
  .filter((m) => m !== forComputedMeasure)
42218
42272
  .map((measure) => {
@@ -47153,6 +47207,7 @@ class PaintFormatStore extends SpreadsheetStore {
47153
47207
  new CellClipboardHandler(this.getters, this.model.dispatch),
47154
47208
  new BorderClipboardHandler(this.getters, this.model.dispatch),
47155
47209
  new TableClipboardHandler(this.getters, this.model.dispatch),
47210
+ new ConditionalFormatClipboardHandler(this.getters, this.model.dispatch),
47156
47211
  ];
47157
47212
  status = "inactive";
47158
47213
  copiedData;
@@ -47163,6 +47218,13 @@ class PaintFormatStore extends SpreadsheetStore {
47163
47218
  this.highlightStore.unRegister(this);
47164
47219
  });
47165
47220
  }
47221
+ handle(cmd) {
47222
+ switch (cmd.type) {
47223
+ case "PAINT_FORMAT":
47224
+ this.paintFormat(cmd.sheetId, cmd.target);
47225
+ break;
47226
+ }
47227
+ }
47166
47228
  activate(args) {
47167
47229
  this.copiedData = this.copyFormats();
47168
47230
  this.status = args.persistent ? "persistent" : "oneOff";
@@ -47172,18 +47234,7 @@ class PaintFormatStore extends SpreadsheetStore {
47172
47234
  this.copiedData = undefined;
47173
47235
  }
47174
47236
  pasteFormat(target) {
47175
- if (this.copiedData) {
47176
- const sheetId = this.getters.getActiveSheetId();
47177
- for (const handler of this.clipboardHandlers) {
47178
- handler.paste({ zones: target, sheetId }, this.copiedData, {
47179
- isCutOperation: false,
47180
- pasteOption: "onlyFormat",
47181
- });
47182
- }
47183
- }
47184
- if (this.status === "oneOff") {
47185
- this.cancel();
47186
- }
47237
+ this.model.dispatch("PAINT_FORMAT", { target, sheetId: this.getters.getActiveSheetId() });
47187
47238
  }
47188
47239
  get isActive() {
47189
47240
  return this.status !== "inactive";
@@ -47197,6 +47248,19 @@ class PaintFormatStore extends SpreadsheetStore {
47197
47248
  }
47198
47249
  return copiedData;
47199
47250
  }
47251
+ paintFormat(sheetId, target) {
47252
+ if (this.copiedData) {
47253
+ for (const handler of this.clipboardHandlers) {
47254
+ handler.paste({ zones: target, sheetId }, this.copiedData, {
47255
+ isCutOperation: false,
47256
+ pasteOption: "onlyFormat",
47257
+ });
47258
+ }
47259
+ }
47260
+ if (this.status === "oneOff") {
47261
+ this.cancel();
47262
+ }
47263
+ }
47200
47264
  get highlights() {
47201
47265
  const data = this.copiedData;
47202
47266
  if (!data) {
@@ -55440,7 +55504,7 @@ class PivotCorePlugin extends CorePlugin {
55440
55504
  case "DUPLICATE_PIVOT": {
55441
55505
  const { pivotId, newPivotId } = cmd;
55442
55506
  const pivot = deepCopy(this.getPivotCore(pivotId).definition);
55443
- pivot.name = _t("%s (copy)", pivot.name);
55507
+ pivot.name = cmd.duplicatedPivotName ?? pivot.name + " (copy)";
55444
55508
  this.addPivot(newPivotId, pivot);
55445
55509
  break;
55446
55510
  }
@@ -55480,7 +55544,7 @@ class PivotCorePlugin extends CorePlugin {
55480
55544
  return `(#${formulaId}) ${this.getPivotName(pivotId)}`;
55481
55545
  }
55482
55546
  getPivotName(pivotId) {
55483
- return _t(this.getPivotCore(pivotId).definition.name);
55547
+ return this.getPivotCore(pivotId).definition.name;
55484
55548
  }
55485
55549
  /**
55486
55550
  * Returns the pivot core definition of the pivot with the given id.
@@ -57122,7 +57186,7 @@ class Evaluator {
57122
57186
  cellsToCompute.addMany(arrayFormulasPositions);
57123
57187
  cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositions));
57124
57188
  this.evaluate(cellsToCompute);
57125
- console.info("evaluate Cells", performance.now() - start, "ms");
57189
+ console.debug("evaluate Cells", performance.now() - start, "ms");
57126
57190
  }
57127
57191
  getArrayFormulasImpactedByChangesOf(positions) {
57128
57192
  const impactedPositions = this.createEmptyPositionSet();
@@ -57166,7 +57230,7 @@ class Evaluator {
57166
57230
  const start = performance.now();
57167
57231
  this.evaluatedCells = new PositionMap();
57168
57232
  this.evaluate(this.getAllCells());
57169
- console.info("evaluate all cells", performance.now() - start, "ms");
57233
+ console.debug("evaluate all cells", performance.now() - start, "ms");
57170
57234
  }
57171
57235
  evaluateFormulaResult(sheetId, formulaString) {
57172
57236
  const compiledFormula = compile(formulaString);
@@ -59571,6 +59635,10 @@ class PivotUIPlugin extends UIPlugin {
59571
59635
  if (!pivot.isValid()) {
59572
59636
  return EMPTY_PIVOT_CELL;
59573
59637
  }
59638
+ if (functionName === "PIVOT" &&
59639
+ !cell.content.replaceAll(" ", "").toUpperCase().startsWith("=PIVOT")) {
59640
+ return EMPTY_PIVOT_CELL;
59641
+ }
59574
59642
  if (functionName === "PIVOT") {
59575
59643
  const includeTotal = args[2] === false ? false : undefined;
59576
59644
  const includeColumnHeaders = args[3] === false ? false : undefined;
@@ -60912,7 +60980,7 @@ class Session extends EventBus {
60912
60980
  this.onMessageReceived(message);
60913
60981
  }
60914
60982
  this.isReplayingInitialRevisions = false;
60915
- console.info("Replayed", numberOfCommands, "commands in", performance.now() - start, "ms");
60983
+ console.debug("Replayed", numberOfCommands, "commands in", performance.now() - start, "ms");
60916
60984
  }
60917
60985
  /**
60918
60986
  * Notify the server that the user client left the collaborative session
@@ -61709,6 +61777,7 @@ class InsertPivotPlugin extends UIPlugin {
61709
61777
  this.dispatch("DUPLICATE_PIVOT", {
61710
61778
  pivotId,
61711
61779
  newPivotId,
61780
+ duplicatedPivotName: _t("%s (copy)", this.getters.getPivotCoreDefinition(pivotId).name),
61712
61781
  });
61713
61782
  const activeSheetId = this.getters.getActiveSheetId();
61714
61783
  const position = this.getters.getSheetIds().indexOf(activeSheetId) + 1;
@@ -66842,10 +66911,9 @@ css /* scss */ `
66842
66911
  user-select: none;
66843
66912
  color: ${TEXT_BODY};
66844
66913
 
66845
- .o-heading-3 {
66914
+ .o-sidePanelTitle {
66846
66915
  line-height: 20px;
66847
66916
  font-size: 16px;
66848
- font-weight: 600;
66849
66917
  }
66850
66918
 
66851
66919
  .o-sidePanelHeader {
@@ -66930,6 +66998,10 @@ css /* scss */ `
66930
66998
  }
66931
66999
  }
66932
67000
  }
67001
+
67002
+ .o-fw-bold {
67003
+ font-weight: 500;
67004
+ }
66933
67005
  `;
66934
67006
  class SidePanel extends owl.Component {
66935
67007
  static template = "o-spreadsheet-SidePanel";
@@ -70340,7 +70412,8 @@ function addFormula(cell) {
70340
70412
  }
70341
70413
  const attrs = [["t", type]];
70342
70414
  const XlsxFormula = adaptFormulaToExcel(formula);
70343
- const node = escapeXml /*xml*/ `<f>${XlsxFormula}</f><v>${cell.value}</v>`;
70415
+ const exportedValue = adaptFormulaValueToExcel(cell.value);
70416
+ const node = escapeXml /*xml*/ `<f>${XlsxFormula}</f><v>${exportedValue}</v>`;
70344
70417
  return { attrs, node };
70345
70418
  }
70346
70419
  function addContent(content, sharedStrings, forceString = false) {
@@ -70375,8 +70448,14 @@ function adaptFormulaToExcel(formulaText) {
70375
70448
  ast = addMissingRequiredArgs(ast);
70376
70449
  return ast;
70377
70450
  });
70451
+ ast = convertAstNodes(ast, "REFERENCE", (ast) => {
70452
+ return ast.value === CellErrorType.InvalidReference ? { ...ast, value: "#REF!" } : ast;
70453
+ });
70378
70454
  return ast ? astToFormula(ast) : formulaText;
70379
70455
  }
70456
+ function adaptFormulaValueToExcel(formulaValue) {
70457
+ return formulaValue === CellErrorType.InvalidReference ? "#REF!" : formulaValue;
70458
+ }
70380
70459
  /**
70381
70460
  * Some Excel function need required args that might not be mandatory in o-spreadsheet.
70382
70461
  * This adds those missing args.
@@ -71699,7 +71778,7 @@ class Model extends EventBus {
71699
71778
  coreHandlers = [];
71700
71779
  constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = false) {
71701
71780
  const start = performance.now();
71702
- console.group("Model creation");
71781
+ console.debug("##### Model creation #####");
71703
71782
  super();
71704
71783
  setDefaultTranslationMethod();
71705
71784
  stateUpdateMessages = repairInitialMessages(data, stateUpdateMessages);
@@ -71771,16 +71850,16 @@ class Model extends EventBus {
71771
71850
  this.joinSession();
71772
71851
  if (config.snapshotRequested) {
71773
71852
  const startSnapshot = performance.now();
71774
- console.info("Snapshot requested");
71853
+ console.debug("Snapshot requested");
71775
71854
  this.session.snapshot(this.exportData());
71776
71855
  this.garbageCollectExternalResources();
71777
- console.info("Snapshot taken in", performance.now() - startSnapshot, "ms");
71856
+ console.debug("Snapshot taken in", performance.now() - startSnapshot, "ms");
71778
71857
  }
71779
71858
  // mark all models as "raw", so they will not be turned into reactive objects
71780
71859
  // by owl, since we do not rely on reactivity
71781
71860
  owl.markRaw(this);
71782
- console.info("Model created in", performance.now() - start, "ms");
71783
- console.groupEnd();
71861
+ console.debug("Model created in", performance.now() - start, "ms");
71862
+ console.debug("######");
71784
71863
  }
71785
71864
  joinSession() {
71786
71865
  this.session.join(this.config.client);
@@ -72004,7 +72083,7 @@ class Model extends EventBus {
72004
72083
  this.finalize();
72005
72084
  const time = performance.now() - start;
72006
72085
  if (time > 5) {
72007
- console.info(type, time, "ms");
72086
+ console.debug(type, time, "ms");
72008
72087
  }
72009
72088
  });
72010
72089
  this.session.save(command, commands, changes);
@@ -72351,6 +72430,7 @@ const constants = {
72351
72430
  PIVOT_TABLE_CONFIG,
72352
72431
  TREND_LINE_XAXIS_ID,
72353
72432
  CHART_AXIS_CHOICES,
72433
+ ChartTerms,
72354
72434
  };
72355
72435
 
72356
72436
  exports.AbstractCellClipboardHandler = AbstractCellClipboardHandler;
@@ -72399,6 +72479,6 @@ exports.tokenColors = tokenColors;
72399
72479
  exports.tokenize = tokenize;
72400
72480
 
72401
72481
 
72402
- __info__.version = "18.0.1";
72403
- __info__.date = "2024-10-14T07:54:24.768Z";
72404
- __info__.hash = "1771f68";
72482
+ __info__.version = "18.0.3";
72483
+ __info__.date = "2024-11-08T12:17:49.992Z";
72484
+ __info__.hash = "5fa5fbb";