@odoo/o-spreadsheet 18.2.32 → 18.2.33

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.32
6
- * @date 2025-10-07T09:59:52.165Z
7
- * @hash a42c448
5
+ * @version 18.2.33
6
+ * @date 2025-10-16T06:39:40.421Z
7
+ * @hash 280596c
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';
@@ -3914,7 +3914,17 @@ function toNumberMatrix(data, argName) {
3914
3914
  return toMatrix(data).map((row) => {
3915
3915
  return row.map((cell) => {
3916
3916
  if (typeof cell.value !== "number") {
3917
- throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects number values for %s, but got a %s.", argName, typeof cell.value));
3917
+ let message = "";
3918
+ if (typeof cell === "object") {
3919
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got an empty value.", argName);
3920
+ }
3921
+ else if (typeof cell === "string") {
3922
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a string.", argName);
3923
+ }
3924
+ else if (typeof cell === "boolean") {
3925
+ message = _t("Function [[FUNCTION_NAME]] expects number values for %s, but got a boolean.", argName);
3926
+ }
3927
+ throw new EvaluationError(message);
3918
3928
  }
3919
3929
  return cell.value;
3920
3930
  });
@@ -8816,7 +8826,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
8816
8826
  pasteCell(origin, target, clipboardOption) {
8817
8827
  const { sheetId, col, row } = target;
8818
8828
  const targetCell = this.getters.getEvaluatedCell(target);
8819
- const originFormat = origin?.format ?? origin.evaluatedCell.format;
8829
+ const originFormat = origin?.format || origin.evaluatedCell.format;
8820
8830
  if (clipboardOption?.pasteOption === "asValue") {
8821
8831
  this.dispatch("UPDATE_CELL", {
8822
8832
  ...target,
@@ -10437,6 +10447,10 @@ const chartShowValuesPlugin = {
10437
10447
  }
10438
10448
  const ctx = chart.ctx;
10439
10449
  ctx.save();
10450
+ const { left, top, height, width } = chart.chartArea;
10451
+ ctx.beginPath();
10452
+ ctx.rect(left, top, width, height);
10453
+ ctx.clip();
10440
10454
  ctx.textAlign = "center";
10441
10455
  ctx.textBaseline = "middle";
10442
10456
  ctx.miterLimit = 1; // Avoid sharp artifacts on strokeText
@@ -13642,7 +13656,7 @@ const GROWTH = {
13642
13656
  ],
13643
13657
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
13644
13658
  assertNonEmptyMatrix(knownDataY, "known_data_y");
13645
- 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)));
13659
+ return expM(predictLinearValues(logM(toNumberMatrix(knownDataY, "known_data_y")), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b)));
13646
13660
  },
13647
13661
  };
13648
13662
  // -----------------------------------------------------------------------------
@@ -13707,7 +13721,7 @@ const LINEST = {
13707
13721
  ],
13708
13722
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13709
13723
  assertNonEmptyMatrix(dataY, "data_y");
13710
- return fullLinearRegression(toNumberMatrix(dataX, "the first argument (data_y)"), toNumberMatrix(dataY, "the second argument (data_x)"), toBoolean(calculateB), toBoolean(verbose));
13724
+ return fullLinearRegression(toNumberMatrix(dataX, "data_x"), toNumberMatrix(dataY, "data_y"), toBoolean(calculateB), toBoolean(verbose));
13711
13725
  },
13712
13726
  isExported: true,
13713
13727
  };
@@ -13724,7 +13738,7 @@ const LOGEST = {
13724
13738
  ],
