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

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.4
6
+ * @date 2025-07-30T11:23:18.805Z
7
+ * @hash 34a4ab3
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":
@@ -8625,12 +8639,12 @@
8625
8639
  avg: _t("Average"),
8626
8640
  sum: _t("Sum"),
8627
8641
  };
8628
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8642
+ const DEFAULT_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8629
8643
  const AGGREGATORS_BY_FIELD_TYPE = {
8630
- integer: NUMBER_CHAR_AGGREGATORS,
8631
- char: NUMBER_CHAR_AGGREGATORS,
8644
+ integer: DEFAULT_AGGREGATORS,
8645
+ char: DEFAULT_AGGREGATORS,
8646
+ datetime: DEFAULT_AGGREGATORS,
8632
8647
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8633
- datetime: ["max", "min", "count_distinct", "count"],
8634
8648
  };
8635
8649
  const AGGREGATORS = {};
8636
8650
  for (const type in AGGREGATORS_BY_FIELD_TYPE) {
@@ -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;
19279
19300
  }
19280
- 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
+ }));
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,
@@ -30478,28 +30516,17 @@ stores.inject(MyMetaStore, storeInstance);
30478
30516
  }
30479
30517
  }
30480
30518
 
30481
- class ChartDashboardMenu extends owl.Component {
30482
- static template = "spreadsheet.ChartDashboardMenu";
30483
- static components = { MenuPopover };
30484
- static props = { figureUI: Object };
30519
+ class ChartDashboardMenuStore extends SpreadsheetStore {
30520
+ chartId;
30521
+ mutators = ["reset"];
30485
30522
  originalChartDefinition;
30486
- fullScreenFigureStore;
30487
- menuState = owl.useState({ isOpen: false, anchorRect: null, menuItems: [] });
30488
- setup() {
30489
- super.setup();
30490
- this.fullScreenFigureStore = useStore(FullScreenChartStore);
30491
- this.originalChartDefinition = this.env.model.getters.getChartDefinition(this.props.figureUI.id);
30492
- owl.onWillUpdateProps(({ figureUI }) => {
30493
- if (figureUI.id !== this.props.figureUI.id) {
30494
- this.originalChartDefinition = this.env.model.getters.getChartDefinition(figureUI.id);
30495
- }
30496
- });
30497
- }
30498
- getMenuItems() {
30499
- return [this.fullScreenMenuItem, ...this.changeChartTypeMenuItems].filter(isDefined);
30523
+ constructor(get, chartId) {
30524
+ super(get);
30525
+ this.chartId = chartId;
30526
+ this.originalChartDefinition = this.getters.getChartDefinition(this.chartId);
30500
30527
  }
30501
30528
  get changeChartTypeMenuItems() {
30502
- const definition = this.env.model.getters.getChartDefinition(this.props.figureUI.id);
30529
+ const definition = this.getters.getChartDefinition(this.chartId);
30503
30530
  if (!["line", "bar", "pie"].includes(definition.type)) {
30504
30531
  return [];
30505
30532
  }
@@ -30508,27 +30535,19 @@ stores.inject(MyMetaStore, storeInstance);
30508
30535
  return {
30509
30536
  id: item.chartType,
30510
30537
  label: item.displayName,
30511
- onClick: () => this.onTypeChange(item.chartType),
30512
- isSelected: item.chartType === this.selectedChartType,
30538
+ onClick: () => this.updateType(item.chartType),
30539
+ isSelected: item.chartType === this.getters.getChartDefinition(this.chartId).type,
30513
30540
  iconClass: this.getIconClasses(item.chartType),
30514
30541
  };
30515
30542
  });
30516
30543
  }
30517
- getIconClasses(type) {
30518
- if (type.includes("bar")) {
30519
- return "fa fa-bar-chart";
30520
- }
30521
- if (type.includes("line")) {
30522
- return "fa fa-line-chart";
30523
- }
30524
- if (type.includes("pie")) {
30525
- return "fa fa-pie-chart";
30526
- }
30527
- return "";
30544
+ reset(chartId) {
30545
+ this.chartId = chartId;
30546
+ this.originalChartDefinition = this.getters.getChartDefinition(chartId);
30528
30547
  }
30529
- onTypeChange(type) {
30530
- const figureId = this.props.figureUI.id;
30531
- const currentDefinition = this.env.model.getters.getChartDefinition(figureId);
30548
+ updateType(type) {
30549
+ const figureId = this.chartId;
30550
+ const currentDefinition = this.getters.getChartDefinition(figureId);
30532
30551
  if (currentDefinition.type === type) {
30533
30552
  return;
30534
30553
  }
@@ -30539,7 +30558,7 @@ stores.inject(MyMetaStore, storeInstance);
30539
30558
  else {
30540
30559
  const newChartInfo = chartSubtypeRegistry.get(type);
30541
30560
  const ChartClass = chartRegistry.get(newChartInfo.chartType);
30542
- const chartCreationContext = this.env.model.getters.getContextCreationChart(figureId);
30561
+ const chartCreationContext = this.getters.getContextCreationChart(figureId);
30543
30562
  if (!chartCreationContext)
30544
30563
  return;
30545
30564
  definition = {
@@ -30547,14 +30566,45 @@ stores.inject(MyMetaStore, storeInstance);
30547
30566
  ...newChartInfo.subtypeDefinition,
30548
30567
  };
30549
30568
  }
30550
- this.env.model.dispatch("UPDATE_CHART", {
30569
+ this.model.dispatch("UPDATE_CHART", {
30551
30570
  definition,
30552
30571
  figureId,
30553
- sheetId: this.env.model.getters.getActiveSheetId(),
30572
+ sheetId: this.getters.getActiveSheetId(),
30554
30573
  });
30555
30574
  }
30556
- get selectedChartType() {
30557
- return this.env.model.getters.getChartDefinition(this.props.figureUI.id).type;
30575
+ getIconClasses(type) {
30576
+ if (type.includes("bar")) {
30577
+ return "fa fa-bar-chart";
30578
+ }
30579
+ if (type.includes("line")) {
30580
+ return "fa fa-line-chart";
30581
+ }
30582
+ if (type.includes("pie")) {
30583
+ return "fa fa-pie-chart";
30584
+ }
30585
+ return "";
30586
+ }
30587
+ }
30588
+
30589
+ class ChartDashboardMenu extends owl.Component {
30590
+ static template = "o-spreadsheet-ChartDashboardMenu";
30591
+ static components = { MenuPopover };
30592
+ static props = { figureUI: Object };
30593
+ fullScreenFigureStore;
30594
+ store;
30595
+ menuState = owl.useState({ isOpen: false, anchorRect: null, menuItems: [] });
30596
+ setup() {
30597
+ super.setup();
30598
+ this.store = useLocalStore(ChartDashboardMenuStore, this.props.figureUI.id);
30599
+ this.fullScreenFigureStore = useStore(FullScreenChartStore);
30600
+ owl.onWillUpdateProps(({ figureUI }) => {
30601
+ if (figureUI.id !== this.props.figureUI.id) {
30602
+ this.store.reset(figureUI.id);
30603
+ }
30604
+ });
30605
+ }
30606
+ getMenuItems() {
30607
+ return [this.fullScreenMenuItem, ...this.store.changeChartTypeMenuItems].filter(isDefined);
30558
30608
  }
30559
30609
  get backgroundColor() {
30560
30610
  const color = this.env.model.getters.getChartDefinition(this.props.figureUI.id).background;
@@ -31696,7 +31746,7 @@ stores.inject(MyMetaStore, storeInstance);
31696
31746
  }
31697
31747
  const criterionValues = getters.getDataValidationRangeValues(sheetId, criterion);
31698
31748
  return criterionValues
31699
- .map((value) => value.toLowerCase())
31749
+ .map((value) => value.value.toLowerCase())
31700
31750
  .includes(value.toString().toLowerCase());
31701
31751
  },
31702
31752
  getErrorString: (criterion) => _t("The value must be a value in the range %s", String(criterion.values[0])),
@@ -32607,6 +32657,9 @@ stores.inject(MyMetaStore, storeInstance);
32607
32657
  get isAutoCompleteDisplayed() {
32608
32658
  return !!this.autoComplete.provider;
32609
32659
  }
32660
+ get canBeToggled() {
32661
+ return this.autoComplete.provider?.canBeToggled ?? true;
32662
+ }
32610
32663
  cycleReferences() {
32611
32664
  const locale = this.getters.getLocale();
32612
32665
  const updated = cycleFixedReference(this.composerSelection, this._currentContent, locale);
@@ -33136,6 +33189,7 @@ stores.inject(MyMetaStore, storeInstance);
33136
33189
  proposals,
33137
33190
  selectProposal: provider.selectProposal,
33138
33191
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
33192
+ canBeToggled: provider.canBeToggled,
33139
33193
  };
33140
33194
  }
33141
33195
  if (exactMatch && this._currentContent !== this.initialContent) {
@@ -33158,6 +33212,7 @@ stores.inject(MyMetaStore, storeInstance);
33158
33212
  proposals,
33159
33213
  selectProposal: provider.selectProposal,
33160
33214
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
33215
+ canBeToggled: provider.canBeToggled,
33161
33216
  };
33162
33217
  }
33163
33218
  }
@@ -33728,9 +33783,13 @@ stores.inject(MyMetaStore, storeInstance);
33728
33783
  }
33729
33784
  }
