@odoo/o-spreadsheet 18.3.23 → 18.3.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.3.23
6
- * @date 2025-10-07T10:00:57.144Z
7
- * @hash b4764cb
5
+ * @version 18.3.25
6
+ * @date 2025-10-30T12:24:11.774Z
7
+ * @hash def7778
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
@@ -3941,7 +3941,17 @@ function toNumberMatrix(data, argName) {
3941
3941
  return toMatrix(data).map((row) => {
3942
3942
  return row.map((cell) => {
3943
3943
  if (typeof cell.value !== "number") {
3944
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects number values for %s, but got a %s.", argName, typeof cell.value));
3944
+ let message = "";
3945
+ if (typeof cell === "object") {
3946
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got an empty value.", argName);
3947
+ }
3948
+ else if (typeof cell === "string") {
3949
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a string.", argName);
3950
+ }
3951
+ else if (typeof cell === "boolean") {
3952
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a boolean.", argName);
3953
+ }
3954
+ throw new EvaluationError(message);
3945
3955
  }
3946
3956
  return cell.value;
3947
3957
  });
@@ -9126,7 +9136,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
9126
9136
  pasteCell(origin, target, clipboardOption) {
9127
9137
  const { sheetId, col, row } = target;
9128
9138
  const targetCell = this.getters.getEvaluatedCell(target);
9129
- const originFormat = origin?.format ?? origin.evaluatedCell.format;
9139
+ const originFormat = origin?.format || origin.evaluatedCell.format;
9130
9140
  if (clipboardOption?.pasteOption === "asValue") {
9131
9141
  this.dispatch("UPDATE_CELL", {
9132
9142
  ...target,
@@ -12832,7 +12842,7 @@ const GROWTH = {
12832
12842
  ],
12833
12843
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
12834
12844
  assertNonEmptyMatrix(knownDataY, "known_data_y");
12835
- 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)));
12845
+ return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "known_data_y")), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b)));
12836
12846
  },
12837
12847
  };
12838
12848
  // -----------------------------------------------------------------------------
@@ -12897,7 +12907,7 @@ const LINEST = {
12897
12907
  ],
12898
12908
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
12899
12909
  assertNonEmptyMatrix(dataY, "data_y");
12900
- return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
12910
+ return fullLinearRegression(toNumberMatrix(dataX, "data_x"), toNumberMatrix(dataY, "data_y"), toBoolean(calculateB), toBoolean(verbose));
12901
12911
  },
12902
12912
  isExported: true,
12903
12913
  };
@@ -12914,7 +12924,7 @@ const LOGEST = {
12914
12924
  ],
12915
12925
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
12916
12926
  assertNonEmptyMatrix(dataY, "data_y");
12917
- const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
12927
+ const coeffs = fullLinearRegression(toNumberMatrix(dataX, "data_x"), logM(toNumberMatrix(dataY, "data_y")), toBoolean(calculateB), toBoolean(verbose));
12918
12928
  for (let i = 0; i < coeffs.length; i++) {
12919
12929
  coeffs[i][0] = Math.exp(coeffs[i][0]);
12920
12930
  }
@@ -13504,7 +13514,7 @@ const TREND = {
13504
13514
  ],
13505
13515
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
13506
13516
  assertNonEmptyMatrix(knownDataY, "known_data_y");
13507
- 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));
13517
+ return predictLinearValues(toNumberMatrix(knownDataY, "known_data_y"), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b));
13508
13518
  },
13509
13519
  };
13510
13520
  // -----------------------------------------------------------------------------
@@ -21046,6 +21056,10 @@ const chartShowValuesPlugin = {
21046
21056
  }
21047
21057
  const ctx = chart.ctx;
21048
21058
  ctx.save();
21059
+ const { left, top, height, width } = chart.chartArea;
21060
+ ctx.beginPath();
21061
+ ctx.rect(left, top, width, height);
21062
+ ctx.clip();
21049
21063
  ctx.textAlign = "center";
21050
21064
  ctx.textBaseline = "middle";
21051
21065
  ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
@@ -25174,6 +25188,7 @@ function getChartTimeOptions(labels, labelFormat, locale) {
25174
25188
  parser: luxonFormat,
25175
25189
  displayFormats,
25176
25190
  unit: timeUnit ?? false,
25191
+ tooltipFormat: luxonFormat,
25177
25192
  };
25178
25193
  }
