@odoo/o-spreadsheet 18.3.23 → 18.3.24

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.23
6
- * @date 2025-10-07T10:00:57.144Z
7
- * @hash b4764cb
5
+ * @version 18.3.24
6
+ * @date 2025-10-16T06:38:12.942Z
7
+ * @hash f13dd1c
8
8
  */
9
9
 
10
10
  'use strict';
@@ -3943,7 +3943,17 @@ function toNumberMatrix(data, argName) {
3943
3943
  return toMatrix(data).map((row) => {
3944
3944
  return row.map((cell) => {
3945
3945
  if (typeof cell.value !== "number") {
3946
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects number values for %s, but got a %s.", argName, typeof cell.value));
3946
+ let message = "";
3947
+ if (typeof cell === "object") {
3948
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got an empty value.", argName);
3949
+ }
3950
+ else if (typeof cell === "string") {
3951
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a string.", argName);
3952
+ }
3953
+ else if (typeof cell === "boolean") {
3954
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a boolean.", argName);
3955
+ }
3956
+ throw new EvaluationError(message);
3947
3957
  }
3948
3958
  return cell.value;
3949
3959
  });
@@ -9128,7 +9138,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
9128
9138
  pasteCell(origin, target, clipboardOption) {
9129
9139
  const { sheetId, col, row } = target;
9130
9140
  const targetCell = this.getters.getEvaluatedCell(target);
9131
- const originFormat = origin?.format ?? origin.evaluatedCell.format;
9141
+ const originFormat = origin?.format || origin.evaluatedCell.format;
9132
9142
  if (clipboardOption?.pasteOption === "asValue") {
9133
9143
  this.dispatch("UPDATE_CELL", {
9134
9144
  ...target,
@@ -12834,7 +12844,7 @@ const GROWTH = {
12834
12844
  ],
12835
12845
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
12836
12846
  assertNonEmptyMatrix(knownDataY, "known_data_y");
12837
- return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "the first argument (known_data_y)")), toNumberMatrix(knownDataX, "the second argument (known_data_x)"), toNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b)));
12847
+ return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "known_data_y")), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b)));
12838
12848
  },
12839
12849
  };
12840
12850
  // -----------------------------------------------------------------------------
@@ -12899,7 +12909,7 @@ const LINEST = {
12899
12909
  ],
12900
12910
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
12901
12911
  assertNonEmptyMatrix(dataY, "data_y");
12902
- return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
12912
+ return fullLinearRegression(toNumberMatrix(dataX, "data_x"), toNumberMatrix(dataY, "data_y"), toBoolean(calculateB), toBoolean(verbose));
12903
12913
  },
12904
12914
  isExported: true,
12905
12915
  };
@@ -12916,7 +12926,7 @@ const LOGEST = {
12916
12926
  ],
12917
12927
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
12918
12928
  assertNonEmptyMatrix(dataY, "data_y");
12919
- const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
12929
+ const coeffs = fullLinearRegression(toNumberMatrix(dataX, "data_x"), logM(toNumberMatrix(dataY, "data_y")), toBoolean(calculateB), toBoolean(verbose));
12920
12930
  for (let i = 0; i < coeffs.length; i++) {
12921
12931
  coeffs[i][0] = Math.exp(coeffs[i][0]);
12922
12932
  }
@@ -13506,7 +13516,7 @@ const TREND = {
13506
13516
  ],
13507
13517
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
13508
13518
  assertNonEmptyMatrix(knownDataY, "known_data_y");
13509
- return predictLinearValues(toNumberMatrix(knownDataY, "the first argument (known_data_y)"), toNumberMatrix(knownDataX, "the second argument (known_data_x)"), toNumberMatrix(newDataX, "the third argument (new_data_y)"), toBoolean(b));
13519
+ return predictLinearValues(toNumberMatrix(knownDataY, "known_data_y"), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b));
13510
13520
  },
13511
13521
  };
13512
13522
  // -----------------------------------------------------------------------------
@@ -21048,6 +21058,10 @@ const chartShowValuesPlugin = {
21048
21058
  }
21049
21059
  const ctx = chart.ctx;
21050
21060
  ctx.save();
21061
+ const { left, top, height, width } = chart.chartArea;
21062
+ ctx.beginPath();
21063
+ ctx.rect(left, top, width, height);
21064
+ ctx.clip();
21051
21065
  ctx.textAlign = "center";
21052
21066
  ctx.textBaseline = "middle";
21053
21067
  ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
@@ -25176,6 +25190,7 @@ function getChartTimeOptions(labels, labelFormat, locale) {
25176
25190
  parser: luxonFormat,
25177
25191
  displayFormats,
25178
25192
  unit: timeUnit ?? false,
25193
+ tooltipFormat: luxonFormat,
25179
25194
  };
