@odoo/o-spreadsheet 18.5.0-alpha.1 → 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.1
6
- * @date 2025-06-27T09:12:49.245Z
7
- * @hash 756e75b
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) {
@@ -853,6 +853,7 @@
853
853
  ];
854
854
  const specialWhiteSpaceRegexp = new RegExp(specialWhiteSpaceSpecialCharacters.join("|"), "g");
855
855
  const newLineRegexp = /(\r\n|\r)/g;
856
+ const whiteSpaceCharacters = specialWhiteSpaceSpecialCharacters.concat([" "]);
856
857
  /**
857
858
  * Replace all different newlines characters by \n
858
859
  */
@@ -1989,8 +1990,9 @@
1989
1990
  const DATE_JS_1900_OFFSET = INITIAL_JS_DAY.getTime() - INITIAL_1900_DAY.getTime();
1990
1991
  const mdyDateRegexp = /^\d{1,2}(\/|-|\s)\d{1,2}((\/|-|\s)\d{1,4})?$/;
1991
1992
  const ymdDateRegexp = /^\d{3,4}(\/|-|\s)\d{1,2}(\/|-|\s)\d{1,2}$/;
1992
- const dateSeparatorsRegex = /\/|-|\s/;
1993
- const dateRegexp = /^(\d{1,4})[\/-\s](\d{1,4})([\/-\s](\d{1,4}))?$/;
1993
+ const whiteSpaceChars = whiteSpaceCharacters.join("");
1994
+ const dateSeparatorsRegex = new RegExp(`\/|-|${whiteSpaceCharacters.join("|")}`);
1995
+ const dateRegexp = new RegExp(`^(\\d{1,4})[\/${whiteSpaceChars}\-](\\d{1,4})([\/${whiteSpaceChars}\-](\\d{1,4}))?$`);
1994
1996
  const timeRegexp = /((\d+(:\d+)?(:\d+)?\s*(AM|PM))|(\d+:\d+(:\d+)?))$/;
1995
1997
  /** Convert a value number representing a date, or return undefined if it isn't possible */
1996
1998
  function valueToDateNumber(value, locale) {
@@ -2900,6 +2902,7 @@
2900
2902
  "isEmpty",
2901
2903
  "isNotEqual",
2902
2904
  "isEqual",
2905
+ "customFormula",
2903
2906
  ]);
2904
2907
 
2905
2908
  const availableDataValidationOperators = new Set([
@@ -5032,11 +5035,11 @@
5032
5035
  }
5033
5036
  }
5034
5037
 
5035
- function evaluateLiteral(literalCell, localeFormat) {
5038
+ function evaluateLiteral(literalCell, localeFormat, position) {
5036
5039
  const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5037
5040
  ? literalCell.content
5038
5041
  : literalCell.parsedValue;
5039
- const functionResult = { value, format: localeFormat.format };
5042
+ const functionResult = { value, format: localeFormat.format, origin: position };
5040
5043
  return createEvaluatedCell(functionResult, localeFormat.locale);
5041
5044
  }
5042
5045
  function parseLiteral(content, locale) {
@@ -5058,10 +5061,11 @@
5058
5061
  }
5059
5062
  return content;
5060
5063
  }
5061
- function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
5064
+ function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell, origin) {
5062
5065
  const link = detectLink(functionResult.value);
5063
5066
  if (!link) {
5064
- return _createEvaluatedCell(functionResult, locale, cell);
5067
+ const evaluateCell = _createEvaluatedCell(functionResult, locale, cell);
5068
+ return addOrigin(evaluateCell, functionResult.origin ?? origin);
5065
5069
  }
5066
5070
  const value = parseLiteral(link.label, locale);
5067
5071
  const format = functionResult.format ||
@@ -5072,10 +5076,10 @@
5072
5076
  value,
5073
5077
  format,
5074
5078
  };
5075
- return {
5079
+ return addOrigin({
5076
5080
  ..._createEvaluatedCell(linkPayload, locale, cell),
5077
5081
  link,
5078
- };
5082
+ }, functionResult.origin ?? origin);
5079
5083
  }
5080
5084
  function _createEvaluatedCell(functionResult, locale, cell) {
5081
5085
  let { value, format, message } = functionResult;
@@ -5165,6 +5169,17 @@
5165
5169
  defaultAlign: "center",
5166
5170
  };
5167
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
+ }
5168
5183
 
5169
5184
  function toCriterionDateNumber(dateValue) {
5170
5185
  const today = DateTime.now();
@@ -6925,6 +6940,8 @@
6925
6940
  function splitTextToWidth(ctx, text, style, width) {
6926
6941
  if (!style)
6927
6942
  style = {};
6943
+ if (isMarkdownLink(text))
6944
+ text = parseMarkdownLink(text).label;
6928
6945
  const brokenText = [];
6929
6946
  // Checking if text contains NEWLINE before split makes it very slightly slower if text contains it,
6930
6947
  // but 5-10x faster if it doesn't
@@ -7843,6 +7860,7 @@
7843
7860
  case "isGreaterOrEqualTo":
7844
7861
  case "isLessThan":
7845
7862
  case "isLessOrEqualTo":
7863
+ case "customFormula":
7846
7864
  rule.values = rule.values.map((v) => changeContentLocale(v));
7847
7865
  return rule;
7848
7866
  case "beginsWithText":
@@ -8765,6 +8783,10 @@
8765
8783
  if (groupValue === null || groupValue === "null") {
8766
8784
  return null;
8767
8785
  }
8786
+ const extractedGroupValue = typeof groupValue === "object" ? groupValue.value : groupValue;
8787
+ if (isEvaluationError(extractedGroupValue)) {
8788
+ return extractedGroupValue;
8789
+ }
8768
8790
  const groupValueString = typeof groupValue === "boolean"
8769
8791
  ? toString(groupValue).toLocaleLowerCase()
8770
8792
  : toString(groupValue);
@@ -10527,6 +10549,7 @@ stores.inject(MyMetaStore, storeInstance);
10527
10549
  /** The possible values for the XLSX polynomial trendline order are defined by the ST_Order simple type (§21.2.3.29) */
10528
10550
  const MAX_XLSX_POLYNOMIAL_DEGREE = 6;
10529
10551
  const FIRST_NUMFMT_ID = 164;
10552
+ const DEFAULT_DOUGHNUT_CHART_HOLE_SIZE = 50;
10530
10553
  const FORCE_DEFAULT_ARGS_FUNCTIONS = {
10531
10554
  FLOOR: [{ type: "NUMBER", value: 1 }],
10532
10555
  CEILING: [{ type: "NUMBER", value: 1 }],
@@ -19026,13 +19049,19 @@ stores.inject(MyMetaStore, storeInstance);
19026
19049
  if (isEvaluationError(cellReference?.value)) {
19027
19050
  return cellReference;
19028
19051
  }
19029
- const column = cellReference === undefined
19030
- ? this.__originCellPosition?.col
19031
- : toZone(cellReference.value).left;
19032
- if (column === undefined) {
19033
- 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;
19034
19061
  }
19035
- return column + 1;
19062
+ return generateMatrix(zone.right - zone.left + 1, 1, (col, row) => ({
19063
+ value: zone.left + col + 1,
19064
+ }));
19036
19065
  },
19037
19066
  isExported: true,
19038
19067
  };
@@ -19263,13 +19292,19 @@ stores.inject(MyMetaStore, storeInstance);
19263
19292
  if (isEvaluationError(cellReference?.value)) {
19264
19293
  return cellReference;
19265
19294
  }
19266
- const row = cellReference === undefined
19267
- ? this.__originCellPosition?.row
19268
- : toZone(cellReference.value).top;
19269
- if (row === undefined) {
19270
- 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;
19271
19300
  }
19272
- return row + 1;
19301
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
19302
+ if (zone.top === zone.bottom) {
19303
+ return zone.top + 1;
19304
+ }
19305
+ return generateMatrix(1, zone.bottom - zone.top + 1, (col, row) => ({
19306
+ value: zone.top + row + 1,
19307
+ }));
19273
19308
  },