33730
33785
  closeAssistant() {
33786
+ if (!this.props.composerStore.canBeToggled)
33787
+ return;
33731
33788
  this.assistant.forcedClosed = true;
33732
33789
  }
33733
33790
  openAssistant() {
33791
+ if (!this.props.composerStore.canBeToggled)
33792
+ return;
33734
33793
  this.assistant.forcedClosed = false;
33735
33794
  }
33736
33795
  onWheel(event) {
@@ -33920,7 +33979,7 @@ stores.inject(MyMetaStore, storeInstance);
33920
33979
  return [...new Set(argsToFocus)];
33921
33980
  }
33922
33981
  autoComplete(value) {
33923
- if (!value || this.assistant.forcedClosed) {
33982
+ if (!value || (this.assistant.forcedClosed && this.props.composerStore.canBeToggled)) {
33924
33983
  return;
33925
33984
  }
33926
33985
  this.props.composerStore.insertAutoCompleteValue(value);
@@ -37498,7 +37557,7 @@ stores.inject(MyMetaStore, storeInstance);
37498
37557
  /** Conversion map CF types in XLSX <=> Cf types in o_spreadsheet */
37499
37558
  const CF_TYPE_CONVERSION_MAP = {
37500
37559
  aboveAverage: undefined,
37501
- expression: undefined,
37560
+ expression: "customFormula",
37502
37561
  cellIs: undefined, // exist but isn't an operator in o_spreadsheet
37503
37562
  colorScale: undefined, // exist but isn't an operator in o_spreadsheet
37504
37563
  dataBar: undefined,
@@ -38102,7 +38161,6 @@ stores.inject(MyMetaStore, storeInstance);
38102
38161
  case "containsErrors":
38103
38162
  case "notContainsErrors":
38104
38163
  case "duplicateValues":
38105
- case "expression":
38106
38164
  case "top10":
38107
38165
  case "uniqueValues":
38108
38166
  case "timePeriod":
@@ -38135,6 +38193,12 @@ stores.inject(MyMetaStore, storeInstance);
38135
38193
  operator = CF_TYPE_CONVERSION_MAP[rule.type];
38136
38194
  values.push(rule.text);
38137
38195
  break;
38196
+ case "expression":
38197
+ if (!rule.formula?.length)
38198
+ continue;
38199
+ operator = CF_TYPE_CONVERSION_MAP[rule.type];
38200
+ values.push(`=${rule.formula[0]}`);
38201
+ break;
38138
38202
  case "containsBlanks":
38139
38203
  case "notContainsBlanks":
38140
38204
  operator = CF_TYPE_CONVERSION_MAP[rule.type];
@@ -38364,6 +38428,8 @@ stores.inject(MyMetaStore, storeInstance);
38364
38428
  return "equal";
38365
38429
  case "isNotEqual":
38366
38430
  return "notEqual";
38431
+ case "customFormula":
38432
+ return "";
38367
38433
  }
38368
38434
  }
38369
38435
  // -------------------------------------
@@ -38699,6 +38765,9 @@ stores.inject(MyMetaStore, storeInstance);
38699
38765
  aggregated: false,
38700
38766
  cumulative: chartData.cumulative || false,
38701
38767
  labelsAsText: false,
38768
+ horizontal: chartData.horizontal,
38769
+ isDoughnut: chartData.isDoughnut,
38770
+ pieHolePercentage: chartData.pieHolePercentage,
38702
38771
  };
