@odoo/o-spreadsheet 18.0.31 → 18.0.33

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.31
6
- * @date 2025-05-30T08:43:10.315Z
7
- * @hash d201086
5
+ * @version 18.0.33
6
+ * @date 2025-06-12T09:17:53.747Z
7
+ * @hash c1d64fb
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -5677,7 +5677,9 @@
5677
5677
  }
5678
5678
 
5679
5679
  function evaluateLiteral(literalCell, localeFormat) {
5680
- const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
5680
+ const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5681
+ ? literalCell.content
5682
+ : literalCell.parsedValue;
5681
5683
  const functionResult = { value, format: localeFormat.format };
5682
5684
  return createEvaluatedCell(functionResult, localeFormat.locale);
5683
5685
  }
@@ -5726,6 +5728,9 @@
5726
5728
  if (isEvaluationError(value)) {
5727
5729
  return errorCell(value, message);
5728
5730
  }
5731
+ if (value === null) {
5732
+ return emptyCell(format);
5733
+ }
5729
5734
  if (isTextFormat(format)) {
5730
5735
  // TO DO:
5731
5736
  // with the next line, the value of the cell is transformed depending on the format.
@@ -5733,9 +5738,6 @@
5733
5738
  // to interpret the value as a number.
5734
5739
  return textCell(toString(value), format, formattedValue);
5735
5740
  }
5736
- if (value === null) {
5737
- return emptyCell(format);
5738
- }
5739
5741
  if (typeof value === "number") {
5740
5742
  if (isDateTimeFormat(format || "")) {
5741
5743
  return dateTimeCell(value, format, formattedValue);
@@ -10121,7 +10123,8 @@ stores.inject(MyMetaStore, storeInstance);
10121
10123
  textsPositions[xPosition].push(yPosition);
10122
10124
  ctx.fillStyle = point.options.backgroundColor;
10123
10125
  ctx.strokeStyle = options.background || "#ffffff";
10124
- drawTextWithBackground(options.callback(value - 0), xPosition, yPosition, ctx);
10126
+ const valueToDisplay = options.callback(Number(value), dataset, i);
10127
+ drawTextWithBackground(valueToDisplay, xPosition, yPosition, ctx);
10125
10128
  }
10126
10129
  }
10127
10130
  }
@@ -10135,7 +10138,7 @@ stores.inject(MyMetaStore, storeInstance);
10135
10138
  }
10136
10139
  for (let i = 0; i < dataset._parsed.length; i++) {
10137
10140
  const value = dataset._parsed[i].x;
10138
- const displayValue = options.callback(value - 0);
10141
+ const displayValue = options.callback(value, dataset, i);
10139
10142
  const point = dataset.data[i];
10140
10143
  const yPosition = point.y;
10141
10144
  let xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
@@ -10170,10 +10173,22 @@ stores.inject(MyMetaStore, storeInstance);
10170
10173
  const midAngle = (startAngle + endAngle) / 2;
10171
10174
  const midRadius = (innerRadius + outerRadius) / 2;
10172
10175
  const x = bar.x + midRadius * Math.cos(midAngle);
10173
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
10176
+ const y = bar.y + midRadius * Math.sin(midAngle);
10177
+ const displayValue = options.callback(value, dataset, i);
10178
+ const textHeight = 12; // ChartJS default
10179
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
10180
+ const radius = outerRadius - innerRadius;
10181
+ // Check if the text fits in the slice. Not perfect, but good enough heuristic.
10182
+ if (textWidth >= radius || radius < textHeight) {
10183
+ continue;
10184
+ }
10185
+ const sliceAngle = endAngle - startAngle;
10186
+ const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
10187
+ if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
10188
+ continue;
10189
+ }
10174
10190
  ctx.fillStyle = chartFontColor(options.background);
10175
10191
  ctx.strokeStyle = options.background || "#ffffff";
10176
- const displayValue = options.callback(value);
10177
10192
  drawTextWithBackground(displayValue, x, y, ctx);
10178
10193
  }
10179
10194
  }
@@ -13390,7 +13405,7 @@ stores.inject(MyMetaStore, storeInstance);
13390
13405
  */
13391
13406
  handleMissingValue(parentElement, missingElementName, optionalArgs) {
13392
13407
  if (optionalArgs?.required) {
13393
- if (optionalArgs?.default) {
13408
+ if (optionalArgs?.default !== undefined) {
13394
13409
  this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
13395
13410
  }
13396
13411
  else {
@@ -30890,7 +30905,7 @@ stores.inject(MyMetaStore, storeInstance);
30890
30905
  return tooltipLabelCallback(tooltipItem);
30891
30906
  };
30892
30907
  const callback = config.options.plugins.chartShowValuesPlugin.callback;
30893
- config.options.plugins.chartShowValuesPlugin.callback = (x) => callback(Math.abs(x));
30908
+ config.options.plugins.chartShowValuesPlugin.callback = (value, dataset, index) => callback(Math.abs(value), dataset, index);
30894
30909
  return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
30895
30910
  }
30896
30911
 
@@ -31165,7 +31180,7 @@ stores.inject(MyMetaStore, storeInstance);
31165
31180
  return new WaterfallChart(definition, this.sheetId, this.getters);
31166
31181
  }
31167
31182
  }
