@eclipse-scout/chart 22.0.29 → 22.0.34

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.
@@ -1,9 +1,9 @@
1
1
  /*
2
- * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
2
+ * Copyright (c) 2010-2022 BSI Business Systems Integration AG.
3
3
  * All rights reserved. This program and the accompanying materials
4
4
  * are made available under the terms of the Eclipse Public License v1.0
5
5
  * which accompanies this distribution, and is available at
6
- * http://www.eclipse.org/legal/epl-v10.html
6
+ * https://www.eclipse.org/legal/epl-v10.html
7
7
  *
8
8
  * Contributors:
9
9
  * BSI Business Systems Integration AG - initial API and implementation
@@ -99,6 +99,13 @@ $.extend(true, ChartJs.overrides, {
99
99
  borderWidth: 2
100
100
  }
101
101
  }
102
+ },
103
+ scatter: {
104
+ elements: {
105
+ point: {
106
+ radius: 3
107
+ }
108
+ }
102
109
  }
103
110
  });
104
111
 
@@ -178,6 +185,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
178
185
  this._resizeHandler = this._onResize.bind(this);
179
186
 
180
187
  this._tooltipTitleGenerator = this._generateTooltipTitle.bind(this);
188
+ this._tooltipItemsGenerator = this._generateTooltipItems.bind(this);
181
189
  this._tooltipLabelGenerator = this._generateTooltipLabel.bind(this);
182
190
  this._tooltipLabelValueGenerator = this._generateTooltipLabelValue.bind(this);
183
191
  this._tooltipLabelColorGenerator = this._generateTooltipLabelColor.bind(this);
@@ -772,7 +780,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
772
780
  return;
773
781
  }
774
782
 
775
- if (config.type === Chart.Type.BUBBLE) {
783
+ if (scout.isOneOf(config.type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
776
784
  this.onlyIntegers = config.data.datasets.every(dataset => dataset.data.every(data => numbers.isInteger(data.x) && numbers.isInteger(data.y)));
777
785
  } else {
778
786
  this.onlyIntegers = config.data.datasets.every(dataset => dataset.data.every(data => numbers.isInteger(data)));
@@ -789,6 +797,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
789
797
  tooltip: {
790
798
  callbacks: {
791
799
  title: this._tooltipTitleGenerator,
800
+ items: this._tooltipItemsGenerator,
792
801
  label: this._tooltipLabelGenerator,
793
802
  labelValue: this._tooltipLabelValueGenerator,
794
803
  labelColor: this._tooltipLabelColorGenerator
@@ -816,21 +825,20 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
816
825
  config = chart.config,
817
826
  dataset = tooltipItem.dataset,
818
827
  title = [];
819
- if (config.type === Chart.Type.BUBBLE) {
828
+ if (scout.isOneOf(config.type, Chart.Type.BUBBLE)) {
820
829
  let xAxis = config.options.scales.x,
821
830
  yAxis = config.options.scales.y,
822
- xAxisLabel = xAxis.title.text,
823
- yAxisLabel = yAxis.title.text;
824
- xAxisLabel = xAxisLabel ? strings.encode(xAxisLabel) : ChartJsRenderer.ARROW_LEFT_RIGHT;
825
- yAxisLabel = yAxisLabel ? strings.encode(yAxisLabel) : ' ' + ChartJsRenderer.ARROW_UP_DOWN + ' ';
831
+ axisLabels = this._getAxisLabels(config);
826
832
  let xTickLabel = xAxis.ticks.callback(dataset.data[tooltipItem.dataIndex].x);
827
833
  if (xTickLabel) {
828
- title.push(this._createTooltipAttribute(xAxisLabel, strings.encode(xTickLabel), true));
834
+ title.push(this._createTooltipAttribute(axisLabels.x, strings.encode(xTickLabel), true));
829
835
  }
830
836
  let yTickLabel = yAxis.ticks.callback(dataset.data[tooltipItem.dataIndex].y);
831
837
  if (yTickLabel) {
832
- title.push(this._createTooltipAttribute(yAxisLabel, strings.encode(yTickLabel), true));
838
+ title.push(this._createTooltipAttribute(axisLabels.y, strings.encode(yTickLabel), true));
833
839
  }
840
+ } else if (scout.isOneOf(config.type, Chart.Type.SCATTER)) {
841
+ // nop, scatter has the title in the items table
834
842
  } else {
835
843
  let label = chart.data.labels[tooltipItem.dataIndex];
836
844
  title.push(this._createTooltipAttribute(config.options.reformatLabels ? this._formatLabel(label) : label, '', true));
@@ -838,6 +846,78 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
838
846
  return title;
839
847
  }
840
848
 
849
+ _getAxisLabels(config) {
850
+ let xAxisLabel = config.options.scales.x.title.text,
851
+ yAxisLabel = config.options.scales.y.title.text;
852
+ xAxisLabel = this._resolveAxisLabel(xAxisLabel, ChartJsRenderer.ARROW_LEFT_RIGHT);
853
+ yAxisLabel = this._resolveAxisLabel(yAxisLabel, ' ' + ChartJsRenderer.ARROW_UP_DOWN + ' ');
854
+
855
+ return {x: xAxisLabel, y: yAxisLabel};
856
+ }
857
+
858
+ _resolveAxisLabel(axisLabel, defaultLabel = '') {
859
+ if (objects.isFunction(axisLabel)) {
860
+ axisLabel = axisLabel();
861
+ axisLabel = objects.isString(axisLabel) ? axisLabel : '';
862
+ }
863
+ return axisLabel ? strings.encode(axisLabel) : defaultLabel;
864
+ }
865
+
866
+ _generateTooltipItems(tooltipItems, tooltipLabel, tooltipLabelValue, tooltipColor) {
867
+ if (!tooltipItems || !tooltipItems.length) {
868
+ return '';
869
+ }
870
+ let tooltipItem = tooltipItems[0],
871
+ chart = tooltipItem.chart,
872
+ config = chart.config,
873
+ xAxisValues = false,
874
+ yAxisValues = false,
875
+ itemsText = '';
876
+
877
+ tooltipItems.forEach(tooltipItem => {
878
+ let {label, labelValue, labelColor} = this._getItemDetails(tooltipItem, tooltipLabel, tooltipLabelValue, tooltipColor);
879
+ if (scout.isOneOf(config.type, Chart.Type.SCATTER)) {
880
+ xAxisValues |= objects.isString(labelValue.x);
881
+ yAxisValues |= objects.isString(labelValue.y);
882
+ itemsText += this._createTooltipScatterAttribute(label, labelValue.x, labelValue.y, false, labelColor);
883
+ } else {
884
+ itemsText += this._createTooltipAttribute(label, labelValue, false, labelColor);
885
+ }
886
+ });
887
+
888
+ // tabular representation for scatter tooltip needs an additional header and footer
889
+ if (scout.isOneOf(config.type, Chart.Type.SCATTER)) {
890
+ let tableHeader = '<table><tbody>';
891
+ let axisLabels = this._getAxisLabels(config);
892
+ tableHeader += this._createTooltipScatterAttribute('',
893
+ xAxisValues ? axisLabels.x : '', // do not show axis label if no values are shown
894
+ yAxisValues ? axisLabels.y : '', // do not show axis label if no values are shown
895
+ true,
896
+ null);
897
+ let tableFooter = '</tbody></table>';
898
+ itemsText = strings.box(tableHeader, itemsText, tableFooter);
899
+ }
900
+
901
+ return itemsText;
902
+ }
903
+
904
+ _getItemDetails(tooltipItem, tooltipLabel, tooltipLabelValue, tooltipColor) {
905
+ let label, labelValue, labelColor;
906
+ if (objects.isFunction(tooltipLabel)) {
907
+ label = tooltipLabel(tooltipItem);
908
+ label = objects.isString(label) ? label : '';
909
+ }
910
+ if (objects.isFunction(tooltipLabelValue)) {
911
+ labelValue = tooltipLabelValue(tooltipItem);
912
+ labelValue = objects.isString(labelValue) || objects.isPlainObject(labelValue) ? labelValue : '';
913
+ }
914
+ if (objects.isFunction(tooltipColor)) {
915
+ labelColor = tooltipColor(tooltipItem);
916
+ labelColor = objects.isPlainObject(labelColor) ? (labelColor.backgroundColor || '') : '';
917
+ }
918
+ return {label, labelValue, labelColor};
919
+ }
920
+
841
921
  _generateTooltipLabel(tooltipItem) {
842
922
  return strings.encode(tooltipItem.dataset.label);
843
923
  }
@@ -847,6 +927,11 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
847
927
  dataset = tooltipItem.dataset;
848
928
  if (config.type === Chart.Type.BUBBLE) {
849
929
  return strings.encode(this._formatLabel(dataset.data[tooltipItem.dataIndex].z));
930
+ } else if (config.type === Chart.Type.SCATTER) {
931
+ return {
932
+ x: strings.encode(this._formatLabel(dataset.data[tooltipItem.dataIndex].x)),
933
+ y: strings.encode(this._formatLabel(dataset.data[tooltipItem.dataIndex].y))
934
+ };
850
935
  }
851
936
  return strings.encode(this._formatLabel(dataset.data[tooltipItem.dataIndex]));
852
937
  }
@@ -855,7 +940,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
855
940
  let config = tooltipItem.chart.config,
856
941
  dataset = tooltipItem.dataset,
857
942
  legendColor, backgroundColor, borderColor, index;
858
- if (scout.isOneOf((dataset.type || config.type), Chart.Type.LINE, Chart.Type.BAR, Chart.Type.BAR_HORIZONTAL, Chart.Type.RADAR, Chart.Type.BUBBLE)) {
943
+ if (scout.isOneOf((dataset.type || config.type), Chart.Type.LINE, Chart.Type.BAR, Chart.Type.BAR_HORIZONTAL, Chart.Type.RADAR, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
859
944
  borderColor = dataset.borderColor;
860
945
  legendColor = dataset.legendColor;
861
946
  index = tooltipItem.datasetIndex;
@@ -893,6 +978,18 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
893
978
  '</div>';
894
979
  }
895
980
 
981
+ _createTooltipScatterAttribute(label, xValue, yValue, isTitle, color) {
982
+ let cssClass = isTitle ? 'attribute title' : 'attribute';
983
+ return '<tr class="' + cssClass + '">' +
984
+ '<td class="color-cell">' +
985
+ (color ? '<div class="color" style="background-color:' + color + '"></div>' : '') +
986
+ '</td>' +
987
+ '<td class="label">' + label + '</td>' +
988
+ (xValue ? '<td class="value">' + xValue + '</td>' : '') +
989
+ (yValue ? '<td class="value">' + yValue + '</td>' : '') +
990
+ '</tr>';
991
+ }
992
+
896
993
  _renderTooltip(context) {
897
994
  let isHideTooltip = context.tooltip.opacity === 0 || context.tooltip._tooltipItems.length < 1;
898
995
  if (isHideTooltip) {
@@ -920,8 +1017,8 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
920
1017
  return;
921
1018
  }
922
1019
  let tooltip = context.tooltip,
923
- tooltipItems = tooltip._tooltipItems;
924
- if (tooltipItems.length < 1) {
1020
+ dataPoints = tooltip.dataPoints;
1021
+ if (dataPoints.length < 1) {
925
1022
  return;
926
1023
  }
927
1024
  if (this._tooltip) {
@@ -932,37 +1029,24 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
932
1029
  let tooltipOptions = tooltip.options || {},
933
1030
  tooltipCallbacks = tooltipOptions.callbacks || {},
934
1031
  tooltipTitle = tooltipCallbacks.title,
1032
+ tooltipItems = tooltipCallbacks.items,
935
1033
  tooltipLabel = tooltipCallbacks.label,
936
1034
  tooltipLabelValue = tooltipCallbacks.labelValue,
937
1035
  tooltipColor = tooltipCallbacks.labelColor,
938
1036
  tooltipText = '';
939
1037
 
940
1038
  if (objects.isFunction(tooltipTitle)) {
941
- tooltipText += arrays.ensure(tooltipTitle(tooltipItems)).join('');
1039
+ tooltipText += arrays.ensure(tooltipTitle(dataPoints)).join('');
1040
+ }
1041
+ if (objects.isFunction(tooltipItems)) {
1042
+ tooltipText += arrays.ensure(tooltipItems(dataPoints, tooltipLabel, tooltipLabelValue, tooltipColor)).join('');
942
1043
  }
943
- tooltipItems.forEach(tooltipItem => {
944
- let label, labelValue, labelColor;
945
- if (objects.isFunction(tooltipLabel)) {
946
- label = tooltipLabel(tooltipItem);
947
- label = objects.isString(label) ? label : '';
948
- }
949
- if (objects.isFunction(tooltipLabelValue)) {
950
- labelValue = tooltipLabelValue(tooltipItem);
951
- labelValue = objects.isString(labelValue) ? labelValue : '';
952
- }
953
- if (objects.isFunction(tooltipColor)) {
954
- labelColor = tooltipColor(tooltipItem);
955
- labelColor = objects.isPlainObject(labelColor) ? (labelColor.backgroundColor || '') : '';
956
- }
957
- tooltipText += this._createTooltipAttribute(label, labelValue, false, labelColor);
958
- });
959
1044
 
960
- let positionAndOffset = this._computeTooltipPositionAndOffset(tooltipItems[0]),
1045
+ let positionAndOffset = this._computeTooltipPositionAndOffset(dataPoints[0]),
961
1046
  origin = graphics.offsetBounds(this.$canvas);
962
1047
  origin.x += tooltip.caretX + positionAndOffset.offsetX;
963
1048
  origin.y += tooltip.caretY + positionAndOffset.offsetY;
964
- // Tooltip adds origin.height to origin.y in 'bottom' mode, but origin.y is already the correct value for 'top' and 'bottom' mode
965
- origin.height = 0;
1049
+ origin.height = positionAndOffset.height;
966
1050
 
967
1051
  this._tooltip = scout.create({
968
1052
  objectType: 'Tooltip',
@@ -1005,7 +1089,8 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1005
1089
  let tooltipPosition = 'top',
1006
1090
  tooltipDirection = 'right',
1007
1091
  offsetX = 0,
1008
- offsetY = 0;
1092
+ offsetY = 0,
1093
+ height = 0;
1009
1094
 
1010
1095
  let chart = tooltipItem.chart,
1011
1096
  datasetIndex = tooltipItem.datasetIndex,
@@ -1039,7 +1124,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1039
1124
  let angle = element.angle;
1040
1125
  tooltipPosition = (0 <= angle && angle < Math.PI) ? 'bottom' : 'top';
1041
1126
  tooltipDirection = (-Math.PI / 2 <= angle && angle < Math.PI / 2) ? 'right' : 'left';
1042
- } else if (config.type === Chart.Type.BUBBLE) {
1127
+ } else if (scout.isOneOf(config.type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
1043
1128
  let element = chart.getDatasetMeta(datasetIndex).data[dataIndex];
1044
1129
  let chartArea = chart.chartArea,
1045
1130
  mid = chartArea.left + (chartArea.width / 2);
@@ -1052,8 +1137,8 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1052
1137
  * @type BarElement
1053
1138
  */
