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