31168
- function getWaterfallConfiguration(chart, labels, dataSeriesLabels, localeFormat) {
31183
+ function getWaterfallConfiguration(chart, labels, dataSeriesLabels, localeFormat, dataSetsValues) {
31169
31184
  const { locale, format } = localeFormat;
31170
31185
  const fontColor = chartFontColor(chart.background);
31171
31186
  const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
@@ -31258,10 +31273,22 @@ stores.inject(MyMetaStore, storeInstance);
31258
31273
  },
31259
31274
  };
31260
31275
  config.options.plugins.waterfallLinesPlugin = { showConnectorLines: chart.showConnectorLines };
31276
+ const subtotalIndexes = dataSetsValues.reduce((subtotalIndexes, ds) => {
31277
+ subtotalIndexes.push((subtotalIndexes.at(-1) || -1) + ds.data.length + 1);
31278
+ return subtotalIndexes;
31279
+ }, []);
31261
31280
  config.options.plugins.chartShowValuesPlugin = {
31262
31281
  showValues: chart.showValues,
31263
31282
  background: chart.background,
31264
- callback: formatTickValue(localeFormat),
31283
+ callback: (value, dataset, index) => {
31284
+ const raw = dataset._dataset.data[index];
31285
+ const delta = raw[1] - raw[0];
31286
+ let sign = delta >= 0 ? "+" : "";
31287
+ if (chart.showSubTotals && subtotalIndexes.includes(index) && sign === "+") {
31288
+ sign = "";
31289
+ }
31290
+ return `${sign}${formatTickValue(localeFormat)(delta)}`;
31291
+ },
31265
31292
  };
31266
31293
  return config;
31267
31294
  }
@@ -31282,10 +31309,7 @@ stores.inject(MyMetaStore, storeInstance);
31282
31309
  const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
31283
31310
  const locale = getters.getLocale();
31284
31311
  const dataSeriesLabels = dataSetsValues.map((dataSet) => dataSet.label);
31285
- const config = getWaterfallConfiguration(chart, labels, dataSeriesLabels, {
31286
- format: dataSetFormat,
31287
- locale,
31288
- });
31312
+ const config = getWaterfallConfiguration(chart, labels, dataSeriesLabels, { format: dataSetFormat, locale }, dataSetsValues);
31289
31313
  config.type = "bar";
31290
31314
  const negativeColor = chart.negativeValuesColor || CHART_WATERFALL_NEGATIVE_COLOR;
31291
31315
  const positiveColor = chart.positiveValuesColor || CHART_WATERFALL_POSITIVE_COLOR;
@@ -32507,19 +32531,26 @@ stores.inject(MyMetaStore, storeInstance);
32507
32531
  .filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
32508
32532
  .map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
32509
32533
  const filterValues = this.env.model.getters.getFilterHiddenValues({ sheetId, ...position });
32510
- const strValues = [...cellValues, ...filterValues];
32511
- const normalizedFilteredValues = filterValues.map(toLowerCase);
32512
- // Set with lowercase values to avoid duplicates
32513
- const normalizedValues = [...new Set(strValues.map(toLowerCase))];
32514
- const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
32515
- return sortedValues.map((normalizedValue) => {
32516
- const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
32517
- -1;
32518
- return {
32519
- checked,
32520
- string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
32521
- };
32522
- });
32534
+ const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
32535
+ const set = new Set();
32536
+ const values = [];
32537
+ const addValue = (value) => {
32538
+ const normalizedValue = toLowerCase(value);
32539
+ if (!set.has(normalizedValue)) {
32540
+ values.push({
32541
+ string: value || "",
32542
+ checked: !normalizedFilteredValues.has(normalizedValue),
32543
+ normalizedValue,
32544
+ });
32545
+ set.add(normalizedValue);
32546
+ }
32547
+ };
32548
+ cellValues.forEach(addValue);
32549
+ filterValues.forEach(addValue);
32550
+ return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
32551
+ numeric: true,
32552
+ sensitivity: "base",
32553
+ }));
32523
32554
  }
32524
32555
  checkValue(value) {
32525
32556
  this.state.selectedValue = value.string;
@@ -42346,13 +42377,14 @@ stores.inject(MyMetaStore, storeInstance);
42346
42377
  if (this.selectedMatchIndex === null) {
42347
42378
  return;
42348
42379
  }
42380
+ this.preserveSelectedMatchIndex = true;
42349
42381
  this.model.dispatch("REPLACE_SEARCH", {
42350
42382
  searchString: this.toSearch,
42351
42383
  replaceWith: this.toReplace,
42352
42384
  matches: [this.searchMatches[this.selectedMatchIndex]],
42353
42385
  searchOptions: this.searchOptions,
42354
42386
  });
42355
- this.selectNextCell(Direction.next);
42387
+ this.preserveSelectedMatchIndex = false;
42356
42388
  }
