evui 3.4.206 → 3.4.208

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 (162) hide show
  1. package/README.md +18 -33
  2. package/dist/404.html +44 -0
  3. package/dist/favicon.ico +0 -0
  4. package/dist/index.js +22645 -0
  5. package/dist/index.umd.cjs +28 -0
  6. package/dist/style.css +1 -0
  7. package/package.json +46 -43
  8. package/dist/evui.common.js +0 -63678
  9. package/dist/evui.common.js.map +0 -1
  10. package/dist/evui.umd.js +0 -63688
  11. package/dist/evui.umd.js.map +0 -1
  12. package/dist/evui.umd.min.js +0 -2
  13. package/dist/evui.umd.min.js.map +0 -1
  14. package/dist/img/EVUI.b82ee81a.svg +0 -293
  15. package/src/assets/logo.png +0 -0
  16. package/src/common/emitter.js +0 -20
  17. package/src/common/utils.bignumber.js +0 -67
  18. package/src/common/utils.debounce.js +0 -223
  19. package/src/common/utils.js +0 -151
  20. package/src/common/utils.table.js +0 -78
  21. package/src/common/utils.throttle.js +0 -83
  22. package/src/common/utils.tree.js +0 -18
  23. package/src/components/button/Button.vue +0 -195
  24. package/src/components/button/index.js +0 -7
  25. package/src/components/buttonGroup/ButtonGroup.vue +0 -11
  26. package/src/components/buttonGroup/index.js +0 -7
  27. package/src/components/calendar/Calendar.vue +0 -725
  28. package/src/components/calendar/index.js +0 -7
  29. package/src/components/calendar/uses.js +0 -1410
  30. package/src/components/chart/Chart.vue +0 -363
  31. package/src/components/chart/ChartToolbar.vue +0 -52
  32. package/src/components/chart/chart.core.js +0 -1170
  33. package/src/components/chart/chartZoom.core.js +0 -540
  34. package/src/components/chart/element/element.bar.js +0 -672
  35. package/src/components/chart/element/element.bar.time.js +0 -166
  36. package/src/components/chart/element/element.heatmap.js +0 -743
  37. package/src/components/chart/element/element.line.js +0 -611
  38. package/src/components/chart/element/element.pie.js +0 -197
  39. package/src/components/chart/element/element.scatter.js +0 -320
  40. package/src/components/chart/element/element.tip.js +0 -717
  41. package/src/components/chart/helpers/helpers.canvas.js +0 -265
  42. package/src/components/chart/helpers/helpers.constant.js +0 -235
  43. package/src/components/chart/helpers/helpers.util.js +0 -400
  44. package/src/components/chart/index.js +0 -9
  45. package/src/components/chart/model/index.js +0 -50
  46. package/src/components/chart/model/model.series.js +0 -125
  47. package/src/components/chart/model/model.store.js +0 -1427
  48. package/src/components/chart/plugins/plugins.interaction.js +0 -1655
  49. package/src/components/chart/plugins/plugins.legend.gradient.js +0 -606
  50. package/src/components/chart/plugins/plugins.legend.js +0 -1543
  51. package/src/components/chart/plugins/plugins.pie.js +0 -254
  52. package/src/components/chart/plugins/plugins.scrollbar.js +0 -732
  53. package/src/components/chart/plugins/plugins.title.js +0 -61
  54. package/src/components/chart/plugins/plugins.tooltip.js +0 -1041
  55. package/src/components/chart/scale/scale.js +0 -951
  56. package/src/components/chart/scale/scale.linear.js +0 -268
  57. package/src/components/chart/scale/scale.logarithmic.js +0 -135
  58. package/src/components/chart/scale/scale.step.js +0 -430
  59. package/src/components/chart/scale/scale.time.category.js +0 -338
  60. package/src/components/chart/scale/scale.time.js +0 -49
  61. package/src/components/chart/style/chart.scss +0 -405
  62. package/src/components/chart/uses.js +0 -721
  63. package/src/components/chartBrush/ChartBrush.vue +0 -323
  64. package/src/components/chartBrush/chartBrush.core.js +0 -691
  65. package/src/components/chartBrush/index.js +0 -9
  66. package/src/components/chartBrush/uses.js +0 -23
  67. package/src/components/chartGroup/ChartGroup.vue +0 -144
  68. package/src/components/chartGroup/index.js +0 -9
  69. package/src/components/chartGroup/style/chartGroup.scss +0 -5
  70. package/src/components/chartGroup/uses.js +0 -53
  71. package/src/components/checkbox/Checkbox.vue +0 -229
  72. package/src/components/checkbox/index.js +0 -7
  73. package/src/components/checkboxGroup/CheckboxGroup.vue +0 -44
  74. package/src/components/checkboxGroup/index.js +0 -7
  75. package/src/components/contextMenu/ContextMenu.vue +0 -95
  76. package/src/components/contextMenu/MenuList.vue +0 -182
  77. package/src/components/contextMenu/index.js +0 -7
  78. package/src/components/contextMenu/uses.js +0 -223
  79. package/src/components/datePicker/DatePicker.vue +0 -504
  80. package/src/components/datePicker/index.js +0 -7
  81. package/src/components/datePicker/uses.js +0 -460
  82. package/src/components/grid/Grid.vue +0 -1535
  83. package/src/components/grid/GridColumnSetting.vue +0 -358
  84. package/src/components/grid/GridFilterSetting.vue +0 -323
  85. package/src/components/grid/GridPagination.vue +0 -75
  86. package/src/components/grid/GridSummary.vue +0 -314
  87. package/src/components/grid/GridToolbar.vue +0 -35
  88. package/src/components/grid/icon/icon-option-button.vue +0 -17
  89. package/src/components/grid/icon/icon-sort-button.vue +0 -67
  90. package/src/components/grid/index.js +0 -11
  91. package/src/components/grid/style/grid.scss +0 -417
  92. package/src/components/grid/uses.js +0 -1629
  93. package/src/components/icon/Icon.vue +0 -53
  94. package/src/components/icon/index.js +0 -8
  95. package/src/components/inputNumber/InputNumber.vue +0 -212
  96. package/src/components/inputNumber/index.js +0 -7
  97. package/src/components/inputNumber/uses.js +0 -217
  98. package/src/components/loading/Loading.vue +0 -125
  99. package/src/components/loading/index.js +0 -7
  100. package/src/components/menu/Menu.vue +0 -79
  101. package/src/components/menu/MenuItem.vue +0 -201
  102. package/src/components/menu/index.js +0 -7
  103. package/src/components/message/Message.vue +0 -229
  104. package/src/components/message/index.js +0 -34
  105. package/src/components/messageBox/MessageBox.vue +0 -358
  106. package/src/components/messageBox/index.js +0 -22
  107. package/src/components/notification/Notification.vue +0 -316
  108. package/src/components/notification/index.js +0 -49
  109. package/src/components/pagination/Pagination.vue +0 -317
  110. package/src/components/pagination/index.js +0 -7
  111. package/src/components/pagination/pageButton.vue +0 -31
  112. package/src/components/progress/Progress.vue +0 -139
  113. package/src/components/progress/index.js +0 -7
  114. package/src/components/radio/Radio.vue +0 -159
  115. package/src/components/radio/index.js +0 -7
  116. package/src/components/radioGroup/RadioGroup.vue +0 -41
  117. package/src/components/radioGroup/index.js +0 -7
  118. package/src/components/scheduler/Scheduler.vue +0 -149
  119. package/src/components/scheduler/index.js +0 -7
  120. package/src/components/scheduler/uses.js +0 -183
  121. package/src/components/select/Select.vue +0 -556
  122. package/src/components/select/index.js +0 -7
  123. package/src/components/select/uses.js +0 -379
  124. package/src/components/slider/Slider.vue +0 -505
  125. package/src/components/slider/index.js +0 -7
  126. package/src/components/slider/uses.js +0 -391
  127. package/src/components/tabPanel/TabPanel.vue +0 -74
  128. package/src/components/tabPanel/index.js +0 -7
  129. package/src/components/tabs/Tabs.vue +0 -517
  130. package/src/components/tabs/index.js +0 -7
  131. package/src/components/textField/TextField.vue +0 -399
  132. package/src/components/textField/index.js +0 -7
  133. package/src/components/timePicker/TimePicker.vue +0 -364
  134. package/src/components/timePicker/index.js +0 -7
  135. package/src/components/toggle/Toggle.vue +0 -115
  136. package/src/components/toggle/index.js +0 -7
  137. package/src/components/tree/Tree.vue +0 -338
  138. package/src/components/tree/TreeNode.vue +0 -293
  139. package/src/components/tree/index.js +0 -7
  140. package/src/components/treeGrid/TreeGrid.vue +0 -1074
  141. package/src/components/treeGrid/TreeGridNode.vue +0 -349
  142. package/src/components/treeGrid/TreeGridToolbar.vue +0 -35
  143. package/src/components/treeGrid/icon/icon-tree.png +0 -0
  144. package/src/components/treeGrid/index.js +0 -9
  145. package/src/components/treeGrid/style/treeGrid.scss +0 -277
  146. package/src/components/treeGrid/uses.js +0 -1178
  147. package/src/components/window/Window.vue +0 -329
  148. package/src/components/window/index.js +0 -7
  149. package/src/components/window/uses.js +0 -908
  150. package/src/directives/clickoutside.js +0 -90
  151. package/src/main.js +0 -120
  152. package/src/style/components/input.scss +0 -108
  153. package/src/style/functions.scss +0 -3
  154. package/src/style/index.scss +0 -6
  155. package/src/style/lib/fonts/EVUI.eot +0 -0
  156. package/src/style/lib/fonts/EVUI.svg +0 -293
  157. package/src/style/lib/fonts/EVUI.ttf +0 -0
  158. package/src/style/lib/fonts/EVUI.woff +0 -0
  159. package/src/style/lib/icon.css +0 -888
  160. package/src/style/mixins.scss +0 -94
  161. package/src/style/themes.scss +0 -69
  162. package/src/style/variables.scss +0 -22
