@odoo/o-spreadsheet 19.0.11 → 19.0.15

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 19.0.11
6
- * @date 2025-11-24T07:46:47.685Z
7
- * @hash f5bdbcc
5
+ * @version 19.0.15
6
+ * @date 2025-12-26T10:19:23.408Z
7
+ * @hash fe625c9
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -182,7 +182,6 @@
182
182
  "REDO",
183
183
  "ADD_MERGE",
184
184
  "REMOVE_MERGE",
185
- "DUPLICATE_SHEET",
186
185
  "UPDATE_LOCALE",
187
186
  "ADD_PIVOT",
188
187
  "UPDATE_PIVOT",
@@ -1895,21 +1894,25 @@
1895
1894
  function profilesContainsZone(profilesStartingPosition, profiles, zone) {
1896
1895
  const leftValue = zone.left;
1897
1896
  const rightValue = zone.right;
1898
- const topValue = zone.top;
1899
- const bottomValue = zone.bottom + 1;
1900
1897
  const leftIndex = binaryPredecessorSearch(profilesStartingPosition, leftValue, 0);
1901
- const rightIndex = binaryPredecessorSearch(profilesStartingPosition, rightValue, leftIndex);
1902
- if (leftIndex === -1 || rightIndex === -1) {
1903
- return false;
1904
- }
1898
+ const rightIndex = rightValue === undefined
1899
+ ? profilesStartingPosition.length - 1
1900
+ : binaryPredecessorSearch(profilesStartingPosition, rightValue, leftIndex);
1901
+ /**
1902
+ * The `profilesStartingPosition` array always contains at least the value `0` at its first position,
1903
+ * ensuring that applying `binaryPredecessorSearch` will always return a valid index.
1904
+ * Therefore, it is not necessary to check if the result of `binaryPredecessorSearch` equals `-1`.
1905
+ */
1906
+ const topValue = zone.top;
1907
+ const bottomValue = zone.bottom === undefined ? undefined : zone.bottom + 1;
1905
1908
  for (let i = leftIndex; i <= rightIndex; i++) {
1906
1909
  const profile = profiles.get(profilesStartingPosition[i]);
1907
- const topPredIndex = binaryPredecessorSearch(profile, topValue, 0, true);
1908
- const bottomSuccIndex = binarySuccessorSearch(profile, bottomValue, 0, true);
1910
+ const topPredIndex = binaryPredecessorSearch(profile, topValue, 0);
1909
1911
  if (topPredIndex === -1 || topPredIndex % 2 !== 0) {
1910
1912
  return false;
1911
1913
  }
1912
- if (topValue < profile[topPredIndex] || bottomValue > profile[bottomSuccIndex]) {
1914
+ const bottomSuccIndex = bottomValue === undefined ? profile.length : binarySuccessorSearch(profile, bottomValue, 0);
1915
+ if (topPredIndex + 1 !== bottomSuccIndex) {
1913
1916
  return false;
1914
1917
  }
1915
1918
  }
@@ -6595,17 +6598,41 @@
6595
6598
  const today = DateTime.now();
6596
6599
  switch (dateValue) {
6597
6600
  case "today":
6598
- return jsDateToNumber(today);
6599
- case "yesterday":
6600
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
6601
- case "tomorrow":
6602
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
6601
+ return Math.floor(jsDateToNumber(today));
6602
+ case "yesterday": {
6603
+ today.setDate(today.getDate() - 1);
6604
+ return Math.floor(jsDateToNumber(today));
6605
+ }
6606
+ case "tomorrow": {
6607
+ today.setDate(today.getDate() + 1);
6608
+ return Math.floor(jsDateToNumber(today));
6609
+ }
6603
6610
  case "lastWeek":
6604
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
6605
- case "lastMonth":
6606
- return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
6611
+ today.setDate(today.getDate() - 6);
6612
+ return Math.floor(jsDateToNumber(today));
6613
+ case "lastMonth": {
6614
+ const lastMonth = today.getMonth() === 0 ? 11 : today.getMonth() - 1;
6615
+ const dateInLastMonth = new DateTime(today.getFullYear(), lastMonth, 1);
6616
+ if (today.getDate() > getDaysInMonth(dateInLastMonth)) {
6617
+ today.setDate(1);
6618
+ }
6619
+ else {
6620
+ today.setDate(today.getDate() + 1);
6621
+ today.setMonth(today.getMonth() - 1);
6622
+ }
6623
+ return Math.floor(jsDateToNumber(today));
6624
+ }
6607
6625
  case "lastYear":
6608
- return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
6626
+ // Handle leap year case
6627
+ if (today.getMonth() === 1 && today.getDate() === 29) {
6628
+ today.setDate(28);
6629
+ today.setFullYear(today.getFullYear() - 1);
6630
+ }
6631
+ else {
6632
+ today.setDate(today.getDate() + 1);
6633
+ today.setFullYear(today.getFullYear() - 1);
6634
+ }
6635
+ return Math.floor(jsDateToNumber(today));
6609
6636
  }
6610
6637
  }
6611
6638
  /** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
@@ -6778,67 +6805,6 @@
6778
6805
  return sheetName !== undefined ? `${getCanonicalSymbolName(sheetName)}!${xc}` : xc;
6779
6806
  }
6780
6807
 
6781
- function createDefaultRows(rowNumber) {
6782
- const rows = [];
6783
- for (let i = 0; i < rowNumber; i++) {
6784
- const row = {
6785
- cells: {},
6786
- };
6787
- rows.push(row);
6788
- }
6789
- return rows;
6790
- }
6791
- function moveHeaderIndexesOnHeaderAddition(indexHeaderAdded, numberAdded, headers) {
6792
- return headers.map((header) => {
6793
- if (header >= indexHeaderAdded) {
6794
- return header + numberAdded;
6795
- }
6796
- return header;
6797
- });
6798
- }
6799
- function moveHeaderIndexesOnHeaderDeletion(deletedHeaders, headers) {
6800
- deletedHeaders = [...deletedHeaders].sort((a, b) => b - a);
6801
- return headers
6802
- .map((header) => {
6803
- for (const deletedHeader of deletedHeaders) {
6804
- if (header > deletedHeader) {
6805
- header--;
6806
- }
6807
- else if (header === deletedHeader) {
6808
- return undefined;
6809
- }
6810
- }
6811
- return header;
6812
- })
6813
- .filter(isDefined);
6814
- }
6815
- function getNextSheetName(existingNames, baseName = "Sheet") {
6816
- let i = 1;
6817
- let name = `${baseName}${i}`;
6818
- while (existingNames.includes(name)) {
6819
- name = `${baseName}${i}`;
6820
- i++;
6821
- }
6822
- return name;
6823
- }
6824
- function getDuplicateSheetName(nameToDuplicate, existingNames) {
6825
- let i = 1;
6826
- const baseName = _t("Copy of %s", nameToDuplicate);
6827
- let name = baseName.toString();
6828
- while (existingNames.includes(name)) {
6829
- name = `${baseName} (${i})`;
6830
- i++;
6831
- }
6832
- return name;
6833
- }
6834
- function isSheetNameEqual(name1, name2) {
6835
- if (name1 === undefined || name2 === undefined) {
6836
- return false;
6837
- }
6838
- return (getUnquotedSheetName(name1.trim().toUpperCase()) ===
6839
- getUnquotedSheetName(name2.trim().toUpperCase()));
6840
- }
6841
-
6842
6808
  function createRange(args, getSheetSize) {
6843
6809
  const unboundedZone = args.zone;
6844
6810
  const zone = boundUnboundedZone(unboundedZone, getSheetSize(args.sheetId));
@@ -7086,7 +7052,7 @@
7086
7052
  elements.sort((a, b) => b - a);
7087
7053
  const groups = groupConsecutive(elements);
7088
7054
  return (range) => {
7089
- if (!isSheetNameEqual(range.sheetId, cmd.sheetId)) {
7055
+ if (range.sheetId !== cmd.sheetId) {
7090
7056
  return { changeType: "NONE" };
7091
7057
  }
7092
7058
  let newRange = range;
@@ -7293,6 +7259,69 @@
7293
7259
  return results.map((r) => r.elem);
7294
7260
  }
7295
7261
 
7262
+ function createDefaultRows(rowNumber) {
7263
+ const rows = [];
7264
+ for (let i = 0; i < rowNumber; i++) {
7265
+ const row = {
7266
+ cells: {},
7267
+ };
7268
+ rows.push(row);
7269
+ }
7270
+ return rows;
7271
+ }
7272
+ function moveHeaderIndexesOnHeaderAddition(indexHeaderAdded, numberAdded, headers) {
7273
+ return headers.map((header) => {
7274
+ if (header >= indexHeaderAdded) {
7275
+ return header + numberAdded;
7276
+ }
7277
+ return header;
7278
+ });
7279
+ }
7280
+ function moveHeaderIndexesOnHeaderDeletion(deletedHeaders, headers) {
7281
+ deletedHeaders = [...deletedHeaders].sort((a, b) => b - a);
7282
+ return headers
7283
+ .map((header) => {
7284
+ for (const deletedHeader of deletedHeaders) {
7285
+ if (header > deletedHeader) {
7286
+ header--;
7287
+ }
7288
+ else if (header === deletedHeader) {
7289
+ return undefined;
7290
+ }
7291
+ }
7292
+ return header;
7293
+ })
7294
+ .filter(isDefined);
7295
+ }
7296
+ function getNextSheetName(existingNames, baseName = "Sheet") {
7297
+ let i = 1;
7298
+ let name = `${baseName}${i}`;
7299
+ while (existingNames.includes(name)) {
7300
+ name = `${baseName}${i}`;
7301
+ i++;
7302
+ }
7303
+ return name;
7304
+ }
7305
+ function getDuplicateSheetName(nameToDuplicate, existingNames) {
7306
+ let i = 1;
7307
+ const baseName = _t("Copy of %s", nameToDuplicate);
7308
+ let name = baseName.toString();
7309
+ while (existingNames.includes(name)) {
7310
+ name = `${baseName} (${i})`;
7311
+ i++;
7312
+ }
7313
+ return name;
7314
+ }
7315
+ const toStandardizedSheetName = memoize(function toStandardizedSheetName(name) {
7316
+ return getUnquotedSheetName(name.trim().toUpperCase());
7317
+ });
7318
+ function isSheetNameEqual(name1, name2) {
7319
+ if (name1 === undefined || name2 === undefined) {
7320
+ return false;
7321
+ }
7322
+ return toStandardizedSheetName(name1) === toStandardizedSheetName(name2);
7323
+ }
7324
+
7296
7325
  function computeTextLinesHeight(textLineHeight, numberOfLines = 1) {
7297
7326
  return numberOfLines * (textLineHeight + MIN_CELL_TEXT_MARGIN) - MIN_CELL_TEXT_MARGIN;
7298
7327
  }
@@ -23878,6 +23907,9 @@ stores.inject(MyMetaStore, storeInstance);
23878
23907
  }));
23879
23908
  }
23880
23909
  function getTreeMapColorScale(tree, coloringOption) {
23910
+ if (tree.length === 0) {
23911
+ return undefined;
23912
+ }
23881
23913
  const treeNodesByLevel = pyramidizeTree(tree);
23882
23914
  const nodes = treeNodesByLevel[treeNodesByLevel.length - 1];
23883
23915
  const minValue = Math.min(...nodes.map((node) => node.value));
@@ -25563,11 +25595,18 @@ stores.inject(MyMetaStore, storeInstance);
25563
25595
  // we have to add the canvas to the DOM otherwise it won't be rendered
25564
25596
  document.body.append(div);
25565
25597
  if ("chartJsConfig" in runtime) {
25598
+ const extensionsLoaded = areChartJSExtensionsLoaded();
25599
+ if (!extensionsLoaded) {
25600
+ registerChartJSExtensions();
25601
+ }
25566
25602
  const config = deepCopy(runtime.chartJsConfig);
25567
25603
  config.plugins = [backgroundColorChartJSPlugin];
25568
25604
  const chart = new window.Chart(canvas, config);
25569
25605
  imageContent = chart.toBase64Image();
25570
25606
  chart.destroy();
25607
+ if (!extensionsLoaded) {
25608
+ unregisterChartJsExtensions();
25609
+ }
25571
25610
  }
25572
25611
  else if (type === "scorecard") {
25573
25612
  const design = getScorecardConfiguration(figure, runtime);
@@ -25597,11 +25636,18 @@ stores.inject(MyMetaStore, storeInstance);
25597
25636
  document.body.append(div);
25598
25637
  let chartBlob = null;
25599
25638
  if ("chartJsConfig" in runtime) {
25639
+ const extensionsLoaded = areChartJSExtensionsLoaded();
25640
+ if (!extensionsLoaded) {
25641
+ registerChartJSExtensions();
25642
+ }
25600
25643
  const config = deepCopy(runtime.chartJsConfig);
25601
25644
  config.plugins = [backgroundColorChartJSPlugin];
25602
25645
  const chart = new window.Chart(canvas, config);
25603
25646
  chartBlob = await new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
25604
25647
  chart.destroy();
25648
+ if (!extensionsLoaded) {
25649
+ unregisterChartJsExtensions();
25650
+ }
25605
25651
  }
25606
25652
  else if (type === "scorecard") {
25607
25653
  const design = getScorecardConfiguration(figure, runtime);
@@ -27727,29 +27773,19 @@ stores.inject(MyMetaStore, storeInstance);
27727
27773
  }
27728
27774
  resetAxisLimits(chartId, limits) {
27729
27775
  for (const axisId of ZOOMABLE_AXIS_IDS) {
27730
- if (limits?.[axisId]) {
27731
- if (!this.originalAxisLimits[chartId]?.[axisId]) {
27732
- this.originalAxisLimits[chartId] = {
27733
- ...this.originalAxisLimits[chartId],
27734
- [axisId]: {},
27735
- };
27736
- }
27737
- this.originalAxisLimits[chartId][axisId]["min"] = limits[axisId].min;
27738
- this.originalAxisLimits[chartId][axisId]["max"] = limits[axisId].max;
27776
+ if (limits[axisId]) {
27777
+ this.originalAxisLimits[chartId] = {
27778
+ ...this.originalAxisLimits[chartId],
27779
+ [axisId]: { ...limits[axisId] },
27780
+ };
27739
27781
  }
27740
- else {
27741
- if (this.originalAxisLimits[chartId]?.[axisId]) {
27742
- delete this.originalAxisLimits[chartId][axisId];
27743
- }
27782
+ else if (this.originalAxisLimits[chartId]?.[axisId]) {
27783
+ delete this.originalAxisLimits[chartId][axisId];
27744
27784
  }
27745
27785
  }
27746
27786
  return "noStateChange";
27747
27787
  }
27748
27788
  updateAxisLimits(chartId, limits) {
27749
- if (limits === undefined) {
27750
- delete this.currentAxesLimits[chartId];
27751
- return "noStateChange";
27752
- }
27753
27789
  let { min, max } = limits;
27754
27790
  if (min > max) {
27755
27791
  [min, max] = [max, min];
@@ -27765,26 +27801,14 @@ stores.inject(MyMetaStore, storeInstance);
27765
27801
  * for the current trend line axes.
27766
27802
  */
27767
27803
  updateTrendLineConfiguration(chartId) {
27768
- if (!this.originalAxisLimits[chartId]) {
27804
+ if (!this.originalAxisLimits[chartId]?.x || !this.currentAxesLimits[chartId]?.x) {
27769
27805
  return "noStateChange";
27770
27806
  }
27771
27807
  const chartLimits = this.originalAxisLimits[chartId].x;
27772
- if (chartLimits === undefined) {
27773
- return "noStateChange";
27774
- }
27775
27808
  for (const axisId of TREND_LINE_AXES_IDS) {
27776
27809
  if (!this.originalAxisLimits[chartId][axisId]) {
27777
27810
  continue;
27778
27811
  }
27779
- if (!this.currentAxesLimits[chartId]?.[axisId]) {
27780
- this.currentAxesLimits[chartId] = {
27781
- ...this.currentAxesLimits[chartId],
27782
- [axisId]: {},
27783
- };
27784
- }
27785
- if (this.currentAxesLimits[chartId]?.x === undefined) {
27786
- return "noStateChange";
27787
- }
27788
27812
  const realRange = chartLimits.max - chartLimits.min;
27789
27813
  const trendingLimits = this.originalAxisLimits[chartId][axisId];
27790
27814
  const trendingRange = trendingLimits.max - trendingLimits.min;
@@ -27792,8 +27816,10 @@ stores.inject(MyMetaStore, storeInstance);
27792
27816
  const intercept = trendingLimits.min - chartLimits.min * slope;
27793
27817
  const newXMin = this.currentAxesLimits[chartId].x.min;
27794
27818
  const newXMax = this.currentAxesLimits[chartId].x.max;
27795
- this.currentAxesLimits[chartId][axisId].min = newXMin * slope + intercept;
27796
- this.currentAxesLimits[chartId][axisId].max = newXMax * slope + intercept;
27819
+ this.currentAxesLimits[chartId][axisId] = {
27820
+ min: newXMin * slope + intercept,
27821
+ max: newXMax * slope + intercept,
27822
+ };
27797
27823
  }
27798
27824
  return "noStateChange";
27799
27825
  }
@@ -27862,8 +27888,9 @@ stores.inject(MyMetaStore, storeInstance);
27862
27888
  hasLinearScale;
27863
27889
  isBarChart;
27864
27890
  chartId = "";
27865
- datasetBoundaries = { xMin: 0, xMax: 0 };
27891
+ datasetBoundaries = { min: 0, max: 0 };
27866
27892
  removeEventListeners = () => { };
27893
+ isMasterChartAllowed = false;
27867
27894
  setup() {
27868
27895
  this.store = useStore(ZoomableChartStore);
27869
27896
  super.setup();
@@ -27879,12 +27906,19 @@ stores.inject(MyMetaStore, storeInstance);
27879
27906
  height:${height};
27880
27907
  `;
27881
27908
  }
27909
+ get masterChartContainerStyle() {
27910
+ const runtime = this.env.model.getters.getChartRuntime(this.props.chartId);
27911
+ if (runtime && !runtime.chartJsConfig.data.datasets.some((ds) => ds.data.length > 1)) {
27912
+ return "opacity: 0.3;";
27913
+ }
27914
+ return "";
27915
+ }
27882
27916
  get sliceable() {
27883
27917
  if (this.props.isFullScreen) {
27884
27918
  return true;
27885
27919
  }
27886
27920
  const definition = this.env.model.getters.getChartDefinition(this.props.chartId);
27887
- return ("zoomable" in definition && definition?.zoomable) ?? false;
27921
+ return ("zoomable" in definition && definition.zoomable) ?? false;
27888
27922
  }
27889
27923
  get axisOffset() {
27890
27924
  return !this.hasLinearScale && this.isBarChart ? 0.5 : 0;
@@ -27909,15 +27943,13 @@ stores.inject(MyMetaStore, storeInstance);
27909
27943
  if (!this.sliceable) {
27910
27944
  return chartData;
27911
27945
  }
27912
- const xAxis = this.store.currentAxesLimits[this.chartId]?.x;
27913
- const xScale = {
27914
- ...chartData.options.scales?.x,
27915
- };
27916
- if (xAxis?.min !== undefined) {
27917
- xScale.min = this.hasLinearScale ? xAxis.min : Math.ceil(xAxis.min) - this.axisOffset;
27918
- }
27919
- if (xAxis?.max !== undefined) {
27920
- xScale.max = this.hasLinearScale ? xAxis.max : Math.floor(xAxis.max) - this.axisOffset;
27946
+ let x = chartData.options.scales.x;
27947
+ const limits = this.store.currentAxesLimits[this.chartId]?.x;
27948
+ if (limits) {
27949
+ x = {
27950
+ ...x,
27951
+ ...this.getStoredBoundaries(),
27952
+ };
27921
27953
  }
27922
27954
  return {
27923
27955
  ...chartData,
@@ -27925,7 +27957,7 @@ stores.inject(MyMetaStore, storeInstance);
27925
27957
  ...chartData.options,
27926
27958
  scales: {
27927
27959
  ...chartData.options.scales,
27928
- x: xScale,
27960
+ x,
27929
27961
  },
27930
27962
  layout: {
27931
27963
  ...chartData.options.layout,
@@ -27940,9 +27972,19 @@ stores.inject(MyMetaStore, storeInstance);
27940
27972
  getAxisLimitsFromDataset(chartData) {
27941
27973
  const data = chartData.data.datasets.map((ds) => ds.data).flat();
27942
27974
  const xValues = data.map((d, i) => (typeof d === "object" && d !== null ? d.x : i));
27943
- const xMin = Math.min(...xValues);
27944
- const xMax = Math.max(...xValues);
27945
- return { xMin, xMax };
27975
+ const min = Math.min(...xValues);
27976
+ const max = Math.max(...xValues);
27977
+ return { min, max };
27978
+ }
27979
+ setMasterChartCursor(runtime) {
27980
+ const masterElement = this.masterChartCanvas?.el;
27981
+ if (runtime && !runtime.chartJsConfig.data.datasets.some((ds) => ds.data.length > 1)) {
27982
+ masterElement.style.cursor = "not-allowed";
27983
+ this.isMasterChartAllowed = false;
27984
+ return;
27985
+ }
27986
+ masterElement.style.cursor = "default";
27987
+ this.isMasterChartAllowed = true;
27946
27988
  }
27947
27989
  createChart(chartRuntime) {
27948
27990
  const chartData = chartRuntime.chartJsConfig;
@@ -27954,12 +27996,14 @@ stores.inject(MyMetaStore, storeInstance);
27954
27996
  chartRuntime.chartJsConfig = updatedData;
27955
27997
  }
27956
27998
  super.createChart(chartRuntime);
27957
- this.hasLinearScale = this.chart?.scales?.x.type === "linear";
27999
+ this.hasLinearScale = this.chart?.scales?.x?.type === "linear";
27958
28000
  if (!this.sliceable || !("masterChartConfig" in chartRuntime)) {
28001
+ this.isMasterChartAllowed = false;
27959
28002
  return;
27960
28003
  }
27961
28004
  this.masterChart?.destroy();
27962
- const masterChartCtx = this.masterChartCanvas.el.getContext("2d");
28005
+ const masterChartCtx = (this.masterChartCanvas?.el).getContext("2d");
28006
+ this.setMasterChartCursor(chartRuntime);
27963
28007
  this.masterChart = new window.Chart(masterChartCtx, this.getMasterChartConfiguration(chartRuntime["masterChartConfig"]));
27964
28008
  this.resetAxesLimits();
27965
28009
  if (this.chart?.options) {
@@ -27968,11 +28012,10 @@ stores.inject(MyMetaStore, storeInstance);
27968
28012
  }
27969
28013
  updateChartJs(chartRuntime) {
27970
28014
  const chartData = chartRuntime.chartJsConfig;
27971
- const newDatasetBoundaries = this.getAxisLimitsFromDataset(chartData);
27972
- if (this.datasetBoundaries.xMin !== newDatasetBoundaries.xMin ||
27973
- this.datasetBoundaries.xMax !== newDatasetBoundaries.xMax) {
28015
+ const { min, max } = this.getAxisLimitsFromDataset(chartData);
28016
+ if (this.datasetBoundaries.min !== min || this.datasetBoundaries.max !== max) {
27974
28017
  this.store.clearAxisLimits(this.chartId);
27975
- this.datasetBoundaries = newDatasetBoundaries;
28018
+ this.datasetBoundaries = { min, max };
27976
28019
  }
27977
28020
  this.isBarChart = chartData?.type === "bar";
27978
28021
  this.chartId = `${chartData.type}-${this.props.chartId}`;
@@ -27981,9 +28024,10 @@ stores.inject(MyMetaStore, storeInstance);
27981
28024
  chartRuntime.chartJsConfig = updatedData;
27982
28025
  }
27983
28026
  super.updateChartJs(chartRuntime);
27984
- this.hasLinearScale = this.chart?.scales?.x.type === "linear";
28027
+ this.hasLinearScale = this.chart?.scales?.x?.type === "linear";
27985
28028
  if (!this.sliceable || !("masterChartConfig" in chartRuntime)) {
27986
28029
  this.masterChart = undefined;
28030
+ this.isMasterChartAllowed = false;
27987
28031
  }
27988
28032
  else {
27989
28033
  const masterChartConfig = this.getMasterChartConfiguration(chartRuntime["masterChartConfig"]);
@@ -27996,6 +28040,7 @@ stores.inject(MyMetaStore, storeInstance);
27996
28040
  this.masterChart.config.options = masterChartConfig.options;
27997
28041
  this.masterChart.update();
27998
28042
  }
28043
+ this.setMasterChartCursor(chartRuntime);
27999
28044
  }
28000
28045
  this.resetAxesLimits();
28001
28046
  if (this.chart?.options) {
@@ -28006,18 +28051,15 @@ stores.inject(MyMetaStore, storeInstance);
28006
28051
  if (!this.chart) {
28007
28052
  return;
28008
28053
  }
28009
- const previousAxisLimits = this.store.originalAxisLimits[this.chartId];
28010
- if (previousAxisLimits?.x?.min === undefined && previousAxisLimits?.x?.max === undefined) {
28054
+ const storedLimits = this.store.originalAxisLimits[this.chartId]?.x;
28055
+ if (!storedLimits) {
28011
28056
  let scales = this.masterChart
28012
28057
  ? this.masterChart.scales
28013
28058
  : this.chart.scales;
28014
- if (!this.hasLinearScale && scales?.x) {
28059
+ if (!this.hasLinearScale && scales.x) {
28015
28060
  scales = {
28016
28061
  ...scales,
28017
- x: {
28018
- min: Math.ceil(scales.x.min) - this.axisOffset,
28019
- max: Math.floor(scales.x.max) + this.axisOffset,
28020
- },
28062
+ x: this.adjustBoundaries(scales.x),
28021
28063
  };
28022
28064
  }
28023
28065
  this.store.resetAxisLimits(this.chartId, scales);
@@ -28032,13 +28074,13 @@ stores.inject(MyMetaStore, storeInstance);
28032
28074
  }
28033
28075
  updateTrendingLineAxes() {
28034
28076
  this.store.updateTrendLineConfiguration(this.chartId);
28035
- const config = this.store.currentAxesLimits[this.chartId];
28077
+ const limits = this.store.currentAxesLimits[this.chartId];
28036
28078
  for (const axisId of [TREND_LINE_XAXIS_ID, MOVING_AVERAGE_TREND_LINE_XAXIS_ID]) {
28037
- if (!this.chart?.config.options?.scales?.[axisId] || !config?.[axisId]) {
28079
+ if (!this.chart?.config?.options?.scales?.[axisId] || !limits?.[axisId]) {
28038
28080
  continue;
28039
28081
  }
28040
- this.chart.config.options.scales[axisId].min = config[axisId].min;
28041
- this.chart.config.options.scales[axisId].max = config[axisId].max;
28082
+ this.chart.config.options.scales[axisId].min = limits[axisId].min;
28083
+ this.chart.config.options.scales[axisId].max = limits[axisId].max;
28042
28084
  }
28043
28085
  }
28044
28086
  get upperBound() {
@@ -28081,29 +28123,71 @@ stores.inject(MyMetaStore, storeInstance);
28081
28123
  offset +
28082
28124
  ((scale.max + 2 * offset - scale.min) * (position - left)) / (right - left));
28083
28125
  }
28084
- updateAxisLimits(xMin, xMax) {
28126
+ /**
28127
+ * Compute min and max from the store, adjusting them if needed for non linear scales.
28128
+ * Getting the value from the store, we have to ensure that the values are integers for
28129
+ * non linear scales (bar and category). To select a bar in the chart, we have to include
28130
+ * the whole bar, which means that for the i-th bar, the selected min should be <= i and
28131
+ * the selected max should be >= i, so using the Math.floor and Math.ceil functions is
28132
+ * the right way to do it.
28133
+ * Sometimes, we can get a minimal value > the maximal value, which arise when the user
28134
+ * select a very small area in the master chart, and hasn't selected the middle of a bar
28135
+ * or a group of bars (in case of more than one data series).
28136
+ * Assuming we have to select the middle of a bar/a groupe of bars, we will reject the
28137
+ * coming value afterward. In this case, we do not update the chart because it would lead
28138
+ * to an empty chart.
28139
+ */
28140
+ getStoredBoundaries() {
28141
+ let { min, max } = this.store.currentAxesLimits[this.chartId].x;
28085
28142
  if (!this.hasLinearScale) {
28086
- this.chart.config.options.scales.x.min = Math.ceil(xMin);
28087
- this.chart.config.options.scales.x.max = Math.floor(xMax);
28143
+ min = Math.ceil(min);
28144
+ max = Math.floor(max);
28088
28145
  }
28089
- else {
28090
- this.chart.config.options.scales.x.min = xMin;
28091
- this.chart.config.options.scales.x.max = xMax;
28146
+ return { min, max };
28147
+ }
28148
+ /**
28149
+ * Adjust the min and max values of an axis if needed for non linear scales.
28150
+ * Here, after rounding (see docstring of getStoredBoundaries), we adjust the min by
28151
+ * substracting the axis offset, and we add it to the max, because when computing from the
28152
+ * scale, chartJs use integer values as the limits for non linear scales. If we have a min
28153
+ * value of 1, it means we want to start displaying from 0.5, and if we have a max value of
28154
+ * 4, it means we want to display until 4.5.
28155
+ * Here, we don't have to check if min > max because we are computing from the scale, and
28156
+ * chartJs ensures that this won't happen, even after our adjustments.
28157
+ */
28158
+ adjustBoundaries({ min, max }) {
28159
+ if (!this.hasLinearScale) {
28160
+ min = Math.ceil(min) - this.axisOffset;
28161
+ max = Math.floor(max) + this.axisOffset;
28162
+ }
28163
+ return { min, max };
28164
+ }
28165
+ updateAxisLimits(xMin, xMax) {
28166
+ if (xMin === xMax) {
28167
+ return;
28168
+ }
28169
+ if (!this.chart) {
28170
+ return;
28092
28171
  }
28093
28172
  this.store.updateAxisLimits(this.chartId, { min: xMin, max: xMax });
28094
- this.updateTrendingLineAxes();
28173
+ const { min, max } = this.getStoredBoundaries();
28174
+ if (max > min || (this.isBarChart && max === min)) {
28175
+ this.chart.config.options.scales.x.min = min;
28176
+ this.chart.config.options.scales.x.max = max;
28177
+ this.updateTrendingLineAxes();
28178
+ this.chart.update();
28179
+ }
28095
28180
  this.masterChart?.update();
28096
- this.chart?.update();
28097
28181
  }
28098
- onPointerDownInMasterChart(ev) {
28182
+ onMasterChartPointerDown(ev) {
28099
28183
  this.removeEventListeners();
28100
28184
  const position = ev.offsetX;
28101
28185
  if (!this.masterChart?.chartArea || !this.chart?.scales?.x) {
28102
28186
  return;
28103
28187
  }
28104
28188
  const { left, right, top, bottom } = this.masterChart.chartArea;
28105
- const xMax = this.upperBound ?? right;
28106
- const xMin = this.lowerBound ?? left;
28189
+ const upperBound = this.upperBound ?? right;
28190
+ const lowerBound = this.lowerBound ?? left;
28107
28191
  if (position < left - 5 || position > right + 5 || ev.offsetY < top || ev.offsetY > bottom) {
28108
28192
  return;
28109
28193
  }
@@ -28111,8 +28195,10 @@ stores.inject(MyMetaStore, storeInstance);
28111
28195
  ev.stopPropagation();
28112
28196
  let startingPositionOnChart, windowSize, startX;
28113
28197
  const startingEventPosition = ev.clientX - (this.masterChartCanvas.el?.getBoundingClientRect().left ?? 0);
28114
- if ((xMin !== left || xMax !== right) && position > xMin + 5 && position < xMax - 5) {
28115
- startingPositionOnChart = ev.offsetX - xMin;
28198
+ if ((lowerBound !== left || upperBound !== right) &&
28199
+ position > lowerBound + 5 &&
28200
+ position < upperBound - 5) {
28201
+ startingPositionOnChart = ev.offsetX - lowerBound;
28116
28202
  this.mode = "moveInMaster";
28117
28203
  const currentLimits = this.store.currentAxesLimits[this.chartId]?.x;
28118
28204
  windowSize =
@@ -28121,31 +28207,29 @@ stores.inject(MyMetaStore, storeInstance);
28121
28207
  }
28122
28208
  else {
28123
28209
  this.mode = "selectInMaster";
28124
- if (Math.abs(position - xMin) < 5) {
28125
- startingPositionOnChart = xMax;
28210
+ if (Math.abs(position - lowerBound) < 5) {
28211
+ startingPositionOnChart = upperBound;
28126
28212
  }
28127
- else if (Math.abs(position - xMax) < 5) {
28128
- startingPositionOnChart = xMin;
28213
+ else if (Math.abs(position - upperBound) < 5) {
28214
+ startingPositionOnChart = lowerBound;
28129
28215
  }
28130
28216
  else {
28131
28217
  startingPositionOnChart = clip(position, left, right);
28132
28218
  }
28133
28219
  startX = this.computeCoordinate(startingPositionOnChart);
28134
28220
  }
28135
- const originalXMin = this.store.originalAxisLimits[this.chartId].x.min;
28136
- const originalXMax = this.store.originalAxisLimits[this.chartId].x.max;
28221
+ const storedMin = this.store.originalAxisLimits[this.chartId].x.min;
28222
+ const storedMax = this.store.originalAxisLimits[this.chartId].x.max;
28137
28223
  const computeNewAxisLimits = (position) => {
28138
- let xMin, xMax;
28139
- const { left, right } = this.masterChart.chartArea;
28140
28224
  if (this.mode === "moveInMaster") {
28141
- xMin = this.computeCoordinate(position - startingPositionOnChart);
28142
- if (xMin < originalXMin) {
28143
- xMin = originalXMin;
28225
+ let min = this.computeCoordinate(position - startingPositionOnChart);
28226
+ if (min < storedMin) {
28227
+ min = storedMin;
28144
28228
  }
28145
- else if (xMin > originalXMax - windowSize) {
28146
- xMin = originalXMax - windowSize;
28229
+ else if (min > storedMax - windowSize) {
28230
+ min = storedMax - windowSize;
28147
28231
  }
28148
- xMax = xMin + windowSize;
28232
+ return { min, max: min + windowSize };
28149
28233
  }
28150
28234
  else if (this.mode === "selectInMaster") {
28151
28235
  const upperBound = clip(position, left, right);
@@ -28154,54 +28238,52 @@ stores.inject(MyMetaStore, storeInstance);
28154
28238
  if (startX === undefined || endX === undefined) {
28155
28239
  return {};
28156
28240
  }
28157
- xMin = Math.min(startX, endX);
28158
- xMax = Math.max(startX, endX);
28241
+ return {
28242
+ min: Math.min(startX, endX),
28243
+ max: Math.max(startX, endX),
28244
+ };
28159
28245
  }
28160
28246
  }
28161
- return { min: xMin, max: xMax };
28247
+ return {};
28162
28248
  };
28163
- const onDragFromMasterChart = (ev) => {
28249
+ const onMasterChartDrag = (ev) => {
28164
28250
  const position = ev.clientX - (this.masterChartCanvas.el?.getBoundingClientRect().left ?? 0);
28165
28251
  if (Math.abs(position - startingEventPosition) < 5) {
28166
28252
  return;
28167
28253
  }
28168
- const { min: xMin, max: xMax } = computeNewAxisLimits(position);
28169
- if (xMin !== undefined && xMax !== undefined) {
28170
- this.updateAxisLimits(xMin, xMax);
28254
+ const { min, max } = computeNewAxisLimits(position);
28255
+ if (min !== undefined && max !== undefined) {
28256
+ this.updateAxisLimits(min, max);
28171
28257
  }
28172
28258
  };
28173
- const onPointerUpInMasterChart = (ev) => {
28259
+ const onMasterChartPointerUp = (ev) => {
28174
28260
  this.removeEventListeners();
28175
- const position = ev.clientX - (this.masterChartCanvas.el?.getBoundingClientRect().left ?? 0);
28176
- if (Math.abs(position - startingEventPosition) > 5) {
28177
- let { min: xMin, max: xMax } = computeNewAxisLimits(position);
28178
- if (xMin !== undefined && xMax !== undefined) {
28179
- if (!this.hasLinearScale) {
28180
- if (this.mode === "moveInMaster" && windowSize && !this.isBarChart) {
28181
- xMin = Math.round(xMin) - this.axisOffset;
28182
- xMax = xMin + windowSize;
28183
- }
28184
- else {
28185
- xMin = Math.ceil(xMin) - this.axisOffset;
28186
- xMax = Math.floor(xMax) + this.axisOffset;
28187
- }
28188
- }
28189
- this.updateAxisLimits(xMin, xMax);
28261
+ let { min, max } = this.chart.scales.x;
28262
+ if (!this.hasLinearScale) {
28263
+ if (this.mode === "moveInMaster") {
28264
+ min = Math.round(min) - this.axisOffset;
28265
+ max = min + windowSize;
28266
+ }
28267
+ else {
28268
+ ({ min, max } = this.adjustBoundaries({ min, max }));
28190
28269
  }
28191
28270
  }
28271
+ this.updateAxisLimits(min, max);
28192
28272
  this.mode = undefined;
28193
28273
  };
28194
28274
  this.removeEventListeners = () => {
28195
- window.removeEventListener("pointermove", onDragFromMasterChart, true);
28196
- window.removeEventListener("pointerup", onPointerUpInMasterChart, true);
28275
+ window.removeEventListener("pointermove", onMasterChartDrag, true);
28276
+ window.removeEventListener("pointerup", onMasterChartPointerUp, true);
28197
28277
  };
28198
- window.addEventListener("pointermove", onDragFromMasterChart, true);
28199
- window.addEventListener("pointerup", onPointerUpInMasterChart, true);
28278
+ window.addEventListener("pointermove", onMasterChartDrag, true);
28279
+ window.addEventListener("pointerup", onMasterChartPointerUp, true);
28200
28280
  }
28201
- onPointerMoveInMasterChart(ev) {
28202
- const { offsetX: x, offsetY: y } = ev;
28281
+ onMasterChartPointerMove(ev) {
28282
+ const { offsetX: x, offsetY: y, target } = ev;
28283
+ if (!target || !this.isMasterChartAllowed) {
28284
+ return;
28285
+ }
28203
28286
  if (this.mode === undefined) {
28204
- const target = ev.target;
28205
28287
  if (!this.masterChart?.chartArea) {
28206
28288
  target["style"].cursor = "default";
28207
28289
  return;
@@ -28223,14 +28305,14 @@ stores.inject(MyMetaStore, storeInstance);
28223
28305
  }
28224
28306
  }
28225
28307
  }
28226
- onMouseLeaveMasterChart(ev) {
28308
+ onMasterChartMouseLeave(ev) {
28227
28309
  const target = ev.target;
28228
- if (!target) {
28310
+ if (!target || !this.isMasterChartAllowed) {
28229
28311
  return;
28230
28312
  }
28231
28313
  target["style"].cursor = "default";
28232
28314
  }
28233
- onDoubleClickInMasterChart(ev) {
28315
+ onMasterChartDoubleClick(ev) {
28234
28316
  this.mode = undefined;
28235
28317
  const position = ev.offsetX;
28236
28318
  if (!this.masterChart?.chartArea || !this.chart?.scales.x) {
@@ -28247,33 +28329,25 @@ stores.inject(MyMetaStore, storeInstance);
28247
28329
  }
28248
28330
  ev.preventDefault();
28249
28331
  ev.stopPropagation();
28250
- let { min: xMin, max: xMax } = this.store.currentAxesLimits[this.chartId]?.x ?? this.chart.scales.x;
28332
+ let { min, max } = this.store.currentAxesLimits[this.chartId]?.x ?? this.chart.scales.x;
28251
28333
  const originalAxisLimits = this.store.originalAxisLimits[this.chartId].x;
28252
28334
  if (!originalAxisLimits) {
28253
28335
  return;
28254
28336
  }
28255
- let originalXMin = originalAxisLimits.min;
28256
- let originalXMax = originalAxisLimits.max;
28257
- if (this.hasLinearScale) {
28258
- originalXMin = Math.ceil(originalXMin) - this.axisOffset;
28259
- originalXMax = Math.floor(originalXMax) + this.axisOffset;
28260
- }
28261
28337
  if (Math.abs(position - lowerBound) < 5) {
28262
- // Reset to original min
28263
- xMin = originalXMin;
28338
+ min = originalAxisLimits.min;
28264
28339
  }
28265
28340
  else if (Math.abs(position - upperBound) < 5) {
28266
- xMax = originalXMax;
28341
+ max = originalAxisLimits.max;
28267
28342
  }
28268
28343
  else if (lowerBound < position && position < upperBound) {
28269
- // Reset to original limits
28270
- xMin = originalXMin;
28271
- xMax = originalXMax;
28344
+ min = originalAxisLimits.min;
28345
+ max = originalAxisLimits.max;
28272
28346
  }
28273
28347
  else {
28274
28348
  return;
28275
28349
  }
28276
- this.updateAxisLimits(xMin, xMax);
28350
+ this.updateAxisLimits(min, max);
28277
28351
  }
28278
28352
  }
28279
28353
 
@@ -31376,7 +31450,7 @@ stores.inject(MyMetaStore, storeInstance);
31376
31450
  return query.replaceAll(/([a-zA-Z0-9]+):([a-zA-Z0-9]+)/g, "NAMESPACE" + "$1" + "NAMESPACE" + "$2");
31377
31451
  }
31378
31452
 
31379
- function getChartMenuActions(figureId, onFigureDeleted, env) {
31453
+ function getChartMenuActions(figureId, env) {
31380
31454
  const chartId = env.model.getters.getChartIdFromFigureId(figureId);
31381
31455
  if (!chartId) {
31382
31456
  return [];
@@ -31396,11 +31470,11 @@ stores.inject(MyMetaStore, storeInstance);
31396
31470
  getCutMenuItem(figureId, env),
31397
31471
  getCopyAsImageMenuItem(figureId, env),
31398
31472
  getDownloadChartMenuItem(figureId, env),
31399
- getDeleteMenuItem(figureId, onFigureDeleted, env),
31473
+ getDeleteMenuItem(figureId, env),
31400
31474
  ];
31401
31475
  return createActions(menuItemSpecs).filter((action) => env.model.getters.isReadonly() ? action.isReadonlyAllowed : true);
31402
31476
  }
31403
- function getImageMenuActions(figureId, onFigureDeleted, env) {
31477
+ function getImageMenuActions(figureId, env) {
31404
31478
  const menuItemSpecs = [
31405
31479
  getCopyMenuItem(figureId, env, _t("Image copied to clipboard")),
31406
31480
  getCutMenuItem(figureId, env),
@@ -31443,11 +31517,11 @@ stores.inject(MyMetaStore, storeInstance);
31443
31517
  },
31444
31518
  icon: "o-spreadsheet-Icon.DOWNLOAD",
31445
31519
  },
31446
- getDeleteMenuItem(figureId, onFigureDeleted, env),
31520
+ getDeleteMenuItem(figureId, env),
31447
31521
  ];
31448
31522
  return createActions(menuItemSpecs);
31449
31523
  }
31450
- function getCarouselMenuActions(figureId, onFigureDeleted, env) {
31524
+ function getCarouselMenuActions(figureId, env) {
31451
31525
  const isChartSelected = (env) => env.model.getters.getSelectedCarouselItem(figureId)?.type === "chart";
31452
31526
  const menuItemSpecs = [
31453
31527
  {
@@ -31466,7 +31540,7 @@ stores.inject(MyMetaStore, storeInstance);
31466
31540
  },
31467
31541
  { ...getCutMenuItem(figureId, env), name: _t("Cut carousel") },
31468
31542
  {
31469
- ...getDeleteMenuItem(figureId, onFigureDeleted, env),
31543
+ ...getDeleteMenuItem(figureId, env),
31470
31544
  name: _t("Delete carousel"),
31471
31545
  separator: true,
31472
31546
  },
@@ -31609,7 +31683,7 @@ stores.inject(MyMetaStore, storeInstance);
31609
31683
  isReadonlyAllowed: true,
31610
31684
  };
31611
31685
  }
31612
- function getDeleteMenuItem(figureId, onFigureDeleted, env) {
31686
+ function getDeleteMenuItem(figureId, env) {
31613
31687
  return {
31614
31688
  id: "delete",
31615
31689
  name: _t("Delete"),
@@ -31618,7 +31692,6 @@ stores.inject(MyMetaStore, storeInstance);
31618
31692
  sheetId: env.model.getters.getActiveSheetId(),
31619
31693
  figureId,
31620
31694
  });
31621
- onFigureDeleted();
31622
31695
  },
31623
31696
  icon: "o-spreadsheet-Icon.TRASH",
31624
31697
  };
@@ -32431,7 +32504,7 @@ stores.inject(MyMetaStore, storeInstance);
32431
32504
  this.menuState.isOpen = true;
32432
32505
  this.menuState.anchorRect = getBoundingRectAsPOJO(ev.currentTarget);
32433
32506
  const figureId = this.env.model.getters.getFigureIdFromChartId(this.props.chartId);
32434
- this.menuState.menuItems = getChartMenuActions(figureId, () => { }, this.env);
32507
+ this.menuState.menuItems = getChartMenuActions(figureId, this.env);
32435
32508
  }
32436
32509
  get fullScreenMenuItem() {
32437
32510
  if (!this.props.hasFullScreenButton) {
@@ -32458,7 +32531,6 @@ stores.inject(MyMetaStore, storeInstance);
32458
32531
  static template = "o-spreadsheet-CarouselFigure";
32459
32532
  static props = {
32460
32533
  figureUI: Object,
32461
- onFigureDeleted: Function,
32462
32534
  editFigureStyle: { type: Function, optional: true },
32463
32535
  isFullScreen: { type: Boolean, optional: true },
32464
32536
  openContextMenu: { type: Function, optional: true },
@@ -32607,7 +32679,6 @@ stores.inject(MyMetaStore, storeInstance);
32607
32679
  static template = "o-spreadsheet-ChartFigure";
32608
32680
  static props = {
32609
32681
  figureUI: Object,
32610
- onFigureDeleted: Function,
32611
32682
  editFigureStyle: { type: Function, optional: true },
32612
32683
  isFullScreen: { type: Boolean, optional: true },
32613
32684
  openContextMenu: { type: Function, optional: true },
@@ -32641,7 +32712,6 @@ stores.inject(MyMetaStore, storeInstance);
32641
32712
  static template = "o-spreadsheet-ImageFigure";
32642
32713
  static props = {
32643
32714
  figureUI: Object,
32644
- onFigureDeleted: Function,
32645
32715
  editFigureStyle: { type: Function, optional: true },
32646
32716
  openContextMenu: { type: Function, optional: true },
32647
32717
  };
@@ -32760,13 +32830,11 @@ stores.inject(MyMetaStore, storeInstance);
32760
32830
  figureUI: Object,
32761
32831
  style: { type: String, optional: true },
32762
32832
  class: { type: String, optional: true },
32763
- onFigureDeleted: { type: Function, optional: true },
32764
32833
  onMouseDown: { type: Function, optional: true },
32765
32834
  onClickAnchor: { type: Function, optional: true },
32766
32835
  };
32767
32836
  static components = { MenuPopover };
32768
32837
  static defaultProps = {
32769
- onFigureDeleted: () => { },
32770
32838
  onMouseDown: () => { },
32771
32839
  onClickAnchor: () => { },
32772
32840
  };
@@ -32844,9 +32912,6 @@ stores.inject(MyMetaStore, storeInstance);
32844
32912
  this.props.figureUI.id,
32845
32913
  this.figureRef.el,
32846
32914
  ]);
32847
- owl.onWillUnmount(() => {
32848
- this.props.onFigureDeleted();
32849
- });
32850
32915
  }
32851
32916
  clickAnchor(dirX, dirY, ev) {
32852
32917
  this.props.onClickAnchor(dirX, dirY, ev);
@@ -32870,7 +32935,6 @@ stores.inject(MyMetaStore, storeInstance);
32870
32935
  sheetId: this.env.model.getters.getActiveSheetId(),
32871
32936
  figureId: this.props.figureUI.id,
32872
32937
  });
32873
- this.props.onFigureDeleted();
32874
32938
  ev.preventDefault();
32875
32939
  ev.stopPropagation();
32876
32940
  break;
@@ -32963,7 +33027,7 @@ stores.inject(MyMetaStore, storeInstance);
32963
33027
  this.menuState.anchorRect = anchorRect;
32964
33028
  this.menuState.menuItems = figureRegistry
32965
33029
  .get(this.props.figureUI.tag)
32966
- .menuBuilder(this.props.figureUI.id, this.props.onFigureDeleted, this.env);
33030
+ .menuBuilder(this.props.figureUI.id, this.env);
32967
33031
  }
32968
33032
  editWrapperStyle(properties) {
32969
33033
  if (this.figureWrapperRef.el) {
@@ -33335,7 +33399,7 @@ stores.inject(MyMetaStore, storeInstance);
33335
33399
  return false;
33336
33400
  }
33337
33401
  if (["lastWeek", "lastMonth", "lastYear"].includes(criterion.dateValue)) {
33338
- const today = jsDateToRoundNumber(DateTime.now());
33402
+ const today = Math.floor(jsDateToNumber(DateTime.now()));
33339
33403
  return isDateBetween(dateValue, today, criterionValue);
33340
33404
  }
33341
33405
  return areDatesSameDay(dateValue, criterionValue);
@@ -34404,7 +34468,8 @@ stores.inject(MyMetaStore, storeInstance);
34404
34468
  assistantStyle["max-height"] = `${availableSpaceAbove - CLOSE_ICON_RADIUS}px`;
34405
34469
  // render top
34406
34470
  // We compensate 2 px of margin on the assistant style + 1px for design reasons
34407
- assistantStyle.transform = `translate(0, calc(-100% - ${cellHeight + 3}px))`;
34471
+ assistantStyle.top = `-3px`;
34472
+ assistantStyle.transform = `translate(0, -100%)`;
34408
34473
  }
34409
34474
  if (cellX + ASSISTANT_WIDTH > this.props.delimitation.width) {
34410
34475
  // render left
@@ -35195,7 +35260,7 @@ stores.inject(MyMetaStore, storeInstance);
35195
35260
  }
35196
35261
  this.selectionStart = start;
35197
35262
  this.selectionEnd = end;
35198
- this.editionMode = "editing";
35263
+ this.stopComposerRangeSelection();
35199
35264
  this.computeFormulaCursorContext();
35200
35265
  this.computeParenthesisRelatedToCursor();
35201
35266
  this.updateAutoCompleteProvider();
@@ -37448,6 +37513,12 @@ stores.inject(MyMetaStore, storeInstance);
37448
37513
  }
37449
37514
  setup() {
37450
37515
  owl.useEffect(() => this.focusedInput.el?.focus(), () => [this.focusedInput.el]);
37516
+ owl.useEffect(() => {
37517
+ // Check the offsetParent to know if the input or an ancestor is `display: none` (eg. when changing side panel tab)
37518
+ if (this.store.hasFocus && this.selectionRef.el?.offsetParent === null) {
37519
+ this.reset();
37520
+ }
37521
+ });
37451
37522
  this.store = useLocalStore(SelectionInputStore, this.props.ranges, this.props.hasSingleRange || false, this.props.colors, this.props.disabledRanges);
37452
37523
  owl.onWillUpdateProps((nextProps) => {
37453
37524
  if (nextProps.ranges.join() !== this.store.selectionInputValues.join()) {
@@ -44192,18 +44263,22 @@ stores.inject(MyMetaStore, storeInstance);
44192
44263
  return messages;
44193
44264
  }
44194
44265
  function fixChartDefinitions(data, initialMessages) {
44266
+ /**
44267
+ * Revisions created after version 18.5.1 contain the full chart definition in the command
44268
+ * if the data was alreay updated to 18.5.1, then those older revision cannot (by definition) be reaplied
44269
+ * and should not be replayed.
44270
+ * FIXME: every command should be versionned when upgraded to allow finer tuning.
44271
+ */
44272
+ if (!data.version || compareVersions(String(data.version), "18.5.1") >= 0) {
44273
+ return initialMessages;
44274
+ }
44195
44275
  const messages = [];
44196
44276
  const map = {};
44197
44277
  for (const sheet of data.sheets || []) {
44198
44278
  sheet.figures?.forEach((figure) => {
44199
44279
  if (figure.tag === "chart") {
44200
44280
  // chart definition
44201
- if (data.version && compareVersions(String(data.version), "18.5.1") <= 0) {
44202
- map[figure.data.chartId] = figure.data;
44203
- }
44204
- else {
44205
- map[figure.id] = figure.data;
44206
- }
44281
+ map[figure.id] = figure.data;
44207
44282
  }
44208
44283
  });
44209
44284
  }
@@ -45227,7 +45302,7 @@ stores.inject(MyMetaStore, storeInstance);
45227
45302
  const leftOffset = isLeftUnbounded || left?.colFixed ? 0 : colCellOffset;
45228
45303
  const topOffset = isTopUnbounded || left?.rowFixed ? 0 : rowCellOffset;
45229
45304
  const isRightFixed = (!right && left?.colFixed) || right?.colFixed;
45230
- const isBottomFixed = (!right && left.rowFixed) || right?.rowFixed;
45305
+ const isBottomFixed = (!right && left?.rowFixed) || right?.rowFixed;
45231
45306
  const isRightUnbounded = range.unboundedZone.right === undefined;
45232
45307
  const isBottomUnbounded = range.unboundedZone.bottom === undefined;
45233
45308
  const rightOffset = isRightUnbounded || isRightFixed ? 0 : colCellOffset;
@@ -47786,13 +47861,42 @@ stores.inject(MyMetaStore, storeInstance);
47786
47861
  pivotRegistry.add("SPREADSHEET", {
47787
47862
  ui: SpreadsheetPivot,
47788
47863
  definition: SpreadsheetPivotRuntimeDefinition,
47789
- externalData: false,
47790
47864
  dateGranularities: [...dateGranularities],
47791
47865
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
47792
47866
  isMeasureCandidate: (field) => field.type !== "boolean",
47793
47867
  isGroupable: () => true,
47794
47868
  canHaveCustomGroup: (field) => field.type === "char" && !field.isCustomField,
47869
+ adaptRanges: (getters, definition, applyChange) => {
47870
+ if (definition.type !== "SPREADSHEET" || !definition.dataSet) {
47871
+ return definition;
47872
+ }
47873
+ const { sheetId, zone } = definition.dataSet;
47874
+ const range = getters.getRangeFromZone(sheetId, zone);
47875
+ const adaptedRange = adaptPivotRange(range, applyChange);
47876
+ if (adaptedRange === range) {
47877
+ return definition;
47878
+ }
47879
+ const dataSet = adaptedRange && {
47880
+ sheetId: adaptedRange.sheetId,
47881
+ zone: adaptedRange.zone,
47882
+ };
47883
+ return { ...definition, dataSet };
47884
+ },
47795
47885
  });
47886
+ function adaptPivotRange(range, applyChange) {
47887
+ if (!range) {
47888
+ return undefined;
47889
+ }
47890
+ const change = applyChange(range);
47891
+ switch (change.changeType) {
47892
+ case "NONE":
47893
+ return range;
47894
+ case "REMOVE":
47895
+ return undefined;
47896
+ default:
47897
+ return change.range;
47898
+ }
47899
+ }
47796
47900
 
47797
47901
  const pivotProperties = {
47798
47902
  name: _t("See pivot properties"),
@@ -49063,7 +49167,6 @@ stores.inject(MyMetaStore, storeInstance);
49063
49167
  }
49064
49168
  get highlights() {
49065
49169
  const position = this.model.getters.getActivePosition();
49066
- const cell = this.getters.getEvaluatedCell(position);
49067
49170
  const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
49068
49171
  const zone = spreader
49069
49172
  ? this.model.getters.getSpreadZone(spreader, { ignoreSpillError: true })
@@ -49071,10 +49174,11 @@ stores.inject(MyMetaStore, storeInstance);
49071
49174
  if (!zone) {
49072
49175
  return [];
49073
49176
  }
49177
+ const isArrayFormulaBlocked = this.model.getters.isArrayFormulaSpillBlocked(spreader ?? position);
49074
49178
  return [
49075
49179
  {
49076
49180
  range: this.model.getters.getRangeFromZone(position.sheetId, zone),
49077
- dashed: cell.value === CellErrorType.SpilledBlocked,
49181
+ dashed: isArrayFormulaBlocked,
49078
49182
  color: "#17A2B8",
49079
49183
  noFill: true,
49080
49184
  thinLine: true,
@@ -50481,9 +50585,7 @@ stores.inject(MyMetaStore, storeInstance);
50481
50585
  */
50482
50586
  class FiguresContainer extends owl.Component {
50483
50587
  static template = "o-spreadsheet-FiguresContainer";
50484
- static props = {
50485
- onFigureDeleted: Function,
50486
- };
50588
+ static props = {};
50487
50589
  static components = { FigureComponent };
50488
50590
  dnd = owl.useState({
50489
50591
  draggedFigure: undefined,
@@ -50693,7 +50795,6 @@ stores.inject(MyMetaStore, storeInstance);
50693
50795
  carouselFigureId: this.dnd.overlappingCarousel.id,
50694
50796
  chartFigureId: figureUI.id,
50695
50797
  });
50696
- this.props.onFigureDeleted();
50697
50798
  }
50698
50799
  this.dnd.draggedFigure = undefined;
50699
50800
  this.dnd.horizontalSnap = undefined;
@@ -50919,16 +51020,16 @@ stores.inject(MyMetaStore, storeInstance);
50919
51020
  `;
50920
51021
  class GridAddRowsFooter extends owl.Component {
50921
51022
  static template = "o-spreadsheet-GridAddRowsFooter";
50922
- static props = {
50923
- focusGrid: Function,
50924
- };
51023
+ static props = {};
50925
51024
  static components = { ValidationMessages };
51025
+ DOMFocusableElementStore;
50926
51026
  inputRef = owl.useRef("inputRef");
50927
51027
  state = owl.useState({
50928
51028
  inputValue: "100",
50929
51029
  errorFlag: false,
50930
51030
  });
50931
51031
  setup() {
51032
+ this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
50932
51033
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
50933
51034
  }
50934
51035
  get addRowsPosition() {
@@ -50946,7 +51047,7 @@ stores.inject(MyMetaStore, storeInstance);
50946
51047
  }
50947
51048
  onKeydown(ev) {
50948
51049
  if (ev.key.toUpperCase() === "ESCAPE") {
50949
- this.props.focusGrid();
51050
+ this.focusDefaultElement();
50950
51051
  }
50951
51052
  else if (ev.key.toUpperCase() === "ENTER") {
50952
51053
  this.onConfirm();
@@ -50973,7 +51074,7 @@ stores.inject(MyMetaStore, storeInstance);
50973
51074
  quantity,
50974
51075
  dimension: "ROW",
50975
51076
  });
50976
- this.props.focusGrid();
51077
+ this.focusDefaultElement();
50977
51078
  // After adding new rows, scroll down to the new last row
50978
51079
  const { scrollX } = this.env.model.getters.getActiveSheetScrollInfo();
50979
51080
  const { end } = this.env.model.getters.getRowDimensions(activeSheetId, rowNumber + quantity - 1);
@@ -50986,7 +51087,12 @@ stores.inject(MyMetaStore, storeInstance);
50986
51087
  if (this.inputRef.el !== document.activeElement || ev.target === this.inputRef.el) {
50987
51088
  return;
50988
51089
  }
50989
- this.props.focusGrid();
51090
+ this.focusDefaultElement();
51091
+ }
51092
+ focusDefaultElement() {
51093
+ if (document.activeElement === this.inputRef.el) {
51094
+ this.DOMFocusableElementStore.focus();
51095
+ }
50990
51096
  }
50991
51097
  }
50992
51098
 
@@ -51261,7 +51367,6 @@ stores.inject(MyMetaStore, storeInstance);
51261
51367
  onCellClicked: { type: Function, optional: true },
51262
51368
  onCellRightClicked: { type: Function, optional: true },
51263
51369
  onGridResized: { type: Function, optional: true },
51264
- onFigureDeleted: { type: Function, optional: true },
51265
51370
  onGridMoved: Function,
51266
51371
  gridOverlayDimensions: String,
51267
51372
  slots: { type: Object, optional: true },
@@ -51276,7 +51381,6 @@ stores.inject(MyMetaStore, storeInstance);
51276
51381
  onCellClicked: () => { },
51277
51382
  onCellRightClicked: () => { },
51278
51383
  onGridResized: () => { },
51279
- onFigureDeleted: () => { },
51280
51384
  };
51281
51385
  gridOverlay = owl.useRef("gridOverlay");
51282
51386
  cellPopovers;
@@ -53822,7 +53926,9 @@ stores.inject(MyMetaStore, storeInstance);
53822
53926
  inputRef = owl.useRef("inputFontSize");
53823
53927
  rootEditorRef = owl.useRef("FontSizeEditor");
53824
53928
  fontSizeListRef = owl.useRef("fontSizeList");
53929
+ DOMFocusableElementStore;
53825
53930
  setup() {
53931
+ this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
53826
53932
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
53827
53933
  }
53828
53934
  get popoverProps() {
@@ -53876,6 +53982,13 @@ stores.inject(MyMetaStore, storeInstance);
53876
53982
  }
53877
53983
  this.props.onToggle?.();
53878
53984
  }
