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