38703
38772
  try {
38704
38773
  const ChartClass = chartRegistry.get(chartData.type);
@@ -39975,6 +40044,12 @@ stores.inject(MyMetaStore, storeInstance);
39975
40044
  const barChartGrouping = this.extractChildAttr(rootChartElement, "c:grouping", "val", {
39976
40045
  default: "clustered",
39977
40046
  }).asString();
40047
+ const chartDirection = this.extractChildAttr(rootChartElement, "c:barDir", "val", {
40048
+ default: "col",
40049
+ }).asString();
40050
+ const chartHoleSize = this.extractChildAttr(rootChartElement, "c:holeSize", "val", {
40051
+ default: "0",
40052
+ }).asNum();
39978
40053
  return {
39979
40054
  title: { text: chartTitle },
39980
40055
  type: CHART_TYPE_CONVERSION_MAP[chartType],
@@ -39988,6 +40063,9 @@ stores.inject(MyMetaStore, storeInstance);
39988
40063
  }).asString()],
39989
40064
  stacked: barChartGrouping === "stacked",
39990
40065
  fontColor: "000000",
40066
+ horizontal: chartDirection === "bar",
40067
+ isDoughnut: chartHoleSize > 0,
40068
+ pieHolePercentage: chartHoleSize,
39991
40069
  };
39992
40070
  })[0];
39993
40071
  }
@@ -50978,6 +51056,9 @@ stores.inject(MyMetaStore, storeInstance);
50978
51056
  pieHolePercentage,
50979
51057
  });
50980
51058
  }
51059
+ get defaultHoleSize() {
51060
+ return DEFAULT_DOUGHNUT_CHART_HOLE_SIZE;
51061
+ }
50981
51062
  }
50982
51063
 
50983
51064
  class RadarChartDesignPanel extends owl.Component {
@@ -52042,7 +52123,8 @@ stores.inject(MyMetaStore, storeInstance);
52042
52123
  static props = {
52043
52124
  editedCf: Object,
52044
52125
  onCancel: Function,
52045
- onSave: Function,
52126
+ onExit: Function,
52127
+ isNewCf: Boolean,
52046
52128
  };
52047
52129
  static components = {
52048
52130
  SelectionInput,
@@ -52067,6 +52149,7 @@ stores.inject(MyMetaStore, storeInstance);
52067
52149
  currentCFType: this.props.editedCf.rule.type,
52068
52150
  ranges: this.props.editedCf.ranges,
52069
52151
  rules: this.getDefaultRules(),
52152
+ hasEditedCf: this.props.isNewCf,
52070
52153
  });
52071
52154
  switch (this.props.editedCf.rule.type) {
52072
52155
  case "CellIsRule":
@@ -52118,6 +52201,9 @@ stores.inject(MyMetaStore, storeInstance);
52118
52201
  ranges: ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
52119
52202
  sheetId,
52120
52203
  });
52204
+ if (result.isSuccessful) {
52205
+ this.state.hasEditedCf = true;
52206
+ }
52121
52207
  const reasons = result.reasons.filter((r) => r !== "NoChanges" /* CommandResult.NoChanges */);
