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