@vaadin/charts 24.7.0-alpha1 → 24.7.0-alpha10

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.
@@ -0,0 +1,1698 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2000 - 2025 Vaadin Ltd.
4
+ *
5
+ * This program is available under Vaadin Commercial License and Service Terms.
6
+ *
7
+ *
8
+ * See https://vaadin.com/commercial-license-and-service-terms for the full
9
+ * license.
10
+ */
11
+ import 'highcharts/es-modules/masters/highstock.src.js';
12
+ import 'highcharts/es-modules/masters/modules/accessibility.src.js';
13
+ import 'highcharts/es-modules/masters/modules/annotations.src.js';
14
+ import 'highcharts/es-modules/masters/highcharts-more.src.js';
15
+ import 'highcharts/es-modules/masters/highcharts-3d.src.js';
16
+ import 'highcharts/es-modules/masters/modules/data.src.js';
17
+ import 'highcharts/es-modules/masters/modules/drilldown.src.js';
18
+ import 'highcharts/es-modules/masters/modules/exporting.src.js';
19
+ import 'highcharts/es-modules/masters/modules/funnel.src.js';
20
+ import 'highcharts/es-modules/masters/modules/heatmap.src.js';
21
+ import 'highcharts/es-modules/masters/modules/solid-gauge.src.js';
22
+ import 'highcharts/es-modules/masters/modules/treemap.src.js';
23
+ import 'highcharts/es-modules/masters/modules/no-data-to-display.src.js';
24
+ import 'highcharts/es-modules/masters/modules/sankey.src.js';
25
+ import 'highcharts/es-modules/masters/modules/timeline.src.js';
26
+ import 'highcharts/es-modules/masters/modules/organization.src.js';
27
+ import 'highcharts/es-modules/masters/modules/xrange.src.js';
28
+ import 'highcharts/es-modules/masters/modules/bullet.src.js';
29
+ import 'highcharts/es-modules/masters/modules/gantt.src.js';
30
+ import 'highcharts/es-modules/masters/modules/draggable-points.src.js';
31
+ import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
32
+ import { beforeNextRender } from '@polymer/polymer/lib/utils/render-status.js';
33
+ import Pointer from 'highcharts/es-modules/Core/Pointer.js';
34
+ import Highcharts from 'highcharts/es-modules/masters/highstock.src.js';
35
+ import { get } from '@vaadin/component-base/src/path-utils.js';
36
+ import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
37
+ import { deepMerge, inflateFunctions } from './helpers.js';
38
+
39
+ ['exportChart', 'exportChartLocal', 'getSVG'].forEach((methodName) => {
40
+ /* eslint-disable @typescript-eslint/no-invalid-this, prefer-arrow-callback */
41
+ Highcharts.wrap(Highcharts.Chart.prototype, methodName, function (proceed, ...args) {
42
+ Highcharts.fireEvent(this, 'beforeExport');
43
+ const result = proceed.apply(this, args);
44
+ Highcharts.fireEvent(this, 'afterExport');
45
+ return result;
46
+ });
47
+ /* eslint-enable @typescript-eslint/no-invalid-this, prefer-arrow-callback */
48
+ });
49
+
50
+ // Monkeypatch the onDocumentMouseMove method to fix the check for the source of the event
51
+ // Due to the fact that the event is attached to the document, the target of the event is
52
+ // the <vaadin-chart> element, so we need to use the composedPath to get the actual target (#7107)
53
+ Pointer.prototype.onDocumentMouseMove = function (e) {
54
+ const chart = this.chart;
55
+ const chartPosition = this.chartPosition;
56
+ const pEvt = this.normalize(e, chartPosition);
57
+ const tooltip = chart.tooltip;
58
+ // If we're outside, hide the tooltip
59
+ if (
60
+ chartPosition &&
61
+ (!tooltip || !tooltip.isStickyOnContact()) &&
62
+ !chart.isInsidePlot(pEvt.chartX - chart.plotLeft, pEvt.chartY - chart.plotTop, {
63
+ visiblePlotOnly: true,
64
+ }) &&
65
+ // Use the first element from the composed path instead of the actual target
66
+ !this.inClass(pEvt.composedPath()[0], 'highcharts-tracker')
67
+ ) {
68
+ this.reset();
69
+ }
70
+ };
71
+
72
+ // Init Highcharts global language defaults
73
+ // No data message should be empty by default
74
+ Highcharts.setOptions({ lang: { noData: '' } });
75
+
76
+ /**
77
+ * @polymerMixin
78
+ * @mixes ResizeMixin
79
+ */
80
+ export const ChartMixin = (superClass) =>
81
+ class extends ResizeMixin(superClass) {
82
+ static get properties() {
83
+ return {
84
+ /**
85
+ * Configuration object that exposes the JS Api to configure the chart.
86
+ *
87
+ * Most important methods are:
88
+ * - `addSeries (Object options, [Boolean redraw], [Mixed animation])`
89
+ * - `addAxis (Object options, [Boolean isX], [Boolean redraw], [Mixed animation])`
90
+ * - `setTitle (Object title, object subtitle, Boolean redraw)`
91
+ *
92
+ * Most important properties are:
93
+ * - `configuration.series`: An array of the chart's series. Detailed API for Series object is
94
+ * available in [API Site](http://api.highcharts.com/class-reference/Highcharts.Series)
95
+ * - `configuration.xAxis`: An array of the chart's x axes. Detailed API for Axis object is
96
+ * available in [API Site](http://api.highcharts.com/class-reference/Highcharts.Axis)
97
+ * - `configuration.yAxis`: An array of the chart's y axes. Detailed API for Axis object is
98
+ * available in [API Site](http://api.highcharts.com/class-reference/Highcharts.Axis)
99
+ * - `configuration.title`: The chart title.
100
+ *
101
+ * For detailed documentation of available API check the [API site](http://api.highcharts.com/class-reference/classes.list)
102
+ * @type {!Highcharts.Chart | undefined}
103
+ */
104
+ configuration: {
105
+ type: Object,
106
+ sync: true,
107
+ },
108
+
109
+ /**
110
+ * If categories are present names are used instead of numbers for the category axis.
111
+ * The format of categories can be an `Array` with a list of categories, such as `['2010', '2011', '2012']`
112
+ * or a mapping `Object`, like `{0:'1',9:'Target (10)', 15: 'Max'}`.
113
+ * @type {ChartCategories | undefined}
114
+ */
115
+ categories: {
116
+ type: Object,
117
+ reflectToAttribute: true,
118
+ sync: true,
119
+ },
120
+
121
+ /**
122
+ * Category-axis maximum value. Defaults to `undefined`.
123
+ * @attr {number} category-max
124
+ */
125
+ categoryMax: {
126
+ type: Number,
127
+ reflectToAttribute: true,
128
+ sync: true,
129
+ },
130
+
131
+ /**
132
+ * Category-axis minimum value. Defaults to `undefined`.
133
+ * @attr {number} category-min
134
+ */
135
+ categoryMin: {
136
+ type: Number,
137
+ reflectToAttribute: true,
138
+ sync: true,
139
+ },
140
+
141
+ /**
142
+ * The position of the category axis. Acceptable values are `left`, `right`, `top` and `bottom`
143
+ * except for bar charts which only accept `left` and `right`.
144
+ * With the default value, charts appear as though they have `category-position="bottom"`
145
+ * except for bar charts that appear as though they have `category-position="left"`.
146
+ *
147
+ * Defaults to `undefined`
148
+ *
149
+ * @attr {left|right|top|bottom} category-position
150
+ * @type {ChartCategoryPosition | undefined}
151
+ */
152
+ categoryPosition: {
153
+ type: String,
154
+ reflectToAttribute: true,
155
+ sync: true,
156
+ },
157
+
158
+ /**
159
+ * Specifies whether to hide legend or show.
160
+ * Legend configuration can be set up via additionalOptions property
161
+ * @attr {boolean} no-legend
162
+ */
163
+ noLegend: {
164
+ type: Boolean,
165
+ reflectToAttribute: true,
166
+ sync: true,
167
+ },
168
+
169
+ /**
170
+ * Specifies how series are stacked on top of each other.
171
+ * Possible values are null, "normal" or "percent".
172
+ * If "stack" property is not defined on the vaadin-chart-series elements, then series will be put into
173
+ * the default stack.
174
+ * @attr {normal|percent} stacking
175
+ * @type {ChartStacking | undefined}
176
+ */
177
+ stacking: {
178
+ type: String,
179
+ reflectToAttribute: true,
180
+ sync: true,
181
+ },
182
+
183
+ /**
184
+ * Specifies whether the chart is a normal chart or a timeline chart.
185
+ * Value of this property is ignored for Gantt charts (type="gantt").
186
+ */
187
+ timeline: {
188
+ type: Boolean,
189
+ reflectToAttribute: true,
190
+ sync: true,
191
+ },
192
+
193
+ /**
194
+ * Represents the title of the chart.
195
+ * @type {string}
196
+ */
197
+ title: {
198
+ type: String,
199
+ reflectToAttribute: true,
200
+ sync: true,
201
+ },
202
+
203
+ /**
204
+ * Whether or not to show tooltip when hovering data points.
205
+ */
206
+ tooltip: {
207
+ type: Boolean,
208
+ reflectToAttribute: true,
209
+ sync: true,
210
+ },
211
+
212
+ /**
213
+ * Sets the default series type of the chart.
214
+ * Note that `'bar'`, `'gauge'` and `'solidgauge'` should be set as default series type.
215
+ */
216
+ type: {
217
+ type: String,
218
+ reflectToAttribute: true,
219
+ sync: true,
220
+ },
221
+
222
+ /**
223
+ * Represents the subtitle of the chart.
224
+ * @type {string | undefined}
225
+ */
226
+ subtitle: {
227
+ type: String,
228
+ reflectToAttribute: true,
229
+ sync: true,
230
+ },
231
+
232
+ /**
233
+ * Specifies whether to show chart in 3 or in 2 dimensions.
234
+ * Some display angles are added by default to the "chart.options3d" (`{alpha: 15, beta: 15, depth: 50}`).
235
+ * 3D display options can be modified via `additionalOptions`.
236
+ * The thickness of a Pie chart can be set on `additionalOptions` through `plotOptions.pie.depth`.
237
+ * 3D is supported by Bar, Column, Pie and Scatter3D charts.
238
+ * More info available at [Highcharts](https://www.highcharts.com/docs/chart-concepts/3d-charts).
239
+ */
240
+ chart3d: {
241
+ type: Boolean,
242
+ reflectToAttribute: true,
243
+ sync: true,
244
+ },
245
+
246
+ /**
247
+ * Specifies the message displayed on a chart without displayable data.
248
+ * @attr {string} empty-text
249
+ * @type {string}
250
+ */
251
+ emptyText: {
252
+ type: String,
253
+ reflectToAttribute: true,
254
+ sync: true,
255
+ },
256
+
257
+ /**
258
+ * Represents additional JSON configuration.
259
+ * @type {Options | undefined}
260
+ */
261
+ additionalOptions: {
262
+ type: Object,
263
+ reflectToAttribute: true,
264
+ sync: true,
265
+ },
266
+
267
+ /**
268
+ * When present, cartesian charts like line, spline, area and column are transformed
269
+ * into the polar coordinate system.
270
+ */
271
+ polar: {
272
+ type: Boolean,
273
+ reflectToAttribute: true,
274
+ sync: true,
275
+ },
276
+ };
277
+ }
278
+
279
+ static get observers() {
280
+ return [
281
+ '__chart3dObserver(chart3d, configuration)',
282
+ '__emptyTextObserver(emptyText, configuration)',
283
+ '__hideLegend(noLegend, configuration)',
284
+ '__polarObserver(polar, configuration)',
285
+ '__stackingObserver(stacking, configuration)',
286
+ '__tooltipObserver(tooltip, configuration)',
287
+ '__updateCategories(categories, configuration)',
288
+ '__updateCategoryMax(categoryMax, configuration)',
289
+ '__updateCategoryMin(categoryMin, configuration)',
290
+ '__updateCategoryPosition(categoryPosition, configuration)',
291
+ '__updateSubtitle(subtitle, configuration)',
292
+ '__updateTitle(title, configuration)',
293
+ '__updateType(type, configuration)',
294
+ '__updateAdditionalOptions(additionalOptions)',
295
+ ];
296
+ }
297
+
298
+ /** @private */
299
+ static __callHighchartsFunction(functionName, redrawCharts, ...args) {
300
+ const functionToCall = Highcharts[functionName];
301
+ if (functionToCall && typeof functionToCall === 'function') {
302
+ args.forEach((arg) => inflateFunctions(arg));
303
+ functionToCall.apply(this.configuration, args);
304
+ if (redrawCharts) {
305
+ Highcharts.charts.forEach((c) => {
306
+ // Ignore `undefined` values that are preserved in the array
307
+ // after their corresponding chart instances are destroyed.
308
+ // See https://github.com/vaadin/flow-components/issues/6607
309
+ if (c !== undefined) {
310
+ c.redraw();
311
+ }
312
+ });
313
+ }
314
+ }
315
+ }
316
+
317
+ constructor() {
318
+ super();
319
+
320
+ this._baseConfig = {
321
+ annotations: [],
322
+ chart: {
323
+ styledMode: true,
324
+ },
325
+ credits: {
326
+ enabled: false,
327
+ },
328
+ exporting: {
329
+ enabled: false,
330
+ },
331
+ title: {
332
+ text: null,
333
+ },
334
+ series: [],
335
+ xAxis: {},
336
+ yAxis: {
337
+ axisGenerated: true,
338
+ },
339
+ };
340
+
341
+ this._baseChart3d = {
342
+ enabled: true,
343
+ alpha: 15,
344
+ beta: 15,
345
+ depth: 50,
346
+ };
347
+ }
348
+
349
+ /**
350
+ * @return {!Options}
351
+ */
352
+ get options() {
353
+ const options = { ...this._baseConfig };
354
+ deepMerge(options, this.additionalOptions);
355
+
356
+ if (this.type) {
357
+ options.chart.type = this.type;
358
+ }
359
+
360
+ if (this.polar) {
361
+ options.chart.polar = true;
362
+ }
363
+
364
+ if (this.title) {
365
+ options.title = {
366
+ text: this.title,
367
+ };
368
+ }
369
+
370
+ if (!options.tooltip) {
371
+ // Workaround for highcharts#7398 to make updating tooltip works
372
+ options.tooltip = {};
373
+ if (!this.tooltip) {
374
+ options.tooltip.enabled = false;
375
+ }
376
+ }
377
+
378
+ if (this.subtitle) {
379
+ options.subtitle = {
380
+ text: this.subtitle,
381
+ };
382
+ }
383
+
384
+ if (this.categories) {
385
+ if (Array.isArray(options.xAxis)) {
386
+ // Set categories on first X axis
387
+ options.xAxis[0].categories = this.categories;
388
+ } else {
389
+ options.xAxis.categories = this.categories;
390
+ }
391
+ }
392
+
393
+ if (isFinite(this.categoryMin)) {
394
+ if (Array.isArray(options.xAxis)) {
395
+ // Set category-min on first X axis
396
+ options.xAxis[0].min = this.categoryMin;
397
+ } else {
398
+ options.xAxis.min = this.categoryMin;
399
+ }
400
+ }
401
+
402
+ if (isFinite(this.categoryMax)) {
403
+ if (Array.isArray(options.xAxis)) {
404
+ // Set category-max on first x axis
405
+ options.xAxis[0].max = this.categoryMax;
406
+ } else {
407
+ options.xAxis.max = this.categoryMax;
408
+ }
409
+ }
410
+
411
+ if (this.noLegend) {
412
+ options.legend = {
413
+ enabled: false,
414
+ };
415
+ }
416
+
417
+ if (this.emptyText) {
418
+ if (!options.lang) {
419
+ options.lang = {};
420
+ }
421
+ options.lang.noData = this.emptyText;
422
+ }
423
+
424
+ if (this.categoryPosition) {
425
+ options.chart.inverted = this.__shouldInvert();
426
+
427
+ if (Array.isArray(options.xAxis)) {
428
+ options.xAxis.forEach((e) => {
429
+ e.opposite = this.__shouldFlipOpposite();
430
+ });
431
+ } else if (options.xAxis) {
432
+ options.xAxis.opposite = this.__shouldFlipOpposite();
433
+ }
434
+ }
435
+
436
+ if (this.stacking) {
437
+ if (!options.plotOptions) {
438
+ options.plotOptions = {};
439
+ }
440
+ if (!options.plotOptions.series) {
441
+ options.plotOptions.series = {};
442
+ }
443
+ options.plotOptions.series.stacking = this.stacking;
444
+ }
445
+
446
+ if (this.chart3d) {
447
+ options.chart.options3d = { ...this._baseChart3d, ...options.chart.options3d };
448
+ }
449
+
450
+ return options;
451
+ }
452
+
453
+ /**
454
+ * Name of the chart events to add to the configuration and its corresponding event for the chart element
455
+ * @private
456
+ */
457
+ get __chartEventNames() {
458
+ return {
459
+ /**
460
+ * Fired when a new series is added.
461
+ * @event chart-add-series
462
+ * @param {Object} detail.originalEvent object with details about the event sent
463
+ * @param {Object} chart Chart object where the event was sent from
464
+ */
465
+ addSeries: 'chart-add-series',
466
+
467
+ /**
468
+ * Fired after a chart is exported.
469
+ * @event chart-after-export
470
+ * @param {Object} detail.originalEvent object with details about the event sent
471
+ * @param {Object} chart Chart object where the event was sent from
472
+ */
473
+ afterExport: 'chart-after-export',
474
+
475
+ /**
476
+ * Fired after a chart is printed.
477
+ * @event chart-after-print
478
+ * @param {Object} detail.originalEvent object with details about the event sent
479
+ * @param {Object} chart Chart object where the event was sent from
480
+ */
481
+ afterPrint: 'chart-after-print',
482
+
483
+ /**
484
+ * Fired before a chart is exported.
485
+ * @event chart-before-export
486
+ * @param {Object} detail.originalEvent object with details about the event sent
487
+ * @param {Object} chart Chart object where the event was sent from
488
+ */
489
+ beforeExport: 'chart-before-export',
490
+
491
+ /**
492
+ * Fired before a chart is printed.
493
+ * @event chart-before-print
494
+ * @param {Object} detail.originalEvent object with details about the event sent
495
+ * @param {Object} chart Chart object where the event was sent from
496
+ */
497
+ beforePrint: 'chart-before-print',
498
+
499
+ /**
500
+ * Fired when clicking on the plot background.
501
+ * @event chart-click
502
+ * @param {Object} detail.originalEvent object with details about the event sent
503
+ * @param {Object} chart Chart object where the event was sent from
504
+ */
505
+ click: 'chart-click',
506
+
507
+ /**
508
+ * Fired when drilldown point is clicked.
509
+ * @event chart-drilldown
510
+ * @param {Object} detail.originalEvent object with details about the event sent
511
+ * @param {Object} chart Chart object where the event was sent from
512
+ */
513
+ drilldown: 'chart-drilldown',
514
+
515
+ /**
516
+ * Fired when drilling up from a drilldown series.
517
+ * @event chart-drillup
518
+ * @param {Object} detail.originalEvent object with details about the event sent
519
+ * @param {Object} chart Chart object where the event was sent from
520
+ */
521
+ drillup: 'chart-drillup',
522
+
523
+ /**
524
+ * Fired after all the series has been drilled up if chart has multiple drilldown series.
525
+ * @event chart-drillupall
526
+ * @param {Object} detail.originalEvent object with details about the event sent
527
+ * @param {Object} chart Chart object where the event was sent from
528
+ */
529
+ drillupall: 'chart-drillupall',
530
+
531
+ /**
532
+ * Fired when the chart is finished loading.
533
+ * @event chart-load
534
+ * @param {Object} detail.originalEvent object with details about the event sent
535
+ * @param {Object} chart Chart object where the event was sent from
536
+ */
537
+ load: 'chart-load',
538
+
539
+ /**
540
+ * Fired when the chart is redraw. Can be called after a `Chart.configuration.redraw()`
541
+ * or after an axis, series or point is modified with the `redraw` option set to `true`.
542
+ * @event chart-redraw
543
+ * @param {Object} detail.originalEvent object with details about the event sent
544
+ * @param {Object} chart Chart object where the event was sent from
545
+ */
546
+ redraw: 'chart-redraw',
547
+
548
+ /**
549
+ * Fired when an area of the chart has been selected.
550
+ * @event chart-selection
551
+ * @param {Object} detail.originalEvent object with details about the event sent
552
+ * @param {Object} chart Chart object where the event was sent from
553
+ */
554
+ selection: 'chart-selection',
555
+ };
556
+ }
557
+
558
+ /**
559
+ * Name of the series events to add to the configuration and its corresponding event for the chart element
560
+ * @private
561
+ */
562
+ get __seriesEventNames() {
563
+ return {
564
+ /**
565
+ * Fired when the series has finished its initial animation.
566
+ * @event series-after-animate
567
+ * @param {Object} detail.originalEvent object with details about the event sent
568
+ * @param {Object} series Series object where the event was sent from
569
+ */
570
+ afterAnimate: 'series-after-animate',
571
+
572
+ /**
573
+ * Fired when the checkbox next to the series' name in the legend is clicked.
574
+ * @event series-checkbox-click
575
+ * @param {Object} detail.originalEvent object with details about the event sent
576
+ * @param {Object} series Series object where the event was sent from
577
+ */
578
+ checkboxClick: 'series-checkbox-click',
579
+
580
+ /**
581
+ * Fired when the series is clicked.
582
+ * @event series-click
583
+ * @param {Object} detail.originalEvent object with details about the event sent
584
+ * @param {Object} series Series object where the event was sent from
585
+ */
586
+ click: 'series-click',
587
+
588
+ /**
589
+ * Fired when the series is hidden after chart generation time.
590
+ * @event series-hide
591
+ * @param {Object} detail.originalEvent object with details about the event sent
592
+ * @param {Object} series Series object where the event was sent from
593
+ */
594
+ hide: 'series-hide',
595
+
596
+ /**
597
+ * Fired when the legend item belonging to the series is clicked.
598
+ * @event series-legend-item-click
599
+ * @param {Object} detail.originalEvent object with details about the event sent
600
+ * @param {Object} series Series object where the event was sent from
601
+ */
602
+ legendItemClick: 'series-legend-item-click',
603
+
604
+ /**
605
+ * Fired when the mouses leave the graph.
606
+ * @event series-mouse-out
607
+ * @param {Object} detail.originalEvent object with details about the event sent
608
+ * @param {Object} series Series object where the event was sent from
609
+ */
610
+ mouseOut: 'series-mouse-out',
611
+
612
+ /**
613
+ * Fired when the mouse enters the graph.
614
+ * @event series-mouse-over
615
+ * @param {Object} detail.originalEvent object with details about the event sent
616
+ * @param {Object} series Series object where the event was sent from
617
+ */
618
+ mouseOver: 'series-mouse-over',
619
+
620
+ /**
621
+ * Fired when the series is show after chart generation time.
622
+ * @event series-show
623
+ * @param {Object} detail.originalEvent object with details about the event sent
624
+ * @param {Object} series Series object where the event was sent from
625
+ */
626
+ show: 'series-show',
627
+ };
628
+ }
629
+
630
+ /**
631
+ * Name of the point events to add to the configuration and its corresponding event for the chart element
632
+ * @private
633
+ */
634
+ get __pointEventNames() {
635
+ return {
636
+ /**
637
+ * Fired when the point is clicked.
638
+ * @event point-click
639
+ * @param {Object} detail.originalEvent object with details about the event sent
640
+ * @param {Object} point Point object where the event was sent from
641
+ */
642
+ click: 'point-click',
643
+
644
+ /**
645
+ * Fired when the legend item belonging to the point is clicked.
646
+ * @event point-legend-item-click
647
+ * @param {Object} detail.originalEvent object with details about the event sent
648
+ * @param {Object} point Point object where the event was sent from
649
+ */
650
+ legendItemClick: 'point-legend-item-click',
651
+
652
+ /**
653
+ * Fired when the mouse leaves the area close to the point.
654
+ * @event point-mouse-out
655
+ * @param {Object} detail.originalEvent object with details about the event sent
656
+ * @param {Object} point Point object where the event was sent from
657
+ */
658
+ mouseOut: 'point-mouse-out',
659
+
660
+ /**
661
+ * Fired when the mouse enters the area close to the point.
662
+ * @event point-mouse-over
663
+ * @param {Object} detail.originalEvent object with details about the event sent
664
+ * @param {Object} point Point object where the event was sent from
665
+ */
666
+ mouseOver: 'point-mouse-over',
667
+
668
+ /**
669
+ * Fired when the point is removed from the series.
670
+ * @event point-remove
671
+ * @param {Object} detail.originalEvent object with details about the event sent
672
+ * @param {Object} point Point object where the event was sent from
673
+ */
674
+ remove: 'point-remove',
675
+
676
+ /**
677
+ * Fired when the point is selected either programmatically or by clicking on the point.
678
+ * @event point-select
679
+ * @param {Object} detail.originalEvent object with details about the event sent
680
+ * @param {Object} point Point object where the event was sent from
681
+ */
682
+ select: 'point-select',
683
+
684
+ /**
685
+ * Fired when the point is unselected either programmatically or by clicking on the point
686
+ * @event point-unselect
687
+ * @param {Object} detail.originalEvent object with details about the event sent
688
+ * @param {Object} point Point object where the event was sent from
689
+ */
690
+ unselect: 'point-unselect',
691
+
692
+ /**
693
+ * Fired when the point is updated programmatically through `.updateConfiguration()` method.
694
+ * @event point-update
695
+ * @param {Object} detail.originalEvent object with details about the event sent
696
+ * @param {Object} point Point object where the event was sent from
697
+ */
698
+ update: 'point-update',
699
+
700
+ /**
701
+ * Fired when starting to drag a point.
702
+ * @event point-drag-start
703
+ * @param {Object} detail.originalEvent object with details about the event sent
704
+ * @param {Object} point Point object where the event was sent from
705
+ */
706
+ dragStart: 'point-drag-start',
707
+
708
+ /**
709
+ * Fired when the point is dropped.
710
+ * @event point-drop
711
+ * @param {Object} detail.originalEvent object with details about the event sent
712
+ * @param {Object} point Point object where the event was sent from
713
+ */
714
+ drop: 'point-drop',
715
+
716
+ /**
717
+ * Fired while dragging a point.
718
+ * @event point-drag
719
+ * @param {Object} detail.originalEvent object with details about the event sent
720
+ * @param {Object} point Point object where the event was sent from
721
+ */
722
+ drag: 'point-drag',
723
+ };
724
+ }
725
+
726
+ /** @private */
727
+ get __xAxesEventNames() {
728
+ return {
729
+ /**
730
+ * Fired when when the minimum and maximum is set for the x axis.
731
+ * @event xaxes-extremes-set
732
+ * @param {Object} detail.originalEvent object with details about the event sent
733
+ * @param {Object} axis Point object where the event was sent from
734
+ */
735
+ afterSetExtremes: 'xaxes-extremes-set',
736
+ };
737
+ }
738
+
739
+ /** @private */
740
+ get __yAxesEventNames() {
741
+ return {
742
+ /**
743
+ * Fired when when the minimum and maximum is set for the y axis.
744
+ * @event yaxes-extremes-set
745
+ * @param {Object} detail.originalEvent object with details about the event sent
746
+ * @param {Object} axis Point object where the event was sent from
747
+ */
748
+ afterSetExtremes: 'yaxes-extremes-set',
749
+ };
750
+ }
751
+
752
+ /** @protected */
753
+ connectedCallback() {
754
+ super.connectedCallback();
755
+ this.__updateStyles();
756
+ beforeNextRender(this, () => {
757
+ // Detect if the chart had already been initialized. This might happen in
758
+ // environments where the chart is lazily attached (e.g Grid).
759
+ if (this.configuration) {
760
+ this.__reflow();
761
+ return;
762
+ }
763
+
764
+ this.__resetChart();
765
+ this.__addChildObserver();
766
+ this.__checkTurboMode();
767
+ });
768
+ }
769
+
770
+ /** @protected */
771
+ ready() {
772
+ super.ready();
773
+
774
+ this.addEventListener('chart-redraw', this.__onRedraw.bind(this));
775
+ }
776
+
777
+ /**
778
+ * Implements resize callback from `ResizeMixin`
779
+ * to reflow when the chart element is resized.
780
+ * @protected
781
+ * @override
782
+ */
783
+ _onResize(contentRect) {
784
+ if (!this.configuration) {
785
+ return;
786
+ }
787
+
788
+ const { height, width } = contentRect;
789
+ const { chartHeight, chartWidth } = this.configuration;
790
+
791
+ if (height !== chartHeight || width !== chartWidth) {
792
+ this.__reflow();
793
+ }
794
+ }
795
+
796
+ /** @private */
797
+ __reflow() {
798
+ if (!this.configuration) {
799
+ return;
800
+ }
801
+ this.configuration.reflow();
802
+ }
803
+
804
+ /** @private */
805
+ __addChildObserver() {
806
+ this._childObserver = new FlattenedNodesObserver(this.$.slot, (info) => {
807
+ this.__addSeries(info.addedNodes.filter(this.__filterSeriesNodes));
808
+ this.__removeSeries(info.removedNodes.filter(this.__filterSeriesNodes));
809
+ this.__cleanupAfterSeriesRemoved(info.removedNodes.filter(this.__filterSeriesNodes));
810
+ });
811
+ }
812
+
813
+ /** @private */
814
+ __filterSeriesNodes(node) {
815
+ const ChartSeries = customElements.get('vaadin-chart-series');
816
+ return node.nodeType === Node.ELEMENT_NODE && node instanceof ChartSeries;
817
+ }
818
+
819
+ /** @private */
820
+ __addSeries(series) {
821
+ if (this.__isSeriesEmpty(series)) {
822
+ return;
823
+ }
824
+ const seriesNodes = Array.from(this.childNodes).filter(this.__filterSeriesNodes);
825
+
826
+ const yAxes = this.configuration.yAxis.reduce((acc, axis, index) => {
827
+ acc[axis.options.id || index] = axis;
828
+ return acc;
829
+ }, {});
830
+
831
+ for (let i = 0, len = series.length; i < len; i++) {
832
+ const seriesElement = series[i];
833
+ const { yAxis: unit, yAxisValueMin: valueMin, yAxisValueMax: valueMax } = seriesElement.options;
834
+
835
+ const idxOnChildList = seriesNodes.indexOf(seriesElement);
836
+ if (!unit && !this.configuration.yAxis.some((e) => e.userOptions.id === undefined)) {
837
+ yAxes[unit] = this.__addAxis({ axisGenerated: true });
838
+ } else if (unit && !yAxes[unit]) {
839
+ yAxes[unit] = this.__addAxis({ id: unit, title: { text: unit }, axisGenerated: true });
840
+ }
841
+ if (isFinite(valueMin)) {
842
+ this.__setYAxisProps(yAxes, unit, { min: valueMin });
843
+ }
844
+ if (isFinite(valueMax)) {
845
+ this.__setYAxisProps(yAxes, unit, { max: valueMax });
846
+ }
847
+
848
+ const seriesConfiguration = this.__updateOrAddSeriesInstance(seriesElement.options, idxOnChildList, false);
849
+
850
+ seriesElement.setSeries(seriesConfiguration);
851
+ }
852
+ this.__removeAxisIfEmpty();
853
+
854
+ this.configuration.redraw();
855
+ }
856
+
857
+ /** @private */
858
+ __removeSeries(seriesNodes) {
859
+ if (this.__isSeriesEmpty(seriesNodes)) {
860
+ return;
861
+ }
862
+
863
+ const ChartSeries = customElements.get('vaadin-chart-series');
864
+
865
+ seriesNodes.forEach((series) => {
866
+ if (series instanceof ChartSeries) {
867
+ series._series.remove();
868
+ }
869
+ });
870
+ }
871
+
872
+ /** @private */
873
+ __setYAxisProps(yAxes, yAxisId, props) {
874
+ if (yAxisId) {
875
+ yAxes[yAxisId].update(props);
876
+ } else {
877
+ this.configuration.yAxis[0].update(props);
878
+ }
879
+ }
880
+
881
+ /** @private */
882
+ __isSeriesEmpty(series) {
883
+ return series === null || series.length === 0;
884
+ }
885
+
886
+ /** @private */
887
+ __cleanupAfterSeriesRemoved(series) {
888
+ if (this.__isSeriesEmpty(series)) {
889
+ return;
890
+ }
891
+
892
+ this.__removeAxisIfEmpty();
893
+
894
+ // Best effort to make chart display custom empty-text messages when series are removed.
895
+ // This is needed because Highcharts currently doesn't react. A condition not catered for is
896
+ // when all points are removed from all series without removing any series.
897
+ this.__updateNoDataElement(this.configuration);
898
+ }
899
+
900
+ /** @private */
901
+ __initChart(options) {
902
+ this.__initEventsListeners(options);
903
+ this.__updateStyledMode(options);
904
+ if (options.chart.type === 'gantt') {
905
+ this.configuration = Highcharts.ganttChart(this.$.chart, options);
906
+ } else if (this.timeline) {
907
+ this.configuration = Highcharts.stockChart(this.$.chart, options);
908
+ } else {
909
+ this.configuration = Highcharts.chart(this.$.chart, options);
910
+ }
911
+ }
912
+
913
+ /** @private */
914
+ __updateStyledMode(options) {
915
+ const styledMode = options.chart.styledMode;
916
+ this.$.chart.toggleAttribute('styled-mode', !!styledMode);
917
+ }
918
+
919
+ /** @protected */
920
+ disconnectedCallback() {
921
+ super.disconnectedCallback();
922
+
923
+ if (this.configuration) {
924
+ this._jsonConfigurationBuffer = this.configuration.userOptions;
925
+ }
926
+
927
+ queueMicrotask(() => {
928
+ if (this.isConnected) {
929
+ return;
930
+ }
931
+
932
+ if (this.configuration) {
933
+ this.configuration.destroy();
934
+ this.configuration = undefined;
935
+
936
+ // Reset series objects to avoid errors while detached
937
+ const seriesNodes = Array.from(this.childNodes).filter(this.__filterSeriesNodes);
938
+ seriesNodes.forEach((series) => {
939
+ series.setSeries(null);
940
+ });
941
+ }
942
+
943
+ if (this._childObserver) {
944
+ this._childObserver.disconnect();
945
+ }
946
+ });
947
+ }
948
+
949
+ /** @private */
950
+ __resetChart() {
951
+ const initialOptions = { ...this.options, ...this._jsonConfigurationBuffer };
952
+ this.__initChart(initialOptions);
953
+ this._jsonConfigurationBuffer = null;
954
+ }
955
+
956
+ /**
957
+ * Search for axis with given `id`.
958
+ *
959
+ * @param {string} id contains the id that will be searched
960
+ * @param {boolean} isXAxis indicates if it will remove x or y axes. Defaults to `false`.
961
+ * @return {Axis}
962
+ * @protected
963
+ */
964
+ __getAxis(id, isXAxis) {
965
+ id = Number.parseInt(id) || id;
966
+ if (this.configuration) {
967
+ return (isXAxis ? this.configuration.xAxis : this.configuration.yAxis).find((axis) => axis.options.id === id);
968
+ }
969
+ }
970
+
971
+ /**
972
+ * Add an axis with given options
973
+ *
974
+ * @param {Object} options axis options
975
+ * @param {boolean} isXAxis indicates if axis is X (`true`) or Y (`false`). Defaults to `false`.
976
+ * @return {!Axis}
977
+ * @protected
978
+ */
979
+ __addAxis(options, isXAxis) {
980
+ if (this.configuration) {
981
+ this.__createEventListeners(
982
+ isXAxis ? this.__xAxesEventNames : this.__yAxesEventNames,
983
+ options,
984
+ 'events',
985
+ 'axis',
986
+ );
987
+ return this.configuration.addAxis(options, isXAxis);
988
+ }
989
+ }
990
+
991
+ /**
992
+ * Iterates over axes (y or x) and removes whenever it doesn't contain any series and was created for unit
993
+ *
994
+ * @param {boolean} isXAxis indicates if it will remove x or y axes. Defaults to `false`.
995
+ * @protected
996
+ */
997
+ __removeAxisIfEmpty(isXAxis) {
998
+ if (this.configuration) {
999
+ (isXAxis ? this.configuration.xAxis : this.configuration.yAxis).forEach((axis) => {
1000
+ if (axis.userOptions.axisGenerated && axis.series.length === 0) {
1001
+ axis.remove();
1002
+ }
1003
+ });
1004
+ }
1005
+ }
1006
+
1007
+ /**
1008
+ * Update the chart configuration.
1009
+ * This JSON API provides a simple single-argument alternative to the configuration property.
1010
+ *
1011
+ * Styling properties specified in this configuration will be ignored. To learn about chart styling
1012
+ * please see the CSS Styling section above.
1013
+ *
1014
+ * @param {!Options} jsonConfiguration Object chart configuration. Most important properties are:
1015
+ *
1016
+ * - annotations `Object[]` custom labels or shapes that can be tied to points, axis coordinates or chart pixel coordinates.
1017
+ * Detailed API for annotations object is available in [API Site](http://api.highcharts.com/highcharts/annotations)
1018
+ * - chart `Object` with options regarding the chart area and plot area as well as general chart options.
1019
+ * Detailed API for chart object is available in [API Site](http://api.highcharts.com/highcharts/chart)
1020
+ * - credits `Object` with options regarding the chart area and plot area as well as general chart options.
1021
+ * Detailed API for credits object is available in [API Site](http://api.highcharts.com/highcharts/credits)
1022
+ * - plotOptions `Object` wrapper for config objects for each series type.
1023
+ * Detailed API for plotOptions object is available in [API Site](http://api.highcharts.com/highcharts/plotOptions)
1024
+ * - series `Object[]` the actual series to append to the chart.
1025
+ * Detailed API for series object is available in [API Site](http://api.highcharts.com/highcharts/series)
1026
+ * - subtitle `Object` the chart's subtitle.
1027
+ * Detailed API for subtitle object is available in [API Site](http://api.highcharts.com/highcharts/subtitle)
1028
+ * - title `Object` the chart's main title.
1029
+ * Detailed API for title object is available in [API Site](http://api.highcharts.com/highcharts/title)
1030
+ * - tooltip `Object` Options for the tooltip that appears when the user hovers over a series or point.
1031
+ * Detailed API for tooltip object is available in [API Site](http://api.highcharts.com/highcharts/tooltip)
1032
+ * - xAxis `Object[]` The X axis or category axis. Normally this is the horizontal axis.
1033
+ * Detailed API for xAxis object is available in [API Site](http://api.highcharts.com/highcharts/xAxis)
1034
+ * - yAxis `Object[]` The Y axis or value axis. Normally this is the vertical axis.
1035
+ * Detailed API for yAxis object is available in [API Site](http://api.highcharts.com/highcharts/yAxis)
1036
+ * - zAxis `Object[]` The Z axis or depth axis for 3D plots.
1037
+ * Detailed API for zAxis object is available in [API Site](http://api.highcharts.com/highcharts/zAxis)
1038
+ *
1039
+ * @param {boolean=} resetConfiguration Optional boolean that should be set to true if no other chart configuration was set before or
1040
+ * if existing configuration should be discarded.
1041
+ */
1042
+ updateConfiguration(jsonConfiguration, resetConfiguration) {
1043
+ if (resetConfiguration || !this._jsonConfigurationBuffer) {
1044
+ this._jsonConfigurationBuffer = {};
1045
+ }
1046
+
1047
+ const configCopy = deepMerge({}, jsonConfiguration);
1048
+ inflateFunctions(configCopy);
1049
+ this._jsonConfigurationBuffer = this.__makeConfigurationBuffer(this._jsonConfigurationBuffer, configCopy);
1050
+
1051
+ beforeNextRender(this, () => {
1052
+ if (!this.configuration || !this._jsonConfigurationBuffer) {
1053
+ return;
1054
+ }
1055
+
1056
+ if (resetConfiguration) {
1057
+ this.__resetChart();
1058
+ return;
1059
+ }
1060
+
1061
+ this.configuration.update(this._jsonConfigurationBuffer, false);
1062
+ if (this._jsonConfigurationBuffer.credits) {
1063
+ this.__updateOrAddCredits(this._jsonConfigurationBuffer.credits);
1064
+ }
1065
+ if (this._jsonConfigurationBuffer.xAxis) {
1066
+ this.__updateOrAddAxes(this._jsonConfigurationBuffer.xAxis, true, false);
1067
+ }
1068
+ if (this._jsonConfigurationBuffer.yAxis) {
1069
+ this.__updateOrAddAxes(this._jsonConfigurationBuffer.yAxis, false, false);
1070
+ }
1071
+ if (this._jsonConfigurationBuffer.series) {
1072
+ this.__updateOrAddSeries(this._jsonConfigurationBuffer.series, false);
1073
+ }
1074
+ this._jsonConfigurationBuffer = null;
1075
+
1076
+ this.configuration.redraw();
1077
+ });
1078
+ }
1079
+
1080
+ /** @private */
1081
+ __makeConfigurationBuffer(target, source) {
1082
+ const _source = Highcharts.merge(source);
1083
+ const _target = Highcharts.merge(target);
1084
+
1085
+ this.__mergeConfigurationArray(_target, _source, 'series');
1086
+ this.__mergeConfigurationArray(_target, _source, 'xAxis');
1087
+ this.__mergeConfigurationArray(_target, _source, 'yAxis');
1088
+
1089
+ return Highcharts.merge(_target, _source);
1090
+ }
1091
+
1092
+ /** @private */
1093
+ __mergeConfigurationArray(target, configuration, entry) {
1094
+ if (!configuration || !configuration[entry] || !Array.isArray(configuration[entry])) {
1095
+ return;
1096
+ }
1097
+
1098
+ if (!target[entry]) {
1099
+ target[entry] = Array.from(configuration[entry]);
1100
+ return;
1101
+ }
1102
+
1103
+ const maxLength = Math.max(target[entry].length, configuration[entry].length);
1104
+ for (let i = 0; i < maxLength; i++) {
1105
+ target[entry][i] = Highcharts.merge(target[entry][i], configuration[entry][i]);
1106
+ }
1107
+ delete configuration[entry];
1108
+ }
1109
+
1110
+ /** @private */
1111
+ __initEventsListeners(configuration) {
1112
+ this.__initChartEventsListeners(configuration);
1113
+ this.__initSeriesEventsListeners(configuration);
1114
+ this.__initPointsEventsListeners(configuration);
1115
+ this.__initAxisEventsListeners(configuration, true);
1116
+ this.__initAxisEventsListeners(configuration, false);
1117
+ }
1118
+
1119
+ /** @private */
1120
+ __initChartEventsListeners(configuration) {
1121
+ this.__createEventListeners(this.__chartEventNames, configuration, 'chart.events', 'chart');
1122
+ }
1123
+
1124
+ /** @private */
1125
+ __initSeriesEventsListeners(configuration) {
1126
+ this.__createEventListeners(this.__seriesEventNames, configuration, 'plotOptions.series.events', 'series');
1127
+ }
1128
+
1129
+ /** @private */
1130
+ __initPointsEventsListeners(configuration) {
1131
+ this.__createEventListeners(this.__pointEventNames, configuration, 'plotOptions.series.point.events', 'point');
1132
+ }
1133
+
1134
+ /** @private */
1135
+ __initAxisEventsListeners(configuration, isXAxis) {
1136
+ let eventNames, axes;
1137
+
1138
+ if (isXAxis) {
1139
+ eventNames = this.__xAxesEventNames;
1140
+ axes = configuration.xAxis;
1141
+ } else {
1142
+ eventNames = this.__yAxesEventNames;
1143
+ axes = configuration.yAxis;
1144
+ }
1145
+
1146
+ if (Array.isArray(axes)) {
1147
+ axes.forEach((axis) => this.__createEventListeners(eventNames, axis, 'events', 'axis'));
1148
+ } else {
1149
+ this.__createEventListeners(eventNames, axes, 'events', 'axis');
1150
+ }
1151
+ }
1152
+
1153
+ /** @private */
1154
+ __createEventListeners(eventList, configuration, pathToAdd, eventType) {
1155
+ const eventObject = this.__ensureObjectPath(configuration, pathToAdd);
1156
+ const self = this;
1157
+
1158
+ for (let keys = Object.keys(eventList), i = 0; i < keys.length; i++) {
1159
+ const key = keys[i];
1160
+ if (!eventObject[key]) {
1161
+ eventObject[key] = function (event) {
1162
+ const customEvent = {
1163
+ bubbles: false,
1164
+ composed: true,
1165
+ detail: {
1166
+ originalEvent: event,
1167
+ [eventType]: event.target,
1168
+ },
1169
+ };
1170
+
1171
+ if (key === 'dragStart') {
1172
+ // for dragStart there is no information about point in the
1173
+ // event object. However, 'this' references the point being dragged
1174
+ customEvent.detail[eventType] = this;
1175
+ }
1176
+
1177
+ if (event.type === 'afterSetExtremes') {
1178
+ if (event.min == null || event.max == null) {
1179
+ return;
1180
+ }
1181
+ }
1182
+
1183
+ // Workaround for vaadin-charts-flow because of https://github.com/vaadin/flow/issues/3102
1184
+ if (event.type === 'selection') {
1185
+ if (event.xAxis && event.xAxis[0]) {
1186
+ customEvent.detail.xAxisMin = event.xAxis[0].min;
1187
+ customEvent.detail.xAxisMax = event.xAxis[0].max;
1188
+ }
1189
+ if (event.yAxis && event.yAxis[0]) {
1190
+ customEvent.detail.yAxisMin = event.yAxis[0].min;
1191
+ customEvent.detail.yAxisMax = event.yAxis[0].max;
1192
+ }
1193
+ }
1194
+ if (event.type === 'click') {
1195
+ if (event.xAxis && event.xAxis[0]) {
1196
+ customEvent.detail.xValue = event.xAxis[0].value;
1197
+ }
1198
+ if (event.yAxis && event.yAxis[0]) {
1199
+ customEvent.detail.yValue = event.yAxis[0].value;
1200
+ }
1201
+ }
1202
+
1203
+ // Workaround for https://github.com/vaadin/vaadin-charts/issues/389
1204
+ // Hook into beforePrint and beforeExport to ensure correct styling
1205
+ if (['beforePrint', 'beforeExport'].indexOf(event.type) >= 0) {
1206
+ // Guard against another print 'before print' event coming before
1207
+ // the 'after print' event.
1208
+ if (!self.tempBodyStyle) {
1209
+ let effectiveCss = '';
1210
+
1211
+ [...self.shadowRoot.querySelectorAll('style')].forEach((style) => {
1212
+ effectiveCss += style.textContent;
1213
+ });
1214
+
1215
+ // Strip off host selectors that target individual instances
1216
+ effectiveCss = effectiveCss.replace(/:host\(.+?\)/gu, (match) => {
1217
+ const selector = match.substr(6, match.length - 7);
1218
+ return self.matches(selector) ? '' : match;
1219
+ });
1220
+
1221
+ // Zoom out a bit to avoid clipping the chart's edge on paper
1222
+ effectiveCss =
1223
+ `${effectiveCss}body {` +
1224
+ ` -moz-transform: scale(0.9, 0.9);` + // Mozilla
1225
+ ` zoom: 0.9;` + // Others
1226
+ ` zoom: 90%;` + // Webkit
1227
+ `}`;
1228
+
1229
+ self.tempBodyStyle = document.createElement('style');
1230
+ self.tempBodyStyle.textContent = effectiveCss;
1231
+ document.body.appendChild(self.tempBodyStyle);
1232
+ if (self.options.chart.styledMode) {
1233
+ document.body.setAttribute('styled-mode', '');
1234
+ }
1235
+ }
1236
+ }
1237
+
1238
+ // Hook into afterPrint and afterExport to revert changes made before
1239
+ if (['afterPrint', 'afterExport'].indexOf(event.type) >= 0) {
1240
+ if (self.tempBodyStyle) {
1241
+ document.body.removeChild(self.tempBodyStyle);
1242
+ delete self.tempBodyStyle;
1243
+ if (self.options.chart.styledMode) {
1244
+ document.body.removeAttribute('styled-mode');
1245
+ }
1246
+ }
1247
+ }
1248
+
1249
+ self.dispatchEvent(new CustomEvent(eventList[key], customEvent));
1250
+
1251
+ if (event.type === 'legendItemClick' && self._visibilityTogglingDisabled) {
1252
+ return false;
1253
+ }
1254
+ };
1255
+ }
1256
+ }
1257
+ }
1258
+
1259
+ /** @private */
1260
+ __ensureObjectPath(object, path) {
1261
+ if (typeof path !== 'string') {
1262
+ return;
1263
+ }
1264
+
1265
+ path = path.split('.');
1266
+ return path.reduce((obj, key) => {
1267
+ if (!obj[key]) {
1268
+ obj[key] = {};
1269
+ }
1270
+ return obj[key];
1271
+ }, object);
1272
+ }
1273
+
1274
+ /** @private */
1275
+ __hasConfigurationBuffer(path) {
1276
+ return get(path, this._jsonConfigurationBuffer) !== undefined;
1277
+ }
1278
+
1279
+ /** @private */
1280
+ __updateOrAddCredits(credits) {
1281
+ if (this.configuration.credits) {
1282
+ this.configuration.credits.update(credits);
1283
+ } else {
1284
+ this.configuration.addCredits(credits);
1285
+ }
1286
+ }
1287
+
1288
+ /** @private */
1289
+ __updateOrAddAxes(axes, isX, redraw) {
1290
+ if (!Array.isArray(axes)) {
1291
+ axes = [axes];
1292
+ }
1293
+ const confAxes = isX ? this.configuration.xAxis : this.configuration.yAxis;
1294
+ for (let i = 0; i < axes.length; i++) {
1295
+ const axis = axes[i];
1296
+ if (confAxes[i]) {
1297
+ confAxes[i].update(axis, redraw);
1298
+ } else {
1299
+ this.configuration.addAxis(axis, isX, redraw);
1300
+ }
1301
+ }
1302
+ }
1303
+
1304
+ /** @private */
1305
+ __updateOrAddSeries(series, redraw) {
1306
+ if (!Array.isArray(series)) {
1307
+ throw new Error('The type of jsonConfiguration.series should be Object[]');
1308
+ }
1309
+ for (let i = 0; i < series.length; i++) {
1310
+ const currentSeries = series[i];
1311
+ this.__updateOrAddSeriesInstance(currentSeries, i, redraw);
1312
+ }
1313
+ }
1314
+
1315
+ /** @private */
1316
+ __updateOrAddSeriesInstance(seriesOptions, position, redraw) {
1317
+ if (this.configuration.series[position]) {
1318
+ this.configuration.series[position].update(seriesOptions, redraw);
1319
+ } else {
1320
+ this.configuration.addSeries(seriesOptions, redraw);
1321
+ }
1322
+ return this.configuration.series[position];
1323
+ }
1324
+
1325
+ /** @private */
1326
+ __updateCategories(categories, config) {
1327
+ if (categories === undefined || !config || this.__hasConfigurationBuffer('xAxis.categories')) {
1328
+ return;
1329
+ }
1330
+
1331
+ this.__updateOrAddAxes([{ categories }], true);
1332
+ }
1333
+
1334
+ /** @private */
1335
+ __updateCategoryMax(max, config) {
1336
+ if (max === undefined || !config || this.__hasConfigurationBuffer('xAxis.max')) {
1337
+ return;
1338
+ }
1339
+
1340
+ if (!isFinite(max)) {
1341
+ console.warn('<vaadin-chart> Acceptable value for "category-max" are Numbers or null');
1342
+ return;
1343
+ }
1344
+
1345
+ this.__updateOrAddAxes([{ max }], true);
1346
+ }
1347
+
1348
+ /** @private */
1349
+ __updateCategoryMin(min, config) {
1350
+ if (min === undefined || !config || this.__hasConfigurationBuffer('xAxis.min')) {
1351
+ return;
1352
+ }
1353
+
1354
+ if (!isFinite(min)) {
1355
+ console.warn('<vaadin-chart> Acceptable value for "category-min" are Numbers or null');
1356
+ return;
1357
+ }
1358
+
1359
+ this.__updateOrAddAxes([{ min }], true);
1360
+ }
1361
+
1362
+ /** @private */
1363
+ __shouldInvert() {
1364
+ // A bar chart will never be inverted, consider using a column chart.
1365
+ // See https://stackoverflow.com/questions/11235251#answer-21739793
1366
+ if (this.type === 'bar' && ['top', 'bottom'].indexOf(this.categoryPosition) >= 0) {
1367
+ console.warn(`<vaadin-chart> Acceptable "category-position" values for bar charts are
1368
+ "left" and "right". For "top" and "bottom" positions please consider using a column chart.`);
1369
+ return;
1370
+ }
1371
+
1372
+ const inverted = ['left', 'right'];
1373
+ return inverted.indexOf(this.categoryPosition) >= 0;
1374
+ }
1375
+
1376
+ /** @private */
1377
+ __shouldFlipOpposite() {
1378
+ const opposite = ['top', 'right'];
1379
+ const oppositeBar = ['right'];
1380
+ return (this.type === 'bar' ? oppositeBar : opposite).indexOf(this.categoryPosition) >= 0;
1381
+ }
1382
+
1383
+ /** @private */
1384
+ __updateCategoryPosition(categoryPosition, config) {
1385
+ if (categoryPosition === undefined || !config || this.__hasConfigurationBuffer('chart.inverted')) {
1386
+ return;
1387
+ }
1388
+
1389
+ const validPositions = ['left', 'right', 'top', 'bottom'];
1390
+
1391
+ if (validPositions.indexOf(categoryPosition) < 0) {
1392
+ console.warn(`<vaadin-chart> Acceptable "category-position" values are ${validPositions}`);
1393
+ return;
1394
+ }
1395
+
1396
+ config.update({
1397
+ chart: {
1398
+ inverted: this.__shouldInvert(),
1399
+ },
1400
+ });
1401
+
1402
+ config.xAxis.forEach((e) =>
1403
+ e.update({
1404
+ opposite: this.__shouldFlipOpposite(),
1405
+ }),
1406
+ );
1407
+ }
1408
+
1409
+ /** @private */
1410
+ __hideLegend(noLegend, config) {
1411
+ if (noLegend === undefined || !config || this.__hasConfigurationBuffer('legend')) {
1412
+ return;
1413
+ }
1414
+
1415
+ if (config.legend) {
1416
+ config.legend.update({ enabled: !noLegend });
1417
+ } else {
1418
+ config.legend = { enabled: !noLegend };
1419
+ }
1420
+ }
1421
+
1422
+ /** @private */
1423
+ __updateTitle(title, config) {
1424
+ if (title === undefined || !config || this.__hasConfigurationBuffer('title')) {
1425
+ return;
1426
+ }
1427
+
1428
+ config.title.update({ text: title });
1429
+ }
1430
+
1431
+ /** @private */
1432
+ __tooltipObserver(tooltip, config) {
1433
+ if (tooltip === undefined || !config || this.__hasConfigurationBuffer('tooltip')) {
1434
+ return;
1435
+ }
1436
+
1437
+ config.tooltip.update({ enabled: tooltip });
1438
+ }
1439
+
1440
+ /** @private */
1441
+ __updateType(type, config) {
1442
+ if (type === undefined || !config || this.__hasConfigurationBuffer('chart.type')) {
1443
+ return;
1444
+ }
1445
+
1446
+ config.update({
1447
+ chart: { type: type || 'line' },
1448
+ });
1449
+ }
1450
+
1451
+ /** @private */
1452
+ __updateSubtitle(subtitle, config) {
1453
+ if (subtitle === undefined || !config || this.__hasConfigurationBuffer('subtitle')) {
1454
+ return;
1455
+ }
1456
+
1457
+ if (!config.subtitle) {
1458
+ config.setSubtitle({ text: subtitle });
1459
+ } else {
1460
+ config.subtitle.update({ text: subtitle });
1461
+ }
1462
+ }
1463
+
1464
+ /** @private */
1465
+ __updateAdditionalOptions(options) {
1466
+ if (this.configuration && options) {
1467
+ this.updateConfiguration(options);
1468
+ }
1469
+ }
1470
+
1471
+ /** @private */
1472
+ __isStackingValid() {
1473
+ if (['normal', 'percent', null].indexOf(this.stacking) === -1) {
1474
+ this.__showWarn('stacking', '"normal", "percent" or null');
1475
+ return false;
1476
+ }
1477
+ return true;
1478
+ }
1479
+
1480
+ /** @private */
1481
+ __stackingObserver(stacking, config) {
1482
+ if (stacking === undefined || !config || this.__hasConfigurationBuffer('plotOptions.series.stacking')) {
1483
+ return;
1484
+ }
1485
+
1486
+ if (!this.__isStackingValid()) {
1487
+ this.stacking = null;
1488
+ return;
1489
+ }
1490
+
1491
+ config.update({
1492
+ plotOptions: {
1493
+ series: { stacking },
1494
+ },
1495
+ });
1496
+ }
1497
+
1498
+ /** @private */
1499
+ __chart3dObserver(chart3d, config) {
1500
+ if (chart3d === undefined || !config || this.__hasConfigurationBuffer('chart.options3d')) {
1501
+ return;
1502
+ }
1503
+
1504
+ if (chart3d) {
1505
+ config.update({
1506
+ chart: {
1507
+ options3d: {
1508
+ ...this._baseChart3d,
1509
+ ...(this.additionalOptions && this.additionalOptions.chart && this.additionalOptions.chart.options3d),
1510
+ enabled: true,
1511
+ },
1512
+ },
1513
+ });
1514
+ } else {
1515
+ config.update({
1516
+ chart: {
1517
+ options3d: {
1518
+ enabled: false,
1519
+ },
1520
+ },
1521
+ });
1522
+ }
1523
+ }
1524
+
1525
+ /** @private */
1526
+ __polarObserver(polar, config) {
1527
+ if (polar === undefined || !config || this.__hasConfigurationBuffer('chart.polar')) {
1528
+ return;
1529
+ }
1530
+
1531
+ config.update({
1532
+ chart: { polar },
1533
+ });
1534
+ }
1535
+
1536
+ /** @private */
1537
+ __emptyTextObserver(emptyText, config) {
1538
+ if (emptyText === undefined || !config || this.__hasConfigurationBuffer('lang.noData')) {
1539
+ return;
1540
+ }
1541
+
1542
+ config.update({
1543
+ lang: {
1544
+ noData: emptyText,
1545
+ },
1546
+ });
1547
+ this.__updateNoDataElement(config);
1548
+ }
1549
+
1550
+ /**
1551
+ * Force the no data text element to become visible if the chart has no data.
1552
+ * This is necessary in cases where Highcharts does not update the element
1553
+ * automatically, for example when setting the language config
1554
+ * @private
1555
+ */
1556
+ __updateNoDataElement(config) {
1557
+ const isEmpty = config.series.every((e) => e.data.length === 0);
1558
+ if (isEmpty) {
1559
+ config.hideNoData();
1560
+ config.showNoData(this.emptyText);
1561
+ }
1562
+ }
1563
+
1564
+ /** @private */
1565
+ __callChartFunction(functionName, ...args) {
1566
+ if (this.configuration) {
1567
+ const functionToCall = this.configuration[functionName];
1568
+ if (functionToCall && typeof functionToCall === 'function') {
1569
+ args.forEach((arg) => inflateFunctions(arg));
1570
+ functionToCall.apply(this.configuration, args);
1571
+ }
1572
+ }
1573
+ }
1574
+
1575
+ /** @private */
1576
+ __callSeriesFunction(functionName, seriesIndex, ...args) {
1577
+ if (this.configuration && this.configuration.series[seriesIndex]) {
1578
+ const series = this.configuration.series[seriesIndex];
1579
+ const functionToCall = series[functionName];
1580
+ if (functionToCall && typeof functionToCall === 'function') {
1581
+ args.forEach((arg) => inflateFunctions(arg));
1582
+ functionToCall.apply(series, args);
1583
+ }
1584
+ }
1585
+ }
1586
+
1587
+ /** @private */
1588
+ __callAxisFunction(functionName, axisCategory, axisIndex, ...args) {
1589
+ /*
1590
+ * AxisCategory:
1591
+ * 0 - xAxis
1592
+ * 1 - yAxis
1593
+ * 2 - zAxis
1594
+ * 3 - colorAxis
1595
+ */
1596
+ if (this.configuration) {
1597
+ let axes;
1598
+ switch (axisCategory) {
1599
+ case 0:
1600
+ axes = this.configuration.xAxis;
1601
+ break;
1602
+ case 1:
1603
+ axes = this.configuration.yAxis;
1604
+ break;
1605
+ case 2:
1606
+ axes = this.configuration.zAxis;
1607
+ break;
1608
+ case 3:
1609
+ axes = this.configuration.colorAxis;
1610
+ break;
1611
+ default:
1612
+ break;
1613
+ }
1614
+ if (axes && axes[axisIndex]) {
1615
+ const axis = axes[axisIndex];
1616
+ const functionToCall = axis[functionName];
1617
+ if (functionToCall && typeof functionToCall === 'function') {
1618
+ args.forEach((arg) => inflateFunctions(arg));
1619
+ functionToCall.apply(axis, args);
1620
+ }
1621
+ }
1622
+ }
1623
+ }
1624
+
1625
+ /** @private */
1626
+ __callPointFunction(functionName, seriesIndex, pointIndex, ...args) {
1627
+ if (
1628
+ this.configuration &&
1629
+ this.configuration.series[seriesIndex] &&
1630
+ this.configuration.series[seriesIndex].data[pointIndex]
1631
+ ) {
1632
+ const point = this.configuration.series[seriesIndex].data[pointIndex];
1633
+ const functionToCall = point[functionName];
1634
+ if (functionToCall && typeof functionToCall === 'function') {
1635
+ functionToCall.apply(point, args);
1636
+ }
1637
+ }
1638
+ }
1639
+
1640
+ /**
1641
+ * Updates chart container and current chart style property depending on flex status
1642
+ * @private
1643
+ */
1644
+ __updateStyles() {
1645
+ // Chrome returns default value if property is not set
1646
+ // check if flex is defined for chart, and different than default value
1647
+ const isFlex = getComputedStyle(this).flex !== '0 1 auto';
1648
+
1649
+ // If chart element is a flexible item the chartContainer should be flex too
1650
+ if (isFlex) {
1651
+ this.$.chart.setAttribute('style', 'flex: 1; ');
1652
+ let style = '';
1653
+ if (this.hasAttribute('style')) {
1654
+ style = this.getAttribute('style');
1655
+ if (!style.endsWith(';')) {
1656
+ style += ';';
1657
+ }
1658
+ }
1659
+ style += 'display: flex;';
1660
+ this.setAttribute('style', style);
1661
+ } else {
1662
+ this.$.chart.setAttribute('style', 'height:100%; width:100%;');
1663
+ }
1664
+ }
1665
+
1666
+ /** @private */
1667
+ __showWarn(propertyName, acceptedValues) {
1668
+ console.warn(`<vaadin-chart> Acceptable values for "${propertyName}" are ${acceptedValues}`);
1669
+ }
1670
+
1671
+ /** @private */
1672
+ __onRedraw() {
1673
+ this.__checkTurboMode();
1674
+ }
1675
+
1676
+ /** @private */
1677
+ __checkTurboMode() {
1678
+ const isDevelopmentMode = !!window.Vaadin.developmentMode;
1679
+
1680
+ if (!this.configuration || !isDevelopmentMode || this.__turboModeWarningAlreadyLogged) {
1681
+ return;
1682
+ }
1683
+
1684
+ const exceedsTurboThreshold = this.configuration.series.some((series) => {
1685
+ const threshold = (series.options && series.options.turboThreshold) || 0;
1686
+ const dataLength = series.data.length;
1687
+
1688
+ return threshold > 0 && dataLength > threshold;
1689
+ });
1690
+
1691
+ if (exceedsTurboThreshold) {
1692
+ this.__turboModeWarningAlreadyLogged = true;
1693
+ console.warn(
1694
+ '<vaadin-chart> Turbo mode has been enabled for one or more series, because the number of data items exceeds the configured threshold. Turbo mode improves the performance of charts with lots of data, but is not compatible with every type of series. Please consult the documentation on compatibility, or how to disable turbo mode.',
1695
+ );
1696
+ }
1697
+ }
1698
+ };