@odoo/o-spreadsheet 18.1.23 → 18.1.25

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.23
6
- * @date 2025-05-30T08:45:44.408Z
7
- * @hash a21fa01
5
+ * @version 18.1.25
6
+ * @date 2025-06-12T09:49:08.707Z
7
+ * @hash a232524
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -5846,7 +5846,9 @@
5846
5846
  }
5847
5847
 
5848
5848
  function evaluateLiteral(literalCell, localeFormat) {
5849
- const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
5849
+ const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5850
+ ? literalCell.content
5851
+ : literalCell.parsedValue;
5850
5852
  const functionResult = { value, format: localeFormat.format };
5851
5853
  return createEvaluatedCell(functionResult, localeFormat.locale);
5852
5854
  }
@@ -5895,6 +5897,9 @@
5895
5897
  if (isEvaluationError(value)) {
5896
5898
  return errorCell(value, message);
5897
5899
  }
5900
+ if (value === null) {
5901
+ return emptyCell(format);
5902
+ }
5898
5903
  if (isTextFormat(format)) {
5899
5904
  // TO DO:
5900
5905
  // with the next line, the value of the cell is transformed depending on the format.
@@ -5902,9 +5907,6 @@
5902
5907
  // to interpret the value as a number.
5903
5908
  return textCell(toString(value), format, formattedValue);
5904
5909
  }
5905
- if (value === null) {
5906
- return emptyCell(format);
5907
- }
5908
5910
  if (typeof value === "number") {
5909
5911
  if (isDateTimeFormat(format || "")) {
5910
5912
  return dateTimeCell(value, format, formattedValue);
@@ -10207,8 +10209,6 @@ stores.inject(MyMetaStore, storeInstance);
10207
10209
  if (isNaN(value)) {
10208
10210
  continue;
10209
10211
  }
10210
- const axisId = chart.config.type === "radar" ? dataset.rAxisID : dataset.yAxisID;
10211
- const displayValue = options.callback(Number(value), axisId);
10212
10212
  const point = dataset.data[i];
10213
10213
  const xPosition = point.x;
10214
10214
  let yPosition = 0;
@@ -10232,7 +10232,8 @@ stores.inject(MyMetaStore, storeInstance);
10232
10232
  textsPositions[xPosition].push(yPosition);
10233
10233
  ctx.fillStyle = point.options.backgroundColor;
10234
10234
  ctx.strokeStyle = options.background || "#ffffff";
10235
- drawTextWithBackground(displayValue, xPosition, yPosition, ctx);
10235
+ const valueToDisplay = options.callback(Number(value), dataset, i);
10236
+ drawTextWithBackground(valueToDisplay, xPosition, yPosition, ctx);
10236
10237
  }
10237
10238
  }
10238
10239
  }
@@ -10249,7 +10250,7 @@ stores.inject(MyMetaStore, storeInstance);
10249
10250
  if (isNaN(value)) {
10250
10251
  continue;
10251
10252
  }
10252
- const displayValue = options.callback(value, dataset.xAxisID);
10253
+ const displayValue = options.callback(value, dataset, i);
10253
10254
  const point = dataset.data[i];
10254
10255
  const yPosition = point.y;
10255
10256
  let xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
@@ -10284,10 +10285,22 @@ stores.inject(MyMetaStore, storeInstance);
10284
10285
  const midAngle = (startAngle + endAngle) / 2;
10285
10286
  const midRadius = (innerRadius + outerRadius) / 2;
10286
10287
  const x = bar.x + midRadius * Math.cos(midAngle);
10287
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
10288
+ const y = bar.y + midRadius * Math.sin(midAngle);
10289
+ const displayValue = options.callback(value, dataset, i);
10290
+ const textHeight = 12; // ChartJS default
10291
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
10292
+ const radius = outerRadius - innerRadius;
10293
+ // Check if the text fits in the slice. Not perfect, but good enough heuristic.
10294
+ if (textWidth >= radius || radius < textHeight) {
10295
+ continue;
10296
+ }
10297
+ const sliceAngle = endAngle - startAngle;
10298
+ const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
10299
+ if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
10300
+ continue;
10301
+ }
10288
10302
  ctx.fillStyle = chartFontColor(options.background);
