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,769 +1,769 @@
1
- import { numberWithComma } from '@/common/utils';
2
- import { cloneDeep, defaultsDeep } from 'lodash-es';
3
-
4
- const modules = {
5
- /**
6
- * Hide legend components by manipulating css
7
- *
8
- * @returns {undefined}
9
- */
10
- createEventFunctions() {
11
- /**
12
- * To show tooltip and item highlighting, add event listener on mousemove
13
- *
14
- * @returns {undefined}
15
- */
16
- this.onMouseMove = (e) => {
17
- if (this.dragInfo?.isMove) {
18
- return;
19
- }
20
-
21
- const { indicator, tooltip, type } = this.options;
22
- const offset = this.getMousePosition(e);
23
- const hitInfo = this.findHitItem(offset);
24
-
25
- if (tooltip?.showAllValueInRange && hitInfo?.items) {
26
- this.addNotHitInfo(hitInfo);
27
- }
28
-
29
- const ctx = this.overlayCtx;
30
-
31
- this.overlayClear();
32
-
33
- if (Object.keys(hitInfo.items).length) {
34
- if ((type !== 'scatter' && type !== 'heatMap') || tooltip.use) {
35
- this.drawItemsHighlight(hitInfo, ctx);
36
- }
37
-
38
- if (tooltip.use) {
39
- this.setTooltipLayoutPosition(hitInfo, e);
40
- if (type === 'scatter') {
41
- this.drawTooltipForScatter(hitInfo, this.tooltipCtx);
42
- } else if (type === 'heatMap') {
43
- this.drawToolTipForHeatMap(hitInfo, this.tooltipCtx);
44
- } else {
45
- this.drawTooltip(hitInfo, this.tooltipCtx);
46
- }
47
- }
48
- } else if (tooltip.use) {
49
- this.hideTooltipDOM();
50
- }
51
-
52
- if (this.dragInfoBackup) {
53
- this.drawSelectionArea(this.dragInfoBackup);
54
- }
55
-
56
- if (indicator.use && type !== 'pie' && type !== 'scatter' && type !== 'heatMap') {
57
- this.drawIndicator(offset, indicator.color);
58
- }
59
- };
60
-
61
- /**
62
- * To clear tooltip and item highlighting, add event listener on mouseleave
63
- *
64
- * @returns {undefined}
65
- */
66
- this.onMouseLeave = () => {
67
- const { tooltip, dragSelection } = this.options;
68
-
69
- if (tooltip.throttledMove) {
70
- this.onMouseMove.cancel();
71
- }
72
-
73
- if (!dragSelection.use || !dragSelection.keepDisplay) {
74
- this.overlayClear();
75
- }
76
-
77
- if (tooltip.use) {
78
- this.tooltipClear();
79
- }
80
- };
81
-
82
- /**
83
- * Dealing with graph item select and invoking user custom dblclick event
84
- *
85
- * @returns {undefined}
86
- */
87
- this.onDblClick = (e) => {
88
- const selectItem = this.options.selectItem;
89
- const args = { e };
90
-
91
- if (selectItem.use) {
92
- const offset = this.getMousePosition(e);
93
- const hitInfo = this.getItemByPosition(offset, selectItem.useApproximateValue);
94
-
95
-
96
- if (hitInfo.label !== null) {
97
- this.render(hitInfo);
98
- }
99
-
100
- ({ label: args.label, value: args.value, sId: args.seriesId, acc: args.acc } = hitInfo);
101
- }
102
-
103
- if (typeof this.listeners['dbl-click'] === 'function') {
104
- this.listeners['dbl-click'](args);
105
- }
106
- };
107
-
108
- /**
109
- * Dealing with graph item select and invoking user custom click event
110
- *
111
- * @returns {undefined}
112
- */
113
- this.onClick = (e) => {
114
- const args = { e };
115
- if (this.options.selectItem.use && this.options.selectItem.useClick) {
116
- const offset = this.getMousePosition(e);
117
- const hitInfo = this.getItemByPosition(offset, false);
118
-
119
- if (hitInfo.label !== null) {
120
- this.render(hitInfo);
121
- }
122
-
123
- ({
124
- label: args.label,
125
- value: args.value,
126
- sId: args.seriesId,
127
- maxIndex: args.dataIndex,
128
- acc: args.acc,
129
- } = hitInfo);
130
- } else if (this.options.selectLabel.use && this.options.selectLabel.useClick) {
131
- const offset = this.getMousePosition(e);
132
- const clickedLabelInfo = this.getLabelInfoByPosition(offset);
133
- const selected = this.selectLabel(clickedLabelInfo.labelIndex);
134
- this.renderWithSelected(selected.dataIndex);
135
-
136
- args.selected = cloneDeep(this.defaultSelectInfo);
137
- } else if (this.options.selectSeries.use && this.options.selectSeries.useClick) {
138
- const offset = this.getMousePosition(e);
139
- const hitInfo = this.getSeriesIdByPosition(offset);
140
- if (hitInfo.sId !== null) {
141
- const selected = this.selectSeries(hitInfo.sId);
142
- this.renderWithSelected(selected.seriesId);
143
- }
144
-
145
- args.selected = cloneDeep(this.defaultSelectInfo);
146
- }
147
-
148
- if (typeof this.listeners.click === 'function') {
149
- if (!this.dragInfoBackup) {
150
- this.listeners.click(args);
151
- }
152
- }
153
- };
154
-
155
- /**
156
- * Start drag-select when dragSelection use option is True and graph type is 'scatter'
157
- *
158
- * @returns {undefined}
159
- */
160
- this.onMouseDown = (e) => {
161
- const { dragSelection, type } = this.options;
162
-
163
- if (dragSelection.use && (type === 'scatter' || type === 'line' || type === 'heatMap')) {
164
- this.removeSelectionArea();
165
- this.dragStart(e, type);
166
- }
167
- };
168
-
169
- this.onWheel = (e) => {
170
- const isTooltipVisible = this.tooltipDOM.style.display === 'block';
171
-
172
- if (isTooltipVisible) {
173
- e.preventDefault();
174
- this.tooltipBodyDOM.scrollTop += e.deltaY;
175
- }
176
- };
177
-
178
- if (this.options?.tooltip?.useScrollbar) {
179
- this.overlayCanvas.addEventListener('wheel', this.onWheel, { passive: false });
180
- }
181
-
182
- this.overlayCanvas.addEventListener('mousemove', this.onMouseMove);
183
- this.overlayCanvas.addEventListener('mouseleave', this.onMouseLeave);
184
- this.overlayCanvas.addEventListener('dblclick', this.onDblClick);
185
- this.overlayCanvas.addEventListener('click', this.onClick);
186
- this.overlayCanvas.addEventListener('mousedown', this.onMouseDown);
187
- },
188
-
189
- /**
190
- * Start drag-move when the mouse pointer is in the graph
191
- *
192
- * @returns {undefined}
193
- */
194
- dragStart(evt, type) {
195
- let [offsetX, offsetY] = this.getMousePosition(evt);
196
- const chartRect = this.chartRect;
197
- const labelOffset = this.labelOffset;
198
- const range = {
199
- x1: chartRect.x1 + labelOffset.left,
200
- x2: chartRect.x2 - labelOffset.right,
201
- y1: chartRect.y1 + labelOffset.top,
202
- y2: chartRect.y2 - labelOffset.bottom,
203
- };
204
-
205
- if (offsetX < range.x1) {
206
- offsetX = range.x1;
207
- }
208
-
209
- if (offsetX > range.x2) {
210
- offsetX = range.x2;
211
- }
212
-
213
- if (offsetY < range.y1) {
214
- offsetY = range.y1;
215
- }
216
-
217
- if (offsetY > range.y2) {
218
- offsetY = range.y2;
219
- }
220
-
221
- this.dragInfo = {
222
- xcp: offsetX,
223
- ycp: offsetY,
224
- range,
225
- };
226
-
227
- /**
228
- * Calculate drag-section position and size, and drawing drag-section
229
- *
230
- * @returns {undefined}
231
- */
232
- const dragMove = (e) => {
233
- e.preventDefault();
234
- const [aOffsetX, aOffsetY] = this.getMousePosition(e);
235
- const dragInfo = this.dragInfo;
236
- const { xcp, ycp, range: aRange } = dragInfo;
237
-
238
- let xep;
239
- let yep;
240
-
241
- dragInfo.isMove = true;
242
-
243
- if (aOffsetX < aRange.x1) {
244
- xep = aRange.x1;
245
- } else if (aOffsetX > aRange.x2) {
246
- xep = aRange.x2;
247
- } else {
248
- xep = aOffsetX;
249
- }
250
-
251
- if (aOffsetY < aRange.y1) {
252
- yep = range.y1;
253
- } else if (aOffsetY > aRange.y2) {
254
- yep = aRange.y2;
255
- } else {
256
- yep = aOffsetY;
257
- }
258
-
259
- if (type === 'heatMap') {
260
- const rangeInfo = { xcp, xep, ycp, yep, range: aRange };
261
- const { xsp, ysp, width, height } = this.getDragInfoForHeatMap(rangeInfo);
262
- dragInfo.xsp = xsp;
263
- dragInfo.ysp = ysp;
264
- dragInfo.width = width;
265
- dragInfo.height = height;
266
- } else {
267
- dragInfo.xsp = Math.min(xcp, xep);
268
- dragInfo.ysp = type === 'scatter' ? Math.min(ycp, yep) : aRange.y1;
269
- dragInfo.width = Math.ceil(Math.abs(xep - xcp));
270
- dragInfo.height = type === 'scatter'
271
- ? Math.ceil(Math.abs(yep - ycp))
272
- : aRange.y2 - aRange.y1;
273
- }
274
-
275
- this.overlayClear();
276
- this.drawSelectionArea(dragInfo);
277
- };
278
-
279
- /**
280
- * invoking user custom click event width items and range in drag-section
281
- *
282
- * @returns {undefined}
283
- */
284
- const dragEnd = (e) => {
285
- const dragInfo = this.dragInfo;
286
-
287
- if (dragInfo?.isMove && dragInfo?.width > 1 && dragInfo?.height > 1) {
288
- const args = {
289
- e,
290
- data: this.findSelectedItems(dragInfo),
291
- range: type === 'heatMap'
292
- ? this.getSelectionRangeForHeatMap(dragInfo)
293
- : this.getSelectionRage(dragInfo),
294
- };
295
-
296
- this.dragInfoBackup = defaultsDeep({}, dragInfo);
297
-
298
- if (typeof this.listeners['drag-select'] === 'function') {
299
- this.listeners['drag-select'](args);
300
- }
301
- }
302
-
303
- if (!this.options.dragSelection.keepDisplay) {
304
- this.removeSelectionArea();
305
- }
306
-
307
- this.dragInfo = null;
308
-
309
- window.removeEventListener('mousemove', dragMove);
310
- window.removeEventListener('mouseup', dragEnd);
311
- };
312
-
313
- window.addEventListener('mousemove', dragMove);
314
- window.addEventListener('mouseup', dragEnd);
315
- },
316
-
317
- /**
318
- * Draw selection-area
319
- *
320
- * @returns {undefined}
321
- */
322
- drawSelectionArea({ xsp, ysp, width, height }) {
323
- const ctx = this.overlayCtx;
324
- const { fillColor, opacity } = this.options.dragSelection;
325
-
326
- ctx.fillStyle = fillColor;
327
- ctx.globalAlpha = opacity;
328
- ctx.fillRect(xsp, ysp, width, height);
329
- ctx.globalAlpha = 1;
330
- },
331
-
332
- /** Remove drag selection area
333
- *
334
- */
335
- removeSelectionArea() {
336
- this.dragInfoBackup = null;
337
- this.overlayClear();
338
- },
339
-
340
- /**
341
- * Computing mouse position on canvas
342
- *
343
- * @returns {array} mouse pointer position
344
- */
345
- getMousePosition(evt) {
346
- const e = evt.originalEvent || evt;
347
- const rect = this.overlayCanvas.getBoundingClientRect();
348
- return [e.clientX - rect.left, e.clientY - rect.top, rect.width, rect.height];
349
- },
350
-
351
- /**
352
- * Find graph item on mouse position
353
- * @param {array} offset return value from getMousePosition()
354
- *
355
- * @returns {object} hit item information
356
- */
357
- findHitItem(offset) {
358
- const sIds = Object.keys(this.seriesList);
359
- const items = {};
360
- const isHorizontal = !!this.options.horizontal;
361
- const ctx = this.tooltipCtx;
362
-
363
- let hitId = null;
364
- let maxs = '';
365
- let maxsw = 0;
366
- let maxv = '';
367
- let maxg = null;
368
- let maxSID = null;
369
-
370
- for (let ix = 0; ix < sIds.length; ix++) {
371
- const sId = sIds[ix];
372
- const series = this.seriesList[sId];
373
-
374
- if (series.findGraphData) {
375
- const item = series.findGraphData(offset, isHorizontal);
376
-
377
- if (item?.data) {
378
- let gdata;
379
-
380
- if (item.data.o === null) {
381
- if (!series.isExistGrp) {
382
- gdata = isHorizontal ? item.data.x : item.data.y;
383
- }
384
- } else if (!isNaN(item.data.o)) {
385
- gdata = item.data.o;
386
- }
387
-
388
- if (gdata !== null && gdata !== undefined) {
389
- const sName = series.name;
390
- const sw = ctx ? ctx.measureText(sName).width : 1;
391
-
392
- item.name = sName;
393
- item.axis = { x: series.xAxisIndex, y: series.yAxisIndex };
394
- items[sId] = item;
395
-
396
- const formattedTxt = this.getFormattedTooltipValue({
397
- seriesName: sName,
398
- value: gdata,
399
- itemData: item.data,
400
- });
401
-
402
- item.data.formatted = formattedTxt;
403
-
404
- if (maxsw < sw) {
405
- maxs = sName;
406
- maxsw = sw;
407
- }
408
-
409
- if (maxv.length <= `${formattedTxt}`.length) {
410
- maxv = `${formattedTxt}`;
411
- }
412
-
413
- if (maxg === null || maxg <= gdata) {
414
- maxg = gdata;
415
- maxSID = sId;
416
- }
417
-
418
- if (item.hit) {
419
- hitId = sId;
420
- }
421
- }
422
- }
423
- }
424
- }
425
-
426
- hitId = hitId === null ? Object.keys(items)[0] : hitId;
427
- const maxHighlight = maxg !== null ? [maxSID, maxg] : null;
428
-
429
- return { items, hitId, maxTip: [maxs, maxv], maxHighlight };
430
- },
431
-
432
- /**
433
- * get formatted value for tooltip
434
- * @param seriesName
435
- * @param value
436
- * @param x
437
- * @param y
438
- * @returns {string}
439
- */
440
- getFormattedTooltipValue({ seriesName, value, itemData }) {
441
- const opt = this.options;
442
- const isHorizontal = !!opt.horizontal;
443
- const tooltipOpt = opt.tooltip;
444
- const tooltipValueFormatter = typeof tooltipOpt?.formatter === 'function'
445
- ? tooltipOpt?.formatter
446
- : tooltipOpt?.formatter?.value;
447
-
448
- let formattedTxt = value;
449
- if (tooltipValueFormatter) {
450
- if (opt.type === 'pie') {
451
- formattedTxt = tooltipValueFormatter({
452
- value,
453
- name: seriesName,
454
- percentage: itemData?.percentage,
455
- });
456
- } else if (opt.type === 'heatMap') {
457
- formattedTxt = tooltipValueFormatter({
458
- x: itemData?.x,
459
- y: itemData?.y,
460
- value: value > -1 ? value : 'error',
461
- });
462
- } else {
463
- formattedTxt = tooltipValueFormatter({
464
- x: isHorizontal ? value : itemData?.x,
465
- y: isHorizontal ? itemData?.y : value,
466
- name: seriesName,
467
- });
468
- }
469
- }
470
-
471
- if (value && (!tooltipValueFormatter || typeof formattedTxt !== 'string')) {
472
- if (opt.type === 'heatMap') {
473
- formattedTxt = value < 0 ? 'error' : numberWithComma(value);
474
- } else {
475
- formattedTxt = numberWithComma(value);
476
- }
477
- }
478
-
479
- return formattedTxt;
480
- },
481
-
482
- /**
483
- * add not hit info
484
- * @param hitInfo
485
- */
486
- addNotHitInfo(hitInfo) {
487
- const ctx = this.tooltipCtx;
488
- const isHorizontal = !!this.options.horizontal;
489
- const hitItemId = Object.keys(hitInfo.items)[0];
490
- const hitItemData = isHorizontal
491
- ? hitInfo.items?.[hitItemId]?.data?.y : hitInfo.items?.[hitItemId]?.data?.x;
492
- let maxSeriesName = '';
493
- let maxValueTxt = '';
494
-
495
- const sIds = Object.keys(this.seriesList);
496
- for (let ix = 0; ix < sIds.length; ix++) {
497
- const sId = sIds[ix];
498
- const series = this.seriesList[sId];
499
-
500
- if (series?.show) {
501
- const hasData = series.data.find(data => (
502
- isHorizontal
503
- ? data?.y === hitItemData
504
- : data?.x === hitItemData
505
- ),
506
- );
507
-
508
- const formattedValue = this.getFormattedTooltipValue({
509
- seriesName: series.name,
510
- value: hasData?.o,
511
- itemData: hasData,
512
- });
513
-
514
- if (hasData && !hitInfo.items[sId]) {
515
- const item = {};
516
- item.color = series.color;
517
- item.hit = false;
518
- item.name = series.name;
519
- item.axis = { x: series.xAxisIndex, y: series.yAxisIndex };
520
- item.index = isHorizontal ? series.yAxisIndex : series.xAxisIndex;
521
- item.data = hasData;
522
- item.data.formatted = formattedValue;
523
-
524
- hitInfo.items[sId] = item;
525
- }
526
-
527
- const maxSeriesNameWidth = ctx ? ctx.measureText(maxSeriesName).width : 1;
528
- const seriesNameWidth = ctx ? ctx.measureText(series.name).width : 1;
529
- if (maxSeriesNameWidth < seriesNameWidth) {
530
- maxSeriesName = series.name;
531
- }
532
-
533
- const maxValueWidth = ctx ? ctx.measureText(maxValueTxt).width : 1;
534
- const valueWidth = ctx ? ctx.measureText(`${formattedValue}`).width : 1;
535
- if (maxValueWidth < valueWidth) {
536
- maxValueTxt = `${formattedValue}`;
537
- }
538
- }
539
- }
540
-
541
- hitInfo.maxTip = [maxSeriesName, maxValueTxt];
542
- },
543
-
544
- /**
545
- *
546
- * @param targetInfo {object} '{ dataIndex: number, seriesID: string }'
547
- * @param chartType {string} 'bar', 'line', 'pie', 'scatter'
548
- *
549
- * @returns {boolean}
550
- */
551
- selectItemByData(targetInfo, chartType) {
552
- this.defaultSelectItemInfo = targetInfo;
553
-
554
- let foundInfo;
555
- if (chartType === 'pie') {
556
- foundInfo = {
557
- type: 'pie',
558
- sId: targetInfo.seriesID,
559
- };
560
- } else {
561
- if (isNaN(targetInfo?.dataIndex)) {
562
- return false;
563
- }
564
-
565
- foundInfo = this.getItem(targetInfo, false);
566
- }
567
-
568
- if (foundInfo) {
569
- this.render(foundInfo);
570
- } else {
571
- return false;
572
- }
573
-
574
- return true;
575
- },
576
-
577
- /**
578
- * render after selected label or selected series
579
- * @param indexList {array} '[0, 1 ...]'
580
- */
581
- renderWithSelected(list) {
582
- if (this.options.selectLabel.use) {
583
- this.defaultSelectInfo.dataIndex = list;
584
- } else if (this.options.selectSeries.use) {
585
- this.defaultSelectInfo.seriesId = list;
586
- }
587
- this.initSelectedInfo();
588
- this.render();
589
- },
590
-
591
- /**
592
- * init defaultSelectInfo object.
593
- * - at selectLabel using: set each series data and label text
594
- * - at selectSeries using: set series state
595
- */
596
- initSelectedInfo() {
597
- if (this.options.selectLabel.use) {
598
- const { limit } = this.options.selectLabel;
599
- if (!this.defaultSelectInfo) {
600
- this.defaultSelectInfo = { dataIndex: [] };
601
- }
602
- const infoObj = this.defaultSelectInfo;
603
- infoObj.dataIndex.splice(limit);
604
- infoObj.label = infoObj.dataIndex.map(i => this.data.labels[i]);
605
- const dataEntries = Object.entries(this.data.data);
606
- infoObj.data = infoObj.dataIndex.map(labelIdx => Object.fromEntries(
607
- dataEntries.map(([sId, data]) => [sId, data[labelIdx]])));
608
- } else if (this.options.selectSeries.use) {
609
- if (!this.defaultSelectInfo) {
610
- this.defaultSelectInfo = { seriesId: [] };
611
- }
612
- }
613
- },
614
-
615
- /**
616
- * Add or delete selected label index, according to policy and option
617
- * (set each series data and label text)
618
- * @param labelIndex {array} '[0, 1 ...]'
619
- */
620
- selectLabel(labelIndex) {
621
- const option = this.options?.selectLabel ?? {};
622
- const before = this.defaultSelectInfo ?? { dataIndex: [] };
623
- const after = cloneDeep(before);
624
-
625
- if (before.dataIndex.includes(labelIndex)) {
626
- const idx = before.dataIndex.indexOf(labelIndex);
627
- after.dataIndex.splice(idx, 1);
628
- } else if (labelIndex > -1) {
629
- after.dataIndex.push(labelIndex);
630
- if (option.limit > 0 && option.limit < after.dataIndex.length) {
631
- if (option.useDeselectOverflow) {
632
- after.dataIndex.splice(0, 1);
633
- } else {
634
- after.dataIndex.pop();
635
- }
636
- }
637
- }
638
-
639
- return after;
640
- },
641
-
642
- selectSeries(seriesId) {
643
- const option = this.options?.selectSeries ?? {};
644
- const before = this.defaultSelectInfo ?? { seriesId: [] };
645
- const after = cloneDeep(before);
646
-
647
- if (before.seriesId.includes(seriesId)) {
648
- const idx = before.seriesId.indexOf(seriesId);
649
- after.seriesId.splice(idx, 1);
650
- } else if (seriesId) {
651
- after.seriesId.push(seriesId);
652
- if (option.limit > 0 && option.limit < after.seriesId.length) {
653
- if (option.useDeselectOverflow) {
654
- after.seriesId.splice(0, 1);
655
- } else {
656
- after.seriesId.pop();
657
- }
658
- }
659
- }
660
-
661
- return after;
662
- },
663
-
664
- /**
665
- * Find items by series within a range
666
- * @param {object} range object for find series items
667
- *
668
- * @returns {object}
669
- */
670
- findSelectedItems(range) {
671
- const items = [];
672
- const sIds = Object.keys(this.seriesList);
673
- for (let ix = 0; ix < sIds.length; ix++) {
674
- const sId = sIds[ix];
675
- const series = this.seriesList[sId];
676
- const findFn = series.findItems;
677
- if (findFn) {
678
- const item = findFn.call(series, range);
679
- if (item && item.length) {
680
- items.push({
681
- seriesName: series.name,
682
- seriesId: sId,
683
- items: item,
684
- });
685
- }
686
- }
687
- }
688
-
689
- return items;
690
- },
691
-
692
- /**
693
- * Returns the data-based range value for a selection
694
- * @param {object} object for calculating data-based range
695
- * object.range: coordinate-based range in graph
696
- * @returns {object}
697
- */
698
- getSelectionRage({ xsp, ysp, width, height, range }) {
699
- const dataRangeX = this.axesSteps.x.length ? this.axesSteps.x[0] : null;
700
- const dataRangeY = this.axesSteps.y.length ? this.axesSteps.y[0] : null;
701
-
702
- if (!dataRangeX || !dataRangeY) {
703
- return null;
704
- }
705
-
706
- const xep = xsp + width;
707
- const yep = ysp + height;
708
- const graphWidth = dataRangeX.graphMax - dataRangeX.graphMin;
709
- const graphHeight = dataRangeY.graphMax - dataRangeY.graphMin;
710
-
711
- const xMinRatio = this.getRatioInRange(range.x1, range.x2, xsp);
712
- const xMaxRatio = this.getRatioInRange(range.x1, range.x2, xep);
713
- const yMinRatio = this.getRatioInRange(range.y1, range.y2, yep);
714
- const yMaxRatio = this.getRatioInRange(range.y1, range.y2, ysp);
715
-
716
- const xMin = dataRangeX.graphMin + graphWidth * xMinRatio;
717
- const xMax = dataRangeX.graphMin + graphWidth * xMaxRatio;
718
- const yMin = dataRangeY.graphMin + graphHeight * (1 - yMinRatio);
719
- const yMax = dataRangeY.graphMin + graphHeight * (1 - yMaxRatio);
720
-
721
- return {
722
- xMin: Math.max(+parseFloat(xMin).toFixed(3), dataRangeX.graphMin),
723
- xMax: Math.min(+parseFloat(xMax).toFixed(3), dataRangeX.graphMax),
724
- yMin: Math.max(+parseFloat(yMin).toFixed(3), dataRangeY.graphMin),
725
- yMax: Math.min(+parseFloat(yMax).toFixed(3), dataRangeY.graphMax),
726
- };
727
- },
728
-
729
- /**
730
- * Returns the position ratio of 'value' between 'min' and 'max'
731
- * @param {number} min min value
732
- * @param {number} max max value
733
- * @param {number} value value is between min and max
734
- *
735
- * @returns {number}
736
- */
737
- getRatioInRange(min, max, value) {
738
- const total = max - min;
739
- const targetValue = value - min;
740
-
741
- return targetValue / total;
742
- },
743
-
744
- getDragInfoForHeatMap(range) {
745
- const sId = Object.keys(this.seriesList)[0];
746
- return this.seriesList[sId].findBlockRange(range);
747
- },
748
-
749
- getSelectionRangeForHeatMap(range) {
750
- const dataRangeX = this.axesSteps.x.length ? this.axesSteps.x[0] : null;
751
- const dataRangeY = this.axesSteps.y.length ? this.axesSteps.y[0] : null;
752
-
753
- if (!dataRangeX || !dataRangeY) {
754
- return null;
755
- }
756
-
757
- const sId = Object.keys(this.seriesList)[0];
758
- const { xMin, xMax, yMin, yMax } = this.seriesList[sId].findSelectionRange(range) ?? {};
759
-
760
- return {
761
- xMin: xMin ?? dataRangeX.graphMin,
762
- xMax: xMax ?? dataRangeX.graphMax,
763
- yMin: yMin ?? dataRangeY.graphMin,
764
- yMax: yMax ?? dataRangeY.graphMax,
765
- };
766
- },
767
- };
768
-
769
- export default modules;
1
+ import { numberWithComma } from '@/common/utils';
2
+ import { cloneDeep, defaultsDeep } from 'lodash-es';
3
+
4
+ const modules = {
5
+ /**
6
+ * Hide legend components by manipulating css
7
+ *
8
+ * @returns {undefined}
9
+ */
10
+ createEventFunctions() {
11
+ /**
12
+ * To show tooltip and item highlighting, add event listener on mousemove
13
+ *
14
+ * @returns {undefined}
15
+ */
16
+ this.onMouseMove = (e) => {
17
+ if (this.dragInfo?.isMove) {
18
+ return;
19
+ }
20
+
21
+ const { indicator, tooltip, type } = this.options;
22
+ const offset = this.getMousePosition(e);
23
+ const hitInfo = this.findHitItem(offset);
24
+
25
+ if (tooltip?.showAllValueInRange && hitInfo?.items) {
26
+ this.addNotHitInfo(hitInfo);
27
+ }
28
+
29
+ const ctx = this.overlayCtx;
30
+
31
+ this.overlayClear();
32
+
33
+ if (Object.keys(hitInfo.items).length) {
34
+ if ((type !== 'scatter' && type !== 'heatMap') || tooltip.use) {
35
+ this.drawItemsHighlight(hitInfo, ctx);
36
+ }
37
+
38
+ if (tooltip.use) {
39
+ this.setTooltipLayoutPosition(hitInfo, e);
40
+ if (type === 'scatter') {
41
+ this.drawTooltipForScatter(hitInfo, this.tooltipCtx);
42
+ } else if (type === 'heatMap') {
43
+ this.drawToolTipForHeatMap(hitInfo, this.tooltipCtx);
44
+ } else {
45
+ this.drawTooltip(hitInfo, this.tooltipCtx);
46
+ }
47
+ }
48
+ } else if (tooltip.use) {
49
+ this.hideTooltipDOM();
50
+ }
51
+
52
+ if (this.dragInfoBackup) {
53
+ this.drawSelectionArea(this.dragInfoBackup);
54
+ }
55
+
56
+ if (indicator.use && type !== 'pie' && type !== 'scatter' && type !== 'heatMap') {
57
+ this.drawIndicator(offset, indicator.color);
58
+ }
59
+ };
60
+
61
+ /**
62
+ * To clear tooltip and item highlighting, add event listener on mouseleave
63
+ *
64
+ * @returns {undefined}
65
+ */
66
+ this.onMouseLeave = () => {
67
+ const { tooltip, dragSelection } = this.options;
68
+
69
+ if (tooltip.throttledMove) {
70
+ this.onMouseMove.cancel();
71
+ }
72
+
73
+ if (!dragSelection.use || !dragSelection.keepDisplay) {
74
+ this.overlayClear();
75
+ }
76
+
77
+ if (tooltip.use) {
78
+ this.tooltipClear();
79
+ }
80
+ };
81
+
82
+ /**
83
+ * Dealing with graph item select and invoking user custom dblclick event
84
+ *
85
+ * @returns {undefined}
86
+ */
87
+ this.onDblClick = (e) => {
88
+ const selectItem = this.options.selectItem;
89
+ const args = { e };
90
+
91
+ if (selectItem.use) {
92
+ const offset = this.getMousePosition(e);
93
+ const hitInfo = this.getItemByPosition(offset, selectItem.useApproximateValue);
94
+
95
+
96
+ if (hitInfo.label !== null) {
97
+ this.render(hitInfo);
98
+ }
99
+
100
+ ({ label: args.label, value: args.value, sId: args.seriesId, acc: args.acc } = hitInfo);
101
+ }
102
+
103
+ if (typeof this.listeners['dbl-click'] === 'function') {
104
+ this.listeners['dbl-click'](args);
105
+ }
106
+ };
107
+
108
+ /**
109
+ * Dealing with graph item select and invoking user custom click event
110
+ *
111
+ * @returns {undefined}
112
+ */
113
+ this.onClick = (e) => {
114
+ const args = { e };
115
+ if (this.options.selectItem.use && this.options.selectItem.useClick) {
116
+ const offset = this.getMousePosition(e);
117
+ const hitInfo = this.getItemByPosition(offset, false);
118
+
119
+ if (hitInfo.label !== null) {
120
+ this.render(hitInfo);
121
+ }
122
+
123
+ ({
124
+ label: args.label,
125
+ value: args.value,
126
+ sId: args.seriesId,
127
+ maxIndex: args.dataIndex,
128
+ acc: args.acc,
129
+ } = hitInfo);
130
+ } else if (this.options.selectLabel.use && this.options.selectLabel.useClick) {
131
+ const offset = this.getMousePosition(e);
132
+ const clickedLabelInfo = this.getLabelInfoByPosition(offset);
133
+ const selected = this.selectLabel(clickedLabelInfo.labelIndex);
134
+ this.renderWithSelected(selected.dataIndex);
135
+
136
+ args.selected = cloneDeep(this.defaultSelectInfo);
137
+ } else if (this.options.selectSeries.use && this.options.selectSeries.useClick) {
138
+ const offset = this.getMousePosition(e);
139
+ const hitInfo = this.getSeriesIdByPosition(offset);
140
+ if (hitInfo.sId !== null) {
141
+ const selected = this.selectSeries(hitInfo.sId);
142
+ this.renderWithSelected(selected.seriesId);
143
+ }
144
+
145
+ args.selected = cloneDeep(this.defaultSelectInfo);
146
+ }
147
+
148
+ if (typeof this.listeners.click === 'function') {
149
+ if (!this.dragInfoBackup) {
150
+ this.listeners.click(args);
151
+ }
152
+ }
153
+ };
154
+
155
+ /**
156
+ * Start drag-select when dragSelection use option is True and graph type is 'scatter'
157
+ *
158
+ * @returns {undefined}
159
+ */
160
+ this.onMouseDown = (e) => {
161
+ const { dragSelection, type } = this.options;
162
+
163
+ if (dragSelection.use && (type === 'scatter' || type === 'line' || type === 'heatMap')) {
164
+ this.removeSelectionArea();
165
+ this.dragStart(e, type);
166
+ }
167
+ };
168
+
169
+ this.onWheel = (e) => {
170
+ const isTooltipVisible = this.tooltipDOM.style.display === 'block';
171
+
172
+ if (isTooltipVisible) {
173
+ e.preventDefault();
174
+ this.tooltipBodyDOM.scrollTop += e.deltaY;
175
+ }
176
+ };
177
+
178
+ if (this.options?.tooltip?.useScrollbar) {
179
+ this.overlayCanvas.addEventListener('wheel', this.onWheel, { passive: false });
180
+ }
181
+
182
+ this.overlayCanvas.addEventListener('mousemove', this.onMouseMove);
183
+ this.overlayCanvas.addEventListener('mouseleave', this.onMouseLeave);
184
+ this.overlayCanvas.addEventListener('dblclick', this.onDblClick);
185
+ this.overlayCanvas.addEventListener('click', this.onClick);
186
+ this.overlayCanvas.addEventListener('mousedown', this.onMouseDown);
187
+ },
188
+
189
+ /**
190
+ * Start drag-move when the mouse pointer is in the graph
191
+ *
192
+ * @returns {undefined}
193
+ */
194
+ dragStart(evt, type) {
195
+ let [offsetX, offsetY] = this.getMousePosition(evt);
196
+ const chartRect = this.chartRect;
197
+ const labelOffset = this.labelOffset;
198
+ const range = {
199
+ x1: chartRect.x1 + labelOffset.left,
200
+ x2: chartRect.x2 - labelOffset.right,
201
+ y1: chartRect.y1 + labelOffset.top,
202
+ y2: chartRect.y2 - labelOffset.bottom,
203
+ };
204
+
205
+ if (offsetX < range.x1) {
206
+ offsetX = range.x1;
207
+ }
208
+
209
+ if (offsetX > range.x2) {
210
+ offsetX = range.x2;
211
+ }
212
+
213
+ if (offsetY < range.y1) {
214
+ offsetY = range.y1;
215
+ }
216
+
217
+ if (offsetY > range.y2) {
218
+ offsetY = range.y2;
219
+ }
220
+
221
+ this.dragInfo = {
222
+ xcp: offsetX,
223
+ ycp: offsetY,
224
+ range,
225
+ };
226
+
227
+ /**
228
+ * Calculate drag-section position and size, and drawing drag-section
229
+ *
230
+ * @returns {undefined}
231
+ */
232
+ const dragMove = (e) => {
233
+ e.preventDefault();
234
+ const [aOffsetX, aOffsetY] = this.getMousePosition(e);
235
+ const dragInfo = this.dragInfo;
236
+ const { xcp, ycp, range: aRange } = dragInfo;
237
+
238
+ let xep;
239
+ let yep;
240
+
241
+ dragInfo.isMove = true;
242
+
243
+ if (aOffsetX < aRange.x1) {
244
+ xep = aRange.x1;
245
+ } else if (aOffsetX > aRange.x2) {
246
+ xep = aRange.x2;
247
+ } else {
248
+ xep = aOffsetX;
249
+ }
250
+
251
+ if (aOffsetY < aRange.y1) {
252
+ yep = range.y1;
253
+ } else if (aOffsetY > aRange.y2) {
254
+ yep = aRange.y2;
255
+ } else {
256
+ yep = aOffsetY;
257
+ }
258
+
259
+ if (type === 'heatMap') {
260
+ const rangeInfo = { xcp, xep, ycp, yep, range: aRange };
261
+ const { xsp, ysp, width, height } = this.getDragInfoForHeatMap(rangeInfo);
262
+ dragInfo.xsp = xsp;
263
+ dragInfo.ysp = ysp;
264
+ dragInfo.width = width;
265
+ dragInfo.height = height;
266
+ } else {
267
+ dragInfo.xsp = Math.min(xcp, xep);
268
+ dragInfo.ysp = type === 'scatter' ? Math.min(ycp, yep) : aRange.y1;
269
+ dragInfo.width = Math.ceil(Math.abs(xep - xcp));
270
+ dragInfo.height = type === 'scatter'
271
+ ? Math.ceil(Math.abs(yep - ycp))
272
+ : aRange.y2 - aRange.y1;
273
+ }
274
+
275
+ this.overlayClear();
276
+ this.drawSelectionArea(dragInfo);
277
+ };
278
+
279
+ /**
280
+ * invoking user custom click event width items and range in drag-section
281
+ *
282
+ * @returns {undefined}
283
+ */
284
+ const dragEnd = (e) => {
285
+ const dragInfo = this.dragInfo;
286
+
287
+ if (dragInfo?.isMove && dragInfo?.width > 1 && dragInfo?.height > 1) {
288
+ const args = {
289
+ e,
290
+ data: this.findSelectedItems(dragInfo),
291
+ range: type === 'heatMap'
292
+ ? this.getSelectionRangeForHeatMap(dragInfo)
293
+ : this.getSelectionRage(dragInfo),
294
+ };
295
+
296
+ this.dragInfoBackup = defaultsDeep({}, dragInfo);
297
+
298
+ if (typeof this.listeners['drag-select'] === 'function') {
299
+ this.listeners['drag-select'](args);
300
+ }
301
+ }
302
+
303
+ if (!this.options.dragSelection.keepDisplay) {
304
+ this.removeSelectionArea();
305
+ }
306
+
307
+ this.dragInfo = null;
308
+
309
+ window.removeEventListener('mousemove', dragMove);
310
+ window.removeEventListener('mouseup', dragEnd);
311
+ };
312
+
313
+ window.addEventListener('mousemove', dragMove);
314
+ window.addEventListener('mouseup', dragEnd);
315
+ },
316
+
317
+ /**
318
+ * Draw selection-area
319
+ *
320
+ * @returns {undefined}
321
+ */
322
+ drawSelectionArea({ xsp, ysp, width, height }) {
323
+ const ctx = this.overlayCtx;
324
+ const { fillColor, opacity } = this.options.dragSelection;
325
+
326
+ ctx.fillStyle = fillColor;
327
+ ctx.globalAlpha = opacity;
328
+ ctx.fillRect(xsp, ysp, width, height);
329
+ ctx.globalAlpha = 1;
330
+ },
331
+
332
+ /** Remove drag selection area
333
+ *
334
+ */
335
+ removeSelectionArea() {
336
+ this.dragInfoBackup = null;
337
+ this.overlayClear();
338
+ },
339
+
340
+ /**
341
+ * Computing mouse position on canvas
342
+ *
343
+ * @returns {array} mouse pointer position
344
+ */
345
+ getMousePosition(evt) {
346
+ const e = evt.originalEvent || evt;
347
+ const rect = this.overlayCanvas.getBoundingClientRect();
348
+ return [e.clientX - rect.left, e.clientY - rect.top, rect.width, rect.height];
349
+ },
350
+
351
+ /**
352
+ * Find graph item on mouse position
353
+ * @param {array} offset return value from getMousePosition()
354
+ *
355
+ * @returns {object} hit item information
356
+ */
357
+ findHitItem(offset) {
358
+ const sIds = Object.keys(this.seriesList);
359
+ const items = {};
360
+ const isHorizontal = !!this.options.horizontal;
361
+ const ctx = this.tooltipCtx;
362
+
363
+ let hitId = null;
364
+ let maxs = '';
365
+ let maxsw = 0;
366
+ let maxv = '';
367
+ let maxg = null;
368
+ let maxSID = null;
369
+
370
+ for (let ix = 0; ix < sIds.length; ix++) {
371
+ const sId = sIds[ix];
372
+ const series = this.seriesList[sId];
373
+
374
+ if (series.findGraphData) {
375
+ const item = series.findGraphData(offset, isHorizontal);
376
+
377
+ if (item?.data) {
378
+ let gdata;
379
+
380
+ if (item.data.o === null) {
381
+ if (!series.isExistGrp) {
382
+ gdata = isHorizontal ? item.data.x : item.data.y;
383
+ }
384
+ } else if (!isNaN(item.data.o)) {
385
+ gdata = item.data.o;
386
+ }
387
+
388
+ if (gdata !== null && gdata !== undefined) {
389
+ const sName = series.name;
390
+ const sw = ctx ? ctx.measureText(sName).width : 1;
391
+
392
+ item.name = sName;
393
+ item.axis = { x: series.xAxisIndex, y: series.yAxisIndex };
394
+ items[sId] = item;
395
+
396
+ const formattedTxt = this.getFormattedTooltipValue({
397
+ seriesName: sName,
398
+ value: gdata,
399
+ itemData: item.data,
400
+ });
401
+
402
+ item.data.formatted = formattedTxt;
403
+
404
+ if (maxsw < sw) {
405
+ maxs = sName;
406
+ maxsw = sw;
407
+ }
408
+
409
+ if (maxv.length <= `${formattedTxt}`.length) {
410
+ maxv = `${formattedTxt}`;
411
+ }
412
+
413
+ if (maxg === null || maxg <= gdata) {
414
+ maxg = gdata;
415
+ maxSID = sId;
416
+ }
417
+
418
+ if (item.hit) {
419
+ hitId = sId;
420
+ }
421
+ }
422
+ }
423
+ }
424
+ }
425
+
426
+ hitId = hitId === null ? Object.keys(items)[0] : hitId;
427
+ const maxHighlight = maxg !== null ? [maxSID, maxg] : null;
428
+
429
+ return { items, hitId, maxTip: [maxs, maxv], maxHighlight };
430
+ },
431
+
432
+ /**
433
+ * get formatted value for tooltip
434
+ * @param seriesName
435
+ * @param value
436
+ * @param x
437
+ * @param y
438
+ * @returns {string}
439
+ */
440
+ getFormattedTooltipValue({ seriesName, value, itemData }) {
441
+ const opt = this.options;
442
+ const isHorizontal = !!opt.horizontal;
443
+ const tooltipOpt = opt.tooltip;
444
+ const tooltipValueFormatter = typeof tooltipOpt?.formatter === 'function'
445
+ ? tooltipOpt?.formatter
446
+ : tooltipOpt?.formatter?.value;
447
+
448
+ let formattedTxt = value;
449
+ if (tooltipValueFormatter) {
450
+ if (opt.type === 'pie') {
451
+ formattedTxt = tooltipValueFormatter({
452
+ value,
453
+ name: seriesName,
454
+ percentage: itemData?.percentage,
455
+ });
456
+ } else if (opt.type === 'heatMap') {
457
+ formattedTxt = tooltipValueFormatter({
458
+ x: itemData?.x,
459
+ y: itemData?.y,
460
+ value: value > -1 ? value : 'error',
461
+ });
462
+ } else {
463
+ formattedTxt = tooltipValueFormatter({
464
+ x: isHorizontal ? value : itemData?.x,
465
+ y: isHorizontal ? itemData?.y : value,
466
+ name: seriesName,
467
+ });
468
+ }
469
+ }
470
+
471
+ if (value && (!tooltipValueFormatter || typeof formattedTxt !== 'string')) {
472
+ if (opt.type === 'heatMap') {
473
+ formattedTxt = value < 0 ? 'error' : numberWithComma(value);
474
+ } else {
475
+ formattedTxt = numberWithComma(value);
476
+ }
477
+ }
478
+
479
+ return formattedTxt;
480
+ },
481
+
482
+ /**
483
+ * add not hit info
484
+ * @param hitInfo
485
+ */
486
+ addNotHitInfo(hitInfo) {
487
+ const ctx = this.tooltipCtx;
488
+ const isHorizontal = !!this.options.horizontal;
489
+ const hitItemId = Object.keys(hitInfo.items)[0];
490
+ const hitItemData = isHorizontal
491
+ ? hitInfo.items?.[hitItemId]?.data?.y : hitInfo.items?.[hitItemId]?.data?.x;
492
+ let maxSeriesName = '';
493
+ let maxValueTxt = '';
494
+
495
+ const sIds = Object.keys(this.seriesList);
496
+ for (let ix = 0; ix < sIds.length; ix++) {
497
+ const sId = sIds[ix];
498
+ const series = this.seriesList[sId];
499
+
500
+ if (series?.show) {
501
+ const hasData = series.data.find(data => (
502
+ isHorizontal
503
+ ? data?.y === hitItemData
504
+ : data?.x === hitItemData
505
+ ),
506
+ );
507
+
508
+ const formattedValue = this.getFormattedTooltipValue({
509
+ seriesName: series.name,
510
+ value: hasData?.o,
511
+ itemData: hasData,
512
+ });
513
+
514
+ if (hasData && !hitInfo.items[sId]) {
515
+ const item = {};
516
+ item.color = series.color;
517
+ item.hit = false;
518
+ item.name = series.name;
519
+ item.axis = { x: series.xAxisIndex, y: series.yAxisIndex };
520
+ item.index = isHorizontal ? series.yAxisIndex : series.xAxisIndex;
521
+ item.data = hasData;
522
+ item.data.formatted = formattedValue;
523
+
524
+ hitInfo.items[sId] = item;
525
+ }
526
+
527
+ const maxSeriesNameWidth = ctx ? ctx.measureText(maxSeriesName).width : 1;
528
+ const seriesNameWidth = ctx ? ctx.measureText(series.name).width : 1;
529
+ if (maxSeriesNameWidth < seriesNameWidth) {
530
+ maxSeriesName = series.name;
531
+ }
532
+
533
+ const maxValueWidth = ctx ? ctx.measureText(maxValueTxt).width : 1;
534
+ const valueWidth = ctx ? ctx.measureText(`${formattedValue}`).width : 1;
535
+ if (maxValueWidth < valueWidth) {
536
+ maxValueTxt = `${formattedValue}`;
537
+ }
538
+ }
539
+ }
540
+
541
+ hitInfo.maxTip = [maxSeriesName, maxValueTxt];
542
+ },
543
+
544
+ /**
545
+ *
546
+ * @param targetInfo {object} '{ dataIndex: number, seriesID: string }'
547
+ * @param chartType {string} 'bar', 'line', 'pie', 'scatter'
548
+ *
549
+ * @returns {boolean}
550
+ */
551
+ selectItemByData(targetInfo, chartType) {
552
+ this.defaultSelectItemInfo = targetInfo;
553
+
554
+ let foundInfo;
555
+ if (chartType === 'pie') {
556
+ foundInfo = {
557
+ type: 'pie',
558
+ sId: targetInfo.seriesID,
559
+ };
560
+ } else {
561
+ if (isNaN(targetInfo?.dataIndex)) {
562
+ return false;
563
+ }
564
+
565
+ foundInfo = this.getItem(targetInfo, false);
566
+ }
567
+
568
+ if (foundInfo) {
569
+ this.render(foundInfo);
570
+ } else {
571
+ return false;
572
+ }
573
+
574
+ return true;
575
+ },
576
+
577
+ /**
578
+ * render after selected label or selected series
579
+ * @param indexList {array} '[0, 1 ...]'
580
+ */
581
+ renderWithSelected(list) {
582
+ if (this.options.selectLabel.use) {
583
+ this.defaultSelectInfo.dataIndex = list;
584
+ } else if (this.options.selectSeries.use) {
585
+ this.defaultSelectInfo.seriesId = list;
586
+ }
587
+ this.initSelectedInfo();
588
+ this.render();
589
+ },
590
+
591
+ /**
592
+ * init defaultSelectInfo object.
593
+ * - at selectLabel using: set each series data and label text
594
+ * - at selectSeries using: set series state
595
+ */
596
+ initSelectedInfo() {
597
+ if (this.options.selectLabel.use) {
598
+ const { limit } = this.options.selectLabel;
599
+ if (!this.defaultSelectInfo) {
600
+ this.defaultSelectInfo = { dataIndex: [] };
601
+ }
602
+ const infoObj = this.defaultSelectInfo;
603
+ infoObj.dataIndex.splice(limit);
604
+ infoObj.label = infoObj.dataIndex.map(i => this.data.labels[i]);
605
+ const dataEntries = Object.entries(this.data.data);
606
+ infoObj.data = infoObj.dataIndex.map(labelIdx => Object.fromEntries(
607
+ dataEntries.map(([sId, data]) => [sId, data[labelIdx]])));
608
+ } else if (this.options.selectSeries.use) {
609
+ if (!this.defaultSelectInfo) {
610
+ this.defaultSelectInfo = { seriesId: [] };
611
+ }
612
+ }
613
+ },
614
+
615
+ /**
616
+ * Add or delete selected label index, according to policy and option
617
+ * (set each series data and label text)
618
+ * @param labelIndex {array} '[0, 1 ...]'
619
+ */
620
+ selectLabel(labelIndex) {
621
+ const option = this.options?.selectLabel ?? {};
622
+ const before = this.defaultSelectInfo ?? { dataIndex: [] };
623
+ const after = cloneDeep(before);
624
+
625
+ if (before.dataIndex.includes(labelIndex)) {
626
+ const idx = before.dataIndex.indexOf(labelIndex);
627
+ after.dataIndex.splice(idx, 1);
628
+ } else if (labelIndex > -1) {
629
+ after.dataIndex.push(labelIndex);
630
+ if (option.limit > 0 && option.limit < after.dataIndex.length) {
631
+ if (option.useDeselectOverflow) {
632
+ after.dataIndex.splice(0, 1);
633
+ } else {
634
+ after.dataIndex.pop();
635
+ }
636
+ }
637
+ }
638
+
639
+ return after;
640
+ },
641
+
642
+ selectSeries(seriesId) {
643
+ const option = this.options?.selectSeries ?? {};
644
+ const before = this.defaultSelectInfo ?? { seriesId: [] };
645
+ const after = cloneDeep(before);
646
+
647
+ if (before.seriesId.includes(seriesId)) {
648
+ const idx = before.seriesId.indexOf(seriesId);
649
+ after.seriesId.splice(idx, 1);
650
+ } else if (seriesId) {
651
+ after.seriesId.push(seriesId);
652
+ if (option.limit > 0 && option.limit < after.seriesId.length) {
653
+ if (option.useDeselectOverflow) {
654
+ after.seriesId.splice(0, 1);
655
+ } else {
656
+ after.seriesId.pop();
657
+ }
658
+ }
659
+ }
660
+
661
+ return after;
662
+ },
663
+
664
+ /**
665
+ * Find items by series within a range
666
+ * @param {object} range object for find series items
667
+ *
668
+ * @returns {object}
669
+ */
670
+ findSelectedItems(range) {
671
+ const items = [];
672
+ const sIds = Object.keys(this.seriesList);
673
+ for (let ix = 0; ix < sIds.length; ix++) {
674
+ const sId = sIds[ix];
675
+ const series = this.seriesList[sId];
676
+ const findFn = series.findItems;
677
+ if (findFn) {
678
+ const item = findFn.call(series, range);
679
+ if (item && item.length) {
680
+ items.push({
681
+ seriesName: series.name,
682
+ seriesId: sId,
683
+ items: item,
684
+ });
685
+ }
686
+ }
687
+ }
688
+
689
+ return items;
690
+ },
691
+
692
+ /**
693
+ * Returns the data-based range value for a selection
694
+ * @param {object} object for calculating data-based range
695
+ * object.range: coordinate-based range in graph
696
+ * @returns {object}
697
+ */
698
+ getSelectionRage({ xsp, ysp, width, height, range }) {
699
+ const dataRangeX = this.axesSteps.x.length ? this.axesSteps.x[0] : null;
700
+ const dataRangeY = this.axesSteps.y.length ? this.axesSteps.y[0] : null;
701
+
702
+ if (!dataRangeX || !dataRangeY) {
703
+ return null;
704
+ }
705
+
706
+ const xep = xsp + width;
707
+ const yep = ysp + height;
708
+ const graphWidth = dataRangeX.graphMax - dataRangeX.graphMin;
709
+ const graphHeight = dataRangeY.graphMax - dataRangeY.graphMin;
710
+
711
+ const xMinRatio = this.getRatioInRange(range.x1, range.x2, xsp);
712
+ const xMaxRatio = this.getRatioInRange(range.x1, range.x2, xep);
713
+ const yMinRatio = this.getRatioInRange(range.y1, range.y2, yep);
714
+ const yMaxRatio = this.getRatioInRange(range.y1, range.y2, ysp);
715
+
716
+ const xMin = dataRangeX.graphMin + graphWidth * xMinRatio;
717
+ const xMax = dataRangeX.graphMin + graphWidth * xMaxRatio;
718
+ const yMin = dataRangeY.graphMin + graphHeight * (1 - yMinRatio);
719
+ const yMax = dataRangeY.graphMin + graphHeight * (1 - yMaxRatio);
720
+
721
+ return {
722
+ xMin: Math.max(+parseFloat(xMin).toFixed(3), dataRangeX.graphMin),
723
+ xMax: Math.min(+parseFloat(xMax).toFixed(3), dataRangeX.graphMax),
724
+ yMin: Math.max(+parseFloat(yMin).toFixed(3), dataRangeY.graphMin),
725
+ yMax: Math.min(+parseFloat(yMax).toFixed(3), dataRangeY.graphMax),
726
+ };
727
+ },
728
+
729
+ /**
730
+ * Returns the position ratio of 'value' between 'min' and 'max'
731
+ * @param {number} min min value
732
+ * @param {number} max max value
733
+ * @param {number} value value is between min and max
734
+ *
735
+ * @returns {number}
736
+ */
737
+ getRatioInRange(min, max, value) {
738
+ const total = max - min;
739
+ const targetValue = value - min;
740
+
741
+ return targetValue / total;
742
+ },
743
+
744
+ getDragInfoForHeatMap(range) {
745
+ const sId = Object.keys(this.seriesList)[0];
746
+ return this.seriesList[sId].findBlockRange(range);
747
+ },
748
+
749
+ getSelectionRangeForHeatMap(range) {
750
+ const dataRangeX = this.axesSteps.x.length ? this.axesSteps.x[0] : null;
751
+ const dataRangeY = this.axesSteps.y.length ? this.axesSteps.y[0] : null;
752
+
753
+ if (!dataRangeX || !dataRangeY) {
754
+ return null;
755
+ }
756
+
757
+ const sId = Object.keys(this.seriesList)[0];
758
+ const { xMin, xMax, yMin, yMax } = this.seriesList[sId].findSelectionRange(range) ?? {};
759
+
760
+ return {
761
+ xMin: xMin ?? dataRangeX.graphMin,
762
+ xMax: xMax ?? dataRangeX.graphMax,
763
+ yMin: yMin ?? dataRangeY.graphMin,
764
+ yMax: yMax ?? dataRangeY.graphMax,
765
+ };
766
+ },
767
+ };
768
+
769
+ export default modules;