@@ -1,1655 +0,0 @@
1
- import { numberWithComma } from '@/common/utils';
2
- import throttle from '@/common/utils.throttle';
3
- import { cloneDeep, defaultsDeep, inRange, isEqual } from 'lodash-es';
4
- import dayjs from 'dayjs';
5
-
6
- const modules = {
7
- /**
8
- * Hide legend components by manipulating css
9
- *
10
- * @returns {undefined}
11
- */
12
- createEventFunctions() {
13
- /**
14
- * To show tooltip and item highlighting, add event listener on mousemove
15
- *
16
- * @returns {undefined}
17
- */
18
- this.onMouseMove = (e) => {
19
- if (this.dragInfo?.isMove || this.isMobile) {
20
- return;
21
- }
22
-
23
- const args = { e };
24
- const { indicator, tooltip, type } = this.options;
25
- const offset = this.getMousePosition(e);
26
- const hitInfo = this.findHitItem(offset);
27
-
28
- if (tooltip?.showAllValueInRange && hitInfo?.items) {
29
- this.addNotHitInfo(hitInfo);
30
- }
31
-
32
- const ctx = this.overlayCtx;
33
-
34
- this.overlayClear();
35
-
36
- if (Object.keys(hitInfo.items).length) {
37
- if (tooltip.use && this.isInitTooltip) {
38
- this.drawItemsHighlight(hitInfo, ctx);
39
-
40
- if (typeof tooltip?.returnValue === 'function') {
41
- const seriesList = [];
42
- Object.keys(hitInfo.items).forEach((sId) => {
43
- seriesList.push({
44
- sId,
45
- data: hitInfo.items[sId].data,
46
- color: hitInfo.items[sId].color,
47
- name: hitInfo.items[sId].name,
48
- dataId: hitInfo.items[sId].id,
49
- index: hitInfo.items[sId].index,
50
- });
51
- });
52
-
53
- this.hideTooltipDOM();
54
- tooltip.returnValue(seriesList, e);
55
- } else if (tooltip?.formatter?.html) {
56
- this.drawCustomTooltip(hitInfo?.items);
57
- this.setCustomTooltipLayoutPosition(hitInfo, e);
58
- } else {
59
- this.setTooltipLayoutPosition(hitInfo, e);
60
- if (type === 'scatter') {
61
- this.drawTooltipForScatter(hitInfo, this.tooltipCtx);
62
- } else if (type === 'heatMap') {
63
- this.drawToolTipForHeatMap(hitInfo, this.tooltipCtx);
64
- } else {
65
- this.drawTooltip(hitInfo, this.tooltipCtx);
66
- }
67
- }
68
- }
69
-
70
- // tooltip이 표시될 때 indicator를 해당 라벨 위치로 이동 (line 차트이거나 line series가 포함된 경우)
71
- const hasLineSeries = Object.values(this.seriesList || {}).some(series => series.type === 'line');
72
- if (tooltip.use && (type === 'line' || hasLineSeries)) {
73
- // indicator를 그리고 실제 위치한 라벨 정보를 받음
74
- const indicatorInfo = this.drawIndicatorForTooltip(hitInfo, indicator.color);
75
-
76
- // 실제 indicator가 위치한 라벨 값을 동기화에 사용
77
- const actualLabelValue = indicatorInfo?.labelValue;
78
- const label = this.getTimeLabel(offset);
79
-
80
- args.hoveredLabel = {
81
- horizontal: this.options.horizontal,
82
- label,
83
- mousePosition: [e.clientX, e.clientY],
84
- dataLabel: actualLabelValue,
85
- };
86
- }
87
- } else if (tooltip.use && this.isInitTooltip) {
88
- if (typeof tooltip?.returnValue === 'function') {
89
- tooltip.returnValue([], e);
90
- }
91
-
92
- this.hideTooltipDOM();
93
- }
94
-
95
- if (this.dragInfoBackup) {
96
- this.drawSelectionArea(this.dragInfoBackup);
97
- }
98
-
99
- // tooltip 기반 indicator가 아직 설정되지 않은 경우에만 일반 indicator 처리
100
- if (!args.hoveredLabel && type !== 'pie' && type !== 'scatter' && type !== 'heatMap') {
101
- // line 차트가 아니고 line series가 없거나, tooltip이 없을 때는 일반 indicator 표시
102
- const hasLineSeries = Object.values(this.seriesList || {}).some(series => series.type === 'line');
103
- if ((type !== 'line' && !hasLineSeries) || !tooltip.use || !Object.keys(hitInfo.items).length) {
104
- this.drawIndicator(offset, indicator.color);
105
- }
106
- const label = this.getTimeLabel(offset);
107
- args.hoveredLabel = {
108
- horizontal: this.options.horizontal,
109
- label,
110
- mousePosition: [e.clientX, e.clientY],
111
- };
112
- } else if (!args.hoveredLabel) {
113
- args.hoveredLabel = {
114
- label: '',
115
- };
116
- }
117
-
118
- if (typeof this.listeners['mouse-move'] === 'function') {
119
- if (type !== 'pie') {
120
- args.curMouseTargetVal = this.getCurMouseTargetVal(offset, hitInfo);
121
- }
122
-
123
- this.listeners['mouse-move'](args);
124
- }
125
- };
126
-
127
- /**
128
- * To clear tooltip and item highlighting, add event listener on mouseleave
129
- *
130
- * @returns {undefined}
131
- */
132
-
133
- this.onMouseLeave = (e) => {
134
- const { tooltip, dragSelection } = this.options;
135
-
136
- if (tooltip.throttledMove) {
137
- this.onMouseMove.cancel();
138
- }
139
-
140
- if (!dragSelection.use || !dragSelection.keepDisplay) {
141
- this.overlayClear();
142
- }
143
-
144
- if (tooltip.use && this.isInitTooltip) {
145
- if (typeof tooltip?.returnValue === 'function') {
146
- tooltip.returnValue([], e);
147
- }
148
-
149
- this.tooltipClear();
150
- }
151
- this.listeners['mouse-leave']();
152
- };
153
-
154
- /**
155
- * Dealing with graph item select and invoking user custom dblclick event
156
- *
157
- * @returns {undefined}
158
- */
159
- this.onDblClick = (e) => {
160
- const args = { e };
161
- const offset = this.getMousePosition(e);
162
-
163
- const {
164
- type: chartType,
165
- selectItem: selectItemOpt,
166
- selectLabel: selectLabelOpt,
167
- selectSeries: selectSeriesOpt,
168
- } = this.options;
169
-
170
- const useSelectItem = selectItemOpt?.use && selectItemOpt?.useClick;
171
- const useSelectLabel = selectLabelOpt?.use && selectLabelOpt?.useClick;
172
- const useSelectSeries = selectSeriesOpt?.use && selectSeriesOpt?.useClick;
173
-
174
- if (useSelectItem) {
175
- args.eventTarget = 'item';
176
- } else if (useSelectLabel) {
177
- args.eventTarget = 'label';
178
- } else if (useSelectSeries) {
179
- args.eventTarget = 'series';
180
- }
181
-
182
- const setSelectedItemInfo = () => {
183
- const hitInfo = this.findHitItem(offset);
184
-
185
- // 실제 클릭된 아이템의 정보 추출 (hitId가 있으면 해당 아이템, 없으면 첫 번째 아이템)
186
- const hitItemId = hitInfo.hitId || Object.keys(hitInfo.items)[0];
187
- const hitItem = hitInfo.items[hitItemId];
188
-
189
- if (hitItem) {
190
- args.label = hitItem.data?.x || hitItem.data?.y;
191
- args.value = hitItem.data?.o;
192
- args.seriesId = hitItemId;
193
- args.dataIndex = hitItem.index;
194
- args.acc = hitItem.data?.acc;
195
- }
196
- };
197
-
198
- const setSelectedLabelInfo = (targetAxis) => {
199
- const hitInfo = this.findHitItem(offset);
200
- const hitItemId = hitInfo.hitId || Object.keys(hitInfo.items)[0];
201
- const hitItem = hitInfo.items[hitItemId];
202
-
203
- const {
204
- labelIndex: clickedLabelIndex,
205
- } = this.getLabelInfoByPosition(offset, targetAxis);
206
-
207
- const {
208
- dataIndex: dataIndexList,
209
- } = this.regulateSelectedLabelInfo(clickedLabelIndex, targetAxis);
210
-
211
- this.defaultSelectInfo = this.getSelectedLabelInfoWithLabelData(dataIndexList, targetAxis);
212
-
213
- if (hitItem) {
214
- args.label = hitItem.data?.x || hitItem.data?.y;
215
- args.seriesId = hitItemId;
216
- args.value = hitItem.data?.o;
217
- args.acc = hitItem.data?.acc;
218
- args.dataIndex = hitItem.index;
219
- }
220
- };
221
-
222
- const setSelectedSeriesInfo = () => {
223
- const hitInfo = this.findHitItem(offset);
224
- const hitItemId = hitInfo.hitId || Object.keys(hitInfo.items)[0];
225
- const hitItem = hitInfo.items[hitItemId];
226
-
227
- const seriesHitInfo = this.getSeriesInfoByPosition(offset);
228
- if (seriesHitInfo.sId !== null) {
229
- const allSelectedList = this.updateSelectedSeriesInfo(seriesHitInfo.sId, true);
230
-
231
- if (hitItem) {
232
- args.label = hitItem.data?.x || hitItem.data?.y;
233
- args.value = hitItem.data?.o;
234
- args.seriesId = allSelectedList.seriesId?.at(0);
235
- args.acc = hitItem.data?.acc;
236
- args.dataIndex = hitItem.index;
237
- }
238
- }
239
- };
240
-
241
- switch (chartType) {
242
- case 'bar': {
243
- if (useSelectLabel) {
244
- setSelectedLabelInfo(this.options.horizontal ? 'yAxis' : 'xAxis');
245
- } else {
246
- setSelectedItemInfo();
247
- }
248
- break;
249
- }
250
-
251
- case 'line': {
252
- if (useSelectItem) {
253
- setSelectedItemInfo();
254
- } else if (useSelectLabel) {
255
- setSelectedLabelInfo();
256
- } else if (useSelectSeries) {
257
- setSelectedSeriesInfo();
258
- }
259
- break;
260
- }
261
- default: {
262
- setSelectedItemInfo();
263
- break;
264
- }
265
- }
266
-
267
- if (typeof this.listeners['dbl-click'] === 'function') {
268
- this.listeners['dbl-click'](args);
269
- }
270
- };
271
-
272
- /**
273
- * Dealing with graph item select and invoking user custom click event
274
- *
275
- * @returns {undefined}
276
- */
277
- this.onClick = (e) => {
278
- if (this.isMouseMove) {
279
- this.isMouseMove = false;
280
- return;
281
- }
282
-
283
- const args = { e };
284
- const offset = this.getMousePosition(e);
285
-
286
- const {
287
- type: chartType,
288
- selectItem: selectItemOpt,
289
- selectLabel: selectLabelOpt,
290
- selectSeries: selectSeriesOpt,
291
- } = this.options;
292
-
293
- const useSelectItem = selectItemOpt?.use && selectItemOpt?.useClick;
294
- const useSelectLabel = selectLabelOpt?.use && selectLabelOpt?.useClick;
295
- const useSelectSeries = selectSeriesOpt?.use && selectSeriesOpt?.useClick;
296
-
297
- const setSelectedItemInfo = () => {
298
- const hitInfo = this.getItemByPosition(offset, false);
299
-
300
- ({
301
- label: args.label,
302
- value: args.value,
303
- sId: args.seriesId,
304
- maxIndex: args.dataIndex,
305
- acc: args.acc,
306
- } = hitInfo);
307
-
308
- if (hitInfo?.sId) {
309
- args.selected = {
310
- eventTarget: 'item',
311
- seriesId: this.isDeselectItem(hitInfo) ? null : hitInfo.sId,
312
- dataIndex: this.isDeselectItem(hitInfo) ? null : hitInfo.maxIndex,
313
- };
314
- }
315
- };
316
-
317
- const setSelectedLabelInfo = (targetAxis) => {
318
- const {
319
- labelIndex: clickedLabelIndex,
320
- } = this.getLabelInfoByPosition(offset, targetAxis);
321
-
322
- const {
323
- dataIndex: dataIndexList,
324
- } = this.regulateSelectedLabelInfo(clickedLabelIndex, targetAxis);
325
-
326
- this.defaultSelectInfo = this.getSelectedLabelInfoWithLabelData(dataIndexList, targetAxis);
327
-
328
- if (targetAxis) {
329
- this.defaultSelectInfo.targetAxis = dataIndexList?.length ? targetAxis : null;
330
- }
331
-
332
- args.selected = {
333
- eventTarget: 'label',
334
- ...cloneDeep(this.defaultSelectInfo),
335
- };
336
- args.label = this.defaultSelectInfo?.label?.at(0);
337
- args.dataIndex = this.defaultSelectInfo?.dataIndex?.at(0);
338
- };
339
-
340
- const setSelectedSeriesInfo = () => {
341
- const itemHitInfo = this.getItemByPosition(offset, false);
342
- const hitInfo = this.getSeriesInfoByPosition(offset);
343
-
344
- if (hitInfo.sId !== null) {
345
- const allSelectedList = this.updateSelectedSeriesInfo(hitInfo.sId, false);
346
- this.defaultSelectInfo.seriesId = allSelectedList.seriesId;
347
-
348
- args.selected = {
349
- eventTarget: 'series',
350
- ...cloneDeep(this.defaultSelectInfo),
351
- };
352
- args.label = itemHitInfo.label;
353
- args.dataIndex = itemHitInfo.maxIndex;
354
- }
355
- };
356
-
357
- switch (chartType) {
358
- default:
359
- case 'bar': {
360
- if (useSelectItem) {
361
- setSelectedItemInfo();
362
- } else if (useSelectLabel) {
363
- setSelectedLabelInfo(this.options.horizontal ? 'yAxis' : 'xAxis');
364
- }
365
- break;
366
- }
367
-
368
- case 'line': {
369
- if (useSelectItem) {
370
- setSelectedItemInfo();
371
- } else if (useSelectLabel) {
372
- setSelectedLabelInfo();
373
- } else if (useSelectSeries) {
374
- setSelectedSeriesInfo();
375
- }
376
- break;
377
- }
378
-
379
- case 'heatMap': {
380
- const isHorizontal = !!this.options.horizontal;
381
- if (useSelectItem && useSelectLabel) {
382
- const { useBothAxis } = selectLabelOpt;
383
-
384
- const location = this.getCurMouseLocation(offset);
385
-
386
- if (location === 'chartBackground') {
387
- this.clearSelectedLabelInfo();
388
- args.deselected = { eventTarget: 'label' };
389
- setSelectedItemInfo();
390
- } else if (location === 'yAxis' || location === 'xAxis') {
391
- this.clearSelectedItemInfo();
392
- args.deselected = { eventTarget: 'item' };
393
-
394
- if (!useBothAxis) {
395
- const selectLabelAxis = isHorizontal ? 'yAxis' : 'xAxis';
396
- if (location !== selectLabelAxis) {
397
- return;
398
- }
399
- }
400
- setSelectedLabelInfo(location);
401
- }
402
- } else if (useSelectItem) {
403
- setSelectedItemInfo();
404
- } else if (useSelectLabel) {
405
- const { useBothAxis } = selectLabelOpt;
406
- const location = this.getCurMouseLocation(offset);
407
-
408
- if ((location === 'yAxis' || location === 'xAxis') && !useBothAxis) {
409
- const selectLabelAxis = isHorizontal ? 'yAxis' : 'xAxis';
410
- if (location !== selectLabelAxis) {
411
- return;
412
- }
413
- }
414
-
415
- if (location !== 'canvas') {
416
- setSelectedLabelInfo(useBothAxis ? location : null);
417
- }
418
- }
419
- break;
420
- }
421
-
422
- case 'pie': {
423
- if (useSelectItem) {
424
- setSelectedItemInfo();
425
- }
426
-
427
- break;
428
- }
429
-
430
- case 'scatter': {
431
- if (useSelectItem) {
432
- setSelectedItemInfo();
433
- }
434
-
435
- // 모바일용 dragSelection
436
- if (this.options.dragSelection?.use && this.isMobile) {
437
- let touchInfo = this.setTouchInfo(e);
438
- this.overlayClear();
439
-
440
- if (this.options.dragSelection.keepDisplay
441
- && (e.layerX < touchInfo.range.x1
442
- || e.layerY < touchInfo.range.y1
443
- || e.layerX > touchInfo.range.x2
444
- || e.layerY > touchInfo.range.y2)) {
445
- this.isTouchOverlay = false;
446
- } else {
447
- touchInfo = this.setTouchBoxDimensions(touchInfo);
448
- this.isTouchOverlay = true;
449
- this.drawSelectionArea(touchInfo);
450
- }
451
-
452
- if (!this.options.dragSelection.keepDisplay) {
453
- setTimeout(() => {
454
- this.isTouchOverlay = false;
455
- this.overlayClear();
456
- }, 100);
457
- }
458
-
459
- args.e = e;
460
- args.touchInfo = touchInfo;
461
- args.data = this.findSelectedItems(touchInfo);
462
- args.range = this.getSelectionRange(touchInfo);
463
-
464
- if (typeof this.listeners['drag-select'] === 'function') {
465
- this.listeners['drag-select'](args);
466
- }
467
- }
468
-
469
- break;
470
- }
471
- }
472
-
473
- if (typeof this.listeners.click === 'function') {
474
- if (!this.dragInfoBackup) {
475
- this.listeners.click(args);
476
- }
477
- }
478
- };
479
-
480
- /**
481
- * Start drag-select when dragSelection use option is True and graph type is 'scatter'
482
- *
483
- * @returns {undefined}
484
- */
485
- this.onMouseDown = (e) => {
486
- const { dragSelection, type } = this.options;
487
-
488
- if (dragSelection.use && (type === 'scatter' || type === 'line' || type === 'heatMap')) {
489
- this.removeSelectionArea();
490
- this.dragStart(e, type);
491
- }
492
- };
493
-
494
- this.onWheel = (e) => {
495
- const isTooltipVisible = this.tooltipDOM?.style?.display === 'block';
496
- const customTooltip = this.tooltipDOM?.querySelector(this.options.tooltip.htmlScrollTarget);
497
-
498
- if (isTooltipVisible
499
- || (customTooltip && customTooltip.scrollHeight > customTooltip.clientHeight)) {
500
- e.preventDefault();
501
-
502
- if (isTooltipVisible) {
503
- this.tooltipBodyDOM.scrollTop += e.deltaY;
504
- }
505
- if (customTooltip) {
506
- customTooltip.scrollTop += e.deltaY;
507
- }
508
- }
509
- };
510
-
511
- if (this.options?.tooltip?.useScrollbar) {
512
- this.overlayCanvas.addEventListener('wheel', this.onWheel, { passive: false });
513
- }
514
- if (this.options?.tooltip?.throttledMove) {
515
- this.onMouseMove = throttle(this.onMouseMove, 30);
516
- }
517
-
518
- this.overlayCanvas.addEventListener('mousemove', this.onMouseMove);
519
- this.overlayCanvas.addEventListener('mouseleave', this.onMouseLeave);
520
- this.overlayCanvas.addEventListener('dblclick', this.onDblClick);
521
- this.overlayCanvas.addEventListener('click', this.onClick);
522
- this.overlayCanvas.addEventListener('mousedown', this.onMouseDown);
523
-
524
- this.dragTouchSelectionEvent = e => this.dragTouchSelectionDestroy(e);
525
- window.addEventListener('click', this.dragTouchSelectionEvent);
526
- },
527
-
528
- /**
529
- * Start drag-move when the mouse pointer is in the graph
530
- *
531
- * @returns {undefined}
532
- */
533
- dragStart(evt, type) {
534
- let [offsetX, offsetY] = this.getMousePosition(evt);
535
- const chartRect = this.chartRect;
536
- const labelOffset = this.labelOffset;
537
- const range = {
538
- x1: chartRect.x1 + labelOffset.left,
539
- x2: chartRect.x2 - labelOffset.right,
540
- y1: chartRect.y1 + labelOffset.top,
541
- y2: chartRect.y2 - labelOffset.bottom,
542
- };
543
-
544
- if (offsetX < range.x1) {
545
- offsetX = range.x1;
546
- }
547
-
548
- if (offsetX > range.x2) {
549
- offsetX = range.x2;
550
- }
551
-
552
- if (offsetY < range.y1) {
553
- offsetY = range.y1;
554
- }
555
-
556
- if (offsetY > range.y2) {
557
- offsetY = range.y2;
558
- }
559
-
560
- this.dragInfo = {
561
- xcp: offsetX,
562
- ycp: offsetY,
563
- range,
564
- };
565
-
566
- /**
567
- * Calculate drag-section position and size, and drawing drag-section
568
- *
569
- * @returns {undefined}
570
- */
571
- const dragMove = (e) => {
572
- e.preventDefault();
573
- const [aOffsetX, aOffsetY] = this.getMousePosition(e);
574
- const dragInfo = this.dragInfo;
575
- const { xcp, ycp, range: aRange } = dragInfo;
576
-
577
- let xep;
578
- let yep;
579
-
580
- dragInfo.isMove = true;
581
- this.isMouseMove = true;
582
-
583
- if (aOffsetX < aRange.x1) {
584
- xep = aRange.x1;
585
- } else if (aOffsetX > aRange.x2) {
586
- xep = aRange.x2;
587
- } else {
588
- xep = aOffsetX;
589
- }
590
-
591
- if (aOffsetY < aRange.y1) {
592
- yep = range.y1;
593
- } else if (aOffsetY > aRange.y2) {
594
- yep = aRange.y2;
595
- } else {
596
- yep = aOffsetY;
597
- }
598
-
599
- if (type === 'heatMap') {
600
- const rangeInfo = { xcp, xep, ycp, yep, range: aRange };
601
- const { xsp, ysp, width, height } = this.getDragInfoForHeatMap(rangeInfo);
602
- dragInfo.xsp = xsp;
603
- dragInfo.ysp = ysp;
604
- dragInfo.width = width;
605
- dragInfo.height = height;
606
- } else {
607
- dragInfo.xsp = Math.min(xcp, xep);
608
- dragInfo.ysp = type === 'scatter' ? Math.min(ycp, yep) : aRange.y1;
609
- dragInfo.width = Math.ceil(Math.abs(xep - xcp));
610
- dragInfo.height = type === 'scatter'
611
- ? Math.ceil(Math.abs(yep - ycp))
612
- : aRange.y2 - aRange.y1;
613
- }
614
-
615
- this.overlayClear();
616
- this.drawSelectionArea(dragInfo);
617
- };
618
-
619
- /**
620
- * invoking user custom click event width items and range in drag-section
621
- *
622
- * @returns {undefined}
623
- */
624
- const dragEnd = (e) => {
625
- const dragInfo = this.dragInfo;
626
-
627
- if (dragInfo?.isMove && dragInfo?.width > 1 && dragInfo?.height > 1) {
628
- const args = {
629
- e,
630
- data: this.findSelectedItems(dragInfo),
631
- range: type === 'heatMap'
632
- ? this.getSelectionRangeForHeatMap(dragInfo)
633
- : this.getSelectionRange(dragInfo),
634
- };
635
-
636
- this.dragInfoBackup = defaultsDeep({}, dragInfo);
637
-
638
- if (typeof this.listeners['drag-select'] === 'function' && !this.options?.zoom?.use) {
639
- this.listeners['drag-select'](args);
640
- } else {
641
- const {
642
- xsp,
643
- range: chartRange,
644
- width: dragWidth,
645
- } = dragInfo;
646
- const dragXsp = xsp - chartRange.x1;
647
-
648
- args.range.dragSelectionInfo = {
649
- dragXsp,
650
- dragXep: dragXsp + dragWidth,
651
- exceptAxesYChartWidth: chartRange.x2 - chartRange.x1,
652
- exceptAxesXChartHeight: chartRange.y2 - chartRange.y1,
653
- chartRange,
654
- chartTitle: this.options.title.text,
655
- };
656
-
657
- this.options.zoom.getRangeInfo(args);
658
- }
659
-
660
- if (!this.options.dragSelection.keepDisplay) {
661
- this.removeSelectionArea();
662
- }
663
- }
664
-
665
- this.dragInfo = null;
666
-
667
- window.removeEventListener('mousemove', dragMove);
668
- window.removeEventListener('mouseup', dragEnd);
669
- };
670
-
671
- window.addEventListener('mousemove', dragMove);
672
- window.addEventListener('mouseup', dragEnd);
673
- },
674
-
675
- /**
676
- * Draw selection-area
677
- *
678
- * @returns {undefined}
679
- */
680
- drawSelectionArea({ xsp, ysp, width, height, range }) {
681
- const ctx = this.overlayCtx;
682
- const { fillColor, opacity } = this.options.dragSelection;
683
-
684
- const chartRect = this.chartRect;
685
- const labelOffset = this.labelOffset;
686
- const newRange = {
687
- x1: chartRect.x1 + labelOffset.left,
688
- x2: chartRect.x2 - labelOffset.right,
689
- y1: chartRect.y1 + labelOffset.top,
690
- y2: chartRect.y2 - labelOffset.bottom,
691
- };
692
-
693
- ctx.fillStyle = fillColor;
694
- ctx.globalAlpha = opacity;
695
-
696
- if (isEqual(newRange, range)) {
697
- ctx.fillRect(xsp, ysp, width, height);
698
- } else {
699
- const rectWidth = (range.x2 - range.x1);
700
- const rectHeight = (range.y2 - range.y1);
701
- const newRectWidth = (newRange.x2 - newRange.x1);
702
- const newRectHeight = (newRange.y2 - newRange.y1);
703
-
704
- const ratioX = (xsp - range.x1) / rectWidth;
705
- const ratioY = (ysp - range.y1) / rectHeight;
706
- const newXsp = newRange.x1 + newRectWidth * ratioX;
707
- const newYsp = newRange.y1 + newRectHeight * ratioY;
708
-
709
- const ratioWidth = width / rectWidth;
710
- const ratioHeight = height / rectHeight;
711
- const newWidth = newRectWidth * ratioWidth;
712
- const newHeight = newRectHeight * ratioHeight;
713
-
714
- ctx.fillRect(newXsp, newYsp, newWidth, newHeight);
715
- }
716
-
717
- ctx.globalAlpha = 1;
718
- },
719
-
720
- /** Remove drag selection area
721
- *
722
- */
723
- removeSelectionArea() {
724
- this.dragInfoBackup = null;
725
- this.overlayClear();
726
- },
727
-
728
- /**
729
- * Computing mouse position on canvas
730
- *
731
- * @returns {array} mouse pointer position
732
- */
733
- getMousePosition(evt) {
734
- const e = evt.originalEvent || evt;
735
- const rect = this.overlayCanvas.getBoundingClientRect();
736
- return [e.clientX - rect.left, e.clientY - rect.top, rect.width, rect.height];
737
- },
738
-
739
- /**
740
- * Get current mouse target value on canvas
741
- * @param {array} offset return value from getMousePosition()
742
- * @param {object} hitInfo return value from findHitItem()
743
- *
744
- * @returns {object} current mouse target value
745
- */
746
- getCurMouseTargetVal(offset, hitInfo) {
747
- const location = this.getCurMouseLocation(offset);
748
-
749
- const curMouseTargetVal = {
750
- location,
751
- labelIdx: -1,
752
- labelVal: '',
753
- dataIdx: -1,
754
- maxDataVal: '',
755
- originVal: '',
756
- };
757
-
758
- if (location === 'chartBackground') {
759
- const { maxHighlight, items } = hitInfo;
760
- if (maxHighlight?.length) {
761
- const [seriesName, value] = maxHighlight;
762
-
763
- if (items[seriesName]) {
764
- curMouseTargetVal.dataIdx = items[seriesName].index;
765
- curMouseTargetVal.maxDataVal = value;
766
- curMouseTargetVal.originVal = hitInfo;
767
- }
768
- }
769
- } else if (location === 'xAxis' || location === 'yAxis') {
770
- const { axesX, axesY } = this.options;
771
-
772
- const setCurMouseLabelVal = (axes, labelIdx, labelVal) => {
773
- curMouseTargetVal.labelIdx = labelIdx;
774
- curMouseTargetVal.labelVal = axes[0].type === 'time' ? dayjs(labelVal).format(axes[0].timeFormat) : labelVal;
775
- curMouseTargetVal.originVal = axes[0].type === 'time' ? dayjs(labelVal) : labelVal;
776
- };
777
-
778
- const setAxisLabelInfo = (targetAxis) => {
779
- const {
780
- labelIndex,
781
- } = this.getLabelInfoByPosition(offset, location);
782
- const { labelVal, labelIdx } = this.getCurMouseLabelVal(targetAxis, offset, labelIndex);
783
- const axesOpt = targetAxis === 'xAxis' ? axesX : axesY;
784
-
785
- setCurMouseLabelVal(axesOpt, labelIdx, labelVal);
786
- };
787
-
788
- setAxisLabelInfo(location);
789
- }
790
-
791
- return curMouseTargetVal;
792
- },
793
-
794
- /**
795
- * Processes touch event to determine touch position within the chart.
796
- *
797
- * @param {TouchEvent} event - the touch event to process
798
- * @returns {object} - the processed touch information
799
- */
800
- setTouchInfo(event) {
801
- let [offsetX, offsetY] = this.getMousePosition(event);
802
- const chartRect = this.chartRect;
803
- const labelOffset = this.labelOffset;
804
- const range = {
805
- x1: chartRect.x1 + labelOffset.left,
806
- x2: chartRect.x2 - labelOffset.right,
807
- y1: chartRect.y1 + labelOffset.top,
808
- y2: chartRect.y2 - labelOffset.bottom,
809
- };
810
-
811
- offsetX = Math.max(range.x1, Math.min(offsetX, range.x2));
812
- offsetY = Math.max(range.y1, Math.min(offsetY, range.y2));
813
-
814
- return {
815
- xcp: offsetX,
816
- ycp: offsetY,
817
- range,
818
- };
819
- },
820
-
821
- /**
822
- * Adjusts the touch box dimensions based on the provided touch information.
823
- *
824
- * @param {object} touchInfo - The touch information including touch position and plotting range
825
- * @returns {object} - The adjusted touch information
826
- */
827
- setTouchBoxDimensions(touchInfo) {
828
- const boxSize = this.options.dragSelection?.size || 50;
829
- const halfBoxSize = boxSize / 2;
830
- const { xcp, ycp, range } = touchInfo;
831
- let xsp = xcp - halfBoxSize;
832
- let ysp = ycp - halfBoxSize;
833
- let width = boxSize;
834
- let height = boxSize;
835
-
836
- this.boxOverflow = {
837
- x1: false,
838
- x2: false,
839
- y1: false,
840
- y2: false,
841
- };
842
-
843
- if (xcp < range.x1 + halfBoxSize) {
844
- xsp = range.x1;
845
- width = halfBoxSize - (range.x1 - xcp);
846
- this.boxOverflow.x1 = true;
847
- }
848
- if (xcp > range.x2 - halfBoxSize) {
849
- width = halfBoxSize - (xcp - range.x2);
850
- this.boxOverflow.x2 = true;
851
- }
852
- if (ycp < range.y1 + halfBoxSize) {
853
- ysp = range.y1;
854
- height = halfBoxSize - (range.y1 - ycp);
855
- this.boxOverflow.y1 = true;
856
- }
857
- if (ycp > range.y2 - halfBoxSize) {
858
- height = halfBoxSize - (ycp - range.y2);
859
- this.boxOverflow.y2 = true;
860
- }
861
-
862
- touchInfo.xsp = xsp;
863
- touchInfo.ysp = ysp;
864
- touchInfo.width = width;
865
- touchInfo.height = height;
866
-
867
- return touchInfo;
868
- },
869
-
870
- /**
871
- * Remove a touch selection.
872
- *
873
- * @param {TouchEvent} e - the touch event to process
874
- * @returns {undefined}
875
- */
876
- dragTouchSelectionDestroy(e) {
877
- if (
878
- this.options.dragSelection?.use
879
- && e.target !== this.overlayCanvas
880
- && this.isTouchOverlay
881
- ) {
882
- this.isTouchOverlay = false;
883
- this.overlayClear();
884
- }
885
- },
886
-
887
- /**
888
- * Find graph item on mouse position
889
- * @param {array} offset return value from getMousePosition()
890
- *
891
- * @returns {object} hit item information
892
- */
893
- findHitItem(offset) {
894
- const sIds = Object.keys(this.seriesList);
895
- const items = {};
896
- const isHorizontal = !!this.options.horizontal;
897
- const ctx = this.tooltipCtx;
898
-
899
- let hitId = null;
900
- let maxs = '';
901
- let maxsw = 0;
902
- let maxv = '';
903
- let maxg = null;
904
- let maxSID = null;
905
-
906
- // 1. 먼저 공통으로 사용할 데이터 인덱스 결정
907
- const targetDataIndex = this.findClosestDataIndex(offset, sIds);
908
-
909
- if (targetDataIndex === -1 && this.options.type !== 'pie') {
910
- return { items, hitId, maxTip: [maxs, maxv], maxHighlight: null };
911
- }
912
-
913
- // 2. 모든 시리즈가 동일한 데이터 인덱스 사용
914
- const allSeriesIsBar = sIds.every(sId => this.seriesList[sId].type === 'bar');
915
-
916
- for (let ix = 0; ix < sIds.length; ix++) {
917
- const sId = sIds[ix];
918
- const series = this.seriesList[sId];
919
-
920
- if (series.findGraphData && series.show) {
921
- // 특정 데이터 인덱스로 데이터 요청
922
- const item = series.findGraphData(offset, isHorizontal, targetDataIndex, !allSeriesIsBar);
923
-
924
- if (item?.data) {
925
- let gdata;
926
-
927
- if (item.data.o === null && series.interpolation !== 'zero') {
928
- if (!series.isExistGrp) {
929
- gdata = isHorizontal ? item.data.x : item.data.y;
930
- }
931
- } else if (!isNaN(item.data.o)) {
932
- gdata = item.data.o;
933
- }
934
-
935
- if (gdata !== null && gdata !== undefined) {
936
- const formattedSeriesName = this.getFormattedTooltipLabel({
937
- dataId: series.id,
938
- seriesId: sId,
939
- seriesName: series.name,
940
- itemData: item.data,
941
- });
942
- const sw = ctx ? ctx.measureText(formattedSeriesName).width : 1;
943
-
944
- item.id = series.id;
945
- item.name = formattedSeriesName;
946
- item.axis = { x: series.xAxisIndex, y: series.yAxisIndex };
947
- items[sId] = item;
948
-
949
- const formattedTxt = this.getFormattedTooltipValue({
950
- dataId: series.id,
951
- seriesId: sId,
952
- seriesName: formattedSeriesName,
953
- value: gdata,
954
- itemData: item.data,
955
- });
956
-
957
- item.data.formatted = formattedTxt;
958
-
959
- if (maxsw < sw) {
960
- maxs = formattedSeriesName;
961
- maxsw = sw;
962
- }
963
-
964
- if (maxv.length <= `${formattedTxt}`.length) {
965
- maxv = `${formattedTxt}`;
966
- }
967
-
968
- if (maxg === null || maxg <= gdata) {
969
- maxg = gdata;
970
- maxSID = sId;
971
- }
972
-
973
- if (item.hit) {
974
- hitId = sId;
975
- }
976
- }
977
- }
978
- }
979
- }
980
-
981
- hitId = hitId === null ? Object.keys(items)[0] : hitId;
982
- const maxHighlight = maxg !== null ? [maxSID, maxg] : null;
983
-
984
- return { items, hitId, maxTip: [maxs, maxv], maxHighlight };
985
- },
986
-
987
- /**
988
- * Find the closest data index (label) based on mouse position
989
- * @param {array} offset mouse position
990
- * @param {array} sIds series IDs
991
- * @returns {number} closest data index
992
- */
993
- findClosestDataIndex(offset, sIds) {
994
- const [xp, yp] = offset;
995
- const isHorizontal = !!this.options.horizontal;
996
- const mousePos = isHorizontal ? yp : xp;
997
-
998
- // 첫 번째 표시 중인 시리즈를 기준으로 라벨 위치 확인
999
- const referenceSeries = sIds.find(sId => this.seriesList[sId]?.show);
1000
- if (!referenceSeries || !this.seriesList[referenceSeries]?.data) {
1001
- return -1;
1002
- }
1003
-
1004
- const referenceData = this.seriesList[referenceSeries].data;
1005
-
1006
- // 데이터 간격 계산 - 모든 데이터(null 포함)의 평균 간격 사용
1007
- let avgInterval = 50;
1008
- if (referenceData.length > 1) {
1009
- const intervals = [];
1010
- for (let i = 1; i < referenceData.length; i++) {
1011
- const prevPoint = referenceData[i - 1];
1012
- const currPoint = referenceData[i];
1013
- if (prevPoint && currPoint) {
1014
- let prevPos;
1015
- let currPos;
1016
-
1017
- if (isHorizontal) {
1018
- prevPos = prevPoint.h ? prevPoint.yp + (prevPoint.h / 2) : prevPoint.yp;
1019
- currPos = currPoint.h ? currPoint.yp + (currPoint.h / 2) : currPoint.yp;
1020
- } else {
1021
- prevPos = prevPoint.w ? prevPoint.xp + (prevPoint.w / 2) : prevPoint.xp;
1022
- currPos = currPoint.w ? currPoint.xp + (currPoint.w / 2) : currPoint.xp;
1023
- }
1024
-
1025
- if (prevPos !== null && currPos !== null) {
1026
- intervals.push(Math.abs(currPos - prevPos));
1027
- }
1028
- }
1029
- }
1030
- if (intervals.length > 0) {
1031
- avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length;
1032
- }
1033
- }
1034
-
1035
- let closestDistance = Infinity;
1036
- let closestIndex = -1;
1037
-
1038
- // 각 라벨에서 가장 가까운 것 찾기
1039
- for (let i = 0; i < referenceData.length; i++) {
1040
- // 이 라벨에 유효한 데이터가 있는 시리즈가 하나 이상 있는지 확인
1041
- const hasValidData = sIds.some((sId) => {
1042
- const series = this.seriesList[sId];
1043
- return series?.show && series.data?.[i]?.o !== null && series.data?.[i]?.o !== undefined;
1044
- });
1045
-
1046
- if (hasValidData) {
1047
- const point = referenceData[i];
1048
- if (point) {
1049
- // 라벨 위치 계산
1050
- let labelPos;
1051
- if (isHorizontal) {
1052
- labelPos = point.h ? point.yp + (point.h / 2) : point.yp;
1053
- } else {
1054
- labelPos = point.w ? point.xp + (point.w / 2) : point.xp;
1055
- }
1056
-
1057
- if (labelPos !== null) {
1058
- const distance = Math.abs(mousePos - labelPos);
1059
- if (distance < closestDistance) {
1060
- closestDistance = distance;
1061
- closestIndex = i;
1062
- }
1063
- }
1064
- }
1065
- }
1066
- }
1067
-
1068
- if (closestDistance >= avgInterval) {
1069
- const useLinearInterpolation = sIds.some((sId) => {
1070
- const series = this.seriesList[sId];
1071
-
1072
- if (series?.show) {
1073
- const passingValue = series.passingValue;
1074
- const interpolation = series.interpolation;
1075
- const hasPassingValueInData = series.hasPassingValueInData;
1076
-
1077
- return interpolation === 'linear' || (interpolation === 'none' && !!passingValue && hasPassingValueInData);
1078
- }
1079
-
1080
- return false;
1081
- });
1082
- return useLinearInterpolation ? closestIndex : -1;
1083
- }
1084
-
1085
- return closestIndex;
1086
- },
1087
-
1088
- /**
1089
- * get formatted label for tooltip
1090
- * @param dataId
1091
- * @param seriesId
1092
- * @param seriesName
1093
- * @param itemData
1094
- * @returns {string}
1095
- */
1096
- getFormattedTooltipLabel({ dataId, seriesId, seriesName, itemData }) {
1097
- const opt = this.options;
1098
- const tooltipOpt = opt.tooltip;
1099
- const tooltipLabelFormatter = tooltipOpt?.formatter?.label;
1100
-
1101
- let formattedLabel = seriesName;
1102
- if (tooltipLabelFormatter) {
1103
- formattedLabel = tooltipLabelFormatter({
1104
- dataId,
1105
- seriesId,
1106
- seriesName,
1107
- itemData,
1108
- });
1109
- }
1110
-
1111
- return formattedLabel;
1112
- },
1113
-
1114
- /**
1115
- * get formatted value for tooltip
1116
- * @param dataId
1117
- * @param seriesId
1118
- * @param seriesName
1119
- * @param value
1120
- * @param itemData
1121
- * @returns {string}
1122
- */
1123
- getFormattedTooltipValue({ dataId, seriesId, seriesName, value, itemData }) {
1124
- const opt = this.options;
1125
- const isHorizontal = !!opt.horizontal;
1126
- const tooltipOpt = opt.tooltip;
1127
- const tooltipValueFormatter = typeof tooltipOpt?.formatter === 'function'
1128
- ? tooltipOpt?.formatter
1129
- : tooltipOpt?.formatter?.value;
1130
-
1131
- let formattedTxt = value;
1132
- if (tooltipValueFormatter) {
1133
- if (opt.type === 'pie') {
1134
- formattedTxt = tooltipValueFormatter({
1135
- value,
1136
- name: seriesName,
1137
- percentage: itemData?.percentage,
1138
- seriesId,
1139
- dataId,
1140
- });
1141
- } else if (opt.type === 'heatMap') {
1142
- formattedTxt = tooltipValueFormatter({
1143
- x: itemData?.x,
1144
- y: itemData?.y,
1145
- value: value > -1 ? value : 'error',
1146
- seriesId,
1147
- dataId,
1148
- });
1149
- } else {
1150
- formattedTxt = tooltipValueFormatter({
1151
- x: isHorizontal ? value : itemData?.x,
1152
- y: isHorizontal ? itemData?.y : value,
1153
- name: seriesName,
1154
- seriesId,
1155
- dataId,
1156
- });
1157
- }
1158
- }
1159
-
1160
- if (value && (!tooltipValueFormatter || typeof formattedTxt !== 'string')) {
1161
- if (opt.type === 'heatMap') {
1162
- formattedTxt = value < 0 ? 'error' : numberWithComma(value);
1163
- } else {
1164
- formattedTxt = numberWithComma(value);
1165
- }
1166
- }
1167
-
1168
- return formattedTxt;
1169
- },
1170
-
1171
- /**
1172
- * add not hit info
1173
- * @param hitInfo
1174
- */
1175
- addNotHitInfo(hitInfo) {
1176
- const ctx = this.tooltipCtx;
1177
- const isHorizontal = !!this.options.horizontal;
1178
- const hitItemId = Object.keys(hitInfo.items)[0];
1179
- const hitItemData = isHorizontal
1180
- ? hitInfo.items?.[hitItemId]?.data?.y : hitInfo.items?.[hitItemId]?.data?.x;
1181
- let maxSeriesName = '';
1182
- let maxValueTxt = '';
1183
-
1184
- const sIds = Object.keys(this.seriesList);
1185
- for (let ix = 0; ix < sIds.length; ix++) {
1186
- const sId = sIds[ix];
1187
- const series = this.seriesList[sId];
1188
-
1189
- if (series?.show) {
1190
- const hasData = series.data.find(data => (
1191
- isHorizontal
1192
- ? data?.y === hitItemData
1193
- : data?.x === hitItemData
1194
- ),
1195
- );
1196
-
1197
- const formattedSeriesName = this.getFormattedTooltipLabel({
1198
- dataId: series.id,
1199
- seriesId: sId,
1200
- seriesName: series.name,
1201
- itemData: hasData,
1202
- });
1203
-
1204
- const formattedValue = this.getFormattedTooltipValue({
1205
- dataId: series.id,
1206
- seriesId: sId,
1207
- seriesName: formattedSeriesName,
1208
- value: hasData?.o,
1209
- itemData: hasData,
1210
- });
1211
-
1212
- // Only add data if there's a valid value for this exact label
1213
- if (hasData && hasData.o !== null && hasData.o !== undefined && !hitInfo.items[sId]) {
1214
- const item = {};
1215
- item.color = series.color;
1216
- item.hit = false;
1217
- item.name = formattedSeriesName;
1218
- item.axis = { x: series.xAxisIndex, y: series.yAxisIndex };
1219
- item.index = isHorizontal ? series.yAxisIndex : series.xAxisIndex;
1220
- item.data = hasData;
1221
- item.data.formatted = formattedValue;
1222
-
1223
- hitInfo.items[sId] = item;
1224
- }
1225
-
1226
- const maxSeriesNameWidth = ctx ? ctx.measureText(maxSeriesName).width : 1;
1227
- const seriesNameWidth = ctx ? ctx.measureText(formattedSeriesName).width : 1;
1228
- if (maxSeriesNameWidth < seriesNameWidth) {
1229
- maxSeriesName = formattedSeriesName;
1230
- }
1231
-
1232
- const maxValueWidth = ctx ? ctx.measureText(maxValueTxt).width : 1;
1233
- const valueWidth = ctx ? ctx.measureText(`${formattedValue}`).width : 1;
1234
- if (maxValueWidth < valueWidth) {
1235
- maxValueTxt = `${formattedValue}`;
1236
- }
1237
- }
1238
- }
1239
-
1240
- hitInfo.maxTip = [maxSeriesName, maxValueTxt];
1241
- },
1242
-
1243
- /**
1244
- * Select Item
1245
- * Set backup data that selected item information
1246
- * render chart
1247
- * @param targetInfo {object} '{ dataIndex: number, seriesID: string }'
1248
- * @param chartType {string} 'bar', 'line', 'pie', 'scatter', 'heatMap'
1249
- *
1250
- */
1251
- selectItemByData(targetInfo, chartType) {
1252
- this.defaultSelectItemInfo = targetInfo;
1253
-
1254
- let foundInfo;
1255
- if (chartType === 'pie') {
1256
- foundInfo = {
1257
- type: 'pie',
1258
- sId: targetInfo.seriesID,
1259
- };
1260
- } else {
1261
- foundInfo = isNaN(targetInfo?.dataIndex) ? null : this.getItem(targetInfo, false);
1262
- }
1263
-
1264
- this.render(foundInfo);
1265
- },
1266
-
1267
- /**
1268
- * Select Label
1269
- * set backup data that selected label information list
1270
- * render chart
1271
- * @param labelIndexList {number[]}
1272
- * @param targetAxis {string | null}
1273
- * @returns {boolean}
1274
- */
1275
- selectLabelByData(labelIndexList, targetAxis) {
1276
- this.defaultSelectInfo = this.getSelectedLabelInfoWithLabelData(labelIndexList, targetAxis);
1277
- this.render();
1278
- },
1279
-
1280
- /**
1281
- * Select Series
1282
- * set backup data that selected series information list
1283
- * render chart
1284
- * @param seriesIdList {number[]} '
1285
- * @returns {boolean}
1286
- */
1287
- selectSeriesByData(seriesIdList) {
1288
- this.defaultSelectInfo.seriesId = seriesIdList;
1289
- this.render();
1290
- },
1291
-
1292
- /**
1293
- * Get each series data and label text
1294
- * @param labelIndexList{number[]}
1295
- * @param targetAxis{string | null}
1296
- * @returns {object[]}
1297
- */
1298
- getSelectedLabelInfoWithLabelData(labelIndexList, targetAxis) {
1299
- const { selectLabel: selectLabelOpt, type: chartType, horizontal } = this.options;
1300
- const result = cloneDeep(this.defaultSelectInfo);
1301
- result.dataIndex = labelIndexList;
1302
-
1303
- switch (chartType) {
1304
- default:
1305
- case 'bar':
1306
- case 'line': {
1307
- result.dataIndex.splice(selectLabelOpt.limit);
1308
-
1309
- result.label = result.dataIndex.map(i => this.data.labels[i]);
1310
-
1311
- const dataEntries = Object.entries(this.data.data);
1312
- result.data = result.dataIndex.map(labelIdx => Object.fromEntries(
1313
- dataEntries.map(([sId, data]) => [sId, data[labelIdx]])));
1314
- break;
1315
- }
1316
-
1317
- case 'heatMap': {
1318
- const { limit, useBothAxis } = this.options.selectLabel;
1319
-
1320
- result.dataIndex.splice(limit);
1321
-
1322
- let targetAxisDirection;
1323
- if (useBothAxis) {
1324
- targetAxisDirection = targetAxis === 'yAxis' ? 'y' : 'x';
1325
- } else {
1326
- targetAxisDirection = horizontal ? 'y' : 'x';
1327
- }
1328
-
1329
- result.label = result.dataIndex.map(i => this.data.labels[targetAxisDirection][i]);
1330
-
1331
- const dataValues = Object.values(this.data.data)[0];
1332
- result.data = dataValues.filter(({ x, y }) =>
1333
- (result.label.includes(targetAxisDirection === 'y' ? y : x)),
1334
- );
1335
- break;
1336
- }
1337
- }
1338
-
1339
- return result;
1340
- },
1341
-
1342
- /**
1343
- * Add or delete selected label index, according to policy and option
1344
- * @param labelIndex {number}
1345
- * @param targetAxis {string | null}
1346
- * @returns after {number[]} '[0, 1 ...]' result Label index List
1347
- */
1348
- regulateSelectedLabelInfo(labelIndex, targetAxis) {
1349
- const option = this.options?.selectLabel ?? {};
1350
- const before = targetAxis === null || this.defaultSelectInfo?.targetAxis === targetAxis
1351
- ? { ...this.defaultSelectInfo, targetAxis }
1352
- : { dataIndex: [], targetAxis };
1353
-
1354
- const after = cloneDeep(before);
1355
-
1356
- if (before.dataIndex.includes(labelIndex)) {
1357
- const idx = before.dataIndex.indexOf(labelIndex);
1358
- after.dataIndex.splice(idx, 1);
1359
- } else if (labelIndex > -1) {
1360
- after.dataIndex.push(labelIndex);
1361
- if (option.limit > 0 && option.limit < after.dataIndex.length) {
1362
- if (option.useDeselectOverflow) {
1363
- after.dataIndex.splice(0, 1);
1364
- } else {
1365
- after.dataIndex.pop();
1366
- }
1367
- }
1368
- }
1369
-
1370
- return after;
1371
- },
1372
-
1373
- /**
1374
- * Add or delete selected series Index,according to policy and option
1375
- * @param seriesId {number}
1376
- * @param keepSelection {boolean}
1377
- * @returns after {number[]} '[0, 1 ...]' result series Id List
1378
- */
1379
- updateSelectedSeriesInfo(seriesId, keepSelection) {
1380
- const option = this.options?.selectSeries ?? {};
1381
- const before = this.defaultSelectInfo ?? { seriesId: [] };
1382
-
1383
- if (typeof before.seriesId === 'string') {
1384
- before.seriesId = [before.seriesId];
1385
- }
1386
-
1387
- const after = cloneDeep(before);
1388
-
1389
- if (before.seriesId.includes(seriesId)) {
1390
- if (!keepSelection) {
1391
- const idx = before.seriesId.indexOf(seriesId);
1392
- after.seriesId.splice(idx, 1);
1393
- }
1394
- } else if (seriesId) {
1395
- after.seriesId.push(seriesId);
1396
- if (option.limit > 0 && option.limit < after.seriesId.length) {
1397
- if (option.useDeselectOverflow) {
1398
- after.seriesId.splice(0, 1);
1399
- } else {
1400
- after.seriesId.pop();
1401
- }
1402
- }
1403
- }
1404
-
1405
- return after;
1406
- },
1407
-
1408
- /**
1409
- * Draw indicator at the label position when tooltip is displayed
1410
- * @param {object} hitInfo hit item information from findHitItem
1411
- * @param {string} color indicator color
1412
- * @returns {object|null} indicator position info with actual label value
1413
- */
1414
- drawIndicatorForTooltip(hitInfo, color) {
1415
- if (!hitInfo?.items || !Object.keys(hitInfo.items).length) {
1416
- return null;
1417
- }
1418
-
1419
- const ctx = this.overlayCtx;
1420
- const { horizontal } = this.options;
1421
- const graphPos = {
1422
- x1: this.chartRect.x1 + this.labelOffset.left,
1423
- x2: this.chartRect.x2 - this.labelOffset.right,
1424
- y1: this.chartRect.y1 + this.labelOffset.top,
1425
- y2: this.chartRect.y2 - this.labelOffset.bottom,
1426
- };
1427
-
1428
- // 첫 번째 시리즈의 데이터를 기준으로 라벨 위치 계산
1429
- const firstSeriesId = Object.keys(hitInfo.items)[0];
1430
- const firstItem = hitInfo.items[firstSeriesId];
1431
-
1432
- if (!firstItem?.data) {
1433
- return null;
1434
- }
1435
-
1436
- // 실제 indicator가 위치하는 라벨 값 추출
1437
- const actualLabelValue = horizontal ? firstItem.data.y : firstItem.data.x;
1438
-
1439
- let indicatorPosition;
1440
-
1441
- if (horizontal) {
1442
- // 수평 차트에서는 Y축 라벨 위치에 수평선
1443
- const yPosition = firstItem.data.yp + (firstItem.data.h ? firstItem.data.h / 2 : 0);
1444
- indicatorPosition = [graphPos.x1, yPosition];
1445
- } else {
1446
- // 수직 차트에서는 X축 라벨 위치에 수직선
1447
- const xPosition = firstItem.data.xp + (firstItem.data.w ? firstItem.data.w / 2 : 0);
1448
- indicatorPosition = [xPosition, graphPos.y1];
1449
- }
1450
-
1451
- ctx.beginPath();
1452
- ctx.save();
1453
- ctx.strokeStyle = color;
1454
- ctx.lineWidth = 1;
1455
-
1456
- if (this.options.indicator?.segments) {
1457
- ctx.setLineDash(this.options.indicator.segments);
1458
- }
1459
-
1460
- if (horizontal) {
1461
- ctx.moveTo(graphPos.x1, indicatorPosition[1] + 0.5);
1462
- ctx.lineTo(graphPos.x2, indicatorPosition[1] + 0.5);
1463
- } else {
1464
- ctx.moveTo(indicatorPosition[0] + 0.5, graphPos.y1);
1465
- ctx.lineTo(indicatorPosition[0] + 0.5, graphPos.y2);
1466
- }
1467
-
1468
- ctx.stroke();
1469
- ctx.restore();
1470
- ctx.closePath();
1471
-
1472
- // 실제 indicator가 위치한 라벨 정보 반환
1473
- return {
1474
- labelValue: actualLabelValue,
1475
- position: indicatorPosition,
1476
- };
1477
- },
1478
-
1479
- /**
1480
- * Find items by series within a range
1481
- * @param {object} range object for find series items
1482
- *
1483
- * @returns {object}
1484
- */
1485
- findSelectedItems(range) {
1486
- const items = [];
1487
- const sIds = Object.keys(this.seriesList);
1488
- for (let ix = 0; ix < sIds.length; ix++) {
1489
- const sId = sIds[ix];
1490
- const series = this.seriesList[sId];
1491
- const findFn = series.findItems;
1492
- if (findFn) {
1493
- const item = findFn.call(series, range);
1494
- if (item && item.length) {
1495
- items.push({
1496
- seriesName: series.name,
1497
- seriesId: sId,
1498
- items: item,
1499
- });
1500
- }
1501
- }
1502
- }
1503
-
1504
- return items;
1505
- },
1506
-
1507
- /**
1508
- * Returns the data-based range value for a selection
1509
- * @param {object} object for calculating data-based range
1510
- * object.range: coordinate-based range in graph
1511
- * @returns {object}
1512
- */
1513
- getSelectionRange({ xsp, ysp, width, height, range }) {
1514
- const dataRangeX = this.axesSteps.x.length ? this.axesSteps.x[0] : null;
1515
- const dataRangeY = this.axesSteps.y.length ? this.axesSteps.y[0] : null;
1516
-
1517
- if (!dataRangeX || !dataRangeY) {
1518
- return null;
1519
- }
1520
-
1521
- const xep = xsp + width;
1522
- const yep = ysp + height;
1523
- const graphWidth = dataRangeX.graphMax - dataRangeX.graphMin;
1524
- const graphHeight = dataRangeY.graphMax - dataRangeY.graphMin;
1525
-
1526
- const xMinRatio = this.getRatioInRange(range.x1, range.x2, xsp);
1527
- const xMaxRatio = this.getRatioInRange(range.x1, range.x2, xep);
1528
- const yMinRatio = this.getRatioInRange(range.y1, range.y2, yep);
1529
- const yMaxRatio = this.getRatioInRange(range.y1, range.y2, ysp);
1530
-
1531
- const xMin = this.isMobile && this.boxOverflow?.x1
1532
- ? Math.min(this.minMax.x[0].min, dataRangeX.graphMin)
1533
- : Math.max(dataRangeX.graphMin + graphWidth * xMinRatio, dataRangeX.graphMin);
1534
- const xMax = this.isMobile && this.boxOverflow?.x2
1535
- ? Math.max(this.minMax.x[0].max, dataRangeX.graphMax)
1536
- : Math.min(dataRangeX.graphMin + graphWidth * xMaxRatio, dataRangeX.graphMax);
1537
- const yMin = this.isMobile && this.boxOverflow?.y2
1538
- ? Math.min(this.minMax.y[0].min, dataRangeY.graphMin)
1539
- : Math.max(dataRangeY.graphMin + graphHeight * (1 - yMinRatio), dataRangeY.graphMin);
1540
- const yMax = this.isMobile && this.boxOverflow?.y1
1541
- ? Math.max(this.minMax.y[0].max, dataRangeY.graphMax)
1542
- : Math.min(dataRangeY.graphMin + graphHeight * (1 - yMaxRatio), dataRangeY.graphMax);
1543
-
1544
- return {
1545
- xMin: +xMin.toFixed(3),
1546
- xMax: +xMax.toFixed(3),
1547
- yMin: +yMin.toFixed(3),
1548
- yMax: +yMax.toFixed(3),
1549
- };
1550
- },
1551
-
1552
- /**
1553
- * Returns the position ratio of 'value' between 'min' and 'max'
1554
- * @param {number} min min value
1555
- * @param {number} max max value
1556
- * @param {number} value value is between min and max
1557
- *
1558
- * @returns {number}
1559
- */
1560
- getRatioInRange(min, max, value) {
1561
- const total = max - min;
1562
- const targetValue = value - min;
1563
-
1564
- return targetValue / total;
1565
- },
1566
-
1567
- getDragInfoForHeatMap(range) {
1568
- const sId = Object.keys(this.seriesList)[0];
1569
- return this.seriesList[sId].findBlockRange(range);
1570
- },
1571
-
1572
- getSelectionRangeForHeatMap(range) {
1573
- const dataRangeX = this.axesSteps.x.length ? this.axesSteps.x[0] : null;
1574
- const dataRangeY = this.axesSteps.y.length ? this.axesSteps.y[0] : null;
1575
-
1576
- if (!dataRangeX || !dataRangeY) {
1577
- return null;
1578
- }
1579
-
1580
- const sId = Object.keys(this.seriesList)[0];
1581
- const { xMin, xMax, yMin, yMax } = this.seriesList[sId].findSelectionRange(range) ?? {};
1582
-
1583
- return {
1584
- xMin: xMin ?? dataRangeX.graphMin,
1585
- xMax: xMax ?? dataRangeX.graphMax,
1586
- yMin: yMin ?? dataRangeY.graphMin,
1587
- yMax: yMax ?? dataRangeY.graphMax,
1588
- };
1589
- },
1590
-
1591
- /**
1592
- * Check hitInfo is deselected Item through re-click
1593
- * @param hitInfo
1594
- * @returns {boolean}
1595
- */
1596
- isDeselectItem(hitInfo) {
1597
- return this.options.selectItem.useDeselectItem
1598
- && hitInfo?.maxIndex === this.defaultSelectItemInfo?.dataIndex
1599
- && hitInfo?.sId === this.defaultSelectItemInfo?.seriesID
1600
- && !isNaN(hitInfo?.maxIndex);
1601
- },
1602
-
1603
- /**
1604
- * Get current mouse location (xAxis, yAxis, chartBackground, canvas)
1605
- * @param offset
1606
- * @returns {string}
1607
- */
1608
- getCurMouseLocation(offset) {
1609
- const [offsetX, offsetY] = offset;
1610
-
1611
- const aPos = {
1612
- x1: this.chartRect.x1 + this.labelOffset.left,
1613
- x2: this.chartRect.x2 - this.labelOffset.right,
1614
- y1: this.chartRect.y1 + this.labelOffset.top,
1615
- y2: this.chartRect.y2 - this.labelOffset.bottom,
1616
- };
1617
- const xAxisStartPoint = aPos[this.axesX[0].units.rectStart];
1618
- const xAxisEndPoint = aPos[this.axesX[0].units.rectEnd];
1619
- const yAxisStartPoint = aPos[this.axesY[0].units.rectStart];
1620
- const yAxisEndPoint = aPos[this.axesY[0].units.rectEnd];
1621
-
1622
- if (
1623
- inRange(offsetX, this.chartRect.x1, aPos.x1)
1624
- && inRange(offsetY, yAxisStartPoint, yAxisEndPoint)
1625
- ) {
1626
- return 'yAxis';
1627
- } else if (
1628
- inRange(offsetX, xAxisStartPoint, xAxisEndPoint)
1629
- && inRange(offsetY, aPos.y2, this.chartRect.y2)) {
1630
- return 'xAxis';
1631
- } else if (
1632
- inRange(offsetX, aPos.x1, aPos.x2)
1633
- && inRange(offsetY, aPos.y1, aPos.y2)) {
1634
- return 'chartBackground';
1635
- }
1636
-
1637
- return 'canvas';
1638
- },
1639
-
1640
- /**
1641
- * Clear 'defaultSelectInfo'
1642
- */
1643
- clearSelectedLabelInfo() {
1644
- this.defaultSelectInfo = { dataIndex: [] };
1645
- },
1646
-
1647
- /**
1648
- * Clear 'defaultSelectItemInfo'
1649
- */
1650
- clearSelectedItemInfo() {
1651
- this.defaultSelectItemInfo = null;
1652
- },
1653
- };
1654
-
1655
- export default modules;