25179
25194
  /**
@@ -26243,6 +26258,7 @@ function getLineChartScales(definition, args) {
26243
26258
  };
26244
26259
  Object.assign(scales.x, axis);
26245
26260
  scales.x.ticks.maxTicksLimit = 15;
26261
+ delete scales?.x?.ticks?.callback;
26246
26262
  }
26247
26263
  else if (axisType === "linear") {
26248
26264
  scales.x.type = "linear";
@@ -30836,6 +30852,74 @@ iconsOnCellRegistry.add("conditional_formatting", (getters, position) => {
30836
30852
  return undefined;
30837
30853
  });
30838
30854
 
30855
+ /**
30856
+ * Get the relative path between two files
30857
+ *
30858
+ * Eg.:
30859
+ * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
30860
+ */
30861
+ function getRelativePath(from, to) {
30862
+ const fromPathParts = from.split("/");
30863
+ const toPathParts = to.split("/");
30864
+ let relPath = "";
30865
+ let startIndex = 0;
30866
+ for (let i = 0; i < fromPathParts.length - 1; i++) {
30867
+ if (fromPathParts[i] === toPathParts[i]) {
30868
+ startIndex++;
30869
+ }
30870
+ else {
30871
+ relPath += "../";
30872
+ }
30873
+ }
30874
+ relPath += toPathParts.slice(startIndex).join("/");
30875
+ return relPath;
30876
+ }
30877
+ /**
30878
+ * Convert an array of element into an object where the objects keys were the elements position in the array.
30879
+ * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
30880
+ *
30881
+ * eg. : ["a", "b"] => {0:"a", 1:"b"}
30882
+ */
30883
+ function arrayToObject(array, indexOffset = 0) {
30884
+ const obj = {};
30885
+ for (let i = 0; i < array.length; i++) {
30886
+ if (array[i]) {
30887
+ obj[i + indexOffset] = array[i];
30888
+ }
30889
+ }
30890
+ return obj;
30891
+ }
30892
+ /**
30893
+ * In xlsx we can have string with unicode characters with the format _x00fa_.
30894
+ * Replace with characters understandable by JS
30895
+ */
30896
+ function fixXlsxUnicode(str) {
30897
+ return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
30898
+ return String.fromCharCode(parseInt(code, 16));
30899
+ });
30900
+ }
30901
+ /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
30902
+ function getSheetDataHeader(sheetData, dimension, index) {
30903
+ if (dimension === "COL") {
30904
+ if (!sheetData.cols[index]) {
30905
+ sheetData.cols[index] = {};
30906
+ }
30907
+ return sheetData.cols[index];
30908
+ }
30909
+ if (!sheetData.rows[index]) {
30910
+ sheetData.rows[index] = {};
30911
+ }
30912
+ return sheetData.rows[index];
30913
+ }
30914
+ /** Prefix the string by "=" if the string looks like a formula */
30915
+ function prefixFormulaWithEqual(formula) {
30916
+ if (formula[0] === "=") {
30917
+ return formula;
30918
+ }
30919
+ const tokens = tokenize(formula);
30920
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
30921
+ }
30922
+
30839
30923
  /**
30840
30924
  * Map of the different types of conversions warnings and their name in error messages
30841
30925
  */
@@ -31343,66 +31427,6 @@ function hexaToInt(hex) {
31343
31427
  */
31344
31428
  const DEFAULT_SYSTEM_COLOR = "FF000000";
31345
31429
 
31346
- /**
31347
- * Get the relative path between two files
31348
- *
31349
- * Eg.:
31350
- * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
31351
- */
31352
- function getRelativePath(from, to) {
31353
- const fromPathParts = from.split("/");
31354
- const toPathParts = to.split("/");
31355
- let relPath = "";
31356
- let startIndex = 0;
31357
- for (let i = 0; i < fromPathParts.length - 1; i++) {
31358
- if (fromPathParts[i] === toPathParts[i]) {
31359
- startIndex++;
31360
- }
31361
- else {
31362
- relPath += "../";
31363
- }
31364
- }
31365
- relPath += toPathParts.slice(startIndex).join("/");
31366
- return relPath;
31367
- }
31368
- /**
31369
- * Convert an array of element into an object where the objects keys were the elements position in the array.
31370
- * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
31371
- *
31372
- * eg. : ["a", "b"] => {0:"a", 1:"b"}
31373
- */
31374
- function arrayToObject(array, indexOffset = 0) {
31375
- const obj = {};
31376
- for (let i = 0; i < array.length; i++) {
31377
- if (array[i]) {
31378
- obj[i + indexOffset] = array[i];
31379
- }
31380
- }
31381
- return obj;
31382
- }
31383
- /**
31384
- * In xlsx we can have string with unicode characters with the format _x00fa_.
31385
- * Replace with characters understandable by JS
31386
- */
31387
- function fixXlsxUnicode(str) {
31388
- return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
31389
- return String.fromCharCode(parseInt(code, 16));
31390
- });
31391
- }
31392
- /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
31393
- function getSheetDataHeader(sheetData, dimension, index) {
31394
- if (dimension === "COL") {
31395
- if (!sheetData.cols[index]) {
31396
- sheetData.cols[index] = {};
31397
- }
31398
- return sheetData.cols[index];
31399
- }
31400
- if (!sheetData.rows[index]) {
31401
- sheetData.rows[index] = {};
31402
- }
31403
- return sheetData.rows[index];
31404
- }
31405
-
31406
31430
  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;