13725
13739
  compute: function (dataY, dataX = [[]], calculateB = { value: true }, verbose = { value: false }) {
13726
13740
  assertNonEmptyMatrix(dataY, "data_y");
13727
- const coeffs = fullLinearRegression(toNumberMatrix(dataX, "the second argument (data_x)"), logM(toNumberMatrix(dataY, "the first argument (data_y)")), toBoolean(calculateB), toBoolean(verbose));
13741
+ const coeffs = fullLinearRegression(toNumberMatrix(dataX, "data_x"), logM(toNumberMatrix(dataY, "data_y")), toBoolean(calculateB), toBoolean(verbose));
13728
13742
  for (let i = 0; i < coeffs.length; i++) {
13729
13743
  coeffs[i][0] = Math.exp(coeffs[i][0]);
13730
13744
  }
@@ -14314,7 +14328,7 @@ const TREND = {
14314
14328
  ],
14315
14329
  compute: function (knownDataY, knownDataX = [[]], newDataX = [[]], b = { value: true }) {
14316
14330
  assertNonEmptyMatrix(knownDataY, "known_data_y");
14317
- 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));
14331
+ return predictLinearValues(toNumberMatrix(knownDataY, "known_data_y"), toNumberMatrix(knownDataX, "known_data_x"), toNumberMatrix(newDataX, "new_data_y"), toBoolean(b));
14318
14332
  },
14319
14333
  };
14320
14334
  // -----------------------------------------------------------------------------
@@ -23745,6 +23759,74 @@ iconsOnCellRegistry.add("conditional_formatting", (getters, position) => {
23745
23759
  }
23746
23760
  });
23747
23761
 
23762
+ /**
23763
+ * Get the relative path between two files
23764
+ *
23765
+ * Eg.:
23766
+ * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
23767
+ */
23768
+ function getRelativePath(from, to) {
23769
+ const fromPathParts = from.split("/");
23770
+ const toPathParts = to.split("/");
23771
+ let relPath = "";
23772
+ let startIndex = 0;
23773
+ for (let i = 0; i < fromPathParts.length - 1; i++) {
23774
+ if (fromPathParts[i] === toPathParts[i]) {
23775
+ startIndex++;
23776
+ }
23777
+ else {
23778
+ relPath += "../";
23779
+ }
23780
+ }
23781
+ relPath += toPathParts.slice(startIndex).join("/");
23782
+ return relPath;
23783
+ }
23784
+ /**
23785
+ * Convert an array of element into an object where the objects keys were the elements position in the array.
23786
+ * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
23787
+ *
23788
+ * eg. : ["a", "b"] => {0:"a", 1:"b"}
23789
+ */
23790
+ function arrayToObject(array, indexOffset = 0) {
23791
+ const obj = {};
23792
+ for (let i = 0; i < array.length; i++) {
23793
+ if (array[i]) {
23794
+ obj[i + indexOffset] = array[i];
23795
+ }
23796
+ }
23797
+ return obj;
23798
+ }
23799
+ /**
23800
+ * In xlsx we can have string with unicode characters with the format _x00fa_.
23801
+ * Replace with characters understandable by JS
23802
+ */
23803
+ function fixXlsxUnicode(str) {
23804
+ return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
23805
+ return String.fromCharCode(parseInt(code, 16));
23806
+ });
23807
+ }
23808
+ /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
23809
+ function getSheetDataHeader(sheetData, dimension, index) {
23810
+ if (dimension === "COL") {
23811
+ if (!sheetData.cols[index]) {
23812
+ sheetData.cols[index] = {};
23813
+ }
23814
+ return sheetData.cols[index];
23815
+ }
23816
+ if (!sheetData.rows[index]) {
23817
+ sheetData.rows[index] = {};
23818
+ }
23819
+ return sheetData.rows[index];
23820
+ }
23821
+ /** Prefix the string by "=" if the string looks like a formula */
23822
+ function prefixFormulaWithEqual(formula) {
23823
+ if (formula[0] === "=") {
23824
+ return formula;
23825
+ }
23826
+ const tokens = tokenize(formula);
23827
+ return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
23828
+ }
23829
+
23748
23830
  /**
23749
23831
  * Map of the different types of conversions warnings and their name in error messages
23750
23832
  */
