@odoo/o-spreadsheet 18.5.0-alpha.2 → 18.5.0-alpha.3

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.5.0-alpha.2
6
- * @date 2025-07-11T11:13:53.317Z
7
- * @hash 6d42178
5
+ * @version 18.5.0-alpha.3
6
+ * @date 2025-07-28T13:43:05.981Z
7
+ * @hash 53dfee8
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -2902,6 +2902,7 @@
2902
2902
  "isEmpty",
2903
2903
  "isNotEqual",
2904
2904
  "isEqual",
2905
+ "customFormula",
2905
2906
  ]);
2906
2907
 
2907
2908
  const availableDataValidationOperators = new Set([
@@ -5034,11 +5035,11 @@
5034
5035
  }
5035
5036
  }
5036
5037
 
5037
- function evaluateLiteral(literalCell, localeFormat) {
5038
+ function evaluateLiteral(literalCell, localeFormat, position) {
5038
5039
  const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5039
5040
  ? literalCell.content
5040
5041
  : literalCell.parsedValue;
5041
- const functionResult = { value, format: localeFormat.format };
5042
+ const functionResult = { value, format: localeFormat.format, origin: position };
5042
5043
  return createEvaluatedCell(functionResult, localeFormat.locale);
5043
5044
  }
5044
5045
  function parseLiteral(content, locale) {
@@ -5060,10 +5061,11 @@
5060
5061
  }
5061
5062
  return content;
5062
5063
  }
5063
- function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
5064
+ function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell, origin) {
5064
5065
  const link = detectLink(functionResult.value);
5065
5066
  if (!link) {
5066
- return _createEvaluatedCell(functionResult, locale, cell);
5067
+ const evaluateCell = _createEvaluatedCell(functionResult, locale, cell);
5068
+ return addOrigin(evaluateCell, functionResult.origin ?? origin);
5067
5069
  }
5068
5070
  const value = parseLiteral(link.label, locale);
5069
5071
  const format = functionResult.format ||
@@ -5074,10 +5076,10 @@
5074
5076
  value,
5075
5077
  format,
5076
5078
  };
5077
- return {
5079
+ return addOrigin({
5078
5080
  ..._createEvaluatedCell(linkPayload, locale, cell),
5079
5081
  link,
5080
- };
5082
+ }, functionResult.origin ?? origin);
5081
5083
  }
5082
5084
  function _createEvaluatedCell(functionResult, locale, cell) {
5083
5085
  let { value, format, message } = functionResult;
@@ -5167,6 +5169,17 @@
5167
5169
  defaultAlign: "center",
5168
5170
  };
5169
5171
  }
5172
+ function addOrigin(cell, origin) {
5173
+ if (cell.value === null) {
5174
+ // ignore empty cells to allow sharing the same object instance
5175
+ return cell;
5176
+ }
5177
+ if ("origin" in cell) {
5178
+ return cell;
5179
+ }
5180
+ cell.origin = origin;
5181
+ return cell;
5182
+ }
5170
5183
 
5171
5184
  function toCriterionDateNumber(dateValue) {
5172
5185
  const today = DateTime.now();
@@ -7847,6 +7860,7 @@
7847
7860
  case "isGreaterOrEqualTo":
7848
7861
  case "isLessThan":
7849
7862
  case "isLessOrEqualTo":
7863
+ case "customFormula":
7850
7864
  rule.values = rule.values.map((v) => changeContentLocale(v));
7851
7865
  return rule;
7852
7866
  case "beginsWithText":
@@ -10535,6 +10549,7 @@ stores.inject(MyMetaStore, storeInstance);
10535
10549
  /** The possible values for the XLSX polynomial trendline order are defined by the ST_Order simple type (§21.2.3.29) */
10536
10550
  const MAX_XLSX_POLYNOMIAL_DEGREE = 6;
10537
10551
  const FIRST_NUMFMT_ID = 164;
10552
+ const DEFAULT_DOUGHNUT_CHART_HOLE_SIZE = 50;
10538
10553
  const FORCE_DEFAULT_ARGS_FUNCTIONS = {
10539
10554
  FLOOR: [{ type: "NUMBER", value: 1 }],
10540
10555
  CEILING: [{ type: "NUMBER", value: 1 }],
@@ -19034,13 +19049,19 @@ stores.inject(MyMetaStore, storeInstance);
19034
19049
  if (isEvaluationError(cellReference?.value)) {
19035
19050
  return cellReference;
19036
19051
  }
19037
- const column = cellReference === undefined
19038
- ? this.__originCellPosition?.col
19039
- : toZone(cellReference.value).left;
19040
- if (column === undefined) {
19041
- return new EvaluationError(_t("In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter."));
19052
+ if (cellReference === undefined) {
19053
+ if (this.__originCellPosition?.col === undefined) {
19054
+ return new EvaluationError(_t("In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter."));
19055
+ }
19056
+ return this.__originCellPosition.col + 1;
19057
+ }
19058
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
19059
+ if (zone.left === zone.right) {
19060
+ return zone.left + 1;
19042
19061
  }
19043
- return column + 1;
19062
+ return generateMatrix(zone.right - zone.left + 1, 1, (col, row) => ({
19063
+ value: zone.left + col + 1,
19064
+ }));
19044
19065
  },
19045
19066
  isExported: true,
19046
19067
  };
@@ -19271,13 +19292,19 @@ stores.inject(MyMetaStore, storeInstance);
19271
19292
  if (isEvaluationError(cellReference?.value)) {
19272
19293
  return cellReference;
19273
19294
  }
19274
- const row = cellReference === undefined
19275
- ? this.__originCellPosition?.row
19276
- : toZone(cellReference.value).top;
19277
- if (row === undefined) {
19278
- return new EvaluationError(_t("In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter."));
19295
+ if (cellReference === undefined) {
19296
+ if (this.__originCellPosition?.row === undefined) {
19297
+ return new EvaluationError(_t("In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter."));
19298
+ }
19299
+ return this.__originCellPosition.row + 1;
19300
+ }
19301
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
19302
+ if (zone.top === zone.bottom) {
19303
+ return zone.top + 1;
19279
19304
  }
19280
- return row + 1;
19305
+ return generateMatrix(1, zone.bottom - zone.top + 1, (col, row) => ({
19306
+ value: zone.top + row + 1,
19307
+ }));
19281
19308
  },
19282
19309
  isExported: true,
19283
19310
  };
