@odoo/o-spreadsheet 18.3.6 → 18.3.8

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.3.6
6
- * @date 2025-05-30T08:46:25.817Z
7
- * @hash afa4379
5
+ * @version 18.3.8
6
+ * @date 2025-06-12T09:51:55.929Z
7
+ * @hash 32dedd1
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -5881,7 +5881,9 @@
5881
5881
  }
5882
5882
 
5883
5883
  function evaluateLiteral(literalCell, localeFormat) {
5884
- const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
5884
+ const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5885
+ ? literalCell.content
5886
+ : literalCell.parsedValue;
5885
5887
  const functionResult = { value, format: localeFormat.format };
5886
5888
  return createEvaluatedCell(functionResult, localeFormat.locale);
5887
5889
  }
@@ -5930,6 +5932,9 @@
5930
5932
  if (isEvaluationError(value)) {
5931
5933
  return errorCell(value, message);
5932
5934
  }
5935
+ if (value === null) {
5936
+ return emptyCell(format);
5937
+ }
5933
5938
  if (isTextFormat(format)) {
5934
5939
  // TO DO:
5935
5940
  // with the next line, the value of the cell is transformed depending on the format.
@@ -5937,9 +5942,6 @@
5937
5942
  // to interpret the value as a number.
5938
5943
  return textCell(toString(value), format, formattedValue);
5939
5944
  }
5940
- if (value === null) {
5941
- return emptyCell(format);
5942
- }
5943
5945
  if (typeof value === "number") {
5944
5946
  if (isDateTimeFormat(format || "")) {
5945
5947
  return dateTimeCell(value, format, formattedValue);
@@ -20956,8 +20958,6 @@ stores.inject(MyMetaStore, storeInstance);
20956
20958
  if (isNaN(value)) {
20957
20959
  continue;
20958
20960
  }
20959
- const axisId = chart.config.type === "radar" ? dataset.rAxisID : dataset.yAxisID;
20960
- const displayValue = options.callback(Number(value), axisId);
20961
20961
  const point = dataset.data[i];
20962
20962
  const xPosition = point.x;
20963
20963
  let yPosition = 0;
@@ -20981,7 +20981,8 @@ stores.inject(MyMetaStore, storeInstance);
20981
20981
  textsPositions[xPosition].push(yPosition);
20982
20982
  ctx.fillStyle = point.options.backgroundColor;
20983
20983
  ctx.strokeStyle = options.background || "#ffffff";
20984
- drawTextWithBackground(displayValue, xPosition, yPosition, ctx);
20984
+ const valueToDisplay = options.callback(Number(value), dataset, i);
20985
+ drawTextWithBackground(valueToDisplay, xPosition, yPosition, ctx);
20985
20986
  }
20986
20987
  }
20987
20988
  }
@@ -20998,7 +20999,7 @@ stores.inject(MyMetaStore, storeInstance);
20998
20999
  if (isNaN(value)) {
20999
21000
  continue;
21000
21001
  }
21001
- const displayValue = options.callback(value, dataset.xAxisID);
21002
+ const displayValue = options.callback(value, dataset, i);
21002
21003
  const point = dataset.data[i];
21003
21004
  const yPosition = point.y;
21004
21005
  let xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
@@ -21033,10 +21034,22 @@ stores.inject(MyMetaStore, storeInstance);
21033
21034
  const midAngle = (startAngle + endAngle) / 2;
21034
21035
  const midRadius = (innerRadius + outerRadius) / 2;
21035
21036
  const x = bar.x + midRadius * Math.cos(midAngle);
21036
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
21037
+ const y = bar.y + midRadius * Math.sin(midAngle);
21038
+ const displayValue = options.callback(value, dataset, i);
21039
+ const textHeight = 12; // ChartJS default
21040
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
21041
+ const radius = outerRadius - innerRadius;
21042
+ // Check if the text fits in the slice. Not perfect, but good enough heuristic.
21043
+ if (textWidth >= radius || radius < textHeight) {
21044
+ continue;
21045
+ }
21046
+ const sliceAngle = endAngle - startAngle;
21047
+ const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
21048
+ if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
21049
+ continue;
21050
+ }
21037
21051
  ctx.fillStyle = chartFontColor(options.background);
21038
21052
  ctx.strokeStyle = options.background || "#ffffff";
