@progress/kendo-charts 1.31.0 → 1.32.0

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.
Files changed (60) hide show
  1. package/README.md +1 -1
  2. package/dist/cdn/js/kendo-charts.js +1 -1
  3. package/dist/cdn/main.js +1 -1
  4. package/dist/es/chart/base-theme.js +22 -2
  5. package/dist/es/chart/categorical-chart.js +6 -26
  6. package/dist/es/chart/constants.js +11 -1
  7. package/dist/es/chart/heatmap-chart/heatmap-chart.js +2 -6
  8. package/dist/es/chart/plotarea/categorical-plotarea.js +83 -16
  9. package/dist/es/chart/plotarea/plotarea-base.js +71 -2
  10. package/dist/es/chart/plotarea/plotarea-factory.js +4 -2
  11. package/dist/es/chart/plotarea/polar-plotarea.js +21 -2
  12. package/dist/es/chart/plotarea/radar-plotarea.js +13 -2
  13. package/dist/es/chart/plotarea/xy-plotarea.js +38 -5
  14. package/dist/es/chart/register-charts.js +12 -7
  15. package/dist/es/chart/scatter-charts/scatter-chart.js +3 -7
  16. package/dist/es/chart/trendlines/calculate-moving-average.js +44 -0
  17. package/dist/es/chart/trendlines/calculate-slope.js +37 -0
  18. package/dist/es/chart/trendlines/linear-trendline.js +61 -0
  19. package/dist/es/chart/trendlines/moving-average.js +65 -0
  20. package/dist/es/chart/trendlines/scatter-linear-trendline.js +63 -0
  21. package/dist/es/chart/trendlines/scatter-moving-average.js +34 -0
  22. package/dist/es/chart/trendlines/scatter-trendline-registry.js +9 -0
  23. package/dist/es/chart/trendlines/scatter-value-getter.js +7 -0
  24. package/dist/es/chart/trendlines/trendline-factory.js +10 -0
  25. package/dist/es/chart/trendlines/trendline-registry.js +9 -0
  26. package/dist/es/chart/utils.js +0 -2
  27. package/dist/es/common/constants.js +1 -0
  28. package/dist/es/common/hash-map.js +7 -11
  29. package/dist/es/core/category-axis.js +37 -30
  30. package/dist/es/core/date-category-axis.js +27 -3
  31. package/dist/es2015/chart/base-theme.js +22 -2
  32. package/dist/es2015/chart/categorical-chart.js +6 -26
  33. package/dist/es2015/chart/constants.js +11 -1
  34. package/dist/es2015/chart/heatmap-chart/heatmap-chart.js +2 -6
  35. package/dist/es2015/chart/plotarea/categorical-plotarea.js +81 -16
  36. package/dist/es2015/chart/plotarea/plotarea-base.js +69 -2
  37. package/dist/es2015/chart/plotarea/plotarea-factory.js +4 -2
  38. package/dist/es2015/chart/plotarea/polar-plotarea.js +21 -2
  39. package/dist/es2015/chart/plotarea/radar-plotarea.js +13 -2
  40. package/dist/es2015/chart/plotarea/xy-plotarea.js +36 -5
  41. package/dist/es2015/chart/register-charts.js +14 -5
  42. package/dist/es2015/chart/scatter-charts/scatter-chart.js +3 -7
  43. package/dist/es2015/chart/trendlines/calculate-moving-average.js +42 -0
  44. package/dist/es2015/chart/trendlines/calculate-slope.js +35 -0
  45. package/dist/es2015/chart/trendlines/linear-trendline.js +51 -0
  46. package/dist/es2015/chart/trendlines/moving-average.js +53 -0
  47. package/dist/es2015/chart/trendlines/scatter-linear-trendline.js +57 -0
  48. package/dist/es2015/chart/trendlines/scatter-moving-average.js +31 -0
  49. package/dist/es2015/chart/trendlines/scatter-trendline-registry.js +9 -0
  50. package/dist/es2015/chart/trendlines/scatter-value-getter.js +4 -0
  51. package/dist/es2015/chart/trendlines/trendline-factory.js +10 -0
  52. package/dist/es2015/chart/trendlines/trendline-registry.js +9 -0
  53. package/dist/es2015/chart/utils.js +0 -2
  54. package/dist/es2015/common/constants.js +1 -0
  55. package/dist/es2015/common/hash-map.js +7 -11
  56. package/dist/es2015/core/category-axis.js +37 -30
  57. package/dist/es2015/core/date-category-axis.js +27 -3
  58. package/dist/npm/main.js +889 -353
  59. package/dist/systemjs/kendo-charts.js +1 -1
  60. package/package.json +1 -1
@@ -1,11 +1,9 @@
1
1
  import ErrorRangeCalculator from './error-bars/error-range-calculator';
2
2
  import CategoricalErrorBar from './error-bars/categorical-error-bar';