25180
25195
  }
25181
25196
  /**
@@ -26245,6 +26260,7 @@ function getLineChartScales(definition, args) {
26245
26260
  };
26246
26261
  Object.assign(scales.x, axis);
26247
26262
  scales.x.ticks.maxTicksLimit = 15;
26263
+ delete scales?.x?.ticks?.callback;
26248
26264
  }
26249
26265
  else if (axisType === "linear") {
26250
26266
  scales.x.type = "linear";
@@ -30838,6 +30854,74 @@ iconsOnCellRegistry.add("conditional_formatting", (getters, position) => {
30838
30854
  return undefined;
30839
30855
  });
30840
30856
 
30857
+ /**
30858
+ * Get the relative path between two files
30859
+ *
30860
+ * Eg.:
30861
+ * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
30862
+ */
30863
+ function getRelativePath(from, to) {
30864
+ const fromPathParts = from.split("/");
30865
+ const toPathParts = to.split("/");
30866
+ let relPath = "";
30867
+ let startIndex = 0;
30868
+ for (let i = 0; i < fromPathParts.length - 1; i++) {
30869
+ if (fromPathParts[i] === toPathParts[i]) {
30870
+ startIndex++;
30871
+ }
30872
+ else {
30873
+ relPath += "../";
30874
+ }
30875
+ }
30876
+ relPath += toPathParts.slice(startIndex).join("/");
30877
+ return relPath;
30878
+ }
30879
+ /**
30880
+ * Convert an array of element into an object where the objects keys were the elements position in the array.
30881
+ * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
30882
+ *
30883
+ * eg. : ["a", "b"] => {0:"a", 1:"b"}
30884
+ */
30885
+ function arrayToObject(array, indexOffset = 0) {
30886
+ const obj = {};
30887
+ for (let i = 0; i < array.length; i++) {
30888
+ if (array[i]) {
30889
+ obj[i + indexOffset] = array[i];
30890
+ }
30891
+ }
30892
+ return obj;
30893
+ }
30894
+ /**
30895
+ * In xlsx we can have string with unicode characters with the format _x00fa_.
30896
+ * Replace with characters understandable by JS
30897
+ */
30898
+ function fixXlsxUnicode(str) {
30899
+ return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
30900
+ return String.fromCharCode(parseInt(code, 16));
30901
+ });
30902
+ }
30903
+ /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
30904
+ function getSheetDataHeader(sheetData, dimension, index) {
30905
+ if (dimension === "COL") {
30906
+ if (!sheetData.cols[index]) {
30907
+ sheetData.cols[index] = {};
30908
+ }
30909
+ return sheetData.cols[index];
30910
+ }
30911
+ if (!sheetData.rows[index]) {
30912
+ sheetData.rows[index] = {};
30913
+ }
30914
+ return sheetData.rows[index];
30915
+ }
30916
+ /** Prefix the string by "=" if the string looks like a formula */
30917
+ function prefixFormulaWithEqual(formula) {
30918
+ if (formula[0] === "=") {
30919
+ return formula;
30920
+ }
30921
+ const tokens = tokenize(formula);
30922
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
30923
+ }
30924
+
30841
30925
  /**
30842
30926
  * Map of the different types of conversions warnings and their name in error messages
30843
30927
  */
@@ -31345,66 +31429,6 @@ function hexaToInt(hex) {
31345
31429
  */
31346
31430
  const DEFAULT_SYSTEM_COLOR = "FF000000";
31347
31431
 
31348
- /**
31349
- * Get the relative path between two files
31350
- *
31351
- * Eg.:
31352
- * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
31353
- */
31354
- function getRelativePath(from, to) {
31355
- const fromPathParts = from.split("/");
31356
- const toPathParts = to.split("/");
31357
- let relPath = "";
31358
- let startIndex = 0;
31359
- for (let i = 0; i < fromPathParts.length - 1; i++) {
31360
- if (fromPathParts[i] === toPathParts[i]) {
31361
- startIndex++;
31362
- }
31363
- else {
31364
- relPath += "../";
31365
- }
31366
- }
31367
- relPath += toPathParts.slice(startIndex).join("/");
31368
- return relPath;
31369
- }
31370
- /**
31371
- * Convert an array of element into an object where the objects keys were the elements position in the array.
31372
- * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
31373
- *
31374
- * eg. : ["a", "b"] => {0:"a", 1:"b"}
31375
- */
31376
- function arrayToObject(array, indexOffset = 0) {
31377
- const obj = {};
31378
- for (let i = 0; i < array.length; i++) {
31379
- if (array[i]) {
31380
- obj[i + indexOffset] = array[i];
31381
- }
31382
- }
31383
- return obj;
31384
- }
31385
- /**
31386
- * In xlsx we can have string with unicode characters with the format _x00fa_.
31387
- * Replace with characters understandable by JS
31388
- */
31389
- function fixXlsxUnicode(str) {
31390
- return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
31391
- return String.fromCharCode(parseInt(code, 16));
31392
- });
31393
- }
31394
- /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
31395
- function getSheetDataHeader(sheetData, dimension, index) {
31396
- if (dimension === "COL") {
31397
- if (!sheetData.cols[index]) {
31398
- sheetData.cols[index] = {};
31399
- }
31400
- return sheetData.cols[index];
31401
- }
31402
- if (!sheetData.rows[index]) {
31403
- sheetData.rows[index] = {};
31404
- }
31405
- return sheetData.rows[index];
31406
- }
31407
-
31408
31432
  const XLSX_DATE_FORMAT_REGEX = /^(yy|yyyy|m{1,5}|d{1,4}|h{1,2}|s{1,2}|am\/pm|a\/m|\s|-|\/|\.|:)+$/i;