21039
- const displayValue = options.callback(value, "y");
21040
21053
  drawTextWithBackground(displayValue, x, y, ctx);
21041
21054
  }
21042
21055
  }
@@ -26357,7 +26370,10 @@ stores.inject(MyMetaStore, storeInstance);
26357
26370
  horizontal: "horizontal" in definition && definition.horizontal,
26358
26371
  showValues: "showValues" in definition ? !!definition.showValues : false,
26359
26372
  background: definition.background,
26360
- callback: formatChartDatasetValue(axisFormats, locale),
26373
+ callback: (value, dataset) => {
26374
+ const axisId = getDatasetAxisId(definition, dataset);
26375
+ return formatChartDatasetValue(axisFormats, locale)(value, axisId);
26376
+ },
26361
26377
  };
26362
26378
  }
26363
26379
  function getSunburstShowValues(definition, args) {
@@ -26375,6 +26391,45 @@ stores.inject(MyMetaStore, storeInstance);
26375
26391
  },
26376
26392
  };
26377
26393
  }
26394
+ function getPyramidChartShowValues(definition, args) {
26395
+ const { axisFormats, locale } = args;
26396
+ return {
26397
+ horizontal: true,
26398
+ showValues: "showValues" in definition ? !!definition.showValues : false,
26399
+ background: definition.background,
26400
+ callback: (value, dataset) => {
26401
+ value = Math.abs(Number(value));
26402
+ return formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
26403
+ },
26404
+ };
26405
+ }
26406
+ function getWaterfallChartShowValues(definition, args) {
26407
+ const { axisFormats, locale, dataSetsValues } = args;
26408
+ const subtotalIndexes = dataSetsValues.reduce((subtotalIndexes, ds) => {
26409
+ subtotalIndexes.push((subtotalIndexes.at(-1) || -1) + ds.data.length + 1);
26410
+ return subtotalIndexes;
26411
+ }, []);
26412
+ return {
26413
+ showValues: "showValues" in definition ? !!definition.showValues : false,
26414
+ background: definition.background,
26415
+ callback: (value, dataset, index) => {
26416
+ const raw = dataset._dataset.data[index];
26417
+ const delta = raw[1] - raw[0];
26418
+ let sign = delta >= 0 ? "+" : "";
26419
+ if (definition.showSubTotals && subtotalIndexes.includes(index) && sign === "+") {
26420
+ sign = "";
26421
+ }
26422
+ return `${sign}${formatChartDatasetValue(axisFormats, locale)(delta, dataset.yAxisID)}`;
26423
+ },
26424
+ };
26425
+ }
26426
+ function getDatasetAxisId(definition, dataset) {
26427
+ if (dataset.rAxisID) {
26428
+ return dataset.rAxisID;
26429
+ }
26430
+ const axisId = "horizontal" in definition && definition.horizontal ? dataset.xAxisID : dataset.yAxisID;
26431
+ return axisId || "y";
26432
+ }
26378
26433
 
26379
26434
  function getChartTitle(definition) {
26380
26435
  const chartTitle = definition.title;
@@ -26777,6 +26832,7 @@ stores.inject(MyMetaStore, storeInstance);
26777
26832
  getPieChartTooltip: getPieChartTooltip,
26778
26833
  getPyramidChartData: getPyramidChartData,
26779
26834
  getPyramidChartScales: getPyramidChartScales,
26835
+ getPyramidChartShowValues: getPyramidChartShowValues,
26780
26836
  getPyramidChartTooltip: getPyramidChartTooltip,
26781
26837
  getRadarChartData: getRadarChartData,
26782
26838
  getRadarChartDatasets: getRadarChartDatasets,
@@ -26796,6 +26852,7 @@ stores.inject(MyMetaStore, storeInstance);
26796
26852
  getTrendDatasetForLineChart: getTrendDatasetForLineChart,
26797
26853
  getWaterfallChartLegend: getWaterfallChartLegend,
26798
26854
  getWaterfallChartScales: getWaterfallChartScales,
26855
+ getWaterfallChartShowValues: getWaterfallChartShowValues,
26799
26856
  getWaterfallChartTooltip: getWaterfallChartTooltip,
26800
26857
  getWaterfallDatasetAndLabels: getWaterfallDatasetAndLabels,
26801
26858
  makeDatasetsCumulative: makeDatasetsCumulative
@@ -28075,7 +28132,7 @@ stores.inject(MyMetaStore, storeInstance);
28075
28132
  title: getChartTitle(definition),
28076
28133
  legend: getBarChartLegend(definition),
28077
28134
  tooltip: getPyramidChartTooltip(definition, chartData),
28078
- chartShowValuesPlugin: getChartShowValues(definition, chartData),
28135
+ chartShowValuesPlugin: getPyramidChartShowValues(definition, chartData),
28079
28136
  },
