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.
- package/README.md +18 -33
- package/dist/404.html +44 -0
- package/dist/favicon.ico +0 -0
- package/dist/index.js +22645 -0
- package/dist/index.umd.cjs +28 -0
- package/dist/style.css +1 -0
- package/package.json +46 -43
- package/dist/evui.common.js +0 -63678
- package/dist/evui.common.js.map +0 -1
- package/dist/evui.umd.js +0 -63688
- package/dist/evui.umd.js.map +0 -1
- package/dist/evui.umd.min.js +0 -2
- package/dist/evui.umd.min.js.map +0 -1
- package/dist/img/EVUI.b82ee81a.svg +0 -293
- package/src/assets/logo.png +0 -0
- package/src/common/emitter.js +0 -20
- package/src/common/utils.bignumber.js +0 -67
- package/src/common/utils.debounce.js +0 -223
- package/src/common/utils.js +0 -151
- package/src/common/utils.table.js +0 -78
- package/src/common/utils.throttle.js +0 -83
- package/src/common/utils.tree.js +0 -18
- package/src/components/button/Button.vue +0 -195
- package/src/components/button/index.js +0 -7
- package/src/components/buttonGroup/ButtonGroup.vue +0 -11
- package/src/components/buttonGroup/index.js +0 -7
- package/src/components/calendar/Calendar.vue +0 -725
- package/src/components/calendar/index.js +0 -7
- package/src/components/calendar/uses.js +0 -1410
- package/src/components/chart/Chart.vue +0 -363
- package/src/components/chart/ChartToolbar.vue +0 -52
- package/src/components/chart/chart.core.js +0 -1170
- package/src/components/chart/chartZoom.core.js +0 -540
- package/src/components/chart/element/element.bar.js +0 -672
- package/src/components/chart/element/element.bar.time.js +0 -166
- package/src/components/chart/element/element.heatmap.js +0 -743
- package/src/components/chart/element/element.line.js +0 -611
- package/src/components/chart/element/element.pie.js +0 -197
- package/src/components/chart/element/element.scatter.js +0 -320
- package/src/components/chart/element/element.tip.js +0 -717
- package/src/components/chart/helpers/helpers.canvas.js +0 -265
- package/src/components/chart/helpers/helpers.constant.js +0 -235
- package/src/components/chart/helpers/helpers.util.js +0 -400
- package/src/components/chart/index.js +0 -9
- package/src/components/chart/model/index.js +0 -50
- package/src/components/chart/model/model.series.js +0 -125
- package/src/components/chart/model/model.store.js +0 -1427
- package/src/components/chart/plugins/plugins.interaction.js +0 -1655
- package/src/components/chart/plugins/plugins.legend.gradient.js +0 -606
- package/src/components/chart/plugins/plugins.legend.js +0 -1543
- package/src/components/chart/plugins/plugins.pie.js +0 -254
- package/src/components/chart/plugins/plugins.scrollbar.js +0 -732
- package/src/components/chart/plugins/plugins.title.js +0 -61
- package/src/components/chart/plugins/plugins.tooltip.js +0 -1041
- package/src/components/chart/scale/scale.js +0 -951
- package/src/components/chart/scale/scale.linear.js +0 -268
- package/src/components/chart/scale/scale.logarithmic.js +0 -135
- package/src/components/chart/scale/scale.step.js +0 -430
- package/src/components/chart/scale/scale.time.category.js +0 -338
- package/src/components/chart/scale/scale.time.js +0 -49
- package/src/components/chart/style/chart.scss +0 -405
- package/src/components/chart/uses.js +0 -721
- package/src/components/chartBrush/ChartBrush.vue +0 -323
- package/src/components/chartBrush/chartBrush.core.js +0 -691
- package/src/components/chartBrush/index.js +0 -9
- package/src/components/chartBrush/uses.js +0 -23
- package/src/components/chartGroup/ChartGroup.vue +0 -144
- package/src/components/chartGroup/index.js +0 -9
- package/src/components/chartGroup/style/chartGroup.scss +0 -5
- package/src/components/chartGroup/uses.js +0 -53
- package/src/components/checkbox/Checkbox.vue +0 -229
- package/src/components/checkbox/index.js +0 -7
- package/src/components/checkboxGroup/CheckboxGroup.vue +0 -44
- package/src/components/checkboxGroup/index.js +0 -7
- package/src/components/contextMenu/ContextMenu.vue +0 -95
- package/src/components/contextMenu/MenuList.vue +0 -182
- package/src/components/contextMenu/index.js +0 -7
- package/src/components/contextMenu/uses.js +0 -223
- package/src/components/datePicker/DatePicker.vue +0 -504
- package/src/components/datePicker/index.js +0 -7
- package/src/components/datePicker/uses.js +0 -460
- package/src/components/grid/Grid.vue +0 -1535
- package/src/components/grid/GridColumnSetting.vue +0 -358
- package/src/components/grid/GridFilterSetting.vue +0 -323
- package/src/components/grid/GridPagination.vue +0 -75
- package/src/components/grid/GridSummary.vue +0 -314
- package/src/components/grid/GridToolbar.vue +0 -35
- package/src/components/grid/icon/icon-option-button.vue +0 -17
- package/src/components/grid/icon/icon-sort-button.vue +0 -67
- package/src/components/grid/index.js +0 -11
- package/src/components/grid/style/grid.scss +0 -417
- package/src/components/grid/uses.js +0 -1629
- package/src/components/icon/Icon.vue +0 -53
- package/src/components/icon/index.js +0 -8
- package/src/components/inputNumber/InputNumber.vue +0 -212
- package/src/components/inputNumber/index.js +0 -7
- package/src/components/inputNumber/uses.js +0 -217
- package/src/components/loading/Loading.vue +0 -125
- package/src/components/loading/index.js +0 -7
- package/src/components/menu/Menu.vue +0 -79
- package/src/components/menu/MenuItem.vue +0 -201
- package/src/components/menu/index.js +0 -7
- package/src/components/message/Message.vue +0 -229
- package/src/components/message/index.js +0 -34
- package/src/components/messageBox/MessageBox.vue +0 -358
- package/src/components/messageBox/index.js +0 -22
- package/src/components/notification/Notification.vue +0 -316
- package/src/components/notification/index.js +0 -49
- package/src/components/pagination/Pagination.vue +0 -317
- package/src/components/pagination/index.js +0 -7
- package/src/components/pagination/pageButton.vue +0 -31
- package/src/components/progress/Progress.vue +0 -139
- package/src/components/progress/index.js +0 -7
- package/src/components/radio/Radio.vue +0 -159
- package/src/components/radio/index.js +0 -7
- package/src/components/radioGroup/RadioGroup.vue +0 -41
- package/src/components/radioGroup/index.js +0 -7
- package/src/components/scheduler/Scheduler.vue +0 -149
- package/src/components/scheduler/index.js +0 -7
- package/src/components/scheduler/uses.js +0 -183
- package/src/components/select/Select.vue +0 -556
- package/src/components/select/index.js +0 -7
- package/src/components/select/uses.js +0 -379
- package/src/components/slider/Slider.vue +0 -505
- package/src/components/slider/index.js +0 -7
- package/src/components/slider/uses.js +0 -391
- package/src/components/tabPanel/TabPanel.vue +0 -74
- package/src/components/tabPanel/index.js +0 -7
- package/src/components/tabs/Tabs.vue +0 -517
- package/src/components/tabs/index.js +0 -7
- package/src/components/textField/TextField.vue +0 -399
- package/src/components/textField/index.js +0 -7
- package/src/components/timePicker/TimePicker.vue +0 -364
- package/src/components/timePicker/index.js +0 -7
- package/src/components/toggle/Toggle.vue +0 -115
- package/src/components/toggle/index.js +0 -7
- package/src/components/tree/Tree.vue +0 -338
- package/src/components/tree/TreeNode.vue +0 -293
- package/src/components/tree/index.js +0 -7
- package/src/components/treeGrid/TreeGrid.vue +0 -1074
- package/src/components/treeGrid/TreeGridNode.vue +0 -349
- package/src/components/treeGrid/TreeGridToolbar.vue +0 -35
- package/src/components/treeGrid/icon/icon-tree.png +0 -0
- package/src/components/treeGrid/index.js +0 -9
- package/src/components/treeGrid/style/treeGrid.scss +0 -277
- package/src/components/treeGrid/uses.js +0 -1178
- package/src/components/window/Window.vue +0 -329
- package/src/components/window/index.js +0 -7
- package/src/components/window/uses.js +0 -908
- package/src/directives/clickoutside.js +0 -90
- package/src/main.js +0 -120
- package/src/style/components/input.scss +0 -108
- package/src/style/functions.scss +0 -3
- package/src/style/index.scss +0 -6
- package/src/style/lib/fonts/EVUI.eot +0 -0
- package/src/style/lib/fonts/EVUI.svg +0 -293
- package/src/style/lib/fonts/EVUI.ttf +0 -0
- package/src/style/lib/fonts/EVUI.woff +0 -0
- package/src/style/lib/icon.css +0 -888
- package/src/style/mixins.scss +0 -94
- package/src/style/themes.scss +0 -69
- 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;
|