1054
1139
  let element = chart.getDatasetMeta(datasetIndex).data[dataIndex];
1055
- let height = element.height,
1056
- width = element.width,
1140
+ height = element.height;
1141
+ let width = element.width,
1057
1142
  // golden ratio: (a + b) / a = a / b = PHI
1058
1143
  // and a + b = width
1059
1144
  // -> b = width / (PHI + 1)
@@ -1061,7 +1146,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1061
1146
 
1062
1147
  offsetY = -height / 2;
1063
1148
  offsetX = tooltipDirection === 'left' ? b : -b;
1064
- } else if (scout.isOneOf(config.type, Chart.Type.LINE, Chart.Type.BUBBLE, Chart.Type.RADAR) || dataset.type === Chart.Type.LINE) {
1149
+ } else if (scout.isOneOf(config.type, Chart.Type.LINE, Chart.Type.BUBBLE, Chart.Type.SCATTER, Chart.Type.RADAR) || dataset.type === Chart.Type.LINE) {
1065
1150
  // noinspection JSValidateTypes
1066
1151
  /**
1067
1152
  * @type PointElement
@@ -1073,7 +1158,8 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1073
1158
  offset += value.r;
1074
1159
  }
1075
1160
 
1076
- offsetY = tooltipPosition === 'top' ? -offset : offset;
1161
+ height = 2 * offset;
1162
+ offsetY = -offset;
1077
1163
  } else if (scout.isOneOf(config.type, Chart.Type.PIE, Chart.Type.DOUGHNUT, Chart.Type.POLAR_AREA)) {
1078
1164
  // noinspection JSValidateTypes
1079
1165
  /**
@@ -1090,12 +1176,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1090
1176
  offsetY = offset * Math.sin(angle);
1091
1177
  }
1092
1178
 
1093
- return {
1094
- tooltipPosition: tooltipPosition,
1095
- tooltipDirection: tooltipDirection,
1096
- offsetX: offsetX,
1097
- offsetY: offsetY
1098
- };
1179
+ return {tooltipPosition, tooltipDirection, offsetX, offsetY, height};
1099
1180
  }
1100
1181
 
1101
1182
  _adjustGrid(config) {
@@ -1159,6 +1240,23 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1159
1240
  }
1160
1241
  }
1161
1242
  }, config.options);
1243
+ } else if (scout.isOneOf(config.type, Chart.Type.SCATTER)) {
1244
+ config.options = $.extend(true, {}, {
1245
+ scales: {
1246
+ x: {
1247
+ minSpaceBetweenTicks: 35,
1248
+ ticks: {
1249
+ padding: 10
1250
+ }
1251
+ },
1252
+ y: {
1253
+ minSpaceBetweenTicks: 35,
1254
+ ticks: {
1255
+ padding: 10
1256
+ }
1257
+ }
1258
+ }
1259
+ }, config.options);
1162
1260
  }
1163
1261
 
1164
1262
  this._adjustXAxis(config);
@@ -1173,7 +1271,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1173
1271
  let type = config.type,
1174
1272
  scales = config.options.scales;
1175
1273
 
1176
- if (this._isHorizontalBar(config) || type === Chart.Type.BUBBLE) {
1274
+ if (this._isHorizontalBar(config) || scout.isOneOf(type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
1177
1275
  scales.x = $.extend(true, {}, {
1178
1276
  beginAtZero: this._isHorizontalBar(config),
1179
1277
  offset: type === Chart.Type.BUBBLE,
@@ -1194,7 +1292,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1194
1292
  }
1195
1293
  }, scales.x);
1196
1294
  }
1197
- if (this._isHorizontalBar(config) || type === Chart.Type.BUBBLE || config.options.reformatLabels) {
1295
+ if (this._isHorizontalBar(config) || scout.isOneOf(type, Chart.Type.BUBBLE, Chart.Type.SCATTER) || config.options.reformatLabels) {
1198
1296
  scales.x = $.extend(true, {}, {
1199
1297
  ticks: {
1200
1298
  callback: this._xLabelFormatter
@@ -1221,7 +1319,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1221
1319
  }, scales.y);
1222
1320
  } else {
1223
1321
  scales.y = $.extend(true, {}, {
1224
- beginAtZero: type !== Chart.Type.BUBBLE,
1322
+ beginAtZero: !scout.isOneOf(type, Chart.Type.BUBBLE, Chart.Type.SCATTER),
1225
1323
  grid: {
1226
1324
  drawBorder: false,
1227
1325
  drawTicks: false
@@ -1313,6 +1411,15 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1313
1411
  borderRadius: 3
1314
1412
  }, plugins.datalabels);
1315
1413
  plugins.datalabels.display = 'auto';
1414
+ } else if (scout.isOneOf(config.type, Chart.Type.SCATTER)) {
1415
+ plugins.datalabels = $.extend(true, {}, {
1416
+ backgroundColor: this._datalabelBackgroundColorHandler,
1417
+ borderRadius: 3,
1418
+ anchor: 'end',
1419
+ align: 'top',
1420
+ offset: 3
1421
+ }, plugins.datalabels);
1422
+ plugins.datalabels.display = 'auto';
1316
1423
  }
1317
1424
  if (config.options.reformatLabels) {
1318
1425
  let handleFormatter = formatter => {
@@ -1401,7 +1508,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1401
1508
  this.chart.session.text('ui.Tri'),
1402
1509
  this.chart.session.text('ui.Trd')];
1403
1510
  for (let i = 0; i < abbreviations.length; i++) {
1404
- if (abs >= 1000000) {
1511
+ if (abs >= 1000) {
1405
1512
  abs = abs / 1000;
1406
1513
  abbreviation = ' ' + abbreviations[i];
1407
1514
  } else {
@@ -1533,6 +1640,8 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1533
1640
  _formatDatalabels(value, context) {
1534
1641
  if (context.chart.config.type === Chart.Type.BUBBLE) {
1535
1642
  return this._formatLabel(value.z);
1643
+ } else if (context.chart.config.type === Chart.Type.SCATTER) {
1644
+ return strings.join(' / ', this._formatLabel(value.x), this._formatLabel(value.y));
1536
1645
  }
1537
1646
  return this._formatLabel(value);
1538
1647
  }
@@ -1608,7 +1717,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1608
1717
  }
1609
1718
  if (checkable) {
1610
1719
  let datasetLength = elem.data.length;
1611
- if (scout.isOneOf(type, Chart.Type.PIE, Chart.Type.DOUGHNUT, Chart.Type.POLAR_AREA, Chart.Type.BUBBLE) || (type === Chart.Type.BAR && (elem.type || Chart.Type.BAR) === Chart.Type.BAR)) {
1720
+ if (scout.isOneOf(type, Chart.Type.PIE, Chart.Type.DOUGHNUT, Chart.Type.POLAR_AREA, Chart.Type.BUBBLE, Chart.Type.SCATTER) || (type === Chart.Type.BAR && (elem.type || Chart.Type.BAR) === Chart.Type.BAR)) {
1612
1721
  let uncheckedBackgroundColor = (multipleColorsPerDataset ? colors.backgroundColors : arrays.init(datasetLength, colors.backgroundColors[idx])),
1613
1722
  uncheckedHoverBackgroundColor = (multipleColorsPerDataset ? colors.hoverBackgroundColors : arrays.init(datasetLength, colors.hoverBackgroundColors[idx])),
1614
1723
 
@@ -1816,7 +1925,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1816
1925
  checkedBackgroundOpacity = 0.2;
1817
1926
  checkedHoverBackgroundOpacity = 0.35;
1818
1927
  checkedHoverBackgroundDarker = 0;
1819
- } else if (type === Chart.Type.BUBBLE) {
1928
+ } else if (scout.isOneOf(type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
1820
1929
  backgroundOpacity = 0.2;
1821
1930
  hoverBackgroundOpacity = 0.35;
1822
1931
  hoverBackgroundDarker = 0;
@@ -1922,7 +2031,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
1922
2031
  elem.text = strings.truncateText(elem.text, horizontalSpace, measureText);
1923
2032
  let dataset = data.datasets[idx],
1924
2033
  legendColor, borderColor, backgroundColor;
1925
- if (dataset && scout.isOneOf((dataset.type || config.type), Chart.Type.LINE, Chart.Type.BAR, Chart.Type.RADAR, Chart.Type.BUBBLE)) {
2034
+ if (dataset && scout.isOneOf((dataset.type || config.type), Chart.Type.LINE, Chart.Type.BAR, Chart.Type.RADAR, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
1926
2035
  legendColor = dataset.legendColor;
1927
2036
  borderColor = this._adjustColorOpacity(dataset.borderColor, 1);
1928
2037
  } else if (data.datasets.length && scout.isOneOf(config.type, Chart.Type.PIE, Chart.Type.DOUGHNUT, Chart.Type.POLAR_AREA)) {
@@ -2086,7 +2195,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2086
2195
  datasetIndex: datasetIndex,
2087
2196
  dataIndex: itemIndex
2088
2197
  };
2089
- if (this.chartJs.config.type === Chart.Type.BUBBLE) {
2198
+ if (scout.isOneOf(this.chartJs.config.type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
2090
2199
  let data = this.chartJs.config.data.datasets[datasetIndex].data[itemIndex];
2091
2200
  clickObject.xIndex = data.x;
2092
2201
  clickObject.yIndex = data.y;
@@ -2285,7 +2394,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2285
2394
  datasets = config.data.datasets,
2286
2395
  dataset = datasets ? datasets[datasetIndex] : null,
2287
2396
  datasetType = dataset ? dataset.type : null;
2288
- if (scout.isOneOf(type, Chart.Type.LINE, Chart.Type.PIE, Chart.Type.DOUGHNUT, Chart.Type.POLAR_AREA) || datasetType === Chart.Type.LINE) {
2397
+ if (scout.isOneOf(type, Chart.Type.LINE, Chart.Type.PIE, Chart.Type.DOUGHNUT, Chart.Type.POLAR_AREA, Chart.Type.SCATTER) || datasetType === Chart.Type.LINE) {
2289
2398
  mode = 'point';
2290
2399
  } else {
2291
2400
  mode = 'dataset';
@@ -2342,7 +2451,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2342
2451
  data.r = Math.sqrt(data.z);
2343
2452
  }
2344
2453
  }));
2345
- let maxMinR = this._computeMaxMinValue(datasets, 'r', true),
2454
+ let maxMinR = this._computeMaxMinValue(config, datasets, 'r', true),
2346
2455
  maxR = maxMinR.maxValue,
2347
2456
  minR = maxMinR.minValue,
2348
2457
  // Compute a scalingFactor and an offset to get the new radius newR = r * scalingFactor + offset.
@@ -2396,29 +2505,35 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2396
2505
  }));
2397
2506
  }
2398
2507
 
2399
- _computeMaxMinValue(datasets, identifier, exact, boundRange, padding, space) {
2508
+ _computeMaxMinValue(config, datasets, identifier, exact, boundRange, padding, space) {
2400
2509
  if (!datasets) {
2401
2510
  return;
2402
2511
  }
2403
2512
 
2404
2513
  let maxValue, minValue;
2405
- for (let i = 0; i < datasets.length; i++) {
2406
- for (let j = 0; j < datasets[i].data.length; j++) {
2407
- let value;
2408
- if (identifier) {
2409
- value = datasets[i].data[j][identifier];
2410
- } else {
2411
- value = datasets[i].data[j];
2412
- }
2413
- if (isNaN(maxValue)) {
2414
- maxValue = value;
2415
- } else {
2416
- maxValue = Math.max(value, maxValue);
2417
- }
2418
- if (isNaN(minValue)) {
2419
- minValue = value;
2420
- } else {
2421
- minValue = Math.min(value, minValue);
2514
+ if (config.type === Chart.Type.SCATTER && identifier === 'r') {
2515
+ // do not move the grid boundaries because of the radii of the points (would look weird)
2516
+ maxValue = 0;
2517
+ minValue = 0;
2518
+ } else {
2519
+ for (let i = 0; i < datasets.length; i++) {
2520
+ for (let j = 0; j < datasets[i].data.length; j++) {
2521
+ let value;
2522
+ if (identifier) {
2523
+ value = datasets[i].data[j][identifier];
2524
+ } else {
2525
+ value = datasets[i].data[j];
2526
+ }
2527
+ if (isNaN(maxValue)) {
2528
+ maxValue = value;
2529
+ } else {
2530
+ maxValue = Math.max(value, maxValue);
2531
+ }
2532
+ if (isNaN(minValue)) {
2533
+ minValue = value;
2534
+ } else {
2535
+ minValue = Math.min(value, minValue);
2536
+ }
2422
2537
  }
2423
2538
  }
2424
2539
  }
@@ -2503,7 +2618,7 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2503
2618
  }
2504
2619
 
2505
2620
  let type = config.type;
2506
- if (!scout.isOneOf(type, Chart.Type.BAR, Chart.Type.LINE, Chart.Type.POLAR_AREA, Chart.Type.RADAR, Chart.Type.BUBBLE)) {
2621
+ if (!scout.isOneOf(type, Chart.Type.BAR, Chart.Type.LINE, Chart.Type.POLAR_AREA, Chart.Type.RADAR, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
2507
2622
  return;
2508
2623
  }
2509
2624
 
@@ -2542,16 +2657,16 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2542
2657
  this._adjustAxisMaxMin(yAxis, maxYTicks, yBoundary);
2543
2658
  }
2544
2659
 
2545
- if (type !== Chart.Type.BUBBLE) {
2660
+ if (!scout.isOneOf(type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
2546
2661
  return;
2547
2662
  }
2548
2663
 
2549
- let xBoundary = this._computeXBoundaryBubble(config, width);
2664
+ let xBoundary = this._computeXBoundaryPointElement(config, width);
2550
2665
  this._adjustAxisMaxMin(xAxis, maxXTicks, xBoundary);
2551
2666
  }
2552
2667
 
2553
- _computeBoundaryBubble(config, identifier, space) {
2554
- if (!config || !config.type || config.type !== Chart.Type.BUBBLE || !config.data || !config.options || !config.options.scales || !(identifier === 'x' || identifier === 'y') || !space) {
2668
+ _computeBoundaryPointElement(config, identifier, space) {
2669
+ if (!config || !config.type || !scout.isOneOf(config.type, Chart.Type.BUBBLE, Chart.Type.SCATTER) || !config.data || !config.options || !config.options.scales || !(identifier === 'x' || identifier === 'y') || !space) {
2555
2670
  return;
2556
2671
  }
2557
2672
 
@@ -2561,16 +2676,16 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2561
2676
  labelMap = config.options[identifier + 'LabelMap'],
2562
2677
  boundary;
2563
2678
 
2564
- let maxR = this._computeMaxMinValue(datasets, 'r', true).maxValue,
2679
+ let maxR = this._computeMaxMinValue(config, datasets, 'r', true).maxValue,
2565
2680
  padding = maxR;
2566
2681
  if (config.options.elements && config.options.elements.point && config.options.elements.point.hoverRadius) {
2567
2682
  padding = padding + config.options.elements.point.hoverRadius;
2568
2683
  }
2569
2684
 
2570
2685
  if (offset) {
2571
- boundary = this._computeMaxMinValue(datasets, identifier, labelMap, true);
2686
+ boundary = this._computeMaxMinValue(config, datasets, identifier, labelMap, true);
2572
2687
  } else {
2573
- boundary = this._computeMaxMinValue(datasets, identifier, labelMap, true, padding, space);
2688
+ boundary = this._computeMaxMinValue(config, datasets, identifier, labelMap, true, padding, space);
2574
2689
  }
2575
2690
  if (labelMap) {
2576
2691
  boundary.maxValue = Math.ceil(boundary.maxValue);
@@ -2579,12 +2694,12 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2579
2694
  return boundary;
2580
2695
  }
2581
2696
 
2582
- _computeXBoundaryBubble(config, width) {
2583
- return this._computeBoundaryBubble(config, 'x', width);
2697
+ _computeXBoundaryPointElement(config, width) {
2698
+ return this._computeBoundaryPointElement(config, 'x', width);
2584
2699
  }
2585
2700
 
2586
- _computeYBoundaryBubble(config, height) {
2587
- return this._computeBoundaryBubble(config, 'y', height);
2701
+ _computeYBoundaryPointElement(config, height) {
2702
+ return this._computeBoundaryPointElement(config, 'y', height);
2588
2703
  }
2589
2704
 
2590
2705
  _computeYBoundaries(config, height) {
@@ -2596,8 +2711,8 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2596
2711
  yBoundary,
2597
2712
  yBoundaryDiffType;
2598
2713
 
2599
- if (type === Chart.Type.BUBBLE) {
2600
- yBoundary = this._computeYBoundaryBubble(config, height);
2714
+ if (scout.isOneOf(type, Chart.Type.BUBBLE, Chart.Type.SCATTER)) {
2715
+ yBoundary = this._computeYBoundaryPointElement(config, height);
2601
2716
  } else {
2602
2717
  let datasets = [],
2603
2718
  datasetsDiffType = [];
@@ -2611,10 +2726,10 @@ export default class ChartJsRenderer extends AbstractChartRenderer {
2611
2726
  });
2612
2727
  }
2613
2728
 
2614
- yBoundary = this._computeMaxMinValue(datasets);
2729
+ yBoundary = this._computeMaxMinValue(config, datasets);
2615
2730
 
2616
2731
  if (datasets.length && datasetsDiffType.length) {
2617
- yBoundaryDiffType = this._computeMaxMinValue(datasetsDiffType);
2732
+ yBoundaryDiffType = this._computeMaxMinValue(config, datasetsDiffType);
2618
2733
  let yBoundaryRange = yBoundary.maxValue - yBoundary.minValue,
2619
2734
  yBoundaryRangeDiffType = yBoundaryDiffType.maxValue - yBoundaryDiffType.minValue;
2620
2735
  if (yBoundaryRange && yBoundaryRangeDiffType && (yBoundaryRange / yBoundaryRangeDiffType > 10 || yBoundaryRangeDiffType / yBoundaryRange > 10)) {
@@ -1,14 +1,14 @@
1
1
  /*
2
- * Copyright (c) 2010-2021 BSI Business Systems Integration AG.
2
+ * Copyright (c) 2010-2022 BSI Business Systems Integration AG.
3
3
  * All rights reserved. This program and the accompanying materials
4
4
  * are made available under the terms of the Eclipse Public License v1.0
5
5
  * which accompanies this distribution, and is available at
6
- * http://www.eclipse.org/legal/epl-v10.html
6
+ * https://www.eclipse.org/legal/epl-v10.html
7
7
  *
8
8
  * Contributors:
9
9
  * BSI Business Systems Integration AG - initial API and implementation
10
10
  */
11
- import {strings} from '@eclipse-scout/core';
11
+ import {objects, strings} from '@eclipse-scout/core';
12
12
  import {AbstractSvgChartRenderer} from '../index';
13
13
  import $ from 'jquery';
14
14
 
@@ -344,13 +344,10 @@ export default class SalesfunnelChartRenderer extends AbstractSvgChartRenderer {
344
344
  }
345
345
 
346
346
  _calcConversionRate(valueBefore, value) {
347
- let returnValue = 0;
348
- if (valueBefore === 0) {
349
- returnValue = undefined;
350
- } else if (value !== valueBefore) {
351
- returnValue = Math.round(value / valueBefore * 100);
347
+ if (objects.isNullOrUndefined(valueBefore) || objects.isNullOrUndefined(value) || valueBefore === 0) {
348
+ return undefined;
352
349
  }
353
- return returnValue;
350
+ return Math.round(value / valueBefore * 100);
354
351
  }
355
352
 
356
353
  _analyzeData(valueGroups) {