28080
28137
  },
28081
28138
  };
@@ -28816,7 +28873,7 @@ stores.inject(MyMetaStore, storeInstance);
28816
28873
  title: getChartTitle(definition),
28817
28874
  legend: getWaterfallChartLegend(definition),
28818
28875
  tooltip: getWaterfallChartTooltip(definition, chartData),
28819
- chartShowValuesPlugin: getChartShowValues(definition, chartData),
28876
+ chartShowValuesPlugin: getWaterfallChartShowValues(definition, chartData),
28820
28877
  waterfallLinesPlugin: { showConnectorLines: definition.showConnectorLines },
28821
28878
  },
28822
28879
  },
@@ -33128,7 +33185,7 @@ stores.inject(MyMetaStore, storeInstance);
33128
33185
  */
33129
33186
  handleMissingValue(parentElement, missingElementName, optionalArgs) {
33130
33187
  if (optionalArgs?.required) {
33131
- if (optionalArgs?.default) {
33188
+ if (optionalArgs?.default !== undefined) {
33132
33189
  this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
33133
33190
  }
33134
33191
  else {
@@ -36081,19 +36138,26 @@ stores.inject(MyMetaStore, storeInstance);
36081
36138
  .filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
36082
36139
  .map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
36083
36140
  const filterValues = this.env.model.getters.getFilterHiddenValues({ sheetId, ...position });
36084
- const strValues = [...cellValues, ...filterValues];
36085
- const normalizedFilteredValues = filterValues.map(toLowerCase);
36086
- // Set with lowercase values to avoid duplicates
36087
- const normalizedValues = [...new Set(strValues.map(toLowerCase))];
36088
- const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
36089
- return sortedValues.map((normalizedValue) => {
36090
- const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
36091
- -1;
36092
- return {
36093
- checked,
36094
- string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
36095
- };
36096
- });
36141
+ const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
36142
+ const set = new Set();
36143
+ const values = [];
36144
+ const addValue = (value) => {
36145
+ const normalizedValue = toLowerCase(value);
36146
+ if (!set.has(normalizedValue)) {
36147
+ values.push({
36148
+ string: value || "",
36149
+ checked: !normalizedFilteredValues.has(normalizedValue),
36150
+ normalizedValue,
36151
+ });
36152
+ set.add(normalizedValue);
36153
+ }
36154
+ };
36155
+ cellValues.forEach(addValue);
36156
+ filterValues.forEach(addValue);
36157
+ return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
36158
+ numeric: true,
36159
+ sensitivity: "base",
36160
+ }));
36097
36161
  }
36098
36162
  checkValue(value) {
36099
36163
  this.state.selectedValue = value.string;
@@ -47368,13 +47432,15 @@ stores.inject(MyMetaStore, storeInstance);
47368
47432
  if (this.selectedMatchIndex === null) {
47369
47433
  return;
47370
47434
  }
47435
+ this.preserveSelectedMatchIndex = true;
47436
+ this.shouldFinalizeUpdateSelection = true;
47371
47437
  this.model.dispatch("REPLACE_SEARCH", {
47372
47438
  searchString: this.toSearch,
47373
47439
  replaceWith: this.toReplace,
47374
47440
  matches: [this.searchMatches[this.selectedMatchIndex]],
47375
47441
  searchOptions: this.searchOptions,
47376
47442
  });
47377
- this.selectNextCell(Direction.next, { jumpToMatchSheet: true, updateSelection: true });
47443
+ this.preserveSelectedMatchIndex = false;
47378
47444
  }
