@odoo/o-spreadsheet 18.1.24 → 18.1.26

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.24
6
- * @date 2025-06-06T09:31:57.011Z
7
- * @hash 0e386b2
5
+ * @version 18.1.26
6
+ * @date 2025-06-19T18:21:37.648Z
7
+ * @hash 06479d4
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);
@@ -10201,6 +10203,7 @@ stores.inject(MyMetaStore, storeInstance);
10201
10203
  if (isTrendLineAxis(dataset.xAxisID) || dataset.hidden) {
10202
10204
  continue;
10203
10205
  }
10206
+ const yAxisScale = chart.scales[dataset.yAxisID];
10204
10207
  for (let i = 0; i < dataset._parsed.length; i++) {
10205
10208
  const parsedValue = dataset._parsed[i];
10206
10209
  const value = Number(chart.config.type === "radar" ? parsedValue.r : parsedValue.y);
@@ -10211,10 +10214,18 @@ stores.inject(MyMetaStore, storeInstance);
10211
10214
  const xPosition = point.x;
10212
10215
  let yPosition = 0;
10213
10216
  if (chart.config.type === "line" || chart.config.type === "radar") {
10214
- yPosition = point.y - 10;
10217
+ yPosition = value < 0 ? point.y + 10 : point.y - 10;
10215
10218
  }
10216
10219
  else {
10217
- yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
10220
+ const yZeroLine = yAxisScale.getPixelForValue(0);
10221
+ const distanceFromAxisOrigin = Math.abs(yZeroLine - point.y);
10222
+ const textHeight = 12; // ChartJS default text height
10223
+ if (distanceFromAxisOrigin < textHeight) {
10224
+ yPosition = value < 0 ? yZeroLine + textHeight / 2 : yZeroLine - textHeight / 2;
10225
+ }
10226
+ else {
10227
+ yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
10228
+ }
10218
10229
  }
10219
10230
  yPosition = Math.min(yPosition, yMax);
10220
10231
  yPosition = Math.max(yPosition, yMin);
@@ -10224,7 +10235,7 @@ stores.inject(MyMetaStore, storeInstance);
10224
10235
  }
10225
10236
  for (const otherPosition of textsPositions[xPosition] || []) {
10226
10237
  if (Math.abs(otherPosition - yPosition) < 13) {
10227
- yPosition = otherPosition - 13;
10238
+ yPosition = value < 0 ? otherPosition + 13 : otherPosition - 13;
10228
10239
  }
10229
10240
  }
10230
10241
  textsPositions[xPosition].push(yPosition);
@@ -10243,6 +10254,8 @@ stores.inject(MyMetaStore, storeInstance);
10243
10254
  if (isTrendLineAxis(dataset.xAxisID)) {
10244
10255
  return; // ignore trend lines
10245
10256
  }
10257
+ const xAxisScale = chart.scales[dataset.xAxisID];
10258
+ const xZeroLine = xAxisScale.getPixelForValue(0);
10246
10259
  for (let i = 0; i < dataset._parsed.length; i++) {
10247
10260
  const value = Number(dataset._parsed[i].x);
10248
10261
  if (isNaN(value)) {
@@ -10251,17 +10264,27 @@ stores.inject(MyMetaStore, storeInstance);
10251
10264
  const displayValue = options.callback(value, dataset, i);
10252
10265
  const point = dataset.data[i];
10253
10266
  const yPosition = point.y;
10254
- let xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
10255
- xPosition = Math.min(xPosition, xMax);
10256
- xPosition = Math.max(xPosition, xMin);
10267
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
10268
+ const distanceFromAxisOrigin = Math.abs(point.x - xZeroLine);
10269
+ const PADDING = 3;
10270
+ let xPosition;
10271
+ if (distanceFromAxisOrigin < textWidth) {
10272
+ xPosition =
10273
+ value < 0 ? xZeroLine - textWidth / 2 - PADDING : xZeroLine + textWidth / 2 + PADDING;
10274
+ }
10275
+ else {
10276
+ xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
10277
+ xPosition = Math.min(xPosition, xMax);
10278
+ xPosition = Math.max(xPosition, xMin);
10279
+ }
10257
10280
  // Avoid overlapping texts with same Y
10258
10281
  if (!textsPositions[yPosition]) {
10259
10282
  textsPositions[yPosition] = [];
10260
10283
  }
10261
- const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
10262
10284
  for (const otherPosition of textsPositions[yPosition]) {
10263
10285
  if (Math.abs(otherPosition - xPosition) < textWidth) {
10264
- xPosition = otherPosition + textWidth + 3;
10286
+ xPosition =
10287
+ value < 0 ? otherPosition - textWidth - PADDING : otherPosition + textWidth + PADDING;
10265
10288
  }
10266
10289
  }
10267
10290
  textsPositions[yPosition].push(xPosition);
@@ -10283,10 +10306,22 @@ stores.inject(MyMetaStore, storeInstance);
10283
10306
  const midAngle = (startAngle + endAngle) / 2;
10284
10307
  const midRadius = (innerRadius + outerRadius) / 2;
10285
10308
  const x = bar.x + midRadius * Math.cos(midAngle);
10286
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
10309
+ const y = bar.y + midRadius * Math.sin(midAngle);
10310
+ const displayValue = options.callback(value, dataset, i);
10311
+ const textHeight = 12; // ChartJS default
10312
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
10313
+ const radius = outerRadius - innerRadius;
10314
+ // Check if the text fits in the slice. Not perfect, but good enough heuristic.
10315
+ if (textWidth >= radius || radius < textHeight) {
10316
+ continue;
10317
+ }
10318
+ const sliceAngle = endAngle - startAngle;
10319
+ const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
10320
+ if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
10321
+ continue;
10322
+ }
10287
10323
  ctx.fillStyle = chartFontColor(options.background);
10288
10324
  ctx.strokeStyle = options.background || "#ffffff";
10289
- const displayValue = options.callback(value, dataset, i);
10290
10325
  drawTextWithBackground(displayValue, x, y, ctx);
10291
10326
  }
