evui 3.3.36 → 3.3.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +40 -40
  3. package/dist/evui.common.js +1907 -1832
  4. package/dist/evui.common.js.map +1 -1
  5. package/dist/evui.umd.js +1907 -1832
  6. package/dist/evui.umd.js.map +1 -1
  7. package/dist/evui.umd.min.js +1 -1
  8. package/dist/evui.umd.min.js.map +1 -1
  9. package/dist/img/{EVUI.7f3588fb.svg → EVUI.b82ee81a.svg} +292 -292
  10. package/dist/img/{icon_mysql.7ea26d5d.svg → icon_mysql.1085fdc9.svg} +78 -78
  11. package/dist/img/{icon_oracle.9009b108.svg → icon_oracle.0572d3ee.svg} +13 -13
  12. package/dist/img/{icon_postgresql.f8fffba9.svg → icon_postgresql.ee12bde8.svg} +58 -58
  13. package/package.json +61 -61
  14. package/src/common/emitter.js +20 -20
  15. package/src/common/utils.debounce.js +223 -223
  16. package/src/common/utils.js +134 -134
  17. package/src/common/utils.table.js +78 -78
  18. package/src/common/utils.throttle.js +83 -83
  19. package/src/common/utils.tree.js +18 -18
  20. package/src/components/button/Button.vue +198 -198
  21. package/src/components/button/index.js +7 -7
  22. package/src/components/buttonGroup/ButtonGroup.vue +11 -11
  23. package/src/components/buttonGroup/index.js +7 -7
  24. package/src/components/calendar/Calendar.vue +661 -661
  25. package/src/components/calendar/index.js +7 -7
  26. package/src/components/calendar/uses.js +1272 -1272
  27. package/src/components/chart/Chart.vue +189 -192
  28. package/src/components/chart/chart.core.js +870 -870
  29. package/src/components/chart/element/element.bar.js +524 -524
  30. package/src/components/chart/element/element.bar.time.js +156 -156
  31. package/src/components/chart/element/element.heatmap.js +533 -533
  32. package/src/components/chart/element/element.line.js +339 -339
  33. package/src/components/chart/element/element.pie.js +197 -197
  34. package/src/components/chart/element/element.scatter.js +184 -184
  35. package/src/components/chart/element/element.tip.js +550 -542
  36. package/src/components/chart/helpers/helpers.canvas.js +265 -265
  37. package/src/components/chart/helpers/helpers.constant.js +206 -206
  38. package/src/components/chart/helpers/helpers.util.js +346 -338
  39. package/src/components/chart/index.js +9 -9
  40. package/src/components/chart/model/index.js +4 -4
  41. package/src/components/chart/model/model.series.js +93 -93
  42. package/src/components/chart/model/model.store.js +977 -967
  43. package/src/components/chart/plugins/plugins.interaction.js +769 -769
  44. package/src/components/chart/plugins/plugins.legend.gradient.js +602 -602
  45. package/src/components/chart/plugins/plugins.legend.js +1155 -1151
  46. package/src/components/chart/plugins/plugins.pie.js +254 -254
  47. package/src/components/chart/plugins/plugins.title.js +56 -56
  48. package/src/components/chart/plugins/plugins.tooltip.js +692 -692
  49. package/src/components/chart/scale/scale.js +848 -848
  50. package/src/components/chart/scale/scale.linear.js +38 -38
  51. package/src/components/chart/scale/scale.logarithmic.js +128 -128
  52. package/src/components/chart/scale/scale.step.js +336 -336
  53. package/src/components/chart/scale/scale.time.category.js +277 -277
  54. package/src/components/chart/scale/scale.time.js +48 -48
  55. package/src/components/chart/style/chart.scss +312 -312
  56. package/src/components/chart/uses.js +264 -252
  57. package/src/components/checkbox/Checkbox.vue +200 -200
  58. package/src/components/checkbox/index.js +7 -7
  59. package/src/components/checkboxGroup/CheckboxGroup.vue +44 -44
  60. package/src/components/checkboxGroup/index.js +7 -7
  61. package/src/components/contextMenu/ContextMenu.vue +80 -80
  62. package/src/components/contextMenu/MenuList.vue +149 -149
  63. package/src/components/contextMenu/index.js +7 -7
  64. package/src/components/contextMenu/uses.js +203 -203
  65. package/src/components/datePicker/DatePicker.vue +437 -437
  66. package/src/components/datePicker/index.js +7 -7
  67. package/src/components/datePicker/uses.js +419 -419
  68. package/src/components/grid/Grid.vue +827 -827
  69. package/src/components/grid/grid.filter.window.vue +493 -493
  70. package/src/components/grid/grid.pagination.vue +75 -75
  71. package/src/components/grid/grid.summary.vue +265 -265
  72. package/src/components/grid/grid.toolbar.vue +26 -26
  73. package/src/components/grid/index.js +11 -11
  74. package/src/components/grid/style/grid.scss +263 -263
  75. package/src/components/grid/uses.js +1002 -1007
  76. package/src/components/icon/Icon.vue +49 -49
  77. package/src/components/icon/index.js +8 -8
  78. package/src/components/inputNumber/InputNumber.vue +212 -212
  79. package/src/components/inputNumber/index.js +7 -7
  80. package/src/components/inputNumber/uses.js +217 -217
  81. package/src/components/loading/Loading.vue +125 -125
  82. package/src/components/loading/index.js +7 -7
  83. package/src/components/menu/Menu.vue +68 -68
  84. package/src/components/menu/MenuItem.vue +187 -187
  85. package/src/components/menu/index.js +7 -7
  86. package/src/components/message/Message.vue +223 -223
  87. package/src/components/message/index.js +31 -31
  88. package/src/components/messageBox/MessageBox.vue +358 -358
  89. package/src/components/messageBox/index.js +22 -22
  90. package/src/components/notification/Notification.vue +316 -316
  91. package/src/components/notification/index.js +49 -49
  92. package/src/components/pagination/Pagination.vue +271 -271
  93. package/src/components/pagination/index.js +7 -7
  94. package/src/components/pagination/pageButton.vue +30 -30
  95. package/src/components/progress/Progress.vue +139 -139
  96. package/src/components/progress/index.js +7 -7
  97. package/src/components/radio/Radio.vue +159 -159
  98. package/src/components/radio/index.js +7 -7
  99. package/src/components/radioGroup/RadioGroup.vue +41 -41
  100. package/src/components/radioGroup/index.js +7 -7
  101. package/src/components/scheduler/Scheduler.vue +149 -149
  102. package/src/components/scheduler/index.js +7 -7
  103. package/src/components/scheduler/uses.js +183 -183
  104. package/src/components/select/Select.vue +440 -440
  105. package/src/components/select/index.js +7 -7
  106. package/src/components/select/uses.js +270 -270
  107. package/src/components/slider/Slider.vue +505 -505
  108. package/src/components/slider/index.js +7 -7
  109. package/src/components/slider/uses.js +390 -390
  110. package/src/components/tabPanel/TabPanel.vue +74 -74
  111. package/src/components/tabPanel/index.js +7 -7
  112. package/src/components/tabs/Tabs.vue +517 -517
  113. package/src/components/tabs/index.js +7 -7
  114. package/src/components/textField/TextField.vue +375 -375
  115. package/src/components/textField/index.js +7 -7
  116. package/src/components/timePicker/TimePicker.vue +352 -352
  117. package/src/components/timePicker/index.js +7 -7
  118. package/src/components/toggle/Toggle.vue +115 -115
  119. package/src/components/toggle/index.js +7 -7
  120. package/src/components/tree/Tree.vue +313 -313
  121. package/src/components/tree/TreeNode.vue +293 -293
  122. package/src/components/tree/index.js +7 -7
  123. package/src/components/treeGrid/TreeGrid.vue +758 -758
  124. package/src/components/treeGrid/TreeGridNode.vue +275 -275
  125. package/src/components/treeGrid/index.js +9 -9
  126. package/src/components/treeGrid/style/treeGrid.scss +261 -261
  127. package/src/components/treeGrid/treeGrid.toolbar.vue +26 -26
  128. package/src/components/treeGrid/uses.js +867 -867
  129. package/src/components/window/Window.vue +329 -329
  130. package/src/components/window/index.js +7 -7
  131. package/src/components/window/uses.js +899 -899
  132. package/src/directives/clickoutside.js +90 -90
  133. package/src/main.js +116 -116
  134. package/src/style/components/input.scss +108 -108
  135. package/src/style/functions.scss +3 -3
  136. package/src/style/index.scss +6 -6
  137. package/src/style/lib/fonts/EVUI.svg +292 -292
  138. package/src/style/lib/icon.css +888 -888
  139. package/src/style/mixins.scss +94 -94
  140. package/src/style/themes.scss +67 -67
  141. package/src/style/variables.scss +22 -22