47379
47445
  /**
47380
47446
  * Apply the replace function to all the matches one time.
@@ -53530,7 +53596,7 @@ stores.inject(MyMetaStore, storeInstance);
53530
53596
  }
53531
53597
  }
53532
53598
  hover(position) {
53533
- if (position.col === this.col && position.row === this.row) {
53599
+ if (!this.getters.isDashboard() || (position.col === this.col && position.row === this.row)) {
53534
53600
  return "noStateChange";
53535
53601
  }
53536
53602
  this.col = position.col;
@@ -55206,6 +55272,9 @@ stores.inject(MyMetaStore, storeInstance);
55206
55272
  let deltaX = lastX - clientX;
55207
55273
  let deltaY = lastY - clientY;
55208
55274
  const elapsedTime = currentTime - lastTime;
55275
+ if (!elapsedTime) {
55276
+ return;
55277
+ }
55209
55278
  velocityX = deltaX / elapsedTime;
55210
55279
  velocityY = deltaY / elapsedTime;
55211
55280
  lastX = clientX;
@@ -55226,6 +55295,11 @@ stores.inject(MyMetaStore, storeInstance);
55226
55295
  function onTouchEnd(ev) {
55227
55296
  isMouseDown = false;
55228
55297
  lastX = lastY = 0;
55298
+ if (resetTimeout) {
55299
+ clearTimeout(resetTimeout);
55300
+ }
55301
+ velocityX *= 1.2;
55302
+ velocityY *= 1.2;
55229
55303
  requestAnimationFrame(scroll);
55230
55304
  }
55231
55305
  function scroll() {
@@ -61424,7 +61498,9 @@ stores.inject(MyMetaStore, storeInstance);
61424
61498
  const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
61425
61499
  const union = this.getters.getRangesUnion(ranges);
61426
61500
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
61427
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
61501
+ if (mergesInTarget.length) {
61502
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
61503
+ }
61428
61504
  const id = this.consumeNextId();
61429
61505
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
61430
61506
  const newTable = cmd.tableType === "dynamic"
@@ -61523,14 +61599,16 @@ stores.inject(MyMetaStore, storeInstance);
61523
61599
  const zoneToCheckIfEmpty = direction === "down"
61524
61600
  ? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
61525
61601
  : { ...zone, right: zone.right + 1, left: zone.right + 1 };
61526
- for (const position of positions(zoneToCheckIfEmpty)) {
61527
- const cellPosition = { sheetId, ...position };
61528
- // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
61529
- const cellContent = this.getters.getCell(cellPosition)?.content;
61530
- if (cellContent ||
61531
- this.getters.isInMerge(cellPosition) ||
61532
- this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
61533
- return "none";
61602
+ for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
61603
+ for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
61604
+ const cellPosition = { sheetId, col, row };
61605
+ // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
61606
+ const cellContent = this.getters.getCell(cellPosition)?.content;
61607
+ if (cellContent ||
61608
+ this.getters.isInMerge(cellPosition) ||
61609
+ this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
61610
+ return "none";
61611
+ }
61534
61612
  }
61535
61613
  }
61536
61614
  return direction;
@@ -62316,7 +62394,7 @@ stores.inject(MyMetaStore, storeInstance);
62316
62394
  break;
62317
62395
  }
62318
62396
  case "UPDATE_PIVOT": {
62319
- this.history.update("pivots", cmd.pivotId, "definition", deepCopy(cmd.pivot));
62397
+ this.history.update("pivots", cmd.pivotId, "definition", this.repairSortedColumn(deepCopy(cmd.pivot)));
62320
62398
  this.compileCalculatedMeasures(cmd.pivot.measures);
62321
62399
  break;
62322
62400
  }
@@ -62387,7 +62465,10 @@ stores.inject(MyMetaStore, storeInstance);
62387
62465
  // Private
62388
62466
  // -------------------------------------------------------------------------
62389
62467
  addPivot(pivotId, pivot, formulaId = this.nextFormulaId.toString()) {
62390
- this.history.update("pivots", pivotId, { definition: deepCopy(pivot), formulaId });
62468
+ this.history.update("pivots", pivotId, {
62469
+ definition: this.repairSortedColumn(deepCopy(pivot)),
62470
+ formulaId,
62471
+ });
62391
62472
  this.compileCalculatedMeasures(pivot.measures);
62392
62473
  this.history.update("formulaIds", formulaId, pivotId);
62393
62474
  this.history.update("nextFormulaId", this.nextFormulaId + 1);
@@ -62476,6 +62557,7 @@ stores.inject(MyMetaStore, storeInstance);
62476
62557
  }
62477
62558
  }
62478
62559
  checkSortedColumnInMeasures(definition) {
62560
+ definition = this.repairSortedColumn(definition);
62479
62561
  const measures = definition.measures.map((measure) => measure.id);
62480
62562
  if (definition.sortedColumn && !measures.includes(definition.sortedColumn.measure)) {
62481
62563
  return "InvalidDefinition" /* CommandResult.InvalidDefinition */;