53985
+ if (ev.key === "Tab") {
53986
+ ev.preventDefault();
53987
+ ev.stopPropagation();
53988
+ this.closeFontList();
53989
+ this.DOMFocusableElementStore.focus();
53990
+ return;
53991
+ }
53879
53992
  }
53880
53993
  }
53881
53994
 
@@ -55211,19 +55324,6 @@ stores.inject(MyMetaStore, storeInstance);
55211
55324
  }
55212
55325
  }
55213
55326
 
55214
- class ChartShowDataMarkers extends owl.Component {
55215
- static template = "o-spreadsheet-ChartShowDataMarkers";
55216
- static components = {
55217
- Checkbox,
55218
- };
55219
- static props = {
55220
- chartId: String,
55221
- definition: Object,
55222
- updateChart: Function,
55223
- canUpdateChart: Function,
55224
- };
55225
- }
55226
-
55227
55327
  class GenericZoomableChartDesignPanel extends ChartWithAxisDesignPanel {
55228
55328
  static template = "o-spreadsheet-GenericZoomableChartDesignPanel";
55229
55329
  static components = {
@@ -55237,6 +55337,26 @@ stores.inject(MyMetaStore, storeInstance);
55237
55337
  }
55238
55338
  }
55239
55339
 
55340
+ class BarChartDesignPanel extends GenericZoomableChartDesignPanel {
55341
+ static template = "o-spreadsheet-BarChartDesignPanel";
55342
+ get isZoomable() {
55343
+ return !this.props.definition.horizontal;
55344
+ }
55345
+ }
55346
+
55347
+ class ChartShowDataMarkers extends owl.Component {
55348
+ static template = "o-spreadsheet-ChartShowDataMarkers";
55349
+ static components = {
55350
+ Checkbox,
55351
+ };
55352
+ static props = {
55353
+ chartId: String,
55354
+ definition: Object,
55355
+ updateChart: Function,
55356
+ canUpdateChart: Function,
55357
+ };
55358
+ }
55359
+
55240
55360
  class ComboChartDesignPanel extends GenericZoomableChartDesignPanel {
55241
55361
  static template = "o-spreadsheet-ComboChartDesignPanel";
55242
55362
  static components = {
@@ -56165,7 +56285,7 @@ stores.inject(MyMetaStore, storeInstance);
56165
56285
  })