42357
42389
  /**
42358
42390
  * Apply the replace function to all the matches one time.
@@ -49697,6 +49729,9 @@ stores.inject(MyMetaStore, storeInstance);
49697
49729
  let deltaX = lastX - clientX;
49698
49730
  let deltaY = lastY - clientY;
49699
49731
  const elapsedTime = currentTime - lastTime;
49732
+ if (!elapsedTime) {
49733
+ return;
49734
+ }
49700
49735
  velocityX = deltaX / elapsedTime;
49701
49736
  velocityY = deltaY / elapsedTime;
49702
49737
  lastX = clientX;
@@ -49717,6 +49752,11 @@ stores.inject(MyMetaStore, storeInstance);
49717
49752
  function onTouchEnd(ev) {
49718
49753
  isMouseDown = false;
49719
49754
  lastX = lastY = 0;
49755
+ if (resetTimeout) {
49756
+ clearTimeout(resetTimeout);
49757
+ }
49758
+ velocityX *= 1.2;
49759
+ velocityY *= 1.2;
49720
49760
  requestAnimationFrame(scroll);
49721
49761
  }
49722
49762
  function scroll() {
@@ -56009,7 +56049,9 @@ stores.inject(MyMetaStore, storeInstance);
56009
56049
  const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
56010
56050
  const union = this.getters.getRangesUnion(ranges);
56011
56051
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
56012
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
56052
+ if (mergesInTarget.length) {
56053
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
56054
+ }
56013
56055
  const id = this.uuidGenerator.smallUuid();
56014
56056
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
56015
56057
  const newTable = cmd.tableType === "dynamic"
@@ -56120,14 +56162,16 @@ stores.inject(MyMetaStore, storeInstance);
56120
56162
  const zoneToCheckIfEmpty = direction === "down"
56121
56163
  ? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
56122
56164
  : { ...zone, right: zone.right + 1, left: zone.right + 1 };
56123
- for (const position of positions(zoneToCheckIfEmpty)) {
56124
- const cellPosition = { sheetId, ...position };
56125
- // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
56126
- const cellContent = this.getters.getCell(cellPosition)?.content;
56127
- if (cellContent ||
56128
- this.getters.isInMerge(cellPosition) ||
56129
- this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
56130
- return "none";
56165
+ for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
56166
+ for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
56167
+ const cellPosition = { sheetId, col, row };
56168
+ // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
56169
+ const cellContent = this.getters.getCell(cellPosition)?.content;
56170
+ if (cellContent ||
56171
+ this.getters.isInMerge(cellPosition) ||
56172
+ this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
56173
+ return "none";
56174
+ }
56131
56175
  }
56132
56176
  }
56133
56177
  return direction;
@@ -65452,9 +65496,10 @@ stores.inject(MyMetaStore, storeInstance);
65452
65496
  const filteredZone = filter.filteredRange?.zone;
65453
65497
  if (!filteredValues || !filteredZone)
65454
65498
  continue;
65499
+ const filteredValuesSet = new Set(filteredValues);
65455
65500
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
65456
65501
  const value = this.getCellValueAsString(sheetId, filter.col, row);
65457
- if (filteredValues.includes(value)) {
65502
+ if (filteredValuesSet.has(value)) {
65458
65503
  hiddenRows.add(row);
65459
65504
  }
65460
65505
  }
@@ -65969,11 +66014,6 @@ stores.inject(MyMetaStore, storeInstance);
65969
66014
  },
65970
66015
  ];
65971
66016
  const sheetId = this.getActiveSheetId();
65972
- const handler = new CellClipboardHandler(this.getters, this.dispatch);
65973
- const data = handler.copy(getClipboardDataPositions(sheetId, target));
65974
- if (!data) {
65975
- return;
65976
- }
65977
66017
  const base = isBasedBefore ? cmd.base : cmd.base + 1;
65978
66018
  const pasteTarget = [
65979
66019
  {
@@ -65983,7 +66023,14 @@ stores.inject(MyMetaStore, storeInstance);
65983
66023
  bottom: !isCol ? base + thickness - 1 : this.getters.getNumberRows(cmd.sheetId) - 1,
65984
66024
  },
65985
66025
  ];
65986
- handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
66026
+ for (const Handler of clipboardHandlersRegistries.cellHandlers.getAll()) {
66027
+ const handler = new Handler(this.getters, this.dispatch);
66028
+ const data = handler.copy(getClipboardDataPositions(sheetId, target));
66029
+ if (!data) {
66030
+ continue;
66031
+ }
66032
+ handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
66033
+ }
65987
66034
  const selection = pasteTarget[0];
65988
66035
  const col = selection.left;
65989
66036
  const row = selection.top;
@@ -74399,9 +74446,9 @@ stores.inject(MyMetaStore, storeInstance);
74399
74446
  exports.tokenize = tokenize;
74400
74447
 
74401
74448
 
74402
- __info__.version = "18.0.31";
74403
- __info__.date = "2025-05-30T08:43:10.315Z";
74404
- __info__.hash = "d201086";
74449
+ __info__.version = "18.0.33";
74450
+ __info__.date = "2025-06-12T09:17:53.747Z";
74451
+ __info__.hash = "c1d64fb";
74405
74452
 
74406
74453
 
74407
74454
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);