@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
@@ -4,10 +4,8 @@ import ClipAnimationMixin from '../mixins/clip-animation-mixin';
4
4
  import ErrorRangeCalculator from '../error-bars/error-range-calculator';
5
5
  import ScatterErrorBar from '../error-bars/scatter-error-bar';
6
6
  import LinePoint from '../line-chart/line-point';
7
- import CategoricalChart from '../categorical-chart';
8
7
 
9
- import hasValue from '../utils/has-value';
10
- import evalOptions from '../utils/eval-options';
8
+ import { hasValue, evalOptions } from '../utils';
11
9
 
12
10
  import { deepExtend, isNumber, isString, defined, isFunction, setDefaultOptions } from '../../common';
13
11
  import { X, Y, MIN_VALUE, MAX_VALUE } from '../../common/constants';
@@ -288,7 +286,7 @@ class ScatterChart extends ChartElement {
288
286
  }
289
287
 
290
288
  for (let pointIx = 0; pointIx < currentSeries.data.length; pointIx++) {
291
- const { valueFields: value, fields } = this._bindPoint(currentSeries, seriesIx, pointIx);
289
+ const { valueFields: value, fields } = this.plotArea.bindPoint(currentSeries, pointIx);
292
290
 
293
291
  callback(value, deepExtend({
294
292
  pointIx: pointIx,
@@ -325,8 +323,6 @@ setDefaultOptions(ScatterChart, {
325
323
  },
326
324
  clip: true
327
325
  });
328
- deepExtend(ScatterChart.prototype, ClipAnimationMixin, {
329
- _bindPoint: CategoricalChart.prototype._bindPoint
330
- });
326
+ deepExtend(ScatterChart.prototype, ClipAnimationMixin);
331
327
 
332
328
  export default ScatterChart;
@@ -0,0 +1,42 @@
1
+ import { MIN_MOVING_AVERAGE_PERIOD } from '../constants';
2
+
3
+ function calculateMovingAverage(sourceValues, valueGetter, period) {
4
+ const averagePoints = [];
5
+ const values = [];
6
+ const start = Math.max(MIN_MOVING_AVERAGE_PERIOD, period) - 1;
7
+
8
+ let end = 0;
9
+ let sum = 0;
10
+
11
+ for (let i = 0; i < sourceValues.length; i++) {
12
+ const value = sourceValues[i];
13
+ const { xValue, yValue } = valueGetter(value);
14
+
15
+ if (isFinite(yValue) && yValue !== null) {
16
+ values.push(yValue);
17
+ sum += yValue;
18
+ end = Math.max(i, end);
19
+ } else {
20
+ values.push(null);
21
+ }
22
+
23
+ if (i >= start) {
24
+ const count = values.filter(value => value !== null).length;
25
+ const lastValue = values.shift() || 0;
26
+
27
+ if (count > 0) {
28
+ const average = sum / count;
29
+ averagePoints.push([xValue, average]);
30
+
31
+ sum -= lastValue;
32
+ continue;
33
+ }
34
+ }
35
+
36
+ averagePoints.push([xValue, null]);
37
+ }
38
+
39
+ return averagePoints.slice(0, end + 1);
40
+ }
41
+
42
+ export default calculateMovingAverage;
@@ -0,0 +1,35 @@
1
+ function calculateSlope(sourceValues, valueGetter) {
2
+ let x = 0;
3
+ let y = 0;
4
+ let x2 = 0;
5
+ let xy = 0;
6
+ let count = 0;
7
+ let slope, intercept;
8
+ let xMin = Number.MAX_VALUE;
9
+ let xMax = Number.MIN_VALUE;
10
+
11
+ for (let i = 0; i < sourceValues.length; i++) {
12
+ const value = sourceValues[i];
13
+ const { xValue, yValue } = valueGetter(value);
14
+
15
+ if (isFinite(xValue) && xValue !== null && isFinite(yValue) && yValue !== null) {
16
+ xMin = Math.min(xValue, xMin);
17
+ xMax = Math.max(xValue, xMax);
18
+
19
+ count++;
20
+ x += xValue;
21
+ y += yValue;
22
+ x2 += Math.pow(xValue, 2);
23
+ xy += xValue * yValue;
24
+ }
25
+ }
26
+
27
+ if (count > 0) {
28
+ slope = (count * xy - x * y) / (count * x2 - Math.pow(x, 2));
29
+ intercept = (y - slope * x) / count;
30
+ }
31
+
32
+ return { slope, intercept, count, xMin, xMax };
33
+ }
34
+
35
+ export default calculateSlope;
@@ -0,0 +1,51 @@
1
+ import calculateSlope from './calculate-slope';
2
+
3
+ function linearTrendline(context) {
4
+ const {
5
+ options,
6
+ categoryAxis,
7
+ seriesValues
8
+ } = context;
9
+
10
+ const { data } = getData({ seriesValues, categoryAxis, options });
11
+ if (data) {
12
+ return Object.assign({}, options,
13
+
14
+ {type: 'line',
15
+ data,
16
+ categoryField: 'category',
17
+ field: 'value'});
18
+ }
19
+
20
+ return null;
21
+ }
22
+
23
+ const valueGetter = fieldName => ({ categoryIx, valueFields }) =>
24
+ ({ xValue: categoryIx + 1, yValue: valueFields[fieldName] });
25
+
26
+ function getData({ seriesValues, categoryAxis, options }) {
27
+ const { slope, intercept, count } = calculateSlope(seriesValues(), valueGetter(options.field));
28
+
29
+ if (count > 0) {
30
+ const data = [];
31
+ const totalRange = categoryAxis.totalRangeIndices();
32
+ const currentRange = categoryAxis.currentRangeIndices();
33
+ const range = {
34
+ min: Math.floor(Math.max(currentRange.min - 1, totalRange.min)),
35
+ max: Math.ceil(Math.min(currentRange.max + 2, totalRange.max))
36
+ };
37
+
38
+ for (let i = range.min; i < range.max; i++) {
39
+ data[i] = {
40
+ category: categoryAxis.categoryAt(i, true),
41
+ value: slope * (i + 1) + intercept
42
+ };
43
+ }
44
+
45
+ return { data };
46
+ }
47
+
48
+ return { data: null };
49
+ }
50
+
51
+ export default linearTrendline;
@@ -0,0 +1,53 @@
1
+ import { MIN_MOVING_AVERAGE_PERIOD } from '../constants';
2
+ import calculateMovingAverage from './calculate-moving-average';
3
+
4
+ function movingAverageTrendline(context) {
5
+ const { options } = context;
6
+
7
+ const { data } = getData(context);
8
+ if (data) {
9
+ return Object.assign({}, options,
10
+
11
+ {type: 'line',
12
+ data,
13
+ categoryField: 'category',
14
+ field: 'value'});
15
+ }
16
+
17
+ return null;
18
+ }
19
+
20
+ const valueGetter = fieldName => ({ categoryIx, valueFields }) =>
21
+ ({ xValue: categoryIx, yValue: valueFields[fieldName] });
22
+
23
+ function calculatePoints({ options, categoryAxis, seriesValues }) {
24
+ const period = (options.trendline || {}).period || MIN_MOVING_AVERAGE_PERIOD;
25
+ const totalRange = categoryAxis.totalRangeIndices();
26
+ const currentRange = categoryAxis.currentRangeIndices();
27
+ const range = {
28
+ min: Math.floor(Math.max(currentRange.min - period, totalRange.min)),
29
+ max: Math.ceil(Math.min(currentRange.max + period + 2, totalRange.max))
30
+ };
31
+
32
+ return calculateMovingAverage(seriesValues(range), valueGetter(options.field), period);
33
+ }
34
+
35
+ function getData(context) {
36
+ const { categoryAxis } = context;
37
+ const points = calculatePoints(context);
38
+ const data = [];
39
+ points.forEach(([categoryIx, value]) => {
40
+ data[categoryIx] = {
41
+ category: categoryAxis.categoryAt(categoryIx, true),
42
+ value
43
+ };
44
+ });
45
+
46
+ if (data.length > 0) {
47
+ return { data };
48
+ }
49
+
50
+ return { data: null };
51
+ }
52
+
53
+ export default movingAverageTrendline;
@@ -0,0 +1,57 @@
1
+ import autoMajorUnit from '../../core/utils/auto-major-unit';
2
+ import { valueOrDefault } from '../../common';
3
+ import calculateSlope from './calculate-slope';
4
+ import scatterValueGetter from './scatter-value-getter';
5
+
6
+ function scatterLinearTrendLine(context) {
7
+ const {
8
+ options,
9
+ seriesValues
10
+ } = context;
11
+
12
+ const data = getData({ seriesValues, options });
13
+ if (data) {
14
+ return Object.assign({}, options,
15
+
16
+ {type: 'scatterLine',
17
+ data});
18
+ }
19
+
20
+ return null;
21
+ }
22
+
23
+ function getData({ seriesValues, options }) {
24
+ let { slope, intercept, count, xMin, xMax } = calculateSlope(seriesValues(), scatterValueGetter(options.field));
25
+
26
+ if (count > 0) {
27
+ const data = [];
28
+ const forecast = (options.trendline || {}).forecast;
29
+ if (forecast) {
30
+ if (forecast.before > 0) {
31
+ xMin -= forecast.before;
32
+ }
33
+
34
+ if (forecast.after > 0) {
35
+ xMax += forecast.after;
36
+ }
37
+ }
38
+
39
+ const samplingInterval = (options.trendline || {}).samplingInterval;
40
+ let delta = valueOrDefault(samplingInterval, autoMajorUnit(xMin, xMax) / 10);
41
+ if (samplingInterval <= 0) {
42
+ delta = xMax - xMin;
43
+ }
44
+
45
+ for (let x = xMin; x <= xMax; x += delta) {
46
+ data.push([ x,
47
+ slope * x + intercept
48
+ ]);
49
+ }
50
+
51
+ return data;
52
+ }
53
+
54
+ return null;
55
+ }
56
+
57
+ export default scatterLinearTrendLine;
@@ -0,0 +1,31 @@
1
+ import calculateMovingAverage from './calculate-moving-average';
2
+ import { MIN_MOVING_AVERAGE_PERIOD } from '../constants';
3
+ import scatterValueGetter from './scatter-value-getter';
4
+
5
+ function scatterMovingAverageTrendline(context) {
6
+ const { options } = context;
7
+
8
+ const data = getData(context);
9
+ if (data) {
10
+ return Object.assign({}, options,
11
+
12
+ {type: 'scatterLine',
13
+ data});
14
+ }
15
+
16
+ return null;
17
+ }
18
+
19
+ function getData({ options, seriesValues }) {
20
+ const period = (options.trendline || {}).period || MIN_MOVING_AVERAGE_PERIOD;
21
+ const range = { before: period, after: period };
22
+ const data = calculateMovingAverage(seriesValues(range), scatterValueGetter(options.field), period);
23
+
24
+ if (data.length > 0) {
25
+ return data;
26
+ }
27
+
28
+ return null;
29
+ }
30
+
31
+ export default scatterMovingAverageTrendline;
@@ -0,0 +1,9 @@
1
+ import scatterLinearTrendLine from './scatter-linear-trendline';
2
+ import scatterMovingAverageTrendline from './scatter-moving-average';
3
+ import { TRENDLINE_LINEAR, TRENDLINE_MOVING_AVERAGE } from '../constants';
4
+
5
+ const scatterRegistry = {};
6
+ scatterRegistry[TRENDLINE_LINEAR] = scatterLinearTrendLine;
7
+ scatterRegistry[TRENDLINE_MOVING_AVERAGE] = scatterMovingAverageTrendline;
8
+
9
+ export default scatterRegistry;
@@ -0,0 +1,4 @@
1
+ const scatterValueGetter = fieldName => ({ valueFields }) =>
2
+ ({ xValue: valueFields.x, yValue: valueFields[fieldName] });
3
+
4
+ export default scatterValueGetter;
@@ -0,0 +1,10 @@
1
+ function trendlineFactory(registry, type, context) {
2
+ const impl = registry[String(type)];
3
+ if (impl) {
4
+ return impl(context);
5
+ }
6
+
7
+ return null;
8
+ }
9
+
10
+ export default trendlineFactory;
@@ -0,0 +1,9 @@
1
+ import linearTrendline from './linear-trendline';
2
+ import movingAverageTrendline from './moving-average';
3
+ import { TRENDLINE_LINEAR, TRENDLINE_MOVING_AVERAGE } from '../constants';
4
+
5
+ const registry = {};
6
+ registry[TRENDLINE_LINEAR] = linearTrendline;
7
+ registry[TRENDLINE_MOVING_AVERAGE] = movingAverageTrendline;
8
+
9
+ export default registry;
@@ -15,5 +15,3 @@ export { default as isDateAxis } from './utils/is-date-axis';
15
15
  export { default as segmentVisible } from './utils/segment-visible';
16
16
  export { default as singleItemOrArray } from './utils/single-item-or-array';
17
17
  export { default as createOutOfRangePoints } from './utils/create-out-of-range-points';
18
-
19
-
@@ -16,6 +16,7 @@ export const FORMAT_REGEX = /\{\d+:?/;
16
16
  export const HEIGHT = "height";
17
17
  export const HIGHLIGHT_ZINDEX = 100;
18
18
  export const INSIDE = "inside";
19
+ export const INHERIT = "inherit";
19
20
  export const LEFT = "left";
20
21
  export const MAX_VALUE = Number.MAX_VALUE;
21
22
  export const MIN_VALUE = -Number.MAX_VALUE;
@@ -1,18 +1,14 @@
1
-
1
+ // TODO: Remove and replace with Map/WeakMap.
2
2
  export default class HashMap {
3
3
  constructor() {
4
- this._map = {};
5
- }
6
-
7
- get(name) {
8
- return this._map[this._key(name)];
4
+ this._map = new Map();
9
5
  }
10
6
 
11
- set(name, value) {
12
- this._map[this._key(name)] = value;
7
+ get(key) {
8
+ return this._map.get(key);
13
9
  }
14
10
 
15
- _key(name) {
16
- return name instanceof Date ? name.getTime() : name;
11
+ set(key, value) {
12
+ this._map.set(key, value);
17
13
  }
18
- }
14
+ }
@@ -81,31 +81,6 @@ class CategoryAxis extends Axis {
81
81
  };
82
82
  }
83
83
 
84
- totalRangeIndices(limit) {
85
- const options = this.options;
86
- let min = isNumber(options.min) ? options.min : 0;
87
- let max;
88
-
89
- if (isNumber(options.max)) {
90
- max = options.max;
91
- } else if (isNumber(options.min)) {
92
- max = min + options.categories.length;
93
- } else {
94
- max = this.totalRange().max || 1;
95
- }
96
-
97
- if (limit) {
98
- const totalRange = this.totalRange();
99
- min = limitValue(min, 0, totalRange.max);
100
- max = limitValue(max, 0, totalRange.max);
101
- }
102
-
103
- return {
104
- min: min,
105
- max: max
106
- };
107
- }
108
-
109
84
  range() {
110
85
  const options = this.options;
111
86
  const min = isNumber(options.min) ? options.min : 0;
@@ -409,7 +384,7 @@ class CategoryAxis extends Axis {
409
384
 
410
385
  scaleRange(scale, cursor) {
411
386
  const position = Math.abs(this.pointOffset(cursor));
412
- const rangeIndices = this.totalRangeIndices();
387
+ const rangeIndices = this.limitedRangeIndices();
413
388
  const range = rangeIndices.max - rangeIndices.min;
414
389
  const delta = this.scaleToDelta(scale, range);
415
390
  const minDelta = position * delta;
@@ -446,7 +421,7 @@ class CategoryAxis extends Axis {
446
421
  labelsRange() {
447
422
  const options = this.options;
448
423
  const { justified, labels: labelOptions } = options;
449
- let { min, max } = this.totalRangeIndices(true);
424
+ let { min, max } = this.limitedRangeIndices(true);
450
425
  const start = Math.floor(min);
451
426
 
452
427
  if (!justified) {
@@ -483,7 +458,7 @@ class CategoryAxis extends Axis {
483
458
  }
484
459
 
485
460
  shouldRenderNote(value) {
486
- const range = this.totalRangeIndices();
461
+ const range = this.limitedRangeIndices();
487
462
 
488
463
  return Math.floor(range.min) <= value && value <= Math.ceil(range.max);
489
464
  }
@@ -516,7 +491,7 @@ class CategoryAxis extends Axis {
516
491
  }
517
492
 
518
493
  pan(delta) {
519
- const range = this.totalRangeIndices(true);
494
+ const range = this.limitedRangeIndices(true);
520
495
  const { scale } = this.scaleOptions();
521
496
  const offset = round(delta / scale, DEFAULT_PRECISION);
522
497
  const totalRange = this.totalRange();
@@ -529,7 +504,7 @@ class CategoryAxis extends Axis {
529
504
  pointsRange(start, end) {
530
505
  const { reverse, vertical } = this.options;
531
506
  const valueAxis = vertical ? Y : X;
532
- const range = this.totalRangeIndices(true);
507
+ const range = this.limitedRangeIndices(true);
533
508
  const { scale, box } = this.scaleOptions();
534
509
  const lineStart = box[valueAxis + (reverse ? 2 : 1)];
535
510
 
@@ -582,6 +557,38 @@ class CategoryAxis extends Axis {
582
557
  };
583
558
  }
584
559
 
560
+ limitedRangeIndices(totalLimit) {
561
+ const options = this.options;
562
+ let min = isNumber(options.min) ? options.min : 0;
563
+ let max;
564
+
565
+ if (isNumber(options.max)) {
566
+ max = options.max;
567
+ } else if (isNumber(options.min)) {
568
+ max = min + options.categories.length;
569
+ } else {
570
+ max = this.totalRange().max || 1;
571
+ }
572
+
573
+ if (totalLimit) {
574
+ const totalRange = this.totalRange();
575
+ min = limitValue(min, 0, totalRange.max);
576
+ max = limitValue(max, 0, totalRange.max);
577
+ }
578
+
579
+ return {
580
+ min: min,
581
+ max: max
582
+ };
583
+ }
584
+
585
+ totalRangeIndices() {
586
+ return {
587
+ min: 0,
588
+ max: this.totalRange().max || 1
589
+ };
590
+ }
591
+
585
592
  mapCategories() {
586
593
  if (!this._categoriesMap) {
587
594
  const map = this._categoriesMap = new HashMap();
@@ -20,7 +20,11 @@ const BASE_UNITS = [
20
20
  const FIT = "fit";
21
21
 
22
22
 
23
- function categoryRange(categories) {
23
+ function categoryRange(categories, clearCache) {
24
+ if (clearCache) {
25
+ categories._range = undefined;
26
+ }
27
+
24
28
  let range = categories._range;
25
29
  if (!range) {
26
30
  range = categories._range = sparseArrayLimits(categories);
@@ -360,10 +364,22 @@ class DateCategoryAxis extends CategoryAxis {
360
364
  options.srcCategories = categories;
361
365
 
362
366
  if (categories.length > 0) {
363
- const range = categoryRange(categories);
367
+ const range = categoryRange(categories, true);
364
368
  const maxDivisions = options.maxDivisions;
369
+ const safeOptions = initUnit(options);
370
+
371
+ const forecast = options._forecast;
372
+ if (forecast) {
373
+ if (forecast.before > 0) {
374
+ range.min = addDuration(range.min, -forecast.before, safeOptions.baseUnit, safeOptions.weekStartDay);
375
+ }
376
+
377
+ if (forecast.after > 0) {
378
+ range.max = addDuration(range.max, forecast.after, safeOptions.baseUnit, safeOptions.weekStartDay);
379
+ }
380
+ }
365
381
 
366
- this.dataRange = new DateRange(range.min, range.max, initUnit(options));
382
+ this.dataRange = new DateRange(range.min, range.max, safeOptions);
367
383
 
368
384
  if (maxDivisions) {
369
385
  const dataRange = this.dataRange.displayRange();
@@ -876,6 +892,14 @@ class DateCategoryAxis extends CategoryAxis {
876
892
  return this.dataRange.total();
877
893
  }
878
894
 
895
+ totalRangeIndices() {
896
+ const range = this.dataRange.total();
897
+ return {
898
+ min: this.dataRange.totalIndex(range.min),
899
+ max: this.dataRange.totalIndex(range.max)
900
+ };
901
+ }
902
+
879
903
  totalCount() {
880
904
  return this.dataRange.totalCount();
881
905
  }