@vaadin/charts 24.6.5 → 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.
- package/package.json +11 -9
- package/src/helpers.js +20 -0
- package/src/vaadin-chart-mixin.d.ts +439 -0
- package/src/vaadin-chart-mixin.js +1698 -0
- package/src/vaadin-chart-series-mixin.d.ts +136 -0
- package/src/vaadin-chart-series-mixin.js +412 -0
- package/src/vaadin-chart-series.d.ts +4 -121
- package/src/vaadin-chart-series.js +4 -384
- package/src/vaadin-chart.d.ts +7 -402
- package/src/vaadin-chart.js +8 -1644
- package/src/vaadin-lit-chart-series.d.ts +11 -0
- package/src/vaadin-lit-chart-series.js +33 -0
- package/src/vaadin-lit-chart.d.ts +11 -0
- package/src/vaadin-lit-chart.js +61 -0
- package/theme/lumo/vaadin-lit-chart.d.ts +2 -0
- package/theme/lumo/vaadin-lit-chart.js +2 -0
- package/theme/material/vaadin-lit-chart.d.ts +2 -0
- package/theme/material/vaadin-lit-chart.js +2 -0
- package/theme/vaadin-chart-base-theme.js +7 -1
- package/vaadin-lit-chart-series.d.ts +1 -0
- package/vaadin-lit-chart-series.js +1 -0
- package/vaadin-lit-chart.d.ts +1 -0
- package/vaadin-lit-chart.js +2 -0
- package/web-types.json +242 -230
- package/web-types.lit.json +107 -86
|
@@ -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
|
+
};
|