10289
10303
  ctx.strokeStyle = options.background || "#ffffff";
10290
- const displayValue = options.callback(value, "y");
10291
10304
  drawTextWithBackground(displayValue, x, y, ctx);
10292
10305
  }
10293
10306
  }
@@ -26193,7 +26206,7 @@ stores.inject(MyMetaStore, storeInstance);
26193
26206
  */
26194
26207
  handleMissingValue(parentElement, missingElementName, optionalArgs) {
26195
26208
  if (optionalArgs?.required) {
26196
- if (optionalArgs?.default) {
26209
+ if (optionalArgs?.default !== undefined) {
26197
26210
  this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
26198
26211
  }
26199
26212
  else {
@@ -30052,9 +30065,51 @@ stores.inject(MyMetaStore, storeInstance);
30052
30065
  horizontal: "horizontal" in definition && definition.horizontal,
30053
30066
  showValues: "showValues" in definition ? !!definition.showValues : false,
30054
30067
  background: definition.background,
30055
- callback: formatChartDatasetValue(axisFormats, locale),
30068
+ callback: (value, dataset) => {
30069
+ const axisId = getDatasetAxisId(definition, dataset);
30070
+ return formatChartDatasetValue(axisFormats, locale)(value, axisId);
30071
+ },
30056
30072
  };
30057
30073
  }
30074
+ function getPyramidChartShowValues(definition, args) {
30075
+ const { axisFormats, locale } = args;
30076
+ return {
30077
+ horizontal: true,
30078
+ showValues: "showValues" in definition ? !!definition.showValues : false,
30079
+ background: definition.background,
30080
+ callback: (value, dataset) => {
30081
+ value = Math.abs(Number(value));
30082
+ return formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
30083
+ },
30084
+ };
30085
+ }
30086
+ function getWaterfallChartShowValues(definition, args) {
30087
+ const { axisFormats, locale, dataSetsValues } = args;
30088
+ const subtotalIndexes = dataSetsValues.reduce((subtotalIndexes, ds) => {
30089
+ subtotalIndexes.push((subtotalIndexes.at(-1) || -1) + ds.data.length + 1);
30090
+ return subtotalIndexes;
30091
+ }, []);
30092
+ return {
30093
+ showValues: "showValues" in definition ? !!definition.showValues : false,
30094
+ background: definition.background,
30095
+ callback: (value, dataset, index) => {
30096
+ const raw = dataset._dataset.data[index];
30097
+ const delta = raw[1] - raw[0];
30098
+ let sign = delta >= 0 ? "+" : "";
30099
+ if (definition.showSubTotals && subtotalIndexes.includes(index) && sign === "+") {
30100
+ sign = "";
30101
+ }
30102
+ return `${sign}${formatChartDatasetValue(axisFormats, locale)(delta, dataset.yAxisID)}`;
30103
+ },
30104
+ };
30105
+ }
30106
+ function getDatasetAxisId(definition, dataset) {
30107
+ if (dataset.rAxisID) {
30108
+ return dataset.rAxisID;
30109
+ }
30110
+ const axisId = "horizontal" in definition && definition.horizontal ? dataset.xAxisID : dataset.yAxisID;
30111
+ return axisId || "y";
30112
+ }
30058
30113
 
30059
30114
  function getChartTitle(definition) {
30060
30115
  const chartTitle = definition.title;
@@ -30263,6 +30318,7 @@ stores.inject(MyMetaStore, storeInstance);
30263
30318
  getPieChartTooltip: getPieChartTooltip,
30264
30319
  getPyramidChartData: getPyramidChartData,
30265
30320
  getPyramidChartScales: getPyramidChartScales,
30321
+ getPyramidChartShowValues: getPyramidChartShowValues,
30266
30322
  getPyramidChartTooltip: getPyramidChartTooltip,
30267
30323
  getRadarChartData: getRadarChartData,
30268
30324
  getRadarChartDatasets: getRadarChartDatasets,
@@ -30276,6 +30332,7 @@ stores.inject(MyMetaStore, storeInstance);
30276
30332
  getTrendDatasetForLineChart: getTrendDatasetForLineChart,
30277
30333
  getWaterfallChartLegend: getWaterfallChartLegend,
30278
30334
  getWaterfallChartScales: getWaterfallChartScales,
30335
+ getWaterfallChartShowValues: getWaterfallChartShowValues,
30279
30336
  getWaterfallChartTooltip: getWaterfallChartTooltip,
30280
30337
  getWaterfallDatasetAndLabels: getWaterfallDatasetAndLabels
30281
30338
  });
@@ -31374,7 +31431,7 @@ stores.inject(MyMetaStore, storeInstance);
31374
31431
  title: getChartTitle(definition),
31375
31432
  legend: getBarChartLegend(definition),
31376
31433
  tooltip: getPyramidChartTooltip(definition, chartData),
31377
- chartShowValuesPlugin: getChartShowValues(definition, chartData),
31434
+ chartShowValuesPlugin: getPyramidChartShowValues(definition, chartData),
31378
31435
  },
