@odoo/o-spreadsheet 19.0.10 → 19.0.12

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.10
6
- * @date 2025-11-12T14:15:51.076Z
7
- * @hash 18ac688
5
+ * @version 19.0.12
6
+ * @date 2025-12-02T05:34:17.495Z
7
+ * @hash 32203f1
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",
@@ -863,7 +862,7 @@
863
862
  };
864
863
  const PIVOT_INDENT = 15;
865
864
  const PIVOT_COLLAPSE_ICON_SIZE = 12;
866
- const PIVOT_MAX_NUMBER_OF_CELLS = 1e5;
865
+ const PIVOT_MAX_NUMBER_OF_CELLS = 5e5;
867
866
  const DEFAULT_CURRENCY = {
868
867
  symbol: "$",
869
868
  position: "before",
@@ -13349,7 +13348,7 @@ stores.inject(MyMetaStore, storeInstance);
13349
13348
  if (!acceptHiddenCells && this.getters.isRowHiddenByUser(sheetId, row))
13350
13349
  continue;
13351
13350
  for (let col = left; col <= right; col++) {
13352
- const cell = this.getters.getCell({ sheetId, col, row });
13351
+ const cell = this.getters.getCorrespondingFormulaCell({ sheetId, col, row });
13353
13352
  if (!cell || !isSubtotalCell(cell)) {
13354
13353
  evaluatedCellToKeep.push(this.getters.getEvaluatedCell({ sheetId, col, row }));
13355
13354
  }
@@ -19345,7 +19344,7 @@ stores.inject(MyMetaStore, storeInstance);
19345
19344
  function setXcToFixedReferenceType(xc, referenceType) {
19346
19345
  let sheetName;
19347
19346
  ({ sheetName, xc } = splitReference(xc));
19348
- sheetName = sheetName ? sheetName + "!" : "";
19347
+ sheetName = sheetName ? getCanonicalSymbolName(sheetName) + "!" : "";
19349
19348
  xc = xc.replace(/\$/g, "");
19350
19349
  const splitIndex = xc.indexOf(":");
19351
19350
  if (splitIndex >= 0) {
@@ -23878,6 +23877,9 @@ stores.inject(MyMetaStore, storeInstance);
23878
23877
  }));
23879
23878
  }