19274
19309
  isExported: true,
19275
19310
  };
@@ -22846,6 +22881,16 @@ stores.inject(MyMetaStore, storeInstance);
22846
22881
  static getDefinitionFromContextCreation(context) {
22847
22882
  throw new Error("This method should be implemented by sub class");
22848
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
+ }
22849
22894
  }
22850
22895
 
22851
22896
  function getBaselineText(baseline, keyValue, baselineMode, humanize, locale) {
@@ -26328,6 +26373,7 @@ stores.inject(MyMetaStore, storeInstance);
26328
26373
  labelRange: context.auxiliaryRange || undefined,
26329
26374
  axesDesign: context.axesDesign,
26330
26375
  showValues: context.showValues,
26376
+ horizontal: context.horizontal,
26331
26377
  };
26332
26378
  }
26333
26379
  getContextCreation() {
@@ -26385,10 +26431,7 @@ stores.inject(MyMetaStore, storeInstance);
26385
26431
  };
26386
26432
  }
26387
26433
  getDefinitionForExcel() {
26388
- const dataSets = this.dataSets
26389
- .map((ds) => toExcelDataset(this.getters, ds))
26390
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
26391
- 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));
26392
26435
  const definition = this.getDefinition();
26393
26436
  return {
26394
26437
  ...definition,
@@ -26976,10 +27019,7 @@ stores.inject(MyMetaStore, storeInstance);
26976
27019
  if (this.aggregated) {
26977
27020
  return undefined;
26978
27021
  }
26979
- const dataSets = this.dataSets
26980
- .map((ds) => toExcelDataset(this.getters, ds))
26981
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
26982
- 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));
26983
27023
  const definition = this.getDefinition();
26984
27024
  return {
26985
27025
  ...definition,
@@ -27447,7 +27487,9 @@ stores.inject(MyMetaStore, storeInstance);
27447
27487
  }
27448
27488
  function getFormulaNumberValue(sheetId, formula, getters) {
27449
27489
  const value = getters.evaluateFormula(sheetId, formula);
27450
- return isMatrix(value) ? undefined : tryToNumber(value, getters.getLocale());
27490
+ return isMultipleElementMatrix(value)
27491
+ ? undefined
27492
+ : tryToNumber(toScalar(value), getters.getLocale());
27451
27493
  }
27452
27494
  function getInvalidGaugeRuntime(chart, getters) {
27453
27495
  return {
@@ -27716,10 +27758,7 @@ stores.inject(MyMetaStore, storeInstance);
27716
27758
  return new LineChart(definition, this.sheetId, this.getters);
27717
27759
  }
27718
27760
  getDefinitionForExcel() {
27719
- const dataSets = this.dataSets
27720
- .map((ds) => toExcelDataset(this.getters, ds))
27721
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
27722
- 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));
27723
27762
  const definition = this.getDefinition();
27724
27763
  return {
27725
27764
  ...definition,
@@ -27807,7 +27846,8 @@ stores.inject(MyMetaStore, storeInstance);
27807
27846
  type: "pie",
27808
27847
  labelRange: context.auxiliaryRange || undefined,
27809
27848
  aggregated: context.aggregated ?? false,
27810
- isDoughnut: false,
27849
+ isDoughnut: context.isDoughnut,
27850
+ pieHolePercentage: context.pieHolePercentage,
27811
27851
  showValues: context.showValues,
27812
27852
  };
27813
27853
  }
@@ -27855,10 +27895,7 @@ stores.inject(MyMetaStore, storeInstance);
27855
27895
  return new PieChart(definition, sheetId, this.getters);
27856
27896
  }
27857
27897
  getDefinitionForExcel() {
27858
- const dataSets = this.dataSets
27859
- .map((ds) => toExcelDataset(this.getters, ds))
27860
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
27861
- 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));
27862
27899
  return {
27863
27900
  ...this.getDefinition(),
27864
27901
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
@@ -28003,8 +28040,22 @@ stores.inject(MyMetaStore, storeInstance);
28003
28040
  showValues: this.showValues,
28004
28041
  };
28005
28042
  }
28006
- getDefinitionForExcel() {
28007
- 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
+ };
28008
28059
  }
28009
28060
  updateRanges(applyChange) {
28010
28061
  const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
@@ -28147,10 +28198,7 @@ stores.inject(MyMetaStore, storeInstance);
28147
28198
  if (this.aggregated) {
28148
28199
  return undefined;
28149
28200
  }
28150
- const dataSets = this.dataSets
28151
- .map((ds) => toExcelDataset(this.getters, ds))
28152
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
28153
- 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));
28154
28202
  const definition = this.getDefinition();
28155
28203
  return {
28156
28204
  ...definition,
@@ -31072,6 +31120,9 @@ stores.inject(MyMetaStore, storeInstance);
31072
31120
  return undefined;
31073
31121
  }
