@odoo/o-spreadsheet 19.0.11 → 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.11
6
- * @date 2025-11-24T07:46:47.685Z
7
- * @hash f5bdbcc
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",
@@ -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
@@ -35194,7 +35239,7 @@ class AbstractComposerStore extends SpreadsheetStore {
35194
35239
  }
35195
35240
  this.selectionStart = start;
35196
35241
  this.selectionEnd = end;
35197
- this.editionMode = "editing";
35242
+ this.stopComposerRangeSelection();
35198
35243
  this.computeFormulaCursorContext();
35199
35244
  this.computeParenthesisRelatedToCursor();
35200
35245
  this.updateAutoCompleteProvider();
@@ -44191,18 +44236,22 @@ function dropCommands(initialMessages, commandType) {
44191
44236
  return messages;
44192
44237
  }
44193
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
+ }
44194
44248
  const messages = [];
44195
44249
  const map = {};
44196
44250
  for (const sheet of data.sheets || []) {
44197
44251
  sheet.figures?.forEach((figure) => {
44198
44252
  if (figure.tag === "chart") {
44199
44253
  // chart definition
44200
- if (data.version && compareVersions(String(data.version), "18.5.1") <= 0) {
44201
- map[figure.data.chartId] = figure.data;
44202
- }
44203
- else {
44204
- map[figure.id] = figure.data;
44205
- }
44254
+ map[figure.id] = figure.data;
44206
44255
  }
44207
44256
  });
44208
44257
  }