31409
31433
  /**
31410
31434
  * Convert excel format to o_spreadsheet format
@@ -31614,9 +31638,9 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
31614
31638
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
31615
31639
  continue;
31616
31640
  operator = convertCFCellIsOperator(rule.operator);
31617
- values.push(prefixFormula(rule.formula[0]));
31641
+ values.push(prefixFormulaWithEqual(rule.formula[0]));
31618
31642
  if (rule.formula.length === 2) {
31619
- values.push(prefixFormula(rule.formula[1]));
31643
+ values.push(prefixFormulaWithEqual(rule.formula[1]));
31620
31644
  }
31621
31645
  break;
31622
31646
  }
@@ -31774,11 +31798,6 @@ function convertIcons(xlsxIconSet, index) {
31774
31798
  ? ICON_SETS[iconSet].neutral
31775
31799
  : ICON_SETS[iconSet].good;
31776
31800
  }
31777
- /** Prefix the string by "=" if the string looks like a formula */
31778
- function prefixFormula(formula) {
31779
- const tokens = tokenize(formula);
31780
- return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
31781
- }
31782
31801
  // ---------------------------------------------------------------------------
31783
31802
  // Warnings
31784
31803
  // ---------------------------------------------------------------------------
@@ -32215,7 +32234,7 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
32215
32234
  dvRules.push(decimalRule);
32216
32235
  break;
32217
32236
  case "list":
32218
- const listRule = convertListrule(dvId++, dv);
32237
+ const listRule = convertListRule(dvId++, dv);
32219
32238
  dvRules.push(listRule);
32220
32239
  break;
32221
32240
  case "date":
@@ -32235,9 +32254,9 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
32235
32254
  return dvRules;
32236
32255
  }
32237
32256
  function convertDecimalRule(id, dv) {
32238
- const values = [dv.formula1.toString()];
32257
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
32239
32258
  if (dv.formula2) {
32240
- values.push(dv.formula2.toString());
32259
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
32241
32260
  }
32242
32261
  return {
32243
32262
  id: id.toString(),
@@ -32249,7 +32268,7 @@ function convertDecimalRule(id, dv) {
32249
32268
  },
32250
32269
  };
32251
32270
  }