10292
10327
  }
@@ -26192,7 +26227,7 @@ stores.inject(MyMetaStore, storeInstance);
26192
26227
  */
26193
26228
  handleMissingValue(parentElement, missingElementName, optionalArgs) {
26194
26229
  if (optionalArgs?.required) {
26195
- if (optionalArgs?.default) {
26230
+ if (optionalArgs?.default !== undefined) {
26196
26231
  this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
26197
26232
  }
26198
26233
  else {
@@ -30065,7 +30100,9 @@ stores.inject(MyMetaStore, storeInstance);
30065
30100
  background: definition.background,
30066
30101
  callback: (value, dataset) => {
30067
30102
  value = Math.abs(Number(value));
30068
- return formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
30103
+ return value === 0
30104
+ ? ""
30105
+ : formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
30069
30106
  },
30070
30107
  };
30071
30108
  }
@@ -33214,19 +33251,26 @@ stores.inject(MyMetaStore, storeInstance);
33214
33251
  .filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
33215
33252
  .map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
33216
33253
  const filterValues = this.env.model.getters.getFilterHiddenValues({ sheetId, ...position });
33217
- const strValues = [...cellValues, ...filterValues];
33218
- const normalizedFilteredValues = filterValues.map(toLowerCase);
33219
- // Set with lowercase values to avoid duplicates
33220
- const normalizedValues = [...new Set(strValues.map(toLowerCase))];
33221
- const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
33222
- return sortedValues.map((normalizedValue) => {
33223
- const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
33224
- -1;
33225
- return {
33226
- checked,
33227
- string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
33228
- };
33229
- });
33254
+ const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
33255
+ const set = new Set();
33256
+ const values = [];
33257
+ const addValue = (value) => {
33258
+ const normalizedValue = toLowerCase(value);
33259
+ if (!set.has(normalizedValue)) {
33260
+ values.push({
33261
+ string: value || "",
33262
+ checked: !normalizedFilteredValues.has(normalizedValue),
33263
+ normalizedValue,
33264
+ });
33265
+ set.add(normalizedValue);
33266
+ }
33267
+ };
33268
+ cellValues.forEach(addValue);
33269
+ filterValues.forEach(addValue);
33270
+ return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
33271
+ numeric: true,
33272
+ sensitivity: "base",
33273
+ }));
33230
33274
  }
33231
33275
  checkValue(value) {
33232
33276
  this.state.selectedValue = value.string;
@@ -34558,6 +34602,10 @@ stores.inject(MyMetaStore, storeInstance);
34558
34602
  });
34559
34603
  };