56166
56286
  .add("bar", {
56167
56287
  configuration: BarConfigPanel,
56168
- design: GenericZoomableChartDesignPanel,
56288
+ design: BarChartDesignPanel,
56169
56289
  })
56170
56290
  .add("combo", {
56171
56291
  configuration: GenericChartConfigPanel,
@@ -56526,7 +56646,7 @@ stores.inject(MyMetaStore, storeInstance);
56526
56646
  }
56527
56647
 
56528
56648
  css /* scss */ `
56529
- .o-cf-preview {
56649
+ .o-spreadsheet .o-cf-preview {
56530
56650
  &.o-cf-cursor-ptr {
56531
56651
  cursor: pointer;
56532
56652
  }
@@ -56534,6 +56654,7 @@ stores.inject(MyMetaStore, storeInstance);
56534
56654
  border-bottom: 1px solid ${GRAY_300};
56535
56655
  height: 80px;
56536
56656
  padding: 10px;
56657
+ box-sizing: border-box;
56537
56658
  position: relative;
56538
56659
  cursor: pointer;
56539
56660
  &:hover,
@@ -56547,7 +56668,6 @@ stores.inject(MyMetaStore, storeInstance);
56547
56668
  .o-cf-preview-icon {
56548
56669
  border: 1px solid ${GRAY_300};
56549
56670
  background-color: #fff;
56550
- position: absolute;
56551
56671
  height: 50px;
56552
56672
  width: 50px;
56553
56673
  .o-icon {
@@ -56556,12 +56676,6 @@ stores.inject(MyMetaStore, storeInstance);
56556
56676
  }
56557
56677
  }
56558
56678
  .o-cf-preview-description {
56559
- left: 65px;
56560
- margin-bottom: auto;
56561
- margin-right: 8px;
56562
- margin-top: auto;
56563
- position: relative;
56564
- width: 142px;
56565
56679
  .o-cf-preview-description-rule {
56566
56680
  margin-bottom: 4px;
56567
56681
  max-height: 2.8em;
@@ -56571,16 +56685,11 @@ stores.inject(MyMetaStore, storeInstance);
56571
56685
  font-size: 12px;
56572
56686
  }
56573
56687
  }
56574
- .o-cf-delete {
56575
- left: 90%;
56576
- top: 39%;
56577
- position: absolute;
56578
- }
56579
56688
  &:not(:hover):not(.o-cf-dragging) .o-cf-drag-handle {
56580
56689
  display: none !important;
56581
56690
  }
56582
56691
  .o-cf-drag-handle {
56583
- left: -8px;
56692
+ left: 2px;
56584
56693
  cursor: move;
56585
56694
  .o-icon {
56586
56695
  width: 6px;
@@ -62385,10 +62494,10 @@ stores.inject(MyMetaStore, storeInstance);
62385
62494
  // existingBorderSideToClear[side] = true means we should clear the border on that
62386
62495
  // side of the existing adjacent zone before adding the new border.
62387
62496
  const existingBorderSideToClear = {
62388
- left: force || !!newBorder?.right,
62389
- right: force || !!newBorder?.left,
62390
- top: force || !!newBorder?.bottom,
62391
- bottom: force || !!newBorder?.top,
62497
+ left: !!newBorder?.right,
62498
+ right: !!newBorder?.left,
62499
+ top: !!newBorder?.bottom,
62500
+ bottom: !!newBorder?.top,
62392
62501
  };
62393
62502
  let editingZone = [zone];
62394
62503
  for (const existingBorder of this.borders[sheetId] ?? []) {
@@ -64257,7 +64366,8 @@ stores.inject(MyMetaStore, storeInstance);
64257
64366
  }
64258
64367
  break;
64259
64368
  case "DUPLICATE_SHEET": {
64260
- for (const figureId in this.figures[cmd.sheetId]) {
64369
+ for (const figure of this.getFigures(cmd.sheetId)) {
64370
+ const figureId = figure.id;
64261
64371
  const fig = this.figures[cmd.sheetId]?.[figureId];
64262
64372
  if (!fig) {
64263
64373
  continue;
@@ -65380,6 +65490,7 @@ stores.inject(MyMetaStore, storeInstance);
65380
65490
  class RangeAdapter {
65381
65491
  getters;
65382
65492
  providers = [];
65493
+ isAdaptingRanges = false;
65383
65494
  constructor(getters) {
65384
65495
  this.getters = getters;
65385
65496
  }
@@ -65411,6 +65522,9 @@ stores.inject(MyMetaStore, storeInstance);
65411
65522
  }
65412
65523
  beforeHandle(command) { }
65413
65524
  handle(cmd) {
65525
+ if (this.isAdaptingRanges) {
65526
+ throw new Error("Plugins cannot dispatch commands during adaptRanges phase");
65527
+ }
65414
65528
  const rangeAdapter = getRangeAdapter(cmd);
65415
65529
  if (rangeAdapter?.applyChange) {
65416
65530
  this.executeOnAllRanges(rangeAdapter.applyChange, rangeAdapter.sheetId, rangeAdapter.sheetName);
@@ -65433,10 +65547,12 @@ stores.inject(MyMetaStore, storeInstance);
65433
65547
  };
65434
65548
  }
65435
65549
  executeOnAllRanges(adaptRange, sheetId, sheetName) {
65550
+ this.isAdaptingRanges = true;
65436
65551
  const func = this.verifyRangeRemoved(adaptRange);
65437
65552
  for (const provider of this.providers) {
65438
65553
  provider(func, sheetId, sheetName);
65439
65554
  }
65555
+ this.isAdaptingRanges = false;
65440
65556
  }
65441
65557
  /**
65442
65558
  * Stores the functions bound to each plugin to be able to iterate over all ranges of the application,
@@ -65753,7 +65869,7 @@ stores.inject(MyMetaStore, storeInstance);
65753
65869
  break;
65754
65870
  case "CREATE_SHEET":
65755
65871
  const sheet = this.createSheet(cmd.sheetId, cmd.name || this.getNextSheetName(), cmd.cols || 26, cmd.rows || 100, cmd.position);
65756
- this.history.update("sheetIdsMapName", sheet.name, sheet.id);
65872
+ this.history.update("sheetIdsMapName", toStandardizedSheetName(sheet.name), sheet.id);
65757
65873
  break;
65758
65874
  case "MOVE_SHEET":
65759
65875
  this.moveSheet(cmd.sheetId, cmd.delta);
@@ -65820,7 +65936,7 @@ stores.inject(MyMetaStore, storeInstance);
65820
65936
  // that depends on a sheet not already imported will not be able to be
65821
65937
  // compiled
65822
65938
  for (const sheet of data.sheets) {
65823
- this.sheetIdsMapName[sheet.name] = sheet.id;
65939
+ this.sheetIdsMapName[toStandardizedSheetName(sheet.name)] = sheet.id;
65824
65940
  }
65825
65941
  for (const sheetData of data.sheets) {
65826
65942
  const name = sheetData.name || "Sheet" + (Object.keys(this.sheets).length + 1);
@@ -65910,12 +66026,7 @@ stores.inject(MyMetaStore, storeInstance);
65910
66026
  }
65911
66027
  getSheetIdByName(name) {
65912
66028
  if (name) {
65913
- const unquotedName = getUnquotedSheetName(name);
65914
- for (const key in this.sheetIdsMapName) {
65915
- if (isSheetNameEqual(key, unquotedName)) {
65916
- return this.sheetIdsMapName[key];
65917
- }
65918
- }
66029
+ return this.sheetIdsMapName[toStandardizedSheetName(name)];
65919
66030
  }
65920
66031
  return undefined;
65921
66032
  }
@@ -66215,8 +66326,8 @@ stores.inject(MyMetaStore, storeInstance);
66215
66326
  const oldName = sheet.name;
66216
66327
  this.history.update("sheets", sheet.id, "name", name.trim());
66217
66328
  const sheetIdsMapName = Object.assign({}, this.sheetIdsMapName);
66218
- delete sheetIdsMapName[oldName];
66219
- sheetIdsMapName[name] = sheet.id;
66329
+ delete sheetIdsMapName[toStandardizedSheetName(oldName)];
66330
+ sheetIdsMapName[toStandardizedSheetName(name)] = sheet.id;
66220
66331
  this.history.update("sheetIdsMapName", sheetIdsMapName);
66221
66332
  }
66222
66333
  hideSheet(sheetId) {
@@ -66254,7 +66365,7 @@ stores.inject(MyMetaStore, storeInstance);
66254
66365
  });
66255
66366
  }
66256
66367
  const sheetIdsMapName = Object.assign({}, this.sheetIdsMapName);
66257
- sheetIdsMapName[newSheet.name] = newSheet.id;
66368
+ sheetIdsMapName[toStandardizedSheetName(newSheet.name)] = newSheet.id;
66258
66369
  this.history.update("sheetIdsMapName", sheetIdsMapName);
66259
66370
  }
66260
66371
  getDuplicateSheetName(sheetName) {
@@ -66271,7 +66382,7 @@ stores.inject(MyMetaStore, storeInstance);
66271
66382
  orderedSheetIds.splice(currentIndex, 1);
66272
66383
  this.history.update("orderedSheetIds", orderedSheetIds);
66273
66384
  const sheetIdsMapName = Object.assign({}, this.sheetIdsMapName);
66274
- delete sheetIdsMapName[name];
66385
+ delete sheetIdsMapName[toStandardizedSheetName(name)];
66275
66386
  this.history.update("sheetIdsMapName", sheetIdsMapName);
66276
66387
  }
66277
66388
  /**
@@ -67591,6 +67702,18 @@ stores.inject(MyMetaStore, storeInstance);
67591
67702
  }
67592
67703
  }
67593
67704
  adaptRanges(applyChange) {
67705
+ for (const pivotId in this.pivots) {
67706
+ const definition = deepCopy(this.pivots[pivotId]?.definition);
67707
+ if (!definition) {
67708
+ continue;
67709
+ }
67710
+ const newDefinition = pivotRegistry
67711
+ .get(definition.type)
67712
+ ?.adaptRanges?.(this.getters, definition, applyChange);
67713
+ if (newDefinition && !deepEquals(definition, newDefinition)) {
67714
+ this.history.update("pivots", pivotId, "definition", newDefinition);
67715
+ }
67716
+ }
67594
67717
  for (const sheetId in this.compiledMeasureFormulas) {
67595
67718
  for (const formulaString in this.compiledMeasureFormulas[sheetId]) {
67596
67719
  const compiledFormula = this.compiledMeasureFormulas[sheetId][formulaString];
@@ -67865,20 +67988,6 @@ stores.inject(MyMetaStore, storeInstance);
67865
67988
  }
67866
67989
  }
67867
67990
 
67868
- function adaptPivotRange(range, applyChange) {
67869
- if (!range) {
67870
- return undefined;
67871
- }
67872
- const change = applyChange(range);
67873
- switch (change.changeType) {
67874
- case "NONE":
67875
- return range;
67876
- case "REMOVE":
67877
- return undefined;
67878
- default:
67879
- return change.range;
67880
- }
67881
- }
67882
67991
  class SpreadsheetPivotCorePlugin extends CorePlugin {
67883
67992
  allowDispatch(cmd) {
67884
67993
  switch (cmd.type) {
@@ -67889,24 +67998,6 @@ stores.inject(MyMetaStore, storeInstance);
67889
67998
  }
67890
67999
  return "Success" /* CommandResult.Success */;
67891
68000
  }
67892
- adaptRanges(applyChange) {
67893
- for (const pivotId of this.getters.getPivotIds()) {
67894
- const definition = this.getters.getPivotCoreDefinition(pivotId);
67895
- if (definition.type !== "SPREADSHEET") {
67896
- continue;
67897
- }
67898
- if (definition.dataSet) {
67899
- const { sheetId, zone } = definition.dataSet;
67900
- const range = this.getters.getRangeFromZone(sheetId, zone);
67901
- const adaptedRange = adaptPivotRange(range, applyChange);
67902
- const dataSet = adaptedRange && {
67903
- sheetId: adaptedRange.sheetId,
67904
- zone: adaptedRange.zone,
67905
- };
67906
- this.dispatch("UPDATE_PIVOT", { pivotId, pivot: { ...definition, dataSet } });
67907
- }
67908
- }
67909
- }
67910
68001
  checkDataSetValidity(definition) {
67911
68002
  if (definition.type === "SPREADSHEET" && definition.dataSet) {
67912
68003
  const { zone, sheetId } = definition.dataSet;
@@ -68905,13 +68996,6 @@ stores.inject(MyMetaStore, storeInstance);
68905
68996
  }
68906
68997
  return result;
68907
68998
  }
68908
- size() {
68909
- let size = 0;
68910
- for (const profile of this.profiles.values()) {
68911
- size += profile.length;
68912
- }
68913
- return size / 2;
68914
- }
68915
68999
  /**
68916
69000
  * iterator of all the zones in the ZoneSet
68917
69001
  */
@@ -68993,13 +69077,6 @@ stores.inject(MyMetaStore, storeInstance);
68993
69077
  clear() {
68994
69078
  this.setsBySheetId = {};
68995
69079
  }
68996
- size() {
68997
- let size = 0;
68998
- for (const sheetId in this.setsBySheetId) {
68999
- size += this.setsBySheetId[sheetId].size();
69000
- }
69001
- return size;
69002
- }
69003
69080
  isEmpty() {
69004
69081
  for (const sheetId in this.setsBySheetId) {
69005
69082
  if (!this.setsBySheetId[sheetId].isEmpty()) {
@@ -69503,6 +69580,9 @@ stores.inject(MyMetaStore, storeInstance);
69503
69580
  const arrayFormulas = this.spreadingRelations.searchFormulaPositionsSpreadingOn(position.sheetId, positionToZone(position));
69504
69581
  return arrayFormulas.find((position) => !this.blockedArrayFormulas.has(position));
69505
69582
  }
69583
+ isArrayFormulaSpillBlocked(position) {
69584
+ return this.blockedArrayFormulas.has(position);
69585
+ }
69506
69586
  updateDependencies(position) {
69507
69587
  // removing dependencies is slow because it requires
69508
69588
  // to traverse the entire r-tree.
@@ -69514,13 +69594,8 @@ stores.inject(MyMetaStore, storeInstance);
69514
69594
  addDependencies(position, dependencies) {
69515
69595
  this.formulaDependencies().addDependencies(position, dependencies);
69516
69596
  for (const range of dependencies) {
69517
- const sheetId = range.sheetId;
69518
- const { left, bottom, right, top } = range.zone;
69519
- for (let col = left; col <= right; col++) {
69520
- for (let row = top; row <= bottom; row++) {
69521
- this.computeAndSave({ sheetId, col, row });
69522
- }
69523
- }
69597
+ // ensure that all ranges are computed
69598
+ this.compilationParams.ensureRange(range, false);
69524
69599
  }
69525
69600
  }
69526
69601
  updateCompilationParameters() {
@@ -69748,6 +69823,10 @@ stores.inject(MyMetaStore, storeInstance);
69748
69823
  this.assertSheetHasEnoughSpaceToSpreadFormulaResult(formulaPosition, formulaReturn);
69749
69824
  const nbColumns = formulaReturn.length;
69750
69825
  const nbRows = formulaReturn[0].length;
69826
+ if (nbRows === 0) {
69827
+ // empty matrix
69828
+ return createEvaluatedCell({ value: 0 }, this.getters.getLocale(), cellData);
69829
+ }
69751
69830
  const resultZone = {
69752
69831
  top: formulaPosition.row,
69753
69832
  bottom: formulaPosition.row + nbRows - 1,
@@ -70018,6 +70097,7 @@ stores.inject(MyMetaStore, storeInstance);
70018
70097
  "getEvaluatedCellsPositions",
70019
70098
  "getSpreadZone",
70020
70099
  "getArrayFormulaSpreadingOn",
70100
+ "isArrayFormulaSpillBlocked",
70021
70101
  "isEmpty",
70022
70102
  ];
70023
70103
  shouldRebuildDependenciesGraph = true;
@@ -70137,6 +70217,9 @@ stores.inject(MyMetaStore, storeInstance);
70137
70217
  getArrayFormulaSpreadingOn(position) {
70138
70218
  return this.evaluator.getArrayFormulaSpreadingOn(position);
70139
70219
  }
70220
+ isArrayFormulaSpillBlocked(position) {
70221
+ return this.evaluator.isArrayFormulaSpillBlocked(position);
70222
+ }
70140
70223
  /**
70141
70224
  * Check if a zone only contains empty cells
70142
70225
  */
@@ -72218,9 +72301,7 @@ stores.inject(MyMetaStore, storeInstance);
72218
72301
  handle(cmd) {
72219
72302
  if (invalidateEvaluationCommands.has(cmd.type)) {
72220
72303
  for (const pivotId of this.getters.getPivotIds()) {
72221
- if (!pivotRegistry.get(this.getters.getPivotCoreDefinition(pivotId).type).externalData) {
72222
- this.setupPivot(pivotId, { recreate: true });
72223
- }
72304
+ this.setupPivot(pivotId, { recreate: true });
72224
72305
  }
72225
72306
  }
72226
72307
  switch (cmd.type) {
@@ -72442,7 +72523,7 @@ stores.inject(MyMetaStore, storeInstance);
72442
72523
  pivot.init({ reload: true });
72443
72524
  }
72444
72525
  setupPivot(pivotId, { recreate } = { recreate: false }) {
72445
- const definition = this.getters.getPivotCoreDefinition(pivotId);
72526
+ const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
72446
72527
  if (!(pivotId in this.pivots)) {
72447
72528
  const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
72448
72529
  this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
@@ -81949,16 +82030,18 @@ stores.inject(MyMetaStore, storeInstance);
81949
82030
  get clickableCells() {
81950
82031
  const cells = [];
81951
82032
  const getters = this.getters;
81952
- const sheetId = getters.getActiveSheetId();
81953
82033
  for (const position of this.getters.getVisibleCellPositions()) {
81954
82034
  const item = this.getClickableItem(position);
81955
82035
  if (!item) {
81956
82036
  continue;
81957
82037
  }
81958
82038
  const title = typeof item.title === "function" ? item.title(position, getters) : item.title;
81959
- const zone = getters.expandZone(sheetId, positionToZone(position));
82039
+ const rect = this.getClickableCellRect(position);
82040
+ if (!rect) {
82041
+ continue;
82042
+ }
81960
82043
  cells.push({
81961
- coordinates: getters.getVisibleRect(zone),
82044
+ coordinates: rect,
81962
82045
  position,
81963
82046
  action: item.execute,
81964
82047
  title: title || "",
@@ -81968,6 +82051,31 @@ stores.inject(MyMetaStore, storeInstance);
81968
82051
  }
81969
82052
  return cells;
81970
82053
  }
82054
+ getClickableCellRect(position) {
82055
+ const zone = this.getters.expandZone(position.sheetId, positionToZone(position));
82056
+ const clickableRect = this.getters.getVisibleRect(zone);
82057
+ const icons = this.getters.getCellIcons(position);
82058
+ const iconsAtPosition = {
82059
+ center: icons.find((icon) => icon.horizontalAlign === "center"),
82060
+ left: icons.find((icon) => icon.horizontalAlign === "left"),
82061
+ right: icons.find((icon) => icon.horizontalAlign === "right"),
82062
+ };
82063
+ if (iconsAtPosition.center?.onClick) {
82064
+ return undefined;
82065
+ }
82066
+ if (iconsAtPosition.right?.onClick) {
82067
+ const cellRect = this.getters.getRect(zone);
82068
+ const iconRect = this.getters.getCellIconRect(iconsAtPosition.right, cellRect);
82069
+ clickableRect.width -= iconRect.width + iconsAtPosition.right.margin;
82070
+ }
82071
+ if (iconsAtPosition.left?.onClick) {
82072
+ const cellRect = this.getters.getRect(zone);
82073
+ const iconRect = this.getters.getCellIconRect(iconsAtPosition.left, cellRect);
82074
+ clickableRect.x += iconRect.width + iconsAtPosition.left.margin;
82075
+ clickableRect.width -= iconRect.width + iconsAtPosition.left.margin;
82076
+ }
82077
+ return clickableRect;
82078
+ }
81971
82079
  }
81972
82080
 
81973
82081
  css /* scss */ `
@@ -82725,7 +82833,7 @@ stores.inject(MyMetaStore, storeInstance);
82725
82833
  height: this.focus === "inactive" ? "26px" : "fit-content",
82726
82834
  "max-height": `130px`,
82727
82835
  }),
