@odoo/o-spreadsheet 18.2.16 → 18.2.18

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.2.16
6
- * @date 2025-06-06T09:32:04.909Z
7
- * @hash 7ee118c
5
+ * @version 18.2.18
6
+ * @date 2025-06-19T18:24:41.051Z
7
+ * @hash 024c134
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -5855,7 +5855,9 @@
5855
5855
  }
5856
5856
 
5857
5857
  function evaluateLiteral(literalCell, localeFormat) {
5858
- const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
5858
+ const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5859
+ ? literalCell.content
5860
+ : literalCell.parsedValue;
5859
5861
  const functionResult = { value, format: localeFormat.format };
5860
5862
  return createEvaluatedCell(functionResult, localeFormat.locale);
5861
5863
  }
@@ -5904,6 +5906,9 @@
5904
5906
  if (isEvaluationError(value)) {
5905
5907
  return errorCell(value, message);
5906
5908
  }
5909
+ if (value === null) {
5910
+ return emptyCell(format);
5911
+ }
5907
5912
  if (isTextFormat(format)) {
5908
5913
  // TO DO:
5909
5914
  // with the next line, the value of the cell is transformed depending on the format.
@@ -5911,9 +5916,6 @@
5911
5916
  // to interpret the value as a number.
5912
5917
  return textCell(toString(value), format, formattedValue);
5913
5918
  }