@@ -24252,66 +24334,6 @@ function hexaToInt(hex) {
24252
24334
  */
24253
24335
  const DEFAULT_SYSTEM_COLOR = "FF000000";
24254
24336
 
24255
- /**
24256
- * Get the relative path between two files
24257
- *
24258
- * Eg.:
24259
- * from "folder1/file1.txt" to "folder2/file2.txt" => "../folder2/file2.txt"
24260
- */
24261
- function getRelativePath(from, to) {
24262
- const fromPathParts = from.split("/");
24263
- const toPathParts = to.split("/");
24264
- let relPath = "";
24265
- let startIndex = 0;
24266
- for (let i = 0; i < fromPathParts.length - 1; i++) {
24267
- if (fromPathParts[i] === toPathParts[i]) {
24268
- startIndex++;
24269
- }
24270
- else {
24271
- relPath += "../";
24272
- }
24273
- }
24274
- relPath += toPathParts.slice(startIndex).join("/");
24275
- return relPath;
24276
- }
24277
- /**
24278
- * Convert an array of element into an object where the objects keys were the elements position in the array.
24279
- * Can give an offset as argument, and all the array indexes will we shifted by this offset in the returned object.
24280
- *
24281
- * eg. : ["a", "b"] => {0:"a", 1:"b"}
24282
- */
24283
- function arrayToObject(array, indexOffset = 0) {
24284
- const obj = {};
24285
- for (let i = 0; i < array.length; i++) {
24286
- if (array[i]) {
24287
- obj[i + indexOffset] = array[i];
24288
- }
24289
- }
24290
- return obj;
24291
- }
24292
- /**
24293
- * In xlsx we can have string with unicode characters with the format _x00fa_.
24294
- * Replace with characters understandable by JS
24295
- */
24296
- function fixXlsxUnicode(str) {
24297
- return str.replace(/_x([0-9a-zA-Z]{4})_/g, (match, code) => {
24298
- return String.fromCharCode(parseInt(code, 16));
24299
- });
24300
- }
24301
- /** Get a header in the SheetData. Create the header if it doesn't exist in the SheetData */
24302
- function getSheetDataHeader(sheetData, dimension, index) {
24303
- if (dimension === "COL") {
24304
- if (!sheetData.cols[index]) {
24305
- sheetData.cols[index] = {};
24306
- }
24307
- return sheetData.cols[index];
24308
- }
24309
- if (!sheetData.rows[index]) {
24310
- sheetData.rows[index] = {};
24311
- }
24312
- return sheetData.rows[index];
24313
- }
24314
-
24315
24337
  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;
24316
24338
  /**
24317
24339
  * Convert excel format to o_spreadsheet format
@@ -24521,9 +24543,9 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
24521
24543
  if (!rule.operator || !rule.formula || rule.formula.length === 0)
24522
24544
  continue;
24523
24545
  operator = convertCFCellIsOperator(rule.operator);
24524
- values.push(prefixFormula(rule.formula[0]));
24546
+ values.push(prefixFormulaWithEqual(rule.formula[0]));
24525
24547
  if (rule.formula.length === 2) {
24526
- values.push(prefixFormula(rule.formula[1]));
24548
+ values.push(prefixFormulaWithEqual(rule.formula[1]));
24527
24549
  }
24528
24550
  break;
24529
24551
  }
@@ -24681,11 +24703,6 @@ function convertIcons(xlsxIconSet, index) {
24681
24703
  ? ICON_SETS[iconSet].neutral
24682
24704
  : ICON_SETS[iconSet].good;
24683
24705
  }
24684
- /** Prefix the string by "=" if the string looks like a formula */
24685
- function prefixFormula(formula) {
24686
- const tokens = tokenize(formula);
24687
- return tokens.length === 1 && tokens[0].type !== "REFERENCE" ? formula : "=" + formula;
24688
- }
24689
24706
  // ---------------------------------------------------------------------------
24690
24707
  // Warnings
24691
24708
  // ---------------------------------------------------------------------------
@@ -25106,7 +25123,7 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
25106
25123
  dvRules.push(decimalRule);
25107
25124
  break;
25108
25125
  case "list":
25109
- const listRule = convertListrule(dvId++, dv);
25126
+ const listRule = convertListRule(dvId++, dv);
25110
25127
  dvRules.push(listRule);
25111
25128
  break;
25112
25129
  case "date":
@@ -25126,9 +25143,9 @@ function convertDataValidationRules(xlsxDataValidations, warningManager) {
25126
25143
  return dvRules;
25127
25144
  }
25128
25145
  function convertDecimalRule(id, dv) {
25129
- const values = [dv.formula1.toString()];
25146
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
25130
25147
  if (dv.formula2) {
25131
- values.push(dv.formula2.toString());
25148
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
25132
25149
  }
25133
25150
  return {
25134
25151
  id: id.toString(),
@@ -25140,7 +25157,7 @@ function convertDecimalRule(id, dv) {
25140
25157
  },
25141
25158
  };
25142
25159
  }
25143
- function convertListrule(id, dv) {
25160
+ function convertListRule(id, dv) {
25144
25161
  const formula1 = dv.formula1.toString();
25145
25162
  const isRangeRule = rangeReference.test(formula1);
25146
25163
  return {
@@ -25156,9 +25173,9 @@ function convertListrule(id, dv) {
25156
25173
  }
25157
25174
  function convertDateRule(id, dv) {
25158
25175
  let criterion;
25159
- const values = [dv.formula1.toString()];
25176
+ const values = [prefixFormulaWithEqual(dv.formula1.toString())];
25160
25177
  if (dv.formula2) {
25161
- values.push(dv.formula2.toString());
25178
+ values.push(prefixFormulaWithEqual(dv.formula2.toString()));
25162
25179
  criterion = {
25163
25180
  type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
25164
25181
  values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
@@ -25185,7 +25202,7 @@ function convertCustomRule(id, dv) {
25185
25202
  isBlocking: dv.errorStyle !== "warning",
25186
25203
  criterion: {
25187
25204
  type: "customFormula",
25188
- values: [`=${dv.formula1.toString()}`],
25205
+ values: [prefixFormulaWithEqual(dv.formula1.toString())],
25189
25206
  },
25190
25207
  };
25191
25208
  }
@@ -28917,6 +28934,7 @@ function getChartTimeOptions(labels, labelFormat, locale) {
28917
28934
  parser: luxonFormat,
28918
28935
  displayFormats,
28919
28936
  unit: timeUnit ?? false,
28937
+ tooltipFormat: luxonFormat,
28920
28938
  };
28921
28939
  }
28922
28940
  /**
@@ -30095,6 +30113,7 @@ function getLineChartScales(definition, args) {
30095
30113
  };
30096
30114
  Object.assign(scales.x, axis);
30097
30115
  scales.x.ticks.maxTicksLimit = 15;
30116
+ delete scales?.x?.ticks?.callback;
30098
30117
  }
30099
30118
  else if (axisType === "linear") {
30100
30119
  scales.x.type = "linear";
@@ -44550,12 +44569,13 @@ class DataValidationEditor extends Component {
44550
44569
  onCloseSidePanel: { type: Function, optional: true },
44551
44570
  };
44552
44571
  state = useState({ rule: this.defaultDataValidationRule, errors: [] });
44572
+ editingSheetId;
44553
44573
  setup() {
44574
+ this.editingSheetId = this.env.model.getters.getActiveSheetId();
44554
44575
  if (this.props.rule) {
44555
- const sheetId = this.env.model.getters.getActiveSheetId();
44556
44576
  this.state.rule = {
44557
44577
  ...this.props.rule,
44558
- ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, sheetId)),
44578
+ ranges: this.props.rule.ranges.map((range) => this.env.model.getters.getRangeString(range, this.editingSheetId)),
44559
44579
  };
44560
44580
  this.state.rule.criterion.type = this.props.rule.criterion.type;
44561
44581
  }
@@ -44589,7 +44609,6 @@ class DataValidationEditor extends Component {
44589
44609
  const locale = this.env.model.getters.getLocale();
44590
44610
  const criterion = rule.criterion;
44591
44611
  const criterionEvaluator = dataValidationEvaluatorRegistry.get(criterion.type);
44592
- const sheetId = this.env.model.getters.getActiveSheetId();
44593
44612
  const values = criterion.values
44594
44613
  .slice(0, criterionEvaluator.numberOfValues(criterion))
44595
44614
  .map((value) => value?.trim())
@@ -44597,8 +44616,8 @@ class DataValidationEditor extends Component {
44597
44616
  .map((value) => canonicalizeContent(value, locale));
44598
44617
  rule.criterion = { ...criterion, values };
44599
44618
  return {
44600
- sheetId,
44601
- ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
44619
+ sheetId: this.editingSheetId,
44620
+ ranges: this.state.rule.ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(this.editingSheetId, xc)),
44602
44621
  rule,
44603
44622
  };
44604
44623
  }
@@ -45128,6 +45147,7 @@ css /* scss */ `
45128
45147
  .o-button {
45129
45148
  height: 19px;
45130
45149
  width: 19px;
45150
+ box-sizing: content-box;
45131
45151
  .o-icon {
45132
45152
  height: 14px;
45133
45153
  width: 14px;
@@ -61137,7 +61157,7 @@ class FormulaDependencyGraph {
61137
61157
  * in the correct order they should be evaluated.
61138
61158
  * This is called a topological ordering (excluding cycles)
61139
61159
  */
61140
- getCellsDependingOn(ranges) {
61160
+ getCellsDependingOn(ranges, ignore) {
61141
61161
  const visited = this.createEmptyPositionSet();
61142
61162
  const queue = Array.from(ranges).reverse();
61143
61163
  while (queue.length > 0) {
@@ -61152,7 +61172,7 @@ class FormulaDependencyGraph {
61152
61172
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
61153
61173
  const nextInQueue = {};
61154
61174
  for (const position of impactedPositions) {
61155
- if (!visited.has(position)) {
61175
+ if (!visited.has(position) && !ignore.has(position)) {
61156
61176
  if (!nextInQueue[position.sheetId]) {
61157
61177
  nextInQueue[position.sheetId] = [];
61158
61178
  }
@@ -61709,7 +61729,7 @@ class Evaluator {
61709
61729
  }
61710
61730
  invalidatePositionsDependingOnSpread(sheetId, resultZone) {
61711
61731
  // the result matrix is split in 2 zones to exclude the array formula position
61712
- const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
61732
+ const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })), this.nextPositionsToUpdate);
61713
61733
  invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
61714
61734
  this.nextPositionsToUpdate.addMany(invalidatedPositions);
61715
61735
  }
@@ -61827,7 +61847,7 @@ class Evaluator {
61827
61847
  for (const sheetId in zonesBySheetIds) {
61828
61848
  ranges.push(...zonesBySheetIds[sheetId].map((zone) => ({ sheetId, zone })));
61829
61849
  }
61830
- return this.formulaDependencies().getCellsDependingOn(ranges);
61850
+ return this.formulaDependencies().getCellsDependingOn(ranges, this.nextPositionsToUpdate);
61831
61851
  }
61832
61852
  }
61833
61853
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
@@ -77306,6 +77326,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
77306
77326
  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, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
77307
77327
 
77308
77328
 
77309
- __info__.version = "18.2.32";
77310
- __info__.date = "2025-10-07T09:59:52.165Z";
77311
- __info__.hash = "a42c448";
77329
+ __info__.version = "18.2.33";
77330
+ __info__.date = "2025-10-16T06:39:40.421Z";
77331
+ __info__.hash = "280596c";