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