52122
52208
  if (!newCf.suppressErrors) {
52123
52209
  this.state.errors = reasons;
@@ -52139,7 +52225,15 @@ stores.inject(MyMetaStore, storeInstance);
52139
52225
  onSave() {
52140
52226
  const result = this.updateConditionalFormat({});
52141
52227
  if (result.length === 0) {
52142
- this.props.onSave();
52228
+ this.props.onExit();
52229
+ }
52230
+ }
52231
+ onCancel() {
52232
+ if (this.state.hasEditedCf) {
52233
+ this.props.onCancel();
52234
+ }
52235
+ else {
52236
+ this.props.onExit();
52143
52237
  }
52144
52238
  }
52145
52239
  getDefaultRules() {
@@ -55701,9 +55795,15 @@ stores.inject(MyMetaStore, storeInstance);
55701
55795
  return domain.reduce((current, acc) => this.filterDataEntriesFromDomainNode(current, acc), dataEntries);
55702
55796
  }
55703
55797
  filterDataEntriesFromDomainNode(dataEntries, domain) {
55704
- const { field, value } = domain;
55798
+ const { field, value, type } = domain;
55705
55799
  const { nameWithGranularity } = this.getDimension(field);
55706
- return dataEntries.filter((entry) => entry[nameWithGranularity]?.value === value);
55800
+ return dataEntries.filter((entry) => {
55801
+ const cellValue = entry[nameWithGranularity]?.value;
55802
+ if (type === "char") {
55803
+ return String(cellValue) === String(value);
55804
+ }
55805
+ return cellValue === value;
55806
+ });
55707
55807
  }
55708
55808
  getDimension(nameWithGranularity) {
55709
55809
  return this.definition.getDimension(nameWithGranularity);
@@ -65655,7 +65755,7 @@ stores.inject(MyMetaStore, storeInstance);
65655
65755
  const EMPTY_ARRAY = [];
65656
65756
 
65657
65757
  const MAX_ITERATION = 30;
65658
- const ERROR_CYCLE_CELL = Object.freeze(createEvaluatedCell(new CircularDependencyError()));
65758
+ const ERROR_CYCLE_CELL = Object.freeze(createEvaluatedCell({ ...new CircularDependencyError(), origin: undefined }));
65659
65759
  const EMPTY_CELL = Object.freeze(createEvaluatedCell({ value: null }));
65660
65760
  class Evaluator {
65661
65761
  context;
@@ -65889,11 +65989,12 @@ stores.inject(MyMetaStore, storeInstance);
65889
65989
  this.cellsBeingComputed.add(cellId);
65890
65990
  return cell.isFormula
65891
65991
  ? this.computeFormulaCell(position, cell)
65892
- : evaluateLiteral(cell, localeFormat);
65992
+ : evaluateLiteral(cell, localeFormat, position);
65893
65993
  }
65894
65994
  catch (e) {
65895
65995
  e.value = e?.value || CellErrorType.GenericError;
65896
65996
  e.message = e?.message || implementationErrorMessage;
65997
+ e.origin = position;
65897
65998
  return createEvaluatedCell(e);
65898
65999
  }
65899
66000
  finally {
@@ -65910,7 +66011,7 @@ stores.inject(MyMetaStore, storeInstance);
65910
66011
  computeFormulaCell(formulaPosition, cellData) {
65911
66012
  const formulaReturn = updateEvalContextAndExecute(cellData.compiledFormula, this.compilationParams, formulaPosition.sheetId, this.buildSafeGetSymbolValue(), formulaPosition);
65912
66013
  if (!isMatrix(formulaReturn)) {
65913
- const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData);
66014
+ const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData, formulaPosition);
65914
66015
  if (evaluatedCell.type === CellValueType.error) {
65915
66016
  evaluatedCell.errorOriginPosition = formulaReturn.errorOriginPosition ?? formulaPosition;
65916
66017
  }
@@ -65986,7 +66087,7 @@ stores.inject(MyMetaStore, storeInstance);
65986
66087
  const spreadValues = (i, j) => {
65987
66088
  const position = { sheetId, col: i + col, row: j + row };
65988
66089
  const cell = this.getters.getCell(position);
65989
- const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
66090
+ const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell, position);
65990
66091
  if (evaluatedCell.type === CellValueType.error) {
65991
66092
  evaluatedCell.errorOriginPosition = matrixResult[i][j].errorOriginPosition ?? position;
65992
66093
  }
@@ -66659,7 +66760,7 @@ stores.inject(MyMetaStore, storeInstance);
66659
66760
  continue;
66660
66761
  }
66661
66762
  const figureId = figure.id;
66662
- const figureData = this.getters.getChart(figureId)?.getDefinitionForExcel();
66763
+ const figureData = this.getters.getChart(figureId)?.getDefinitionForExcel(this.getters);
66663
66764
  if (figureData) {
66664
66765
  figures.push({
66665
66766
  ...figure,
@@ -67028,8 +67129,16 @@ stores.inject(MyMetaStore, storeInstance);
67028
67129
  }
67029
67130
  getDataValidationRangeValues(sheetId, criterion) {
67030
67131
  const range = this.getters.getRangeFromSheetXC(sheetId, String(criterion.values[0]));
67031
- const criterionValues = this.getters.getRangeValues(range);
67032
- return criterionValues.map((value) => value?.toString()).filter(isDefined);
67132
+ const values = [];
67133
+ const labelsSet = new Set();
67134
+ for (const p of positions(range.zone)) {
67135
+ const cell = this.getters.getEvaluatedCell({ ...p, sheetId: range.sheetId });
67136
+ if (cell.formattedValue && !labelsSet.has(cell.formattedValue)) {
67137
+ labelsSet.add(cell.formattedValue);
67138
+ values.push({ label: cell.formattedValue, value: cell.value?.toString() || "" });
67139
+ }
67140
+ }
67141
+ return values;
67033
67142
  }
67034
67143
  isCellValidCheckbox(cellPosition) {
67035
67144
  if (!this.getters.isMainCellPosition(cellPosition)) {
@@ -67572,6 +67681,23 @@ stores.inject(MyMetaStore, storeInstance);
67572
67681
  static getters = ["getRowSize", "getHeaderSize", "getMaxAnchorOffset"];
67573
67682
  tallestCellInRow = {};
67574
67683
  ctx = document.createElement("canvas").getContext("2d");
67684
+ beforeHandle(cmd) {
67685
+ switch (cmd.type) {
67686
+ // Ensure rows are updated before "UPDATE_CELL" is dispatched from cell plugin.
67687
+ // "UPDATE_CELL" uses the Sheet core plugin to access row data.
67688
+ // If "ADD_COLUMNS_ROWS" has not been processed yet by header_sizes_ui,
67689
+ // size updates may apply to incorrect (pre-insert) rows.
67690
+ case "ADD_COLUMNS_ROWS":
67691
+ if (cmd.dimension === "COL") {
67692
+ return;
67693
+ }
67694
+ const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
67695
+ const newCells = Array(cmd.quantity).fill(undefined);
67696
+ const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
67697
+ this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
67698
+ break;
67699
+ }
67700
+ }
67575
67701
  handle(cmd) {
67576
67702
  switch (cmd.type) {
67577
67703
  case "START":
@@ -67601,16 +67727,6 @@ stores.inject(MyMetaStore, storeInstance);
67601
67727
  this.history.update("tallestCellInRow", cmd.sheetId, tallestCells);
67602
67728
  break;
67603
67729
  }
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
67730
  case "RESIZE_COLUMNS_ROWS":
67615
67731
  {
67616
67732
  const sheetId = cmd.sheetId;
@@ -74159,6 +74275,14 @@ stores.inject(MyMetaStore, storeInstance);
74159
74275
  const isBasedBefore = cmd.base < start;
74160
74276
  const deltaCol = isBasedBefore && isCol ? thickness : 0;
74161
74277
  const deltaRow = isBasedBefore && !isCol ? thickness : 0;
74278
+ const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
74279
+ const originalSize = Object.fromEntries(toRemove.map((element) => {
74280
+ const size = isCol
74281
+ ? this.getters.getColSize(cmd.sheetId, element)
74282
+ : this.getters.getUserRowSize(cmd.sheetId, element);
74283
+ const isDefaultCol = isCol && size === DEFAULT_CELL_WIDTH;
74284
+ return [element, isDefaultCol ? undefined : size];
74285
+ }));
74162
74286
  const target = [
74163
74287
  {
74164
74288
  left: isCol ? start + deltaCol : 0,
@@ -74189,13 +74313,12 @@ stores.inject(MyMetaStore, storeInstance);
74189
74313
  const col = selection.left;
74190
74314
  const row = selection.top;
74191
74315
  this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
74192
- const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
74193
74316
  let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
74194
74317
  const resizingGroups = {};
74195
74318
  for (const element of toRemove) {
74196
- const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
74319
+ const size = originalSize[element];
74197
74320
  const currentSize = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, currentIndex);
74198
- if (size !== currentSize) {
74321
+ if (size && size !== currentSize) {
74199
74322
  resizingGroups[size] ??= [];
74200
74323
  resizingGroups[size].push(currentIndex);
74201
74324
  currentIndex += 1;
@@ -75621,6 +75744,7 @@ stores.inject(MyMetaStore, storeInstance);
75621
75744
 
75622
75745
  autoCompleteProviders.add("dataValidation", {
75623
75746
  displayAllOnInitialContent: true,
75747
+ canBeToggled: false,
75624
75748
  getProposals(tokenAtCursor, content) {
75625
75749
  if (isFormula(content)) {
75626
75750
  return [];
@@ -75636,25 +75760,30 @@ stores.inject(MyMetaStore, storeInstance);
75636
75760
  }
75637
75761
  const sheetId = this.composer.currentEditedCell.sheetId;
75638
75762
  const values = rule.criterion.type === "isValueInRange"
75639
- ? Array.from(new Set(this.getters.getDataValidationRangeValues(sheetId, rule.criterion)))
75640
- : rule.criterion.values;
75763
+ ? this.getters.getDataValidationRangeValues(sheetId, rule.criterion)
75764
+ : rule.criterion.values.map((value) => ({ label: value, value }));
75641
75765
  const isChip = rule.criterion.displayStyle === "chip";
75642
75766
  if (!isChip) {
75643
- return values.map((value) => ({ text: value }));
75767
+ return values.map((value) => ({
75768
+ text: value.value,
75769
+ fuzzySearchKey: value.label,
75770
+ htmlContent: [{ value: value.label }],
75771
+ }));
75644
75772
  }
75645
75773
  const colors = rule.criterion.colors;
75646
75774
  return values.map((value) => {
75647
- const color = colors?.[value];
75775
+ const color = colors?.[value.value];
75648
75776
  return {
75649
- text: value,
75777
+ text: value.value,
75650
75778
  htmlContent: [
75651
75779
  {
75652
- value,
75780
+ value: value.label,
75653
75781
  color: color ? chipTextColor(color) : undefined,
75654
75782
  backgroundColor: color || GRAY_200,
75655
75783
  classes: ["badge rounded-pill fs-6 fw-normal w-100 mt-1 text-start"],
75656
75784
  },
75657
75785
  ],
75786
+ fuzzySearchKey: value.label,
75658
75787
  };
75659
75788
  });
75660
75789
  },
@@ -77186,14 +77315,12 @@ stores.inject(MyMetaStore, storeInstance);
77186
77315
  this.editionState = "initializing";
77187
77316
  }
77188
77317
  stopEdition() {
77189
- const input = this.sheetNameRef.el;
77190
- if (!this.state.isEditing || !input)
77318
+ if (!this.state.isEditing || !this.sheetNameRef.el)
77191
77319
  return;
77192
77320
  this.state.isEditing = false;
77193
77321
  this.editionState = "initializing";
77194
- input.blur();
77322
+ this.sheetNameRef.el.blur();
77195
77323
  const inputValue = this.getInputContent() || "";
77196
- input.innerText = inputValue;
77197
77324
  interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
77198
77325
  }
77199
77326
  cancelEdition() {
@@ -81611,6 +81738,9 @@ stores.inject(MyMetaStore, storeInstance);
81611
81738
  case "combo":
81612
81739
  plot = addComboChart(chart.data);
81613
81740
  break;
81741
+ case "pyramid":
81742
+ plot = addPyramidChart(chart.data);
81743
+ break;
81614
81744
  case "line":
81615
81745
  plot = addLineChart(chart.data);
81616
81746
  break;
@@ -81618,12 +81748,12 @@ stores.inject(MyMetaStore, storeInstance);
81618
81748
  plot = addScatterChart(chart.data);
81619
81749
  break;
81620
81750
  case "pie":
81621
- plot = addDoughnutChart(chart.data, chartSheetIndex, data, { holeSize: 0 });
81751
+ plot = addDoughnutChart(chart.data, chartSheetIndex, data);
81622
81752
  break;
81623
81753
  case "radar":
81624
81754
  plot = addRadarChart(chart.data);
81625
81755
  }
81626
- let position = "t";
81756
+ let position = "none";
81627
81757
  switch (chart.data.legendPosition) {
81628
81758
  case "bottom":
81629
81759
  position = "b";
@@ -81653,7 +81783,7 @@ stores.inject(MyMetaStore, storeInstance);
81653
81783
  ${plot}
81654
81784
  ${shapeProperty({ backgroundColor: chart.data.backgroundColor })}
81655
81785
  </c:plotArea>
81656
- ${addLegend(position, fontColor)}
81786
+ ${position !== "none" ? addLegend(position, fontColor) : ""}
81657
81787
  </c:chart>
81658
81788
  </c:chartSpace>
81659
81789
  `;
@@ -81811,6 +81941,7 @@ stores.inject(MyMetaStore, storeInstance);
81811
81941
  //
81812
81942
  // overlap and gapWitdh seems to be by default at -20 and 20 in chart.js.
81813
81943
  // See https://www.chartjs.org/docs/latest/charts/bar.html and https://www.chartjs.org/docs/latest/charts/bar.html#barpercentage-vs-categorypercentage
81944
+ const chartDirection = chart.horizontal ? "bar" : "col";
81814
81945
  const dataSetsColors = chart.dataSets.map((ds) => ds.backgroundColor ?? "");
81815
81946
  const colors = new ColorGenerator(chart.dataSets.length, dataSetsColors);
81816
81947
  const leftDataSetsNodes = [];
@@ -81847,7 +81978,7 @@ stores.inject(MyMetaStore, storeInstance);
81847
81978
  ${leftDataSetsNodes.length
81848
81979
  ? escapeXml /*xml*/ `
81849
81980
  <c:barChart>
81850
- <c:barDir val="col"/>
81981
+ <c:barDir val="${chartDirection}"/>
81851
81982
  <c:grouping val="${grouping}"/>
81852
81983
  <c:overlap val="${overlap}"/>
81853
81984
  <c:gapWidth val="70"/>
@@ -81857,8 +81988,12 @@ stores.inject(MyMetaStore, storeInstance);
81857
81988
  <c:axId val="${catAxId}" />
81858
81989
  <c:axId val="${valAxId}" />
81859
81990
  </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)}
81991
+ ${chartDirection === "col"
81992
+ ? addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor)
81993
+ : addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.y?.title, chart.fontColor, undefined, "maxMin")}
81994
+ ${chartDirection === "col"
81995
+ ? addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)
81996
+ : addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.x?.title, chart.fontColor, undefined, undefined, "max")}
81862
81997
  `
81863
81998
  : ""}
81864
81999
  ${rightDataSetsNodes.length
@@ -81984,7 +82119,7 @@ stores.inject(MyMetaStore, storeInstance);
81984
82119
  : ""}
81985
82120
  ${!useRightAxisForBarSerie || leftDataSetsNodes.length
81986
82121
  ? escapeXml /*xml*/ `
81987
- ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor, leftDataSetsNodes.length ? 1 : 0)}
82122
+ ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor, 0)}
81988
82123
  ${addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)}
81989
82124
  `
81990
82125
  : ""}
@@ -81996,6 +82131,94 @@ stores.inject(MyMetaStore, storeInstance);
81996
82131
  : ""}
81997
82132
  `;
81998
82133
  }
82134
+ function addPyramidChart(chart) {
82135
+ const dataSets = chart.dataSets;
82136
+ const dataSetsColors = dataSets.map((ds) => ds.backgroundColor ?? "");
82137
+ const colors = new ColorGenerator(dataSets.length, dataSetsColors);
82138
+ const leftDataSet = dataSets[0];
82139
+ const rightDataSet = dataSets[1];
82140
+ const firstColor = toXlsxHexColor(colors.next());
82141
+ const secondColor = toXlsxHexColor(colors.next());
82142
+ const { maxValue, majorUnit } = getPyramidChartHorizontalAxisConfig(chart.maxValue);
82143
+ const labelRangeEl = chart.labelRange
82144
+ ? escapeXml `<c:cat>${stringRef(chart.labelRange)}</c:cat>`
82145
+ : "";
82146
+ const leftBarDataSetNode = escapeXml /*xml*/ `
82147
+ <c:ser>
82148
+ <c:idx val="0"/>
82149
+ <c:order val="0"/>
82150
+ <c:invertIfNegative val="0" />
82151
+ ${extractDataSetLabel(leftDataSet.label)}
82152
+ ${shapeProperty({
82153
+ backgroundColor: firstColor,
82154
+ line: { color: firstColor },
82155
+ })}
82156
+ ${labelRangeEl}
82157
+ <!-- x-coordinate values -->
82158
+ <c:val>
82159
+ ${numberRef(leftDataSet.range)}
82160
+ </c:val>
82161
+ </c:ser>
82162
+ `;
82163
+ const rightBarDataSetNode = escapeXml /*xml*/ `
82164
+ <c:ser>
82165
+ <c:idx val="1"/>
82166
+ <c:order val="1"/>
82167
+ <c:invertIfNegative val="0" />
82168
+ ${extractDataSetLabel(rightDataSet.label)}
82169
+ ${shapeProperty({
82170
+ backgroundColor: secondColor,
82171
+ line: { color: secondColor },
82172
+ })}
82173
+ ${chart.labelRange ? escapeXml /*xml*/ `<c:cat>${stringRef(chart.labelRange)}</c:cat>` : ""}
82174
+ <!-- x-coordinate values -->
82175
+ <c:val>
82176
+ ${numberRef(rightDataSet.range)}
82177
+ </c:val>
82178
+ </c:ser>
82179
+ `;
82180
+ return escapeXml /*xml*/ `
82181
+ <c:barChart>
82182
+ <c:barDir val="bar"/>
82183
+ <c:grouping val="clustered"/>
82184
+ <c:varyColors val="0" />
82185
+ ${leftBarDataSetNode}
82186
+ <c:gapWidth val="50" />
82187
+ <c:axId val="${catAxId}" />
82188
+ <c:axId val="${valAxId}" />
82189
+ </c:barChart>
82190
+ <c:barChart>
82191
+ <c:barDir val="bar"/>
82192
+ <c:grouping val="clustered"/>
82193
+ <c:varyColors val="0" />
82194
+ ${rightBarDataSetNode}
82195
+ <c:gapWidth val="50" />
82196
+ <c:axId val="${secondaryCatAxId}" />
82197
+ <c:axId val="${secondaryValAxId}" />
82198
+ </c:barChart>
82199
+ ${addAx("r", "c:catAx", catAxId, valAxId, chart.axesDesign?.y?.title, chart.fontColor, 0, "maxMin", "autoZero", "high")}
82200
+ ${addAx("b", "c:valAx", valAxId, catAxId, chart.axesDesign?.x?.title, chart.fontColor, 0, "maxMin", "max", "nextTo", maxValue, majorUnit, "#0;#0")}
82201
+ ${addAx("t", "c:valAx", secondaryValAxId, secondaryCatAxId, undefined, chart.fontColor, 1)}
82202
+ ${addAx("l", "c:catAx", secondaryCatAxId, secondaryValAxId, undefined, chart.fontColor, 1, "maxMin")}
82203
+ `;
82204
+ }
82205
+ function getPyramidChartHorizontalAxisConfig(maxValue) {
82206
+ const adjustMaxToDivisibleBy = (value, divisor) => {
82207
+ let adjusted = Math.ceil(value);
82208
+ while (adjusted % divisor !== 0) {
82209
+ adjusted++;
82210
+ }
82211
+ return adjusted;
82212
+ };
82213
+ const tickCount = 4;
82214
+ const interval = tickCount - 1;
82215
+ const adjustedMax = adjustMaxToDivisibleBy(maxValue, interval);
82216
+ const majorUnit = adjustedMax / interval;
82217
+ return {
82218
+ maxValue: adjustedMax,
82219
+ majorUnit,
82220
+ };
82221
+ }
81999
82222
  function addLineChart(chart) {
82000
82223
  const dataSetsColors = chart.dataSets.map((ds) => ds.backgroundColor ?? "");
82001
82224
  const colors = new ColorGenerator(chart.dataSets.length, dataSetsColors);
@@ -82188,7 +82411,7 @@ stores.inject(MyMetaStore, storeInstance);
82188
82411
  `}
82189
82412
  `;
82190
82413
  }
82191
- function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
82414
+ function addDoughnutChart(chart, chartSheetIndex, data) {
82192
82415
  const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
82193
82416
  const colors = new ColorGenerator(maxLength);
82194
82417
  const doughnutColors = range(0, maxLength).map(() => toXlsxHexColor(colors.next()));
@@ -82226,7 +82449,7 @@ stores.inject(MyMetaStore, storeInstance);
82226
82449
  return escapeXml /*xml*/ `
82227
82450
  <c:doughnutChart>
82228
82451
  <c:varyColors val="1" />
82229
- <c:holeSize val="${holeSize}" />
82452
+ <c:holeSize val="${chart.pieHolePercentage ?? (chart.isDoughnut ? DEFAULT_DOUGHNUT_CHART_HOLE_SIZE : 0)}" />
82230
82453
  ${insertDataLabels()}
82231
82454
  ${joinXmlNodes(dataSetsNodes)}
82232
82455
  </c:doughnutChart>
@@ -82245,25 +82468,35 @@ stores.inject(MyMetaStore, storeInstance);
82245
82468
  </dLbls>
82246
82469
  `;
82247
82470
  }
82248
- function addAx(position, axisName, axId, crossAxId, title, defaultFontColor, deleteAxis = 0) {
82471
+ function addAx(position, axisName, axId, crossAxId, title, defaultFontColor, deleteAxis = 0, orientation = "minMax", crossPosition, tickLabelPosition = "nextTo", maxValue, majorUnit, format = "General") {
82249
82472
  // Each Axis present inside a graph needs to be identified by an unsigned integer in order to be referenced by its crossAxis.
82250
82473
  // I.e. x-axis, will reference y-axis and vice-versa.
82251
82474
  const color = title?.color ? toXlsxHexColor(title.color) : defaultFontColor;
82252
82475
  const fontSize = title?.fontSize ?? CHART_AXIS_TITLE_FONT_SIZE;
82476
+ const crossBetweenEl = axisName === "c:valAx" ? escapeXml `<c:crossBetween val="between" />` : "";
82477
+ const maxValueEl = maxValue ? escapeXml `<c:max val="${maxValue}" />` : "";
82478
+ const minValueEl = maxValue ? escapeXml `<c:min val="${-maxValue}" />` : "";
82479
+ const majorUnitEl = majorUnit ? escapeXml `<c:majorUnit val="${majorUnit}" />` : "";
82253
82480
  return escapeXml /*xml*/ `
82254
82481
  <${axisName}>
82255
82482
  <c:axId val="${axId}"/>
82256
82483
  <c:crossAx val="${crossAxId}"/> <!-- reference to the other axe of the chart -->
82257
- <c:crosses val="${position === "b" || position === "l" ? "min" : "max"}"/>
82484
+ <c:crosses val="${crossPosition || (position === "b" || position === "l" ? "min" : "max")}"/>
82485
+ <c:auto val="1"/>
82486
+ ${crossBetweenEl}
82258
82487
  <c:delete val="${deleteAxis}"/> <!-- by default, axis are not displayed -->
82259
82488
  <c:scaling>
82260
- <c:orientation val="minMax" />
82489
+ <c:orientation val="${orientation}" />
82490
+ ${maxValueEl}
82491
+ ${minValueEl}
82261
82492
  </c:scaling>
82493
+ ${majorUnitEl}
82262
82494
  <c:axPos val="${position}" />
82495
+ <c:tickLblPos val="${tickLabelPosition}" />
82263
82496
  ${insertMajorGridLines()}
82264
82497
  <c:majorTickMark val="out" />
82265
82498
  <c:minorTickMark val="none" />
82266
- <c:numFmt formatCode="General" sourceLinked="1" />
82499
+ <c:numFmt formatCode="${format}" sourceLinked="${format === "General" ? "1" : "0"}" />
82267
82500
  <c:title>
82268
82501
  ${insertText(title?.text ?? "", color, fontSize, title)}
82269
82502
  </c:title>
@@ -82458,7 +82691,10 @@ stores.inject(MyMetaStore, storeInstance);
82458
82691
  function addCellIsRule(cf, rule, dxfs) {
82459
82692
  const ruleAttributes = commonCfAttributes(cf);
82460
82693
  const operator = convertOperator(rule.operator);
82461
- ruleAttributes.push(...cellRuleTypeAttributes(rule), ["operator", operator]);
82694
+ ruleAttributes.push(...cellRuleTypeAttributes(rule));
82695
+ if (operator.length) {
82696
+ ruleAttributes.push(["operator", operator]);
82697
+ }
82462
82698
  const formulas = cellRuleFormula(cf.ranges, rule).map((formula) => escapeXml /*xml*/ `<formula>${formula}</formula>`);
82463
82699
  const dxf = {
82464
82700
  font: {
@@ -82504,6 +82740,8 @@ stores.inject(MyMetaStore, storeInstance);
82504
82740
  case "isLessThan":
82505
82741
  case "isLessOrEqualTo":
82506
82742
  return [values[0]];
82743
+ case "customFormula":
82744
+ return values[0].startsWith("=") ? [values[0].slice(1)] : [values[0]];
82507
82745
  case "isBetween":
82508
82746
  case "isNotBetween":
82509
82747
  return [values[0], values[1]];
@@ -82532,6 +82770,8 @@ stores.inject(MyMetaStore, storeInstance);
82532
82770
  case "isBetween":
82533
82771
  case "isNotBetween":
82534
82772
  return [["type", "cellIs"]];
82773
+ case "customFormula":
82774
+ return [["type", "expression"]];
82535
82775
  }
82536
82776
  }
82537
82777
  function addDataBarRule(cf, rule) {
@@ -84517,6 +84757,8 @@ stores.inject(MyMetaStore, storeInstance);
84517
84757
  WaterfallChartDesignPanel,
84518
84758
  ComboChartDesignPanel,
84519
84759
  FunnelChartDesignPanel,
84760
+ SunburstChartDesignPanel,
84761
+ TreeMapChartDesignPanel,
84520
84762
  ChartTypePicker,
84521
84763
  FigureComponent,
84522
84764
  MenuPopover,
@@ -84546,6 +84788,7 @@ stores.inject(MyMetaStore, storeInstance);
84546
84788
  };
84547
84789
  const stores = {
84548
84790
  useStoreProvider,
84791
+ ChartDashboardMenuStore,
84549
84792
  DependencyContainer,
84550
84793
  CellPopoverStore,
84551
84794
  ComposerFocusStore,
@@ -84632,9 +84875,9 @@ stores.inject(MyMetaStore, storeInstance);
84632
84875
  exports.tokenize = tokenize;
84633
84876
 
84634
84877
 
84635
- __info__.version = "18.5.0-alpha.2";
84636
- __info__.date = "2025-07-11T11:13:53.317Z";
84637
- __info__.hash = "6d42178";
84878
+ __info__.version = "18.5.0-alpha.4";
84879
+ __info__.date = "2025-07-30T11:23:18.805Z";
84880
+ __info__.hash = "34a4ab3";
84638
84881
 
84639
84882
 
84640
84883
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);