@@ -22854,6 +22881,16 @@ stores.inject(MyMetaStore, storeInstance);
22854
22881
  static getDefinitionFromContextCreation(context) {
22855
22882
  throw new Error("This method should be implemented by sub class");
22856
22883
  }
22884
+ getCommonDataSetAttributesForExcel(labelRange, dataSets, shouldRemoveFirstLabel) {
22885
+ const excelDataSets = dataSets
22886
+ .map((ds) => toExcelDataset(this.getters, ds))
22887
+ .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
22888
+ const excelLabelRange = toExcelLabelRange(this.getters, labelRange, shouldRemoveFirstLabel);
22889
+ return {
22890
+ dataSets: excelDataSets,
22891
+ labelRange: excelLabelRange,
22892
+ };
22893
+ }
22857
22894
  }
22858
22895
 
22859
22896
  function getBaselineText(baseline, keyValue, baselineMode, humanize, locale) {
@@ -26336,6 +26373,7 @@ stores.inject(MyMetaStore, storeInstance);
26336
26373
  labelRange: context.auxiliaryRange || undefined,
26337
26374
  axesDesign: context.axesDesign,
26338
26375
  showValues: context.showValues,
26376
+ horizontal: context.horizontal,
26339
26377
  };
26340
26378
  }
26341
26379
  getContextCreation() {
@@ -26393,10 +26431,7 @@ stores.inject(MyMetaStore, storeInstance);
26393
26431
  };
26394
26432
  }
26395
26433
  getDefinitionForExcel() {
26396
- const dataSets = this.dataSets
26397
- .map((ds) => toExcelDataset(this.getters, ds))
26398
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
26399
- const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
26434
+ const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
26400
26435
  const definition = this.getDefinition();
26401
26436
  return {
26402
26437
  ...definition,
@@ -26984,10 +27019,7 @@ stores.inject(MyMetaStore, storeInstance);
26984
27019
  if (this.aggregated) {
26985
27020
  return undefined;
26986
27021
  }
26987
- const dataSets = this.dataSets
26988
- .map((ds) => toExcelDataset(this.getters, ds))
26989
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
26990
- const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
27022
+ const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
26991
27023
  const definition = this.getDefinition();
26992
27024
  return {
26993
27025
  ...definition,
@@ -27726,10 +27758,7 @@ stores.inject(MyMetaStore, storeInstance);
27726
27758
  return new LineChart(definition, this.sheetId, this.getters);
27727
27759
  }
27728
27760
  getDefinitionForExcel() {
27729
- const dataSets = this.dataSets
27730
- .map((ds) => toExcelDataset(this.getters, ds))
27731
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
27732
- const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
27761
+ const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
27733
27762
  const definition = this.getDefinition();
27734
27763
  return {
27735
27764
  ...definition,
@@ -27817,7 +27846,8 @@ stores.inject(MyMetaStore, storeInstance);
27817
27846
  type: "pie",
27818
27847
  labelRange: context.auxiliaryRange || undefined,
27819
27848
  aggregated: context.aggregated ?? false,
27820
- isDoughnut: false,
27849
+ isDoughnut: context.isDoughnut,
27850
+ pieHolePercentage: context.pieHolePercentage,
27821
27851
  showValues: context.showValues,
27822
27852
  };
27823
27853
  }
@@ -27865,10 +27895,7 @@ stores.inject(MyMetaStore, storeInstance);
27865
27895
  return new PieChart(definition, sheetId, this.getters);
27866
27896
  }
27867
27897
  getDefinitionForExcel() {
27868
- const dataSets = this.dataSets
27869
- .map((ds) => toExcelDataset(this.getters, ds))
27870
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
27871
- const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
27898
+ const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
27872
27899
  return {
27873
27900
  ...this.getDefinition(),
27874
27901
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
@@ -28013,8 +28040,22 @@ stores.inject(MyMetaStore, storeInstance);
28013
28040
  showValues: this.showValues,
28014
28041
  };
28015
28042
  }
28016
- getDefinitionForExcel() {
28017
- return undefined;
28043
+ getDefinitionForExcel(getters) {
28044
+ const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
28045
+ const definition = this.getDefinition();
28046
+ const chartData = getPyramidChartData(definition, this.dataSets, this.labelRange, getters);
28047
+ const { dataSetsValues } = chartData;
28048
+ const maxValue = Math.max(...dataSetsValues.map((dataSet) => Math.max(...dataSet.data.map(Math.abs))));
28049
+ return {
28050
+ ...definition,
28051
+ horizontal: true,
28052
+ backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
28053
+ fontColor: toXlsxHexColor(chartFontColor(this.background)),
28054
+ dataSets,
28055
+ labelRange,
28056
+ verticalAxis: getDefinedAxis(definition),
28057
+ maxValue,
28058
+ };
28018
28059
  }
28019
28060
  updateRanges(applyChange) {
28020
28061
  const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
@@ -28157,10 +28198,7 @@ stores.inject(MyMetaStore, storeInstance);
28157
28198
  if (this.aggregated) {
28158
28199
  return undefined;
28159
28200
  }
28160
- const dataSets = this.dataSets
28161
- .map((ds) => toExcelDataset(this.getters, ds))
28162
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
28163
- const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
28201
+ const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
28164
28202
  const definition = this.getDefinition();
28165
28203
  return {
28166
28204
  ...definition,
@@ -32607,6 +32645,9 @@ stores.inject(MyMetaStore, storeInstance);
32607
32645
  get isAutoCompleteDisplayed() {
32608
32646
  return !!this.autoComplete.provider;
32609
32647
  }
32648
+ get canBeToggled() {
32649
+ return this.autoComplete.provider?.canBeToggled ?? true;
32650
+ }
32610
32651
  cycleReferences() {
32611
32652
  const locale = this.getters.getLocale();
32612
32653
  const updated = cycleFixedReference(this.composerSelection, this._currentContent, locale);
@@ -33136,6 +33177,7 @@ stores.inject(MyMetaStore, storeInstance);
33136
33177
  proposals,
33137
33178
  selectProposal: provider.selectProposal,
33138
33179
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
33180
+ canBeToggled: provider.canBeToggled,
33139
33181
  };
33140
33182
  }
33141
33183
  if (exactMatch && this._currentContent !== this.initialContent) {
@@ -33158,6 +33200,7 @@ stores.inject(MyMetaStore, storeInstance);
33158
33200
  proposals,
33159
33201
  selectProposal: provider.selectProposal,
33160
33202
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
33203
+ canBeToggled: provider.canBeToggled,
33161
33204
  };
33162
33205
  }
33163
33206
  }
@@ -33728,9 +33771,13 @@ stores.inject(MyMetaStore, storeInstance);
33728
33771
  }
