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