@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
  'use strict';
@@ -2903,6 +2903,7 @@ const availableConditionalFormatOperators = new Set([
2903
2903
  "isEmpty",
2904
2904
  "isNotEqual",
2905
2905
  "isEqual",
2906
+ "customFormula",
2906
2907
  ]);
2907
2908
 
2908
2909
  const availableDataValidationOperators = new Set([
@@ -5035,11 +5036,11 @@ function isTextFormat(format) {
5035
5036
  }
5036
5037
  }
5037
5038
 
5038
- function evaluateLiteral(literalCell, localeFormat) {
5039
+ function evaluateLiteral(literalCell, localeFormat, position) {
5039
5040
  const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
5040
5041
  ? literalCell.content
5041
5042
  : literalCell.parsedValue;
5042
- const functionResult = { value, format: localeFormat.format };
5043
+ const functionResult = { value, format: localeFormat.format, origin: position };
5043
5044
  return createEvaluatedCell(functionResult, localeFormat.locale);
5044
5045
  }
5045
5046
  function parseLiteral(content, locale) {
@@ -5061,10 +5062,11 @@ function parseLiteral(content, locale) {
5061
5062
  }
5062
5063
  return content;
5063
5064
  }
5064
- function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
5065
+ function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell, origin) {
5065
5066
  const link = detectLink(functionResult.value);
5066
5067
  if (!link) {
5067
- return _createEvaluatedCell(functionResult, locale, cell);
5068
+ const evaluateCell = _createEvaluatedCell(functionResult, locale, cell);
5069
+ return addOrigin(evaluateCell, functionResult.origin ?? origin);
5068
5070
  }
5069
5071
  const value = parseLiteral(link.label, locale);
5070
5072
  const format = functionResult.format ||
@@ -5075,10 +5077,10 @@ function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
5075
5077
  value,
5076
5078
  format,
5077
5079
  };
5078
- return {
5080
+ return addOrigin({
5079
5081
  ..._createEvaluatedCell(linkPayload, locale, cell),
5080
5082
  link,
5081
- };
5083
+ }, functionResult.origin ?? origin);
5082
5084
  }
5083
5085
  function _createEvaluatedCell(functionResult, locale, cell) {
5084
5086
  let { value, format, message } = functionResult;
@@ -5168,6 +5170,17 @@ function errorCell(value, message) {
5168
5170
  defaultAlign: "center",
5169
5171
  };
5170
5172
  }
5173
+ function addOrigin(cell, origin) {
5174
+ if (cell.value === null) {
5175
+ // ignore empty cells to allow sharing the same object instance
5176
+ return cell;
5177
+ }
5178
+ if ("origin" in cell) {
5179
+ return cell;
5180
+ }
5181
+ cell.origin = origin;
5182
+ return cell;
5183
+ }
5171
5184
 