@@ -1,870 +1,870 @@
1
- import throttle from '@/common/utils.throttle';
2
- import Model from './model';
3
- import TimeScale from './scale/scale.time';
4
- import LinearScale from './scale/scale.linear';
5
- import LogarithmicScale from './scale/scale.logarithmic';
6
- import StepScale from './scale/scale.step';
7
- import TimeCategoryScale from './scale/scale.time.category';
8
- import Title from './plugins/plugins.title';
9
- import Legend from './plugins/plugins.legend';
10
- import GradientLegend from './plugins/plugins.legend.gradient';
11
- import Interaction from './plugins/plugins.interaction';
12
- import Tooltip from './plugins/plugins.tooltip';
13
- import Pie from './plugins/plugins.pie';
14
- import Tip from './element/element.tip';
15
-
16
- class EvChart {
17
- constructor(target, data, options, listeners, defaultSelectItemInfo, defaultSelectInfo) {
18
- Object.keys(Model).forEach(key => Object.assign(this, Model[key]));
19
- Object.assign(this, Title);
20
- Object.assign(this, Legend);
21
- Object.assign(this, Interaction);
22
- Object.assign(this, Tooltip);
23
- Object.assign(this, Pie);
24
- Object.assign(this, Tip);
25
-
26
- if (options.type === 'heatMap' && options.legend.type === 'gradient') {
27
- Object.assign(this, GradientLegend);
28
- }
29
-
30
- this.target = target;
31
- this.data = data;
32
- this.options = options;
33
- this.listeners = listeners;
34
-
35
- this.wrapperDOM = document.createElement('div');
36
- this.wrapperDOM.className = 'ev-chart-wrapper';
37
- this.chartDOM = document.createElement('div');
38
- this.chartDOM.className = 'ev-chart-container';
39
- this.wrapperDOM.appendChild(this.chartDOM);
40
- this.target.appendChild(this.wrapperDOM);
41
-
42
- this.displayCanvas = document.createElement('canvas');
43
- this.displayCanvas.setAttribute('style', 'display: block;');
44
- this.displayCtx = this.displayCanvas.getContext('2d');
45
- this.bufferCanvas = document.createElement('canvas');
46
- this.bufferCanvas.setAttribute('style', 'display: block;');
47
- this.bufferCtx = this.bufferCanvas.getContext('2d');
48
- this.overlayCanvas = document.createElement('canvas');
49
- this.overlayCanvas.setAttribute('style', 'display: block;');
50
- this.overlayCtx = this.overlayCanvas.getContext('2d');
51
-
52
- this.pixelRatio = window.devicePixelRatio || 1;
53
- this.oldPixelRatio = this.pixelRatio;
54
-
55
- this.chartDOM.appendChild(this.displayCanvas);
56
- this.chartDOM.appendChild(this.overlayCanvas);
57
-
58
- this.overlayCanvas.style.position = 'absolute';
59
- this.overlayCanvas.style.top = '0px';
60
- this.overlayCanvas.style.left = '0px';
61
-
62
- this.isInitLegend = false;
63
- this.isInitTitle = false;
64
- this.isInit = false;
65
- this.seriesList = {};
66
- this.lastTip = { pos: null, value: null };
67
- this.seriesInfo = {
68
- charts: { pie: [], bar: [], line: [], scatter: [], heatMap: [] },
69
- count: 0,
70
- };
71
-
72
- this.defaultSelectItemInfo = defaultSelectItemInfo;
73
- this.defaultSelectInfo = defaultSelectInfo;
74
- }
75
-
76
- /**
77
- * Initialize chart object
78
- *
79
- * @returns {undefined}
80
- */
81
- init() {
82
- const { series, data, labels, groups } = this.data;
83
- const { type, axesX, axesY, tooltip, horizontal } = this.options;
84
-
85
- this.createSeriesSet(series, type, horizontal);
86
- if (groups.length) {
87
- this.addGroupInfo(groups);
88
- }
89
-
90
- this.createDataSet(data, labels);
91
- this.minMax = this.getStoreMinMax();
92
-
93
- this.initRect();
94
-
95
- this.axesX = this.createAxes('x', axesX);
96
- this.axesY = this.createAxes('y', axesY);
97
-
98
- this.axesRange = this.getAxesRange();
99
- this.labelOffset = this.getLabelOffset();
100
- this.initSelectedInfo();
101
-
102
- this.drawChart();
103
-
104
- if (tooltip.use) {
105
- this.createTooltipDOM();
106
-
107
- if (tooltip.throttledMove) {
108
- this.onMouseMove = throttle(this.onMouseMove, 30);
109
- }
110
- }
111
-
112
- this.createEventFunctions();
113
- this.isInit = true;
114
- }
115
-
116
- /**
117
- * Initialize chart rectangle
118
- *
119
- * @returns {undefined}
120
- */
121
- initRect() {
122
- const opt = this.options;
123
- if (opt.title.show) {
124
- if (!this.isInitTitle) {
125
- this.initTitle();
126
- }
127
-
128
- this.showTitle();
129
- }
130
-
131
- if (opt.legend.show) {
132
- if (!this.isInitLegend) {
133
- this.initLegend();
134
- }
135
-
136
- this.setLegendPosition();
137
- }
138
-
139
- this.chartRect = this.getChartRect();
140
- }
141
-
142
- /**
143
- * To draw canvas chart, it processes several sequential jobs
144
- * @param {any} [hitInfo=undefined] from mousemove callback (object or undefined)
145
- *
146
- * @returns {undefined}
147
- */
148
- drawChart(hitInfo) {
149
- this.initScale();
150
- this.labelRange = this.getAxesLabelRange();
151
- this.axesSteps = this.calculateSteps();
152
- this.drawAxis(hitInfo);
153
- this.drawSeries(hitInfo);
154
- this.drawTip();
155
- if (this.bufferCanvas) {
156
- this.displayCtx.drawImage(this.bufferCanvas, 0, 0);
157
- }
158
- }
159
-
160
- /**
161
- * Draw each series
162
- * @param {any} [hitInfo=undefined] legend mouseover callback (object or undefined)
163
- *
164
- * @returns {undefined}
165
- */
166
- drawSeries(hitInfo) {
167
- const { maxTip, selectLabel, selectItem, selectSeries } = this.options;
168
-
169
- const opt = {
170
- ctx: this.bufferCtx,
171
- chartRect: this.chartRect,
172
- labelOffset: this.labelOffset,
173
- axesSteps: this.axesSteps,
174
- maxTipOpt: { background: maxTip.background, color: maxTip.color },
175
- selectLabel: { option: selectLabel, selected: this.defaultSelectInfo },
176
- selectSeries: { option: selectSeries, selected: this.defaultSelectInfo },
177
- overlayCtx: this.overlayCtx,
178
- };
179
-
180
- let showIndex = 0;
181
- let showSeriesCount = 0;
182
-
183
- this.seriesInfo.charts.bar.forEach((series) => {
184
- if (this.seriesList[series].show) {
185
- showSeriesCount++;
186
- }
187
- });
188
-
189
- const chartKeys = Object.keys(this.seriesInfo.charts);
190
- for (let ix = 0; ix < chartKeys.length; ix++) {
191
- const chartType = chartKeys[ix];
192
- const chartTypeSet = this.seriesInfo.charts[chartType];
193
-
194
- for (let jx = 0; jx < chartTypeSet.length; jx++) {
195
- const series = this.seriesList[chartTypeSet[jx]];
196
-
197
- switch (chartType) {
198
- case 'line':
199
- case 'heatMap': {
200
- const legendHitInfo = hitInfo?.legend;
201
-
202
- series.draw({
203
- legendHitInfo,
204
- ...opt,
205
- });
206
- break;
207
- }
208
- case 'bar': {
209
- const legendHitInfo = hitInfo?.legend;
210
- const { thickness, cPadRatio, borderRadius } = this.options;
211
-
212
- series.draw({
213
- thickness,
214
- cPadRatio,
215
- borderRadius,
216
- showSeriesCount,
217
- showIndex,
218
- legendHitInfo,
219
- ...opt,
220
- });
221
-
222
- if (series.show) {
223
- showIndex++;
224
- }
225
- break;
226
- }
227
- case 'pie': {
228
- const selectInfo = this.lastHitInfo ?? { sId: this.defaultSelectItemInfo?.seriesID };
229
- const legendHitInfo = hitInfo?.legend;
230
-
231
- if (this.options.sunburst) {
232
- this.drawSunburst({
233
- selectInfo,
234
- legendHitInfo,
235
- });
236
- } else {
237
- this.drawPie({
238
- selectInfo,
239
- legendHitInfo,
240
- });
241
- }
242
-
243
- if (this.options.doughnutHoleSize > 0) {
244
- this.drawDoughnutHole();
245
- }
246
- break;
247
- }
248
- case 'scatter': {
249
- const legendHitInfo = hitInfo?.legend;
250
-
251
- let selectInfo;
252
- if (selectItem.use && selectItem.useSeriesOpacity) {
253
- const lastHitInfo = this.lastHitInfo;
254
- const defaultSelectInfo = this.defaultSelectItemInfo;
255
-
256
- if (lastHitInfo?.maxIndex || lastHitInfo?.maxIndex === 0) {
257
- selectInfo = {
258
- seriesID: lastHitInfo.sId,
259
- dataIndex: lastHitInfo.maxIndex,
260
- };
261
- } else if (defaultSelectInfo?.dataIndex || defaultSelectInfo?.dataIndex === 0) {
262
- selectInfo = { ...defaultSelectInfo };
263
- } else {
264
- selectInfo = null;
265
- }
266
- }
267
-
268
- series.draw({
269
- legendHitInfo,
270
- selectInfo,
271
- ...opt,
272
- });
273
- break;
274
- }
275
- default: {
276
- break;
277
- }
278
- }
279
- }
280
- }
281
- }
282
-
283
- /**
284
- * Draw Tip with hitInfo and defaultSelectItemInfo
285
- */
286
- drawTip() {
287
- let tipLocationInfo;
288
-
289
- if (this.lastHitInfo) {
290
- tipLocationInfo = this.lastHitInfo;
291
- } else if (this.defaultSelectItemInfo) {
292
- tipLocationInfo = this.getItem(this.defaultSelectItemInfo, false);
293
- } else {
294
- tipLocationInfo = null;
295
- }
296
-
297
- this.drawTips(tipLocationInfo);
298
- }
299
-
300
- /**
301
- * Create axes
302
- * @param {string} dir axis direction
303
- * @param {array} axes axes array
304
- *
305
- * @returns {array} axes objects in array
306
- */
307
- createAxes(dir, axes = []) {
308
- const ctx = this.bufferCtx;
309
- const labels = this.options.type === 'heatMap'
310
- ? this.data.labels[dir]
311
- : this.data.labels;
312
- const options = this.options;
313
- return axes.map((axis) => {
314
- switch (axis.type) {
315
- case 'linear':
316
- return new LinearScale(dir, axis, ctx, options);
317
- case 'time':
318
- if (axis.categoryMode) {
319
- return new TimeCategoryScale(dir, axis, ctx, labels, options);
320
- }
321
- return new TimeScale(dir, axis, ctx, options);
322
- case 'log':
323
- return new LogarithmicScale(dir, axis, ctx);
324
- case 'step':
325
- return new StepScale(dir, axis, ctx, labels, options);
326
- default:
327
- return false;
328
- }
329
- });
330
- }
331
-
332
- /**
333
- * Calculate min/max value, label and size information for each axis
334
- *
335
- * @returns {object} axes min/max information
336
- */
337
- getAxesRange() {
338
- /* eslint-disable max-len */
339
- const axesXMinMax = this.axesX.map((axis, index) => axis.calculateScaleRange(this.minMax.x[index], this.chartRect));
340
- const axesYMinMax = this.axesY.map((axis, index) => axis.calculateScaleRange(this.minMax.y[index], this.chartRect));
341
- /* eslint-enable max-len */
342
-
343
- return { x: axesXMinMax, y: axesYMinMax };
344
- }
345
-
346
- /**
347
- * Draw each axis
348
- *
349
- * @returns {undefined}
350
- */
351
- drawAxis(hitInfo) {
352
- this.axesX.forEach((axis, index) => {
353
- axis.draw(
354
- this.chartRect,
355
- this.labelOffset,
356
- this.axesSteps.x[index],
357
- hitInfo,
358
- this.defaultSelectInfo);
359
- });
360
-
361
- this.axesY.forEach((axis, index) => {
362
- axis.draw(
363
- this.chartRect,
364
- this.labelOffset,
365
- this.axesSteps.y[index],
366
- hitInfo,
367
- this.defaultSelectInfo);
368
- });
369
- }
370
-
371
- /**
372
- * With each axis's min/max value and label information, calculate how many labels in each axis
373
- *
374
- * @returns {object} each axis's label steps in axes array
375
- */
376
- calculateSteps() {
377
- const axesXMinMax = this.axesX.map((axis, index) => {
378
- const range = {
379
- minValue: this.axesRange.x[index].min,
380
- maxValue: this.axesRange.x[index].max,
381
- minSteps: this.labelRange.x[index].min,
382
- maxSteps: this.labelRange.x[index].max,
383
- };
384
- return axis.calculateSteps(range);
385
- });
386
-
387
- const axesYMinMax = this.axesY.map((axis, index) => {
388
- const range = {
389
- minValue: this.axesRange.y[index].min,
390
- maxValue: this.axesRange.y[index].max,
391
- minSteps: this.labelRange.y[index].min,
392
- maxSteps: this.labelRange.y[index].max,
393
- };
394
- return axis.calculateSteps(range);
395
- });
396
-
397
- return { x: axesXMinMax, y: axesYMinMax };
398
- }
399
-
400
- /**
401
- * Calculate axis's min/max label steps
402
- *
403
- * @returns {object} axes's label range
404
- */
405
- getAxesLabelRange() {
406
- const axesXSteps = this.axesX.map((axis, index) => {
407
- const size = this.axesRange.x[index].size;
408
- return axis.calculateLabelRange('x', this.chartRect, this.labelOffset, size.width);
409
- });
410
-
411
- const axesYSteps = this.axesY.map((axis, index) => {
412
- const size = this.axesRange.y[index].size;
413
- return axis.calculateLabelRange('y', this.chartRect, this.labelOffset, size.height);
414
- });
415
-
416
- return { x: axesXSteps, y: axesYSteps };
417
- }
418
-
419
- /**
420
- * Reset devicePixelRatio for high DPI
421
- *
422
- * @returns {undefined}
423
- */
424
- initScale() {
425
- const devicePixelRatio = window.devicePixelRatio || 1;
426
- const backingStoreRatio = this.displayCtx.webkitBackingStorePixelRatio
427
- || this.displayCtx.mozBackingStorePixelRatio
428
- || this.displayCtx.msBackingStorePixelRatio
429
- || this.displayCtx.oBackingStorePixelRatio
430
- || this.displayCtx.backingStorePixelRatio
431
- || 1;
432
-
433
- this.pixelRatio = devicePixelRatio / backingStoreRatio;
434
-
435
- if (this.oldPixelRatio !== this.pixelRatio) {
436
- this.oldPixelRatio = this.pixelRatio;
437
- }
438
-
439
- this.bufferCtx.scale(this.pixelRatio, this.pixelRatio);
440
- this.overlayCtx.scale(this.pixelRatio, this.pixelRatio);
441
- }
442
-
443
- /**
444
- * Get chart DOM size and set canvas size
445
- *
446
- * @returns {object} chart size information
447
- */
448
- getChartDOMRect() {
449
- const rect = this.chartDOM?.getBoundingClientRect();
450
- const width = rect?.width || 10;
451
- const height = rect?.height || 10;
452
-
453
- this.setWidth(width);
454
- this.setHeight(height);
455
-
456
- return { width, height };
457
- }
458
-
459
- /**
460
- * Calculate chart size
461
- *
462
- * @returns {object} chart size information
463
- */
464
- getChartRect() {
465
- const { width, height } = this.getChartDOMRect();
466
-
467
- const padding = this.options.padding;
468
- const xAxisTitleOpt = this.options.axesX?.[0]?.title;
469
- const yAxisTitleOpt = this.options.axesY?.[0]?.title;
470
- const titleMargin = 10;
471
-
472
- let xAxisTitleHeight = 0;
473
- if (xAxisTitleOpt?.use && xAxisTitleOpt?.text) {
474
- const fontSize = isNaN(xAxisTitleOpt?.fontSize) ? 12 : xAxisTitleOpt?.fontSize;
475
- xAxisTitleHeight = fontSize + titleMargin;
476
- }
477
-
478
- let yAxisTitleHeight = 0;
479
- if (yAxisTitleOpt?.use && yAxisTitleOpt?.text) {
480
- const fontSize = isNaN(yAxisTitleOpt?.fontSize) ? 12 : yAxisTitleOpt?.fontSize;
481
- yAxisTitleHeight = fontSize + titleMargin;
482
- }
483
-
484
- const horizontalPadding = padding.left + padding.right;
485
- const verticalPadding = padding.top + padding.bottom + xAxisTitleHeight + yAxisTitleHeight;
486
- const chartWidth = width > horizontalPadding ? width - horizontalPadding : width;
487
- const chartHeight = height > verticalPadding ? height - verticalPadding : height;
488
-
489
- const x1 = padding.left;
490
- const x2 = Math.max(width - padding.right, x1 + 2);
491
- const y1 = padding.top + yAxisTitleHeight;
492
- const y2 = Math.max(height - padding.bottom - xAxisTitleHeight, y1 + 2);
493
-
494
- return {
495
- x1,
496
- x2,
497
- y1,
498
- y2,
499
- chartWidth,
500
- chartHeight,
501
- width,
502
- height,
503
- };
504
- }
505
-
506
- /**
507
- * Set canvas width
508
- * @param {number} width canvas width from chartDOM.width
509
- *
510
- * @returns {undefined}
511
- */
512
- setWidth(width) {
513
- if (!this.displayCanvas) {
514
- return;
515
- }
516
-
517
- this.displayCanvas.width = width * this.pixelRatio;
518
- this.displayCanvas.style.width = `${width}px`;
519
- this.bufferCanvas.width = width * this.pixelRatio;
520
- this.bufferCanvas.style.width = `${width}px`;
521
- this.overlayCanvas.width = width * this.pixelRatio;
522
- this.overlayCanvas.style.width = `${width}px`;
523
- }
524
-
525
- /**
526
- * Set canvas height
527
- * @param {number} height canvas width from chartDOM.height
528
- *
529
- * @returns {undefined}
530
- */
531
- setHeight(height) {
532
- if (!this.displayCanvas) {
533
- return;
534
- }
535
-
536
- this.displayCanvas.height = height * this.pixelRatio;
537
- this.displayCanvas.style.height = `${height}px`;
538
- this.bufferCanvas.height = height * this.pixelRatio;
539
- this.bufferCanvas.style.height = `${height}px`;
540
- this.overlayCanvas.height = height * this.pixelRatio;
541
- this.overlayCanvas.style.height = `${height}px`;
542
- }
543
-
544
- /**
545
- * Calculate labels offset from chart rect (Axis 영역을 벗어나는 label 크기 계산)
546
- *
547
- * ex)
548
- * Y축 label의 넓이와 (X축 최소값 label 넓이 / 2) 중 넓은 값이 left label offset으로 처리됨
549
- *
550
- * 0 |
551
- * |
552
- * |
553
- * 0 ----------------------
554
- * hh:mm hh:mm
555
- *
556
- * @returns {object} label offset for edge
557
- */
558
- getLabelOffset() {
559
- const axesX = this.axesX;
560
- const axesY = this.axesY;
561
- const range = this.axesRange;
562
- const labelOffset = { top: 2, left: 2, right: 2, bottom: 2 };
563
- const labelBuffer = { width: 14, height: 4 };
564
-
565
- let lw = 0;
566
- let lh = 0;
567
-
568
- axesX.forEach((axis, index) => {
569
- if (axis.labelStyle?.show) {
570
- lw = range.x[index].size.width + labelBuffer.width;
571
- lh = range.x[index].size.height + labelBuffer.height;
572
-
573
- if (axis.position === 'bottom') {
574
- if (lh > labelOffset.bottom) {
575
- labelOffset.bottom = lh;
576
- }
577
- } else if (axis.position === 'top') {
578
- if (lh > labelOffset.top) {
579
- labelOffset.top = lh;
580
- }
581
- }
582
-
583
- labelOffset.left = (lw / 2) > labelOffset.left ? (lw / 2) : labelOffset.left;
584
- labelOffset.right = (lw / 2) > labelOffset.right ? (lw / 2) : labelOffset.right;
585
- }
586
- });
587
-
588
- axesY.forEach((axis, index) => {
589
- if (axis.labelStyle?.show) {
590
- lw = Math.max(range.y[index].size.width + labelBuffer.width, 42 + labelBuffer.width);
591
-
592
- if (axis.position === 'left') {
593
- if (lw > labelOffset.left) {
594
- labelOffset.left = lw;
595
- }
596
- } else if (axis.position === 'right') {
597
- if (lw > labelOffset.right) {
598
- labelOffset.right = lw;
599
- }
600
- }
601
-
602
- labelOffset.top = (lh / 2) > labelOffset.top ? (lh / 2) : labelOffset.top;
603
- labelOffset.bottom = (lh / 2) > labelOffset.bottom ? (lh / 2) : labelOffset.bottom;
604
- }
605
- });
606
-
607
- return labelOffset;
608
- }
609
-
610
- /**
611
- * To re-render chart, reset properties, canvas and then render chart.
612
- * @param {object} updateInfo information for each components are needed to update
613
- *
614
- * @returns {undefined}
615
- */
616
- update(updateInfo) {
617
- const options = this.options;
618
- const data = this.data.data;
619
- const labels = this.data.labels;
620
- const groups = this.data.groups;
621
- const series = this.data.series;
622
-
623
- const { updateSeries, updateSelTip, updateLegend, updateData } = updateInfo;
624
-
625
- if (!this.isInit) {
626
- return;
627
- }
628
-
629
- this.resetProps();
630
-
631
- if (updateSeries) {
632
- this.seriesInfo = null;
633
- this.seriesList = null;
634
- this.lastTip = null;
635
-
636
- this.seriesInfo = {
637
- charts: {
638
- pie: [],
639
- bar: [],
640
- line: [],
641
- scatter: [],
642
- heatMap: [],
643
- },
644
- count: 0,
645
- };
646
- this.seriesList = {};
647
- this.lastTip = { pos: null, value: null };
648
-
649
- this.createSeriesSet(series, options.type, options.horizontal);
650
-
651
- if (this.legendDOM) {
652
- this.updateLegend();
653
- }
654
- }
655
-
656
- if (updateSelTip.update) {
657
- this.lastTip.value = null;
658
-
659
- if (!updateSelTip.keepDomain) {
660
- this.lastTip.pos = null;
661
- this.lastHitInfo = null;
662
- }
663
- }
664
-
665
- if (groups.length) {
666
- this.addGroupInfo(groups);
667
- }
668
-
669
- this.createDataSet(data, labels);
670
-
671
- // title update
672
- if (options.title.show) {
673
- if (!this.isInitTitle) {
674
- this.initTitle();
675
- }
676
-
677
- this.showTitle();
678
- } else if (this.isInitTitle) {
679
- this.hideTitle();
680
- }
681
-
682
- if (options.legend.show) {
683
- const useTable = !!options.legend?.table?.use
684
- && options.type !== 'heatmap'
685
- && options.type !== 'scatter';
686
-
687
- if (!this.isInitLegend) {
688
- this.initLegend();
689
- } else if (updateSeries) {
690
- this.updateLegend();
691
- } else if (updateLegend) {
692
- this.forceUpdateLegend();
693
- } else if (useTable && updateData) {
694
- this.updateLegendTableValues();
695
- }
696
-
697
- this.setLegendPosition();
698
- this.updateLegendContainerSize();
699
- this.showLegend();
700
- } else if (this.isInitLegend) {
701
- this.hideLegend();
702
- }
703
- this.chartRect = this.getChartRect();
704
-
705
- this.minMax = this.getStoreMinMax();
706
- this.axesX = this.createAxes('x', options.axesX);
707
- this.axesY = this.createAxes('y', options.axesY);
708
- this.axesRange = this.getAxesRange();
709
- this.labelOffset = this.getLabelOffset();
710
- this.initSelectedInfo();
711
-
712
- this.render(updateInfo?.hitInfo);
713
-
714
- const isDragMove = this.dragInfo && this.drawSelectionArea;
715
- if (isDragMove) {
716
- this.drawSelectionArea(this.dragInfo);
717
- } else if (this.dragInfoBackup) {
718
- this.dragInfoBackup = null;
719
- }
720
- }
721
-
722
- /**
723
- * To re-render chart, reset properties
724
- *
725
- * @returns {undefined}
726
- */
727
- resetProps() {
728
- this.axesX[0] = null;
729
- this.axesY[0] = null;
730
- this.axesX = null;
731
- this.axesY = null;
732
- this.minMax = null;
733
- this.axesRange = null;
734
- this.labelOffset = null;
735
- this.chartRect = null;
736
- this.pieDataSet = [];
737
- }
738
-
739
- /**
740
- * Clear overlay canvas
741
- *
742
- * @returns {undefined}
743
- */
744
- overlayClear() {
745
- this.clearRectRatio = (this.pixelRatio < 1) ? this.pixelRatio : 1;
746
-
747
- this.overlayCtx.clearRect(0, 0, this.overlayCanvas.width / this.clearRectRatio,
748
- this.overlayCanvas.height / this.clearRectRatio);
749
- }
750
-
751
- /**
752
- * Clear display and buffer canvas
753
- *
754
- * @returns {undefined}
755
- */
756
- clear() {
757
- this.clearRectRatio = (this.pixelRatio < 1) ? this.pixelRatio : 1;
758
- if (this.displayCanvas) {
759
- this.displayCtx.clearRect(0, 0, this.displayCanvas.width / this.clearRectRatio,
760
- this.displayCanvas.height / this.clearRectRatio);
761
- }
762
- if (this.bufferCanvas) {
763
- this.bufferCtx.clearRect(0, 0, this.bufferCanvas.width / this.clearRectRatio,
764
- this.bufferCanvas.height / this.clearRectRatio);
765
- }
766
- if (this.overlayCanvas) {
767
- this.overlayCtx.clearRect(0, 0, this.overlayCanvas.width / this.clearRectRatio,
768
- this.overlayCanvas.height / this.clearRectRatio);
769
- }
770
- }
771
-
772
- /**
773
- * Resize chart
774
- *
775
- * @returns {undefined}
776
- */
777
- resize() {
778
- this.clear();
779
- this.bufferCtx.restore();
780
- this.bufferCtx.save();
781
-
782
- this.initRect();
783
- this.initScale();
784
- this.chartRect = this.getChartRect();
785
- this.drawChart();
786
- }
787
-
788
- /**
789
- * Render chart
790
- * @param {any} [hitInfo=undefined] hit item from mouse click/dblclick
791
- *
792
- * @returns {undefined}
793
- */
794
- render(hitInfo) {
795
- if (this.isInit) {
796
- this.clear();
797
- this.chartRect = this.getChartRect();
798
- this.drawChart(hitInfo);
799
- }
800
- }
801
-
802
- /**
803
- * destroy chart component
804
- *
805
- * @returns {undefined}
806
- */
807
- destroy() {
808
- if (!this.isInit) {
809
- return;
810
- }
811
-
812
- const target = this.target;
813
-
814
- if (this.options.legend.show) {
815
- if (this.legendBoxDOM) {
816
- this.legendBoxDOM.removeEventListener('click', this.onLegendBoxClick);
817
- this.legendBoxDOM.removeEventListener('mouseover', this.onLegendBoxOver);
818
- this.legendBoxDOM.removeEventListener('mouseleave', this.onLegendBoxLeave);
819
- }
820
-
821
- if (this.resizeDOM) {
822
- this.resizeDOM.removeEventListener('mousedown', this.onResizeMouseDown);
823
- }
824
- }
825
-
826
- if (this.overlayCanvas) {
827
- this.overlayCanvas.removeEventListener('mousemove', this.onMouseMove);
828
- this.overlayCanvas.removeEventListener('mouseleave', this.onMouseLeave);
829
- this.overlayCanvas.removeEventListener('dblclick', this.onDblClick);
830
- this.overlayCanvas.removeEventListener('click', this.onClick);
831
- this.overlayCanvas.removeEventListener('mousedown', this.onMouseDown);
832
- this.overlayCanvas.removeEventListener('wheel', this.onWheel);
833
- }
834
-
835
- if (this.options.tooltip.use) {
836
- this.tooltipCanvas.remove();
837
- this.tooltipCanvas = null;
838
- this.tooltipDOM.remove();
839
- this.tooltipDOM = null;
840
- }
841
-
842
- this.wrapperDOM = null;
843
- this.chartDOM = null;
844
- this.legendDOM = null;
845
- this.legendBoxDOM = null;
846
- this.resizeDOM = null;
847
- this.ghostDOM = null;
848
- this.titleDOM = null;
849
- this.displayCanvas = null;
850
- this.bufferCanvas = null;
851
- this.overlayCanvas = null;
852
-
853
- while (target.hasChildNodes()) {
854
- target.removeChild(target.firstChild);
855
- }
856
- }
857
-
858
- /**
859
- * hide chart tooltip
860
- *
861
- * @returns {undefined}
862
- */
863
- hideTooltip() {
864
- if (this.options.tooltip.use && this.tooltipDOM?.style) {
865
- this.tooltipDOM.style.display = 'none';
866
- }
867
- }
868
- }
869
-
870
- export default EvChart;
1
+ import throttle from '@/common/utils.throttle';
2
+ import Model from './model';
3
+ import TimeScale from './scale/scale.time';
4
+ import LinearScale from './scale/scale.linear';
5
+ import LogarithmicScale from './scale/scale.logarithmic';
6
+ import StepScale from './scale/scale.step';
7
+ import TimeCategoryScale from './scale/scale.time.category';
8
+ import Title from './plugins/plugins.title';
9
+ import Legend from './plugins/plugins.legend';
10
+ import GradientLegend from './plugins/plugins.legend.gradient';
11
+ import Interaction from './plugins/plugins.interaction';
12
+ import Tooltip from './plugins/plugins.tooltip';
13
+ import Pie from './plugins/plugins.pie';
14
+ import Tip from './element/element.tip';
15
+
16
+ class EvChart {
17
+ constructor(target, data, options, listeners, defaultSelectItemInfo, defaultSelectInfo) {
18
+ Object.keys(Model).forEach(key => Object.assign(this, Model[key]));
19
+ Object.assign(this, Title);
20
+ Object.assign(this, Legend);
21
+ Object.assign(this, Interaction);
22
+ Object.assign(this, Tooltip);
23
+ Object.assign(this, Pie);
24
+ Object.assign(this, Tip);
25
+
26
+ if (options.type === 'heatMap' && options.legend.type === 'gradient') {
27
+ Object.assign(this, GradientLegend);
28
+ }
29
+
30
+ this.target = target;
31
+ this.data = data;
32
+ this.options = options;
33
+ this.listeners = listeners;
34
+
35
+ this.wrapperDOM = document.createElement('div');
36
+ this.wrapperDOM.className = 'ev-chart-wrapper';
37
+ this.chartDOM = document.createElement('div');
38
+ this.chartDOM.className = 'ev-chart-container';
39
+ this.wrapperDOM.appendChild(this.chartDOM);
40
+ this.target.appendChild(this.wrapperDOM);
41
+
42
+ this.displayCanvas = document.createElement('canvas');
43
+ this.displayCanvas.setAttribute('style', 'display: block;');
44
+ this.displayCtx = this.displayCanvas.getContext('2d');
45
+ this.bufferCanvas = document.createElement('canvas');
46
+ this.bufferCanvas.setAttribute('style', 'display: block;');
47
+ this.bufferCtx = this.bufferCanvas.getContext('2d');
48
+ this.overlayCanvas = document.createElement('canvas');
49
+ this.overlayCanvas.setAttribute('style', 'display: block;');
50
+ this.overlayCtx = this.overlayCanvas.getContext('2d');
51
+
52
+ this.pixelRatio = window.devicePixelRatio || 1;
53
+ this.oldPixelRatio = this.pixelRatio;
54
+
55
+ this.chartDOM.appendChild(this.displayCanvas);
56
+ this.chartDOM.appendChild(this.overlayCanvas);
57
+
58
+ this.overlayCanvas.style.position = 'absolute';
59
+ this.overlayCanvas.style.top = '0px';
60
+ this.overlayCanvas.style.left = '0px';
61
+
62
+ this.isInitLegend = false;
63
+ this.isInitTitle = false;
64
+ this.isInit = false;
65
+ this.seriesList = {};
66
+ this.lastTip = { pos: null, value: null };
67
+ this.seriesInfo = {
68
+ charts: { pie: [], bar: [], line: [], scatter: [], heatMap: [] },
69
+ count: 0,
70
+ };
71
+
72
+ this.defaultSelectItemInfo = defaultSelectItemInfo;
73
+ this.defaultSelectInfo = defaultSelectInfo;
74
+ }
75
+
76
+ /**
77
+ * Initialize chart object
78
+ *
79
+ * @returns {undefined}
80
+ */
81
+ init() {
82
+ const { series, data, labels, groups } = this.data;
83
+ const { type, axesX, axesY, tooltip, horizontal } = this.options;
84
+
85
+ this.createSeriesSet(series, type, horizontal);
86
+ if (groups.length) {
87
+ this.addGroupInfo(groups);
88
+ }
89
+
90
+ this.createDataSet(data, labels);
91
+ this.minMax = this.getStoreMinMax();
92
+
93
+ this.initRect();
94
+
95
+ this.axesX = this.createAxes('x', axesX);
96
+ this.axesY = this.createAxes('y', axesY);
97
+
98
+ this.axesRange = this.getAxesRange();
99
+ this.labelOffset = this.getLabelOffset();
100
+ this.initSelectedInfo();
101
+
102
+ this.drawChart();
103
+
104
+ if (tooltip.use) {
105
+ this.createTooltipDOM();
106
+
107
+ if (tooltip.throttledMove) {
108
+ this.onMouseMove = throttle(this.onMouseMove, 30);
109
+ }
110
+ }
111
+
112
+ this.createEventFunctions();
113
+ this.isInit = true;
114
+ }
115
+
116
+ /**
117
+ * Initialize chart rectangle
118
+ *
119
+ * @returns {undefined}
120
+ */
121
+ initRect() {
122
+ const opt = this.options;
123
+ if (opt.title.show) {
124
+ if (!this.isInitTitle) {
125
+ this.initTitle();
126
+ }
127
+
128
+ this.showTitle();
129
+ }
130
+
131
+ if (opt.legend.show) {
132
+ if (!this.isInitLegend) {
133
+ this.initLegend();
134
+ }
135
+
136
+ this.setLegendPosition();
137
+ }
138
+
139
+ this.chartRect = this.getChartRect();
140
+ }
141
+
142
+ /**
143
+ * To draw canvas chart, it processes several sequential jobs
144
+ * @param {any} [hitInfo=undefined] from mousemove callback (object or undefined)
145
+ *
146
+ * @returns {undefined}
147
+ */
148
+ drawChart(hitInfo) {
149
+ this.initScale();
150
+ this.labelRange = this.getAxesLabelRange();
151
+ this.axesSteps = this.calculateSteps();
152
+ this.drawAxis(hitInfo);
153
+ this.drawSeries(hitInfo);
154
+ this.drawTip();
155
+ if (this.bufferCanvas) {
156
+ this.displayCtx.drawImage(this.bufferCanvas, 0, 0);
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Draw each series
162
+ * @param {any} [hitInfo=undefined] legend mouseover callback (object or undefined)
163
+ *
164
+ * @returns {undefined}
165
+ */
166
+ drawSeries(hitInfo) {
167
+ const { maxTip, selectLabel, selectItem, selectSeries } = this.options;
168
+
169
+ const opt = {
170
+ ctx: this.bufferCtx,
171
+ chartRect: this.chartRect,
172
+ labelOffset: this.labelOffset,
173
+ axesSteps: this.axesSteps,
174
+ maxTipOpt: { background: maxTip.background, color: maxTip.color },
175
+ selectLabel: { option: selectLabel, selected: this.defaultSelectInfo },
176
+ selectSeries: { option: selectSeries, selected: this.defaultSelectInfo },
177
+ overlayCtx: this.overlayCtx,
178
+ };
179
+
180
+ let showIndex = 0;
181
+ let showSeriesCount = 0;
182
+
183
+ this.seriesInfo.charts.bar.forEach((series) => {
184
+ if (this.seriesList[series].show) {
185
+ showSeriesCount++;
186
+ }
187
+ });
188
+
189
+ const chartKeys = Object.keys(this.seriesInfo.charts);
190
+ for (let ix = 0; ix < chartKeys.length; ix++) {
191
+ const chartType = chartKeys[ix];
192
+ const chartTypeSet = this.seriesInfo.charts[chartType];
193
+
194
+ for (let jx = 0; jx < chartTypeSet.length; jx++) {
195
+ const series = this.seriesList[chartTypeSet[jx]];
196
+
197
+ switch (chartType) {
198
+ case 'line':
199
+ case 'heatMap': {
200
+ const legendHitInfo = hitInfo?.legend;
201
+
202
+ series.draw({
203
+ legendHitInfo,
204
+ ...opt,
205
+ });
206
+ break;
207
+ }
208
+ case 'bar': {
209
+ const legendHitInfo = hitInfo?.legend;
210
+ const { thickness, cPadRatio, borderRadius } = this.options;
211
+
212
+ series.draw({
213
+ thickness,
214
+ cPadRatio,
215
+ borderRadius,
216
+ showSeriesCount,
217
+ showIndex,
218
+ legendHitInfo,
219
+ ...opt,
220
+ });
221
+
222
+ if (series.show) {
223
+ showIndex++;
224
+ }
225
+ break;
226
+ }
227
+ case 'pie': {
228
+ const selectInfo = this.lastHitInfo ?? { sId: this.defaultSelectItemInfo?.seriesID };
229
+ const legendHitInfo = hitInfo?.legend;
230
+
231
+ if (this.options.sunburst) {
232
+ this.drawSunburst({
233
+ selectInfo,
234
+ legendHitInfo,
235
+ });
236
+ } else {
237
+ this.drawPie({
238
+ selectInfo,
239
+ legendHitInfo,
240
+ });
241
+ }
242
+
243
+ if (this.options.doughnutHoleSize > 0) {
244
+ this.drawDoughnutHole();
245
+ }
246
+ break;
247
+ }
248
+ case 'scatter': {
249
+ const legendHitInfo = hitInfo?.legend;
250
+
251
+ let selectInfo;
252
+ if (selectItem.use && selectItem.useSeriesOpacity) {
253
+ const lastHitInfo = this.lastHitInfo;
254
+ const defaultSelectInfo = this.defaultSelectItemInfo;
255
+
256
+ if (lastHitInfo?.maxIndex || lastHitInfo?.maxIndex === 0) {
257
+ selectInfo = {
258
+ seriesID: lastHitInfo.sId,
259
+ dataIndex: lastHitInfo.maxIndex,
260
+ };
261
+ } else if (defaultSelectInfo?.dataIndex || defaultSelectInfo?.dataIndex === 0) {
262
+ selectInfo = { ...defaultSelectInfo };
263
+ } else {
264
+ selectInfo = null;
265
+ }
266
+ }
267
+
268
+ series.draw({
269
+ legendHitInfo,
270
+ selectInfo,
271
+ ...opt,
272
+ });
273
+ break;
274
+ }
275
+ default: {
276
+ break;
277
+ }
278
+ }
279
+ }
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Draw Tip with hitInfo and defaultSelectItemInfo
285
+ */
286
+ drawTip() {
287
+ let tipLocationInfo;
288
+
289
+ if (this.lastHitInfo) {
290
+ tipLocationInfo = this.lastHitInfo;
291
+ } else if (this.defaultSelectItemInfo) {
292
+ tipLocationInfo = this.getItem(this.defaultSelectItemInfo, false);
293
+ } else {
294
+ tipLocationInfo = null;
295
+ }
296
+
297
+ this.drawTips(tipLocationInfo);
298
+ }
299
+
300
+ /**
301
+ * Create axes
302
+ * @param {string} dir axis direction
303
+ * @param {array} axes axes array
304
+ *
305
+ * @returns {array} axes objects in array
306
+ */
307
+ createAxes(dir, axes = []) {
308
+ const ctx = this.bufferCtx;
309
+ const labels = this.options.type === 'heatMap'
310
+ ? this.data.labels[dir]
311
+ : this.data.labels;
312
+ const options = this.options;
313
+ return axes.map((axis) => {
314
+ switch (axis.type) {
315
+ case 'linear':
316
+ return new LinearScale(dir, axis, ctx, options);
317
+ case 'time':
318
+ if (axis.categoryMode) {
319
+ return new TimeCategoryScale(dir, axis, ctx, labels, options);
320
+ }
321
+ return new TimeScale(dir, axis, ctx, options);
322
+ case 'log':
323
+ return new LogarithmicScale(dir, axis, ctx);
324
+ case 'step':
325
+ return new StepScale(dir, axis, ctx, labels, options);
326
+ default:
327
+ return false;
328
+ }
329
+ });
330
+ }
331
+
332
+ /**
333
+ * Calculate min/max value, label and size information for each axis
334
+ *
335
+ * @returns {object} axes min/max information
336
+ */
337
+ getAxesRange() {
338
+ /* eslint-disable max-len */
339
+ const axesXMinMax = this.axesX.map((axis, index) => axis.calculateScaleRange(this.minMax.x[index], this.chartRect));
340
+ const axesYMinMax = this.axesY.map((axis, index) => axis.calculateScaleRange(this.minMax.y[index], this.chartRect));
341
+ /* eslint-enable max-len */
342
+
343
+ return { x: axesXMinMax, y: axesYMinMax };
344
+ }
345
+
346
+ /**
347
+ * Draw each axis
348
+ *
349
+ * @returns {undefined}
350
+ */
351
+ drawAxis(hitInfo) {
352
+ this.axesX.forEach((axis, index) => {
353
+ axis.draw(
354
+ this.chartRect,
355
+ this.labelOffset,
356
+ this.axesSteps.x[index],
357
+ hitInfo,
358
+ this.defaultSelectInfo);
359
+ });
360
+
361
+ this.axesY.forEach((axis, index) => {
362
+ axis.draw(
363
+ this.chartRect,
364
+ this.labelOffset,
365
+ this.axesSteps.y[index],
366
+ hitInfo,
367
+ this.defaultSelectInfo);
368
+ });
369
+ }
370
+
371
+ /**
372
+ * With each axis's min/max value and label information, calculate how many labels in each axis
373
+ *
374
+ * @returns {object} each axis's label steps in axes array
375
+ */
376
+ calculateSteps() {
377
+ const axesXMinMax = this.axesX.map((axis, index) => {
378
+ const range = {
379
+ minValue: this.axesRange.x[index].min,
380
+ maxValue: this.axesRange.x[index].max,
381
+ minSteps: this.labelRange.x[index].min,
382
+ maxSteps: this.labelRange.x[index].max,
383
+ };
384
+ return axis.calculateSteps(range);
385
+ });
386
+
387
+ const axesYMinMax = this.axesY.map((axis, index) => {
388
+ const range = {
389
+ minValue: this.axesRange.y[index].min,
390
+ maxValue: this.axesRange.y[index].max,
391
+ minSteps: this.labelRange.y[index].min,
392
+ maxSteps: this.labelRange.y[index].max,
393
+ };
394
+ return axis.calculateSteps(range);
395
+ });
396
+
397
+ return { x: axesXMinMax, y: axesYMinMax };
398
+ }
399
+
400
+ /**
401
+ * Calculate axis's min/max label steps
402
+ *
403
+ * @returns {object} axes's label range
404
+ */
405
+ getAxesLabelRange() {
406
+ const axesXSteps = this.axesX.map((axis, index) => {
407
+ const size = this.axesRange.x[index].size;
408
+ return axis.calculateLabelRange('x', this.chartRect, this.labelOffset, size.width);
409
+ });
410
+
411
+ const axesYSteps = this.axesY.map((axis, index) => {
412
+ const size = this.axesRange.y[index].size;
413
+ return axis.calculateLabelRange('y', this.chartRect, this.labelOffset, size.height);
414
+ });
415
+
416
+ return { x: axesXSteps, y: axesYSteps };
417
+ }
418
+
419
+ /**
420
+ * Reset devicePixelRatio for high DPI
421
+ *
422
+ * @returns {undefined}
423
+ */
424
+ initScale() {
425
+ const devicePixelRatio = window.devicePixelRatio || 1;
426
+ const backingStoreRatio = this.displayCtx.webkitBackingStorePixelRatio
427
+ || this.displayCtx.mozBackingStorePixelRatio
428
+ || this.displayCtx.msBackingStorePixelRatio
429
+ || this.displayCtx.oBackingStorePixelRatio
430
+ || this.displayCtx.backingStorePixelRatio
431
+ || 1;
432
+
433
+ this.pixelRatio = devicePixelRatio / backingStoreRatio;
434
+
435
+ if (this.oldPixelRatio !== this.pixelRatio) {
436
+ this.oldPixelRatio = this.pixelRatio;
437
+ }
438
+
439
+ this.bufferCtx.scale(this.pixelRatio, this.pixelRatio);
440
+ this.overlayCtx.scale(this.pixelRatio, this.pixelRatio);
441
+ }
442
+
443
+ /**
444
+ * Get chart DOM size and set canvas size
445
+ *
446
+ * @returns {object} chart size information
447
+ */
448
+ getChartDOMRect() {
449
+ const rect = this.chartDOM?.getBoundingClientRect();
450
+ const width = rect?.width || 10;
451
+ const height = rect?.height || 10;
452
+
453
+ this.setWidth(width);
454
+ this.setHeight(height);
455
+
456
+ return { width, height };
457
+ }
458
+
459
+ /**
460
+ * Calculate chart size
461
+ *
462
+ * @returns {object} chart size information
463
+ */
464
+ getChartRect() {
465
+ const { width, height } = this.getChartDOMRect();
466
+
467
+ const padding = this.options.padding;
468
+ const xAxisTitleOpt = this.options.axesX?.[0]?.title;
469
+ const yAxisTitleOpt = this.options.axesY?.[0]?.title;
470
+ const titleMargin = 10;
471
+
472
+ let xAxisTitleHeight = 0;
473
+ if (xAxisTitleOpt?.use && xAxisTitleOpt?.text) {
474
+ const fontSize = isNaN(xAxisTitleOpt?.fontSize) ? 12 : xAxisTitleOpt?.fontSize;
475
+ xAxisTitleHeight = fontSize + titleMargin;
476
+ }
477
+
478
+ let yAxisTitleHeight = 0;
479
+ if (yAxisTitleOpt?.use && yAxisTitleOpt?.text) {
480
+ const fontSize = isNaN(yAxisTitleOpt?.fontSize) ? 12 : yAxisTitleOpt?.fontSize;
481
+ yAxisTitleHeight = fontSize + titleMargin;
482
+ }
483
+
484
+ const horizontalPadding = padding.left + padding.right;
485
+ const verticalPadding = padding.top + padding.bottom + xAxisTitleHeight + yAxisTitleHeight;
486
+ const chartWidth = width > horizontalPadding ? width - horizontalPadding : width;
487
+ const chartHeight = height > verticalPadding ? height - verticalPadding : height;
488
+
489
+ const x1 = padding.left;
490
+ const x2 = Math.max(width - padding.right, x1 + 2);
491
+ const y1 = padding.top + yAxisTitleHeight;
492
+ const y2 = Math.max(height - padding.bottom - xAxisTitleHeight, y1 + 2);
493
+
494
+ return {
495
+ x1,
496
+ x2,
497
+ y1,
498
+ y2,
499
+ chartWidth,
500
+ chartHeight,
501
+ width,
502
+ height,
503
+ };
504
+ }
505
+
506
+ /**
507
+ * Set canvas width
508
+ * @param {number} width canvas width from chartDOM.width
509
+ *
510
+ * @returns {undefined}
511
+ */
512
+ setWidth(width) {
513
+ if (!this.displayCanvas) {
514
+ return;
515
+ }
516
+
517
+ this.displayCanvas.width = width * this.pixelRatio;
518
+ this.displayCanvas.style.width = `${width}px`;
519
+ this.bufferCanvas.width = width * this.pixelRatio;
520
+ this.bufferCanvas.style.width = `${width}px`;
521
+ this.overlayCanvas.width = width * this.pixelRatio;
522
+ this.overlayCanvas.style.width = `${width}px`;
523
+ }
524
+
525
+ /**
526
+ * Set canvas height
527
+ * @param {number} height canvas width from chartDOM.height
528
+ *
529
+ * @returns {undefined}
530
+ */
531
+ setHeight(height) {
532
+ if (!this.displayCanvas) {
533
+ return;
534
+ }
535
+
536
+ this.displayCanvas.height = height * this.pixelRatio;
537
+ this.displayCanvas.style.height = `${height}px`;
538
+ this.bufferCanvas.height = height * this.pixelRatio;
539
+ this.bufferCanvas.style.height = `${height}px`;
540
+ this.overlayCanvas.height = height * this.pixelRatio;
541
+ this.overlayCanvas.style.height = `${height}px`;
542
+ }
543
+
544
+ /**
545
+ * Calculate labels offset from chart rect (Axis 영역을 벗어나는 label 크기 계산)
546
+ *
547
+ * ex)
548
+ * Y축 label의 넓이와 (X축 최소값 label 넓이 / 2) 중 넓은 값이 left label offset으로 처리됨
549
+ *
550
+ * 0 |
551
+ * |
552
+ * |
553
+ * 0 ----------------------
554
+ * hh:mm hh:mm
555
+ *
556
+ * @returns {object} label offset for edge
557
+ */
558
+ getLabelOffset() {
559
+ const axesX = this.axesX;
560
+ const axesY = this.axesY;
561
+ const range = this.axesRange;
562
+ const labelOffset = { top: 2, left: 2, right: 2, bottom: 2 };
563
+ const labelBuffer = { width: 14, height: 4 };
564
+
565
+ let lw = 0;
566
+ let lh = 0;
567
+
568
+ axesX.forEach((axis, index) => {
569
+ if (axis.labelStyle?.show) {
570
+ lw = range.x[index].size.width + labelBuffer.width;
571
+ lh = range.x[index].size.height + labelBuffer.height;
572
+
573
+ if (axis.position === 'bottom') {
574
+ if (lh > labelOffset.bottom) {
575
+ labelOffset.bottom = lh;
576
+ }
577
+ } else if (axis.position === 'top') {
578
+ if (lh > labelOffset.top) {
579
+ labelOffset.top = lh;
580
+ }
581
+ }
582
+
583
+ labelOffset.left = (lw / 2) > labelOffset.left ? (lw / 2) : labelOffset.left;
584
+ labelOffset.right = (lw / 2) > labelOffset.right ? (lw / 2) : labelOffset.right;
585
+ }
586
+ });
587
+
588
+ axesY.forEach((axis, index) => {
589
+ if (axis.labelStyle?.show) {
590
+ lw = Math.max(range.y[index].size.width + labelBuffer.width, 42 + labelBuffer.width);
591
+
592
+ if (axis.position === 'left') {
593
+ if (lw > labelOffset.left) {
594
+ labelOffset.left = lw;
595
+ }
596
+ } else if (axis.position === 'right') {
597
+ if (lw > labelOffset.right) {
598
+ labelOffset.right = lw;
599
+ }
600
+ }
601
+
602
+ labelOffset.top = (lh / 2) > labelOffset.top ? (lh / 2) : labelOffset.top;
603
+ labelOffset.bottom = (lh / 2) > labelOffset.bottom ? (lh / 2) : labelOffset.bottom;
604
+ }
605
+ });
606
+
607
+ return labelOffset;
608
+ }
609
+
610
+ /**
611
+ * To re-render chart, reset properties, canvas and then render chart.
612
+ * @param {object} updateInfo information for each components are needed to update
613
+ *
614
+ * @returns {undefined}
615
+ */
616
+ update(updateInfo) {
617
+ const options = this.options;
618
+ const data = this.data.data;
619
+ const labels = this.data.labels;
620
+ const groups = this.data.groups;
621
+ const series = this.data.series;
622
+
623
+ const { updateSeries, updateSelTip, updateLegend, updateData } = updateInfo;
624
+
625
+ if (!this.isInit) {
626
+ return;
627
+ }
628
+
629
+ this.resetProps();
630
+
631
+ if (updateSeries) {
632
+ this.seriesInfo = null;
633
+ this.seriesList = null;
634
+ this.lastTip = null;
635
+
636
+ this.seriesInfo = {
637
+ charts: {
638
+ pie: [],
639
+ bar: [],
640
+ line: [],
641
+ scatter: [],
642
+ heatMap: [],
643
+ },
644
+ count: 0,
645
+ };
646
+ this.seriesList = {};
647
+ this.lastTip = { pos: null, value: null };
648
+
649
+ this.createSeriesSet(series, options.type, options.horizontal);
650
+
651
+ if (this.legendDOM) {
652
+ this.updateLegend();
653
+ }
654
+ }
655
+
656
+ if (updateSelTip.update) {
657
+ this.lastTip.value = null;
658
+
659
+ if (!updateSelTip.keepDomain) {
660
+ this.lastTip.pos = null;
661
+ this.lastHitInfo = null;
662
+ }
663
+ }
664
+
665
+ if (groups.length) {
666
+ this.addGroupInfo(groups);
667
+ }
668
+
669
+ this.createDataSet(data, labels);
670
+
671
+ // title update
672
+ if (options.title.show) {
673
+ if (!this.isInitTitle) {
674
+ this.initTitle();
675
+ }
676
+
677
+ this.showTitle();
678
+ } else if (this.isInitTitle) {
679
+ this.hideTitle();
680
+ }
681
+
682
+ if (options.legend.show) {
683
+ const useTable = !!options.legend?.table?.use
684
+ && options.type !== 'heatmap'
685
+ && options.type !== 'scatter';
686
+
687
+ if (!this.isInitLegend) {
688
+ this.initLegend();
689
+ } else if (updateSeries) {
690
+ this.updateLegend();
691
+ } else if (updateLegend) {
692
+ this.forceUpdateLegend();
693
+ } else if (useTable && updateData) {
694
+ this.updateLegendTableValues();
695
+ }
696
+
697
+ this.setLegendPosition();
698
+ this.updateLegendContainerSize();
699
+ this.showLegend();
700
+ } else if (this.isInitLegend) {
701
+ this.hideLegend();
702
+ }
703
+ this.chartRect = this.getChartRect();
704
+
705
+ this.minMax = this.getStoreMinMax();
706
+ this.axesX = this.createAxes('x', options.axesX);
707
+ this.axesY = this.createAxes('y', options.axesY);
708
+ this.axesRange = this.getAxesRange();
709
+ this.labelOffset = this.getLabelOffset();
710
+ this.initSelectedInfo();
711
+
712
+ this.render(updateInfo?.hitInfo);
713
+
714
+ const isDragMove = this.dragInfo && this.drawSelectionArea;
715
+ if (isDragMove) {
716
+ this.drawSelectionArea(this.dragInfo);
717
+ } else if (this.dragInfoBackup) {
718
+ this.dragInfoBackup = null;
719
+ }
720
+ }
721
+
722
+ /**
723
+ * To re-render chart, reset properties
724
+ *
725
+ * @returns {undefined}
726
+ */
727
+ resetProps() {
728
+ this.axesX[0] = null;
729
+ this.axesY[0] = null;
730
+ this.axesX = null;
731
+ this.axesY = null;
732
+ this.minMax = null;
733
+ this.axesRange = null;
734
+ this.labelOffset = null;
735
+ this.chartRect = null;
736
+ this.pieDataSet = [];
737
+ }
738
+
739
+ /**
740
+ * Clear overlay canvas
741
+ *
742
+ * @returns {undefined}
743
+ */
744
+ overlayClear() {
745
+ this.clearRectRatio = (this.pixelRatio < 1) ? this.pixelRatio : 1;
746
+
747
+ this.overlayCtx.clearRect(0, 0, this.overlayCanvas.width / this.clearRectRatio,
748
+ this.overlayCanvas.height / this.clearRectRatio);
749
+ }
750
+
751
+ /**
752
+ * Clear display and buffer canvas
753
+ *
754
+ * @returns {undefined}
755
+ */
756
+ clear() {
757
+ this.clearRectRatio = (this.pixelRatio < 1) ? this.pixelRatio : 1;
758
+ if (this.displayCanvas) {
759
+ this.displayCtx.clearRect(0, 0, this.displayCanvas.width / this.clearRectRatio,
760
+ this.displayCanvas.height / this.clearRectRatio);
761
+ }
762
+ if (this.bufferCanvas) {
763
+ this.bufferCtx.clearRect(0, 0, this.bufferCanvas.width / this.clearRectRatio,
764
+ this.bufferCanvas.height / this.clearRectRatio);
765
+ }
766
+ if (this.overlayCanvas) {
767
+ this.overlayCtx.clearRect(0, 0, this.overlayCanvas.width / this.clearRectRatio,
768
+ this.overlayCanvas.height / this.clearRectRatio);
769
+ }
770
+ }
771
+
772
+ /**
773
+ * Resize chart
774
+ *
775
+ * @returns {undefined}
776
+ */
777
+ resize() {
778
+ this.clear();
779
+ this.bufferCtx.restore();
780
+ this.bufferCtx.save();
781
+
782
+ this.initRect();
783
+ this.initScale();
784
+ this.chartRect = this.getChartRect();
785
+ this.drawChart();
786
+ }
787
+
788
+ /**
789
+ * Render chart
790
+ * @param {any} [hitInfo=undefined] hit item from mouse click/dblclick
791
+ *
792
+ * @returns {undefined}
793
+ */
794
+ render(hitInfo) {
795
+ if (this.isInit) {
796
+ this.clear();
797
+ this.chartRect = this.getChartRect();
798
+ this.drawChart(hitInfo);
799
+ }
800
+ }
801
+
802
+ /**
803
+ * destroy chart component
804
+ *
805
+ * @returns {undefined}
806
+ */
807
+ destroy() {
808
+ if (!this.isInit) {
809
+ return;
810
+ }
811
+
812
+ const target = this.target;
813
+
814
+ if (this.options.legend.show) {
815
+ if (this.legendBoxDOM) {
816
+ this.legendBoxDOM.removeEventListener('click', this.onLegendBoxClick);
817
+ this.legendBoxDOM.removeEventListener('mouseover', this.onLegendBoxOver);
818
+ this.legendBoxDOM.removeEventListener('mouseleave', this.onLegendBoxLeave);
819
+ }
820
+
821
+ if (this.resizeDOM) {
822
+ this.resizeDOM.removeEventListener('mousedown', this.onResizeMouseDown);
823
+ }
824
+ }
825
+
826
+ if (this.overlayCanvas) {
827
+ this.overlayCanvas.removeEventListener('mousemove', this.onMouseMove);
828
+ this.overlayCanvas.removeEventListener('mouseleave', this.onMouseLeave);
829
+ this.overlayCanvas.removeEventListener('dblclick', this.onDblClick);
830
+ this.overlayCanvas.removeEventListener('click', this.onClick);
831
+ this.overlayCanvas.removeEventListener('mousedown', this.onMouseDown);
832
+ this.overlayCanvas.removeEventListener('wheel', this.onWheel);
833
+ }
834
+
835
+ if (this.options.tooltip.use) {
836
+ this.tooltipCanvas.remove();
837
+ this.tooltipCanvas = null;
838
+ this.tooltipDOM.remove();
839
+ this.tooltipDOM = null;
840
+ }
841
+
842
+ this.wrapperDOM = null;
843
+ this.chartDOM = null;
844
+ this.legendDOM = null;
845
+ this.legendBoxDOM = null;
846
+ this.resizeDOM = null;
847
+ this.ghostDOM = null;
848
+ this.titleDOM = null;
849
+ this.displayCanvas = null;
850
+ this.bufferCanvas = null;
851
+ this.overlayCanvas = null;
852
+
853
+ while (target.hasChildNodes()) {
854
+ target.removeChild(target.firstChild);
855
+ }
856
+ }
857
+
858
+ /**
859
+ * hide chart tooltip
860
+ *
861
+ * @returns {undefined}
862
+ */
863
+ hideTooltip() {
864
+ if (this.options.tooltip.use && this.tooltipDOM?.style) {
865
+ this.tooltipDOM.style.display = 'none';
866
+ }
867
+ }
868
+ }
869
+
870
+ export default EvChart;