31074
31122
  get errorOriginPositionString() {
31123
+ if (this.env.model.getters.isDashboard()) {
31124
+ return "";
31125
+ }
31075
31126
  const evaluationError = this.evaluationError;
31076
31127
  const position = evaluationError?.errorOriginPosition;
31077
31128
  if (!position || deepEquals(position, this.props.cellPosition)) {
@@ -32594,6 +32645,9 @@ stores.inject(MyMetaStore, storeInstance);
32594
32645
  get isAutoCompleteDisplayed() {
32595
32646
  return !!this.autoComplete.provider;
32596
32647
  }
32648
+ get canBeToggled() {
32649
+ return this.autoComplete.provider?.canBeToggled ?? true;
32650
+ }
32597
32651
  cycleReferences() {
32598
32652
  const locale = this.getters.getLocale();
32599
32653
  const updated = cycleFixedReference(this.composerSelection, this._currentContent, locale);
@@ -33123,6 +33177,7 @@ stores.inject(MyMetaStore, storeInstance);
33123
33177
  proposals,
33124
33178
  selectProposal: provider.selectProposal,
33125
33179
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
33180
+ canBeToggled: provider.canBeToggled,
33126
33181
  };
33127
33182
  }
33128
33183
  if (exactMatch && this._currentContent !== this.initialContent) {
@@ -33145,6 +33200,7 @@ stores.inject(MyMetaStore, storeInstance);
33145
33200
  proposals,
33146
33201
  selectProposal: provider.selectProposal,
33147
33202
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
33203
+ canBeToggled: provider.canBeToggled,
33148
33204
  };
33149
33205
  }
33150
33206
  }
@@ -33715,9 +33771,13 @@ stores.inject(MyMetaStore, storeInstance);
33715
33771
  }
33716
33772
  }
33717
33773
  closeAssistant() {
33774
+ if (!this.props.composerStore.canBeToggled)
33775
+ return;
33718
33776
  this.assistant.forcedClosed = true;
33719
33777
  }
33720
33778
  openAssistant() {
33779
+ if (!this.props.composerStore.canBeToggled)
33780
+ return;
33721
33781
  this.assistant.forcedClosed = false;
33722
33782
  }
33723
33783
  onWheel(event) {
@@ -33907,7 +33967,7 @@ stores.inject(MyMetaStore, storeInstance);
33907
33967
  return [...new Set(argsToFocus)];
33908
33968
  }
33909
33969
  autoComplete(value) {
33910
- if (!value || this.assistant.forcedClosed) {
33970
+ if (!value || (this.assistant.forcedClosed && this.props.composerStore.canBeToggled)) {
33911
33971
  return;
33912
33972
  }
33913
33973
  this.props.composerStore.insertAutoCompleteValue(value);
@@ -37485,7 +37545,7 @@ stores.inject(MyMetaStore, storeInstance);
37485
37545
  /** Conversion map CF types in XLSX <=> Cf types in o_spreadsheet */
37486
37546
  const CF_TYPE_CONVERSION_MAP = {
37487
37547
  aboveAverage: undefined,
37488
- expression: undefined,
37548
+ expression: "customFormula",
37489
37549
  cellIs: undefined, // exist but isn't an operator in o_spreadsheet
37490
37550
  colorScale: undefined, // exist but isn't an operator in o_spreadsheet
37491
37551
  dataBar: undefined,
@@ -38089,7 +38149,6 @@ stores.inject(MyMetaStore, storeInstance);
38089
38149
  case "containsErrors":
38090
38150
  case "notContainsErrors":
38091
38151
  case "duplicateValues":
38092
- case "expression":
38093
38152
  case "top10":
38094
38153
  case "uniqueValues":
38095
38154
  case "timePeriod":
@@ -38122,6 +38181,12 @@ stores.inject(MyMetaStore, storeInstance);
38122
38181
  operator = CF_TYPE_CONVERSION_MAP[rule.type];
38123
38182
  values.push(rule.text);
38124
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;
38125
38190
  case "containsBlanks":
38126
38191
  case "notContainsBlanks":
38127
38192
  operator = CF_TYPE_CONVERSION_MAP[rule.type];
@@ -38351,6 +38416,8 @@ stores.inject(MyMetaStore, storeInstance);
38351
38416
  return "equal";
38352
38417
  case "isNotEqual":
38353
38418
  return "notEqual";
38419
+ case "customFormula":
38420
+ return "";
38354
38421
  }
38355
38422
  }
38356
38423
  // -------------------------------------
@@ -38686,6 +38753,9 @@ stores.inject(MyMetaStore, storeInstance);
38686
38753
  aggregated: false,
38687
38754
  cumulative: chartData.cumulative || false,
38688
38755
  labelsAsText: false,
38756
+ horizontal: chartData.horizontal,
38757
+ isDoughnut: chartData.isDoughnut,
38758
+ pieHolePercentage: chartData.pieHolePercentage,
38689
38759
  };