31379
31436
  },
31380
31437
  };
@@ -31837,7 +31894,7 @@ stores.inject(MyMetaStore, storeInstance);
31837
31894
  title: getChartTitle(definition),
31838
31895
  legend: getWaterfallChartLegend(definition),
31839
31896
  tooltip: getWaterfallChartTooltip(definition, chartData),
31840
- chartShowValuesPlugin: getChartShowValues(definition, chartData),
31897
+ chartShowValuesPlugin: getWaterfallChartShowValues(definition, chartData),
31841
31898
  waterfallLinesPlugin: { showConnectorLines: definition.showConnectorLines },
31842
31899
  },
31843
31900
  },
@@ -33171,19 +33228,26 @@ stores.inject(MyMetaStore, storeInstance);
33171
33228
  .filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
33172
33229
  .map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
33173
33230
  const filterValues = this.env.model.getters.getFilterHiddenValues({ sheetId, ...position });
33174
- const strValues = [...cellValues, ...filterValues];
33175
- const normalizedFilteredValues = filterValues.map(toLowerCase);
33176
- // Set with lowercase values to avoid duplicates
33177
- const normalizedValues = [...new Set(strValues.map(toLowerCase))];
33178
- const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
33179
- return sortedValues.map((normalizedValue) => {
33180
- const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
33181
- -1;
33182
- return {
33183
- checked,
33184
- string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
33185
- };
33186
- });
33231
+ const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
33232
+ const set = new Set();
33233
+ const values = [];
33234
+ const addValue = (value) => {
33235
+ const normalizedValue = toLowerCase(value);
33236
+ if (!set.has(normalizedValue)) {
33237
+ values.push({
33238
+ string: value || "",
33239
+ checked: !normalizedFilteredValues.has(normalizedValue),
33240
+ normalizedValue,
33241
+ });
33242
+ set.add(normalizedValue);
33243
+ }
33244
+ };
33245
+ cellValues.forEach(addValue);
33246
+ filterValues.forEach(addValue);
33247
+ return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
33248
+ numeric: true,
33249
+ sensitivity: "base",
33250
+ }));
33187
33251
  }
33188
33252
  checkValue(value) {
33189
33253
  this.state.selectedValue = value.string;
@@ -44320,13 +44384,15 @@ stores.inject(MyMetaStore, storeInstance);
44320
44384
  if (this.selectedMatchIndex === null) {
44321
44385
  return;
44322
44386
  }
44387
+ this.preserveSelectedMatchIndex = true;
44388
+ this.shouldFinalizeUpdateSelection = true;
44323
44389
  this.model.dispatch("REPLACE_SEARCH", {
44324
44390
  searchString: this.toSearch,
44325
44391
  replaceWith: this.toReplace,
44326
44392
  matches: [this.searchMatches[this.selectedMatchIndex]],
44327
44393
  searchOptions: this.searchOptions,
44328
44394
  });
44329
- this.selectNextCell(Direction.next, { jumpToMatchSheet: true, updateSelection: true });
44395
+ this.preserveSelectedMatchIndex = false;
44330
44396
  }