5914
- if (value === null) {
5915
- return emptyCell(format);
5916
- }
5917
5919
  if (typeof value === "number") {
5918
5920
  if (isDateTimeFormat(format || "")) {
5919
5921
  return dateTimeCell(value, format, formattedValue);
@@ -10365,6 +10367,7 @@ stores.inject(MyMetaStore, storeInstance);
10365
10367
  if (isTrendLineAxis(dataset.xAxisID) || dataset.hidden) {
10366
10368
  continue;
10367
10369
  }
10370
+ const yAxisScale = chart.scales[dataset.yAxisID];
10368
10371
  for (let i = 0; i < dataset._parsed.length; i++) {
10369
10372
  const parsedValue = dataset._parsed[i];
10370
10373
  const value = Number(chart.config.type === "radar" ? parsedValue.r : parsedValue.y);
@@ -10375,10 +10378,18 @@ stores.inject(MyMetaStore, storeInstance);
10375
10378
  const xPosition = point.x;
10376
10379
  let yPosition = 0;
10377
10380
  if (chart.config.type === "line" || chart.config.type === "radar") {
10378
- yPosition = point.y - 10;
10381
+ yPosition = value < 0 ? point.y + 10 : point.y - 10;
10379
10382
  }
10380
10383
  else {
10381
- yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
10384
+ const yZeroLine = yAxisScale.getPixelForValue(0);
10385
+ const distanceFromAxisOrigin = Math.abs(yZeroLine - point.y);
10386
+ const textHeight = 12; // ChartJS default text height
10387
+ if (distanceFromAxisOrigin < textHeight) {
10388
+ yPosition = value < 0 ? yZeroLine + textHeight / 2 : yZeroLine - textHeight / 2;
10389
+ }
10390
+ else {
10391
+ yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
10392
+ }
10382
10393
  }
10383
10394
  yPosition = Math.min(yPosition, yMax);
10384
10395
  yPosition = Math.max(yPosition, yMin);
@@ -10388,7 +10399,7 @@ stores.inject(MyMetaStore, storeInstance);
10388
10399
  }
10389
10400
  for (const otherPosition of textsPositions[xPosition] || []) {
10390
10401
  if (Math.abs(otherPosition - yPosition) < 13) {
10391
- yPosition = otherPosition - 13;
10402
+ yPosition = value < 0 ? otherPosition + 13 : otherPosition - 13;
10392
10403
  }
10393
10404
  }
10394
10405
  textsPositions[xPosition].push(yPosition);
@@ -10407,6 +10418,8 @@ stores.inject(MyMetaStore, storeInstance);
10407
10418
  if (isTrendLineAxis(dataset.xAxisID)) {
10408
10419
  return; // ignore trend lines
10409
10420
  }
10421
+ const xAxisScale = chart.scales[dataset.xAxisID];
10422
+ const xZeroLine = xAxisScale.getPixelForValue(0);
10410
10423
  for (let i = 0; i < dataset._parsed.length; i++) {
10411
10424
  const value = Number(dataset._parsed[i].x);
10412
10425
  if (isNaN(value)) {
@@ -10415,17 +10428,27 @@ stores.inject(MyMetaStore, storeInstance);
10415
10428
  const displayValue = options.callback(value, dataset, i);
10416
10429
  const point = dataset.data[i];
10417
10430
  const yPosition = point.y;
10418
- let xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
10419
- xPosition = Math.min(xPosition, xMax);
10420
- xPosition = Math.max(xPosition, xMin);
10431
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
10432
+ const distanceFromAxisOrigin = Math.abs(point.x - xZeroLine);
10433
+ const PADDING = 3;
10434
+ let xPosition;
10435
+ if (distanceFromAxisOrigin < textWidth) {
10436
+ xPosition =
10437
+ value < 0 ? xZeroLine - textWidth / 2 - PADDING : xZeroLine + textWidth / 2 + PADDING;
10438
+ }
10439
+ else {
10440
+ xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
10441
+ xPosition = Math.min(xPosition, xMax);
10442
+ xPosition = Math.max(xPosition, xMin);
10443
+ }
10421
10444
  // Avoid overlapping texts with same Y
10422
10445
  if (!textsPositions[yPosition]) {
10423
10446
  textsPositions[yPosition] = [];
10424
10447
  }
10425
- const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
10426
10448
  for (const otherPosition of textsPositions[yPosition]) {
10427
10449
  if (Math.abs(otherPosition - xPosition) < textWidth) {
10428
- xPosition = otherPosition + textWidth + 3;
10450
+ xPosition =
10451
+ value < 0 ? otherPosition - textWidth - PADDING : otherPosition + textWidth + PADDING;
10429
10452
  }
10430
10453
  }
10431
10454
  textsPositions[yPosition].push(xPosition);
@@ -10447,10 +10470,22 @@ stores.inject(MyMetaStore, storeInstance);
10447
10470
  const midAngle = (startAngle + endAngle) / 2;
10448
10471
  const midRadius = (innerRadius + outerRadius) / 2;
10449
10472
  const x = bar.x + midRadius * Math.cos(midAngle);
10450
- const y = bar.y + midRadius * Math.sin(midAngle) + 7;
10473
+ const y = bar.y + midRadius * Math.sin(midAngle);
10474
+ const displayValue = options.callback(value, dataset, i);
10475
+ const textHeight = 12; // ChartJS default
10476
+ const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
10477
+ const radius = outerRadius - innerRadius;
10478
+ // Check if the text fits in the slice. Not perfect, but good enough heuristic.
10479
+ if (textWidth >= radius || radius < textHeight) {
10480
+ continue;
10481
+ }
10482
+ const sliceAngle = endAngle - startAngle;
10483
+ const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
10484
+ if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
10485
+ continue;
10486
+ }
10451
10487
  ctx.fillStyle = chartFontColor(options.background);
10452
10488
  ctx.strokeStyle = options.background || "#ffffff";
10453
- const displayValue = options.callback(value, dataset, i);
10454
10489
  drawTextWithBackground(displayValue, x, y, ctx);
10455
10490
  }
10456
10491
  }
@@ -26216,7 +26251,7 @@ stores.inject(MyMetaStore, storeInstance);
26216
26251
  */
26217
26252
  handleMissingValue(parentElement, missingElementName, optionalArgs) {
26218
26253
  if (optionalArgs?.required) {
26219
- if (optionalArgs?.default) {
26254
+ if (optionalArgs?.default !== undefined) {
26220
26255
  this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
26221
26256
  }
26222
26257
  else {
@@ -30097,7 +30132,9 @@ stores.inject(MyMetaStore, storeInstance);
30097
30132
  background: definition.background,
30098
30133
  callback: (value, dataset) => {
30099
30134
  value = Math.abs(Number(value));
30100
- return formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
30135
+ return value === 0
30136
+ ? ""
30137
+ : formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
30101
30138
  },
30102
30139
  };
30103
30140
  }
@@ -33413,19 +33450,26 @@ stores.inject(MyMetaStore, storeInstance);
33413
33450
  .filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
33414
33451
  .map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
33415
33452
  const filterValues = this.env.model.getters.getFilterHiddenValues({ sheetId, ...position });
33416
- const strValues = [...cellValues, ...filterValues];
33417
- const normalizedFilteredValues = filterValues.map(toLowerCase);
33418
- // Set with lowercase values to avoid duplicates
33419
- const normalizedValues = [...new Set(strValues.map(toLowerCase))];
33420
- const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
33421
- return sortedValues.map((normalizedValue) => {
33422
- const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
33423
- -1;
33424
- return {
33425
- checked,
33426
- string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
33427
- };
33428
- });
33453
+ const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
33454
+ const set = new Set();
33455
+ const values = [];
33456
+ const addValue = (value) => {
33457
+ const normalizedValue = toLowerCase(value);
33458
+ if (!set.has(normalizedValue)) {
33459
+ values.push({
33460
+ string: value || "",
33461
+ checked: !normalizedFilteredValues.has(normalizedValue),
33462
+ normalizedValue,
33463
+ });
33464
+ set.add(normalizedValue);
33465
+ }
33466
+ };
33467
+ cellValues.forEach(addValue);
33468
+ filterValues.forEach(addValue);
33469
+ return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
33470
+ numeric: true,
33471
+ sensitivity: "base",
33472
+ }));
33429
33473
  }