@@ -62489,6 +62571,26 @@ stores.inject(MyMetaStore, storeInstance);
62489
62571
  }
62490
62572
  return "Success" /* CommandResult.Success */;
62491
62573
  }
62574
+ repairSortedColumn(definition) {
62575
+ if (definition.sortedColumn) {
62576
+ // Fix for an upgrade issue: the sortedColumn measure was not updated
62577
+ // from using fieldName to using id. If the sortedColumn measure matches
62578
+ // a measure fieldName in the definition, update it to use the measure's id instead
62579
+ // of its fieldName.
62580
+ // TODO: add an upgrade step to fix this in master and remove this code
62581
+ const sortedMeasure = definition.measures.find((measure) => measure.fieldName === definition.sortedColumn?.measure);
62582
+ if (sortedMeasure) {
62583
+ return {
62584
+ ...definition,
62585
+ sortedColumn: {
62586
+ ...definition.sortedColumn,
62587
+ measure: sortedMeasure.id,
62588
+ },
62589
+ };
62590
+ }
62591
+ }
62592
+ return definition;
62593
+ }
62492
62594
  // ---------------------------------------------------------------------
62493
62595
  // Import/Export
62494
62596
  // ---------------------------------------------------------------------
@@ -71132,9 +71234,10 @@ stores.inject(MyMetaStore, storeInstance);
71132
71234
  const filteredZone = filter.filteredRange?.zone;
71133
71235
  if (!filteredValues || !filteredZone)
71134
71236
  continue;
71237
+ const filteredValuesSet = new Set(filteredValues);
71135
71238
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
71136
71239
  const value = this.getCellValueAsString(sheetId, filter.col, row);
71137
- if (filteredValues.includes(value)) {
71240
+ if (filteredValuesSet.has(value)) {
71138
71241
  hiddenRows.add(row);
71139
71242
  }
71140
71243
  }
@@ -71648,11 +71751,6 @@ stores.inject(MyMetaStore, storeInstance);
71648
71751
  },
71649
71752
  ];
71650
71753
  const sheetId = this.getActiveSheetId();
71651
- const handler = new CellClipboardHandler(this.getters, this.dispatch);
71652
- const data = handler.copy(getClipboardDataPositions(sheetId, target));
71653
- if (!data) {
71654
- return;
71655
- }
71656
71754
  const base = isBasedBefore ? cmd.base : cmd.base + 1;
71657
71755
  const pasteTarget = [
71658
71756
  {
@@ -71662,7 +71760,14 @@ stores.inject(MyMetaStore, storeInstance);
71662
71760
  bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
71663
71761
  },
71664
71762
  ];
71665
- handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
71763
+ for (const Handler of clipboardHandlersRegistries.cellHandlers.getAll()) {
71764
+ const handler = new Handler(this.getters, this.dispatch);
71765
+ const data = handler.copy(getClipboardDataPositions(sheetId, target));
71766
+ if (!data) {
71767
+ continue;
71768
+ }
71769
+ handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
71770
+ }
71666
71771
  const selection = pasteTarget[0];
71667
71772
  const col = selection.left;
71668
71773
  const row = selection.top;
@@ -80562,9 +80667,9 @@ stores.inject(MyMetaStore, storeInstance);
80562
80667
  exports.tokenize = tokenize;
80563
80668
 
80564
80669
 
80565
- __info__.version = "18.3.6";
80566
- __info__.date = "2025-05-30T08:46:25.817Z";
80567
- __info__.hash = "afa4379";
80670
+ __info__.version = "18.3.8";
80671
+ __info__.date = "2025-06-12T09:51:55.929Z";
80672
+ __info__.hash = "32dedd1";
80568
80673
 
80569
80674
 
80570
80675
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);