38690
38760
  try {
38691
38761
  const ChartClass = chartRegistry.get(chartData.type);
@@ -39220,40 +39290,112 @@ stores.inject(MyMetaStore, storeInstance);
39220
39290
  * In all the sheets, replace the table-only references in the formula cells with standard references.
39221
39291
  */
39222
39292
  function convertTableFormulaReferences(convertedSheets, xlsxSheets) {
39293
+ let deconstructedSheets = null;
39223
39294
  for (const tableSheet of convertedSheets) {
39224
39295
  const tables = xlsxSheets.find((s) => isSheetNameEqual(s.sheetName, tableSheet.name)).tables;
39296
+ if (!tables || tables.length === 0) {
39297
+ continue;
39298
+ }
39299
+ // Only deconstruct sheets if we are sure there are tables to process
39300
+ if (!deconstructedSheets) {
39301
+ deconstructedSheets = deconstructSheets(convertedSheets);
39302
+ }
39225
39303
  for (const table of tables) {
39226
- const tabRef = table.name + "[";
39227
- for (const sheet of convertedSheets) {
39228
- for (const xc in sheet.cells) {
39229
- const cell = sheet.cells[xc];
39230
- let cellContent = sheet.cells[xc];
39231
- if (cell && cellContent && cellContent.startsWith("=")) {
39232
- let refIndex;
39233
- while ((refIndex = cellContent.indexOf(tabRef)) !== -1) {
39234
- let endIndex = refIndex + tabRef.length;
39235
- let openBrackets = 1;
39236
- while (openBrackets > 0 && endIndex < cellContent.length) {
39237
- if (cellContent[endIndex] === "[") {
39238
- openBrackets++;
39239
- }
39240
- else if (cellContent[endIndex] === "]") {
39241
- openBrackets--;
39242
- }
39243
- endIndex++;
39244
- }
39245
- const reference = cellContent.slice(refIndex + tabRef.length, endIndex - 1);
39246
- const sheetPrefix = tableSheet.id === sheet.id ? "" : tableSheet.name + "!";
39247
- const convertedRef = convertTableReference(sheetPrefix, reference, table, xc);
39248
- cellContent =
39249
- cellContent.slice(0, refIndex) + convertedRef + cellContent.slice(endIndex);
39304
+ for (const sheetId in deconstructedSheets) {
39305
+ const sheet = convertedSheets.find((s) => s.id === sheetId);
39306
+ for (const xc in deconstructedSheets[sheetId]) {
39307
+ const deconstructedCell = deconstructedSheets[sheetId][xc];
39308
+ for (let i = deconstructedCell.length - 3; i >= 0; i -= 2) {
39309
+ const possibleTable = deconstructedSheets[sheetId][xc][i];
39310
+ if (!possibleTable.endsWith(table.name)) {
39311
+ continue;
39250
39312
  }
39313
+ const possibleRef = deconstructedSheets[sheetId][xc][i + 1];
39314
+ const sheetPrefix = tableSheet.id === sheet.id ? "" : tableSheet.name + "!";
39315
+ const convertedRef = convertTableReference(sheetPrefix, possibleRef, table, xc);
39316
+ deconstructedSheets[sheetId][xc][i + 2] =
39317
+ possibleTable.slice(0, possibleTable.indexOf(table.name)) +
39318
+ convertedRef +
39319
+ deconstructedSheets[sheetId][xc][i + 2];
39320
+ deconstructedSheets[sheetId][xc].splice(i, 2);
39321
+ }
39322
+ // sheet.cells[xc] = cellContent;
39323
+ }
39324
+ }
39325
+ }
39326
+ }
39327
+ if (!deconstructedSheets) {
39328
+ return;
39329
+ }
39330
+ for (const sheetId in deconstructedSheets) {
39331
+ const sheet = convertedSheets.find((s) => s.id === sheetId);
39332
+ for (const xc in deconstructedSheets[sheetId]) {
39333
+ const deconstructedCell = deconstructedSheets[sheetId][xc];
39334
+ if (deconstructedCell.length === 1) {
39335
+ sheet.cells[xc] = deconstructedCell[0];
39336
+ continue;
39337
+ }
39338
+ let newContent = "";
39339
+ for (let i = 0; i < deconstructedCell.length; i += 2) {
39340
+ newContent += deconstructedCell[i] + "[" + deconstructedCell[i + 1] + "]";
39341
+ }
39342
+ newContent += deconstructedCell[deconstructedCell.length - 1];
39343
+ sheet.cells[xc] = newContent;
39344
+ }
39345
+ }
39346
+ }
39347
+ /**
39348
+ * Deconstruct the content of the cells in the sheets to extract possible table references.
39349
+ * Example from "=AVERAGE(Table1[colName1])-AVERAGE(Table2[colName2])":
39350
+ * return --> ["=AVERAGE(Table1", "colName1", ")-AVERAGE(Table2", "colName2", ")"]
39351
+ */
39352
+ function deconstructSheets(convertedSheets) {
39353
+ const deconstructedSheets = {};
39354
+ for (const sheet of convertedSheets) {
39355
+ for (const xc in sheet.cells) {
39356
+ const cellContent = sheet.cells[xc];
39357
+ if (!cellContent || !cellContent.startsWith("=")) {
39358
+ continue;
39359
+ }
39360
+ const startIndex = cellContent.indexOf("[");
39361
+ if (startIndex === -1) {
39362
+ continue;
39363
+ }
39364
+ const deconstructedCell = [];
39365
+ let possibleTable = cellContent.slice(0, startIndex);
39366
+ let possibleRef = "";
39367
+ let openBrackets = 1;
39368
+ let mainPossibleTableIndex = 0;
39369
+ let mainOpenBracketIndex = startIndex;
39370
+ for (let index = startIndex + 1; index < cellContent.length; index++) {
39371
+ if (cellContent[index] === "[") {
39372
+ if (openBrackets === 0) {
39373
+ possibleTable = cellContent.slice(mainPossibleTableIndex, index);
39374
+ mainOpenBracketIndex = index;
39251
39375
  }
39252
- sheet.cells[xc] = cellContent;
39376
+ openBrackets++;
39377
+ continue;
39253
39378
  }
39379
+ if (cellContent[index] === "]") {
39380
+ openBrackets--;
39381
+ if (openBrackets === 0) {
39382
+ possibleRef = cellContent.slice(mainOpenBracketIndex + 1, index);
39383
+ deconstructedCell.push(possibleTable);
39384
+ deconstructedCell.push(possibleRef);
39385
+ mainPossibleTableIndex = index + 1;
39386
+ }
39387
+ }
39388
+ }
39389
+ if (deconstructedCell.length) {
39390
+ if (!deconstructedSheets[sheet.id]) {
39391
+ deconstructedSheets[sheet.id] = {};
39392
+ }
39393
+ deconstructedCell.push(cellContent.slice(mainPossibleTableIndex));
39394
+ deconstructedSheets[sheet.id][xc] = [...deconstructedCell];
39254
39395
  }
39255
39396
  }
39256
39397
  }
39398
+ return deconstructedSheets;
39257
39399
  }
39258
39400
  /**
39259
39401
  * Convert table-specific references in formulas into standard references. A table reference is composed of columns names,
@@ -39890,6 +40032,12 @@ stores.inject(MyMetaStore, storeInstance);
39890
40032
  const barChartGrouping = this.extractChildAttr(rootChartElement, "c:grouping", "val", {
39891
40033
  default: "clustered",
39892
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();
39893
40041
  return {
39894
40042
  title: { text: chartTitle },
39895
40043
  type: CHART_TYPE_CONVERSION_MAP[chartType],
@@ -39903,6 +40051,9 @@ stores.inject(MyMetaStore, storeInstance);
39903
40051
  }).asString()],
39904
40052
  stacked: barChartGrouping === "stacked",
39905
40053
  fontColor: "000000",
40054
+ horizontal: chartDirection === "bar",
40055
+ isDoughnut: chartHoleSize > 0,
40056
+ pieHolePercentage: chartHoleSize,
39906
40057
  };
39907
40058
  })[0];
39908
40059
  }
@@ -45565,10 +45716,10 @@ stores.inject(MyMetaStore, storeInstance);
45565
45716
  const cellValue = isFormula(content)
45566
45717
  ? this.getters.evaluateFormula(this.sheetId, content)
45567
45718
  : parseLiteral(content, this.getters.getLocale());
45568
- if (isMatrix(cellValue)) {
45719
+ if (isMultipleElementMatrix(cellValue)) {
45569
45720
  return true;
45570
45721
  }
45571
- const validationResult = this.getters.getValidationResultForCellValue(cellValue, cellPosition);
45722
+ const validationResult = this.getters.getValidationResultForCellValue(toScalar(cellValue), cellPosition);
45572
45723
  if (!validationResult.isValid && validationResult.rule.isBlocking) {
45573
45724
  return false;
45574
45725
  }
@@ -50680,10 +50831,10 @@ stores.inject(MyMetaStore, storeInstance);
50680
50831
  return tryToNumber(value, locale) !== undefined;
50681
50832
  }
50682
50833
  const evaluatedValue = this.env.model.getters.evaluateFormula(this.sheetId, value);
50683
- if (isMatrix(evaluatedValue)) {
50834
+ if (isMultipleElementMatrix(evaluatedValue)) {
50684
50835
  return false;
50685
50836
  }
50686
- return tryToNumber(evaluatedValue, locale) !== undefined;
50837
+ return tryToNumber(toScalar(evaluatedValue), locale) !== undefined;
50687
50838
  }
50688
50839
  get sheetId() {
50689
50840
  const chart = this.env.model.getters.getChart(this.props.figureId);
@@ -50893,6 +51044,9 @@ stores.inject(MyMetaStore, storeInstance);
50893
51044
  pieHolePercentage,
50894
51045
  });
50895
51046
  }
51047
+ get defaultHoleSize() {
51048
+ return DEFAULT_DOUGHNUT_CHART_HOLE_SIZE;
51049
+ }
50896
51050
  }
50897
51051
 
50898
51052
  class RadarChartDesignPanel extends owl.Component {
@@ -51537,12 +51691,32 @@ stores.inject(MyMetaStore, storeInstance);
51537
51691
  static template = "o-spreadsheet-ChartPanel";
51538
51692
  static components = { Section, ChartTypePicker };
51539
51693
  static props = { onCloseSidePanel: Function, figureId: String };
51694
+ panelContentRef;
51695
+ scrollPositions = {
51696
+ configuration: 0,
51697
+ design: 0,
51698
+ };
51540
51699
  store;
51541
51700
  get figureId() {
51542
51701
  return this.props.figureId;
51543
51702
  }
51544
51703
  setup() {
51545
51704
  this.store = useLocalStore(MainChartPanelStore);
51705
+ this.panelContentRef = owl.useRef("panelContent");
51706
+ owl.useEffect(() => {
51707
+ const el = this.panelContentRef.el;
51708
+ const activePanel = this.store.panel;
51709
+ if (el) {
51710
+ el.scrollTop = this.scrollPositions[activePanel];
51711
+ }
51712
+ }, () => [this.store.panel]);
51713
+ }
51714
+ switchPanel(panel) {
51715
+ const el = this.panelContentRef.el;
51716
+ if (el) {
51717
+ this.scrollPositions[this.store.panel] = el.scrollTop;
51718
+ }
51719
+ this.store.activatePanel(panel);
51546
51720
  }
51547
51721
  updateChart(figureId, updateDefinition) {
51548
51722
  if (figureId !== this.figureId) {
@@ -55494,10 +55668,7 @@ stores.inject(MyMetaStore, storeInstance);
55494
55668
  if (finalCell.value === null) {
55495
55669
  return { value: _t("(Undefined)") };
55496
55670
  }
55497
- return {
55498
- value: finalCell.value,
55499
- format: finalCell.format,
55500
- };
55671
+ return finalCell;
55501
55672
  }
55502
55673
  getPivotCellValueAndFormat(measureId, domain) {
55503
55674
  const dataEntries = this.filterDataEntriesFromDomain(this.dataEntries, domain);
@@ -55599,9 +55770,15 @@ stores.inject(MyMetaStore, storeInstance);
55599
55770
  return domain.reduce((current, acc) => this.filterDataEntriesFromDomainNode(current, acc), dataEntries);
55600
55771
  }
55601
55772
  filterDataEntriesFromDomainNode(dataEntries, domain) {
55602
- const { field, value } = domain;
55773
+ const { field, value, type } = domain;
55603
55774
  const { nameWithGranularity } = this.getDimension(field);
55604
- 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
+ });
55605
55782
  }
55606
55783
  getDimension(nameWithGranularity) {
55607
55784
  return this.definition.getDimension(nameWithGranularity);
@@ -55735,7 +55912,6 @@ stores.inject(MyMetaStore, storeInstance);
55735
55912
  ui: SpreadsheetPivot,
55736
55913
  definition: SpreadsheetPivotRuntimeDefinition,
55737
55914
  externalData: false,
55738
- onIterationEndEvaluation: (pivot) => pivot.markAsDirtyForEvaluation(),
55739
55915
  dateGranularities: [...dateGranularities],
55740
55916
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
55741
55917
  isMeasureCandidate: (field) => field.type !== "boolean",
@@ -64386,7 +64562,7 @@ stores.inject(MyMetaStore, storeInstance);
64386
64562
  onIterationEndEvaluationRegistry.add("pivots", (getters) => {
64387
64563
  for (const pivotId of getters.getPivotIds()) {
64388
64564
  const pivot = getters.getPivot(pivotId);
64389
- pivotRegistry.get(pivot.type).onIterationEndEvaluation(pivot);
64565
+ pivot.markAsDirtyForEvaluation?.();
64390
64566
  }
64391
64567
  });
64392
64568
 
@@ -65554,7 +65730,7 @@ stores.inject(MyMetaStore, storeInstance);
65554
65730
  const EMPTY_ARRAY = [];
65555
65731
 
65556
65732
  const MAX_ITERATION = 30;
65557
- const ERROR_CYCLE_CELL = Object.freeze(createEvaluatedCell(new CircularDependencyError()));
65733
+ const ERROR_CYCLE_CELL = Object.freeze(createEvaluatedCell({ ...new CircularDependencyError(), origin: undefined }));
65558
65734
  const EMPTY_CELL = Object.freeze(createEvaluatedCell({ value: null }));
65559
65735
  class Evaluator {
65560
65736
  context;
@@ -65788,11 +65964,12 @@ stores.inject(MyMetaStore, storeInstance);
65788
65964
  this.cellsBeingComputed.add(cellId);
65789
65965
  return cell.isFormula
65790
65966
  ? this.computeFormulaCell(position, cell)
65791
- : evaluateLiteral(cell, localeFormat);
65967
+ : evaluateLiteral(cell, localeFormat, position);
65792
65968
  }
65793
65969
  catch (e) {
65794
65970
  e.value = e?.value || CellErrorType.GenericError;
65795
65971
  e.message = e?.message || implementationErrorMessage;
65972
+ e.origin = position;
65796
65973
  return createEvaluatedCell(e);
65797
65974
  }
65798
65975
  finally {
@@ -65809,7 +65986,7 @@ stores.inject(MyMetaStore, storeInstance);
65809
65986
  computeFormulaCell(formulaPosition, cellData) {
65810
65987
  const formulaReturn = updateEvalContextAndExecute(cellData.compiledFormula, this.compilationParams, formulaPosition.sheetId, this.buildSafeGetSymbolValue(), formulaPosition);
65811
65988
  if (!isMatrix(formulaReturn)) {
65812
- const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData);
65989
+ const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData, formulaPosition);
65813
65990
  if (evaluatedCell.type === CellValueType.error) {
65814
65991
  evaluatedCell.errorOriginPosition = formulaReturn.errorOriginPosition ?? formulaPosition;
65815
65992
  }
@@ -65885,7 +66062,7 @@ stores.inject(MyMetaStore, storeInstance);
65885
66062
  const spreadValues = (i, j) => {
65886
66063
  const position = { sheetId, col: i + col, row: j + row };
65887
66064
  const cell = this.getters.getCell(position);
65888
- const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
66065
+ const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell, position);
65889
66066
  if (evaluatedCell.type === CellValueType.error) {
65890
66067
  evaluatedCell.errorOriginPosition = matrixResult[i][j].errorOriginPosition ?? position;
65891
66068
  }
@@ -66558,7 +66735,7 @@ stores.inject(MyMetaStore, storeInstance);
66558
66735
  continue;
66559
66736
  }
66560
66737
  const figureId = figure.id;
66561
- const figureData = this.getters.getChart(figureId)?.getDefinitionForExcel();
66738
+ const figureData = this.getters.getChart(figureId)?.getDefinitionForExcel(this.getters);
66562
66739
  if (figureData) {
66563
66740
  figures.push({
66564
66741
  ...figure,
@@ -66850,12 +67027,12 @@ stores.inject(MyMetaStore, storeInstance);
66850
67027
  }
66851
67028
  return this.getters.evaluateFormula(sheetId, value) ?? "";
66852
67029
  });
66853
- if (evaluatedCriterionValues.some(isMatrix)) {
67030
+ if (evaluatedCriterionValues.some(isMultipleElementMatrix)) {
66854
67031
  return false;
66855
67032
  }
66856
67033
  const evaluatedCriterion = {
66857
67034
  type: rule.operator,
66858
- values: evaluatedCriterionValues,
67035
+ values: evaluatedCriterionValues.map(toScalar),
66859
67036
  };
66860
67037
  return evaluator.isValueValid(cell.value ?? "", evaluatedCriterion, this.getters, sheetId);
66861
67038
  }
@@ -67015,10 +67192,10 @@ stores.inject(MyMetaStore, storeInstance);
67015
67192
  const evaluator = criterionEvaluatorRegistry.get(criterion.type);
67016
67193
  const offset = this.getCellOffsetInRule(cellPosition, rule);
67017
67194
  const evaluatedCriterionValues = this.getEvaluatedCriterionValues(sheetId, offset, criterion);
67018
- if (evaluatedCriterionValues.some(isMatrix)) {
67195
+ if (evaluatedCriterionValues.some(isMultipleElementMatrix)) {
67019
67196
  return undefined;
67020
67197
  }
67021
- const evaluatedCriterion = { ...criterion, values: evaluatedCriterionValues };
67198
+ const evaluatedCriterion = { ...criterion, values: evaluatedCriterionValues.map(toScalar) };
67022
67199
  if (evaluator.isValueValid(cellValue, evaluatedCriterion, this.getters, sheetId)) {
67023
67200
  return undefined;
67024
67201
  }
@@ -67471,6 +67648,23 @@ stores.inject(MyMetaStore, storeInstance);
67471
67648
  static getters = ["getRowSize", "getHeaderSize", "getMaxAnchorOffset"];
67472
67649
  tallestCellInRow = {};
67473
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
+ }
67474
67668
  handle(cmd) {
67475
67669
  switch (cmd.type) {
67476
67670
  case "START":
@@ -67500,16 +67694,6 @@ stores.inject(MyMetaStore, storeInstance);
67500
67694
  this.history.update("tallestCellInRow", cmd.sheetId, tallestCells);
67501
67695
  break;
67502
67696
  }
67503
- case "ADD_COLUMNS_ROWS": {
67504
- if (cmd.dimension === "COL") {
67505
- return;
67506
- }
67507
- const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
67508
- const newCells = Array(cmd.quantity).fill(undefined);
67509
- const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
67510
- this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
67511
- break;
67512
- }
67513
67697
  case "RESIZE_COLUMNS_ROWS":
67514
67698
  {
67515
67699
  const sheetId = cmd.sheetId;
@@ -67661,13 +67845,13 @@ stores.inject(MyMetaStore, storeInstance);
67661
67845
  super(custom, params);
67662
67846
  this.getters = params.getters;
67663
67847
  }
67664
- init(params) {
67848
+ markAsDirtyForEvaluation() {
67665
67849
  this.cache = {};
67666
67850
  this.rankAsc = {};
67667
67851
  this.rankDesc = {};
67668
67852
  this.runningTotal = {};
67669
67853
  this.runningTotalInPercent = {};
67670
- super.init(params);
67854
+ super.markAsDirtyForEvaluation?.();
67671
67855
  }
67672
67856
  getPivotCellValueAndFormat(measureName, domain) {
67673
67857
  return this.getMeasureDisplayValue(measureName, domain);
@@ -67792,7 +67976,7 @@ stores.inject(MyMetaStore, storeInstance);
67792
67976
  return this.getSubTreeMatchingDomain(node.children, domain, domainLevel + 1);
67793
67977
  }
67794
67978
  }
67795
- return tree;
67979
+ return [];
67796
67980
  }
67797
67981
  treeToLeafDomains(tree, parentDomain = []) {
67798
67982
  const domains = [];
@@ -73531,12 +73715,12 @@ stores.inject(MyMetaStore, storeInstance);
73531
73715
  }
73532
73716
  return this.getters.evaluateFormula(sheetId, value) ?? "";
73533
73717
  });
73534
- if (evaluatedCriterionValues.some(isMatrix)) {
73718
+ if (evaluatedCriterionValues.some(isMultipleElementMatrix)) {
73535
73719
  continue;
73536
73720
  }
73537
73721
  const evaluatedCriterion = {
73538
73722
  type: filterValue.type,
73539
- values: evaluatedCriterionValues,
73723
+ values: evaluatedCriterionValues.map(toScalar),
73540
73724
  dateValue: filterValue.dateValue,
73541
73725
  };
73542
73726
  for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
@@ -74058,6 +74242,14 @@ stores.inject(MyMetaStore, storeInstance);
74058
74242
  const isBasedBefore = cmd.base < start;
74059
74243
  const deltaCol = isBasedBefore && isCol ? thickness : 0;
74060
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
+ }));
74061
74253
  const target = [
74062
74254
  {
74063
74255
  left: isCol ? start + deltaCol : 0,
@@ -74088,13 +74280,12 @@ stores.inject(MyMetaStore, storeInstance);
74088
74280
  const col = selection.left;
74089
74281
  const row = selection.top;
74090
74282
  this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
74091
- const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
74092
74283
  let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
74093
74284
  const resizingGroups = {};
74094
74285
  for (const element of toRemove) {
74095
- const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
74286
+ const size = originalSize[element];
74096
74287
  const currentSize = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, currentIndex);
74097
- if (size !== currentSize) {
74288
+ if (size && size !== currentSize) {
74098
74289
  resizingGroups[size] ??= [];
74099
74290
  resizingGroups[size].push(currentIndex);
74100
74291
  currentIndex += 1;
@@ -75520,6 +75711,7 @@ stores.inject(MyMetaStore, storeInstance);
75520
75711
 
75521
75712
  autoCompleteProviders.add("dataValidation", {
75522
75713
  displayAllOnInitialContent: true,
75714
+ canBeToggled: false,
75523
75715
  getProposals(tokenAtCursor, content) {
75524
75716
  if (isFormula(content)) {
75525
75717
  return [];
@@ -77085,14 +77277,12 @@ stores.inject(MyMetaStore, storeInstance);
77085
77277
  this.editionState = "initializing";
77086
77278
  }
77087
77279
  stopEdition() {
77088
- const input = this.sheetNameRef.el;
77089
- if (!this.state.isEditing || !input)
77280
+ if (!this.state.isEditing || !this.sheetNameRef.el)
77090
77281
  return;
77091
77282
  this.state.isEditing = false;
77092
77283
  this.editionState = "initializing";
77093
- input.blur();
77284
+ this.sheetNameRef.el.blur();
77094
77285
  const inputValue = this.getInputContent() || "";
77095
- input.innerText = inputValue;
77096
77286
  interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
77097
77287
  }
77098
77288
  cancelEdition() {
@@ -81510,6 +81700,9 @@ stores.inject(MyMetaStore, storeInstance);
81510
81700
  case "combo":
81511
81701
  plot = addComboChart(chart.data);
81512
81702
  break;
81703
+ case "pyramid":
81704
+ plot = addPyramidChart(chart.data);
81705
+ break;
81513
81706
  case "line":
81514
81707
  plot = addLineChart(chart.data);
81515
81708
  break;
@@ -81517,12 +81710,12 @@ stores.inject(MyMetaStore, storeInstance);
81517
81710
  plot = addScatterChart(chart.data);
81518
81711
  break;
81519
81712
  case "pie":
81520
- plot = addDoughnutChart(chart.data, chartSheetIndex, data, { holeSize: 0 });
81713
+ plot = addDoughnutChart(chart.data, chartSheetIndex, data);
81521
81714
  break;
81522
81715
  case "radar":
81523
81716
  plot = addRadarChart(chart.data);
81524
81717
  }
81525
- let position = "t";
81718
+ let position = "none";
81526
81719
  switch (chart.data.legendPosition) {
81527
81720
  case "bottom":
81528
81721
  position = "b";
@@ -81552,7 +81745,7 @@ stores.inject(MyMetaStore, storeInstance);
81552
81745
  ${plot}
81553
81746
  ${shapeProperty({ backgroundColor: chart.data.backgroundColor })}
81554
81747
  </c:plotArea>
81555
- ${addLegend(position, fontColor)}
81748
+ ${position !== "none" ? addLegend(position, fontColor) : ""}
81556
81749
  </c:chart>
81557
81750
  </c:chartSpace>
81558
81751
  `;
@@ -81710,6 +81903,7 @@ stores.inject(MyMetaStore, storeInstance);
81710
81903
  //
81711
81904
  // overlap and gapWitdh seems to be by default at -20 and 20 in chart.js.
81712
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";
81713
81907
  const dataSetsColors = chart.dataSets.map((ds) => ds.backgroundColor ?? "");
81714
81908
  const colors = new ColorGenerator(chart.dataSets.length, dataSetsColors);
81715
81909
  const leftDataSetsNodes = [];
@@ -81746,7 +81940,7 @@ stores.inject(MyMetaStore, storeInstance);
81746
81940
  ${leftDataSetsNodes.length
81747
81941
  ? escapeXml /*xml*/ `
81748
81942
  <c:barChart>
81749
- <c:barDir val="col"/>
81943
+ <c:barDir val="${chartDirection}"/>
81750
81944
  <c:grouping val="${grouping}"/>
81751
81945
  <c:overlap val="${overlap}"/>
81752
81946
  <c:gapWidth val="70"/>
@@ -81756,8 +81950,12 @@ stores.inject(MyMetaStore, storeInstance);
81756
81950
  <c:axId val="${catAxId}" />
81757
81951
  <c:axId val="${valAxId}" />
81758
81952
  </c:barChart>
81759
- ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor)}
81760
- ${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")}
81761
81959
  `
81762
81960
  : ""}
81763
81961
  ${rightDataSetsNodes.length
@@ -81883,7 +82081,7 @@ stores.inject(MyMetaStore, storeInstance);
81883
82081
  : ""}
81884
82082
  ${!useRightAxisForBarSerie || leftDataSetsNodes.length
81885
82083
  ? escapeXml /*xml*/ `
81886
- ${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)}
81887
82085
  ${addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)}
81888
82086
  `
81889
82087
  : ""}
@@ -81895,6 +82093,94 @@ stores.inject(MyMetaStore, storeInstance);
81895
82093
  : ""}
81896
82094
  `;
81897
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
+ }
81898
82184
  function addLineChart(chart) {
81899
82185
  const dataSetsColors = chart.dataSets.map((ds) => ds.backgroundColor ?? "");
81900
82186
  const colors = new ColorGenerator(chart.dataSets.length, dataSetsColors);
@@ -82087,7 +82373,7 @@ stores.inject(MyMetaStore, storeInstance);
82087
82373
  `}
82088
82374
  `;
82089
82375
  }
82090
- function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
82376
+ function addDoughnutChart(chart, chartSheetIndex, data) {
82091
82377
  const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
82092
82378
  const colors = new ColorGenerator(maxLength);
82093
82379
  const doughnutColors = range(0, maxLength).map(() => toXlsxHexColor(colors.next()));
@@ -82125,7 +82411,7 @@ stores.inject(MyMetaStore, storeInstance);
82125
82411
  return escapeXml /*xml*/ `
82126
82412
  <c:doughnutChart>
82127
82413
  <c:varyColors val="1" />
82128
- <c:holeSize val="${holeSize}" />
82414
+ <c:holeSize val="${chart.pieHolePercentage ?? (chart.isDoughnut ? DEFAULT_DOUGHNUT_CHART_HOLE_SIZE : 0)}" />
82129
82415
  ${insertDataLabels()}
82130
82416
  ${joinXmlNodes(dataSetsNodes)}
82131
82417
  </c:doughnutChart>
@@ -82144,25 +82430,35 @@ stores.inject(MyMetaStore, storeInstance);
82144
82430
  </dLbls>
82145
82431
  `;
82146
82432
  }
82147
- 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") {
82148
82434
  // Each Axis present inside a graph needs to be identified by an unsigned integer in order to be referenced by its crossAxis.
82149
82435
  // I.e. x-axis, will reference y-axis and vice-versa.
82150
82436
  const color = title?.color ? toXlsxHexColor(title.color) : defaultFontColor;
82151
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}" />` : "";
82152
82442
  return escapeXml /*xml*/ `
82153
82443
  <${axisName}>
82154
82444
  <c:axId val="${axId}"/>
82155
82445
  <c:crossAx val="${crossAxId}"/> <!-- reference to the other axe of the chart -->
82156
- <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}
82157
82449
  <c:delete val="${deleteAxis}"/> <!-- by default, axis are not displayed -->
82158
82450
  <c:scaling>
82159
- <c:orientation val="minMax" />
82451
+ <c:orientation val="${orientation}" />
82452
+ ${maxValueEl}
82453
+ ${minValueEl}
82160
82454
  </c:scaling>
82455
+ ${majorUnitEl}
82161
82456
  <c:axPos val="${position}" />
82457
+ <c:tickLblPos val="${tickLabelPosition}" />
82162
82458
  ${insertMajorGridLines()}
82163
82459
  <c:majorTickMark val="out" />
82164
82460
  <c:minorTickMark val="none" />
82165
- <c:numFmt formatCode="General" sourceLinked="1" />
82461
+ <c:numFmt formatCode="${format}" sourceLinked="${format === "General" ? "1" : "0"}" />
82166
82462
  <c:title>
82167
82463
  ${insertText(title?.text ?? "", color, fontSize, title)}
82168
82464
  </c:title>
@@ -82357,7 +82653,10 @@ stores.inject(MyMetaStore, storeInstance);
82357
82653
  function addCellIsRule(cf, rule, dxfs) {
82358
82654
  const ruleAttributes = commonCfAttributes(cf);
82359
82655
  const operator = convertOperator(rule.operator);
82360
- ruleAttributes.push(...cellRuleTypeAttributes(rule), ["operator", operator]);
82656
+ ruleAttributes.push(...cellRuleTypeAttributes(rule));
82657
+ if (operator.length) {
82658
+ ruleAttributes.push(["operator", operator]);
82659
+ }
82361
82660
  const formulas = cellRuleFormula(cf.ranges, rule).map((formula) => escapeXml /*xml*/ `<formula>${formula}</formula>`);
82362
82661
  const dxf = {
82363
82662
  font: {
@@ -82403,6 +82702,8 @@ stores.inject(MyMetaStore, storeInstance);
82403
82702
  case "isLessThan":
82404
82703
  case "isLessOrEqualTo":
82405
82704
  return [values[0]];
82705
+ case "customFormula":
82706
+ return values[0].startsWith("=") ? [values[0].slice(1)] : [values[0]];
82406
82707
  case "isBetween":
82407
82708
  case "isNotBetween":
82408
82709
  return [values[0], values[1]];
@@ -82431,6 +82732,8 @@ stores.inject(MyMetaStore, storeInstance);
82431
82732
  case "isBetween":
82432
82733
  case "isNotBetween":
82433
82734
  return [["type", "cellIs"]];
82735
+ case "customFormula":
82736
+ return [["type", "expression"]];
82434
82737
  }
82435
82738
  }
82436
82739
  function addDataBarRule(cf, rule) {
@@ -84464,6 +84767,7 @@ stores.inject(MyMetaStore, storeInstance);
84464
84767
  PivotSidePanelStore,
84465
84768
  PivotMeasureDisplayPanelStore,
84466
84769
  ClientFocusStore,
84770
+ GridRenderer,
84467
84771
  };
84468
84772
  function addFunction(functionName, functionDescription) {
84469
84773
  functionRegistry.add(functionName, functionDescription);
@@ -84530,9 +84834,9 @@ stores.inject(MyMetaStore, storeInstance);
84530
84834
  exports.tokenize = tokenize;
84531
84835
 
84532
84836
 
84533
- __info__.version = "18.5.0-alpha.1";
84534
- __info__.date = "2025-06-27T09:12:49.245Z";
84535
- __info__.hash = "756e75b";
84837
+ __info__.version = "18.5.0-alpha.3";
84838
+ __info__.date = "2025-07-28T13:43:05.981Z";
84839
+ __info__.hash = "53dfee8";
84536
84840
 
84537
84841
 
84538
84842
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);