@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
  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
  }
@@ -47291,12 +47310,13 @@ class DataValidationEditor extends Component {
47291
47310
  onCloseSidePanel: { type: Function, optional: true },
47292
47311
  };
47293
47312
  state = useState({ rule: this.defaultDataValidationRule, errors: [] });
47313
+ editingSheetId;
47294
47314
  setup() {
47315
+ this.editingSheetId = this.env.model.getters.getActiveSheetId();
47295
47316
  if (this.props.rule) {
47296
- const sheetId = this.env.model.getters.getActiveSheetId();
47297
47317
  this.state.rule = {
47298
47318
  ...this.props.rule,
47299
- ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, sheetId)),
47319
+ ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, this.editingSheetId)),
47300
47320
  };
47301
47321
  this.state.rule.criterion.type = this.props.rule.criterion.type;
47302
47322
  }
@@ -47330,7 +47350,6 @@ class DataValidationEditor extends Component {
47330
47350
  const locale = this.env.model.getters.getLocale();
47331
47351
  const criterion = rule.criterion;
47332
47352
  const criterionEvaluator = dataValidationEvaluatorRegistry.get(criterion.type);
47333
- const sheetId = this.env.model.getters.getActiveSheetId();
47334
47353
  const values = criterion.values
47335
47354
  .slice(0, criterionEvaluator.numberOfValues(criterion))
47336
47355
  .map((value) => value?.trim())
@@ -47338,8 +47357,8 @@ class DataValidationEditor extends Component {
47338
47357
  .map((value) => canonicalizeContent(value, locale));
47339
47358
  rule.criterion = { ...criterion, values };
47340
47359
  return {
47341
- sheetId,
47342
- ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
47360
+ sheetId: this.editingSheetId,
47361
+ ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(this.editingSheetId, xc)),
47343
47362
  rule,
47344
47363
  };
47345
47364
  }
@@ -47869,6 +47888,7 @@ css /* scss */ `
47869
47888
  .o-button {
47870
47889
  height: 19px;
47871
47890
  width: 19px;
47891
+ box-sizing: content-box;
47872
47892
  .o-icon {
47873
47893
  height: 14px;
47874
47894
  width: 14px;
@@ -64059,7 +64079,7 @@ class FormulaDependencyGraph {
64059
64079
  * in the correct order they should be evaluated.
64060
64080
  * This is called a topological ordering (excluding cycles)
64061
64081
  */
64062
- getCellsDependingOn(ranges) {
64082
+ getCellsDependingOn(ranges, ignore) {
64063
64083
  const visited = this.createEmptyPositionSet();
64064
64084
  const queue = Array.from(ranges).reverse();
64065
64085
  while (queue.length > 0) {
@@ -64074,7 +64094,7 @@ class FormulaDependencyGraph {
64074
64094
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
64075
64095
  const nextInQueue = {};
64076
64096
  for (const position of impactedPositions) {
64077
- if (!visited.has(position)) {
64097
+ if (!visited.has(position) && !ignore.has(position)) {
64078
64098
  if (!nextInQueue[position.sheetId]) {
64079
64099
  nextInQueue[position.sheetId] = [];
64080
64100
  }
@@ -64631,7 +64651,7 @@ class Evaluator {
64631
64651
  }
64632
64652
  invalidatePositionsDependingOnSpread(sheetId, resultZone) {
64633
64653
  // 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 })));
64654
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })), this.nextPositionsToUpdate);
64635
64655
  invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
64636
64656
  this.nextPositionsToUpdate.addMany(invalidatedPositions);
64637
64657
  }
@@ -64749,7 +64769,7 @@ class Evaluator {
64749
64769
  for (const sheetId in zonesBySheetIds) {
64750
64770
  ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
64751
64771
  }
64752
- return this.formulaDependencies().getCellsDependingOn(ranges);
64772
+ return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
64753
64773
  }
64754
64774
  }
64755
64775
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -65961,7 +65981,8 @@ class DynamicTablesPlugin extends CoreViewPlugin {
65961
65981
  const topLeft = { col: unionZone.left, row: unionZone.top, sheetId };
65962
65982
  const parentSpreadingCell = this.getters.getArrayFormulaSpreadingOn(topLeft);
65963
65983
  if (!parentSpreadingCell) {
65964
- return false;
65984
+ const evaluatedCell = this.getters.getEvaluatedCell(topLeft);
65985
+ return (evaluatedCell.value === CellErrorType.SpilledBlocked && !evaluatedCell.errorOriginPosition);
65965
65986
  }
65966
65987
  else if (deepEquals(parentSpreadingCell, topLeft) && getZoneArea(unionZone) === 1) {
65967
65988
  return true;
@@ -80986,6 +81007,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
80986
81007
  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
81008
 
80988
81009
 
80989
- __info__.version = "18.3.23";
80990
- __info__.date = "2025-10-07T10:00:57.144Z";
80991
- __info__.hash = "b4764cb";
81010
+ __info__.version = "18.3.24";
81011
+ __info__.date = "2025-10-16T06:38:12.942Z";
81012
+ __info__.hash = "f13dd1c";