31407
31431
  /**
31408
31432
  * Convert excel format to o_spreadsheet format
@@ -31612,9 +31636,9 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
31612
31636
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
31613
31637
  continue;
31614
31638
  operator = convertCFCellIsOperator(rule.operator);
31615
- values.push(prefixFormula(rule.formula[0]));
31639
+ values.push(prefixFormulaWithEqual(rule.formula[0]));
31616
31640
  if (rule.formula.length === 2) {
31617
- values.push(prefixFormula(rule.formula[1]));
31641
+ values.push(prefixFormulaWithEqual(rule.formula[1]));
31618
31642
  }
31619
31643
  break;
31620
31644
  }
@@ -31772,11 +31796,6 @@ function convertIcons(xlsxIconSet, index) {
31772
31796
  ? ICON_SETS[iconSet].neutral
31773
31797
  : ICON_SETS[iconSet].good;
31774
31798
  }
31775
- /** Prefix the string by "=" if the string looks like a formula */
31776
- function prefixFormula(formula) {
31777
- const tokens = tokenize(formula);
31778
- return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
31779
- }
31780
31799
  // ---------------------------------------------------------------------------
31781
31800
  // Warnings
31782
31801
  // ---------------------------------------------------------------------------
@@ -32213,7 +32232,7 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
32213
32232
  dvRules.push(decimalRule);
32214
32233
  break;
32215
32234
  case "list":
32216
- const listRule = convertListrule(dvId++, dv);
32235
+ const listRule = convertListRule(dvId++, dv);
32217
32236
  dvRules.push(listRule);
32218
32237
  break;
32219
32238
  case "date":
@@ -32233,9 +32252,9 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
32233
32252
  return dvRules;
32234
32253
  }
32235
32254
  function convertDecimalRule(id, dv) {
32236
- const values = [dv.formula1.toString()];
32255
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
32237
32256
  if (dv.formula2) {
32238
- values.push(dv.formula2.toString());
32257
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
32239
32258
  }
32240
32259
  return {
32241
32260
  id: id.toString(),
@@ -32247,7 +32266,7 @@ function convertDecimalRule(id, dv) {
32247
32266
  },
32248
32267
  };
32249
32268
  }