@@ -47785,7 +47834,6 @@ const dateGranularities = [
47785
47834
  pivotRegistry.add("SPREADSHEET", {
47786
47835
  ui: SpreadsheetPivot,
47787
47836
  definition: SpreadsheetPivotRuntimeDefinition,
47788
- externalData: false,
47789
47837
  dateGranularities: [...dateGranularities],
47790
47838
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
47791
47839
  isMeasureCandidate: (field) => field.type !== "boolean",
@@ -53821,7 +53869,9 @@ class FontSizeEditor extends Component {
53821
53869
  inputRef = useRef("inputFontSize");
53822
53870
  rootEditorRef = useRef("FontSizeEditor");
53823
53871
  fontSizeListRef = useRef("fontSizeList");
53872
+ DOMFocusableElementStore;
53824
53873
  setup() {
53874
+ this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
53825
53875
  useExternalListener(window, "click", this.onExternalClick, { capture: true });
53826
53876
  }
53827
53877
  get popoverProps() {
@@ -53875,6 +53925,13 @@ class FontSizeEditor extends Component {
53875
53925
  }
53876
53926
  this.props.onToggle?.();
53877
53927
  }
53928
+ if (ev.key === "Tab") {
53929
+ ev.preventDefault();
53930
+ ev.stopPropagation();
53931
+ this.closeFontList();
53932
+ this.DOMFocusableElementStore.focus();
53933
+ return;
53934
+ }
53878
53935
  }
53879
53936
  }
53880
53937
 
@@ -55210,19 +55267,6 @@ class ChartWithAxisDesignPanel extends Component {
55210
55267
  }
55211
55268
  }
55212
55269
 
55213
- class ChartShowDataMarkers extends Component {
55214
- static template = "o-spreadsheet-ChartShowDataMarkers";
55215
- static components = {
55216
- Checkbox,
55217
- };
55218
- static props = {
55219
- chartId: String,
55220
- definition: Object,
55221
- updateChart: Function,
55222
- canUpdateChart: Function,
55223
- };
55224
- }
55225
-
55226
55270
  class GenericZoomableChartDesignPanel extends ChartWithAxisDesignPanel {
55227
55271
  static template = "o-spreadsheet-GenericZoomableChartDesignPanel";
55228
55272
  static components = {
@@ -55236,6 +55280,26 @@ class GenericZoomableChartDesignPanel extends ChartWithAxisDesignPanel {
55236
55280
  }
55237
55281
  }
55238
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
+
55239
55303
  class ComboChartDesignPanel extends GenericZoomableChartDesignPanel {
55240
55304
  static template = "o-spreadsheet-ComboChartDesignPanel";
55241
55305
  static components = {
@@ -56164,7 +56228,7 @@ chartSidePanelComponentRegistry
56164
56228
  })
56165
56229
  .add("bar", {
56166
56230
  configuration: BarConfigPanel,
56167
- design: GenericZoomableChartDesignPanel,
56231
+ design: BarChartDesignPanel,
56168
56232
  })
56169
56233
  .add("combo", {
56170
56234
  configuration: GenericChartConfigPanel,
@@ -64256,7 +64320,8 @@ class FigurePlugin extends CorePlugin {
64256
64320
  }
64257
64321
  break;
64258
64322
  case "DUPLICATE_SHEET": {
64259
- for (const figureId in this.figures[cmd.sheetId]) {
64323
+ for (const figure of this.getFigures(cmd.sheetId)) {
64324
+ const figureId = figure.id;
64260
64325
  const fig = this.figures[cmd.sheetId]?.[figureId];
64261
64326
  if (!fig) {
64262
64327
  continue;
@@ -67734,10 +67799,17 @@ class PivotCorePlugin extends CorePlugin {
67734
67799
  if (!pivot) {
67735
67800
  continue;
67736
67801
  }
67737
- for (const measure of pivot.definition.measures) {
67802
+ const def = deepCopy(pivot.definition);
67803
+ for (const measure of def.measures) {
67738
67804
  if (measure.computedBy?.formula === formulaString) {
67739
- const measureIndex = pivot.definition.measures.indexOf(measure);
67740
- 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 });
67741
67813
  }
67742
67814
  }
67743
67815
  }
@@ -67898,6 +67970,9 @@ class SpreadsheetPivotCorePlugin extends CorePlugin {
67898
67970
  const { sheetId, zone } = definition.dataSet;
67899
67971
  const range = this.getters.getRangeFromZone(sheetId, zone);
67900
67972
  const adaptedRange = adaptPivotRange(range, applyChange);
67973
+ if (adaptedRange === range) {
67974
+ return;
67975
+ }
67901
67976
  const dataSet = adaptedRange && {
67902
67977
  sheetId: adaptedRange.sheetId,
67903
67978
  zone: adaptedRange.zone,
@@ -72217,9 +72292,7 @@ class PivotUIPlugin extends CoreViewPlugin {
72217
72292
  handle(cmd) {
72218
72293
  if (invalidateEvaluationCommands.has(cmd.type)) {
72219
72294
  for (const pivotId of this.getters.getPivotIds()) {
72220
- if (!pivotRegistry.get(this.getters.getPivotCoreDefinition(pivotId).type).externalData) {
72221
- this.setupPivot(pivotId, { recreate: true });
72222
- }
72295
+ this.setupPivot(pivotId, { recreate: true });
72223
72296
  }
72224
72297
  }
72225
72298
  switch (cmd.type) {
@@ -72441,7 +72514,7 @@ class PivotUIPlugin extends CoreViewPlugin {
72441
72514
  pivot.init({ reload: true });
72442
72515
  }
72443
72516
  setupPivot(pivotId, { recreate } = { recreate: false }) {
72444
- const definition = this.getters.getPivotCoreDefinition(pivotId);
72517
+ const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
72445
72518
  if (!(pivotId in this.pivots)) {
72446
72519
  const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
72447
72520
  this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
@@ -81948,16 +82021,18 @@ class ClickableCellsStore extends SpreadsheetStore {
81948
82021
  get clickableCells() {
81949
82022
  const cells = [];
81950
82023
  const getters = this.getters;
81951
- const sheetId = getters.getActiveSheetId();
81952
82024
  for (const position of this.getters.getVisibleCellPositions()) {
81953
82025
  const item = this.getClickableItem(position);
81954
82026
  if (!item) {
81955
82027
  continue;
81956
82028
  }
81957
82029
  const title = typeof item.title === "function" ? item.title(position, getters) : item.title;
81958
- const zone = getters.expandZone(sheetId, positionToZone(position));
82030
+ const rect = this.getClickableCellRect(position);
82031
+ if (!rect) {
82032
+ continue;
82033
+ }
81959
82034
  cells.push({
81960
- coordinates: getters.getVisibleRect(zone),
82035
+ coordinates: rect,
81961
82036
  position,
81962
82037
  action: item.execute,
81963
82038
  title: title || "",
@@ -81967,6 +82042,31 @@ class ClickableCellsStore extends SpreadsheetStore {
81967
82042
  }
81968
82043
  return cells;
81969
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
+ }
81970
82070
  }
81971
82071
 
81972
82072
  css /* scss */ `
@@ -82724,7 +82824,7 @@ class SmallBottomBar extends Component {
82724
82824
  height: this.focus === "inactive" ? "26px" : "fit-content",
82725
82825
  "max-height": `130px`,
82726
82826
  }),
82727
- showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
82827
+ showAssistant: false, // Hide assistant in small composer as it gets cropped ATM
82728
82828
  placeholder: this.composerStore.placeholder,
82729
82829
  };
82730
82830
  }
@@ -88956,6 +89056,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
88956
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 };
88957
89057
 
88958
89058
 
88959
- __info__.version = "19.0.11";
88960
- __info__.date = "2025-11-24T07:46:47.685Z";
88961
- __info__.hash = "f5bdbcc";
89059
+ __info__.version = "19.0.12";
89060
+ __info__.date = "2025-12-02T05:34:17.495Z";
89061
+ __info__.hash = "32203f1";