34560
34604
  const CAN_REMOVE_COLUMNS_ROWS = (dimension, env) => {
34605
+ if ((dimension === "COL" && env.model.getters.getActiveRows().size > 0) ||
34606
+ (dimension === "ROW" && env.model.getters.getActiveCols().size > 0)) {
34607
+ return false;
34608
+ }
34561
34609
  const sheetId = env.model.getters.getActiveSheetId();
34562
34610
  const selectedElements = env.model.getters.getElementsFromSelection(dimension);
34563
34611
  const includesAllVisibleHeaders = env.model.getters.checkElementsIncludeAllVisibleHeaders(sheetId, dimension, selectedElements);
@@ -37537,11 +37585,11 @@ stores.inject(MyMetaStore, storeInstance);
37537
37585
  * transformation function given
37538
37586
  */
37539
37587
  addTransformation(executed, toTransforms, fn) {
37540
- for (let toTransform of toTransforms) {
37541
- if (!this.content[toTransform]) {
37542
- this.content[toTransform] = new Map();
37543
- }
37544
- this.content[toTransform].set(executed, fn);
37588
+ if (!this.content[executed]) {
37589
+ this.content[executed] = new Map();
37590
+ }
37591
+ for (const toTransform of toTransforms) {
37592
+ this.content[executed].set(toTransform, fn);
37545
37593
  }
37546
37594
  return this;
37547
37595
  }
@@ -37550,7 +37598,7 @@ stores.inject(MyMetaStore, storeInstance);
37550
37598
  * that the executed command happened.
37551
37599
  */
37552
37600
  getTransformation(toTransform, executed) {
37553
- return this.content[toTransform] && this.content[toTransform].get(executed);
37601
+ return this.content[executed] && this.content[executed].get(toTransform);
37554
37602
  }
37555
37603
  }
37556
37604
  const otRegistry = new OTRegistry();
@@ -58094,7 +58142,9 @@ stores.inject(MyMetaStore, storeInstance);
58094
58142
  const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
58095
58143
  const union = this.getters.getRangesUnion(ranges);
58096
58144
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
58097
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
58145
+ if (mergesInTarget.length) {
58146
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
58147
+ }
58098
58148
  const id = `${nextTableId++}`;
58099
58149
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
58100
58150
  const newTable = cmd.tableType === "dynamic"
@@ -58205,14 +58255,16 @@ stores.inject(MyMetaStore, storeInstance);
58205
58255
  const zoneToCheckIfEmpty = direction === "down"
58206
58256
  ? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
58207
58257
  : { ...zone, right: zone.right + 1, left: zone.right + 1 };
58208
- for (const position of positions(zoneToCheckIfEmpty)) {
58209
- const cellPosition = { sheetId, ...position };
58210
- // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
58211
- const cellContent = this.getters.getCell(cellPosition)?.content;
58212
- if (cellContent ||
58213
- this.getters.isInMerge(cellPosition) ||
58214
- this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
58215
- return "none";
58258
+ for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
58259
+ for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
58260
+ const cellPosition = { sheetId, col, row };
58261
+ // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
58262
+ const cellContent = this.getters.getCell(cellPosition)?.content;
58263
+ if (cellContent ||
58264
+ this.getters.isInMerge(cellPosition) ||
58265
+ this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
58266
+ return "none";
58267
+ }
58216
58268
  }
58217
58269
  }
58218
58270
  return direction;
@@ -64241,10 +64293,20 @@ stores.inject(MyMetaStore, storeInstance);
64241
64293
  */
64242
64294
  function transformAll(toTransform, executed) {
64243
64295
  let transformedCommands = [...toTransform];
64296
+ const possibleTransformations = new Set(otRegistry.getKeys());
64244
64297
  for (const executedCommand of executed) {
64245
- transformedCommands = transformedCommands
64246
- .map((cmd) => transform(cmd, executedCommand))
64247
- .filter(isDefined);
64298
+ // If the executed command is not in the registry, we skip it
64299
+ // because we know there won't be any transformation impacting the
64300
+ // commands to transform.
64301
+ if (possibleTransformations.has(executedCommand.type)) {
64302
+ transformedCommands = transformedCommands.reduce((acc, cmd) => {
64303
+ const transformed = transform(cmd, executedCommand);
64304
+ if (transformed) {
64305
+ acc.push(transformed);
64306
+ }
64307
+ return acc;
64308
+ }, []);
64309
+ }
64248
64310
  }
64249
64311
  return transformedCommands;
64250
64312
  }
@@ -65939,7 +66001,7 @@ stores.inject(MyMetaStore, storeInstance);
65939
66001
  }
65940
66002
  const position = this.getters.getCellPosition(cell.id);
65941
66003
  const colSize = this.getters.getColSize(sheetId, position.col);
65942
- if (cell.isFormula) {
66004
+ if (cell.isFormula || this.getters.getArrayFormulaSpreadingOn(position)) {
65943
66005
  const content = this.getters.getEvaluatedCell(position).formattedValue;
65944
66006
  const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
65945
66007
  if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
@@ -67578,9 +67640,10 @@ stores.inject(MyMetaStore, storeInstance);
67578
67640
  const filteredZone = filter.filteredRange?.zone;
67579
67641
  if (!filteredValues || !filteredZone)
67580
67642
  continue;
67643
+ const filteredValuesSet = new Set(filteredValues);
67581
67644
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
67582
67645
  const value = this.getCellValueAsString(sheetId, filter.col, row);
67583
- if (filteredValues.includes(value)) {
67646
+ if (filteredValuesSet.has(value)) {
67584
67647
  hiddenRows.add(row);
67585
67648
  }
67586
67649
  }
@@ -76510,9 +76573,9 @@ stores.inject(MyMetaStore, storeInstance);
76510
76573
  exports.tokenize = tokenize;
76511
76574
 
76512
76575
 
76513
- __info__.version = "18.1.24";
76514
- __info__.date = "2025-06-06T09:31:57.011Z";
76515
- __info__.hash = "0e386b2";
76576
+ __info__.version = "18.1.26";
76577
+ __info__.date = "2025-06-19T18:21:37.648Z";
76578
+ __info__.hash = "06479d4";
76516
76579
 
76517
76580
 
76518
76581
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);