82728
- showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
82836
+ showAssistant: false, // Hide assistant in small composer as it gets cropped ATM
82729
82837
  placeholder: this.composerStore.placeholder,
82730
82838
  };
82731
82839
  }
@@ -84217,7 +84325,7 @@ stores.inject(MyMetaStore, storeInstance);
84217
84325
  document.activeElement?.contains(this.spreadsheetRef.el)) {
84218
84326
  this.focusGrid();
84219
84327
  }
84220
- }, () => [this.env.model.getters.getActiveSheetId()]);
84328
+ });
84221
84329
  owl.useExternalListener(window, "resize", () => this.render(true));
84222
84330
  // For some reason, the wheel event is not properly registered inside templates
84223
84331
  // in Chromium-based browsers based on chromium 125
@@ -89007,9 +89115,9 @@ stores.inject(MyMetaStore, storeInstance);
89007
89115
  exports.tokenize = tokenize;
89008
89116
 
89009
89117
 
89010
- __info__.version = "19.0.11";
89011
- __info__.date = "2025-11-24T07:46:47.685Z";
89012
- __info__.hash = "f5bdbcc";
89118
+ __info__.version = "19.0.15";
89119
+ __info__.date = "2025-12-26T10:19:23.408Z";
89120
+ __info__.hash = "fe625c9";
89013
89121
 
89014
89122
 
89015
89123
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);