33729
33772
  }
33730
33773
  closeAssistant() {
33774
+ if (!this.props.composerStore.canBeToggled)
33775
+ return;
33731
33776
  this.assistant.forcedClosed = true;
33732
33777
  }
33733
33778
  openAssistant() {
33779
+ if (!this.props.composerStore.canBeToggled)
33780
+ return;
33734
33781
  this.assistant.forcedClosed = false;
33735
33782
  }
33736
33783
  onWheel(event) {
@@ -33920,7 +33967,7 @@ stores.inject(MyMetaStore, storeInstance);
33920
33967
  return [...new Set(argsToFocus)];
33921
33968
  }
33922
33969
  autoComplete(value) {
33923
- if (!value || this.assistant.forcedClosed) {
33970
+ if (!value || (this.assistant.forcedClosed && this.props.composerStore.canBeToggled)) {
33924
33971
  return;
33925
33972
  }
33926
33973
  this.props.composerStore.insertAutoCompleteValue(value);
@@ -37498,7 +37545,7 @@ stores.inject(MyMetaStore, storeInstance);
37498
37545
  /** Conversion map CF types in XLSX <=> Cf types in o_spreadsheet */
37499
37546
  const CF_TYPE_CONVERSION_MAP = {
37500
37547
  aboveAverage: undefined,
37501
- expression: undefined,
37548
+ expression: "customFormula",
37502
37549
  cellIs: undefined, // exist but isn't an operator in o_spreadsheet
37503
37550
  colorScale: undefined, // exist but isn't an operator in o_spreadsheet
37504
37551
  dataBar: undefined,
@@ -38102,7 +38149,6 @@ stores.inject(MyMetaStore, storeInstance);
38102
38149
  case "containsErrors":
38103
38150
  case "notContainsErrors":
38104
38151
  case "duplicateValues":
38105
- case "expression":
38106
38152
  case "top10":
38107
38153
  case "uniqueValues":
38108
38154
  case "timePeriod":
@@ -38135,6 +38181,12 @@ stores.inject(MyMetaStore, storeInstance);
38135
38181
  operator = CF_TYPE_CONVERSION_MAP[rule.type];
38136
38182
  values.push(rule.text);
38137
38183
  break;
38184
+ case "expression":
38185
+ if (!rule.formula?.length)
38186
+ continue;
38187
+ operator = CF_TYPE_CONVERSION_MAP[rule.type];
38188
+ values.push(`=${rule.formula[0]}`);
38189
+ break;
38138
38190
  case "containsBlanks":
38139
38191
  case "notContainsBlanks":
38140
38192
  operator = CF_TYPE_CONVERSION_MAP[rule.type];
@@ -38364,6 +38416,8 @@ stores.inject(MyMetaStore, storeInstance);
38364
38416
  return "equal";
38365
38417
  case "isNotEqual":
38366
38418
  return "notEqual";
38419
+ case "customFormula":
38420
+ return "";
38367
38421
  }
38368
38422
  }
38369
38423
  // -------------------------------------
@@ -38699,6 +38753,9 @@ stores.inject(MyMetaStore, storeInstance);
38699
38753
  aggregated: false,
38700
38754
  cumulative: chartData.cumulative || false,
38701
38755
  labelsAsText: false,
38756
+ horizontal: chartData.horizontal,
38757
+ isDoughnut: chartData.isDoughnut,
38758
+ pieHolePercentage: chartData.pieHolePercentage,
38702
38759
  };