3
3
 
4
- import SeriesBinder from './series-binder';
5
4
  import { ERROR_LOW_FIELD, ERROR_HIGH_FIELD } from './constants';
6
5
 
7
- import evalOptions from './utils/eval-options';
8
- import categoriesCount from './utils/categories-count';
6
+ import { evalOptions, categoriesCount } from './utils';
9
7
 
10
8
  import { ChartElement, Box } from '../core';
11
9
 
@@ -420,9 +418,9 @@ class CategoricalChart extends ChartElement {
420
418
  }
421
419
 
422
420
  limitPoint(point) {
423
- const limittedSlot = this.categoryAxis.limitSlot(point.box);
424
- if (!limittedSlot.equals(point.box)) {
425
- point.reflow(limittedSlot);
421
+ const limitedSlot = this.categoryAxis.limitSlot(point.box);
422
+ if (!limitedSlot.equals(point.box)) {
423
+ point.reflow(limitedSlot);
426
424
  }
427
425
  }
428
426
 
@@ -476,7 +474,7 @@ class CategoricalChart extends ChartElement {
476
474
  for (let seriesIx = 0; seriesIx < seriesCount; seriesIx++) {
477
475
  const currentSeries = series[seriesIx];
478
476
  const currentCategory = this.categoryAxis.categoryAt(categoryIx);
479
- const pointData = this._bindPoint(currentSeries, seriesIx, categoryIx);
477
+ const pointData = this.plotArea.bindPoint(currentSeries, categoryIx);
480
478
 
481
479
  callback(pointData, {
482
480
  category: currentCategory,
@@ -497,7 +495,7 @@ class CategoricalChart extends ChartElement {
497
495
  const outOfRangePoint = series[field];
498
496
  if (outOfRangePoint) {
499
497
  const categoryIx = outOfRangePoint.categoryIx;
500
- const pointData = this._bindPoint(series, seriesIx, categoryIx, outOfRangePoint.item);
498
+ const pointData = this.plotArea.bindPoint(series, categoryIx, outOfRangePoint.item);
501
499
 
502
500
  callback(pointData, {
503
501
  category: outOfRangePoint.category,
@@ -509,24 +507,6 @@ class CategoricalChart extends ChartElement {
509
507
  }
510
508
  }
511
509
 
512
- _bindPoint(series, seriesIx, categoryIx, item) {
513
- if (!this._bindCache) {
514
- this._bindCache = [];
515
- }
516
-
517
- let bindCache = this._bindCache[seriesIx];
518
- if (!bindCache) {
519
- bindCache = this._bindCache[seriesIx] = [];
520
- }
521
-
522
- let data = bindCache[categoryIx];
523
- if (!data) {
524
- data = bindCache[categoryIx] = SeriesBinder.current.bindPoint(series, categoryIx, item);
525
- }
526
-
527
- return data;
528
- }
529
-
530
510
  formatPointValue(point, format) {
531
511
  if (point.value === null) {
532
512
  return "";
@@ -62,6 +62,12 @@ const EQUALLY_SPACED_SERIES = [
62
62
  BULLET, RANGE_COLUMN, RANGE_BAR, WATERFALL, HORIZONTAL_WATERFALL
63
63
  ];
64
64
 
65
+ const TRENDLINE_LINEAR = 'linearTrendline';
66
+ const TRENDLINE_MOVING_AVERAGE = 'movingAverageTrendline';
67
+ const TRENDLINE_SERIES = [
68
+ TRENDLINE_LINEAR, TRENDLINE_MOVING_AVERAGE
69
+ ];
70
+
65
71
  const LEGEND_ITEM_CLICK = "legendItemClick";
66
72
  const LEGEND_ITEM_HOVER = "legendItemHover";
67
73
  const LEGEND_ITEM_LEAVE = "legendItemLeave";
@@ -100,6 +106,8 @@ const MOUSEWHEEL_ZOOM_RATE = 0.3;
100
106
  const DRILLDOWN = "drilldown";
101
107
  const DRILLDOWN_FIELD = "drilldown";
102
108
 
109
+ const MIN_MOVING_AVERAGE_PERIOD = 2;
110
+
103
111
  export {
104
112
  INITIAL_ANIMATION_DURATION, FADEIN,
105
113
  LEGEND_ITEM_CLICK, LEGEND_ITEM_HOVER, LEGEND_ITEM_LEAVE,
@@ -124,5 +132,7 @@ export {
124
132
  SHOW_TOOLTIP, HIDE_TOOLTIP,
125
133
  EQUALLY_SPACED_SERIES, ABOVE, BELOW,
126
134
  HEATMAP,
127
- DRILLDOWN, DRILLDOWN_FIELD
135
+ DRILLDOWN, DRILLDOWN_FIELD,
136
+ MIN_MOVING_AVERAGE_PERIOD,
137
+ TRENDLINE_SERIES, TRENDLINE_LINEAR, TRENDLINE_MOVING_AVERAGE
128
138
  };
@@ -1,7 +1,6 @@
1
1
  import { deepExtend, defined, isFunction, setDefaultOptions } from '../../common';
2
2
  import { MAX_VALUE, MIN_VALUE } from '../../common/constants';
3
3
  import { Box, ChartElement } from '../../core';
4
- import CategoricalChart from '../categorical-chart';
5
4
  import evalOptions from '../utils/eval-options';
6
5
  import colorScale from './color-scale';
7
6
  import HeatmapPoint from './heatmap-point';
@@ -36,7 +35,7 @@ class HeatmapChart extends ChartElement {
36
35
  const currentSeries = series[seriesIx];
37
36
 
38
37
  for (let pointIx = 0; pointIx < currentSeries.data.length; pointIx++) {
39
- const { valueFields } = this._bindPoint(currentSeries, seriesIx, pointIx);
38
+ const { valueFields } = this.plotArea.bindPoint(currentSeries, pointIx);
40
39
  if (defined(valueFields.value) && valueFields.value !== null) {
41
40
  this.valueRange.min = Math.min(this.valueRange.min, valueFields.value);
42
41
  this.valueRange.max = Math.max(this.valueRange.max, valueFields.value);
@@ -191,7 +190,7 @@ class HeatmapChart extends ChartElement {
191
190
  const yRange = yAxis.currentRangeIndices();
192
191
 
193
192
  for (let pointIx = 0; pointIx < currentSeries.data.length; pointIx++) {
194
- const { valueFields: value, fields } = this._bindPoint(currentSeries, seriesIx, pointIx);
193
+ const { valueFields: value, fields } = this.plotArea.bindPoint(currentSeries, pointIx);
195
194
  const xIndex = xAxis.totalIndex(value.x);
196
195
  const yIndex = yAxis.totalIndex(value.y);
197
196
  const xIn = xRange.min <= xIndex && xIndex <= xRange.max;
@@ -234,8 +233,5 @@ setDefaultOptions(HeatmapChart, {
234
233
  },
235
234
  clip: true
236
235
  });
237
- deepExtend(HeatmapChart.prototype, {
238
- _bindPoint: CategoricalChart.prototype._bindPoint
239
- });
240
236
 
241
237
  export default HeatmapChart;
@@ -14,6 +14,8 @@ import OHLCChart from '../ohlc-chart/ohlc-chart';
14
14
  import CandlestickChart from '../candlestick-chart/candlestick-chart';
15
15
  import BoxPlotChart from '../box-plot-chart/box-plot-chart';
16
16
  import WaterfallChart from '../waterfall-chart/waterfall-chart';
17
+ import trendlineFactory from '../trendlines/trendline-factory';
18
+ import trendlineRegistry from '../trendlines/trendline-registry';
17
19
 
18
20
  import { CategoryAxis, DateCategoryAxis, NumericAxis, LogarithmicAxis, Point } from '../../core';
19
21
 
@@ -25,7 +27,7 @@ import { BAR, COLUMN, BULLET, VERTICAL_BULLET, LINE, VERTICAL_LINE, AREA, VERTIC
25
27
  BOX_PLOT, VERTICAL_BOX_PLOT, OHLC, CANDLESTICK, LOGARITHMIC, STEP, EQUALLY_SPACED_SERIES } from '../constants';
26
28
 
27
29
  import { DATE, MAX_VALUE } from '../../common/constants';
28
- import { setDefaultOptions, inArray, isNumber, deepExtend, defined, eventElement, grep } from '../../common';
30
+ import { setDefaultOptions, inArray, deepExtend, defined, eventElement, grep } from '../../common';
29
31
 
30
32
  const AREA_SERIES = [ AREA, VERTICAL_AREA, RANGE_AREA, VERTICAL_RANGE_AREA ];
31
33
  const OUT_OF_RANGE_SERIES = [ LINE, VERTICAL_LINE ].concat(AREA_SERIES);
@@ -36,6 +38,8 @@ class CategoricalPlotArea extends PlotAreaBase {
36
38
  this.namedCategoryAxes = {};
37
39
  this.namedValueAxes = {};
38
40
  this.valueAxisRangeTracker = new AxisGroupRangeTracker();
41
+ this._seriesPointsCache = {};
42
+ this._currentPointsCache = {};
39
43
 
40
44
  if (series.length > 0) {
41
45
  this.invertAxes = inArray(
@@ -51,12 +55,15 @@ class CategoricalPlotArea extends PlotAreaBase {
51
55
  }
52
56
  }
53
57
  }
54
-
55
58
  }
56
59
 
57
60
  render(panes = this.panes) {
61
+ this.series = [...this.originalSeries];
58
62
  this.createCategoryAxes(panes);
63
+
59
64
  this.aggregateCategories(panes);
65
+ this.createTrendlineSeries(panes);
66
+
60
67
  this.createCategoryAxesLabels(panes);
61
68
  this.createCharts(panes);
62
69
  this.createValueAxes(panes);
@@ -83,6 +90,60 @@ class CategoricalPlotArea extends PlotAreaBase {
83
90
  }
84
91
  }
85
92
 
93
+ trendlineFactory(options, series) {
94
+ const categoryAxis = this.seriesCategoryAxis(options);
95
+ const seriesValues = this.seriesValues.bind(this, series.index);
96
+
97
+ const trendline = trendlineFactory(trendlineRegistry, options.type, {
98
+ options,
99
+ categoryAxis,
100
+ seriesValues
101
+ });
102
+
103
+ if (trendline) {
104
+ // Inherit settings
105
+ trendline.categoryAxis = series.categoryAxis;
106
+ trendline.valueAxis = series.valueAxis;
107
+
108
+ return this.filterSeries(trendline, categoryAxis);
109
+ }
110
+
111
+ return trendline;
112
+ }
113
+
114
+ trendlineAggregateForecast() {
115
+ return this.series
116
+ .map(series => (series.trendline || {}).forecast)
117
+ .filter(forecast => forecast !== undefined)
118
+ .reduce((result, forecast) => ({
119
+ before: Math.max(result.before, forecast.before || 0),
120
+ after: Math.max(result.after, forecast.after || 0)
121
+ }), { before: 0, after: 0 });
122
+ }
123
+
124
+ seriesValues(seriesIx, range) {
125
+ const result = [];
126
+
127
+ let series = this.srcSeries[seriesIx];
128
+ const categoryAxis = this.seriesCategoryAxis(series);
129
+ const dateAxis = equalsIgnoreCase(categoryAxis.options.type, DATE);
130
+ if (dateAxis) {
131
+ this._seriesPointsCache = {};
132
+ this._currentPointsCache = {};
133
+ categoryAxis.options.dataItems = [];
134
+ series = this.aggregateSeries(series, categoryAxis, categoryAxis.totalRangeIndices());
135
+ }
136
+
137
+ const min = range ? range.min : 0;
138
+ const max = range ? range.max : series.data.length;
139
+ for (let categoryIx = min; categoryIx < max; categoryIx++) {
140
+ const data = this.bindPoint(series, categoryIx);
141
+ result.push({ categoryIx, category: data.fields.category, valueFields: data.valueFields });
142
+ }
143
+
144
+ return result;
145
+ }
146
+
86
147
  createCharts(panes) {
87
148
  const seriesByPane = this.groupSeriesByPane();
88
149
 
@@ -146,21 +207,24 @@ class CategoricalPlotArea extends PlotAreaBase {
146
207
  }
147
208
 
148
209
  aggregateCategories(panes) {
149
- const series = this.srcSeries || this.series;
210
+ const series = [...this.series];
150
211
  const processedSeries = [];
151
212
  this._currentPointsCache = {};
152
213
  this._seriesPointsCache = this._seriesPointsCache || {};
153
214
 
154
215
  for (let i = 0; i < series.length; i++) {
155
216
  let currentSeries = series[i];
156
- const categoryAxis = this.seriesCategoryAxis(currentSeries);
157
- const axisPane = this.findPane(categoryAxis.options.pane);
158
- const dateAxis = equalsIgnoreCase(categoryAxis.options.type, DATE);
159
217
 
160
- if ((dateAxis || currentSeries.categoryField) && inArray(axisPane, panes)) {
161
- currentSeries = this.aggregateSeries(currentSeries, categoryAxis);
162
- } else {
163
- currentSeries = this.filterSeries(currentSeries, categoryAxis);
218
+ if (!this.isTrendline(currentSeries)) {
219
+ const categoryAxis = this.seriesCategoryAxis(currentSeries);
220
+ const axisPane = this.findPane(categoryAxis.options.pane);
221
+ const dateAxis = equalsIgnoreCase(categoryAxis.options.type, DATE);
222
+
223
+ if ((dateAxis || currentSeries.categoryField) && inArray(axisPane, panes)) {
224
+ currentSeries = this.aggregateSeries(currentSeries, categoryAxis, categoryAxis.currentRangeIndices());
225
+ } else {
226
+ currentSeries = this.filterSeries(currentSeries, categoryAxis);
227
+ }
164
228
  }
165
229
 
166
230
  processedSeries.push(currentSeries);
@@ -177,7 +241,7 @@ class CategoricalPlotArea extends PlotAreaBase {
177
241
  const dataLength = (series.data || {}).length;
178
242
  categoryAxis._seriesMax = Math.max(categoryAxis._seriesMax || 0, dataLength);
179
243
 
180
- if (!(isNumber(categoryAxis.options.min) || isNumber(categoryAxis.options.max))) {
244
+ if (!(defined(categoryAxis.options.min) || defined(categoryAxis.options.max))) {
181
245
  return series;
182
246
  }
183
247
 
@@ -204,7 +268,7 @@ class CategoricalPlotArea extends PlotAreaBase {
204
268
 
205
269
  seriesSourcePoints(series, categoryAxis) {
206
270
  const key = `${ series.index };${ categoryAxis.categoriesHash() }`;
207
- if (this._seriesPointsCache[key]) {
271
+ if (this._seriesPointsCache && this._seriesPointsCache[key]) {
208
272
  this._currentPointsCache[key] = this._seriesPointsCache[key];
209
273
  return this._seriesPointsCache[key];
210
274
  }
@@ -216,7 +280,7 @@ class CategoricalPlotArea extends PlotAreaBase {
216
280
  const getFn = dateAxis ? getDateField : getField;
217
281
  const result = [];
218
282
  if (!dateAxis) {
219
- categoryAxis.mapCategories();//fixes major performance issue caused by searching for the index for large data
283
+ categoryAxis.mapCategories(); //fixes major performance issue caused by searching for the index for large data
220
284
  }
221
285
 
222
286
  for (let idx = 0; idx < srcData.length; idx++) {
@@ -239,7 +303,7 @@ class CategoricalPlotArea extends PlotAreaBase {
239
303
  return result;
240
304
  }
241
305
 
242
- aggregateSeries(series, categoryAxis) {
306
+ aggregateSeries(series, categoryAxis, range) {
243
307
  const srcData = series.data;
244
308
  if (!srcData.length) {
245
309
  return series;
@@ -249,9 +313,9 @@ class CategoricalPlotArea extends PlotAreaBase {
249
313
  const result = deepExtend({}, series);
250
314
  const aggregator = new SeriesAggregator(deepExtend({}, series), SeriesBinder.current, DefaultAggregates.current);
251
315
  const data = result.data = [];
316
+
252
317
  const dataItems = categoryAxis.options.dataItems || [];
253
318
 
254
- const range = categoryAxis.currentRangeIndices();
255
319
  const categoryItem = (idx) => {
256
320
  const categoryIdx = idx - range.min;
257
321
  let point = srcPoints[idx];
@@ -594,6 +658,7 @@ class CategoricalPlotArea extends PlotAreaBase {
594
658
  let categoryAxis;
595
659
 
596
660
  if (isDateAxis(axisOptions, categories[0])) {
661
+ axisOptions._forecast = this.trendlineAggregateForecast();
597
662
  categoryAxis = new DateCategoryAxis(axisOptions, this.chartService);
598
663
  } else {
599
664
  categoryAxis = new CategoryAxis(axisOptions, this.chartService);
@@ -769,4 +834,4 @@ setDefaultOptions(CategoricalPlotArea, {
769
834
 
770
835
  deepExtend(CategoricalPlotArea.prototype, PlotAreaEventsMixin);
771
836
 
772
- export default CategoricalPlotArea;
837
+ export default CategoricalPlotArea;
@@ -4,9 +4,11 @@ import { ChartElement, Box } from '../../core';
4
4
  import Crosshair from '../crosshair/crosshair';
5
5
  import Pane from '../pane';
6
6
  import { hasValue } from '../utils';
7
+ import SeriesBinder from '../series-binder';
7
8
 
8
- import { WHITE, BLACK, X, Y, COORD_PRECISION, TOP, BOTTOM, LEFT, RIGHT, START, END } from '../../common/constants';
9
- import { append, deepExtend, defined, getSpacing, getTemplate, inArray, isFunction, isString, limitValue, round, setDefaultOptions } from '../../common';
9
+ import { WHITE, BLACK, X, Y, COORD_PRECISION, TOP, BOTTOM, LEFT, RIGHT, START, END, INHERIT } from '../../common/constants';
10
+ import { append, deepExtend, defined, getSpacing, getTemplate, inArray, isFunction, isString, limitValue, round, setDefaultOptions, last } from '../../common';
11
+ import { TRENDLINE_SERIES } from '../constants';
10
12
 
11
13
  class PlotAreaBase extends ChartElement {
12
14
  constructor(series, options, chartService) {
@@ -22,6 +24,8 @@ class PlotAreaBase extends ChartElement {
22
24
  this.crosshairs = [];
23
25
  this.chartService = chartService;
24
26
  this.originalOptions = options;
27
+ this.originalSeries = series;
28
+ this._bindCache = new WeakMap();
25
29
 
26
30
  this.createPanes();
27
31
  this.render();
@@ -38,6 +42,21 @@ class PlotAreaBase extends ChartElement {
38
42
  }
39
43
  }
40
44
 
45
+ bindPoint(series, pointIx, item) {
46
+ let cached = this._bindCache.get(series);
47
+ if (!cached) {
48
+ cached = [];
49
+ this._bindCache.set(series, cached);
50
+ }
51
+
52
+ let data = cached[pointIx];
53
+ if (!data) {
54
+ data = cached[pointIx] = SeriesBinder.current.bindPoint(series, pointIx, item);
55
+ }
56
+
57
+ return data;
58
+ }
59
+
41
60
  createPanes() {
42
61
  const titleOptions = this.options.title || {};
43
62
  const paneDefaults = this.options.paneDefaults;
@@ -317,6 +336,8 @@ class PlotAreaBase extends ChartElement {
317
336
  panesArray[i].empty();
318
337
  }
319
338
 
339
+ this._bindCache = new WeakMap();
340
+
320
341
  this.render(panesArray);
321
342
  this.detachLabels();
322
343
  this.reflowAxes(this.panes);
@@ -937,6 +958,52 @@ class PlotAreaBase extends ChartElement {
937
958
 
938
959
  return labelAxis;
939
960
  }
961
+
962
+ isTrendline(series) {
963
+ return series && inArray(series.type, TRENDLINE_SERIES);
964
+ }
965
+
966
+ trendlineFactory() { /* abstract */ }
967
+
968
+ createTrendlineSeries() {
969
+ const modifiedSeries = [];
970
+
971
+ this.series = this.series.map(series => {
972
+ if (!this.isTrendline(series)) {
973
+ return series;
974
+ }
975
+
976
+ const forSeries = this.seriesByName(series.for);
977
+ if (!forSeries) {
978
+ throw new Error('Invalid Configuration: Unable to locate linked series ' +
979
+ `"${series.for}" for trendline "${series.name}".`);
980
+ }
981
+
982
+ const valueFields = SeriesBinder.current.valueFields(forSeries);
983
+ const field = last(valueFields); // Use the last field for multi-field series
984
+
985
+ const trendlineSeries = this.trendlineFactory(Object.assign({}, {field}, series), forSeries);
986
+ if (trendlineSeries) {
987
+ if (forSeries.visible === false) {
988
+ trendlineSeries.visible = false;
989
+ }
990
+
991
+ if (trendlineSeries.color === INHERIT) {
992
+ trendlineSeries.color = forSeries.color;
993
+ }
994
+
995
+ modifiedSeries.push(trendlineSeries);
996
+ }
997
+
998
+ return trendlineSeries;
999
+ }).filter(series => series !== null);
1000
+
1001
+ return modifiedSeries;
1002
+ }
1003
+
1004
+ seriesByName(name) {
1005
+ return this.series.find(series => series.name === name);
1006
+ }
940
1007
  }
941
1008
 
942
1009
  function isSingleAxis(axis) {
@@ -1,5 +1,6 @@
1
1
  import filterSeriesByType from '../utils/filter-series-by-type';
2
2
  import { Class } from '../../common';
3
+ import { TRENDLINE_SERIES } from '../constants';
3
4
 
4
5
  class PlotAreaFactory extends Class {
5
6
 
@@ -24,8 +25,9 @@ class PlotAreaFactory extends Class {
24
25
  for (let idx = 0; idx < registry.length; idx++) {
25
26
  const entry = registry[idx];
26
27
  series = filterSeriesByType(srcSeries, entry.seriesTypes);
28
+ const trendlines = filterSeriesByType(srcSeries, TRENDLINE_SERIES);
27
29
 
28
- if (series.length > 0) {
30
+ if ((series.length - trendlines.length) > 0) {
29
31
  match = entry;
30
32
  break;
31
33
  }
@@ -37,4 +39,4 @@ class PlotAreaFactory extends Class {
37
39
 
38
40
  PlotAreaFactory.current = new PlotAreaFactory();
39
41
 
40
- export default PlotAreaFactory;
42
+ export default PlotAreaFactory;
@@ -13,6 +13,7 @@ import filterSeriesByType from '../utils/filter-series-by-type';
13
13
 
14
14
  import { ARC } from '../../common/constants';
15
15
  import { deepExtend, eventElement, setDefaultOptions } from '../../common';
16
+ import XYPlotArea from './xy-plotarea';
16
17
 
17
18
  class PolarPlotArea extends PolarPlotAreaBase {
18
19
  createPolarAxis() {
@@ -23,6 +24,13 @@ class PolarPlotArea extends PolarPlotAreaBase {
23
24
  this.appendAxis(polarAxis);
24
25
  }
25
26
 
27
+ render() {
28
+ this.series = [...this.originalSeries];
29
+ this.createTrendlineSeries();
30
+
31
+ super.render();
32
+ }
33
+
26
34
  valueAxisOptions(defaults) {
27
35
  return deepExtend(defaults, {
28
36
  majorGridLines: { type: ARC },
@@ -35,6 +43,15 @@ class PolarPlotArea extends PolarPlotAreaBase {
35
43
  this.axisY = this.valueAxis;
36
44
  }
37
45
 
46
+ trendlineFactory(options, series) {
47
+ const trendline = XYPlotArea.prototype.trendlineFactory.call(this, options, series);
48
+ if (trendline) {
49
+ trendline.type = POLAR_LINE;
50
+ }
51
+
52
+ return trendline;
53
+ }
54
+
38
55
  appendChart(chart, pane) {
39
56
  this.valueAxisRangeTracker.update(chart.yAxisRanges);
40
57
 
@@ -114,6 +131,8 @@ setDefaultOptions(PolarPlotArea, {
114
131
  yAxis: {}
115
132
  });
116
133
 
117
- deepExtend(PolarPlotArea.prototype, PlotAreaEventsMixin);
134
+ deepExtend(PolarPlotArea.prototype, PlotAreaEventsMixin, {
135
+ seriesValues: XYPlotArea.prototype.seriesValues
136
+ });
118
137
 
119
- export default PolarPlotArea;
138
+ export default PolarPlotArea;
@@ -22,6 +22,7 @@ class RadarPlotArea extends PolarPlotAreaBase {
22
22
  this.categoryAxis = categoryAxis;
23
23
  this.appendAxis(categoryAxis);
24
24
  this.aggregateCategories();
25
+ this.createTrendlineSeries();
25
26
  this.createCategoryAxesLabels();
26
27
  }
27
28
 
@@ -57,6 +58,15 @@ class RadarPlotArea extends PolarPlotAreaBase {
57
58
  return currentSeries;
58
59
  }
59
60
 
61
+ trendlineFactory(options, series) {
62
+ const trendline = CategoricalPlotArea.prototype.trendlineFactory.call(this, options, series);
63
+ if (trendline) {
64
+ trendline.type = RADAR_LINE;
65
+ }
66
+
67
+ return trendline;
68
+ }
69
+
60
70
  createCharts() {
61
71
  const series = this.filterVisibleSeries(this.series);
62
72
  const pane = this.panes[0];
@@ -153,7 +163,8 @@ class RadarPlotArea extends PolarPlotAreaBase {
153
163
  deepExtend(RadarPlotArea.prototype, PlotAreaEventsMixin, {
154
164
  appendChart: CategoricalPlotArea.prototype.appendChart,
155
165
  aggregateSeries: CategoricalPlotArea.prototype.aggregateSeries,
156
- seriesSourcePoints: CategoricalPlotArea.prototype.seriesSourcePoints
166
+ seriesSourcePoints: CategoricalPlotArea.prototype.seriesSourcePoints,
167
+ seriesValues: CategoricalPlotArea.prototype.seriesValues
157
168
  });
158
169
 
159
170
  setDefaultOptions(RadarPlotArea, {
@@ -163,4 +174,4 @@ setDefaultOptions(RadarPlotArea, {
163
174
  valueAxis: {}
164
175
  });
165
176
 
166
- export default RadarPlotArea;
177
+ export default RadarPlotArea;
@@ -5,12 +5,12 @@ import ScatterChart from '../scatter-charts/scatter-chart';
5
5
  import ScatterLineChart from '../scatter-charts/scatter-line-chart';
6
6
  import BubbleChart from '../bubble-chart/bubble-chart';
7
7
  import SeriesBinder from '../series-binder';
8
+ import trendlineFactory from '../trendlines/trendline-factory';
9
+ import scatterTrendlineRegistry from '../trendlines/scatter-trendline-registry';
8
10
 
9
11
  import { NumericAxis, LogarithmicAxis, DateValueAxis, Point } from '../../core';
10
12
 
11
- import filterSeriesByType from '../utils/filter-series-by-type';
12
- import equalsIgnoreCase from '../utils/equals-ignore-case';
13
- import singleItemOrArray from '../utils/single-item-or-array';
13
+ import { filterSeriesByType, equalsIgnoreCase, singleItemOrArray } from '../utils';
14
14
 
15
15
  import { SCATTER, SCATTER_LINE, BUBBLE, LOGARITHMIC } from '../constants';
16
16
 
@@ -27,8 +27,10 @@ class XYPlotArea extends PlotAreaBase {
27
27
  }
28
28
 
29
29
  render(panes = this.panes) {
30
- const seriesByPane = this.groupSeriesByPane();
30
+ this.series = [...this.originalSeries];
31
+ this.createTrendlineSeries();
31
32
 
33
+ const seriesByPane = this.groupSeriesByPane();
32
34
  for (let i = 0; i < panes.length; i++) {
33
35
  const pane = panes[i];
34
36
  const paneSeries = seriesByPane[pane.options.name || "default"] || [];
@@ -251,6 +253,35 @@ class XYPlotArea extends PlotAreaBase {
251
253
  updateAxisOptions(this.options, index, vertical, options);
252
254
  updateAxisOptions(this.originalOptions, index, vertical, options);
253
255
  }
256
+
257
+ trendlineFactory(options, series) {
258
+ const seriesValues = this.seriesValues.bind(this, series.index);
259
+
260
+ const trendline = trendlineFactory(scatterTrendlineRegistry, options.type, {
261
+ options,
262
+ seriesValues
263
+ });
264
+
265
+ if (trendline) {
266
+ // Inherit settings
267
+ trendline.xAxis = series.xAxis;
268
+ trendline.yAxis = series.yAxis;
269
+ }
270
+
271
+ return trendline;
272
+ }
273
+
274
+ seriesValues(seriesIx) {
275
+ const result = [];
276
+ const currentSeries = this.series[seriesIx];
277
+
278
+ for (let pointIx = 0; pointIx < currentSeries.data.length; pointIx++) {
279
+ const data = this.bindPoint(currentSeries, pointIx);
280
+ result.push({ pointIx, valueFields: data.valueFields });
281
+ }
282
+
283
+ return result;
284
+ }
254
285
  }
255
286
 
256
287
  function updateAxisOptions(targetOptions, axisIndex, vertical, options) {
@@ -265,4 +296,4 @@ setDefaultOptions(XYPlotArea, {
265
296
 
266
297
  deepExtend(XYPlotArea.prototype, PlotAreaEventsMixin);
267
298
 
268
- export default XYPlotArea;
299
+ export default XYPlotArea;
@@ -15,7 +15,7 @@ import { COLUMN, DONUT, PIE, FUNNEL, PYRAMID, BAR, LINE, VERTICAL_LINE, AREA, VE
15
15
  RANGE_BAR, WATERFALL, HORIZONTAL_WATERFALL, SCATTER, SCATTER_LINE, BUBBLE,
16
16
  POLAR_AREA, POLAR_LINE, POLAR_SCATTER, RADAR_AREA, RADAR_COLUMN, RADAR_LINE, CATEGORY,
17
17
  RANGE_AREA, VERTICAL_RANGE_AREA, X_ERROR_LOW_FIELD, X_ERROR_HIGH_FIELD, Y_ERROR_LOW_FIELD, Y_ERROR_HIGH_FIELD,
18
- ERROR_LOW_FIELD, ERROR_HIGH_FIELD, HEATMAP, DRILLDOWN_FIELD } from './constants';
18
+ ERROR_LOW_FIELD, ERROR_HIGH_FIELD, HEATMAP, DRILLDOWN_FIELD, TRENDLINE_SERIES } from './constants';
19
19
  import { X, Y, VALUE } from '../common/constants';
20
20
 
21
21
  const COLOR = "color";
@@ -30,19 +30,28 @@ const TO = "to";
30
30
  PlotAreaFactory.current.register(CategoricalPlotArea, [
31
31
  BAR, COLUMN, LINE, VERTICAL_LINE, AREA, VERTICAL_AREA,
32
32
  CANDLESTICK, OHLC, BULLET, VERTICAL_BULLET, BOX_PLOT, VERTICAL_BOX_PLOT,
33
- RANGE_COLUMN, RANGE_BAR, WATERFALL, HORIZONTAL_WATERFALL, RANGE_AREA, VERTICAL_RANGE_AREA
33
+ RANGE_COLUMN, RANGE_BAR, WATERFALL, HORIZONTAL_WATERFALL, RANGE_AREA, VERTICAL_RANGE_AREA,
34
+ ...TRENDLINE_SERIES
34
35
  ]);
35
36
 
36
37
  PlotAreaFactory.current.register(XYPlotArea, [
37
- SCATTER, SCATTER_LINE, BUBBLE
38
+ SCATTER, SCATTER_LINE, BUBBLE,
39
+ ...TRENDLINE_SERIES
38
40
  ]);
39
41
 
40
42
  PlotAreaFactory.current.register(PiePlotArea, [ PIE ]);
41
43
  PlotAreaFactory.current.register(DonutPlotArea, [ DONUT ]);
42
44
  PlotAreaFactory.current.register(FunnelPlotArea, [ FUNNEL, PYRAMID ]);
43
45
 
44
- PlotAreaFactory.current.register(PolarPlotArea, [ POLAR_AREA, POLAR_LINE, POLAR_SCATTER ]);
45
- PlotAreaFactory.current.register(RadarPlotArea, [ RADAR_AREA, RADAR_COLUMN, RADAR_LINE ]);
46
+ PlotAreaFactory.current.register(PolarPlotArea, [
47
+ POLAR_AREA, POLAR_LINE, POLAR_SCATTER,
48
+ ...TRENDLINE_SERIES
49
+ ]);
50
+
51
+ PlotAreaFactory.current.register(RadarPlotArea, [
52
+ RADAR_AREA, RADAR_COLUMN, RADAR_LINE,
53
+ ...TRENDLINE_SERIES
54
+ ]);
46
55
 
47
56
  PlotAreaFactory.current.register(HeatmapPlotArea, [ HEATMAP ]);
48
57