32252
- function convertListrule(id, dv) {
32271
+ function convertListRule(id, dv) {
32253
32272
  const formula1 = dv.formula1.toString();
32254
32273
  const isRangeRule = rangeReference.test(formula1);
32255
32274
  return {
@@ -32265,9 +32284,9 @@ function convertListrule(id, dv) {
32265
32284
  }
32266
32285
  function convertDateRule(id, dv) {
32267
32286
  let criterion;
32268
- const values = [dv.formula1.toString()];
32287
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
32269
32288
  if (dv.formula2) {
32270
- values.push(dv.formula2.toString());
32289
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
32271
32290
  criterion = {
32272
32291
  type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
32273
32292
  values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
@@ -32294,7 +32313,7 @@ function convertCustomRule(id, dv) {
32294
32313
  isBlocking: dv.errorStyle !== "warning",
32295
32314
  criterion: {
32296
32315
  type: "customFormula",
32297
- values: [`=${dv.formula1.toString()}`],
32316
+ values: [prefixFormulaWithEqual(dv.formula1.toString())],
32298
32317
  },
32299
32318
  };
32300
32319
  }
@@ -47293,12 +47312,13 @@ class DataValidationEditor extends owl.Component {
47293
47312
  onCloseSidePanel: { type: Function, optional: true },
47294
47313
  };
47295
47314
  state = owl.useState({ rule: this.defaultDataValidationRule, errors: [] });
47315
+ editingSheetId;
47296
47316
  setup() {
47317
+ this.editingSheetId = this.env.model.getters.getActiveSheetId();
47297
47318
  if (this.props.rule) {
47298
- const sheetId = this.env.model.getters.getActiveSheetId();
47299
47319
  this.state.rule = {
47300
47320
  ...this.props.rule,
47301
- ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, sheetId)),
47321
+ ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, this.editingSheetId)),
47302
47322
  };
47303
47323
  this.state.rule.criterion.type = this.props.rule.criterion.type;
47304
47324
  }
@@ -47332,7 +47352,6 @@ class DataValidationEditor extends owl.Component {
47332
47352
  const locale = this.env.model.getters.getLocale();
47333
47353
  const criterion = rule.criterion;
47334
47354
  const criterionEvaluator = dataValidationEvaluatorRegistry.get(criterion.type);
47335
- const sheetId = this.env.model.getters.getActiveSheetId();
47336
47355
  const values = criterion.values
47337
47356
  .slice(0, criterionEvaluator.numberOfValues(criterion))
47338
47357
  .map((value) => value?.trim())
@@ -47340,8 +47359,8 @@ class DataValidationEditor extends owl.Component {
47340
47359
  .map((value) => canonicalizeContent(value, locale));
47341
47360
  rule.criterion = { ...criterion, values };
47342
47361
  return {
47343
- sheetId,
47344
- ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
47362
+ sheetId: this.editingSheetId,
47363
+ ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(this.editingSheetId, xc)),
47345
47364
  rule,
47346
47365
  };
47347
47366
  }
@@ -47871,6 +47890,7 @@ css /* scss */ `
47871
47890
  .o-button {
47872
47891
  height: 19px;
47873
47892
  width: 19px;
47893
+ box-sizing: content-box;
47874
47894
  .o-icon {
47875
47895
  height: 14px;
47876
47896
  width: 14px;
@@ -64061,7 +64081,7 @@ class FormulaDependencyGraph {
64061
64081
  * in the correct order they should be evaluated.
64062
64082
  * This is called a topological ordering (excluding cycles)
64063
64083
  */
64064
- getCellsDependingOn(ranges) {
64084
+ getCellsDependingOn(ranges, ignore) {
64065
64085
  const visited = this.createEmptyPositionSet();
64066
64086
  const queue = Array.from(ranges).reverse();
64067
64087
  while (queue.length > 0) {
@@ -64076,7 +64096,7 @@ class FormulaDependencyGraph {
64076
64096
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
64077
64097
  const nextInQueue = {};
64078
64098
  for (const position of impactedPositions) {
64079
- if (!visited.has(position)) {
64099
+ if (!visited.has(position) && !ignore.has(position)) {
64080
64100
  if (!nextInQueue[position.sheetId]) {
64081
64101
  nextInQueue[position.sheetId] = [];
64082
64102
  }
@@ -64633,7 +64653,7 @@ class Evaluator {
64633
64653
  }
64634
64654
  invalidatePositionsDependingOnSpread(sheetId, resultZone) {
64635
64655
  // the result matrix is split in 2 zones to exclude the array formula position
64636
- const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
64656
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })), this.nextPositionsToUpdate);
64637
64657
  invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
64638
64658
  this.nextPositionsToUpdate.addMany(invalidatedPositions);
64639
64659
  }
@@ -64751,7 +64771,7 @@ class Evaluator {
64751
64771
  for (const sheetId in zonesBySheetIds) {
64752
64772
  ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
64753
64773
  }
64754
- return this.formulaDependencies().getCellsDependingOn(ranges);
64774
+ return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
64755
64775
  }
64756
64776
  }
64757
64777
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -65963,7 +65983,8 @@ class DynamicTablesPlugin extends CoreViewPlugin {
65963
65983
  const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
65964
65984
  const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
65965
65985
  if (!parentSpreadingCell) {
65966
- return false;
65986
+ const evaluatedCell = this.getters.getEvaluatedCell(topLeft);
65987
+ return (evaluatedCell.value === CellErrorType.SpilledBlocked && !evaluatedCell.errorOriginPosition);
65967
65988
  }
65968
65989
  else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
65969
65990
  return true;
@@ -81034,6 +81055,6 @@ exports.tokenColors = tokenColors;
81034
81055
  exports.tokenize = tokenize;
81035
81056
 
81036
81057
 
81037
- __info__.version = "18.3.23";
81038
- __info__.date = "2025-10-07T10:00:57.144Z";
81039
- __info__.hash = "b4764cb";
81058
+ __info__.version = "18.3.24";
81059
+ __info__.date = "2025-10-16T06:38:12.942Z";
81060
+ __info__.hash = "f13dd1c";