44331
44397
  /**
44332
44398
  * Apply the replace function to all the matches one time.
@@ -51822,6 +51888,9 @@ stores.inject(MyMetaStore, storeInstance);
51822
51888
  let deltaX = lastX - clientX;
51823
51889
  let deltaY = lastY - clientY;
51824
51890
  const elapsedTime = currentTime - lastTime;
51891
+ if (!elapsedTime) {
51892
+ return;
51893
+ }
51825
51894
  velocityX = deltaX / elapsedTime;
51826
51895
  velocityY = deltaY / elapsedTime;
51827
51896
  lastX = clientX;
@@ -51842,6 +51911,11 @@ stores.inject(MyMetaStore, storeInstance);
51842
51911
  function onTouchEnd(ev) {
51843
51912
  isMouseDown = false;
51844
51913
  lastX = lastY = 0;
51914
+ if (resetTimeout) {
51915
+ clearTimeout(resetTimeout);
51916
+ }
51917
+ velocityX *= 1.2;
51918
+ velocityY *= 1.2;
51845
51919
  requestAnimationFrame(scroll);
51846
51920
  }
51847
51921
  function scroll() {
@@ -58041,7 +58115,9 @@ stores.inject(MyMetaStore, storeInstance);
58041
58115
  const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
58042
58116
  const union = this.getters.getRangesUnion(ranges);
58043
58117
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
58044
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
58118
+ if (mergesInTarget.length) {
58119
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
58120
+ }
58045
58121
  const id = `${nextTableId++}`;
58046
58122
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
58047
58123
  const newTable = cmd.tableType === "dynamic"
@@ -58152,14 +58228,16 @@ stores.inject(MyMetaStore, storeInstance);
58152
58228
  const zoneToCheckIfEmpty = direction === "down"
58153
58229
  ? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
58154
58230
  : { ...zone, right: zone.right + 1, left: zone.right + 1 };
58155
- for (const position of positions(zoneToCheckIfEmpty)) {
58156
- const cellPosition = { sheetId, ...position };
58157
- // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
58158
- const cellContent = this.getters.getCell(cellPosition)?.content;
58159
- if (cellContent ||
58160
- this.getters.isInMerge(cellPosition) ||
58161
- this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
58162
- return "none";
58231
+ for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
58232
+ for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
58233
+ const cellPosition = { sheetId, col, row };
58234
+ // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
58235
+ const cellContent = this.getters.getCell(cellPosition)?.content;
58236
+ if (cellContent ||
58237
+ this.getters.isInMerge(cellPosition) ||
58238
+ this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
58239
+ return "none";
58240
+ }
58163
58241
  }
58164
58242
  }
58165
58243
  return direction;
@@ -58943,7 +59021,7 @@ stores.inject(MyMetaStore, storeInstance);
58943
59021
  break;
58944
59022
  }
58945
59023
  case "UPDATE_PIVOT": {
58946
- this.history.update("pivots", cmd.pivotId, "definition", deepCopy(cmd.pivot));
59024
+ this.history.update("pivots", cmd.pivotId, "definition", this.repairSortedColumn(deepCopy(cmd.pivot)));
58947
59025
  this.compileCalculatedMeasures(cmd.pivot.measures);
58948
59026
  break;
58949
59027
  }
@@ -59014,7 +59092,10 @@ stores.inject(MyMetaStore, storeInstance);
59014
59092
  // Private
59015
59093
  // -------------------------------------------------------------------------
59016
59094
  addPivot(pivotId, pivot, formulaId = this.nextFormulaId.toString()) {
59017
- this.history.update("pivots", pivotId, { definition: deepCopy(pivot), formulaId });
59095
+ this.history.update("pivots", pivotId, {
59096
+ definition: this.repairSortedColumn(deepCopy(pivot)),
59097
+ formulaId,
59098
+ });
59018
59099
  this.compileCalculatedMeasures(pivot.measures);
59019
59100
  this.history.update("formulaIds", formulaId, pivotId);
59020
59101
  this.history.update("nextFormulaId", this.nextFormulaId + 1);
@@ -59107,6 +59188,26 @@ stores.inject(MyMetaStore, storeInstance);
59107
59188
  }
59108
59189
  return "Success" /* CommandResult.Success */;