33430
33474
  checkValue(value) {
33431
33475
  this.state.selectedValue = value.string;
@@ -34759,6 +34803,10 @@ stores.inject(MyMetaStore, storeInstance);
34759
34803
  });
34760
34804
  };
34761
34805
  const CAN_REMOVE_COLUMNS_ROWS = (dimension, env) => {
34806
+ if ((dimension === "COL" && env.model.getters.getActiveRows().size > 0) ||
34807
+ (dimension === "ROW" && env.model.getters.getActiveCols().size > 0)) {
34808
+ return false;
34809
+ }
34762
34810
  const sheetId = env.model.getters.getActiveSheetId();
34763
34811
  const selectedElements = env.model.getters.getElementsFromSelection(dimension);
34764
34812
  const includesAllVisibleHeaders = env.model.getters.checkElementsIncludeAllVisibleHeaders(sheetId, dimension, selectedElements);
@@ -37738,11 +37786,11 @@ stores.inject(MyMetaStore, storeInstance);
37738
37786
  * transformation function given
37739
37787
  */
37740
37788
  addTransformation(executed, toTransforms, fn) {
37741
- for (let toTransform of toTransforms) {
37742
- if (!this.content[toTransform]) {
37743
- this.content[toTransform] = new Map();
37744
- }
37745
- this.content[toTransform].set(executed, fn);
37789
+ if (!this.content[executed]) {
37790
+ this.content[executed] = new Map();
37791
+ }
37792
+ for (const toTransform of toTransforms) {
37793
+ this.content[executed].set(toTransform, fn);
37746
37794
  }
37747
37795
  return this;
37748
37796
  }
@@ -37751,7 +37799,7 @@ stores.inject(MyMetaStore, storeInstance);
37751
37799
  * that the executed command happened.
37752
37800
  */
37753
37801
  getTransformation(toTransform, executed) {
37754
- return this.content[toTransform] && this.content[toTransform].get(executed);
37802
+ return this.content[executed] && this.content[executed].get(toTransform);
37755
37803
  }
37756
37804
  }
37757
37805
  const otRegistry = new OTRegistry();
@@ -58592,7 +58640,9 @@ stores.inject(MyMetaStore, storeInstance);
58592
58640
  const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
58593
58641
  const union = this.getters.getRangesUnion(ranges);
58594
58642
  const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
58595
- this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
58643
+ if (mergesInTarget.length) {
58644
+ this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
58645
+ }
58596
58646
  const id = this.consumeNextId();
58597
58647
  const config = cmd.config || DEFAULT_TABLE_CONFIG;
58598
58648
  const newTable = cmd.tableType === "dynamic"
@@ -58691,14 +58741,16 @@ stores.inject(MyMetaStore, storeInstance);
58691
58741
  const zoneToCheckIfEmpty = direction === "down"
58692
58742
  ? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
58693
58743
  : { ...zone, right: zone.right + 1, left: zone.right + 1 };
58694
- for (const position of positions(zoneToCheckIfEmpty)) {
58695
- const cellPosition = { sheetId, ...position };
58696
- // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
58697
- const cellContent = this.getters.getCell(cellPosition)?.content;
58698
- if (cellContent ||
58699
- this.getters.isInMerge(cellPosition) ||
58700
- this.getTablesOverlappingZones(sheetId, [positionToZone(position)]).length) {
58701
- return "none";
58744
+ for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
58745
+ for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
58746
+ const cellPosition = { sheetId, col, row };
58747
+ // Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
58748
+ const cellContent = this.getters.getCell(cellPosition)?.content;
58749
+ if (cellContent ||
58750
+ this.getters.isInMerge(cellPosition) ||
58751
+ this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
58752
+ return "none";
58753
+ }
58702
58754
  }
58703
58755
  }
58704
58756
  return direction;
@@ -64741,10 +64793,20 @@ stores.inject(MyMetaStore, storeInstance);
64741
64793
  */
64742
64794
  function transformAll(toTransform, executed) {
64743
64795
  let transformedCommands = [...toTransform];
64796
+ const possibleTransformations = new Set(otRegistry.getKeys());
64744
64797
  for (const executedCommand of executed) {
64745
- transformedCommands = transformedCommands
64746
- .map((cmd) => transform(cmd, executedCommand))
64747
- .filter(isDefined);
64798
+ // If the executed command is not in the registry, we skip it
64799
+ // because we know there won't be any transformation impacting the
64800
+ // commands to transform.
64801
+ if (possibleTransformations.has(executedCommand.type)) {
64802
+ transformedCommands = transformedCommands.reduce((acc, cmd) => {
64803
+ const transformed = transform(cmd, executedCommand);
64804
+ if (transformed) {
64805
+ acc.push(transformed);
64806
+ }
64807
+ return acc;
64808
+ }, []);
64809
+ }
64748
64810
  }
64749
64811
  return transformedCommands;
64750
64812
  }
@@ -66433,7 +66495,7 @@ stores.inject(MyMetaStore, storeInstance);
66433
66495
  }