23880
23879
  function getTreeMapColorScale(tree, coloringOption) {
23880
+ if (tree.length === 0) {
23881
+ return undefined;
23882
+ }
23881
23883
  const treeNodesByLevel = pyramidizeTree(tree);
23882
23884
  const nodes = treeNodesByLevel[treeNodesByLevel.length - 1];
23883
23885
  const minValue = Math.min(...nodes.map((node) => node.value));
@@ -25563,11 +25565,18 @@ stores.inject(MyMetaStore, storeInstance);
25563
25565
  // we have to add the canvas to the DOM otherwise it won't be rendered
25564
25566
  document.body.append(div);
25565
25567
  if ("chartJsConfig" in runtime) {
25568
+ const extensionsLoaded = areChartJSExtensionsLoaded();
25569
+ if (!extensionsLoaded) {
25570
+ registerChartJSExtensions();
25571
+ }
25566
25572
  const config = deepCopy(runtime.chartJsConfig);
25567
25573
  config.plugins = [backgroundColorChartJSPlugin];
25568
25574
  const chart = new window.Chart(canvas, config);
25569
25575
  imageContent = chart.toBase64Image();
25570
25576
  chart.destroy();
25577
+ if (!extensionsLoaded) {
25578
+ unregisterChartJsExtensions();
25579
+ }
25571
25580
  }
25572
25581
  else if (type === "scorecard") {
25573
25582
  const design = getScorecardConfiguration(figure, runtime);
@@ -25597,11 +25606,18 @@ stores.inject(MyMetaStore, storeInstance);
25597
25606
  document.body.append(div);
25598
25607
  let chartBlob = null;
25599
25608
  if ("chartJsConfig" in runtime) {
25609
+ const extensionsLoaded = areChartJSExtensionsLoaded();
25610
+ if (!extensionsLoaded) {
25611
+ registerChartJSExtensions();
25612
+ }
25600
25613
  const config = deepCopy(runtime.chartJsConfig);
25601
25614
  config.plugins = [backgroundColorChartJSPlugin];
25602
25615
  const chart = new window.Chart(canvas, config);
25603
25616
  chartBlob = await new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
25604
25617
  chart.destroy();
25618
+ if (!extensionsLoaded) {
25619
+ unregisterChartJsExtensions();
25620
+ }
25605
25621
  }
25606
25622
  else if (type === "scorecard") {
25607
25623
  const design = getScorecardConfiguration(figure, runtime);
@@ -27727,29 +27743,19 @@ stores.inject(MyMetaStore, storeInstance);
27727
27743
  }
27728
27744
  resetAxisLimits(chartId, limits) {
27729
27745
  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;
27746
+ if (limits[axisId]) {
27747
+ this.originalAxisLimits[chartId] = {
27748
+ ...this.originalAxisLimits[chartId],
27749
+ [axisId]: { ...limits[axisId] },
27750
+ };
27739
27751
  }
27740
- else {
27741
- if (this.originalAxisLimits[chartId]?.[axisId]) {
27742
- delete this.originalAxisLimits[chartId][axisId];
27743
- }
27752
+ else if (this.originalAxisLimits[chartId]?.[axisId]) {
27753
+ delete this.originalAxisLimits[chartId][axisId];
27744
27754
  }
27745
27755
  }
27746
27756
  return "noStateChange";
27747
27757
  }
27748
27758
  updateAxisLimits(chartId, limits) {
27749
- if (limits === undefined) {
27750
- delete this.currentAxesLimits[chartId];
27751
- return "noStateChange";
27752
- }
27753
27759
  let { min, max } = limits;
27754
27760
  if (min > max) {
27755
27761
  [min, max] = [max, min];
@@ -27765,26 +27771,14 @@ stores.inject(MyMetaStore, storeInstance);
27765
27771
  * for the current trend line axes.
27766
27772
  */
27767
27773
  updateTrendLineConfiguration(chartId) {
27768
- if (!this.originalAxisLimits[chartId]) {
27774
+ if (!this.originalAxisLimits[chartId]?.x || !this.currentAxesLimits[chartId]?.x) {
27769
27775
  return "noStateChange";
27770
27776
  }
27771
27777
  const chartLimits = this.originalAxisLimits[chartId].x;
27772
- if (chartLimits === undefined) {
27773
- return "noStateChange";
27774
- }
27775
27778
  for (const axisId of TREND_LINE_AXES_IDS) {
27776
27779
  if (!this.originalAxisLimits[chartId][axisId]) {
27777
27780
  continue;
27778
27781
  }
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
27782
  const realRange = chartLimits.max - chartLimits.min;
27789
27783
  const trendingLimits = this.originalAxisLimits[chartId][axisId];
27790
27784
  const trendingRange = trendingLimits.max - trendingLimits.min;
@@ -27792,8 +27786,10 @@ stores.inject(MyMetaStore, storeInstance);
27792
27786
  const intercept = trendingLimits.min - chartLimits.min * slope;
27793
27787
  const newXMin = this.currentAxesLimits[chartId].x.min;
27794
27788
  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;
27789
+ this.currentAxesLimits[chartId][axisId] = {
27790
+ min: newXMin * slope + intercept,
27791
+ max: newXMax * slope + intercept,
27792
+ };
27797
27793
  }
27798
27794
  return "noStateChange";
27799
27795
  }
@@ -27862,8 +27858,9 @@ stores.inject(MyMetaStore, storeInstance);
27862
27858
  hasLinearScale;
27863
27859
  isBarChart;
27864
27860
  chartId = "";
27865
- datasetBoundaries = { xMin: 0, xMax: 0 };
27861
+ datasetBoundaries = { min: 0, max: 0 };
27866
27862
  removeEventListeners = () => { };
27863
+ isMasterChartAllowed = false;
27867
27864
  setup() {
27868
27865
  this.store = useStore(ZoomableChartStore);
27869
27866
  super.setup();
@@ -27879,12 +27876,19 @@ stores.inject(MyMetaStore, storeInstance);
27879
27876
  height:${height};
27880
27877
  `;
27881
27878
  }
27879
+ get masterChartContainerStyle() {
27880
+ const runtime = this.env.model.getters.getChartRuntime(this.props.chartId);
27881
+ if (runtime && !runtime.chartJsConfig.data.datasets.some((ds) => ds.data.length > 1)) {
27882
+ return "opacity: 0.3;";
27883
+ }
27884
+ return "";
27885
+ }
27882
27886
  get sliceable() {
27883
27887
  if (this.props.isFullScreen) {
27884
27888
  return true;
27885
27889
  }
27886
27890
  const definition = this.env.model.getters.getChartDefinition(this.props.chartId);
27887
- return ("zoomable" in definition && definition?.zoomable) ?? false;
27891
+ return ("zoomable" in definition && definition.zoomable) ?? false;
27888
27892
  }
27889
27893
  get axisOffset() {
27890
27894
  return !this.hasLinearScale && this.isBarChart ? 0.5 : 0;
@@ -27909,15 +27913,13 @@ stores.inject(MyMetaStore, storeInstance);
27909
27913
  if (!this.sliceable) {
27910
27914
  return chartData;
27911
27915
  }
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;
27916
+ let x = chartData.options.scales.x;
27917
+ const limits = this.store.currentAxesLimits[this.chartId]?.x;
27918
+ if (limits) {
27919
+ x = {
27920
+ ...x,
27921
+ ...this.getStoredBoundaries(),
27922
+ };
27921
27923
  }
27922
27924
  return {
27923
27925
  ...chartData,
@@ -27925,7 +27927,7 @@ stores.inject(MyMetaStore, storeInstance);
27925
27927
  ...chartData.options,
27926
27928
  scales: {
27927
27929
  ...chartData.options.scales,
27928
- x: xScale,
27930
+ x,
27929
27931
  },
27930
27932
  layout: {
27931
27933
  ...chartData.options.layout,
@@ -27940,9 +27942,19 @@ stores.inject(MyMetaStore, storeInstance);
27940
27942
  getAxisLimitsFromDataset(chartData) {
27941
27943
  const data = chartData.data.datasets.map((ds) => ds.data).flat();
27942
27944
  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 };
27945
+ const min = Math.min(...xValues);
27946
+ const max = Math.max(...xValues);
27947
+ return { min, max };
27948
+ }
27949
+ setMasterChartCursor(runtime) {
27950
+ const masterElement = this.masterChartCanvas?.el;
27951
+ if (runtime && !runtime.chartJsConfig.data.datasets.some((ds) => ds.data.length > 1)) {
27952
+ masterElement.style.cursor = "not-allowed";
27953
+ this.isMasterChartAllowed = false;
27954
+ return;
27955
+ }
27956
+ masterElement.style.cursor = "default";
27957
+ this.isMasterChartAllowed = true;
27946
27958
  }
27947
27959
  createChart(chartRuntime) {
27948
27960
  const chartData = chartRuntime.chartJsConfig;
@@ -27954,12 +27966,14 @@ stores.inject(MyMetaStore, storeInstance);
27954
27966
  chartRuntime.chartJsConfig = updatedData;
27955
27967
  }
27956
27968
  super.createChart(chartRuntime);
27957
- this.hasLinearScale = this.chart?.scales?.x.type === "linear";
27969
+ this.hasLinearScale = this.chart?.scales?.x?.type === "linear";
27958
27970
  if (!this.sliceable || !("masterChartConfig" in chartRuntime)) {
27971
+ this.isMasterChartAllowed = false;
27959
27972
  return;
27960
27973
  }
27961
27974
  this.masterChart?.destroy();
27962
- const masterChartCtx = this.masterChartCanvas.el.getContext("2d");
27975
+ const masterChartCtx = (this.masterChartCanvas?.el).getContext("2d");
27976
+ this.setMasterChartCursor(chartRuntime);
27963
27977
  this.masterChart = new window.Chart(masterChartCtx, this.getMasterChartConfiguration(chartRuntime["masterChartConfig"]));
27964
27978
  this.resetAxesLimits();
27965
27979
  if (this.chart?.options) {
@@ -27968,11 +27982,10 @@ stores.inject(MyMetaStore, storeInstance);
27968
27982
  }
27969
27983
  updateChartJs(chartRuntime) {
27970
27984
  const chartData = chartRuntime.chartJsConfig;
27971
- const newDatasetBoundaries = this.getAxisLimitsFromDataset(chartData);
27972
- if (this.datasetBoundaries.xMin !== newDatasetBoundaries.xMin ||
27973
- this.datasetBoundaries.xMax !== newDatasetBoundaries.xMax) {
27985
+ const { min, max } = this.getAxisLimitsFromDataset(chartData);
27986
+ if (this.datasetBoundaries.min !== min || this.datasetBoundaries.max !== max) {
27974
27987
  this.store.clearAxisLimits(this.chartId);
27975
- this.datasetBoundaries = newDatasetBoundaries;
27988
+ this.datasetBoundaries = { min, max };
27976
27989
  }
27977
27990
  this.isBarChart = chartData?.type === "bar";
27978
27991
  this.chartId = `${chartData.type}-${this.props.chartId}`;
@@ -27981,9 +27994,10 @@ stores.inject(MyMetaStore, storeInstance);
27981
27994
  chartRuntime.chartJsConfig = updatedData;
27982
27995
  }
27983
27996
  super.updateChartJs(chartRuntime);
27984
- this.hasLinearScale = this.chart?.scales?.x.type === "linear";
27997
+ this.hasLinearScale = this.chart?.scales?.x?.type === "linear";
27985
27998
  if (!this.sliceable || !("masterChartConfig" in chartRuntime)) {
27986
27999
  this.masterChart = undefined;
28000
+ this.isMasterChartAllowed = false;
27987
28001
  }
27988
28002
  else {
27989
28003
  const masterChartConfig = this.getMasterChartConfiguration(chartRuntime["masterChartConfig"]);
@@ -27996,6 +28010,7 @@ stores.inject(MyMetaStore, storeInstance);
27996
28010
  this.masterChart.config.options = masterChartConfig.options;
27997
28011
  this.masterChart.update();
27998
28012
  }
28013
+ this.setMasterChartCursor(chartRuntime);
27999
28014
  }
28000
28015
  this.resetAxesLimits();
28001
28016
  if (this.chart?.options) {
@@ -28006,18 +28021,15 @@ stores.inject(MyMetaStore, storeInstance);
28006
28021
  if (!this.chart) {
28007
28022
  return;
28008
28023
  }
28009
- const previousAxisLimits = this.store.originalAxisLimits[this.chartId];
28010
- if (previousAxisLimits?.x?.min === undefined && previousAxisLimits?.x?.max === undefined) {
28024
+ const storedLimits = this.store.originalAxisLimits[this.chartId]?.x;
28025
+ if (!storedLimits) {
28011
28026
  let scales = this.masterChart
28012
28027
  ? this.masterChart.scales
28013
28028
  : this.chart.scales;
28014
- if (!this.hasLinearScale && scales?.x) {
28029
+ if (!this.hasLinearScale && scales.x) {
28015
28030
  scales = {
28016
28031
  ...scales,
28017
- x: {
28018
- min: Math.ceil(scales.x.min) - this.axisOffset,
28019
- max: Math.floor(scales.x.max) + this.axisOffset,
28020
- },
28032
+ x: this.adjustBoundaries(scales.x),
28021
28033
  };
28022
28034
  }
28023
28035
  this.store.resetAxisLimits(this.chartId, scales);
@@ -28032,13 +28044,13 @@ stores.inject(MyMetaStore, storeInstance);
28032
28044
  }
28033
28045
  updateTrendingLineAxes() {
28034
28046
  this.store.updateTrendLineConfiguration(this.chartId);
28035
- const config = this.store.currentAxesLimits[this.chartId];
28047
+ const limits = this.store.currentAxesLimits[this.chartId];
28036
28048
  for (const axisId of [TREND_LINE_XAXIS_ID, MOVING_AVERAGE_TREND_LINE_XAXIS_ID]) {
28037
- if (!this.chart?.config.options?.scales?.[axisId] || !config?.[axisId]) {
28049
+ if (!this.chart?.config?.options?.scales?.[axisId] || !limits?.[axisId]) {
28038
28050
  continue;
28039
28051
  }
28040
- this.chart.config.options.scales[axisId].min = config[axisId].min;
28041
- this.chart.config.options.scales[axisId].max = config[axisId].max;
28052
+ this.chart.config.options.scales[axisId].min = limits[axisId].min;
28053
+ this.chart.config.options.scales[axisId].max = limits[axisId].max;
28042
28054
  }
28043
28055
  }
28044
28056
  get upperBound() {
@@ -28081,29 +28093,71 @@ stores.inject(MyMetaStore, storeInstance);
28081
28093
  offset +
28082
28094
  ((scale.max + 2 * offset - scale.min) * (position - left)) / (right - left));
28083
28095
  }
28084
- updateAxisLimits(xMin, xMax) {
28096
+ /**
28097
+ * Compute min and max from the store, adjusting them if needed for non linear scales.
28098
+ * Getting the value from the store, we have to ensure that the values are integers for
28099
+ * non linear scales (bar and category). To select a bar in the chart, we have to include
28100
+ * the whole bar, which means that for the i-th bar, the selected min should be <= i and
28101
+ * the selected max should be >= i, so using the Math.floor and Math.ceil functions is
28102
+ * the right way to do it.
28103
+ * Sometimes, we can get a minimal value > the maximal value, which arise when the user
28104
+ * select a very small area in the master chart, and hasn't selected the middle of a bar
28105
+ * or a group of bars (in case of more than one data series).
28106
+ * Assuming we have to select the middle of a bar/a groupe of bars, we will reject the
28107
+ * coming value afterward. In this case, we do not update the chart because it would lead
28108
+ * to an empty chart.
28109
+ */
28110
+ getStoredBoundaries() {
28111
+ let { min, max } = this.store.currentAxesLimits[this.chartId].x;
28085
28112
  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);
28113
+ min = Math.ceil(min);
28114
+ max = Math.floor(max);
28088
28115
  }
28089
- else {
28090
- this.chart.config.options.scales.x.min = xMin;
28091
- this.chart.config.options.scales.x.max = xMax;
28116
+ return { min, max };
28117
+ }
28118
+ /**
28119
+ * Adjust the min and max values of an axis if needed for non linear scales.
28120
+ * Here, after rounding (see docstring of getStoredBoundaries), we adjust the min by
28121
+ * substracting the axis offset, and we add it to the max, because when computing from the
28122
+ * scale, chartJs use integer values as the limits for non linear scales. If we have a min
28123
+ * value of 1, it means we want to start displaying from 0.5, and if we have a max value of
28124
+ * 4, it means we want to display until 4.5.
28125
+ * Here, we don't have to check if min > max because we are computing from the scale, and
28126
+ * chartJs ensures that this won't happen, even after our adjustments.
28127
+ */
28128
+ adjustBoundaries({ min, max }) {
28129
+ if (!this.hasLinearScale) {
28130
+ min = Math.ceil(min) - this.axisOffset;
28131
+ max = Math.floor(max) + this.axisOffset;
28132
+ }
28133
+ return { min, max };
28134
+ }
28135
+ updateAxisLimits(xMin, xMax) {
28136
+ if (xMin === xMax) {
28137
+ return;
28138
+ }
28139
+ if (!this.chart) {
28140
+ return;
28092
28141
  }
28093
28142
  this.store.updateAxisLimits(this.chartId, { min: xMin, max: xMax });
28094
- this.updateTrendingLineAxes();
28143
+ const { min, max } = this.getStoredBoundaries();
28144
+ if (max > min || (this.isBarChart && max === min)) {
28145
+ this.chart.config.options.scales.x.min = min;
28146
+ this.chart.config.options.scales.x.max = max;
28147
+ this.updateTrendingLineAxes();
28148
+ this.chart.update();
28149
+ }
28095
28150
  this.masterChart?.update();
28096
- this.chart?.update();
28097
28151
  }
28098
- onPointerDownInMasterChart(ev) {
28152
+ onMasterChartPointerDown(ev) {
28099
28153
  this.removeEventListeners();
28100
28154
  const position = ev.offsetX;
28101
28155
  if (!this.masterChart?.chartArea || !this.chart?.scales?.x) {
28102
28156
  return;
28103
28157
  }
28104
28158
  const { left, right, top, bottom } = this.masterChart.chartArea;
28105
- const xMax = this.upperBound ?? right;
28106
- const xMin = this.lowerBound ?? left;
28159
+ const upperBound = this.upperBound ?? right;
28160
+ const lowerBound = this.lowerBound ?? left;
28107
28161
  if (position < left - 5 || position > right + 5 || ev.offsetY < top || ev.offsetY > bottom) {
28108
28162
  return;
28109
28163
  }
@@ -28111,8 +28165,10 @@ stores.inject(MyMetaStore, storeInstance);
28111
28165
  ev.stopPropagation();
28112
28166
  let startingPositionOnChart, windowSize, startX;
28113
28167
  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;
28168
+ if ((lowerBound !== left || upperBound !== right) &&
28169
+ position > lowerBound + 5 &&
28170
+ position < upperBound - 5) {
28171
+ startingPositionOnChart = ev.offsetX - lowerBound;
28116
28172
  this.mode = "moveInMaster";
28117
28173
  const currentLimits = this.store.currentAxesLimits[this.chartId]?.x;
28118
28174
  windowSize =
@@ -28121,31 +28177,29 @@ stores.inject(MyMetaStore, storeInstance);
28121
28177
  }
28122
28178
  else {
28123
28179
  this.mode = "selectInMaster";
28124
- if (Math.abs(position - xMin) < 5) {
28125
- startingPositionOnChart = xMax;
28180
+ if (Math.abs(position - lowerBound) < 5) {
28181
+ startingPositionOnChart = upperBound;
28126
28182
  }
28127
- else if (Math.abs(position - xMax) < 5) {
28128
- startingPositionOnChart = xMin;
28183
+ else if (Math.abs(position - upperBound) < 5) {
28184
+ startingPositionOnChart = lowerBound;
28129
28185
  }
28130
28186
  else {
28131
28187
  startingPositionOnChart = clip(position, left, right);
28132
28188
  }
28133
28189
  startX = this.computeCoordinate(startingPositionOnChart);
28134
28190
  }
28135
- const originalXMin = this.store.originalAxisLimits[this.chartId].x.min;
28136
- const originalXMax = this.store.originalAxisLimits[this.chartId].x.max;
28191
+ const storedMin = this.store.originalAxisLimits[this.chartId].x.min;
28192
+ const storedMax = this.store.originalAxisLimits[this.chartId].x.max;
28137
28193
  const computeNewAxisLimits = (position) => {
28138
- let xMin, xMax;
28139
- const { left, right } = this.masterChart.chartArea;
28140
28194
  if (this.mode === "moveInMaster") {
28141
- xMin = this.computeCoordinate(position - startingPositionOnChart);
28142
- if (xMin < originalXMin) {
28143
- xMin = originalXMin;
28195
+ let min = this.computeCoordinate(position - startingPositionOnChart);
28196
+ if (min < storedMin) {
28197
+ min = storedMin;
28144
28198
  }
28145
- else if (xMin > originalXMax - windowSize) {
28146
- xMin = originalXMax - windowSize;
28199
+ else if (min > storedMax - windowSize) {
28200
+ min = storedMax - windowSize;
28147
28201
  }
28148
- xMax = xMin + windowSize;
28202
+ return { min, max: min + windowSize };
28149
28203
  }
28150
28204
  else if (this.mode === "selectInMaster") {
28151
28205
  const upperBound = clip(position, left, right);
@@ -28154,54 +28208,52 @@ stores.inject(MyMetaStore, storeInstance);
28154
28208
  if (startX === undefined || endX === undefined) {
28155
28209
  return {};
28156
28210
  }
28157
- xMin = Math.min(startX, endX);
28158
- xMax = Math.max(startX, endX);
28211
+ return {
28212
+ min: Math.min(startX, endX),
28213
+ max: Math.max(startX, endX),
28214
+ };
28159
28215
  }
28160
28216
  }
28161
- return { min: xMin, max: xMax };
28217
+ return {};
28162
28218
  };
28163
- const onDragFromMasterChart = (ev) => {
28219
+ const onMasterChartDrag = (ev) => {
28164
28220
  const position = ev.clientX - (this.masterChartCanvas.el?.getBoundingClientRect().left ?? 0);
28165
28221
  if (Math.abs(position - startingEventPosition) < 5) {
28166
28222
  return;
28167
28223
  }
28168
- const { min: xMin, max: xMax } = computeNewAxisLimits(position);
28169
- if (xMin !== undefined && xMax !== undefined) {
28170
- this.updateAxisLimits(xMin, xMax);
28224
+ const { min, max } = computeNewAxisLimits(position);
28225
+ if (min !== undefined && max !== undefined) {
28226
+ this.updateAxisLimits(min, max);
28171
28227
  }
28172
28228
  };
28173
- const onPointerUpInMasterChart = (ev) => {
28229
+ const onMasterChartPointerUp = (ev) => {
28174
28230
  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);
28231
+ let { min, max } = this.chart.scales.x;
28232
+ if (!this.hasLinearScale) {
28233
+ if (this.mode === "moveInMaster") {
28234
+ min = Math.round(min) - this.axisOffset;
28235
+ max = min + windowSize;
28236
+ }
28237
+ else {
28238
+ ({ min, max } = this.adjustBoundaries({ min, max }));
28190
28239
  }
28191
28240
  }
28241
+ this.updateAxisLimits(min, max);
28192
28242
  this.mode = undefined;
28193
28243
  };
28194
28244
  this.removeEventListeners = () => {
28195
- window.removeEventListener("pointermove", onDragFromMasterChart, true);
28196
- window.removeEventListener("pointerup", onPointerUpInMasterChart, true);
28245
+ window.removeEventListener("pointermove", onMasterChartDrag, true);
28246
+ window.removeEventListener("pointerup", onMasterChartPointerUp, true);
28197
28247
  };
28198
- window.addEventListener("pointermove", onDragFromMasterChart, true);
28199
- window.addEventListener("pointerup", onPointerUpInMasterChart, true);
28248
+ window.addEventListener("pointermove", onMasterChartDrag, true);
28249
+ window.addEventListener("pointerup", onMasterChartPointerUp, true);
28200
28250
  }
28201
- onPointerMoveInMasterChart(ev) {
28202
- const { offsetX: x, offsetY: y } = ev;
28251
+ onMasterChartPointerMove(ev) {
28252
+ const { offsetX: x, offsetY: y, target } = ev;
28253
+ if (!target || !this.isMasterChartAllowed) {
28254
+ return;
28255
+ }
28203
28256
  if (this.mode === undefined) {
28204
- const target = ev.target;
28205
28257
  if (!this.masterChart?.chartArea) {
28206
28258
  target["style"].cursor = "default";
28207
28259
  return;
@@ -28223,14 +28275,14 @@ stores.inject(MyMetaStore, storeInstance);
28223
28275
  }
28224
28276
  }
28225
28277
  }
28226
- onMouseLeaveMasterChart(ev) {
28278
+ onMasterChartMouseLeave(ev) {
28227
28279
  const target = ev.target;
28228
- if (!target) {
28280
+ if (!target || !this.isMasterChartAllowed) {
28229
28281
  return;
28230
28282
  }
28231
28283
  target["style"].cursor = "default";
28232
28284
  }
28233
- onDoubleClickInMasterChart(ev) {
28285
+ onMasterChartDoubleClick(ev) {
28234
28286
  this.mode = undefined;
28235
28287
  const position = ev.offsetX;
28236
28288
  if (!this.masterChart?.chartArea || !this.chart?.scales.x) {
@@ -28247,33 +28299,25 @@ stores.inject(MyMetaStore, storeInstance);
28247
28299
  }
28248
28300
  ev.preventDefault();
28249
28301
  ev.stopPropagation();
28250
- let { min: xMin, max: xMax } = this.store.currentAxesLimits[this.chartId]?.x ?? this.chart.scales.x;
28302
+ let { min, max } = this.store.currentAxesLimits[this.chartId]?.x ?? this.chart.scales.x;
28251
28303
  const originalAxisLimits = this.store.originalAxisLimits[this.chartId].x;
28252
28304
  if (!originalAxisLimits) {
28253
28305
  return;
28254
28306
  }
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
28307
  if (Math.abs(position - lowerBound) < 5) {
28262
- // Reset to original min
28263
- xMin = originalXMin;
28308
+ min = originalAxisLimits.min;
28264
28309
  }
28265
28310
  else if (Math.abs(position - upperBound) < 5) {
28266
- xMax = originalXMax;
28311
+ max = originalAxisLimits.max;
28267
28312
  }
28268
28313
  else if (lowerBound < position && position < upperBound) {
28269
- // Reset to original limits
28270
- xMin = originalXMin;
28271
- xMax = originalXMax;
28314
+ min = originalAxisLimits.min;
28315
+ max = originalAxisLimits.max;
28272
28316
  }
28273
28317
  else {
28274
28318
  return;
28275
28319
  }
28276
- this.updateAxisLimits(xMin, xMax);
28320
+ this.updateAxisLimits(min, max);
28277
28321
  }
28278
28322
  }
28279
28323
 
@@ -34404,7 +34448,8 @@ stores.inject(MyMetaStore, storeInstance);
34404
34448
  assistantStyle["max-height"] = `${availableSpaceAbove - CLOSE_ICON_RADIUS}px`;
34405
34449
  // render top
34406
34450
  // We compensate 2 px of margin on the assistant style + 1px for design reasons
34407
- assistantStyle.transform = `translate(0, calc(-100% - ${cellHeight + 3}px))`;
34451
+ assistantStyle.top = `-3px`;
34452
+ assistantStyle.transform = `translate(0, -100%)`;
34408
34453
  }
34409
34454
  if (cellX + ASSISTANT_WIDTH > this.props.delimitation.width) {
34410
34455
  // render left
@@ -34524,16 +34569,12 @@ stores.inject(MyMetaStore, storeInstance);
34524
34569
  processTabKey(ev, direction) {
34525
34570
  ev.preventDefault();
34526
34571
  ev.stopPropagation();
34527
- if (!this.assistant.forcedClosed) {
34528
- this.props.composerStore.autoCompleteOrStop(direction);
34529
- }
34572
+ this.props.composerStore.autoCompleteOrStop(direction, this.assistant.forcedClosed);
34530
34573
  }
34531
34574
  processEnterKey(ev, direction) {
34532
34575
  ev.preventDefault();
34533
34576
  ev.stopPropagation();
34534
- if (!this.assistant.forcedClosed) {
34535
- this.props.composerStore.autoCompleteOrStop(direction);
34536
- }
34577
+ this.props.composerStore.autoCompleteOrStop(direction, this.assistant.forcedClosed);
34537
34578
  }
34538
34579
  processNewLineEvent(ev) {
34539
34580
  ev.preventDefault();
@@ -34709,7 +34750,6 @@ stores.inject(MyMetaStore, storeInstance);
34709
34750
  return;
34710
34751
  }
34711
34752
  const newSelection = this.contentHelper.getCurrentSelection();
34712
- this.props.composerStore.stopComposerRangeSelection();
34713
34753
  const isCurrentlyInactive = this.props.composerStore.editionMode === "inactive";
34714
34754
  this.props.onComposerContentFocused(newSelection);
34715
34755
  if (!isCurrentlyInactive) {
@@ -35200,6 +35240,7 @@ stores.inject(MyMetaStore, storeInstance);
35200
35240
  }
35201
35241
  this.selectionStart = start;
35202
35242
  this.selectionEnd = end;
35243
+ this.stopComposerRangeSelection();
35203
35244
  this.computeFormulaCursorContext();
35204
35245
  this.computeParenthesisRelatedToCursor();
35205
35246
  this.updateAutoCompleteProvider();
@@ -35870,10 +35911,13 @@ stores.inject(MyMetaStore, storeInstance);
35870
35911
  hideHelp() {
35871
35912
  this.autoComplete.hide();
35872
35913
  }
35873
- autoCompleteOrStop(direction) {
35914
+ autoCompleteOrStop(direction, assistantForcedClosed = false) {
35874
35915
  if (this.editionMode !== "inactive") {
35875
35916
  const autoComplete = this.autoComplete;
35876
- if (autoComplete.provider && autoComplete.selectedIndex !== undefined) {
35917
+ const suppressAutocomplete = assistantForcedClosed && this.canBeToggled;
35918
+ if (!suppressAutocomplete &&
35919
+ autoComplete.provider &&
35920
+ autoComplete.selectedIndex !== undefined) {
35877
35921
  const autoCompleteValue = autoComplete.provider.proposals[autoComplete.selectedIndex]?.text;
35878
35922
  if (autoCompleteValue) {
35879
35923
  this.autoComplete.provider?.selectProposal(autoCompleteValue);
@@ -44193,18 +44237,22 @@ stores.inject(MyMetaStore, storeInstance);
44193
44237
  return messages;
44194
44238
  }
44195
44239
  function fixChartDefinitions(data, initialMessages) {
44240
+ /**
44241
+ * Revisions created after version 18.5.1 contain the full chart definition in the command
44242
+ * if the data was alreay updated to 18.5.1, then those older revision cannot (by definition) be reaplied
44243
+ * and should not be replayed.
44244
+ * FIXME: every command should be versionned when upgraded to allow finer tuning.
44245
+ */
44246
+ if (!data.version || compareVersions(String(data.version), "18.5.1") >= 0) {
44247
+ return initialMessages;
44248
+ }
44196
44249
  const messages = [];
44197
44250
  const map = {};
44198
44251
  for (const sheet of data.sheets || []) {
44199
44252
  sheet.figures?.forEach((figure) => {
44200
44253
  if (figure.tag === "chart") {
44201
44254
  // chart definition
44202
- if (data.version && compareVersions(String(data.version), "18.5.1") <= 0) {
44203
- map[figure.data.chartId] = figure.data;
44204
- }
44205
- else {
44206
- map[figure.id] = figure.data;
44207
- }
44255
+ map[figure.id] = figure.data;
44208
44256
  }
44209
44257
  });
44210
44258
  }
@@ -47787,7 +47835,6 @@ stores.inject(MyMetaStore, storeInstance);
47787
47835
  pivotRegistry.add("SPREADSHEET", {
47788
47836
  ui: SpreadsheetPivot,
47789
47837
  definition: SpreadsheetPivotRuntimeDefinition,
47790
- externalData: false,
47791
47838
  dateGranularities: [...dateGranularities],
47792
47839
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
47793
47840
  isMeasureCandidate: (field) => field.type !== "boolean",
@@ -51564,10 +51611,11 @@ stores.inject(MyMetaStore, storeInstance);
51564
51611
  this.state.waitingForMove = false;
51565
51612
  }
51566
51613
  onMouseMove(ev) {
51567
- if (this.env.isMobile()) {
51568
- return;
51569
- }
51570
- if (this.state.isResizing || this.state.isMoving || this.state.isSelecting) {
51614
+ if (this.env.isMobile() ||
51615
+ this.env.model.getters.isReadonly() ||
51616
+ this.state.isResizing ||
51617
+ this.state.isMoving ||
51618
+ this.state.isSelecting) {
51571
51619
  return;
51572
51620
  }
51573
51621
  this._computeHandleDisplay(ev);
@@ -51634,6 +51682,10 @@ stores.inject(MyMetaStore, storeInstance);
51634
51682
  if (index < 0) {
51635
51683
  return;
51636
51684
  }
51685
+ if (this.env.model.getters.isReadonly()) {
51686
+ this._selectElement(index, false);
51687
+ return;
51688
+ }
51637
51689
  if (this.state.waitingForMove) {
51638
51690
  if (!this.env.model.getters.isGridSelectionActive()) {
51639
51691
  this._selectElement(index, false);
@@ -52995,7 +53047,7 @@ stores.inject(MyMetaStore, storeInstance);
52995
53047
  const verticalScrollFactor = 1;
52996
53048
  const horizontalScrollFactor = 1;
52997
53049
  const resetTimeoutDuration = 100;
52998
- function useTouchScroll(ref, updateScroll, canMoveUp) {
53050
+ function useTouchScroll(ref, updateScroll, canMoveUp, canMoveDown) {
52999
53051
  let lastX = 0;
53000
53052
  let lastY = 0;
53001
53053
  let velocityX = 0;
@@ -53032,7 +53084,7 @@ stores.inject(MyMetaStore, storeInstance);
53032
53084
  lastX = clientX;
53033
53085
  lastY = clientY;
53034
53086
  lastTime = currentTime;
53035
- if (canMoveUp()) {
53087
+ if ((deltaY < 0 && canMoveUp()) || (deltaY > 0 && canMoveDown())) {
53036
53088
  if (event.cancelable) {
53037
53089
  event.preventDefault();
53038
53090
  }
@@ -53818,7 +53870,9 @@ stores.inject(MyMetaStore, storeInstance);
53818
53870
  inputRef = owl.useRef("inputFontSize");
53819
53871
  rootEditorRef = owl.useRef("FontSizeEditor");
53820
53872
  fontSizeListRef = owl.useRef("fontSizeList");
53873
+ DOMFocusableElementStore;
53821
53874
  setup() {
53875
+ this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
53822
53876
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
53823
53877
  }
53824
53878
  get popoverProps() {
@@ -53872,6 +53926,13 @@ stores.inject(MyMetaStore, storeInstance);
53872
53926
  }
53873
53927
  this.props.onToggle?.();
53874
53928
  }
53929
+ if (ev.key === "Tab") {
53930
+ ev.preventDefault();
53931
+ ev.stopPropagation();
53932
+ this.closeFontList();
53933
+ this.DOMFocusableElementStore.focus();
53934
+ return;
53935
+ }
53875
53936
  }
53876
53937
  }
53877
53938
 
@@ -55207,19 +55268,6 @@ stores.inject(MyMetaStore, storeInstance);
55207
55268
  }
55208
55269
  }
55209
55270
 
55210
- class ChartShowDataMarkers extends owl.Component {
55211
- static template = "o-spreadsheet-ChartShowDataMarkers";
55212
- static components = {
55213
- Checkbox,
55214
- };
55215
- static props = {
55216
- chartId: String,
55217
- definition: Object,
55218
- updateChart: Function,
55219
- canUpdateChart: Function,
55220
- };
55221
- }
55222
-
55223
55271
  class GenericZoomableChartDesignPanel extends ChartWithAxisDesignPanel {
55224
55272
  static template = "o-spreadsheet-GenericZoomableChartDesignPanel";
55225
55273
  static components = {
@@ -55233,6 +55281,26 @@ stores.inject(MyMetaStore, storeInstance);
55233
55281
  }
55234
55282
  }
55235
55283
 
55284
+ class BarChartDesignPanel extends GenericZoomableChartDesignPanel {
55285
+ static template = "o-spreadsheet-BarChartDesignPanel";
55286
+ get isZoomable() {
55287
+ return !this.props.definition.horizontal;
55288
+ }
55289
+ }
55290
+
55291
+ class ChartShowDataMarkers extends owl.Component {
55292
+ static template = "o-spreadsheet-ChartShowDataMarkers";
55293
+ static components = {
55294
+ Checkbox,
55295
+ };
55296
+ static props = {
55297
+ chartId: String,
55298
+ definition: Object,
55299
+ updateChart: Function,
55300
+ canUpdateChart: Function,
55301
+ };
55302
+ }
55303
+
55236
55304
  class ComboChartDesignPanel extends GenericZoomableChartDesignPanel {
55237
55305
  static template = "o-spreadsheet-ComboChartDesignPanel";
55238
55306
  static components = {
@@ -56161,7 +56229,7 @@ stores.inject(MyMetaStore, storeInstance);
56161
56229
  })
56162
56230
  .add("bar", {
56163
56231
  configuration: BarConfigPanel,
56164
- design: GenericZoomableChartDesignPanel,
56232
+ design: BarChartDesignPanel,
56165
56233
  })
56166
56234
  .add("combo", {
56167
56235
  configuration: GenericChartConfigPanel,
@@ -59270,7 +59338,6 @@ stores.inject(MyMetaStore, storeInstance);
59270
59338
  // replace the whole token
59271
59339
  start = tokenAtCursor.start;
59272
59340
  }
59273
- this.composer.stopComposerRangeSelection();
59274
59341
  this.composer.changeComposerCursorSelection(start, end);
59275
59342
  this.composer.replaceComposerCursorSelection(value);
59276
59343
  }
@@ -59288,7 +59355,6 @@ stores.inject(MyMetaStore, storeInstance);
59288
59355
  // replace the whole token
59289
59356
  start = tokenAtCursor.start;
59290
59357
  }
59291
- this.composer.stopComposerRangeSelection();
59292
59358
  this.composer.changeComposerCursorSelection(start, end);
59293
59359
  this.composer.replaceComposerCursorSelection(value);
59294
59360
  }
@@ -60749,6 +60815,7 @@ stores.inject(MyMetaStore, storeInstance);
60749
60815
  getPanelProps(panelInfo) {
60750
60816
  const state = this.computeState(panelInfo);
60751
60817
  if (state.isOpen) {
60818
+ panelInfo.currentPanelProps = state.props ?? panelInfo.currentPanelProps;
60752
60819
  return state.props ?? {};
60753
60820
  }
60754
60821
  return {};
@@ -60760,11 +60827,11 @@ stores.inject(MyMetaStore, storeInstance);
60760
60827
  }
60761
60828
  return undefined;
60762
60829
  }
60763
- open(componentTag, initialPanelProps = {}) {
60830
+ open(componentTag, currentPanelProps = {}) {
60764
60831
  if (this.screenWidthStore.isSmall) {
60765
60832
  return;
60766
60833
  }
60767
- const newPanelInfo = { initialPanelProps, componentTag, size: DEFAULT_SIDE_PANEL_SIZE };
60834
+ const newPanelInfo = { currentPanelProps, componentTag, size: DEFAULT_SIDE_PANEL_SIZE };
60768
60835
  const state = this.computeState(newPanelInfo);
60769
60836
  if (!state.isOpen) {
60770
60837
  return;
@@ -60788,8 +60855,8 @@ stores.inject(MyMetaStore, storeInstance);
60788
60855
  }
60789
60856
  this._openPanel("secondaryPanel", newPanelInfo, state);
60790
60857
  }
60791
- replace(componentTag, currentPanelKey, initialPanelProps = {}) {
60792
- const newPanelInfo = { initialPanelProps, componentTag, size: DEFAULT_SIDE_PANEL_SIZE };
60858
+ replace(componentTag, currentPanelKey, currentPanelProps = {}) {
60859
+ const newPanelInfo = { currentPanelProps, componentTag, size: DEFAULT_SIDE_PANEL_SIZE };
60793
60860
  const state = this.computeState(newPanelInfo);
60794
60861
  if (!state.isOpen) {
60795
60862
  return;
@@ -60819,10 +60886,10 @@ stores.inject(MyMetaStore, storeInstance);
60819
60886
  _openPanel(panel, newPanel, state) {
60820
60887
  const currentPanel = this[panel];
60821
60888
  if (currentPanel && newPanel.componentTag !== currentPanel.componentTag) {
60822
- currentPanel.initialPanelProps?.onCloseSidePanel?.();
60889
+ currentPanel.currentPanelProps?.onCloseSidePanel?.();
60823
60890
  }
60824
60891
  this[panel] = {
60825
- initialPanelProps: state.props ?? {},
60892
+ currentPanelProps: state.props ?? {},
60826
60893
  componentTag: newPanel.componentTag,
60827
60894
  size: currentPanel?.size || DEFAULT_SIDE_PANEL_SIZE,
60828
60895
  isCollapsed: currentPanel?.isCollapsed || false,
@@ -60844,16 +60911,16 @@ stores.inject(MyMetaStore, storeInstance);
60844
60911
  close() {
60845
60912
  if (this.mainPanel?.isPinned) {
60846
60913
  if (this.secondaryPanel) {
60847
- this.secondaryPanel.initialPanelProps.onCloseSidePanel?.();
60914
+ this.secondaryPanel.currentPanelProps.onCloseSidePanel?.();
60848
60915
  this.secondaryPanel = undefined;
60849
60916
  }
60850
60917
  return;
60851
60918
  }
60852
- this.mainPanel?.initialPanelProps.onCloseSidePanel?.();
60919
+ this.mainPanel?.currentPanelProps.onCloseSidePanel?.();
60853
60920
  this.mainPanel = undefined;
60854
60921
  }
60855
60922
  closeMainPanel() {
60856
- this.mainPanel?.initialPanelProps.onCloseSidePanel?.();
60923
+ this.mainPanel?.currentPanelProps.onCloseSidePanel?.();
60857
60924
  this.mainPanel = this.secondaryPanel || undefined;
60858
60925
  this.secondaryPanel = undefined;
60859
60926
  }
@@ -60885,7 +60952,7 @@ stores.inject(MyMetaStore, storeInstance);
60885
60952
  }
60886
60953
  this.mainPanel.isPinned = !this.mainPanel.isPinned;
60887
60954
  if (!this.mainPanel.isPinned && this.secondaryPanel) {
60888
- this.secondaryPanel?.initialPanelProps.onCloseSidePanel?.();
60955
+ this.secondaryPanel?.currentPanelProps.onCloseSidePanel?.();
60889
60956
  this.mainPanel = this.secondaryPanel;
60890
60957
  this.secondaryPanel = undefined;
60891
60958
  }
@@ -60904,7 +60971,7 @@ stores.inject(MyMetaStore, storeInstance);
60904
60971
  panelInfo.size = COLLAPSED_SIDE_PANEL_SIZE;
60905
60972
  }
60906
60973
  }
60907
- computeState({ componentTag, initialPanelProps }) {
60974
+ computeState({ componentTag, currentPanelProps: initialPanelProps, }) {
60908
60975
  const customComputeState = sidePanelRegistry.get(componentTag).computeState;
60909
60976
  const state = customComputeState
60910
60977
  ? customComputeState(this.getters, initialPanelProps)
@@ -60914,7 +60981,7 @@ stores.inject(MyMetaStore, storeInstance);
60914
60981
  changeSpreadsheetWidth(width) {
60915
60982
  this.availableWidth = width - MIN_SHEET_VIEW_WIDTH;
60916
60983
  if (this.secondaryPanel && width - this.totalPanelSize < MIN_SHEET_VIEW_WIDTH) {
60917
- this.secondaryPanel?.initialPanelProps.onCloseSidePanel?.();
60984
+ this.secondaryPanel?.currentPanelProps.onCloseSidePanel?.();
60918
60985
  this.secondaryPanel = undefined;
60919
60986
  }
60920
60987
  if (this.mainPanel && width - this.totalPanelSize < MIN_SHEET_VIEW_WIDTH) {
@@ -61074,6 +61141,10 @@ stores.inject(MyMetaStore, storeInstance);
61074
61141
  useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
61075
61142
  const { scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
61076
61143
  return scrollY > 0;
61144
+ }, () => {
61145
+ const { maxOffsetY } = this.env.model.getters.getMaximumSheetOffset();
61146
+ const { scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
61147
+ return scrollY < maxOffsetY;
61077
61148
  });
61078
61149
  }
61079
61150
  get highlights() {
@@ -62366,22 +62437,34 @@ stores.inject(MyMetaStore, storeInstance);
62366
62437
  addBorder(sheetId, zone, newBorder, force = false) {
62367
62438
  const borders = [];
62368
62439
  const plannedBorder = newBorder ? { zone, style: newBorder } : undefined;
62369
- const sideToClear = {
62370
- left: force || !!newBorder?.left,
62371
- right: force || !!newBorder?.right,
62372
- top: force || !!newBorder?.top,
62373
- bottom: force || !!newBorder?.bottom,
62440
+ // For each side, decide if we must clear the border on the *adjacent*
62441
+ // existing cell when we draw on the opposite side of the new zone.
62442
+ //
62443
+ // Example:
62444
+ // - newBorder.right is set → we draw border on the RIGHT side of `zone`
62445
+ // - the cell on the right may already have a LEFT border on that edge
62446
+ // In that case we clear that LEFT border, so only the new RIGHT border
62447
+ // remains on the shared edge.
62448
+ //
62449
+ // existingBorderSideToClear[side] = true means we should clear the border on that
62450
+ // side of the existing adjacent zone before adding the new border.
62451
+ const existingBorderSideToClear = {
62452
+ left: force || !!newBorder?.right,
62453
+ right: force || !!newBorder?.left,
62454
+ top: force || !!newBorder?.bottom,
62455
+ bottom: force || !!newBorder?.top,
62374
62456
  };
62375
62457
  let editingZone = [zone];
62376
62458
  for (const existingBorder of this.borders[sheetId] ?? []) {
62377
62459
  const inter = intersection(existingBorder.zone, zone);
62378
62460
  if (!inter) {
62379
- // Clear adjacent borders on which you write
62461
+ // Check if the existing border is adjacent to the new zone
62380
62462
  const adjacentEdge = adjacent(existingBorder.zone, zone);
62381
- if (adjacentEdge && sideToClear[adjacentEdge.position]) {
62463
+ if (adjacentEdge && existingBorderSideToClear[adjacentEdge.position]) {
62382
62464
  for (const newZone of splitIfAdjacent(existingBorder.zone, zone)) {
62383
62465
  const border = this.computeBorderFromZone(newZone, existingBorder);
62384
62466
  const adjacentEdge = adjacent(newZone, zone);
62467
+ // Clear the existing border on the side that touches the new zone
62385
62468
  switch (adjacentEdge?.position) {
62386
62469
  case "left":
62387
62470
  border.style.left = undefined;
@@ -64238,7 +64321,8 @@ stores.inject(MyMetaStore, storeInstance);
64238
64321
  }
64239
64322
  break;
64240
64323
  case "DUPLICATE_SHEET": {
64241
- for (const figureId in this.figures[cmd.sheetId]) {
64324
+ for (const figure of this.getFigures(cmd.sheetId)) {
64325
+ const figureId = figure.id;
64242
64326
  const fig = this.figures[cmd.sheetId]?.[figureId];
64243
64327
  if (!fig) {
64244
64328
  continue;
@@ -67716,10 +67800,17 @@ stores.inject(MyMetaStore, storeInstance);
67716
67800
  if (!pivot) {
67717
67801
  continue;
67718
67802
  }
67719
- for (const measure of pivot.definition.measures) {
67803
+ const def = deepCopy(pivot.definition);
67804
+ for (const measure of def.measures) {
67720
67805
  if (measure.computedBy?.formula === formulaString) {
67721
- const measureIndex = pivot.definition.measures.indexOf(measure);
67722
- this.history.update("pivots", pivotId, "definition", "measures", measureIndex, "computedBy", { formula: newFormulaString, sheetId });
67806
+ const measureIndex = def.measures.indexOf(measure);
67807
+ if (measureIndex !== -1) {
67808
+ def.measures[measureIndex].computedBy = {
67809
+ formula: newFormulaString,
67810
+ sheetId,
67811
+ };
67812
+ }
67813
+ this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
67723
67814
  }
67724
67815
  }
67725
67816
  }
@@ -67880,6 +67971,9 @@ stores.inject(MyMetaStore, storeInstance);
67880
67971
  const { sheetId, zone } = definition.dataSet;
67881
67972
  const range = this.getters.getRangeFromZone(sheetId, zone);
67882
67973
  const adaptedRange = adaptPivotRange(range, applyChange);
67974
+ if (adaptedRange === range) {
67975
+ return;
67976
+ }
67883
67977
  const dataSet = adaptedRange && {
67884
67978
  sheetId: adaptedRange.sheetId,
67885
67979
  zone: adaptedRange.zone,
@@ -72199,9 +72293,7 @@ stores.inject(MyMetaStore, storeInstance);
72199
72293
  handle(cmd) {
72200
72294
  if (invalidateEvaluationCommands.has(cmd.type)) {
72201
72295
  for (const pivotId of this.getters.getPivotIds()) {
72202
- if (!pivotRegistry.get(this.getters.getPivotCoreDefinition(pivotId).type).externalData) {
72203
- this.setupPivot(pivotId, { recreate: true });
72204
- }
72296
+ this.setupPivot(pivotId, { recreate: true });
72205
72297
  }
72206
72298
  }
72207
72299
  switch (cmd.type) {
@@ -72423,7 +72515,7 @@ stores.inject(MyMetaStore, storeInstance);
72423
72515
  pivot.init({ reload: true });
72424
72516
  }
72425
72517
  setupPivot(pivotId, { recreate } = { recreate: false }) {
72426
- const definition = this.getters.getPivotCoreDefinition(pivotId);
72518
+ const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
72427
72519
  if (!(pivotId in this.pivots)) {
72428
72520
  const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
72429
72521
  this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
@@ -78675,6 +78767,7 @@ stores.inject(MyMetaStore, storeInstance);
78675
78767
  "getFigureUI",
78676
78768
  "getPositionAnchorOffset",
78677
78769
  "getGridOffset",
78770
+ "getMaximumSheetOffset",
78678
78771
  ];
78679
78772
  viewports = {};
78680
78773
  /**
@@ -81929,16 +82022,18 @@ stores.inject(MyMetaStore, storeInstance);
81929
82022
  get clickableCells() {
81930
82023
  const cells = [];
81931
82024
  const getters = this.getters;
81932
- const sheetId = getters.getActiveSheetId();
81933
82025
  for (const position of this.getters.getVisibleCellPositions()) {
81934
82026
  const item = this.getClickableItem(position);
81935
82027
  if (!item) {
81936
82028
  continue;
81937
82029
  }
81938
82030
  const title = typeof item.title === "function" ? item.title(position, getters) : item.title;
81939
- const zone = getters.expandZone(sheetId, positionToZone(position));
82031
+ const rect = this.getClickableCellRect(position);
82032
+ if (!rect) {
82033
+ continue;
82034
+ }
81940
82035
  cells.push({
81941
- coordinates: getters.getVisibleRect(zone),
82036
+ coordinates: rect,
81942
82037
  position,
81943
82038
  action: item.execute,
81944
82039
  title: title || "",
@@ -81948,6 +82043,31 @@ stores.inject(MyMetaStore, storeInstance);
81948
82043
  }
81949
82044
  return cells;
81950
82045
  }
82046
+ getClickableCellRect(position) {
82047
+ const zone = this.getters.expandZone(position.sheetId, positionToZone(position));
82048
+ const clickableRect = this.getters.getVisibleRect(zone);
82049
+ const icons = this.getters.getCellIcons(position);
82050
+ const iconsAtPosition = {
82051
+ center: icons.find((icon) => icon.horizontalAlign === "center"),
82052
+ left: icons.find((icon) => icon.horizontalAlign === "left"),
82053
+ right: icons.find((icon) => icon.horizontalAlign === "right"),
82054
+ };
82055
+ if (iconsAtPosition.center?.onClick) {
82056
+ return undefined;
82057
+ }
82058
+ if (iconsAtPosition.right?.onClick) {
82059
+ const cellRect = this.getters.getRect(zone);
82060
+ const iconRect = this.getters.getCellIconRect(iconsAtPosition.right, cellRect);
82061
+ clickableRect.width -= iconRect.width + iconsAtPosition.right.margin;
82062
+ }
82063
+ if (iconsAtPosition.left?.onClick) {
82064
+ const cellRect = this.getters.getRect(zone);
82065
+ const iconRect = this.getters.getCellIconRect(iconsAtPosition.left, cellRect);
82066
+ clickableRect.x += iconRect.width + iconsAtPosition.left.margin;
82067
+ clickableRect.width -= iconRect.width + iconsAtPosition.left.margin;
82068
+ }
82069
+ return clickableRect;
82070
+ }
81951
82071
  }
81952
82072
 
81953
82073
  css /* scss */ `
@@ -81986,6 +82106,10 @@ stores.inject(MyMetaStore, storeInstance);
81986
82106
  useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
81987
82107
  const { scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
81988
82108
  return scrollY > 0;
82109
+ }, () => {
82110
+ const { maxOffsetY } = this.env.model.getters.getMaximumSheetOffset();
82111
+ const { scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
82112
+ return scrollY < maxOffsetY;
81989
82113
  });
81990
82114
  }
81991
82115
  get gridContainer() {
@@ -82701,7 +82825,7 @@ stores.inject(MyMetaStore, storeInstance);
82701
82825
  height: this.focus === "inactive" ? "26px" : "fit-content",
82702
82826
  "max-height": `130px`,
82703
82827
  }),
82704
- showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
82828
+ showAssistant: false, // Hide assistant in small composer as it gets cropped ATM
82705
82829
  placeholder: this.composerStore.placeholder,
82706
82830
  };
82707
82831
  }
@@ -84030,7 +84154,7 @@ stores.inject(MyMetaStore, storeInstance);
84030
84154
  border-radius: 4px;
84031
84155
  font-weight: 500;
84032
84156
  font-size: 14px;
84033
- height: 32px;
84157
+ min-height: 32px;
84034
84158
  line-height: 16px;
84035
84159
  flex-grow: 1;
84036
84160
  background-color: ${BUTTON_BG};
@@ -88983,9 +89107,9 @@ stores.inject(MyMetaStore, storeInstance);
88983
89107
  exports.tokenize = tokenize;
88984
89108
 
88985
89109
 
88986
- __info__.version = "19.0.10";
88987
- __info__.date = "2025-11-12T14:15:51.076Z";
88988
- __info__.hash = "18ac688";
89110
+ __info__.version = "19.0.12";
89111
+ __info__.date = "2025-12-02T05:34:17.495Z";
89112
+ __info__.hash = "32203f1";
88989
89113
 
88990
89114
 
88991
89115
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);