59109
59190
  }
59191
+ repairSortedColumn(definition) {
59192
+ if (definition.sortedColumn) {
59193
+ // Fix for an upgrade issue: the sortedColumn measure was not updated
59194
+ // from using fieldName to using id. If the sortedColumn measure matches
59195
+ // a measure fieldName in the definition, update it to use the measure's id instead
59196
+ // of its fieldName.
59197
+ // TODO: add an upgrade step to fix this in master and remove this code
59198
+ const sortedMeasure = definition.measures.find((measure) => measure.fieldName === definition.sortedColumn?.measure);
59199
+ if (sortedMeasure) {
59200
+ return {
59201
+ ...definition,
59202
+ sortedColumn: {
59203
+ ...definition.sortedColumn,
59204
+ measure: sortedMeasure.id,
59205
+ },
59206
+ };
59207
+ }
59208
+ }
59209
+ return definition;
59210
+ }
59110
59211
  // ---------------------------------------------------------------------
59111
59212
  // Import/Export
59112
59213
  // ---------------------------------------------------------------------
@@ -67502,9 +67603,10 @@ stores.inject(MyMetaStore, storeInstance);
67502
67603
  const filteredZone = filter.filteredRange?.zone;
67503
67604
  if (!filteredValues || !filteredZone)
67504
67605
  continue;
67606
+ const filteredValuesSet = new Set(filteredValues);
67505
67607
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
67506
67608
  const value = this.getCellValueAsString(sheetId, filter.col, row);
67507
- if (filteredValues.includes(value)) {
67609
+ if (filteredValuesSet.has(value)) {
67508
67610
  hiddenRows.add(row);
67509
67611
  }
67510
67612
  }
@@ -68015,11 +68117,6 @@ stores.inject(MyMetaStore, storeInstance);
68015
68117
  },
68016
68118
  ];
68017
68119
  const sheetId = this.getActiveSheetId();
68018
- const handler = new CellClipboardHandler(this.getters, this.dispatch);
68019
- const data = handler.copy(getClipboardDataPositions(sheetId, target));
68020
- if (!data) {
68021
- return;
68022
- }
68023
68120
  const base = isBasedBefore ? cmd.base : cmd.base + 1;
68024
68121
  const pasteTarget = [
68025
68122
  {
@@ -68029,7 +68126,14 @@ stores.inject(MyMetaStore, storeInstance);
68029
68126
  bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
68030
68127
  },
68031
68128
  ];
68032
- handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
68129
+ for (const Handler of clipboardHandlersRegistries.cellHandlers.getAll()) {
68130
+ const handler = new Handler(this.getters, this.dispatch);
68131
+ const data = handler.copy(getClipboardDataPositions(sheetId, target));
68132
+ if (!data) {
68133
+ continue;
68134
+ }
68135
+ handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
68136
+ }
68033
68137
  const selection = pasteTarget[0];
68034
68138
  const col = selection.left;
68035
68139
  const row = selection.top;
@@ -76432,9 +76536,9 @@ stores.inject(MyMetaStore, storeInstance);
76432
76536
  exports.tokenize = tokenize;
76433
76537
 
76434
76538
 
76435
- __info__.version = "18.1.23";
76436
- __info__.date = "2025-05-30T08:45:44.408Z";
76437
- __info__.hash = "a21fa01";
76539
+ __info__.version = "18.1.25";
76540
+ __info__.date = "2025-06-12T09:49:08.707Z";
76541
+ __info__.hash = "a232524";
76438
76542
 
76439
76543
 
76440
76544
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);