66434
66496
  const position = this.getters.getCellPosition(cell.id);
66435
66497
  const colSize = this.getters.getColSize(sheetId, position.col);
66436
- if (cell.isFormula) {
66498
+ if (cell.isFormula || this.getters.getArrayFormulaSpreadingOn(position)) {
66437
66499
  const content = this.getters.getEvaluatedCell(position).formattedValue;
66438
66500
  const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
66439
66501
  if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
@@ -68072,9 +68134,10 @@ stores.inject(MyMetaStore, storeInstance);
68072
68134
  const filteredZone = filter.filteredRange?.zone;
68073
68135
  if (!filteredValues || !filteredZone)
68074
68136
  continue;
68137
+ const filteredValuesSet = new Set(filteredValues);
68075
68138
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
68076
68139
  const value = this.getCellValueAsString(sheetId, filter.col, row);
68077
- if (filteredValues.includes(value)) {
68140
+ if (filteredValuesSet.has(value)) {
68078
68141
  hiddenRows.add(row);
68079
68142
  }
68080
68143
  }
@@ -76988,9 +77051,9 @@ stores.inject(MyMetaStore, storeInstance);
76988
77051
  exports.tokenize = tokenize;
76989
77052
 
76990
77053
 
76991
- __info__.version = "18.2.16";
76992
- __info__.date = "2025-06-06T09:32:04.909Z";
76993
- __info__.hash = "7ee118c";
77054
+ __info__.version = "18.2.18";
77055
+ __info__.date = "2025-06-19T18:24:41.051Z";
77056
+ __info__.hash = "024c134";
76994
77057
 
76995
77058
 
76996
77059
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);