@odoo/o-spreadsheet 16.5.0-alpha.3 → 16.5.0-alpha.4

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.
@@ -3475,14 +3475,15 @@
3475
3475
  createChart(chartData) {
3476
3476
  const canvas = this.canvas.el;
3477
3477
  const ctx = canvas.getContext("2d");
3478
+ // @ts-ignore
3478
3479
  this.chart = new window.Chart(ctx, chartData);
3479
3480
  }
3480
3481
  updateChartJs(chartRuntime) {
3481
3482
  const chartData = chartRuntime.chartJsConfig;
3482
3483
  if (chartData.data && chartData.data.datasets) {
3483
3484
  this.chart.data = chartData.data;
3484
- if (chartData.options?.title) {
3485
- this.chart.config.options.title = chartData.options.title;
3485
+ if (chartData.options?.plugins?.title) {
3486
+ this.chart.config.options.plugins.title = chartData.options.plugins.title;
3486
3487
  }
3487
3488
  if (chartData.options && "valueLabel" in chartData.options) {
3488
3489
  if (chartData.options?.valueLabel) {
@@ -3492,12 +3493,13 @@
3492
3493
  }
3493
3494
  }
3494
3495
  else {
3495
- this.chart.data.datasets = undefined;
3496
+ this.chart.data.datasets = [];
3496
3497
  }
3497
- this.chart.config.options.tooltips = chartData.options?.tooltips;
3498
- this.chart.config.options.legend = chartData.options?.legend;
3498
+ this.chart.config.options.plugins.tooltip = chartData.options.plugins.tooltip;
3499
+ this.chart.config.options.plugins.legend = chartData.options.plugins.legend;
3499
3500
  this.chart.config.options.scales = chartData.options?.scales;
3500
- this.chart.update({ duration: 0 });
3501
+ // ?
3502
+ this.chart.update("active");
3501
3503
  }
3502
3504
  }
3503
3505
  ChartJsComponent.props = {
@@ -16179,49 +16181,40 @@
16179
16181
  * Get a default chart js configuration
16180
16182
  */
16181
16183
  function getDefaultChartJsRuntime(chart, labels, fontColor, { format, locale }) {
16182
- return {
16183
- type: chart.type,
16184
- options: {
16185
- // https://www.chartjs.org/docs/latest/general/responsive.html
16186
- responsive: true,
16187
- maintainAspectRatio: false,
16188
- layout: {
16189
- padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
16190
- },
16191
- elements: {
16192
- line: {
16193
- fill: false, // do not fill the area under line charts
16194
- },
16195
- point: {
16196
- hitRadius: 15, // increased hit radius to display point tooltip when hovering nearby
16197
- },
16198
- },
16199
- animation: {
16200
- duration: 0, // general animation time
16184
+ const options = {
16185
+ // https://www.chartjs.org/docs/latest/general/responsive.html
16186
+ responsive: true,
16187
+ maintainAspectRatio: false,
16188
+ layout: {
16189
+ padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
16190
+ },
16191
+ elements: {
16192
+ line: {
16193
+ fill: false, // do not fill the area under line charts
16201
16194
  },
16202
- hover: {
16203
- animationDuration: 10, // duration of animations when hovering an item
16195
+ point: {
16196
+ hitRadius: 15, // increased hit radius to display point tooltip when hovering nearby
16204
16197
  },
16205
- responsiveAnimationDuration: 0,
16198
+ },
16199
+ animation: false,
16200
+ plugins: {
16206
16201
  title: {
16207
16202
  display: !!chart.title,
16208
- fontSize: 22,
16209
- fontStyle: "normal",
16210
16203
  text: _t(chart.title),
16211
- fontColor,
16204
+ color: fontColor,
16205
+ font: { size: 22, weight: "normal" },
16212
16206
  },
16213
16207
  legend: {
16214
16208
  // Disable default legend onClick (show/hide dataset), to allow us to set a global onClick on the chart container.
16215
16209
  // If we want to re-enable this in the future, we need to override the default onClick to stop the event propagation
16216
- onClick: undefined,
16210
+ onClick: () => { },
16217
16211
  },
16218
- tooltips: {
16212
+ tooltip: {
16219
16213
  callbacks: {
16220
- label: function (tooltipItem, data) {
16221
- let xLabel = data.datasets?.[tooltipItem.datasetIndex || 0]?.label;
16222
- const yLabel = tooltipItem.yLabel !== ""
16223
- ? tooltipItem.yLabel
16224
- : data.datasets?.[tooltipItem.datasetIndex || 0]?.data?.[tooltipItem.index || 0];
16214
+ label: function (tooltipItem) {
16215
+ let xLabel = tooltipItem.label;
16216
+ // tooltipItem.parsed.y can be an object or a number for pie charts
16217
+ const yLabel = tooltipItem.parsed.y ?? tooltipItem.parsed;
16225
16218
  const yLabelStr = format && typeof yLabel === "number"
16226
16219
  ? formatValue(yLabel, { format, locale })
16227
16220
  : yLabel?.toLocaleString() || "";
@@ -16230,10 +16223,15 @@
16230
16223
  },
16231
16224
  },
16232
16225
  },
16226
+ };
16227
+ return {
16228
+ type: chart.type,
16229
+ options,
16233
16230
  data: {
16234
16231
  labels: labels.map(truncateLabel),
16235
16232
  datasets: [],
16236
16233
  },
16234
+ plugins: [],
16237
16235
  };
16238
16236
  }
16239
16237
  function getChartLabelFormat(getters, range) {
@@ -16430,7 +16428,7 @@
16430
16428
  const fontColor = chartFontColor(chart.background);
16431
16429
  const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
16432
16430
  const legend = {
16433
- labels: { fontColor },
16431
+ labels: { color: fontColor },
16434
16432
  };
16435
16433
  if ((!chart.labelRange && chart.dataSets.length === 1) || chart.legendPosition === "none") {
16436
16434
  legend.display = false;
@@ -16438,42 +16436,35 @@
16438
16436
  else {
16439
16437
  legend.position = chart.legendPosition;
16440
16438
  }
16441
- config.options.legend = { ...config.options?.legend, ...legend };
16439
+ config.options.plugins.legend = { ...config.options.plugins?.legend, ...legend };
16442
16440
  config.options.layout = {
16443
16441
  padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
16444
16442
  };
16445
16443
  config.options.scales = {
16446
- xAxes: [
16447
- {
16448
- ticks: {
16449
- // x axis configuration
16450
- maxRotation: 60,
16451
- minRotation: 15,
16452
- padding: 5,
16453
- labelOffset: 2,
16454
- fontColor,
16455
- },
16444
+ x: {
16445
+ ticks: {
16446
+ padding: 5,
16447
+ color: fontColor,
16456
16448
  },
16457
- ],
16458
- yAxes: [
16459
- {
16460
- position: chart.verticalAxisPosition,
16461
- ticks: {
16462
- fontColor,
16463
- // y axis configuration
16464
- beginAtZero: true,
16465
- callback: (value) => {
16466
- return localeFormat.format
16467
- ? formatValue(value, localeFormat)
16468
- : value?.toLocaleString() || value;
16469
- },
16449
+ },
16450
+ y: {
16451
+ position: chart.verticalAxisPosition,
16452
+ beginAtZero: true,
16453
+ ticks: {
16454
+ color: fontColor,
16455
+ callback: (value) => {
16456
+ return localeFormat.format
16457
+ ? formatValue(value, localeFormat)
16458
+ : value?.toLocaleString() || value;
16470
16459
  },
16471
16460
  },
16472
- ],
16461
+ },
16473
16462
  };
16474
16463
  if (chart.stacked) {
16475
- config.options.scales.xAxes[0].stacked = true;
16476
- config.options.scales.yAxes[0].stacked = true;
16464
+ // @ts-ignore chart.js type is broken
16465
+ config.options.scales.x.stacked = true;
16466
+ // @ts-ignore chart.js type is broken
16467
+ config.options.scales.y.stacked = true;
16477
16468
  }
16478
16469
  return config;
16479
16470
  }
@@ -16677,17 +16668,18 @@
16677
16668
  padding: { left: 30, right: 30, top: chart.title ? 10 : 25, bottom: 25 },
16678
16669
  };
16679
16670
  config.options.needle = {
16680
- radiusPercentage: 2,
16681
- widthPercentage: 3.2,
16682
- lengthPercentage: 80,
16683
- color: "#000000",
16671
+ width: 10,
16672
+ borderColor: "#000000",
16673
+ backgroundColor: "#000000",
16684
16674
  };
16685
16675
  config.options.valueLabel = {
16686
16676
  display: false,
16687
- formatter: null,
16688
- color: "#FFFFFF",
16677
+ font: {
16678
+ size: 30,
16679
+ color: "#FFFFFF",
16680
+ },
16689
16681
  backgroundColor: "#000000",
16690
- fontSize: 30,
16682
+ borderColor: "#000000",
16691
16683
  borderRadius: 5,
16692
16684
  padding: {
16693
16685
  top: 5,
@@ -16695,7 +16687,6 @@
16695
16687
  bottom: 5,
16696
16688
  left: 5,
16697
16689
  },
16698
- bottomMarginPercentage: 5,
16699
16690
  };
16700
16691
  return config;
16701
16692
  }
@@ -16737,16 +16728,12 @@
16737
16728
  data.push(point.value);
16738
16729
  backgroundColor.push(point.color);
16739
16730
  });
16740
- // There's a bug in gauge lib when the last element in `data` is 0 (i.e. when the range maximum is 0).
16741
- // The value wrongly fallbacks to 1 because 0 is falsy
16742
- // See https://github.com/haiiaaa/chartjs-gauge/pull/33
16743
- // https://github.com/haiiaaa/chartjs-gauge/blob/2ea50541d754d710cb30c2502fa690ac5dc27afd/src/controllers/controller.gauge.js#L52
16744
16731
  data.push(maxNeedleValue);
16745
16732
  backgroundColor.push(colors.upperColor);
16746
16733
  const dataRange = chart.dataRange;
16747
16734
  const deltaBeyondRangeLimit = needleCoverage / 30;
16748
16735
  let needleValue = minNeedleValue - deltaBeyondRangeLimit; // make needle value always at the minimum by default
16749
- let cellFormatter = null;
16736
+ let cellFormatter = undefined;
16750
16737
  let displayValue = false;
16751
16738
  if (dataRange !== undefined) {
16752
16739
  const cell = getters.getEvaluatedCell({
@@ -16761,6 +16748,7 @@
16761
16748
  // smaller and slightly larger than minRange and maxRange to mark the fact
16762
16749
  // that the needle is out of the range limits
16763
16750
  needleValue = clip(cell.value, minNeedleValue - deltaBeyondRangeLimit, maxNeedleValue + deltaBeyondRangeLimit);
16751
+ // show the original value, not the clipped one
16764
16752
  cellFormatter = () => getters.getRangeFormattedValues(dataRange)[0];
16765
16753
  displayValue = true;
16766
16754
  }
@@ -16808,31 +16796,29 @@
16808
16796
  },
16809
16797
  };
16810
16798
  /**
16811
- * Regex to test if a format string is a date format that can be translated into a moment time format
16799
+ * Regex to test if a format string is a date format that can be translated into a luxon time format
16812
16800
  */
16813
- const timeFormatMomentCompatible = /^((d|dd|m|mm|yyyy|yy|hh|h|ss|a)(-|:|\s|\/))*(d|dd|m|mm|yyyy|yy|hh|h|ss|a)$/i;
16801
+ const timeFormatLuxonCompatible = /^((d|dd|m|mm|yyyy|yy|hh|h|ss|a)(-|:|\s|\/))*(d|dd|m|mm|yyyy|yy|hh|h|ss|a)$/i;
16814
16802
  /** Get the time options for the XAxis of ChartJS */
16815
16803
  function getChartTimeOptions(labels, labelFormat, locale) {
16816
- const momentFormat = convertDateFormatForMoment(labelFormat);
16817
- const timeUnit = getBestTimeUnitForScale(labels, momentFormat, locale);
16804
+ const luxonFormat = convertDateFormatForLuxon(labelFormat);
16805
+ const timeUnit = getBestTimeUnitForScale(labels, luxonFormat, locale);
16818
16806
  const displayFormats = {};
16819
16807
  if (timeUnit) {
16820
- displayFormats[timeUnit] = momentFormat;
16808
+ displayFormats[timeUnit] = luxonFormat;
16821
16809
  }
16822
16810
  return {
16823
- parser: momentFormat,
16811
+ parser: luxonFormat,
16824
16812
  displayFormats,
16825
- unit: timeUnit,
16813
+ unit: timeUnit ?? false,
16826
16814
  };
16827
16815
  }
16828
16816
  /**
16829
16817
  * Convert the given date format into a format that moment.js understands.
16830
16818
  *
16831
- * https://momentjs.com/docs/#/parsing/string-format/
16819
+ * https://github.com/moment/luxon/blob/master/docs/formatting.md#table-of-tokens
16832
16820
  */
16833
- function convertDateFormatForMoment(format) {
16834
- format = format.replace(/y/g, "Y");
16835
- format = format.replace(/d/g, "D");
16821
+ function convertDateFormatForLuxon(format) {
16836
16822
  // "m" before "h" === month, "m" after "h" === minute
16837
16823
  const indexH = format.indexOf("h");
16838
16824
  if (indexH >= 0) {
@@ -16903,6 +16889,8 @@
16903
16889
  return "year";
16904
16890
  }
16905
16891
 
16892
+ // @ts-ignore
16893
+ const Chart = window.Chart;
16906
16894
  class LineChart extends AbstractChart {
16907
16895
  dataSets;
16908
16896
  labelRange;
@@ -17032,7 +17020,7 @@
17032
17020
  return canBeDateChart(labelRange, getters) || canBeLinearChart(labelRange, getters);
17033
17021
  }
17034
17022
  function getChartAxisType(chart, getters) {
17035
- if (isDateChart(chart, getters)) {
17023
+ if (isDateChart(chart, getters) && isLuxonTimeAdapterInstalled()) {
17036
17024
  return "time";
17037
17025
  }
17038
17026
  if (isLinearChart(chart, getters)) {
@@ -17055,7 +17043,7 @@
17055
17043
  col: labelRange.zone.left,
17056
17044
  row: labelRange.zone.top,
17057
17045
  }).format;
17058
- return Boolean(labelFormat && timeFormatMomentCompatible.test(labelFormat));
17046
+ return Boolean(labelFormat && timeFormatLuxonCompatible.test(labelFormat));
17059
17047
  }
17060
17048
  function canBeLinearChart(labelRange, getters) {
17061
17049
  if (!labelRange) {
@@ -17075,10 +17063,11 @@
17075
17063
  const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
17076
17064
  const legend = {
17077
17065
  labels: {
17078
- fontColor,
17066
+ color: fontColor,
17079
17067
  generateLabels(chart) {
17068
+ // color the legend labels with the dataset color, without any transparency
17080
17069
  const { data } = chart;
17081
- const labels = window.Chart.defaults.global.legend.labels.generateLabels(chart);
17070
+ const labels = Chart.defaults.plugins.legend.labels.generateLabels(chart);
17082
17071
  for (const [index, label] of labels.entries()) {
17083
17072
  label.fillStyle = data.datasets[index].borderColor;
17084
17073
  }
@@ -17092,41 +17081,33 @@
17092
17081
  else {
17093
17082
  legend.position = chart.legendPosition;
17094
17083
  }
17095
- config.options.legend = { ...config.options?.legend, ...legend };
17084
+ Object.assign(config.options.plugins.legend || {}, legend);
17096
17085
  config.options.layout = {
17097
17086
  padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
17098
17087
  };
17099
17088
  config.options.scales = {
17100
- xAxes: [
17101
- {
17102
- ticks: {
17103
- // x axis configuration
17104
- maxRotation: 60,
17105
- minRotation: 15,
17106
- padding: 5,
17107
- labelOffset: 2,
17108
- fontColor,
17109
- },
17089
+ x: {
17090
+ ticks: {
17091
+ padding: 5,
17092
+ color: fontColor,
17110
17093
  },
17111
- ],
17112
- yAxes: [
17113
- {
17114
- position: chart.verticalAxisPosition,
17115
- ticks: {
17116
- fontColor,
17117
- // y axis configuration
17118
- beginAtZero: true,
17119
- callback: (value) => {
17120
- return localeFormat.format
17121
- ? formatValue(value, localeFormat)
17122
- : value?.toLocaleString() || value;
17123
- },
17094
+ },
17095
+ y: {
17096
+ position: chart.verticalAxisPosition,
17097
+ beginAtZero: true,
17098
+ ticks: {
17099
+ color: fontColor,
17100
+ callback: (value) => {
17101
+ return localeFormat.format
17102
+ ? formatValue(value, localeFormat)
17103
+ : value?.toLocaleString() || value;
17124
17104
  },
17125
17105
  },
17126
- ],
17106
+ },
17127
17107
  };
17128
- if (chart.stacked) {
17129
- config.options.scales.yAxes[0].stacked = true;
17108
+ if (chart.stacked && config.options?.scales?.y) {
17109
+ // @ts-ignore chart.js type is wrong
17110
+ config.options.scales.y.stacked = true;
17130
17111
  }
17131
17112
  return config;
17132
17113
  }
@@ -17153,15 +17134,18 @@
17153
17134
  const config = getLineConfiguration(chart, labels, localeFormat);
17154
17135
  const labelFormat = getChartLabelFormat(getters, chart.labelRange);
17155
17136
  if (axisType === "time") {
17156
- config.options.scales.xAxes[0].type = "time";
17157
- config.options.scales.xAxes[0].time = getChartTimeOptions(labels, labelFormat, locale);
17158
- config.options.scales.xAxes[0].ticks.maxTicksLimit = 15;
17137
+ const axis = {
17138
+ type: "time",
17139
+ time: getChartTimeOptions(labels, labelFormat, locale),
17140
+ };
17141
+ Object.assign(config.options.scales.x, axis);
17142
+ config.options.scales.x.ticks.maxTicksLimit = 15;
17159
17143
  }
17160
17144
  else if (axisType === "linear") {
17161
- config.options.scales.xAxes[0].type = "linear";
17162
- config.options.scales.xAxes[0].ticks.callback = (value) => formatValue(value, localeFormat);
17163
- config.options.tooltips.callbacks.title = (tooltipItem) => {
17164
- return formatValue(tooltipItem[0]?.xLabel || "", localeFormat);
17145
+ config.options.scales.x.type = "linear";
17146
+ config.options.scales.x.ticks.callback = (value) => formatValue(value, { format: labelFormat, locale });
17147
+ config.options.plugins.tooltip.callbacks.title = (tooltipItem) => {
17148
+ return formatValue(tooltipItem[0]?.label || "", localeFormat);
17165
17149
  };
17166
17150
  }
17167
17151
  const colors = new ChartColors();
@@ -17179,7 +17163,7 @@
17179
17163
  const dataset = {
17180
17164
  label,
17181
17165
  data,
17182
- lineTension: 0,
17166
+ tension: 0,
17183
17167
  borderColor: color,
17184
17168
  backgroundColor,
17185
17169
  pointBackgroundColor: color,
@@ -17188,6 +17172,22 @@
17188
17172
  config.data.datasets.push(dataset);
17189
17173
  }
17190
17174
  return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
17175
+ }
17176
+ let missingTimeAdapterAlreadyWarned = false;
17177
+ function isLuxonTimeAdapterInstalled() {
17178
+ // @ts-ignore
17179
+ if (!window.Chart) {
17180
+ return false;
17181
+ }
17182
+ // @ts-ignore
17183
+ const adapter = new window.Chart._adapters._date({});
17184
+ // @ts-ignore
17185
+ const isInstalled = adapter._id === "luxon";
17186
+ if (!isInstalled && !missingTimeAdapterAlreadyWarned) {
17187
+ missingTimeAdapterAlreadyWarned = true;
17188
+ console.warn("'chartjs-adapter-luxon' time adapter is not installed. Time scale axes are disabled.");
17189
+ }
17190
+ return isInstalled;
17191
17191
  }
17192
17192
 
17193
17193
  class PieChart extends AbstractChart {
@@ -17292,7 +17292,7 @@
17292
17292
  const fontColor = chartFontColor(chart.background);
17293
17293
  const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
17294
17294
  const legend = {
17295
- labels: { fontColor },
17295
+ labels: { color: fontColor },
17296
17296
  };
17297
17297
  if ((!chart.labelRange && chart.dataSets.length === 1) || chart.legendPosition === "none") {
17298
17298
  legend.display = false;
@@ -17300,12 +17300,12 @@
17300
17300
  else {
17301
17301
  legend.position = chart.legendPosition;
17302
17302
  }
17303
- config.options.legend = { ...config.options?.legend, ...legend };
17303
+ Object.assign(config.options.plugins.legend || {}, legend);
17304
17304
  config.options.layout = {
17305
17305
  padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
17306
17306
  };
17307
- config.options.tooltips.callbacks.title = function (tooltipItems, data) {
17308
- return data.datasets[tooltipItems[0].datasetIndex].label;
17307
+ config.options.plugins.tooltip.callbacks.title = function (tooltipItems) {
17308
+ return tooltipItems[0].dataset.label;
17309
17309
  };
17310
17310
  return config;
17311
17311
  }
@@ -18069,6 +18069,278 @@
18069
18069
  };
18070
18070
  }
18071
18071
 
18072
+ // @ts-ignore
18073
+ if (window.Chart) {
18074
+ // @ts-ignore
18075
+ const DoughnutController = window.Chart?.DoughnutController;
18076
+ /**
18077
+ * Example :
18078
+ * const chart = new Chart(ctx, {
18079
+ * type: "gauge",
18080
+ * data: {
18081
+ * datasets: [
18082
+ * {
18083
+ * borderWidth: 5,
18084
+ * data: [10, 20, 10],
18085
+ * value: 23,
18086
+ * backgroundColor: [
18087
+ * "#dc3545", //red
18088
+ * "#ffc107", //orange
18089
+ * "#28a745", //green
18090
+ * ],
18091
+ * },
18092
+ * ],
18093
+ * },
18094
+ * options: {
18095
+ * plugins: {
18096
+ * title: {
18097
+ * display: true,
18098
+ * text: "Custom Chart Title",
18099
+ * padding: {
18100
+ * top: 10,
18101
+ * bottom: 30,
18102
+ * },
18103
+ * font : {
18104
+ * size : 30
18105
+ * }
18106
+ * },
18107
+ * },
18108
+ * valueLabel: {
18109
+ * display: true,
18110
+ * format: (value) => {
18111
+ * return value.toFixed(2) + "%";
18112
+ * },
18113
+ * font : {
18114
+ * size : 35,
18115
+ * family : "Arial",
18116
+ * color : "#FC0",
18117
+ * },
18118
+ * backgroundColor : "#ccc",
18119
+ * borderColor : "#090",
18120
+ * borderRadius : 50
18121
+ * },
18122
+ * needle: {
18123
+ * display: true,
18124
+ * width: 50,
18125
+ * color: "#DC5",
18126
+ * backgroundColor : "#ccc",
18127
+ * },
18128
+ * },
18129
+ * });
18130
+ */
18131
+ class GaugeController extends DoughnutController {
18132
+ static id = "gauge";
18133
+ static defaults = {
18134
+ ...DoughnutController.defaults,
18135
+ circumference: 180,
18136
+ rotation: 270,
18137
+ };
18138
+ static overrides = {
18139
+ aspectRatio: 2,
18140
+ plugins: {
18141
+ legend: {
18142
+ display: false,
18143
+ },
18144
+ tooltip: {
18145
+ enabled: false,
18146
+ },
18147
+ },
18148
+ };
18149
+ get chartHeight() {
18150
+ return Math.abs(this.chart.chartArea.top - this.chart.chartArea.bottom);
18151
+ }
18152
+ get chartWidth() {
18153
+ return Math.abs(this.chart.chartArea.left - this.chart.chartArea.right);
18154
+ }
18155
+ get needleOptions() {
18156
+ const { config } = this.chart;
18157
+ const options = config.options;
18158
+ // @ts-ignore
18159
+ return options?.needle || {};
18160
+ }
18161
+ get valueLabelOptions() {
18162
+ const { config } = this.chart;
18163
+ const options = config.options;
18164
+ // @ts-ignore
18165
+ return options?.valueLabel || {};
18166
+ }
18167
+ get minValue() {
18168
+ const dataset = this.getDataset();
18169
+ // @ts-ignore
18170
+ return dataset.minValue || 0;
18171
+ }
18172
+ getValueAngleInPercent(value) {
18173
+ const dataset = this.getDataset();
18174
+ const data = dataset.data || [];
18175
+ const max = Math.max(...data);
18176
+ const min = this.minValue;
18177
+ return (value - min) / (max - min);
18178
+ }
18179
+ drawValueLabel(params) {
18180
+ const { ctx } = this.chart;
18181
+ if (this.valueLabelOptions.display === false) {
18182
+ return;
18183
+ }
18184
+ // draw background rectangle
18185
+ ctx.save();
18186
+ ctx.beginPath();
18187
+ ctx.fillStyle = params.valueLabel.backgroundColor;
18188
+ ctx.strokeStyle = params.valueLabel.borderColor;
18189
+ ctx.roundRect(params.valueLabel.rect.x, params.valueLabel.rect.y, params.valueLabel.rect.width, params.valueLabel.rect.height, params.valueLabel.borderRadius);
18190
+ ctx.stroke();
18191
+ ctx.fill();
18192
+ // draw value text
18193
+ ctx.textAlign = "center";
18194
+ ctx.textBaseline = "middle";
18195
+ ctx.font = params.valueLabel.font;
18196
+ ctx.fillStyle = params.valueLabel.textColor;
18197
+ ctx.fillText(params.valueLabel.valueText, params.valueLabel.textPosition.x, params.valueLabel.textPosition.y);
18198
+ ctx.restore();
18199
+ }
18200
+ drawNeedle(params) {
18201
+ const { ctx } = this.chart;
18202
+ if (this.needleOptions.display === false) {
18203
+ return;
18204
+ }
18205
+ // translate & rotate next paths
18206
+ ctx.save();
18207
+ ctx.translate(params.needle.position.x, params.needle.position.y);
18208
+ ctx.rotate(-Math.PI / 2 + Math.PI * params.needle.percent);
18209
+ // draw circle
18210
+ ctx.beginPath();
18211
+ ctx.fillStyle = params.needle.backgroundColor;
18212
+ ctx.strokeStyle = params.needle.borderColor;
18213
+ ctx.ellipse(0, 0, params.needle.width / 2, params.needle.width / 2, 0, 0, 2 * Math.PI);
18214
+ ctx.fill();
18215
+ ctx.stroke();
18216
+ ctx.closePath();
18217
+ // draw needle
18218
+ ctx.fillStyle = params.needle.backgroundColor;
18219
+ ctx.strokeStyle = params.needle.borderColor;
18220
+ ctx.beginPath();
18221
+ ctx.moveTo(-params.needle.width / 2, 0);
18222
+ ctx.lineTo(0, -params.needle.height + 10);
18223
+ ctx.lineTo(+params.needle.width / 2, 0);
18224
+ ctx.closePath();
18225
+ ctx.fill();
18226
+ ctx.stroke();
18227
+ ctx.restore();
18228
+ }
18229
+ computeRenderingParams() {
18230
+ const { ctx, config } = this.chart;
18231
+ const options = config.options;
18232
+ options.layout = options.layout || {};
18233
+ options.layout.padding = options.layout.padding || {};
18234
+ const formatter = this.valueLabelOptions.formatter;
18235
+ const fmt = typeof formatter === "function" ? formatter : (value) => value;
18236
+ const dataset = this.getDataset();
18237
+ // @ts-ignore value is a custom property for gauge charts
18238
+ const value = dataset.value || 0;
18239
+ const valueText = fmt(value).toString();
18240
+ const percent = this.getValueAngleInPercent(value);
18241
+ const fontFamily = this.valueLabelOptions.font?.family || "Arial";
18242
+ const optionsFontSize = this.valueLabelOptions.font?.size || 0;
18243
+ const fontSize = optionsFontSize >= 1 ? optionsFontSize : 30;
18244
+ const font = `${fontSize}px ${fontFamily}`;
18245
+ const padding = {
18246
+ right: 0,
18247
+ left: 0,
18248
+ top: 0,
18249
+ bottom: 0,
18250
+ ...this.valueLabelOptions.padding,
18251
+ };
18252
+ padding.left = padding.left >= 0 ? padding.left : 10;
18253
+ padding.right = padding.right >= 0 ? padding.right : 10;
18254
+ padding.top = padding.top >= 0 ? padding.top : 10;
18255
+ padding.bottom = padding.bottom >= 0 ? padding.bottom : 10;
18256
+ const chartArea = this.chart.chartArea;
18257
+ const offsetX = this.offsetX || 0;
18258
+ const offsetY = this.offsetY || 0;
18259
+ const centerX = (chartArea.left + chartArea.right) / 2;
18260
+ const centerY = (chartArea.top + chartArea.bottom) / 2;
18261
+ const center = {
18262
+ x: centerX + offsetX,
18263
+ y: centerY + offsetY,
18264
+ };
18265
+ const textPosition = {
18266
+ x: center.x,
18267
+ y: center.y,
18268
+ };
18269
+ ctx.save();
18270
+ ctx.font = font;
18271
+ const metrics = ctx.measureText(valueText);
18272
+ ctx.restore();
18273
+ const textHeight = metrics.fontBoundingBoxAscent - metrics.fontBoundingBoxDescent;
18274
+ return {
18275
+ needle: {
18276
+ borderColor: this.needleOptions.borderColor || "#000",
18277
+ backgroundColor: this.needleOptions.backgroundColor || "#000",
18278
+ width: this.needleOptions.width || 10,
18279
+ height: Math.min(this.chartHeight, this.chartWidth / 2),
18280
+ position: {
18281
+ x: textPosition.x,
18282
+ y: textPosition.y,
18283
+ },
18284
+ percent,
18285
+ },
18286
+ valueLabel: {
18287
+ textColor: this.valueLabelOptions.font?.color || "#FFF",
18288
+ backgroundColor: this.valueLabelOptions.backgroundColor || "#000",
18289
+ borderColor: this.valueLabelOptions.borderColor || "#000",
18290
+ borderRadius: this.valueLabelOptions.borderRadius || 10,
18291
+ rect: {
18292
+ x: center.x - metrics.width / 2 - padding.left,
18293
+ y: center.y + textHeight / 2 + padding.top,
18294
+ width: metrics.width + padding.left + padding.right,
18295
+ height: -(textHeight + 6 + padding.top + padding.bottom),
18296
+ },
18297
+ valueText,
18298
+ textPosition,
18299
+ textHeight,
18300
+ font,
18301
+ },
18302
+ };
18303
+ }
18304
+ draw() {
18305
+ super.draw();
18306
+ const params = this.computeRenderingParams();
18307
+ this.drawNeedle(params);
18308
+ this.drawValueLabel(params);
18309
+ }
18310
+ updateElements(elements, start, count, mode) {
18311
+ super.updateElements(elements, start, count, mode);
18312
+ const dataset = this.getDataset();
18313
+ const data = this.getDataset().data;
18314
+ // @ts-ignore
18315
+ const minValue = dataset.minValue;
18316
+ // @ts-ignore
18317
+ const rotation = this.chart.options.rotation || 0;
18318
+ // @ts-ignore
18319
+ const circumference = this.chart.options.circumference || 0;
18320
+ for (let arcIndex = 0; arcIndex < data.length; arcIndex++) {
18321
+ const previousValue = arcIndex === 0 ? minValue : data[arcIndex - 1];
18322
+ const startAngleInPercent = this.getValueAngleInPercent(previousValue);
18323
+ const endAngleInPercent = this.getValueAngleInPercent(data[arcIndex]);
18324
+ const startAngle = degreesToRadians(rotation + circumference * startAngleInPercent) - Math.PI / 2;
18325
+ const endAngle = degreesToRadians(rotation + circumference * endAngleInPercent) - Math.PI / 2;
18326
+ const arcCircumference = endAngle - startAngle;
18327
+ const arc = elements[arcIndex];
18328
+ const propertiesUpdates = {
18329
+ startAngle,
18330
+ endAngle,
18331
+ circumference: arcCircumference,
18332
+ };
18333
+ this.updateElement(arc, arcIndex, propertiesUpdates, mode);
18334
+ }
18335
+ }
18336
+ }
18337
+ function degreesToRadians(degrees) {
18338
+ return (degrees * Math.PI) / 180;
18339
+ }
18340
+ // @ts-ignore
18341
+ window.Chart.register(GaugeController);
18342
+ }
18343
+
18072
18344
  //------------------------------------------------------------------------------
18073
18345
  // Helpers
18074
18346
  //------------------------------------------------------------------------------
@@ -36418,7 +36690,8 @@
36418
36690
  if (!chart) {
36419
36691
  throw new Error(`No chart for the given id: ${figureId}`);
36420
36692
  }
36421
- this.history.update("charts", figureId, this.createRuntimeChart(chart));
36693
+ const runtime = this.createRuntimeChart(chart);
36694
+ this.history.update("charts", figureId, runtime);
36422
36695
  }
36423
36696
  return this.charts[figureId];
36424
36697
  }
@@ -49506,7 +49779,14 @@
49506
49779
  `;
49507
49780
  }
49508
49781
 
49509
- const TABLE_DEFAULT_STYLE = escapeXml /*xml*/ `<tableStyleInfo name="TableStyleLight8" showFirstColumn="0" showLastColumn="0" showRowStripes="0" showColumnStripes="0"/>`;
49782
+ const TABLE_DEFAULT_ATTRS = [
49783
+ ["name", "TableStyleLight8"],
49784
+ ["showFirstColumn", "0"],
49785
+ ["showLastColumn", "0"],
49786
+ ["showRowStripes", "0"],
49787
+ ["showColumnStripes", "0"],
49788
+ ];
49789
+ const TABLE_DEFAULT_STYLE = escapeXml /*xml*/ `<tableStyleInfo ${formatAttributes(TABLE_DEFAULT_ATTRS)}/>`;
49510
49790
  function createTable(table, tableId, sheetData) {
49511
49791
  const tableAttributes = [
49512
49792
  ["id", tableId],
@@ -50625,9 +50905,9 @@
50625
50905
  Object.defineProperty(exports, '__esModule', { value: true });
50626
50906
 
50627
50907
 
50628
- __info__.version = '16.5.0-alpha.3';
50629
- __info__.date = '2023-08-09T09:07:55.435Z';
50630
- __info__.hash = 'b0e8fd5';
50908
+ __info__.version = '16.5.0-alpha.4';
50909
+ __info__.date = '2023-08-10T09:39:12.478Z';
50910
+ __info__.hash = 'c8db018';
50631
50911
 
50632
50912
 
50633
50913
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);