38703
38760
  try {
38704
38761
  const ChartClass = chartRegistry.get(chartData.type);
@@ -39975,6 +40032,12 @@ stores.inject(MyMetaStore, storeInstance);
39975
40032
  const barChartGrouping = this.extractChildAttr(rootChartElement, "c:grouping", "val", {
39976
40033
  default: "clustered",
39977
40034
  }).asString();
40035
+ const chartDirection = this.extractChildAttr(rootChartElement, "c:barDir", "val", {
40036
+ default: "col",
40037
+ }).asString();
40038
+ const chartHoleSize = this.extractChildAttr(rootChartElement, "c:holeSize", "val", {
40039
+ default: "0",
40040
+ }).asNum();
39978
40041
  return {
39979
40042
  title: { text: chartTitle },
39980
40043
  type: CHART_TYPE_CONVERSION_MAP[chartType],
@@ -39988,6 +40051,9 @@ stores.inject(MyMetaStore, storeInstance);
39988
40051
  }).asString()],
39989
40052
  stacked: barChartGrouping === "stacked",
39990
40053
  fontColor: "000000",
40054
+ horizontal: chartDirection === "bar",
40055
+ isDoughnut: chartHoleSize > 0,
40056
+ pieHolePercentage: chartHoleSize,
39991
40057
  };
39992
40058
  })[0];
39993
40059
  }
@@ -50978,6 +51044,9 @@ stores.inject(MyMetaStore, storeInstance);
50978
51044
  pieHolePercentage,
50979
51045
  });
50980
51046
  }
51047
+ get defaultHoleSize() {
51048
+ return DEFAULT_DOUGHNUT_CHART_HOLE_SIZE;
51049
+ }
50981
51050
  }
50982
51051
 
50983
51052
  class RadarChartDesignPanel extends owl.Component {
@@ -55701,9 +55770,15 @@ stores.inject(MyMetaStore, storeInstance);
55701
55770
  return domain.reduce((current, acc) => this.filterDataEntriesFromDomainNode(current, acc), dataEntries);
55702
55771
  }
55703
55772
  filterDataEntriesFromDomainNode(dataEntries, domain) {
55704
- const { field, value } = domain;
55773
+ const { field, value, type } = domain;
55705
55774
  const { nameWithGranularity } = this.getDimension(field);
55706
- return dataEntries.filter((entry) => entry[nameWithGranularity]?.value === value);
55775
+ return dataEntries.filter((entry) => {
55776
+ const cellValue = entry[nameWithGranularity]?.value;
55777
+ if (type === "char") {
55778
+ return String(cellValue) === String(value);
55779
+ }
55780
+ return cellValue === value;
55781
+ });
55707
55782
  }