5172
5185
  function toCriterionDateNumber(dateValue) {
5173
5186
  const today = DateTime.now();
@@ -7848,6 +7861,7 @@ function changeCFRuleLocale(rule, changeContentLocale) {
7848
7861
  case "isGreaterOrEqualTo":
7849
7862
  case "isLessThan":
7850
7863
  case "isLessOrEqualTo":
7864
+ case "customFormula":
7851
7865
  rule.values = rule.values.map((v) => changeContentLocale(v));
7852
7866
  return rule;
7853
7867
  case "beginsWithText":
@@ -8626,12 +8640,12 @@ const AGGREGATOR_NAMES = {
8626
8640
  avg: _t("Average"),
8627
8641
  sum: _t("Sum"),
8628
8642
  };
8629
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8643
+ const DEFAULT_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8630
8644
  const AGGREGATORS_BY_FIELD_TYPE = {
8631
- integer: NUMBER_CHAR_AGGREGATORS,
8632
- char: NUMBER_CHAR_AGGREGATORS,
8645
+ integer: DEFAULT_AGGREGATORS,
8646
+ char: DEFAULT_AGGREGATORS,
8647
+ datetime: DEFAULT_AGGREGATORS,
8633
8648
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8634
- datetime: ["max", "min", "count_distinct", "count"],
8635
8649
  };
8636
8650
  const AGGREGATORS = {};
8637
8651
  for (const type in AGGREGATORS_BY_FIELD_TYPE) {
@@ -10536,6 +10550,7 @@ const EXCEL_IMPORT_DEFAULT_NUMBER_OF_ROWS = 100;
10536
10550
  /** The possible values for the XLSX polynomial trendline order are defined by the ST_Order simple type (§21.2.3.29) */
10537
10551
  const MAX_XLSX_POLYNOMIAL_DEGREE = 6;
10538
10552
  const FIRST_NUMFMT_ID = 164;
10553
+ const DEFAULT_DOUGHNUT_CHART_HOLE_SIZE = 50;
10539
10554
  const FORCE_DEFAULT_ARGS_FUNCTIONS = {
10540
10555
  FLOOR: [{ type: "NUMBER", value: 1 }],
10541
10556
  CEILING: [{ type: "NUMBER", value: 1 }],
@@ -19035,13 +19050,19 @@ const COLUMN = {
19035
19050
  if (isEvaluationError(cellReference?.value)) {
19036
19051
  return cellReference;
19037
19052
  }
19038
- const column = cellReference === undefined
19039
- ? this.__originCellPosition?.col
19040
- : toZone(cellReference.value).left;
19041
- if (column === undefined) {
19042
- return new EvaluationError(_t("In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter."));
19053
+ if (cellReference === undefined) {
19054
+ if (this.__originCellPosition?.col === undefined) {
19055
+ return new EvaluationError(_t("In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter."));
19056
+ }
19057
+ return this.__originCellPosition.col + 1;
19058
+ }
19059
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
19060
+ if (zone.left === zone.right) {
19061
+ return zone.left + 1;
19043
19062
  }
19044
- return column + 1;
19063
+ return generateMatrix(zone.right - zone.left + 1, 1, (col, row) => ({
19064
+ value: zone.left + col + 1,
19065
+ }));
19045
19066
  },
19046
19067
  isExported: true,
19047
19068
  };
@@ -19272,13 +19293,19 @@ const ROW = {
19272
19293
  if (isEvaluationError(cellReference?.value)) {
19273
19294
  return cellReference;
19274
19295
  }
19275
- const row = cellReference === undefined
19276
- ? this.__originCellPosition?.row
19277
- : toZone(cellReference.value).top;
19278
- if (row === undefined) {
19279
- return new EvaluationError(_t("In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter."));
19296
+ if (cellReference === undefined) {
19297
+ if (this.__originCellPosition?.row === undefined) {
19298
+ return new EvaluationError(_t("In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter."));
19299
+ }
19300
+ return this.__originCellPosition.row + 1;
19280
19301
  }
19281
- return row + 1;
19302
+ const zone = this.getters.getRangeFromSheetXC(this.getters.getActiveSheetId(), cellReference.value).zone;
19303
+ if (zone.top === zone.bottom) {
19304
+ return zone.top + 1;
19305
+ }
19306
+ return generateMatrix(1, zone.bottom - zone.top + 1, (col, row) => ({
19307
+ value: zone.top + row + 1,
19308
+ }));
19282
19309
  },
19283
19310
  isExported: true,
19284
19311
  };
@@ -22855,6 +22882,16 @@ class AbstractChart {
22855
22882
  static getDefinitionFromContextCreation(context) {
22856
22883
  throw new Error("This method should be implemented by sub class");
22857
22884
  }
22885
+ getCommonDataSetAttributesForExcel(labelRange, dataSets, shouldRemoveFirstLabel) {
22886
+ const excelDataSets = dataSets
22887
+ .map((ds) => toExcelDataset(this.getters, ds))
22888
+ .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
22889
+ const excelLabelRange = toExcelLabelRange(this.getters, labelRange, shouldRemoveFirstLabel);
22890
+ return {
22891
+ dataSets: excelDataSets,
22892
+ labelRange: excelLabelRange,
22893
+ };
22894
+ }
22858
22895
  }
22859
22896
 
22860
22897
  function getBaselineText(baseline, keyValue, baselineMode, humanize, locale) {
@@ -26337,6 +26374,7 @@ class BarChart extends AbstractChart {
26337
26374
  labelRange: context.auxiliaryRange || undefined,
26338
26375
  axesDesign: context.axesDesign,
26339
26376
  showValues: context.showValues,
26377
+ horizontal: context.horizontal,
26340
26378
  };
26341
26379
  }
26342
26380
  getContextCreation() {
@@ -26394,10 +26432,7 @@ class BarChart extends AbstractChart {
26394
26432
  };
26395
26433
  }
26396
26434
  getDefinitionForExcel() {
26397
- const dataSets = this.dataSets
26398
- .map((ds) => toExcelDataset(this.getters, ds))
26399
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
26400
- const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
26435
+ const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
26401
26436
  const definition = this.getDefinition();
26402
26437
  return {
26403
26438
  ...definition,
@@ -26985,10 +27020,7 @@ class ComboChart extends AbstractChart {
26985
27020
  if (this.aggregated) {
26986
27021
  return undefined;
26987
27022
  }
26988
- const dataSets = this.dataSets
26989
- .map((ds) => toExcelDataset(this.getters, ds))
26990
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
26991
- const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
27023
+ const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
26992
27024
  const definition = this.getDefinition();
26993
27025
  return {
26994
27026
  ...definition,
@@ -27727,10 +27759,7 @@ class LineChart extends AbstractChart {
27727
27759
  return new LineChart(definition, this.sheetId, this.getters);
27728
27760
  }
27729
27761
  getDefinitionForExcel() {
27730
- const dataSets = this.dataSets
27731
- .map((ds) => toExcelDataset(this.getters, ds))
27732
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
27733
- const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
27762
+ const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
27734
27763
  const definition = this.getDefinition();
27735
27764
  return {
27736
27765
  ...definition,
@@ -27818,7 +27847,8 @@ class PieChart extends AbstractChart {
27818
27847
  type: "pie",
27819
27848
  labelRange: context.auxiliaryRange || undefined,
27820
27849
  aggregated: context.aggregated ?? false,
27821
- isDoughnut: false,
27850
+ isDoughnut: context.isDoughnut,
27851
+ pieHolePercentage: context.pieHolePercentage,
27822
27852
  showValues: context.showValues,
27823
27853
  };
27824
27854
  }
@@ -27866,10 +27896,7 @@ class PieChart extends AbstractChart {
27866
27896
  return new PieChart(definition, sheetId, this.getters);
27867
27897
  }
27868
27898
  getDefinitionForExcel() {
27869
- const dataSets = this.dataSets
27870
- .map((ds) => toExcelDataset(this.getters, ds))
27871
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
27872
- const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
27899
+ const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
27873
27900
  return {
27874
27901
  ...this.getDefinition(),
27875
27902
  backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
@@ -28014,8 +28041,22 @@ class PyramidChart extends AbstractChart {
28014
28041
  showValues: this.showValues,
28015
28042
  };
28016
28043
  }
28017
- getDefinitionForExcel() {
28018
- return undefined;
28044
+ getDefinitionForExcel(getters) {
28045
+ const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
28046
+ const definition = this.getDefinition();
28047
+ const chartData = getPyramidChartData(definition, this.dataSets, this.labelRange, getters);
28048
+ const { dataSetsValues } = chartData;
28049
+ const maxValue = Math.max(...dataSetsValues.map((dataSet) => Math.max(...dataSet.data.map(Math.abs))));
28050
+ return {
28051
+ ...definition,
28052
+ horizontal: true,
28053
+ backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR),
28054
+ fontColor: toXlsxHexColor(chartFontColor(this.background)),
28055
+ dataSets,
28056
+ labelRange,
28057
+ verticalAxis: getDefinedAxis(definition),
28058
+ maxValue,
28059
+ };
28019
28060
  }
28020
28061
  updateRanges(applyChange) {
28021
28062
  const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
@@ -28158,10 +28199,7 @@ class RadarChart extends AbstractChart {
28158
28199
  if (this.aggregated) {
28159
28200
  return undefined;
28160
28201
  }
28161
- const dataSets = this.dataSets
28162
- .map((ds) => toExcelDataset(this.getters, ds))
28163
- .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference);
28164
- const labelRange = toExcelLabelRange(this.getters, this.labelRange, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
28202
+ const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
28165
28203
  const definition = this.getDefinition();
28166
28204
  return {
28167
28205
  ...definition,
@@ -30479,28 +30517,17 @@ class MenuPopover extends owl.Component {
30479
30517
  }
30480
30518
  }
30481
30519
 
30482
- class ChartDashboardMenu extends owl.Component {
30483
- static template = "spreadsheet.ChartDashboardMenu";
30484
- static components = { MenuPopover };
30485
- static props = { figureUI: Object };
30520
+ class ChartDashboardMenuStore extends SpreadsheetStore {
30521
+ chartId;
30522
+ mutators = ["reset"];
30486
30523
  originalChartDefinition;
30487
- fullScreenFigureStore;
30488
- menuState = owl.useState({ isOpen: false, anchorRect: null, menuItems: [] });
30489
- setup() {
30490
- super.setup();
30491
- this.fullScreenFigureStore = useStore(FullScreenChartStore);
30492
- this.originalChartDefinition = this.env.model.getters.getChartDefinition(this.props.figureUI.id);
30493
- owl.onWillUpdateProps(({ figureUI }) => {
30494
- if (figureUI.id !== this.props.figureUI.id) {
30495
- this.originalChartDefinition = this.env.model.getters.getChartDefinition(figureUI.id);
30496
- }
30497
- });
30498
- }
30499
- getMenuItems() {
30500
- return [this.fullScreenMenuItem, ...this.changeChartTypeMenuItems].filter(isDefined);
30524
+ constructor(get, chartId) {
30525
+ super(get);
30526
+ this.chartId = chartId;
30527
+ this.originalChartDefinition = this.getters.getChartDefinition(this.chartId);
30501
30528
  }
30502
30529
  get changeChartTypeMenuItems() {
30503
- const definition = this.env.model.getters.getChartDefinition(this.props.figureUI.id);
30530
+ const definition = this.getters.getChartDefinition(this.chartId);
30504
30531
  if (!["line", "bar", "pie"].includes(definition.type)) {
30505
30532
  return [];
30506
30533
  }
@@ -30509,27 +30536,19 @@ class ChartDashboardMenu extends owl.Component {
30509
30536
  return {
30510
30537
  id: item.chartType,
30511
30538
  label: item.displayName,
30512
- onClick: () => this.onTypeChange(item.chartType),
30513
- isSelected: item.chartType === this.selectedChartType,
30539
+ onClick: () => this.updateType(item.chartType),
30540
+ isSelected: item.chartType === this.getters.getChartDefinition(this.chartId).type,
30514
30541
  iconClass: this.getIconClasses(item.chartType),
30515
30542
  };
30516
30543
  });
30517
30544
  }
30518
- getIconClasses(type) {
30519
- if (type.includes("bar")) {
30520
- return "fa fa-bar-chart";
30521
- }
30522
- if (type.includes("line")) {
30523
- return "fa fa-line-chart";
30524
- }
30525
- if (type.includes("pie")) {
30526
- return "fa fa-pie-chart";
30527
- }
30528
- return "";
30545
+ reset(chartId) {
30546
+ this.chartId = chartId;
30547
+ this.originalChartDefinition = this.getters.getChartDefinition(chartId);
30529
30548
  }
30530
- onTypeChange(type) {
30531
- const figureId = this.props.figureUI.id;
30532
- const currentDefinition = this.env.model.getters.getChartDefinition(figureId);
30549
+ updateType(type) {
30550
+ const figureId = this.chartId;
30551
+ const currentDefinition = this.getters.getChartDefinition(figureId);
30533
30552
  if (currentDefinition.type === type) {
30534
30553
  return;
30535
30554
  }
@@ -30540,7 +30559,7 @@ class ChartDashboardMenu extends owl.Component {
30540
30559
  else {
30541
30560
  const newChartInfo = chartSubtypeRegistry.get(type);
30542
30561
  const ChartClass = chartRegistry.get(newChartInfo.chartType);
30543
- const chartCreationContext = this.env.model.getters.getContextCreationChart(figureId);
30562
+ const chartCreationContext = this.getters.getContextCreationChart(figureId);
30544
30563
  if (!chartCreationContext)
30545
30564
  return;
30546
30565
  definition = {
@@ -30548,14 +30567,45 @@ class ChartDashboardMenu extends owl.Component {
30548
30567
  ...newChartInfo.subtypeDefinition,
30549
30568
  };
30550
30569
  }
30551
- this.env.model.dispatch("UPDATE_CHART", {
30570
+ this.model.dispatch("UPDATE_CHART", {
30552
30571
  definition,
30553
30572
  figureId,
30554
- sheetId: this.env.model.getters.getActiveSheetId(),
30573
+ sheetId: this.getters.getActiveSheetId(),
30555
30574
  });
30556
30575
  }
30557
- get selectedChartType() {
30558
- return this.env.model.getters.getChartDefinition(this.props.figureUI.id).type;
30576
+ getIconClasses(type) {
30577
+ if (type.includes("bar")) {
30578
+ return "fa fa-bar-chart";
30579
+ }
30580
+ if (type.includes("line")) {
30581
+ return "fa fa-line-chart";
30582
+ }
30583
+ if (type.includes("pie")) {
30584
+ return "fa fa-pie-chart";
30585
+ }
30586
+ return "";
30587
+ }
30588
+ }
30589
+
30590
+ class ChartDashboardMenu extends owl.Component {
30591
+ static template = "o-spreadsheet-ChartDashboardMenu";
30592
+ static components = { MenuPopover };
30593
+ static props = { figureUI: Object };
30594
+ fullScreenFigureStore;
30595
+ store;
30596
+ menuState = owl.useState({ isOpen: false, anchorRect: null, menuItems: [] });
30597
+ setup() {
30598
+ super.setup();
30599
+ this.store = useLocalStore(ChartDashboardMenuStore, this.props.figureUI.id);
30600
+ this.fullScreenFigureStore = useStore(FullScreenChartStore);
30601
+ owl.onWillUpdateProps(({ figureUI }) => {
30602
+ if (figureUI.id !== this.props.figureUI.id) {
30603
+ this.store.reset(figureUI.id);
30604
+ }
30605
+ });
30606
+ }
30607
+ getMenuItems() {
30608
+ return [this.fullScreenMenuItem, ...this.store.changeChartTypeMenuItems].filter(isDefined);
30559
30609
  }
30560
30610
  get backgroundColor() {
30561
30611
  const color = this.env.model.getters.getChartDefinition(this.props.figureUI.id).background;
@@ -31697,7 +31747,7 @@ criterionEvaluatorRegistry.add("isValueInRange", {
31697
31747
  }
31698
31748
  const criterionValues = getters.getDataValidationRangeValues(sheetId, criterion);
31699
31749
  return criterionValues
31700
- .map((value) => value.toLowerCase())
31750
+ .map((value) => value.value.toLowerCase())
31701
31751
  .includes(value.toString().toLowerCase());
31702
31752
  },
31703
31753
  getErrorString: (criterion) => _t("The value must be a value in the range %s", String(criterion.values[0])),
@@ -32608,6 +32658,9 @@ class AbstractComposerStore extends SpreadsheetStore {
32608
32658
  get isAutoCompleteDisplayed() {
32609
32659
  return !!this.autoComplete.provider;
32610
32660
  }
32661
+ get canBeToggled() {
32662
+ return this.autoComplete.provider?.canBeToggled ?? true;
32663
+ }
32611
32664
  cycleReferences() {
32612
32665
  const locale = this.getters.getLocale();
32613
32666
  const updated = cycleFixedReference(this.composerSelection, this._currentContent, locale);
@@ -33137,6 +33190,7 @@ class AbstractComposerStore extends SpreadsheetStore {
33137
33190
  proposals,
33138
33191
  selectProposal: provider.selectProposal,
33139
33192
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
33193
+ canBeToggled: provider.canBeToggled,
33140
33194
  };
33141
33195
  }
33142
33196
  if (exactMatch && this._currentContent !== this.initialContent) {
@@ -33159,6 +33213,7 @@ class AbstractComposerStore extends SpreadsheetStore {
33159
33213
  proposals,
33160
33214
  selectProposal: provider.selectProposal,
33161
33215
  autoSelectFirstProposal: provider.autoSelectFirstProposal ?? false,
33216
+ canBeToggled: provider.canBeToggled,
33162
33217
  };
33163
33218
  }
33164
33219
  }
@@ -33729,9 +33784,13 @@ class Composer extends owl.Component {
33729
33784
  }
33730
33785
  }
33731
33786
  closeAssistant() {
33787
+ if (!this.props.composerStore.canBeToggled)
33788
+ return;
33732
33789
  this.assistant.forcedClosed = true;
33733
33790
  }
33734
33791
  openAssistant() {
33792
+ if (!this.props.composerStore.canBeToggled)
33793
+ return;
33735
33794
  this.assistant.forcedClosed = false;
33736
33795
  }
33737
33796
  onWheel(event) {
@@ -33921,7 +33980,7 @@ class Composer extends owl.Component {
33921
33980
  return [...new Set(argsToFocus)];
33922
33981
  }
33923
33982
  autoComplete(value) {
33924
- if (!value || this.assistant.forcedClosed) {
33983
+ if (!value || (this.assistant.forcedClosed && this.props.composerStore.canBeToggled)) {
33925
33984
  return;
33926
33985
  }
33927
33986
  this.props.composerStore.insertAutoCompleteValue(value);
@@ -37499,7 +37558,7 @@ const CF_OPERATOR_TYPE_CONVERSION_MAP = {
37499
37558
  /** Conversion map CF types in XLSX <=> Cf types in o_spreadsheet */
37500
37559
  const CF_TYPE_CONVERSION_MAP = {
37501
37560
  aboveAverage: undefined,
37502
- expression: undefined,
37561
+ expression: "customFormula",
37503
37562
  cellIs: undefined, // exist but isn't an operator in o_spreadsheet
37504
37563
  colorScale: undefined, // exist but isn't an operator in o_spreadsheet
37505
37564
  dataBar: undefined,
@@ -38103,7 +38162,6 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
38103
38162
  case "containsErrors":
38104
38163
  case "notContainsErrors":
38105
38164
  case "duplicateValues":
38106
- case "expression":
38107
38165
  case "top10":
38108
38166
  case "uniqueValues":
38109
38167
  case "timePeriod":
@@ -38136,6 +38194,12 @@ function convertConditionalFormats(xlsxCfs, dxfs, warningManager) {
38136
38194
  operator = CF_TYPE_CONVERSION_MAP[rule.type];
38137
38195
  values.push(rule.text);
38138
38196
  break;
38197
+ case "expression":
38198
+ if (!rule.formula?.length)
38199
+ continue;
38200
+ operator = CF_TYPE_CONVERSION_MAP[rule.type];
38201
+ values.push(`=${rule.formula[0]}`);
38202
+ break;
38139
38203
  case "containsBlanks":
38140
38204
  case "notContainsBlanks":
38141
38205
  operator = CF_TYPE_CONVERSION_MAP[rule.type];
@@ -38365,6 +38429,8 @@ function convertOperator(operator) {
38365
38429
  return "equal";
38366
38430
  case "isNotEqual":
38367
38431
  return "notEqual";
38432
+ case "customFormula":
38433
+ return "";
38368
38434
  }
38369
38435
  }
38370
38436
  // -------------------------------------
@@ -38700,6 +38766,9 @@ function convertChartData(chartData) {
38700
38766
  aggregated: false,
38701
38767
  cumulative: chartData.cumulative || false,
38702
38768
  labelsAsText: false,
38769
+ horizontal: chartData.horizontal,
38770
+ isDoughnut: chartData.isDoughnut,
38771
+ pieHolePercentage: chartData.pieHolePercentage,
38703
38772
  };
38704
38773
  try {
38705
38774
  const ChartClass = chartRegistry.get(chartData.type);
@@ -39976,6 +40045,12 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
39976
40045
  const barChartGrouping = this.extractChildAttr(rootChartElement, "c:grouping", "val", {
39977
40046
  default: "clustered",
39978
40047
  }).asString();
40048
+ const chartDirection = this.extractChildAttr(rootChartElement, "c:barDir", "val", {
40049
+ default: "col",
40050
+ }).asString();
40051
+ const chartHoleSize = this.extractChildAttr(rootChartElement, "c:holeSize", "val", {
40052
+ default: "0",
40053
+ }).asNum();
39979
40054
  return {
39980
40055
  title: { text: chartTitle },
39981
40056
  type: CHART_TYPE_CONVERSION_MAP[chartType],
@@ -39989,6 +40064,9 @@ class XlsxChartExtractor extends XlsxBaseExtractor {
39989
40064
  }).asString()],
39990
40065
  stacked: barChartGrouping === "stacked",
39991
40066
  fontColor: "000000",
40067
+ horizontal: chartDirection === "bar",
40068
+ isDoughnut: chartHoleSize > 0,
40069
+ pieHolePercentage: chartHoleSize,
39992
40070
  };
39993
40071
  })[0];
39994
40072
  }
@@ -50979,6 +51057,9 @@ class PieChartDesignPanel extends owl.Component {
50979
51057
  pieHolePercentage,
50980
51058
  });
50981
51059
  }
51060
+ get defaultHoleSize() {
51061
+ return DEFAULT_DOUGHNUT_CHART_HOLE_SIZE;
51062
+ }
50982
51063
  }
50983
51064
 
50984
51065
  class RadarChartDesignPanel extends owl.Component {
@@ -52043,7 +52124,8 @@ class ConditionalFormattingEditor extends owl.Component {
52043
52124
  static props = {
52044
52125
  editedCf: Object,
52045
52126
  onCancel: Function,
52046
- onSave: Function,
52127
+ onExit: Function,
52128
+ isNewCf: Boolean,
52047
52129
  };
52048
52130
  static components = {
52049
52131
  SelectionInput,
@@ -52068,6 +52150,7 @@ class ConditionalFormattingEditor extends owl.Component {
52068
52150
  currentCFType: this.props.editedCf.rule.type,
52069
52151
  ranges: this.props.editedCf.ranges,
52070
52152
  rules: this.getDefaultRules(),
52153
+ hasEditedCf: this.props.isNewCf,
52071
52154
  });
52072
52155
  switch (this.props.editedCf.rule.type) {
52073
52156
  case "CellIsRule":
@@ -52119,6 +52202,9 @@ class ConditionalFormattingEditor extends owl.Component {
52119
52202
  ranges: ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
52120
52203
  sheetId,
52121
52204
  });
52205
+ if (result.isSuccessful) {
52206
+ this.state.hasEditedCf = true;
52207
+ }
52122
52208
  const reasons = result.reasons.filter((r) => r !== "NoChanges" /* CommandResult.NoChanges */);
52123
52209
  if (!newCf.suppressErrors) {
52124
52210
  this.state.errors = reasons;
@@ -52140,7 +52226,15 @@ class ConditionalFormattingEditor extends owl.Component {
52140
52226
  onSave() {
52141
52227
  const result = this.updateConditionalFormat({});
52142
52228
  if (result.length === 0) {
52143
- this.props.onSave();
52229
+ this.props.onExit();
52230
+ }
52231
+ }
52232
+ onCancel() {
52233
+ if (this.state.hasEditedCf) {
52234
+ this.props.onCancel();
52235
+ }
52236
+ else {
52237
+ this.props.onExit();
52144
52238
  }
52145
52239
  }
52146
52240
  getDefaultRules() {
@@ -55702,9 +55796,15 @@ class SpreadsheetPivot {
55702
55796
  return domain.reduce((current, acc) => this.filterDataEntriesFromDomainNode(current, acc), dataEntries);
55703
55797
  }
55704
55798
  filterDataEntriesFromDomainNode(dataEntries, domain) {
55705
- const { field, value } = domain;
55799
+ const { field, value, type } = domain;
55706
55800
  const { nameWithGranularity } = this.getDimension(field);
55707
- return dataEntries.filter((entry) => entry[nameWithGranularity]?.value === value);
55801
+ return dataEntries.filter((entry) => {
55802
+ const cellValue = entry[nameWithGranularity]?.value;
55803
+ if (type === "char") {
55804
+ return String(cellValue) === String(value);
55805
+ }
55806
+ return cellValue === value;
55807
+ });
55708
55808
  }
55709
55809
  getDimension(nameWithGranularity) {
55710
55810
  return this.definition.getDimension(nameWithGranularity);
@@ -65656,7 +65756,7 @@ class SpreadingRelation {
65656
65756
  const EMPTY_ARRAY = [];
65657
65757
 
65658
65758
  const MAX_ITERATION = 30;
65659
- const ERROR_CYCLE_CELL = Object.freeze(createEvaluatedCell(new CircularDependencyError()));
65759
+ const ERROR_CYCLE_CELL = Object.freeze(createEvaluatedCell({ ...new CircularDependencyError(), origin: undefined }));
65660
65760
  const EMPTY_CELL = Object.freeze(createEvaluatedCell({ value: null }));
65661
65761
  class Evaluator {
65662
65762
  context;
@@ -65890,11 +65990,12 @@ class Evaluator {
65890
65990
  this.cellsBeingComputed.add(cellId);
65891
65991
  return cell.isFormula
65892
65992
  ? this.computeFormulaCell(position, cell)
65893
- : evaluateLiteral(cell, localeFormat);
65993
+ : evaluateLiteral(cell, localeFormat, position);
65894
65994
  }
65895
65995
  catch (e) {
65896
65996
  e.value = e?.value || CellErrorType.GenericError;
65897
65997
  e.message = e?.message || implementationErrorMessage;
65998
+ e.origin = position;
65898
65999
  return createEvaluatedCell(e);
65899
66000
  }
65900
66001
  finally {
@@ -65911,7 +66012,7 @@ class Evaluator {
65911
66012
  computeFormulaCell(formulaPosition, cellData) {
65912
66013
  const formulaReturn = updateEvalContextAndExecute(cellData.compiledFormula, this.compilationParams, formulaPosition.sheetId, this.buildSafeGetSymbolValue(), formulaPosition);
65913
66014
  if (!isMatrix(formulaReturn)) {
65914
- const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData);
66015
+ const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(formulaReturn), this.getters.getLocale(), cellData, formulaPosition);
65915
66016
  if (evaluatedCell.type === CellValueType.error) {
65916
66017
  evaluatedCell.errorOriginPosition = formulaReturn.errorOriginPosition ?? formulaPosition;
65917
66018
  }
@@ -65987,7 +66088,7 @@ class Evaluator {
65987
66088
  const spreadValues = (i, j) => {
65988
66089
  const position = { sheetId, col: i + col, row: j + row };
65989
66090
  const cell = this.getters.getCell(position);
65990
- const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
66091
+ const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell, position);
65991
66092
  if (evaluatedCell.type === CellValueType.error) {
65992
66093
  evaluatedCell.errorOriginPosition = matrixResult[i][j].errorOriginPosition ?? position;
65993
66094
  }
@@ -66660,7 +66761,7 @@ class EvaluationChartPlugin extends CoreViewPlugin {
66660
66761
  continue;
66661
66762
  }
66662
66763
  const figureId = figure.id;
66663
- const figureData = this.getters.getChart(figureId)?.getDefinitionForExcel();
66764
+ const figureData = this.getters.getChart(figureId)?.getDefinitionForExcel(this.getters);
66664
66765
  if (figureData) {
66665
66766
  figures.push({
66666
66767
  ...figure,
@@ -67029,8 +67130,16 @@ class EvaluationDataValidationPlugin extends CoreViewPlugin {
67029
67130
  }
67030
67131
  getDataValidationRangeValues(sheetId, criterion) {
67031
67132
  const range = this.getters.getRangeFromSheetXC(sheetId, String(criterion.values[0]));
67032
- const criterionValues = this.getters.getRangeValues(range);
67033
- return criterionValues.map((value) => value?.toString()).filter(isDefined);
67133
+ const values = [];
67134
+ const labelsSet = new Set();
67135
+ for (const p of positions(range.zone)) {
67136
+ const cell = this.getters.getEvaluatedCell({ ...p, sheetId: range.sheetId });
67137
+ if (cell.formattedValue && !labelsSet.has(cell.formattedValue)) {
67138
+ labelsSet.add(cell.formattedValue);
67139
+ values.push({ label: cell.formattedValue, value: cell.value?.toString() || "" });
67140
+ }
67141
+ }
67142
+ return values;
67034
67143
  }
67035
67144
  isCellValidCheckbox(cellPosition) {
67036
67145
  if (!this.getters.isMainCellPosition(cellPosition)) {
@@ -67573,6 +67682,23 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
67573
67682
  static getters = ["getRowSize", "getHeaderSize", "getMaxAnchorOffset"];
67574
67683
  tallestCellInRow = {};
67575
67684
  ctx = document.createElement("canvas").getContext("2d");
67685
+ beforeHandle(cmd) {
67686
+ switch (cmd.type) {
67687
+ // Ensure rows are updated before "UPDATE_CELL" is dispatched from cell plugin.
67688
+ // "UPDATE_CELL" uses the Sheet core plugin to access row data.
67689
+ // If "ADD_COLUMNS_ROWS" has not been processed yet by header_sizes_ui,
67690
+ // size updates may apply to incorrect (pre-insert) rows.
67691
+ case "ADD_COLUMNS_ROWS":
67692
+ if (cmd.dimension === "COL") {
67693
+ return;
67694
+ }
67695
+ const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
67696
+ const newCells = Array(cmd.quantity).fill(undefined);
67697
+ const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
67698
+ this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
67699
+ break;
67700
+ }
67701
+ }
67576
67702
  handle(cmd) {
67577
67703
  switch (cmd.type) {
67578
67704
  case "START":
@@ -67602,16 +67728,6 @@ class HeaderSizeUIPlugin extends CoreViewPlugin {
67602
67728
  this.history.update("tallestCellInRow", cmd.sheetId, tallestCells);
67603
67729
  break;
67604
67730
  }
67605
- case "ADD_COLUMNS_ROWS": {
67606
- if (cmd.dimension === "COL") {
67607
- return;
67608
- }
67609
- const addIndex = getAddHeaderStartIndex(cmd.position, cmd.base);
67610
- const newCells = Array(cmd.quantity).fill(undefined);
67611
- const newTallestCells = insertItemsAtIndex(this.tallestCellInRow[cmd.sheetId], newCells, addIndex);
67612
- this.history.update("tallestCellInRow", cmd.sheetId, newTallestCells);
67613
- break;
67614
- }
67615
67731
  case "RESIZE_COLUMNS_ROWS":
67616
67732
  {
67617
67733
  const sheetId = cmd.sheetId;
@@ -74160,6 +74276,14 @@ class GridSelectionPlugin extends UIPlugin {
74160
74276
  const isBasedBefore = cmd.base < start;
74161
74277
  const deltaCol = isBasedBefore && isCol ? thickness : 0;
74162
74278
  const deltaRow = isBasedBefore && !isCol ? thickness : 0;
74279
+ const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
74280
+ const originalSize = Object.fromEntries(toRemove.map((element) => {
74281
+ const size = isCol
74282
+ ? this.getters.getColSize(cmd.sheetId, element)
74283
+ : this.getters.getUserRowSize(cmd.sheetId, element);
74284
+ const isDefaultCol = isCol && size === DEFAULT_CELL_WIDTH;
74285
+ return [element, isDefaultCol ? undefined : size];
74286
+ }));
74163
74287
  const target = [
74164
74288
  {
74165
74289
  left: isCol ? start + deltaCol : 0,
@@ -74190,13 +74314,12 @@ class GridSelectionPlugin extends UIPlugin {
74190
74314
  const col = selection.left;
74191
74315
  const row = selection.top;
74192
74316
  this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
74193
- const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
74194
74317
  let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
74195
74318
  const resizingGroups = {};
74196
74319
  for (const element of toRemove) {
74197
- const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
74320
+ const size = originalSize[element];
74198
74321
  const currentSize = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, currentIndex);
74199
- if (size !== currentSize) {
74322
+ if (size && size !== currentSize) {
74200
74323
  resizingGroups[size] ??= [];
74201
74324
  resizingGroups[size].push(currentIndex);
74202
74325
  currentIndex += 1;
@@ -75622,6 +75745,7 @@ const coreViewsPluginRegistry = new Registry()
75622
75745
 
75623
75746
  autoCompleteProviders.add("dataValidation", {
75624
75747
  displayAllOnInitialContent: true,
75748
+ canBeToggled: false,
75625
75749
  getProposals(tokenAtCursor, content) {
75626
75750
  if (isFormula(content)) {
75627
75751
  return [];
@@ -75637,25 +75761,30 @@ autoCompleteProviders.add("dataValidation", {
75637
75761
  }
75638
75762
  const sheetId = this.composer.currentEditedCell.sheetId;
75639
75763
  const values = rule.criterion.type === "isValueInRange"
75640
- ? Array.from(new Set(this.getters.getDataValidationRangeValues(sheetId, rule.criterion)))
75641
- : rule.criterion.values;
75764
+ ? this.getters.getDataValidationRangeValues(sheetId, rule.criterion)
75765
+ : rule.criterion.values.map((value) => ({ label: value, value }));
75642
75766
  const isChip = rule.criterion.displayStyle === "chip";
75643
75767
  if (!isChip) {
75644
- return values.map((value) => ({ text: value }));
75768
+ return values.map((value) => ({
75769
+ text: value.value,
75770
+ fuzzySearchKey: value.label,
75771
+ htmlContent: [{ value: value.label }],
75772
+ }));
75645
75773
  }
75646
75774
  const colors = rule.criterion.colors;
75647
75775
  return values.map((value) => {
75648
- const color = colors?.[value];
75776
+ const color = colors?.[value.value];
75649
75777
  return {
75650
- text: value,
75778
+ text: value.value,
75651
75779
  htmlContent: [
75652
75780
  {
75653
- value,
75781
+ value: value.label,
75654
75782
  color: color ? chipTextColor(color) : undefined,
75655
75783
  backgroundColor: color || GRAY_200,
75656
75784
  classes: ["badge rounded-pill fs-6 fw-normal w-100 mt-1 text-start"],
75657
75785
  },
75658
75786
  ],
75787
+ fuzzySearchKey: value.label,
75659
75788
  };
75660
75789
  });
75661
75790
  },
@@ -77187,14 +77316,12 @@ class BottomBarSheet extends owl.Component {
77187
77316
  this.editionState = "initializing";
77188
77317
  }
77189
77318
  stopEdition() {
77190
- const input = this.sheetNameRef.el;
77191
- if (!this.state.isEditing || !input)
77319
+ if (!this.state.isEditing || !this.sheetNameRef.el)
77192
77320
  return;
77193
77321
  this.state.isEditing = false;
77194
77322
  this.editionState = "initializing";
77195
- input.blur();
77323
+ this.sheetNameRef.el.blur();
77196
77324
  const inputValue = this.getInputContent() || "";
77197
- input.innerText = inputValue;
77198
77325
  interactiveRenameSheet(this.env, this.props.sheetId, inputValue, () => this.startEdition());
77199
77326
  }
77200
77327
  cancelEdition() {
@@ -81612,6 +81739,9 @@ function createChart(chart, chartSheetIndex, data) {
81612
81739
  case "combo":
81613
81740
  plot = addComboChart(chart.data);
81614
81741
  break;
81742
+ case "pyramid":
81743
+ plot = addPyramidChart(chart.data);
81744
+ break;
81615
81745
  case "line":
81616
81746
  plot = addLineChart(chart.data);
81617
81747
  break;
@@ -81619,12 +81749,12 @@ function createChart(chart, chartSheetIndex, data) {
81619
81749
  plot = addScatterChart(chart.data);
81620
81750
  break;
81621
81751
  case "pie":
81622
- plot = addDoughnutChart(chart.data, chartSheetIndex, data, { holeSize: 0 });
81752
+ plot = addDoughnutChart(chart.data, chartSheetIndex, data);
81623
81753
  break;
81624
81754
  case "radar":
81625
81755
  plot = addRadarChart(chart.data);
81626
81756
  }
81627
- let position = "t";
81757
+ let position = "none";
81628
81758
  switch (chart.data.legendPosition) {
81629
81759
  case "bottom":
81630
81760
  position = "b";
@@ -81654,7 +81784,7 @@ function createChart(chart, chartSheetIndex, data) {
81654
81784
  ${plot}
81655
81785
  ${shapeProperty({ backgroundColor: chart.data.backgroundColor })}
81656
81786
  </c:plotArea>
81657
- ${addLegend(position, fontColor)}
81787
+ ${position !== "none" ? addLegend(position, fontColor) : ""}
81658
81788
  </c:chart>
81659
81789
  </c:chartSpace>
81660
81790
  `;
@@ -81812,6 +81942,7 @@ function addBarChart(chart) {
81812
81942
  //
81813
81943
  // overlap and gapWitdh seems to be by default at -20 and 20 in chart.js.
81814
81944
  // See https://www.chartjs.org/docs/latest/charts/bar.html and https://www.chartjs.org/docs/latest/charts/bar.html#barpercentage-vs-categorypercentage
81945
+ const chartDirection = chart.horizontal ? "bar" : "col";
81815
81946
  const dataSetsColors = chart.dataSets.map((ds) => ds.backgroundColor ?? "");
81816
81947
  const colors = new ColorGenerator(chart.dataSets.length, dataSetsColors);
81817
81948
  const leftDataSetsNodes = [];
@@ -81848,7 +81979,7 @@ function addBarChart(chart) {
81848
81979
  ${leftDataSetsNodes.length
81849
81980
  ? escapeXml /*xml*/ `
81850
81981
  <c:barChart>
81851
- <c:barDir val="col"/>
81982
+ <c:barDir val="${chartDirection}"/>
81852
81983
  <c:grouping val="${grouping}"/>
81853
81984
  <c:overlap val="${overlap}"/>
81854
81985
  <c:gapWidth val="70"/>
@@ -81858,8 +81989,12 @@ function addBarChart(chart) {
81858
81989
  <c:axId val="${catAxId}" />
81859
81990
  <c:axId val="${valAxId}" />
81860
81991
  </c:barChart>
81861
- ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor)}
81862
- ${addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)}
81992
+ ${chartDirection === "col"
81993
+ ? addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor)
81994
+ : addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.y?.title, chart.fontColor, undefined, "maxMin")}
81995
+ ${chartDirection === "col"
81996
+ ? addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)
81997
+ : addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.x?.title, chart.fontColor, undefined, undefined, "max")}
81863
81998
  `
81864
81999
  : ""}
81865
82000
  ${rightDataSetsNodes.length
@@ -81985,7 +82120,7 @@ function addComboChart(chart) {
81985
82120
  : ""}
81986
82121
  ${!useRightAxisForBarSerie || leftDataSetsNodes.length
81987
82122
  ? escapeXml /*xml*/ `
81988
- ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor, leftDataSetsNodes.length ? 1 : 0)}
82123
+ ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor, 0)}
81989
82124
  ${addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)}
81990
82125
  `
81991
82126
  : ""}
@@ -81997,6 +82132,94 @@ function addComboChart(chart) {
81997
82132
  : ""}
81998
82133
  `;
81999
82134
  }
82135
+ function addPyramidChart(chart) {
82136
+ const dataSets = chart.dataSets;
82137
+ const dataSetsColors = dataSets.map((ds) => ds.backgroundColor ?? "");
82138
+ const colors = new ColorGenerator(dataSets.length, dataSetsColors);
82139
+ const leftDataSet = dataSets[0];
82140
+ const rightDataSet = dataSets[1];
82141
+ const firstColor = toXlsxHexColor(colors.next());
82142
+ const secondColor = toXlsxHexColor(colors.next());
82143
+ const { maxValue, majorUnit } = getPyramidChartHorizontalAxisConfig(chart.maxValue);
82144
+ const labelRangeEl = chart.labelRange
82145
+ ? escapeXml `<c:cat>${stringRef(chart.labelRange)}</c:cat>`
82146
+ : "";
82147
+ const leftBarDataSetNode = escapeXml /*xml*/ `
82148
+ <c:ser>
82149
+ <c:idx val="0"/>
82150
+ <c:order val="0"/>
82151
+ <c:invertIfNegative val="0" />
82152
+ ${extractDataSetLabel(leftDataSet.label)}
82153
+ ${shapeProperty({
82154
+ backgroundColor: firstColor,
82155
+ line: { color: firstColor },
82156
+ })}
82157
+ ${labelRangeEl}
82158
+ <!-- x-coordinate values -->
82159
+ <c:val>
82160
+ ${numberRef(leftDataSet.range)}
82161
+ </c:val>
82162
+ </c:ser>
82163
+ `;
82164
+ const rightBarDataSetNode = escapeXml /*xml*/ `
82165
+ <c:ser>
82166
+ <c:idx val="1"/>
82167
+ <c:order val="1"/>
82168
+ <c:invertIfNegative val="0" />
82169
+ ${extractDataSetLabel(rightDataSet.label)}
82170
+ ${shapeProperty({
82171
+ backgroundColor: secondColor,
82172
+ line: { color: secondColor },
82173
+ })}
82174
+ ${chart.labelRange ? escapeXml /*xml*/ `<c:cat>${stringRef(chart.labelRange)}</c:cat>` : ""}
82175
+ <!-- x-coordinate values -->
82176
+ <c:val>
82177
+ ${numberRef(rightDataSet.range)}
82178
+ </c:val>
82179
+ </c:ser>
82180
+ `;
82181
+ return escapeXml /*xml*/ `
82182
+ <c:barChart>
82183
+ <c:barDir val="bar"/>
82184
+ <c:grouping val="clustered"/>
82185
+ <c:varyColors val="0" />
82186
+ ${leftBarDataSetNode}
82187
+ <c:gapWidth val="50" />
82188
+ <c:axId val="${catAxId}" />
82189
+ <c:axId val="${valAxId}" />
82190
+ </c:barChart>
82191
+ <c:barChart>
82192
+ <c:barDir val="bar"/>
82193
+ <c:grouping val="clustered"/>
82194
+ <c:varyColors val="0" />
82195
+ ${rightBarDataSetNode}
82196
+ <c:gapWidth val="50" />
82197
+ <c:axId val="${secondaryCatAxId}" />
82198
+ <c:axId val="${secondaryValAxId}" />
82199
+ </c:barChart>
82200
+ ${addAx("r", "c:catAx", catAxId, valAxId, chart.axesDesign?.y?.title, chart.fontColor, 0, "maxMin", "autoZero", "high")}
82201
+ ${addAx("b", "c:valAx", valAxId, catAxId, chart.axesDesign?.x?.title, chart.fontColor, 0, "maxMin", "max", "nextTo", maxValue, majorUnit, "#0;#0")}
82202
+ ${addAx("t", "c:valAx", secondaryValAxId, secondaryCatAxId, undefined, chart.fontColor, 1)}
82203
+ ${addAx("l", "c:catAx", secondaryCatAxId, secondaryValAxId, undefined, chart.fontColor, 1, "maxMin")}
82204
+ `;
82205
+ }
82206
+ function getPyramidChartHorizontalAxisConfig(maxValue) {
82207
+ const adjustMaxToDivisibleBy = (value, divisor) => {
82208
+ let adjusted = Math.ceil(value);
82209
+ while (adjusted % divisor !== 0) {
82210
+ adjusted++;
82211
+ }
82212
+ return adjusted;
82213
+ };
82214
+ const tickCount = 4;
82215
+ const interval = tickCount - 1;
82216
+ const adjustedMax = adjustMaxToDivisibleBy(maxValue, interval);
82217
+ const majorUnit = adjustedMax / interval;
82218
+ return {
82219
+ maxValue: adjustedMax,
82220
+ majorUnit,
82221
+ };
82222
+ }
82000
82223
  function addLineChart(chart) {
82001
82224
  const dataSetsColors = chart.dataSets.map((ds) => ds.backgroundColor ?? "");
82002
82225
  const colors = new ColorGenerator(chart.dataSets.length, dataSetsColors);
@@ -82189,7 +82412,7 @@ function addRadarChart(chart) {
82189
82412
  `}
82190
82413
  `;
82191
82414
  }
82192
- function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSize: 50 }) {
82415
+ function addDoughnutChart(chart, chartSheetIndex, data) {
82193
82416
  const maxLength = largeMax(chart.dataSets.map((ds) => getRangeSize(ds.range, chartSheetIndex, data)));
82194
82417
  const colors = new ColorGenerator(maxLength);
82195
82418
  const doughnutColors = range(0, maxLength).map(() => toXlsxHexColor(colors.next()));
@@ -82227,7 +82450,7 @@ function addDoughnutChart(chart, chartSheetIndex, data, { holeSize } = { holeSiz
82227
82450
  return escapeXml /*xml*/ `
82228
82451
  <c:doughnutChart>
82229
82452
  <c:varyColors val="1" />
82230
- <c:holeSize val="${holeSize}" />
82453
+ <c:holeSize val="${chart.pieHolePercentage ?? (chart.isDoughnut ? DEFAULT_DOUGHNUT_CHART_HOLE_SIZE : 0)}" />
82231
82454
  ${insertDataLabels()}
82232
82455
  ${joinXmlNodes(dataSetsNodes)}
82233
82456
  </c:doughnutChart>
@@ -82246,25 +82469,35 @@ function insertDataLabels({ showLeaderLines } = { showLeaderLines: false }) {
82246
82469
  </dLbls>
82247
82470
  `;
82248
82471
  }
82249
- function addAx(position, axisName, axId, crossAxId, title, defaultFontColor, deleteAxis = 0) {
82472
+ function addAx(position, axisName, axId, crossAxId, title, defaultFontColor, deleteAxis = 0, orientation = "minMax", crossPosition, tickLabelPosition = "nextTo", maxValue, majorUnit, format = "General") {
82250
82473
  // Each Axis present inside a graph needs to be identified by an unsigned integer in order to be referenced by its crossAxis.
82251
82474
  // I.e. x-axis, will reference y-axis and vice-versa.
82252
82475
  const color = title?.color ? toXlsxHexColor(title.color) : defaultFontColor;
82253
82476
  const fontSize = title?.fontSize ?? CHART_AXIS_TITLE_FONT_SIZE;
82477
+ const crossBetweenEl = axisName === "c:valAx" ? escapeXml `<c:crossBetween val="between" />` : "";
82478
+ const maxValueEl = maxValue ? escapeXml `<c:max val="${maxValue}" />` : "";
82479
+ const minValueEl = maxValue ? escapeXml `<c:min val="${-maxValue}" />` : "";
82480
+ const majorUnitEl = majorUnit ? escapeXml `<c:majorUnit val="${majorUnit}" />` : "";
82254
82481
  return escapeXml /*xml*/ `
82255
82482
  <${axisName}>
82256
82483
  <c:axId val="${axId}"/>
82257
82484
  <c:crossAx val="${crossAxId}"/> <!-- reference to the other axe of the chart -->
82258
- <c:crosses val="${position === "b" || position === "l" ? "min" : "max"}"/>
82485
+ <c:crosses val="${crossPosition || (position === "b" || position === "l" ? "min" : "max")}"/>
82486
+ <c:auto val="1"/>
82487
+ ${crossBetweenEl}
82259
82488
  <c:delete val="${deleteAxis}"/> <!-- by default, axis are not displayed -->
82260
82489
  <c:scaling>
82261
- <c:orientation val="minMax" />
82490
+ <c:orientation val="${orientation}" />
82491
+ ${maxValueEl}
82492
+ ${minValueEl}
82262
82493
  </c:scaling>
82494
+ ${majorUnitEl}
82263
82495
  <c:axPos val="${position}" />
82496
+ <c:tickLblPos val="${tickLabelPosition}" />
82264
82497
  ${insertMajorGridLines()}
82265
82498
  <c:majorTickMark val="out" />
82266
82499
  <c:minorTickMark val="none" />
82267
- <c:numFmt formatCode="General" sourceLinked="1" />
82500
+ <c:numFmt formatCode="${format}" sourceLinked="${format === "General" ? "1" : "0"}" />
82268
82501
  <c:title>
82269
82502
  ${insertText(title?.text ?? "", color, fontSize, title)}
82270
82503
  </c:title>
@@ -82459,7 +82692,10 @@ function addConditionalFormatting(dxfs, conditionalFormats) {
82459
82692
  function addCellIsRule(cf, rule, dxfs) {
82460
82693
  const ruleAttributes = commonCfAttributes(cf);
82461
82694
  const operator = convertOperator(rule.operator);
82462
- ruleAttributes.push(...cellRuleTypeAttributes(rule), ["operator", operator]);
82695
+ ruleAttributes.push(...cellRuleTypeAttributes(rule));
82696
+ if (operator.length) {
82697
+ ruleAttributes.push(["operator", operator]);
82698
+ }
82463
82699
  const formulas = cellRuleFormula(cf.ranges, rule).map((formula) => escapeXml /*xml*/ `<formula>${formula}</formula>`);
82464
82700
  const dxf = {
82465
82701
  font: {
@@ -82505,6 +82741,8 @@ function cellRuleFormula(ranges, rule) {
82505
82741
  case "isLessThan":
82506
82742
  case "isLessOrEqualTo":
82507
82743
  return [values[0]];
82744
+ case "customFormula":
82745
+ return values[0].startsWith("=") ? [values[0].slice(1)] : [values[0]];
82508
82746
  case "isBetween":
82509
82747
  case "isNotBetween":
82510
82748
  return [values[0], values[1]];
@@ -82533,6 +82771,8 @@ function cellRuleTypeAttributes(rule) {
82533
82771
  case "isBetween":
82534
82772
  case "isNotBetween":
82535
82773
  return [["type", "cellIs"]];
82774
+ case "customFormula":
82775
+ return [["type", "expression"]];
82536
82776
  }
82537
82777
  }
82538
82778
  function addDataBarRule(cf, rule) {
@@ -84518,6 +84758,8 @@ const components = {
84518
84758
  WaterfallChartDesignPanel,
84519
84759
  ComboChartDesignPanel,
84520
84760
  FunnelChartDesignPanel,
84761
+ SunburstChartDesignPanel,
84762
+ TreeMapChartDesignPanel,
84521
84763
  ChartTypePicker,
84522
84764
  FigureComponent,
84523
84765
  MenuPopover,
@@ -84547,6 +84789,7 @@ const hooks = {
84547
84789
  };
84548
84790
  const stores = {
84549
84791
  useStoreProvider,
84792
+ ChartDashboardMenuStore,
84550
84793
  DependencyContainer,
84551
84794
  CellPopoverStore,
84552
84795
  ComposerFocusStore,
@@ -84633,6 +84876,6 @@ exports.tokenColors = tokenColors;
84633
84876
  exports.tokenize = tokenize;
84634
84877
 
84635
84878
 
84636
- __info__.version = "18.5.0-alpha.2";
84637
- __info__.date = "2025-07-11T11:13:53.317Z";
84638
- __info__.hash = "6d42178";
84879
+ __info__.version = "18.5.0-alpha.4";
84880
+ __info__.date = "2025-07-30T11:23:18.805Z";
84881
+ __info__.hash = "34a4ab3";