32250
- function convertListrule(id, dv) {
32269
+ function convertListRule(id, dv) {
32251
32270
  const formula1 = dv.formula1.toString();
32252
32271
  const isRangeRule = rangeReference.test(formula1);
32253
32272
  return {
@@ -32263,9 +32282,9 @@ function convertListrule(id, dv) {
32263
32282
  }
32264
32283
  function convertDateRule(id, dv) {
32265
32284
  let criterion;
32266
- const values = [dv.formula1.toString()];
32285
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
32267
32286
  if (dv.formula2) {
32268
- values.push(dv.formula2.toString());
32287
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
32269
32288
  criterion = {
32270
32289
  type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
32271
32290
  values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
@@ -32292,7 +32311,7 @@ function convertCustomRule(id, dv) {
32292
32311
  isBlocking: dv.errorStyle !== "warning",
32293
32312
  criterion: {
32294
32313
  type: "customFormula",
32295
- values: [`=${dv.formula1.toString()}`],
32314
+ values: [prefixFormulaWithEqual(dv.formula1.toString())],
32296
32315
  },
32297
32316
  };
32298
32317
  }
@@ -35331,8 +35350,10 @@ const LEGACY_VERSION_MAPPING = {
35331
35350
  17: "17.4",
35332
35351
  16: "17.3",
35333
35352
  15: "17.2",
35353
+ "14.5": "16.4.1",
35334
35354
  14: "16.4",
35335
35355
  13: "16.3",
35356
+ "12.5": "15.4.1",
35336
35357
  12: "15.4",
35337
35358
  // not accurate starting at this point
35338
35359
  11: "0.10",
@@ -47291,12 +47312,13 @@ class DataValidationEditor extends Component {
47291
47312
  onCloseSidePanel: { type: Function, optional: true },
47292
47313
  };
47293
47314
  state = useState({ rule: this.defaultDataValidationRule, errors: [] });
47315
+ editingSheetId;
47294
47316
  setup() {
47317
+ this.editingSheetId = this.env.model.getters.getActiveSheetId();
47295
47318
  if (this.props.rule) {
47296
- const sheetId = this.env.model.getters.getActiveSheetId();
47297
47319
  this.state.rule = {
47298
47320
  ...this.props.rule,
47299
- 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)),
47300
47322
  };
47301
47323
  this.state.rule.criterion.type = this.props.rule.criterion.type;
47302
47324
  }
@@ -47330,7 +47352,6 @@ class DataValidationEditor extends Component {
47330
47352
  const locale = this.env.model.getters.getLocale();
47331
47353
  const criterion = rule.criterion;
47332
47354
  const criterionEvaluator = dataValidationEvaluatorRegistry.get(criterion.type);
47333
- const sheetId = this.env.model.getters.getActiveSheetId();
47334
47355
  const values = criterion.values
47335
47356
  .slice(0, criterionEvaluator.numberOfValues(criterion))
47336
47357
  .map((value) => value?.trim())
@@ -47338,8 +47359,8 @@ class DataValidationEditor extends Component {
47338
47359
  .map((value) => canonicalizeContent(value, locale));
47339
47360
  rule.criterion = { ...criterion, values };
47340
47361
  return {
47341
- sheetId,
47342
- 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)),
47343
47364
  rule,
47344
47365
  };
47345
47366
  }
@@ -47869,6 +47890,7 @@ css /* scss */ `
47869
47890
  .o-button {
47870
47891
  height: 19px;
47871
47892
  width: 19px;
47893
+ box-sizing: content-box;
47872
47894
  .o-icon {
47873
47895
  height: 14px;
47874
47896
  width: 14px;
@@ -64059,7 +64081,7 @@ class FormulaDependencyGraph {
64059
64081
  * in the correct order they should be evaluated.
64060
64082
  * This is called a topological ordering (excluding cycles)
64061
64083
  */
64062
- getCellsDependingOn(ranges) {
64084
+ getCellsDependingOn(ranges, ignore) {
64063
64085
  const visited = this.createEmptyPositionSet();
64064
64086
  const queue = Array.from(ranges).reverse();
64065
64087
  while (queue.length > 0) {
@@ -64074,7 +64096,7 @@ class FormulaDependencyGraph {
64074
64096
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
64075
64097
  const nextInQueue = {};
64076
64098
  for (const position of impactedPositions) {
64077
- if (!visited.has(position)) {
64099
+ if (!visited.has(position) && !ignore.has(position)) {
64078
64100
  if (!nextInQueue[position.sheetId]) {
64079
64101
  nextInQueue[position.sheetId] = [];
64080
64102
  }
@@ -64631,7 +64653,7 @@ class Evaluator {
64631
64653
  }
64632
64654
  invalidatePositionsDependingOnSpread(sheetId, resultZone) {
64633
64655
  // the result matrix is split in 2 zones to exclude the array formula position
64634
- 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);
64635
64657
  invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
64636
64658
  this.nextPositionsToUpdate.addMany(invalidatedPositions);
64637
64659
  }
@@ -64749,7 +64771,7 @@ class Evaluator {
64749
64771
  for (const sheetId in zonesBySheetIds) {
64750
64772
  ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
64751
64773
  }
64752
- return this.formulaDependencies().getCellsDependingOn(ranges);
64774
+ return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
64753
64775
  }
64754
64776
  }
64755
64777
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -65961,7 +65983,8 @@ class DynamicTablesPlugin extends CoreViewPlugin {
65961
65983
  const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
65962
65984
  const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
65963
65985
  if (!parentSpreadingCell) {
65964
- return false;
65986
+ const evaluatedCell = this.getters.getEvaluatedCell(topLeft);
65987
+ return (evaluatedCell.value === CellErrorType.SpilledBlocked && !evaluatedCell.errorOriginPosition);
65965
65988
  }
65966
65989
  else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
65967
65990
  return true;
@@ -80986,6 +81009,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
80986
81009
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
80987
81010
 
80988
81011
 
80989
- __info__.version = "18.3.23";
80990
- __info__.date = "2025-10-07T10:00:57.144Z";
80991
- __info__.hash = "b4764cb";
81012
+ __info__.version = "18.3.25";
81013
+ __info__.date = "2025-10-30T12:24:11.774Z";
81014
+ __info__.hash = "def7778";