55708
55783
  getDimension(nameWithGranularity) {
55709
55784
  return this.definition.getDimension(nameWithGranularity);
@@ -65655,7 +65730,7 @@ stores.inject(MyMetaStore, storeInstance);
65655
65730
  const EMPTY_ARRAY = [];
65656
65731
 
65657
65732
  const MAX_ITERATION = 30;
65658
- const ERROR_CYCLE_CELL = Object.freeze(createEvaluatedCell(new CircularDependencyError()));
65733
+ const ERROR_CYCLE_CELL = Object.freeze(createEvaluatedCell({ ...new CircularDependencyError(), origin: undefined }));
65659
65734
  const EMPTY_CELL = Object.freeze(createEvaluatedCell({ value: null }));
65660
65735
  class Evaluator {
65661
65736
  context;
@@ -65889,11 +65964,12 @@ stores.inject(MyMetaStore, storeInstance);
65889
65964
  this.cellsBeingComputed.add(cellId);
65890
65965
  return cell.isFormula
65891
65966
  ? this.computeFormulaCell(position, cell)
65892
- : evaluateLiteral(cell, localeFormat);
65967
+ : evaluateLiteral(cell, localeFormat, position);
65893
65968
  }
65894
65969
  catch (e) {
65895
65970
  e.value = e?.value || CellErrorType.GenericError;
65896
65971
  e.message = e?.message || implementationErrorMessage;
65972
+ e.origin = position;
65897
65973
  return createEvaluatedCell(e);
65898
65974
  }
65899
65975
  finally {
@@ -65910,7 +65986,7 @@ stores.inject(MyMetaStore, storeInstance);
65910
65986
  computeFormulaCell(formulaPosition, cellData) {
65911
65987
  const formulaReturn = updateEvalContextAndExecute(cellData.compiledFormula, this.compilationParams, formulaPosition.sheetId, this.buildSafeGetSymbolValue(), formulaPosition);
65912
65988
  if (!isMatrix(formulaReturn)) {
65913
- const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData);
65989
+ const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData, formulaPosition);
65914
65990
  if (evaluatedCell.type === CellValueType.error) {
65915
65991
  evaluatedCell.errorOriginPosition = formulaReturn.errorOriginPosition ?? formulaPosition;
65916
65992
  }
@@ -65986,7 +66062,7 @@ stores.inject(MyMetaStore, storeInstance);
65986
66062
  const spreadValues = (i, j) => {
65987
66063
  const position = { sheetId, col: i + col, row: j + row };
65988
66064
  const cell = this.getters.getCell(position);
65989
- const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
66065
+ const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell, position);
65990
66066
  if (evaluatedCell.type === CellValueType.error) {
65991
66067
  evaluatedCell.errorOriginPosition = matrixResult[i][j].errorOriginPosition ?? position;
65992
66068
  }
@@ -66659,7 +66735,7 @@ stores.inject(MyMetaStore, storeInstance);
66659
66735
  continue;
66660
66736
  }
66661
66737
  const figureId = figure.id;
66662
- const figureData = this.getters.getChart(figureId)?.getDefinitionForExcel();
66738
+ const figureData = this.getters.getChart(figureId)?.getDefinitionForExcel(this.getters);
66663
66739
  if (figureData) {
66664
66740
  figures.push({
66665
66741
  ...figure,
@@ -67572,6 +67648,23 @@ stores.inject(MyMetaStore, storeInstance);
67572
67648
  static getters = ["getRowSize", "getHeaderSize", "getMaxAnchorOffset"];
67573
67649
  tallestCellInRow = {};
67574
67650
  ctx = document.createElement("canvas").getContext("2d");
67651
+ beforeHandle(cmd) {
67652
+ switch (cmd.type) {
67653
+ // Ensure rows are updated before "UPDATE_CELL" is dispatched from cell plugin.
67654
+ // "UPDATE_CELL" uses the Sheet core plugin to access row data.
67655
+ // If "ADD_COLUMNS_ROWS" has not been processed yet by header_sizes_ui,
67656
+ // size updates may apply to incorrect (pre-insert) rows.
67657
+ case "ADD_COLUMNS_ROWS":
67658
+ if (cmd.dimension === "COL") {
67659
+ return;
67660
+ }
67661
+ const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
67662
+ const newCells = Array(cmd.quantity).fill(undefined);
67663
+ const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
67664
+ this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
67665
+ break;
67666
+ }
67667
+ }
67575
67668
  handle(cmd) {
67576
67669
  switch (cmd.type) {
67577
67670
  case "START":
@@ -67601,16 +67694,6 @@ stores.inject(MyMetaStore, storeInstance);
67601
67694
  this.history.update("tallestCellInRow", cmd.sheetId, tallestCells);
67602
67695
  break;
67603
67696
  }
67604
- case "ADD_COLUMNS_ROWS": {
67605
- if (cmd.dimension === "COL") {
67606
- return;
67607
- }
67608
- const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
67609
- const newCells = Array(cmd.quantity).fill(undefined);
67610
- const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
67611
- this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
67612
- break;
67613
- }
67614
67697
  case "RESIZE_COLUMNS_ROWS":
67615
67698
  {
67616
67699
  const sheetId = cmd.sheetId;
@@ -74159,6 +74242,14 @@ stores.inject(MyMetaStore, storeInstance);
74159
74242
  const isBasedBefore = cmd.base < start;
74160
74243
  const deltaCol = isBasedBefore && isCol ? thickness : 0;
74161
74244
  const deltaRow = isBasedBefore && !isCol ? thickness : 0;
74245
+ const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
74246
+ const originalSize = Object.fromEntries(toRemove.map((element) => {
74247
+ const size = isCol
74248
+ ? this.getters.getColSize(cmd.sheetId, element)
74249
+ : this.getters.getUserRowSize(cmd.sheetId, element);
74250
+ const isDefaultCol = isCol && size === DEFAULT_CELL_WIDTH;
74251
+ return [element, isDefaultCol ? undefined : size];
74252
+ }));
74162
74253
  const target = [
74163
74254
  {
74164
74255
  left: isCol ? start + deltaCol : 0,
@@ -74189,13 +74280,12 @@ stores.inject(MyMetaStore, storeInstance);
74189
74280
  const col = selection.left;
74190
74281
  const row = selection.top;
74191
74282
  this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
74192
- const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
74193
74283
  let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
74194
74284
  const resizingGroups = {};
74195
74285
  for (const element of toRemove) {
74196
- const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
74286
+ const size = originalSize[element];
74197
74287
  const currentSize = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, currentIndex);
74198
- if (size !== currentSize) {
74288
+ if (size && size !== currentSize) {
74199
74289
  resizingGroups[size] ??= [];
74200
74290
  resizingGroups[size].push(currentIndex);
74201
74291
  currentIndex += 1;
@@ -75621,6 +75711,7 @@ stores.inject(MyMetaStore, storeInstance);
75621
75711
 
75622
75712
  autoCompleteProviders.add("dataValidation", {
75623
75713
  displayAllOnInitialContent: true,
75714
+ canBeToggled: false,
75624
75715
  getProposals(tokenAtCursor, content) {
75625
75716
  if (isFormula(content)) {
75626
75717
  return [];
@@ -77186,14 +77277,12 @@ stores.inject(MyMetaStore, storeInstance);
77186
77277
  this.editionState = "initializing";
77187
77278
  }
77188
77279
  stopEdition() {
77189
- const input = this.sheetNameRef.el;
77190
- if (!this.state.isEditing || !input)
77280
+ if (!this.state.isEditing || !this.sheetNameRef.el)
77191
77281
  return;
77192
77282
  this.state.isEditing = false;
77193
77283
  this.editionState = "initializing";
77194
- input.blur();
77284
+ this.sheetNameRef.el.blur();
77195
77285
  const inputValue = this.getInputContent() || "";
77196
- input.innerText = inputValue;
77197
77286
  interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
77198
77287
  }
77199
77288
  cancelEdition() {
@@ -81611,6 +81700,9 @@ stores.inject(MyMetaStore, storeInstance);
81611
81700
  case "combo":
81612
81701
  plot = addComboChart(chart.data);
81613
81702
  break;
81703
+ case "pyramid":
81704
+ plot = addPyramidChart(chart.data);
81705
+ break;
81614
81706
  case "line":
81615
81707
  plot = addLineChart(chart.data);
81616
81708
  break;
@@ -81618,12 +81710,12 @@ stores.inject(MyMetaStore, storeInstance);
81618
81710
  plot = addScatterChart(chart.data);
81619
81711
  break;
81620
81712
  case "pie":
81621
- plot = addDoughnutChart(chart.data, chartSheetIndex, data, { holeSize: 0 });
81713
+ plot = addDoughnutChart(chart.data, chartSheetIndex, data);
81622
81714
  break;
81623
81715
  case "radar":
81624
81716
  plot = addRadarChart(chart.data);
81625
81717
  }
81626
- let position = "t";
81718
+ let position = "none";
81627
81719
  switch (chart.data.legendPosition) {
81628
81720
  case "bottom":
81629
81721
  position = "b";
@@ -81653,7 +81745,7 @@ stores.inject(MyMetaStore, storeInstance);
81653
81745
  ${plot}
81654
81746
  ${shapeProperty({ backgroundColor: chart.data.backgroundColor })}
81655
81747
  </c:plotArea>
81656
- ${addLegend(position, fontColor)}
81748
+ ${position !== "none" ? addLegend(position, fontColor) : ""}
81657
81749
  </c:chart>
81658
81750
  </c:chartSpace>
81659
81751
  `;
@@ -81811,6 +81903,7 @@ stores.inject(MyMetaStore, storeInstance);
81811
81903
  //
81812
81904
  // overlap and gapWitdh seems to be by default at -20 and 20 in chart.js.
81813
81905
  // See https://www.chartjs.org/docs/latest/charts/bar.html and https://www.chartjs.org/docs/latest/charts/bar.html#barpercentage-vs-categorypercentage
81906
+ const chartDirection = chart.horizontal ? "bar" : "col";
81814
81907
  const dataSetsColors = chart.dataSets.map((ds) => ds.backgroundColor ?? "");
81815
81908
  const colors = new ColorGenerator(chart.dataSets.length, dataSetsColors);
81816
81909
  const leftDataSetsNodes = [];
@@ -81847,7 +81940,7 @@ stores.inject(MyMetaStore, storeInstance);
81847
81940
  ${leftDataSetsNodes.length
81848
81941
  ? escapeXml /*xml*/ `
81849
81942
  <c:barChart>
81850
- <c:barDir val="col"/>
81943
+ <c:barDir val="${chartDirection}"/>
81851
81944
  <c:grouping val="${grouping}"/>
81852
81945
  <c:overlap val="${overlap}"/>
81853
81946
  <c:gapWidth val="70"/>
@@ -81857,8 +81950,12 @@ stores.inject(MyMetaStore, storeInstance);
81857
81950
  <c:axId val="${catAxId}" />
81858
81951
  <c:axId val="${valAxId}" />
81859
81952
  </c:barChart>
81860
- ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor)}
81861
- ${addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)}
81953
+ ${chartDirection === "col"
81954
+ ? addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor)
81955
+ : addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.y?.title, chart.fontColor, undefined, "maxMin")}
81956
+ ${chartDirection === "col"
81957
+ ? addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)
81958
+ : addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.x?.title, chart.fontColor, undefined, undefined, "max")}
81862
81959
  `
81863
81960
  : ""}
81864
81961
  ${rightDataSetsNodes.length
@@ -81984,7 +82081,7 @@ stores.inject(MyMetaStore, storeInstance);
81984
82081
  : ""}
81985
82082
  ${!useRightAxisForBarSerie || leftDataSetsNodes.length
81986
82083
  ? escapeXml /*xml*/ `
81987
- ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor, leftDataSetsNodes.length ? 1 : 0)}
82084
+ ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor, 0)}
81988
82085
  ${addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)}
81989
82086
  `
81990
82087
  : ""}
@@ -81996,6 +82093,94 @@ stores.inject(MyMetaStore, storeInstance);
81996
82093
  : ""}
81997
82094
  `;
81998
82095
  }
82096
+ function addPyramidChart(chart) {
82097
+ const dataSets = chart.dataSets;
82098
+ const dataSetsColors = dataSets.map((ds) => ds.backgroundColor ?? "");
82099
+ const colors = new ColorGenerator(dataSets.length, dataSetsColors);
82100
+ const leftDataSet = dataSets[0];
82101
+ const rightDataSet = dataSets[1];
82102
+ const firstColor = toXlsxHexColor(colors.next());
82103
+ const secondColor = toXlsxHexColor(colors.next());
82104
+ const { maxValue, majorUnit } = getPyramidChartHorizontalAxisConfig(chart.maxValue);
82105
+ const labelRangeEl = chart.labelRange
82106
+ ? escapeXml `<c:cat>${stringRef(chart.labelRange)}</c:cat>`
82107
+ : "";
82108
+ const leftBarDataSetNode = escapeXml /*xml*/ `
82109
+ <c:ser>
82110
+ <c:idx val="0"/>
82111
+ <c:order val="0"/>
82112
+ <c:invertIfNegative val="0" />
82113
+ ${extractDataSetLabel(leftDataSet.label)}
82114
+ ${shapeProperty({
82115
+ backgroundColor: firstColor,
82116
+ line: { color: firstColor },
82117
+ })}
82118
+ ${labelRangeEl}
82119
+ <!-- x-coordinate values -->
82120
+ <c:val>
82121
+ ${numberRef(leftDataSet.range)}
82122
+ </c:val>
82123
+ </c:ser>
82124
+ `;
82125
+ const rightBarDataSetNode = escapeXml /*xml*/ `
82126
+ <c:ser>
82127
+ <c:idx val="1"/>
82128
+ <c:order val="1"/>
82129
+ <c:invertIfNegative val="0" />
82130
+ ${extractDataSetLabel(rightDataSet.label)}
82131
+ ${shapeProperty({
82132
+ backgroundColor: secondColor,
82133
+ line: { color: secondColor },
82134
+ })}
82135
+ ${chart.labelRange ? escapeXml /*xml*/ `<c:cat>${stringRef(chart.labelRange)}</c:cat>` : ""}
82136
+ <!-- x-coordinate values -->
82137
+ <c:val>
82138
+ ${numberRef(rightDataSet.range)}
82139
+ </c:val>
82140
+ </c:ser>
82141
+ `;
82142
+ return escapeXml /*xml*/ `
82143
+ <c:barChart>
82144
+ <c:barDir val="bar"/>
82145
+ <c:grouping val="clustered"/>
82146
+ <c:varyColors val="0" />
82147
+ ${leftBarDataSetNode}
82148
+ <c:gapWidth val="50" />
82149
+ <c:axId val="${catAxId}" />
82150
+ <c:axId val="${valAxId}" />
82151
+ </c:barChart>
82152
+ <c:barChart>
82153
+ <c:barDir val="bar"/>
82154
+ <c:grouping val="clustered"/>
82155
+ <c:varyColors val="0" />
82156
+ ${rightBarDataSetNode}
82157
+ <c:gapWidth val="50" />
82158
+ <c:axId val="${secondaryCatAxId}" />
82159
+ <c:axId val="${secondaryValAxId}" />
82160
+ </c:barChart>
82161
+ ${addAx("r", "c:catAx", catAxId, valAxId, chart.axesDesign?.y?.title, chart.fontColor, 0, "maxMin", "autoZero", "high")}
82162
+ ${addAx("b", "c:valAx", valAxId, catAxId, chart.axesDesign?.x?.title, chart.fontColor, 0, "maxMin", "max", "nextTo", maxValue, majorUnit, "#0;#0")}
82163
+ ${addAx("t", "c:valAx", secondaryValAxId, secondaryCatAxId, undefined, chart.fontColor, 1)}
82164
+ ${addAx("l", "c:catAx", secondaryCatAxId, secondaryValAxId, undefined, chart.fontColor, 1, "maxMin")}
82165
+ `;
82166
+ }
82167
+ function getPyramidChartHorizontalAxisConfig(maxValue) {
82168
+ const adjustMaxToDivisibleBy = (value, divisor) => {
82169
+ let adjusted = Math.ceil(value);
82170
+ while (adjusted % divisor !== 0) {
82171
+ adjusted++;
82172
+ }
82173
+ return adjusted;
82174
+ };
82175
+ const tickCount = 4;
82176
+ const interval = tickCount - 1;
82177
+ const adjustedMax = adjustMaxToDivisibleBy(maxValue, interval);
82178
+ const majorUnit = adjustedMax / interval;
82179
+ return {
82180
+ maxValue: adjustedMax,
82181
+ majorUnit,
82182
+ };
82183
+ }
81999
82184
  function addLineChart(chart) {
82000
82185
  const dataSetsColors = chart.dataSets.map((ds) => ds.backgroundColor ?? "");
82001
82186
  const colors = new ColorGenerator(chart.dataSets.length, dataSetsColors);
@@ -82188,7 +82373,7 @@ stores.inject(MyMetaStore, storeInstance);
82188
82373
  `}
82189
82374
  `;
82190
82375
  }
82191
- function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
82376
+ function addDoughnutChart(chart, chartSheetIndex, data) {
82192
82377
  const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
82193
82378
  const colors = new ColorGenerator(maxLength);
82194
82379
  const doughnutColors = range(0, maxLength).map(() => toXlsxHexColor(colors.next()));
@@ -82226,7 +82411,7 @@ stores.inject(MyMetaStore, storeInstance);
82226
82411
  return escapeXml /*xml*/ `
82227
82412
  <c:doughnutChart>
82228
82413
  <c:varyColors val="1" />
82229
- <c:holeSize val="${holeSize}" />
82414
+ <c:holeSize val="${chart.pieHolePercentage ?? (chart.isDoughnut ? DEFAULT_DOUGHNUT_CHART_HOLE_SIZE : 0)}" />
82230
82415
  ${insertDataLabels()}
82231
82416
  ${joinXmlNodes(dataSetsNodes)}
82232
82417
  </c:doughnutChart>
@@ -82245,25 +82430,35 @@ stores.inject(MyMetaStore, storeInstance);
82245
82430
  </dLbls>
82246
82431
  `;
82247
82432
  }
82248
- function addAx(position, axisName, axId, crossAxId, title, defaultFontColor, deleteAxis = 0) {
82433
+ function addAx(position, axisName, axId, crossAxId, title, defaultFontColor, deleteAxis = 0, orientation = "minMax", crossPosition, tickLabelPosition = "nextTo", maxValue, majorUnit, format = "General") {
82249
82434
  // Each Axis present inside a graph needs to be identified by an unsigned integer in order to be referenced by its crossAxis.
82250
82435
  // I.e. x-axis, will reference y-axis and vice-versa.
82251
82436
  const color = title?.color ? toXlsxHexColor(title.color) : defaultFontColor;
82252
82437
  const fontSize = title?.fontSize ?? CHART_AXIS_TITLE_FONT_SIZE;
82438
+ const crossBetweenEl = axisName === "c:valAx" ? escapeXml `<c:crossBetween val="between" />` : "";
82439
+ const maxValueEl = maxValue ? escapeXml `<c:max val="${maxValue}" />` : "";
82440
+ const minValueEl = maxValue ? escapeXml `<c:min val="${-maxValue}" />` : "";
82441
+ const majorUnitEl = majorUnit ? escapeXml `<c:majorUnit val="${majorUnit}" />` : "";
82253
82442
  return escapeXml /*xml*/ `
82254
82443
  <${axisName}>
82255
82444
  <c:axId val="${axId}"/>
82256
82445
  <c:crossAx val="${crossAxId}"/> <!-- reference to the other axe of the chart -->
82257
- <c:crosses val="${position === "b" || position === "l" ? "min" : "max"}"/>
82446
+ <c:crosses val="${crossPosition || (position === "b" || position === "l" ? "min" : "max")}"/>
82447
+ <c:auto val="1"/>
82448
+ ${crossBetweenEl}
82258
82449
  <c:delete val="${deleteAxis}"/> <!-- by default, axis are not displayed -->
82259
82450
  <c:scaling>
82260
- <c:orientation val="minMax" />
82451
+ <c:orientation val="${orientation}" />
82452
+ ${maxValueEl}
82453
+ ${minValueEl}
82261
82454
  </c:scaling>
82455
+ ${majorUnitEl}
82262
82456
  <c:axPos val="${position}" />
82457
+ <c:tickLblPos val="${tickLabelPosition}" />
82263
82458
  ${insertMajorGridLines()}
82264
82459
  <c:majorTickMark val="out" />
82265
82460
  <c:minorTickMark val="none" />
82266
- <c:numFmt formatCode="General" sourceLinked="1" />
82461
+ <c:numFmt formatCode="${format}" sourceLinked="${format === "General" ? "1" : "0"}" />
82267
82462
  <c:title>
82268
82463
  ${insertText(title?.text ?? "", color, fontSize, title)}
82269
82464
  </c:title>
@@ -82458,7 +82653,10 @@ stores.inject(MyMetaStore, storeInstance);
82458
82653
  function addCellIsRule(cf, rule, dxfs) {
82459
82654
  const ruleAttributes = commonCfAttributes(cf);
82460
82655
  const operator = convertOperator(rule.operator);
82461
- ruleAttributes.push(...cellRuleTypeAttributes(rule), ["operator", operator]);
82656
+ ruleAttributes.push(...cellRuleTypeAttributes(rule));
82657
+ if (operator.length) {
82658
+ ruleAttributes.push(["operator", operator]);
82659
+ }
82462
82660
  const formulas = cellRuleFormula(cf.ranges, rule).map((formula) => escapeXml /*xml*/ `<formula>${formula}</formula>`);
82463
82661
  const dxf = {
82464
82662
  font: {
@@ -82504,6 +82702,8 @@ stores.inject(MyMetaStore, storeInstance);
82504
82702
  case "isLessThan":
82505
82703
  case "isLessOrEqualTo":
82506
82704
  return [values[0]];
82705
+ case "customFormula":
82706
+ return values[0].startsWith("=") ? [values[0].slice(1)] : [values[0]];
82507
82707
  case "isBetween":
82508
82708
  case "isNotBetween":
82509
82709
  return [values[0], values[1]];
@@ -82532,6 +82732,8 @@ stores.inject(MyMetaStore, storeInstance);
82532
82732
  case "isBetween":
82533
82733
  case "isNotBetween":
82534
82734
  return [["type", "cellIs"]];
82735
+ case "customFormula":
82736
+ return [["type", "expression"]];
82535
82737
  }
82536
82738
  }
82537
82739
  function addDataBarRule(cf, rule) {
@@ -84632,9 +84834,9 @@ stores.inject(MyMetaStore, storeInstance);
84632
84834
  exports.tokenize = tokenize;
84633
84835
 
84634
84836
 
84635
- __info__.version = "18.5.0-alpha.2";
84636
- __info__.date = "2025-07-11T11:13:53.317Z";
84637
- __info__.hash = "6d42178";
84837
+ __info__.version = "18.5.0-alpha.3";
84838
+ __info__.date = "2025-07-28T13:43:05.981Z";
84839
+ __info__.hash = "53dfee8";
84638
84840
 
84639
84841
 
84640
84842
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);