@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
  'use strict';
@@ -183,7 +183,6 @@ const invalidateEvaluationCommands = new Set([
183
183
  "REDO",
184
184
  "ADD_MERGE",
185
185
  "REMOVE_MERGE",
186
- "DUPLICATE_SHEET",
187
186
  "UPDATE_LOCALE",
188
187
  "ADD_PIVOT",
189
188
  "UPDATE_PIVOT",
@@ -23879,6 +23878,9 @@ function getTreeMapGroupColors(definition, tree) {
23879
23878
  }));
23880
23879
  }
23881
23880
  function getTreeMapColorScale(tree, coloringOption) {
23881
+ if (tree.length === 0) {
23882
+ return undefined;
23883
+ }
23882
23884
  const treeNodesByLevel = pyramidizeTree(tree);
23883
23885
  const nodes = treeNodesByLevel[treeNodesByLevel.length - 1];
23884
23886
  const minValue = Math.min(...nodes.map((node) => node.value));
@@ -25564,11 +25566,18 @@ function chartToImageUrl(runtime, figure, type) {
25564
25566
  // we have to add the canvas to the DOM otherwise it won't be rendered
25565
25567
  document.body.append(div);
25566
25568
  if ("chartJsConfig" in runtime) {
25569
+ const extensionsLoaded = areChartJSExtensionsLoaded();
25570
+ if (!extensionsLoaded) {
25571
+ registerChartJSExtensions();
25572
+ }
25567
25573
  const config = deepCopy(runtime.chartJsConfig);
25568
25574
  config.plugins = [backgroundColorChartJSPlugin];
25569
25575
  const chart = new window.Chart(canvas, config);
25570
25576
  imageContent = chart.toBase64Image();
25571
25577
  chart.destroy();
25578
+ if (!extensionsLoaded) {
25579
+ unregisterChartJsExtensions();
25580
+ }
25572
25581
  }
25573
25582
  else if (type === "scorecard") {
25574
25583
  const design = getScorecardConfiguration(figure, runtime);
@@ -25598,11 +25607,18 @@ async function chartToImageFile(runtime, figure, type) {
25598
25607
  document.body.append(div);
25599
25608
  let chartBlob = null;
25600
25609
  if ("chartJsConfig" in runtime) {
25610
+ const extensionsLoaded = areChartJSExtensionsLoaded();
25611
+ if (!extensionsLoaded) {
25612
+ registerChartJSExtensions();
25613
+ }
25601
25614
  const config = deepCopy(runtime.chartJsConfig);
25602
25615
  config.plugins = [backgroundColorChartJSPlugin];
25603
25616
  const chart = new window.Chart(canvas, config);
25604
25617
  chartBlob = await new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
25605
25618
  chart.destroy();
25619
+ if (!extensionsLoaded) {
25620
+ unregisterChartJsExtensions();
25621
+ }
25606
25622
  }
25607
25623
  else if (type === "scorecard") {
25608
25624
  const design = getScorecardConfiguration(figure, runtime);
@@ -27728,29 +27744,19 @@ class ZoomableChartStore extends SpreadsheetStore {
27728
27744
  }
27729
27745
  resetAxisLimits(chartId, limits) {
27730
27746
  for (const axisId of ZOOMABLE_AXIS_IDS) {
27731
- if (limits?.[axisId]) {
27732
- if (!this.originalAxisLimits[chartId]?.[axisId]) {
27733
- this.originalAxisLimits[chartId] = {
27734
- ...this.originalAxisLimits[chartId],
27735
- [axisId]: {},
27736
- };
27737
- }
27738
- this.originalAxisLimits[chartId][axisId]["min"] = limits[axisId].min;
27739
- this.originalAxisLimits[chartId][axisId]["max"] = limits[axisId].max;
27747
+ if (limits[axisId]) {
27748
+ this.originalAxisLimits[chartId] = {
27749
+ ...this.originalAxisLimits[chartId],
27750
+ [axisId]: { ...limits[axisId] },
27751
+ };
27740
27752
  }
27741
- else {
27742
- if (this.originalAxisLimits[chartId]?.[axisId]) {
27743
- delete this.originalAxisLimits[chartId][axisId];
27744
- }
27753
+ else if (this.originalAxisLimits[chartId]?.[axisId]) {
27754
+ delete this.originalAxisLimits[chartId][axisId];
27745
27755
  }
27746
27756
  }
27747
27757
  return "noStateChange";
27748
27758
  }
27749
27759
  updateAxisLimits(chartId, limits) {
27750
- if (limits === undefined) {
27751
- delete this.currentAxesLimits[chartId];
27752
- return "noStateChange";
27753
- }
27754
27760
  let { min, max } = limits;
27755
27761
  if (min > max) {
27756
27762
  [min, max] = [max, min];
@@ -27766,26 +27772,14 @@ class ZoomableChartStore extends SpreadsheetStore {
27766
27772
  * for the current trend line axes.
27767
27773
  */
27768
27774
  updateTrendLineConfiguration(chartId) {
27769
- if (!this.originalAxisLimits[chartId]) {
27775
+ if (!this.originalAxisLimits[chartId]?.x || !this.currentAxesLimits[chartId]?.x) {
27770
27776
  return "noStateChange";
27771
27777
  }
27772
27778
  const chartLimits = this.originalAxisLimits[chartId].x;
27773
- if (chartLimits === undefined) {
27774
- return "noStateChange";
27775
- }
27776
27779
  for (const axisId of TREND_LINE_AXES_IDS) {
27777
27780
  if (!this.originalAxisLimits[chartId][axisId]) {
27778
27781
  continue;
27779
27782
  }
27780
- if (!this.currentAxesLimits[chartId]?.[axisId]) {
27781
- this.currentAxesLimits[chartId] = {
27782
- ...this.currentAxesLimits[chartId],
27783
- [axisId]: {},
27784
- };
27785
- }
27786
- if (this.currentAxesLimits[chartId]?.x === undefined) {
27787
- return "noStateChange";
27788
- }
27789
27783
  const realRange = chartLimits.max - chartLimits.min;
27790
27784
  const trendingLimits = this.originalAxisLimits[chartId][axisId];
27791
27785
  const trendingRange = trendingLimits.max - trendingLimits.min;
@@ -27793,8 +27787,10 @@ class ZoomableChartStore extends SpreadsheetStore {
27793
27787
  const intercept = trendingLimits.min - chartLimits.min * slope;
27794
27788
  const newXMin = this.currentAxesLimits[chartId].x.min;
27795
27789
  const newXMax = this.currentAxesLimits[chartId].x.max;
27796
- this.currentAxesLimits[chartId][axisId].min = newXMin * slope + intercept;
27797
- this.currentAxesLimits[chartId][axisId].max = newXMax * slope + intercept;
27790
+ this.currentAxesLimits[chartId][axisId] = {
27791
+ min: newXMin * slope + intercept,
27792
+ max: newXMax * slope + intercept,
27793
+ };
27798
27794
  }
27799
27795
  return "noStateChange";
27800
27796
  }
@@ -27863,8 +27859,9 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27863
27859
  hasLinearScale;
27864
27860
  isBarChart;
27865
27861
  chartId = "";
27866
- datasetBoundaries = { xMin: 0, xMax: 0 };
27862
+ datasetBoundaries = { min: 0, max: 0 };
27867
27863
  removeEventListeners = () => { };
27864
+ isMasterChartAllowed = false;
27868
27865
  setup() {
27869
27866
  this.store = useStore(ZoomableChartStore);
27870
27867
  super.setup();
@@ -27880,12 +27877,19 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27880
27877
  height:${height};
27881
27878
  `;
27882
27879
  }
27880
+ get masterChartContainerStyle() {
27881
+ const runtime = this.env.model.getters.getChartRuntime(this.props.chartId);
27882
+ if (runtime && !runtime.chartJsConfig.data.datasets.some((ds) => ds.data.length > 1)) {
27883
+ return "opacity: 0.3;";
27884
+ }
27885
+ return "";
27886
+ }
27883
27887
  get sliceable() {
27884
27888
  if (this.props.isFullScreen) {
27885
27889
  return true;
27886
27890
  }
27887
27891
  const definition = this.env.model.getters.getChartDefinition(this.props.chartId);
27888
- return ("zoomable" in definition && definition?.zoomable) ?? false;
27892
+ return ("zoomable" in definition && definition.zoomable) ?? false;
27889
27893
  }
27890
27894
  get axisOffset() {
27891
27895
  return !this.hasLinearScale && this.isBarChart ? 0.5 : 0;
@@ -27910,15 +27914,13 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27910
27914
  if (!this.sliceable) {
27911
27915
  return chartData;
27912
27916
  }
27913
- const xAxis = this.store.currentAxesLimits[this.chartId]?.x;
27914
- const xScale = {
27915
- ...chartData.options.scales?.x,
27916
- };
27917
- if (xAxis?.min !== undefined) {
27918
- xScale.min = this.hasLinearScale ? xAxis.min : Math.ceil(xAxis.min) - this.axisOffset;
27919
- }
27920
- if (xAxis?.max !== undefined) {
27921
- xScale.max = this.hasLinearScale ? xAxis.max : Math.floor(xAxis.max) - this.axisOffset;
27917
+ let x = chartData.options.scales.x;
27918
+ const limits = this.store.currentAxesLimits[this.chartId]?.x;
27919
+ if (limits) {
27920
+ x = {
27921
+ ...x,
27922
+ ...this.getStoredBoundaries(),
27923
+ };
27922
27924
  }
27923
27925
  return {
27924
27926
  ...chartData,
@@ -27926,7 +27928,7 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27926
27928
  ...chartData.options,
27927
27929
  scales: {
27928
27930
  ...chartData.options.scales,
27929
- x: xScale,
27931
+ x,
27930
27932
  },
27931
27933
  layout: {
27932
27934
  ...chartData.options.layout,
@@ -27941,9 +27943,19 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27941
27943
  getAxisLimitsFromDataset(chartData) {
27942
27944
  const data = chartData.data.datasets.map((ds) => ds.data).flat();
27943
27945
  const xValues = data.map((d, i) => (typeof d === "object" && d !== null ? d.x : i));
27944
- const xMin = Math.min(...xValues);
27945
- const xMax = Math.max(...xValues);
27946
- return { xMin, xMax };
27946
+ const min = Math.min(...xValues);
27947
+ const max = Math.max(...xValues);
27948
+ return { min, max };
27949
+ }
27950
+ setMasterChartCursor(runtime) {
27951
+ const masterElement = this.masterChartCanvas?.el;
27952
+ if (runtime && !runtime.chartJsConfig.data.datasets.some((ds) => ds.data.length > 1)) {
27953
+ masterElement.style.cursor = "not-allowed";
27954
+ this.isMasterChartAllowed = false;
27955
+ return;
27956
+ }
27957
+ masterElement.style.cursor = "default";
27958
+ this.isMasterChartAllowed = true;
27947
27959
  }
27948
27960
  createChart(chartRuntime) {
27949
27961
  const chartData = chartRuntime.chartJsConfig;
@@ -27955,12 +27967,14 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27955
27967
  chartRuntime.chartJsConfig = updatedData;
27956
27968
  }
27957
27969
  super.createChart(chartRuntime);
27958
- this.hasLinearScale = this.chart?.scales?.x.type === "linear";
27970
+ this.hasLinearScale = this.chart?.scales?.x?.type === "linear";
27959
27971
  if (!this.sliceable || !("masterChartConfig" in chartRuntime)) {
27972
+ this.isMasterChartAllowed = false;
27960
27973
  return;
27961
27974
  }
27962
27975
  this.masterChart?.destroy();
27963
- const masterChartCtx = this.masterChartCanvas.el.getContext("2d");
27976
+ const masterChartCtx = (this.masterChartCanvas?.el).getContext("2d");
27977
+ this.setMasterChartCursor(chartRuntime);
27964
27978
  this.masterChart = new window.Chart(masterChartCtx, this.getMasterChartConfiguration(chartRuntime["masterChartConfig"]));
27965
27979
  this.resetAxesLimits();
27966
27980
  if (this.chart?.options) {
@@ -27969,11 +27983,10 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27969
27983
  }
27970
27984
  updateChartJs(chartRuntime) {
27971
27985
  const chartData = chartRuntime.chartJsConfig;
27972
- const newDatasetBoundaries = this.getAxisLimitsFromDataset(chartData);
27973
- if (this.datasetBoundaries.xMin !== newDatasetBoundaries.xMin ||
27974
- this.datasetBoundaries.xMax !== newDatasetBoundaries.xMax) {
27986
+ const { min, max } = this.getAxisLimitsFromDataset(chartData);
27987
+ if (this.datasetBoundaries.min !== min || this.datasetBoundaries.max !== max) {
27975
27988
  this.store.clearAxisLimits(this.chartId);
27976
- this.datasetBoundaries = newDatasetBoundaries;
27989
+ this.datasetBoundaries = { min, max };
27977
27990
  }
27978
27991
  this.isBarChart = chartData?.type === "bar";
27979
27992
  this.chartId = `${chartData.type}-${this.props.chartId}`;
@@ -27982,9 +27995,10 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27982
27995
  chartRuntime.chartJsConfig = updatedData;
27983
27996
  }
27984
27997
  super.updateChartJs(chartRuntime);
27985
- this.hasLinearScale = this.chart?.scales?.x.type === "linear";
27998
+ this.hasLinearScale = this.chart?.scales?.x?.type === "linear";
27986
27999
  if (!this.sliceable || !("masterChartConfig" in chartRuntime)) {
27987
28000
  this.masterChart = undefined;
28001
+ this.isMasterChartAllowed = false;
27988
28002
  }
27989
28003
  else {
27990
28004
  const masterChartConfig = this.getMasterChartConfiguration(chartRuntime["masterChartConfig"]);
@@ -27997,6 +28011,7 @@ class ZoomableChartJsComponent extends ChartJsComponent {
27997
28011
  this.masterChart.config.options = masterChartConfig.options;
27998
28012
  this.masterChart.update();
27999
28013
  }
28014
+ this.setMasterChartCursor(chartRuntime);
28000
28015
  }
28001
28016
  this.resetAxesLimits();
28002
28017
  if (this.chart?.options) {
@@ -28007,18 +28022,15 @@ class ZoomableChartJsComponent extends ChartJsComponent {
28007
28022
  if (!this.chart) {
28008
28023
  return;
28009
28024
  }
28010
- const previousAxisLimits = this.store.originalAxisLimits[this.chartId];
28011
- if (previousAxisLimits?.x?.min === undefined && previousAxisLimits?.x?.max === undefined) {
28025
+ const storedLimits = this.store.originalAxisLimits[this.chartId]?.x;
28026
+ if (!storedLimits) {
28012
28027
  let scales = this.masterChart
28013
28028
  ? this.masterChart.scales
28014
28029
  : this.chart.scales;
28015
- if (!this.hasLinearScale && scales?.x) {
28030
+ if (!this.hasLinearScale && scales.x) {
28016
28031
  scales = {
28017
28032
  ...scales,
28018
- x: {
28019
- min: Math.ceil(scales.x.min) - this.axisOffset,
28020
- max: Math.floor(scales.x.max) + this.axisOffset,
28021
- },
28033
+ x: this.adjustBoundaries(scales.x),
28022
28034
  };
28023
28035
  }
28024
28036
  this.store.resetAxisLimits(this.chartId, scales);
@@ -28033,13 +28045,13 @@ class ZoomableChartJsComponent extends ChartJsComponent {
28033
28045
  }
28034
28046
  updateTrendingLineAxes() {
28035
28047
  this.store.updateTrendLineConfiguration(this.chartId);
28036
- const config = this.store.currentAxesLimits[this.chartId];
28048
+ const limits = this.store.currentAxesLimits[this.chartId];
28037
28049
  for (const axisId of [TREND_LINE_XAXIS_ID, MOVING_AVERAGE_TREND_LINE_XAXIS_ID]) {
28038
- if (!this.chart?.config.options?.scales?.[axisId] || !config?.[axisId]) {
28050
+ if (!this.chart?.config?.options?.scales?.[axisId] || !limits?.[axisId]) {
28039
28051
  continue;
28040
28052
  }
28041
- this.chart.config.options.scales[axisId].min = config[axisId].min;
28042
- this.chart.config.options.scales[axisId].max = config[axisId].max;
28053
+ this.chart.config.options.scales[axisId].min = limits[axisId].min;
28054
+ this.chart.config.options.scales[axisId].max = limits[axisId].max;
28043
28055
  }
28044
28056
  }
28045
28057
  get upperBound() {
@@ -28082,29 +28094,71 @@ class ZoomableChartJsComponent extends ChartJsComponent {
28082
28094
  offset +
28083
28095
  ((scale.max + 2 * offset - scale.min) * (position - left)) / (right - left));
28084
28096
  }
28085
- updateAxisLimits(xMin, xMax) {
28097
+ /**
28098
+ * Compute min and max from the store, adjusting them if needed for non linear scales.
28099
+ * Getting the value from the store, we have to ensure that the values are integers for
28100
+ * non linear scales (bar and category). To select a bar in the chart, we have to include
28101
+ * the whole bar, which means that for the i-th bar, the selected min should be <= i and
28102
+ * the selected max should be >= i, so using the Math.floor and Math.ceil functions is
28103
+ * the right way to do it.
28104
+ * Sometimes, we can get a minimal value > the maximal value, which arise when the user
28105
+ * select a very small area in the master chart, and hasn't selected the middle of a bar
28106
+ * or a group of bars (in case of more than one data series).
28107
+ * Assuming we have to select the middle of a bar/a groupe of bars, we will reject the
28108
+ * coming value afterward. In this case, we do not update the chart because it would lead
28109
+ * to an empty chart.
28110
+ */
28111
+ getStoredBoundaries() {
28112
+ let { min, max } = this.store.currentAxesLimits[this.chartId].x;
28086
28113
  if (!this.hasLinearScale) {
28087
- this.chart.config.options.scales.x.min = Math.ceil(xMin);
28088
- this.chart.config.options.scales.x.max = Math.floor(xMax);
28114
+ min = Math.ceil(min);
28115
+ max = Math.floor(max);
28089
28116
  }
28090
- else {
28091
- this.chart.config.options.scales.x.min = xMin;
28092
- this.chart.config.options.scales.x.max = xMax;
28117
+ return { min, max };
28118
+ }
28119
+ /**
28120
+ * Adjust the min and max values of an axis if needed for non linear scales.
28121
+ * Here, after rounding (see docstring of getStoredBoundaries), we adjust the min by
28122
+ * substracting the axis offset, and we add it to the max, because when computing from the
28123
+ * scale, chartJs use integer values as the limits for non linear scales. If we have a min
28124
+ * value of 1, it means we want to start displaying from 0.5, and if we have a max value of
28125
+ * 4, it means we want to display until 4.5.
28126
+ * Here, we don't have to check if min > max because we are computing from the scale, and
28127
+ * chartJs ensures that this won't happen, even after our adjustments.
28128
+ */
28129
+ adjustBoundaries({ min, max }) {
28130
+ if (!this.hasLinearScale) {
28131
+ min = Math.ceil(min) - this.axisOffset;
28132
+ max = Math.floor(max) + this.axisOffset;
28133
+ }
28134
+ return { min, max };
28135
+ }
28136
+ updateAxisLimits(xMin, xMax) {
28137
+ if (xMin === xMax) {
28138
+ return;
28139
+ }
28140
+ if (!this.chart) {
28141
+ return;
28093
28142
  }
28094
28143
  this.store.updateAxisLimits(this.chartId, { min: xMin, max: xMax });
28095
- this.updateTrendingLineAxes();
28144
+ const { min, max } = this.getStoredBoundaries();
28145
+ if (max > min || (this.isBarChart && max === min)) {
28146
+ this.chart.config.options.scales.x.min = min;
28147
+ this.chart.config.options.scales.x.max = max;
28148
+ this.updateTrendingLineAxes();
28149
+ this.chart.update();
28150
+ }
28096
28151
  this.masterChart?.update();
28097
- this.chart?.update();
28098
28152
  }
28099
- onPointerDownInMasterChart(ev) {
28153
+ onMasterChartPointerDown(ev) {
28100
28154
  this.removeEventListeners();
28101
28155
  const position = ev.offsetX;
28102
28156
  if (!this.masterChart?.chartArea || !this.chart?.scales?.x) {
28103
28157
  return;
28104
28158
  }
28105
28159
  const { left, right, top, bottom } = this.masterChart.chartArea;
28106
- const xMax = this.upperBound ?? right;
28107
- const xMin = this.lowerBound ?? left;
28160
+ const upperBound = this.upperBound ?? right;
28161
+ const lowerBound = this.lowerBound ?? left;
28108
28162
  if (position < left - 5 || position > right + 5 || ev.offsetY < top || ev.offsetY > bottom) {
28109
28163
  return;
28110
28164
  }
@@ -28112,8 +28166,10 @@ class ZoomableChartJsComponent extends ChartJsComponent {
28112
28166
  ev.stopPropagation();
28113
28167
  let startingPositionOnChart, windowSize, startX;
28114
28168
  const startingEventPosition = ev.clientX - (this.masterChartCanvas.el?.getBoundingClientRect().left ?? 0);
28115
- if ((xMin !== left || xMax !== right) && position > xMin + 5 && position < xMax - 5) {
28116
- startingPositionOnChart = ev.offsetX - xMin;
28169
+ if ((lowerBound !== left || upperBound !== right) &&
28170
+ position > lowerBound + 5 &&
28171
+ position < upperBound - 5) {
28172
+ startingPositionOnChart = ev.offsetX - lowerBound;
28117
28173
  this.mode = "moveInMaster";
28118
28174
  const currentLimits = this.store.currentAxesLimits[this.chartId]?.x;
28119
28175
  windowSize =
@@ -28122,31 +28178,29 @@ class ZoomableChartJsComponent extends ChartJsComponent {
28122
28178
  }
28123
28179
  else {
28124
28180
  this.mode = "selectInMaster";
28125
- if (Math.abs(position - xMin) < 5) {
28126
- startingPositionOnChart = xMax;
28181
+ if (Math.abs(position - lowerBound) < 5) {
28182
+ startingPositionOnChart = upperBound;
28127
28183
  }
28128
- else if (Math.abs(position - xMax) < 5) {
28129
- startingPositionOnChart = xMin;
28184
+ else if (Math.abs(position - upperBound) < 5) {
28185
+ startingPositionOnChart = lowerBound;
28130
28186
  }
28131
28187
  else {
28132
28188
  startingPositionOnChart = clip(position, left, right);
28133
28189
  }
28134
28190
  startX = this.computeCoordinate(startingPositionOnChart);
28135
28191
  }
28136
- const originalXMin = this.store.originalAxisLimits[this.chartId].x.min;
28137
- const originalXMax = this.store.originalAxisLimits[this.chartId].x.max;
28192
+ const storedMin = this.store.originalAxisLimits[this.chartId].x.min;
28193
+ const storedMax = this.store.originalAxisLimits[this.chartId].x.max;
28138
28194
  const computeNewAxisLimits = (position) => {
28139
- let xMin, xMax;
28140
- const { left, right } = this.masterChart.chartArea;
28141
28195
  if (this.mode === "moveInMaster") {
28142
- xMin = this.computeCoordinate(position - startingPositionOnChart);
28143
- if (xMin < originalXMin) {
28144
- xMin = originalXMin;
28196
+ let min = this.computeCoordinate(position - startingPositionOnChart);
28197
+ if (min < storedMin) {
28198
+ min = storedMin;
28145
28199
  }
28146
- else if (xMin > originalXMax - windowSize) {
28147
- xMin = originalXMax - windowSize;
28200
+ else if (min > storedMax - windowSize) {
28201
+ min = storedMax - windowSize;
28148
28202
  }
28149
- xMax = xMin + windowSize;
28203
+ return { min, max: min + windowSize };
28150
28204
  }
28151
28205
  else if (this.mode === "selectInMaster") {
28152
28206
  const upperBound = clip(position, left, right);
@@ -28155,54 +28209,52 @@ class ZoomableChartJsComponent extends ChartJsComponent {
28155
28209
  if (startX === undefined || endX === undefined) {
28156
28210
  return {};
28157
28211
  }
28158
- xMin = Math.min(startX, endX);
28159
- xMax = Math.max(startX, endX);
28212
+ return {
28213
+ min: Math.min(startX, endX),
28214
+ max: Math.max(startX, endX),
28215
+ };
28160
28216
  }
28161
28217
  }
28162
- return { min: xMin, max: xMax };
28218
+ return {};
28163
28219
  };
28164
- const onDragFromMasterChart = (ev) => {
28220
+ const onMasterChartDrag = (ev) => {
28165
28221
  const position = ev.clientX - (this.masterChartCanvas.el?.getBoundingClientRect().left ?? 0);
28166
28222
  if (Math.abs(position - startingEventPosition) < 5) {
28167
28223
  return;
28168
28224
  }
28169
- const { min: xMin, max: xMax } = computeNewAxisLimits(position);
28170
- if (xMin !== undefined && xMax !== undefined) {
28171
- this.updateAxisLimits(xMin, xMax);
28225
+ const { min, max } = computeNewAxisLimits(position);
28226
+ if (min !== undefined && max !== undefined) {
28227
+ this.updateAxisLimits(min, max);
28172
28228
  }
28173
28229
  };
28174
- const onPointerUpInMasterChart = (ev) => {
28230
+ const onMasterChartPointerUp = (ev) => {
28175
28231
  this.removeEventListeners();
28176
- const position = ev.clientX - (this.masterChartCanvas.el?.getBoundingClientRect().left ?? 0);
28177
- if (Math.abs(position - startingEventPosition) > 5) {
28178
- let { min: xMin, max: xMax } = computeNewAxisLimits(position);
28179
- if (xMin !== undefined && xMax !== undefined) {
28180
- if (!this.hasLinearScale) {
28181
- if (this.mode === "moveInMaster" && windowSize && !this.isBarChart) {
28182
- xMin = Math.round(xMin) - this.axisOffset;
28183
- xMax = xMin + windowSize;
28184
- }
28185
- else {
28186
- xMin = Math.ceil(xMin) - this.axisOffset;
28187
- xMax = Math.floor(xMax) + this.axisOffset;
28188
- }
28189
- }
28190
- this.updateAxisLimits(xMin, xMax);
28232
+ let { min, max } = this.chart.scales.x;
28233
+ if (!this.hasLinearScale) {
28234
+ if (this.mode === "moveInMaster") {
28235
+ min = Math.round(min) - this.axisOffset;
28236
+ max = min + windowSize;
28237
+ }
28238
+ else {
28239
+ ({ min, max } = this.adjustBoundaries({ min, max }));
28191
28240
  }
28192
28241
  }
28242
+ this.updateAxisLimits(min, max);
28193
28243
  this.mode = undefined;
28194
28244
  };
28195
28245
  this.removeEventListeners = () => {
28196
- window.removeEventListener("pointermove", onDragFromMasterChart, true);
28197
- window.removeEventListener("pointerup", onPointerUpInMasterChart, true);
28246
+ window.removeEventListener("pointermove", onMasterChartDrag, true);
28247
+ window.removeEventListener("pointerup", onMasterChartPointerUp, true);
28198
28248
  };
28199
- window.addEventListener("pointermove", onDragFromMasterChart, true);
28200
- window.addEventListener("pointerup", onPointerUpInMasterChart, true);
28249
+ window.addEventListener("pointermove", onMasterChartDrag, true);
28250
+ window.addEventListener("pointerup", onMasterChartPointerUp, true);
28201
28251
  }
28202
- onPointerMoveInMasterChart(ev) {
28203
- const { offsetX: x, offsetY: y } = ev;
28252
+ onMasterChartPointerMove(ev) {
28253
+ const { offsetX: x, offsetY: y, target } = ev;
28254
+ if (!target || !this.isMasterChartAllowed) {
28255
+ return;
28256
+ }
28204
28257
  if (this.mode === undefined) {
28205
- const target = ev.target;
28206
28258
  if (!this.masterChart?.chartArea) {
28207
28259
  target["style"].cursor = "default";
28208
28260
  return;
@@ -28224,14 +28276,14 @@ class ZoomableChartJsComponent extends ChartJsComponent {
28224
28276
  }
28225
28277
  }
28226
28278
  }
28227
- onMouseLeaveMasterChart(ev) {
28279
+ onMasterChartMouseLeave(ev) {
28228
28280
  const target = ev.target;
28229
- if (!target) {
28281
+ if (!target || !this.isMasterChartAllowed) {
28230
28282
  return;
28231
28283
  }
28232
28284
  target["style"].cursor = "default";
28233
28285
  }
28234
- onDoubleClickInMasterChart(ev) {
28286
+ onMasterChartDoubleClick(ev) {
28235
28287
  this.mode = undefined;
28236
28288
  const position = ev.offsetX;
28237
28289
  if (!this.masterChart?.chartArea || !this.chart?.scales.x) {
@@ -28248,33 +28300,25 @@ class ZoomableChartJsComponent extends ChartJsComponent {
28248
28300
  }
28249
28301
  ev.preventDefault();
28250
28302
  ev.stopPropagation();
28251
- let { min: xMin, max: xMax } = this.store.currentAxesLimits[this.chartId]?.x ?? this.chart.scales.x;
28303
+ let { min, max } = this.store.currentAxesLimits[this.chartId]?.x ?? this.chart.scales.x;
28252
28304
  const originalAxisLimits = this.store.originalAxisLimits[this.chartId].x;
28253
28305
  if (!originalAxisLimits) {
28254
28306
  return;
28255
28307
  }
28256
- let originalXMin = originalAxisLimits.min;
28257
- let originalXMax = originalAxisLimits.max;
28258
- if (this.hasLinearScale) {
28259
- originalXMin = Math.ceil(originalXMin) - this.axisOffset;
28260
- originalXMax = Math.floor(originalXMax) + this.axisOffset;
28261
- }
28262
28308
  if (Math.abs(position - lowerBound) < 5) {
28263
- // Reset to original min
28264
- xMin = originalXMin;
28309
+ min = originalAxisLimits.min;
28265
28310
  }
28266
28311
  else if (Math.abs(position - upperBound) < 5) {
28267
- xMax = originalXMax;
28312
+ max = originalAxisLimits.max;
28268
28313
  }
28269
28314
  else if (lowerBound < position && position < upperBound) {
28270
- // Reset to original limits
28271
- xMin = originalXMin;
28272
- xMax = originalXMax;
28315
+ min = originalAxisLimits.min;
28316
+ max = originalAxisLimits.max;
28273
28317
  }
28274
28318
  else {
28275
28319
  return;
28276
28320
  }
28277
- this.updateAxisLimits(xMin, xMax);
28321
+ this.updateAxisLimits(min, max);
28278
28322
  }
28279
28323
  }
28280
28324
 
@@ -34405,7 +34449,8 @@ class Composer extends owl.Component {
34405
34449
  assistantStyle["max-height"] = `${availableSpaceAbove - CLOSE_ICON_RADIUS}px`;
34406
34450
  // render top
34407
34451
  // We compensate 2 px of margin on the assistant style + 1px for design reasons
34408
- assistantStyle.transform = `translate(0, calc(-100% - ${cellHeight + 3}px))`;
34452
+ assistantStyle.top = `-3px`;
34453
+ assistantStyle.transform = `translate(0, -100%)`;
34409
34454
  }
34410
34455
  if (cellX + ASSISTANT_WIDTH > this.props.delimitation.width) {
34411
34456
  // render left
@@ -35196,7 +35241,7 @@ class AbstractComposerStore extends SpreadsheetStore {
35196
35241
  }
35197
35242
  this.selectionStart = start;
35198
35243
  this.selectionEnd = end;
35199
- this.editionMode = "editing";
35244
+ this.stopComposerRangeSelection();
35200
35245
  this.computeFormulaCursorContext();
35201
35246
  this.computeParenthesisRelatedToCursor();
35202
35247
  this.updateAutoCompleteProvider();
@@ -44193,18 +44238,22 @@ function dropCommands(initialMessages, commandType) {
44193
44238
  return messages;
44194
44239
  }
44195
44240
  function fixChartDefinitions(data, initialMessages) {
44241
+ /**
44242
+ * Revisions created after version 18.5.1 contain the full chart definition in the command
44243
+ * if the data was alreay updated to 18.5.1, then those older revision cannot (by definition) be reaplied
44244
+ * and should not be replayed.
44245
+ * FIXME: every command should be versionned when upgraded to allow finer tuning.
44246
+ */
44247
+ if (!data.version || compareVersions(String(data.version), "18.5.1") >= 0) {
44248
+ return initialMessages;
44249
+ }
44196
44250
  const messages = [];
44197
44251
  const map = {};
44198
44252
  for (const sheet of data.sheets || []) {
44199
44253
  sheet.figures?.forEach((figure) => {
44200
44254
  if (figure.tag === "chart") {
44201
44255
  // chart definition
44202
- if (data.version && compareVersions(String(data.version), "18.5.1") <= 0) {
44203
- map[figure.data.chartId] = figure.data;
44204
- }
44205
- else {
44206
- map[figure.id] = figure.data;
44207
- }
44256
+ map[figure.id] = figure.data;
44208
44257
  }
44209
44258
  });
44210
44259
  }
@@ -47787,7 +47836,6 @@ const dateGranularities = [
47787
47836
  pivotRegistry.add("SPREADSHEET", {
47788
47837
  ui: SpreadsheetPivot,
47789
47838
  definition: SpreadsheetPivotRuntimeDefinition,
47790
- externalData: false,
47791
47839
  dateGranularities: [...dateGranularities],
47792
47840
  datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
47793
47841
  isMeasureCandidate: (field) => field.type !== "boolean",
@@ -53823,7 +53871,9 @@ class FontSizeEditor extends owl.Component {
53823
53871
  inputRef = owl.useRef("inputFontSize");
53824
53872
  rootEditorRef = owl.useRef("FontSizeEditor");
53825
53873
  fontSizeListRef = owl.useRef("fontSizeList");
53874
+ DOMFocusableElementStore;
53826
53875
  setup() {
53876
+ this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
53827
53877
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
53828
53878
  }
53829
53879
  get popoverProps() {
@@ -53877,6 +53927,13 @@ class FontSizeEditor extends owl.Component {
53877
53927
  }
53878
53928
  this.props.onToggle?.();
53879
53929
  }
53930
+ if (ev.key === "Tab") {
53931
+ ev.preventDefault();
53932
+ ev.stopPropagation();
53933
+ this.closeFontList();
53934
+ this.DOMFocusableElementStore.focus();
53935
+ return;
53936
+ }
53880
53937
  }
53881
53938
  }
53882
53939
 
@@ -55212,19 +55269,6 @@ class ChartWithAxisDesignPanel extends owl.Component {
55212
55269
  }
55213
55270
  }
55214
55271
 
55215
- class ChartShowDataMarkers extends owl.Component {
55216
- static template = "o-spreadsheet-ChartShowDataMarkers";
55217
- static components = {
55218
- Checkbox,
55219
- };
55220
- static props = {
55221
- chartId: String,
55222
- definition: Object,
55223
- updateChart: Function,
55224
- canUpdateChart: Function,
55225
- };
55226
- }
55227
-
55228
55272
  class GenericZoomableChartDesignPanel extends ChartWithAxisDesignPanel {
55229
55273
  static template = "o-spreadsheet-GenericZoomableChartDesignPanel";
55230
55274
  static components = {
@@ -55238,6 +55282,26 @@ class GenericZoomableChartDesignPanel extends ChartWithAxisDesignPanel {
55238
55282
  }
55239
55283
  }
55240
55284
 
55285
+ class BarChartDesignPanel extends GenericZoomableChartDesignPanel {
55286
+ static template = "o-spreadsheet-BarChartDesignPanel";
55287
+ get isZoomable() {
55288
+ return !this.props.definition.horizontal;
55289
+ }
55290
+ }
55291
+
55292
+ class ChartShowDataMarkers extends owl.Component {
55293
+ static template = "o-spreadsheet-ChartShowDataMarkers";
55294
+ static components = {
55295
+ Checkbox,
55296
+ };
55297
+ static props = {
55298
+ chartId: String,
55299
+ definition: Object,
55300
+ updateChart: Function,
55301
+ canUpdateChart: Function,
55302
+ };
55303
+ }
55304
+
55241
55305
  class ComboChartDesignPanel extends GenericZoomableChartDesignPanel {
55242
55306
  static template = "o-spreadsheet-ComboChartDesignPanel";
55243
55307
  static components = {
@@ -56166,7 +56230,7 @@ chartSidePanelComponentRegistry
56166
56230
  })
56167
56231
  .add("bar", {
56168
56232
  configuration: BarConfigPanel,
56169
- design: GenericZoomableChartDesignPanel,
56233
+ design: BarChartDesignPanel,
56170
56234
  })
56171
56235
  .add("combo", {
56172
56236
  configuration: GenericChartConfigPanel,
@@ -64258,7 +64322,8 @@ class FigurePlugin extends CorePlugin {
64258
64322
  }
64259
64323
  break;
64260
64324
  case "DUPLICATE_SHEET": {
64261
- for (const figureId in this.figures[cmd.sheetId]) {
64325
+ for (const figure of this.getFigures(cmd.sheetId)) {
64326
+ const figureId = figure.id;
64262
64327
  const fig = this.figures[cmd.sheetId]?.[figureId];
64263
64328
  if (!fig) {
64264
64329
  continue;
@@ -67736,10 +67801,17 @@ class PivotCorePlugin extends CorePlugin {
67736
67801
  if (!pivot) {
67737
67802
  continue;
67738
67803
  }
67739
- for (const measure of pivot.definition.measures) {
67804
+ const def = deepCopy(pivot.definition);
67805
+ for (const measure of def.measures) {
67740
67806
  if (measure.computedBy?.formula === formulaString) {
67741
- const measureIndex = pivot.definition.measures.indexOf(measure);
67742
- this.history.update("pivots", pivotId, "definition", "measures", measureIndex, "computedBy", { formula: newFormulaString, sheetId });
67807
+ const measureIndex = def.measures.indexOf(measure);
67808
+ if (measureIndex !== -1) {
67809
+ def.measures[measureIndex].computedBy = {
67810
+ formula: newFormulaString,
67811
+ sheetId,
67812
+ };
67813
+ }
67814
+ this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
67743
67815
  }
67744
67816
  }
67745
67817
  }
@@ -67900,6 +67972,9 @@ class SpreadsheetPivotCorePlugin extends CorePlugin {
67900
67972
  const { sheetId, zone } = definition.dataSet;
67901
67973
  const range = this.getters.getRangeFromZone(sheetId, zone);
67902
67974
  const adaptedRange = adaptPivotRange(range, applyChange);
67975
+ if (adaptedRange === range) {
67976
+ return;
67977
+ }
67903
67978
  const dataSet = adaptedRange && {
67904
67979
  sheetId: adaptedRange.sheetId,
67905
67980
  zone: adaptedRange.zone,
@@ -72219,9 +72294,7 @@ class PivotUIPlugin extends CoreViewPlugin {
72219
72294
  handle(cmd) {
72220
72295
  if (invalidateEvaluationCommands.has(cmd.type)) {
72221
72296
  for (const pivotId of this.getters.getPivotIds()) {
72222
- if (!pivotRegistry.get(this.getters.getPivotCoreDefinition(pivotId).type).externalData) {
72223
- this.setupPivot(pivotId, { recreate: true });
72224
- }
72297
+ this.setupPivot(pivotId, { recreate: true });
72225
72298
  }
72226
72299
  }
72227
72300
  switch (cmd.type) {
@@ -72443,7 +72516,7 @@ class PivotUIPlugin extends CoreViewPlugin {
72443
72516
  pivot.init({ reload: true });
72444
72517
  }
72445
72518
  setupPivot(pivotId, { recreate } = { recreate: false }) {
72446
- const definition = this.getters.getPivotCoreDefinition(pivotId);
72519
+ const definition = deepCopy(this.getters.getPivotCoreDefinition(pivotId));
72447
72520
  if (!(pivotId in this.pivots)) {
72448
72521
  const Pivot = withPivotPresentationLayer(pivotRegistry.get(definition.type).ui);
72449
72522
  this.pivots[pivotId] = new Pivot(this.custom, { definition, getters: this.getters });
@@ -81950,16 +82023,18 @@ class ClickableCellsStore extends SpreadsheetStore {
81950
82023
  get clickableCells() {
81951
82024
  const cells = [];
81952
82025
  const getters = this.getters;
81953
- const sheetId = getters.getActiveSheetId();
81954
82026
  for (const position of this.getters.getVisibleCellPositions()) {
81955
82027
  const item = this.getClickableItem(position);
81956
82028
  if (!item) {
81957
82029
  continue;
81958
82030
  }
81959
82031
  const title = typeof item.title === "function" ? item.title(position, getters) : item.title;
81960
- const zone = getters.expandZone(sheetId, positionToZone(position));
82032
+ const rect = this.getClickableCellRect(position);
82033
+ if (!rect) {
82034
+ continue;
82035
+ }
81961
82036
  cells.push({
81962
- coordinates: getters.getVisibleRect(zone),
82037
+ coordinates: rect,
81963
82038
  position,
81964
82039
  action: item.execute,
81965
82040
  title: title || "",
@@ -81969,6 +82044,31 @@ class ClickableCellsStore extends SpreadsheetStore {
81969
82044
  }
81970
82045
  return cells;
81971
82046
  }
82047
+ getClickableCellRect(position) {
82048
+ const zone = this.getters.expandZone(position.sheetId, positionToZone(position));
82049
+ const clickableRect = this.getters.getVisibleRect(zone);
82050
+ const icons = this.getters.getCellIcons(position);
82051
+ const iconsAtPosition = {
82052
+ center: icons.find((icon) => icon.horizontalAlign === "center"),
82053
+ left: icons.find((icon) => icon.horizontalAlign === "left"),
82054
+ right: icons.find((icon) => icon.horizontalAlign === "right"),
82055
+ };
82056
+ if (iconsAtPosition.center?.onClick) {
82057
+ return undefined;
82058
+ }
82059
+ if (iconsAtPosition.right?.onClick) {
82060
+ const cellRect = this.getters.getRect(zone);
82061
+ const iconRect = this.getters.getCellIconRect(iconsAtPosition.right, cellRect);
82062
+ clickableRect.width -= iconRect.width + iconsAtPosition.right.margin;
82063
+ }
82064
+ if (iconsAtPosition.left?.onClick) {
82065
+ const cellRect = this.getters.getRect(zone);
82066
+ const iconRect = this.getters.getCellIconRect(iconsAtPosition.left, cellRect);
82067
+ clickableRect.x += iconRect.width + iconsAtPosition.left.margin;
82068
+ clickableRect.width -= iconRect.width + iconsAtPosition.left.margin;
82069
+ }
82070
+ return clickableRect;
82071
+ }
81972
82072
  }
81973
82073
 
81974
82074
  css /* scss */ `
@@ -82726,7 +82826,7 @@ class SmallBottomBar extends owl.Component {
82726
82826
  height: this.focus === "inactive" ? "26px" : "fit-content",
82727
82827
  "max-height": `130px`,
82728
82828
  }),
82729
- showAssistant: !isIOS(), // Hide assistant on iOS as it breaks visually
82829
+ showAssistant: false, // Hide assistant in small composer as it gets cropped ATM
82730
82830
  placeholder: this.composerStore.placeholder,
82731
82831
  };
82732
82832
  }
@@ -89008,6 +89108,6 @@ exports.tokenColors = tokenColors;
89008
89108
  exports.tokenize = tokenize;
89009
89109
 
89010
89110
 
89011
- __info__.version = "19.0.11";
89012
- __info__.date = "2025-11-24T07:46:47.685Z";
89013
- __info__.hash = "f5bdbcc";
89111
+ __info__.version = "19.0.12";
89112
+ __info__.date = "2025-12-02T05:34:17.495Z";
89113
+ __info__.hash = "32203f1";