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,1041 +0,0 @@
|
|
|
1
|
-
import { convertToPercent } from '@/common/utils';
|
|
2
|
-
import debounce from '@/common/utils.debounce';
|
|
3
|
-
import { inRange } from 'lodash-es';
|
|
4
|
-
import Canvas from '../helpers/helpers.canvas';
|
|
5
|
-
import Util from '../helpers/helpers.util';
|
|
6
|
-
|
|
7
|
-
const LINE_SPACING = 8;
|
|
8
|
-
const VALUE_MARGIN = 50;
|
|
9
|
-
const SCROLL_WIDTH = 17;
|
|
10
|
-
const BODY_PADDING = 8;
|
|
11
|
-
|
|
12
|
-
const modules = {
|
|
13
|
-
/**
|
|
14
|
-
* Create tooltip DOM
|
|
15
|
-
*
|
|
16
|
-
* @returns {undefined}
|
|
17
|
-
*/
|
|
18
|
-
createTooltipDOM() {
|
|
19
|
-
this.tooltipDOM = document.createElement('div');
|
|
20
|
-
this.tooltipDOM.className = 'ev-chart-tooltip';
|
|
21
|
-
|
|
22
|
-
this.tooltipHeaderDOM = document.createElement('div');
|
|
23
|
-
this.tooltipHeaderDOM.className = 'ev-chart-tooltip-header';
|
|
24
|
-
|
|
25
|
-
this.tooltipBodyDOM = document.createElement('div');
|
|
26
|
-
this.tooltipBodyDOM.className = 'ev-chart-tooltip-body';
|
|
27
|
-
|
|
28
|
-
this.tooltipCanvas = document.createElement('canvas');
|
|
29
|
-
this.tooltipCanvas.className = 'ev-chart-tooltip-canvas';
|
|
30
|
-
this.tooltipCtx = this.tooltipCanvas.getContext('2d');
|
|
31
|
-
|
|
32
|
-
this.tooltipDOM.style.display = 'none';
|
|
33
|
-
|
|
34
|
-
if (!this.options.tooltip?.formatter?.html) {
|
|
35
|
-
this.setDefaultTooltipLayout();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
document.body.appendChild(this.tooltipDOM);
|
|
39
|
-
|
|
40
|
-
if (this.options.tooltip.debouncedHide) {
|
|
41
|
-
this.hideTooltipDOM = debounce(() => {
|
|
42
|
-
this.tooltipDOM.style.display = 'none';
|
|
43
|
-
}, 200);
|
|
44
|
-
} else {
|
|
45
|
-
this.hideTooltipDOM = () => {
|
|
46
|
-
this.tooltipDOM.style.display = 'none';
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
this.isInitTooltip = true;
|
|
50
|
-
},
|
|
51
|
-
|
|
52
|
-
setDefaultTooltipLayout() {
|
|
53
|
-
this.tooltipBodyDOM.appendChild(this.tooltipCanvas);
|
|
54
|
-
this.tooltipDOM.appendChild(this.tooltipHeaderDOM);
|
|
55
|
-
this.tooltipDOM.appendChild(this.tooltipBodyDOM);
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* get Tooltip's font style by Type ('title' | 'contents')
|
|
60
|
-
* @param {string} type 'title' | 'contents'
|
|
61
|
-
* @returns {string}
|
|
62
|
-
*/
|
|
63
|
-
getFontStyle(type) {
|
|
64
|
-
const opt = this.options?.tooltip;
|
|
65
|
-
const fontSize = opt?.fontSize?.[type] ?? 14;
|
|
66
|
-
const fontFamily = opt?.fontFamily ?? 'Roboto';
|
|
67
|
-
|
|
68
|
-
return `normal normal lighter ${Math.max(fontSize, 0)}px ${fontFamily}`;
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
getTextHeight() {
|
|
72
|
-
return (this.options?.tooltip?.fontSize?.contents ?? 14) + 6;
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
getColorMargin() {
|
|
76
|
-
return (this.options?.tooltip?.fontSize?.contents ?? 14) + 2;
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
getBoxPadding() {
|
|
80
|
-
const {
|
|
81
|
-
top = 0,
|
|
82
|
-
right = 20,
|
|
83
|
-
bottom = 3,
|
|
84
|
-
left = 16,
|
|
85
|
-
} = this.options?.tooltip?.rowPadding ?? {};
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
t: top,
|
|
89
|
-
l: left,
|
|
90
|
-
b: bottom,
|
|
91
|
-
r: right,
|
|
92
|
-
};
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Set tooltip DOM's position and style
|
|
97
|
-
* @param {object} hitInfo value and mouse position touched
|
|
98
|
-
* @param {object} e mousemove callback
|
|
99
|
-
*
|
|
100
|
-
* @returns {object} tooltip layout information
|
|
101
|
-
*/
|
|
102
|
-
setTooltipLayoutPosition(hitInfo, e) {
|
|
103
|
-
const ctx = this.tooltipCtx;
|
|
104
|
-
const mouseX = e.pageX;
|
|
105
|
-
const mouseY = e.pageY;
|
|
106
|
-
const items = hitInfo.items;
|
|
107
|
-
const [maxSeries, maxValue] = hitInfo.maxTip;
|
|
108
|
-
const seriesKeys = Object.keys(items);
|
|
109
|
-
const seriesLen = seriesKeys.length;
|
|
110
|
-
const boxPadding = this.getBoxPadding();
|
|
111
|
-
const opt = this.options.tooltip;
|
|
112
|
-
const seriesColorMarginRight = this.getColorMargin();
|
|
113
|
-
|
|
114
|
-
// Draw hidden tooltip header DOM to calculate height
|
|
115
|
-
const sId = hitInfo.hitId;
|
|
116
|
-
const hitItem = items[sId].data;
|
|
117
|
-
const hitAxis = items[sId].axis;
|
|
118
|
-
const titleFormatter = opt.formatter?.title;
|
|
119
|
-
|
|
120
|
-
if (this.axesX.length && this.axesY.length && opt.showHeader) {
|
|
121
|
-
if (titleFormatter) {
|
|
122
|
-
this.tooltipHeaderDOM.textContent = titleFormatter({
|
|
123
|
-
x: hitItem.x,
|
|
124
|
-
y: hitItem.y,
|
|
125
|
-
});
|
|
126
|
-
} else {
|
|
127
|
-
this.tooltipHeaderDOM.textContent = this.options.horizontal
|
|
128
|
-
? this.axesY[hitAxis.y].getLabelFormat(hitItem.y)
|
|
129
|
-
: this.axesX[hitAxis.x].getLabelFormat(hitItem.x);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (opt.textOverflow) {
|
|
134
|
-
this.tooltipHeaderDOM.classList.add(`ev-chart-tooltip-header--${opt.textOverflow}`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
this.tooltipHeaderDOM.style.visibility = 'hidden';
|
|
138
|
-
|
|
139
|
-
// calculate and decide width of canvas El(contentsWidth)
|
|
140
|
-
ctx.save();
|
|
141
|
-
ctx.font = this.getFontStyle('contents');
|
|
142
|
-
const isHorizontal = !!this.options.horizontal;
|
|
143
|
-
const label = isHorizontal ? items[hitInfo.hitId]?.data?.y : items[hitInfo.hitId]?.data?.x;
|
|
144
|
-
const tooltipValue = label?.length > maxSeries.length ? label : maxSeries;
|
|
145
|
-
const nw = Math.round(ctx.measureText(tooltipValue).width);
|
|
146
|
-
const vw = Math.round(ctx.measureText(maxValue).width);
|
|
147
|
-
const expectedContentsWidth = nw + vw + boxPadding.l + boxPadding.r
|
|
148
|
-
+ seriesColorMarginRight + VALUE_MARGIN + SCROLL_WIDTH;
|
|
149
|
-
const contentsWidth = expectedContentsWidth > opt.maxWidth
|
|
150
|
-
? opt.maxWidth
|
|
151
|
-
: expectedContentsWidth;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
// Calculate height of canvas El(tooltip body El) with wrapped line count
|
|
155
|
-
let textLineCnt = opt.textOverflow === 'wrap' ? 0 : seriesLen;
|
|
156
|
-
|
|
157
|
-
if (opt.textOverflow === 'wrap') {
|
|
158
|
-
const seriesNameSpaceWidth = opt.maxWidth - (Math.round(ctx.measureText(maxValue).width)
|
|
159
|
-
+ boxPadding.l + boxPadding.r + seriesColorMarginRight + VALUE_MARGIN + SCROLL_WIDTH);
|
|
160
|
-
|
|
161
|
-
// count wrap line
|
|
162
|
-
const seriesNames = Object.values(items).map(s => s.name);
|
|
163
|
-
seriesNames.forEach((name) => {
|
|
164
|
-
if (ctx.measureText(name).width > seriesNameSpaceWidth) {
|
|
165
|
-
let line = '';
|
|
166
|
-
for (let jx = 0; jx < name.length; jx++) {
|
|
167
|
-
const char = name[jx];
|
|
168
|
-
const temp = `${line}${char}`;
|
|
169
|
-
if (ctx.measureText(temp).width > seriesNameSpaceWidth) {
|
|
170
|
-
line = char;
|
|
171
|
-
textLineCnt += 1;
|
|
172
|
-
} else {
|
|
173
|
-
line = temp;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
textLineCnt += 1;
|
|
178
|
-
});
|
|
179
|
-
ctx.restore();
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
// Calculate height of canvas El(tooltip body El) with useScrollbar, maxHeight option
|
|
184
|
-
const expectedContentsHeight = boxPadding.t
|
|
185
|
-
+ (textLineCnt * this.getTextHeight())
|
|
186
|
-
+ (seriesLen * LINE_SPACING)
|
|
187
|
-
+ boxPadding.b;
|
|
188
|
-
|
|
189
|
-
let contentsHeight;
|
|
190
|
-
if (opt.useScrollbar && expectedContentsHeight > opt.maxHeight) {
|
|
191
|
-
this.tooltipBodyDOM.style.overflowY = 'auto';
|
|
192
|
-
contentsHeight = opt.maxHeight;
|
|
193
|
-
} else {
|
|
194
|
-
this.tooltipBodyDOM.style.overflowY = 'hidden';
|
|
195
|
-
contentsHeight = expectedContentsHeight;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// set width / height to all DOM elements (canvas, tooltip(wrapper), header, body)
|
|
199
|
-
this.tooltipCanvas.width = contentsWidth * this.pixelRatio;
|
|
200
|
-
this.tooltipCanvas.height = expectedContentsHeight * this.pixelRatio;
|
|
201
|
-
this.tooltipCanvas.style.width = `${contentsWidth}px`;
|
|
202
|
-
this.tooltipCanvas.style.height = `${expectedContentsHeight}px`;
|
|
203
|
-
this.tooltipHeaderDOM.style.width = `${contentsWidth}px`;
|
|
204
|
-
this.tooltipHeaderDOM.style.height = 'auto';
|
|
205
|
-
this.tooltipDOM.style.height = 'auto';
|
|
206
|
-
this.tooltipBodyDOM.style.height = `${contentsHeight + 6}px`;
|
|
207
|
-
this.tooltipDOM.style.display = 'block';
|
|
208
|
-
|
|
209
|
-
// set tooltipDOM's positions
|
|
210
|
-
const bodyWidth = document.body.clientWidth;
|
|
211
|
-
const bodyHeight = document.body.clientHeight;
|
|
212
|
-
const distanceMouseAndTooltip = 20;
|
|
213
|
-
const tooltipDOMHeight = this.tooltipDOM?.offsetHeight
|
|
214
|
-
|| this.tooltipHeaderDOM?.offsetHeight + contentsHeight + BODY_PADDING;
|
|
215
|
-
const maximumPosX = bodyWidth - contentsWidth - distanceMouseAndTooltip;
|
|
216
|
-
const maximumPosY = bodyHeight - tooltipDOMHeight - distanceMouseAndTooltip;
|
|
217
|
-
const expectedPosX = mouseX + distanceMouseAndTooltip;
|
|
218
|
-
const expectedPosY = mouseY + distanceMouseAndTooltip;
|
|
219
|
-
const reversedPosX = mouseX - contentsWidth - distanceMouseAndTooltip;
|
|
220
|
-
const reversedPosY = mouseY - tooltipDOMHeight - distanceMouseAndTooltip;
|
|
221
|
-
this.tooltipDOM.style.left = expectedPosX > maximumPosX
|
|
222
|
-
? `${reversedPosX}px`
|
|
223
|
-
: `${expectedPosX}px`;
|
|
224
|
-
this.tooltipDOM.style.top = expectedPosY > maximumPosY
|
|
225
|
-
? `${reversedPosY}px`
|
|
226
|
-
: `${expectedPosY}px`;
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Draw series color shape
|
|
231
|
-
* @param {object} context tooltip canvas context
|
|
232
|
-
* @param {string} shape // 'circle' | 'rect' (default)
|
|
233
|
-
* @param {object} centerPosition // {x: number, y: number}
|
|
234
|
-
*/
|
|
235
|
-
drawSeriesColorShape(context, shape, centerPosition) {
|
|
236
|
-
const fontSize = this.options.tooltip?.fontSize?.contents;
|
|
237
|
-
const { x, y } = centerPosition;
|
|
238
|
-
|
|
239
|
-
if (shape === 'circle') {
|
|
240
|
-
context.beginPath();
|
|
241
|
-
const circleSize = fontSize / 2;
|
|
242
|
-
context.arc(x, y - (circleSize / 2), circleSize, 0, 2 * Math.PI);
|
|
243
|
-
context.fill();
|
|
244
|
-
} else {
|
|
245
|
-
const rectSize = fontSize;
|
|
246
|
-
context.fillRect(x - (rectSize / 3), y - (rectSize / 1.2), rectSize, rectSize);
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Draw tooltip canvas
|
|
252
|
-
* @param {object} hitInfo mousemove callback
|
|
253
|
-
* @param {object} context tooltip canvas context
|
|
254
|
-
*
|
|
255
|
-
* @returns {undefined}
|
|
256
|
-
*/
|
|
257
|
-
drawTooltip(hitInfo, context) {
|
|
258
|
-
const ctx = context;
|
|
259
|
-
const items = hitInfo.items;
|
|
260
|
-
const [, maxValue] = hitInfo.maxTip;
|
|
261
|
-
const seriesKeys = this.alignSeriesList(Object.keys(items));
|
|
262
|
-
const boxPadding = this.getBoxPadding();
|
|
263
|
-
const isHorizontal = this.options.horizontal;
|
|
264
|
-
const opt = this.options.tooltip;
|
|
265
|
-
const textHeight = this.getTextHeight();
|
|
266
|
-
const seriesColorMarginRight = this.getColorMargin();
|
|
267
|
-
|
|
268
|
-
// draw Tooltip header DOM
|
|
269
|
-
if (this.axesX.length && this.axesY.length && opt.showHeader) {
|
|
270
|
-
this.tooltipHeaderDOM.style.visibility = 'visible';
|
|
271
|
-
} else {
|
|
272
|
-
this.tooltipHeaderDOM.style.display = 'none';
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// draw tooltip contents (series, value combination)
|
|
276
|
-
let x = 2;
|
|
277
|
-
let y = 2;
|
|
278
|
-
|
|
279
|
-
x += Util.aliasPixel(x);
|
|
280
|
-
y += Util.aliasPixel(y);
|
|
281
|
-
|
|
282
|
-
ctx.save();
|
|
283
|
-
ctx.scale(this.pixelRatio, this.pixelRatio);
|
|
284
|
-
|
|
285
|
-
if (this.tooltipBodyDOM.style.overflowY === 'auto') {
|
|
286
|
-
boxPadding.r += SCROLL_WIDTH;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
x += boxPadding.l;
|
|
290
|
-
y += boxPadding.t;
|
|
291
|
-
|
|
292
|
-
ctx.font = this.getFontStyle('contents');
|
|
293
|
-
|
|
294
|
-
const seriesList = [];
|
|
295
|
-
seriesKeys.forEach((seriesName) => {
|
|
296
|
-
seriesList.push({
|
|
297
|
-
id: seriesName,
|
|
298
|
-
data: items[seriesName].data,
|
|
299
|
-
color: items[seriesName].color,
|
|
300
|
-
name: items[seriesName].name,
|
|
301
|
-
dataId: items[seriesName].id,
|
|
302
|
-
});
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
if (opt.sortByValue) {
|
|
306
|
-
seriesList.sort((a, b) => {
|
|
307
|
-
let prev = a.data.o;
|
|
308
|
-
let next = b.data.o;
|
|
309
|
-
|
|
310
|
-
if (prev === null || prev === undefined) {
|
|
311
|
-
prev = isHorizontal ? a.data.x : a.data.y;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (next === null || next === undefined) {
|
|
315
|
-
next = isHorizontal ? b.data.x : b.data.y;
|
|
316
|
-
}
|
|
317
|
-
return next - prev;
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
this.setTooltipDOMStyle(opt);
|
|
322
|
-
|
|
323
|
-
let textLineCnt = 1;
|
|
324
|
-
for (let ix = 0; ix < seriesList.length; ix++) {
|
|
325
|
-
const gdata = seriesList[ix].data;
|
|
326
|
-
const color = seriesList[ix].color;
|
|
327
|
-
const name = seriesList[ix].name;
|
|
328
|
-
const valueText = gdata.formatted;
|
|
329
|
-
|
|
330
|
-
let itemX = x + 4;
|
|
331
|
-
let itemY = y + (textLineCnt * textHeight);
|
|
332
|
-
itemX += Util.aliasPixel(itemX);
|
|
333
|
-
itemY += Util.aliasPixel(itemY);
|
|
334
|
-
|
|
335
|
-
ctx.beginPath();
|
|
336
|
-
|
|
337
|
-
if (typeof color !== 'string') {
|
|
338
|
-
ctx.fillStyle = Canvas.createGradient(
|
|
339
|
-
ctx,
|
|
340
|
-
isHorizontal,
|
|
341
|
-
{ x: itemX - 4, y: itemY, w: 12, h: -12 },
|
|
342
|
-
color,
|
|
343
|
-
);
|
|
344
|
-
} else {
|
|
345
|
-
ctx.fillStyle = color;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const curTooltipInfo = {
|
|
349
|
-
id: seriesList[ix].id,
|
|
350
|
-
name: seriesList[ix].name,
|
|
351
|
-
value: valueText,
|
|
352
|
-
dataId: seriesList[ix].dataId,
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
// 1. Draw series color
|
|
356
|
-
this.drawSeriesColorShape(ctx, opt.colorShape, { x: itemX, y: itemY });
|
|
357
|
-
|
|
358
|
-
// 2. Draw series name
|
|
359
|
-
ctx.fillStyle = typeof opt.fontColor.label === 'function' ? opt.fontColor.label(curTooltipInfo) : opt.fontColor.label ?? opt.fontColor;
|
|
360
|
-
|
|
361
|
-
const seriesNameSpaceWidth = opt.maxWidth - Math.round(ctx.measureText(maxValue).width)
|
|
362
|
-
- boxPadding.l - boxPadding.r - seriesColorMarginRight - VALUE_MARGIN;
|
|
363
|
-
const xPos = itemX + seriesColorMarginRight;
|
|
364
|
-
const yPos = itemY;
|
|
365
|
-
|
|
366
|
-
if (seriesNameSpaceWidth > ctx.measureText(name).width) { // draw normally
|
|
367
|
-
ctx.fillText(name, xPos, yPos);
|
|
368
|
-
} else if (opt.textOverflow === 'wrap') { // draw with wrap
|
|
369
|
-
let line = '';
|
|
370
|
-
let yPosWithWrap = yPos;
|
|
371
|
-
|
|
372
|
-
for (let jx = 0; jx < name.length; jx++) {
|
|
373
|
-
const char = name[jx];
|
|
374
|
-
const temp = `${line}${char}`;
|
|
375
|
-
|
|
376
|
-
if (ctx.measureText(temp).width > seriesNameSpaceWidth) {
|
|
377
|
-
ctx.fillText(line, xPos, yPosWithWrap);
|
|
378
|
-
line = char;
|
|
379
|
-
textLineCnt += 1;
|
|
380
|
-
yPosWithWrap += textHeight;
|
|
381
|
-
} else {
|
|
382
|
-
line = temp;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
ctx.fillText(line, xPos, yPosWithWrap);
|
|
386
|
-
} else { // draw with ellipsis
|
|
387
|
-
const shortSeriesName = Util.truncateLabelWithEllipsis(name, seriesNameSpaceWidth, ctx);
|
|
388
|
-
ctx.fillText(shortSeriesName, xPos, yPos);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
ctx.save();
|
|
392
|
-
|
|
393
|
-
// 3. Draw value
|
|
394
|
-
ctx.fillStyle = typeof opt.fontColor.value === 'function' ? opt.fontColor.value(curTooltipInfo) : opt.fontColor.value ?? opt.fontColor;
|
|
395
|
-
ctx.textAlign = 'right';
|
|
396
|
-
ctx.fillText(valueText, this.tooltipDOM.offsetWidth - boxPadding.r, itemY);
|
|
397
|
-
ctx.restore();
|
|
398
|
-
ctx.closePath();
|
|
399
|
-
|
|
400
|
-
// 4. add lineSpacing
|
|
401
|
-
y += LINE_SPACING;
|
|
402
|
-
textLineCnt += 1;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
ctx.restore();
|
|
406
|
-
},
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* Draw tooltip canvas for heatmap
|
|
410
|
-
* @param {object} hitInfo mousemove callback
|
|
411
|
-
* @param {object} context tooltip canvas context
|
|
412
|
-
*
|
|
413
|
-
* @returns {undefined}
|
|
414
|
-
*/
|
|
415
|
-
drawToolTipForHeatMap(hitInfo, context) {
|
|
416
|
-
const ctx = context;
|
|
417
|
-
const items = hitInfo.items;
|
|
418
|
-
const sId = hitInfo.hitId;
|
|
419
|
-
const hitItem = items[sId].data;
|
|
420
|
-
const hitAxis = items[sId].axis;
|
|
421
|
-
const hitColor = items[sId].color;
|
|
422
|
-
const boxPadding = this.getBoxPadding();
|
|
423
|
-
const isHorizontal = this.options.horizontal;
|
|
424
|
-
const opt = this.options.tooltip;
|
|
425
|
-
const series = Object.values(this.seriesList)[0];
|
|
426
|
-
const textHeight = this.getTextHeight();
|
|
427
|
-
const seriesColorMarginRight = this.getColorMargin();
|
|
428
|
-
|
|
429
|
-
let isShow = false;
|
|
430
|
-
let valueText = hitItem.formatted;
|
|
431
|
-
const { colorState, isGradient } = series;
|
|
432
|
-
if (isGradient) {
|
|
433
|
-
const { min, max } = series.valueOpt;
|
|
434
|
-
const ratio = convertToPercent(hitItem.o - min, max - min);
|
|
435
|
-
const { start, end } = colorState[0];
|
|
436
|
-
isShow = (start <= ratio && ratio <= end) || hitItem.o === -1;
|
|
437
|
-
} else {
|
|
438
|
-
const colorItem = colorState.find(({ id }) => id === hitItem.cId);
|
|
439
|
-
isShow = colorItem?.show;
|
|
440
|
-
valueText = colorItem?.label ?? valueText;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (!isShow) {
|
|
444
|
-
this.tooltipClear();
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// draw Tooltip header DOM
|
|
449
|
-
if (this.axesX.length && this.axesY.length && opt.showHeader) {
|
|
450
|
-
this.tooltipHeaderDOM.style.visibility = 'visible';
|
|
451
|
-
} else {
|
|
452
|
-
this.tooltipHeaderDOM.style.display = 'none';
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
this.setTooltipDOMStyle(opt);
|
|
456
|
-
|
|
457
|
-
// draw tooltip contents (series, value combination)
|
|
458
|
-
ctx.save();
|
|
459
|
-
ctx.scale(this.pixelRatio, this.pixelRatio);
|
|
460
|
-
|
|
461
|
-
if (this.tooltipBodyDOM.style.overflowY === 'auto') {
|
|
462
|
-
boxPadding.r += SCROLL_WIDTH;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
const itemX = boxPadding.l + 2;
|
|
466
|
-
const itemY = boxPadding.t + textHeight + 2;
|
|
467
|
-
|
|
468
|
-
ctx.font = this.getFontStyle('contents');
|
|
469
|
-
|
|
470
|
-
ctx.beginPath();
|
|
471
|
-
|
|
472
|
-
if (typeof hitColor !== 'string') {
|
|
473
|
-
ctx.fillStyle = Canvas.createGradient(
|
|
474
|
-
ctx,
|
|
475
|
-
isHorizontal,
|
|
476
|
-
{ x: itemX, y: itemY, w: 12, h: -12 },
|
|
477
|
-
hitColor,
|
|
478
|
-
);
|
|
479
|
-
} else {
|
|
480
|
-
ctx.fillStyle = hitColor;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const curTooltipInfo = {
|
|
484
|
-
id: hitInfo.hitId,
|
|
485
|
-
name: hitItem.y,
|
|
486
|
-
value: valueText,
|
|
487
|
-
dataId: items[sId].id,
|
|
488
|
-
};
|
|
489
|
-
|
|
490
|
-
// 1. Draw value color
|
|
491
|
-
this.drawSeriesColorShape(ctx, opt.colorShape, { x: itemX, y: itemY });
|
|
492
|
-
|
|
493
|
-
// 2. Draw value y names
|
|
494
|
-
ctx.fillStyle = typeof opt.fontColor.label === 'function' ? opt.fontColor.label(curTooltipInfo) : opt.fontColor.label ?? opt.fontColor;
|
|
495
|
-
|
|
496
|
-
if (this.axesY.length) {
|
|
497
|
-
ctx.fillText(
|
|
498
|
-
this.axesY[hitAxis.y].getLabelFormat(hitItem.y),
|
|
499
|
-
itemX + seriesColorMarginRight, itemY,
|
|
500
|
-
);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// 3. Draw value
|
|
504
|
-
ctx.textAlign = 'right';
|
|
505
|
-
ctx.fillStyle = typeof opt.fontColor.value === 'function' ? opt.fontColor.value(curTooltipInfo) : opt.fontColor.value ?? opt.fontColor;
|
|
506
|
-
ctx.fillText(valueText, this.tooltipDOM.offsetWidth - boxPadding.r, itemY);
|
|
507
|
-
ctx.closePath();
|
|
508
|
-
},
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
*
|
|
512
|
-
* @param hitInfo
|
|
513
|
-
* @param context
|
|
514
|
-
*/
|
|
515
|
-
drawTooltipForScatter(hitInfo, context) {
|
|
516
|
-
const ctx = context;
|
|
517
|
-
const items = hitInfo.items;
|
|
518
|
-
const [, maxValue] = hitInfo.maxTip;
|
|
519
|
-
const seriesKeys = this.alignSeriesList(Object.keys(items));
|
|
520
|
-
const boxPadding = this.getBoxPadding();
|
|
521
|
-
const opt = this.options.tooltip;
|
|
522
|
-
const textHeight = this.getTextHeight();
|
|
523
|
-
const seriesColorMarginRight = this.getColorMargin();
|
|
524
|
-
|
|
525
|
-
// draw Tooltip header DOM
|
|
526
|
-
if (this.axesX.length && this.axesY.length && opt.showHeader) {
|
|
527
|
-
this.tooltipHeaderDOM.style.visibility = 'visible';
|
|
528
|
-
} else {
|
|
529
|
-
this.tooltipHeaderDOM.style.display = 'none';
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
let x = 2;
|
|
533
|
-
let y = 2;
|
|
534
|
-
|
|
535
|
-
x += Util.aliasPixel(x);
|
|
536
|
-
y += Util.aliasPixel(y);
|
|
537
|
-
|
|
538
|
-
ctx.save();
|
|
539
|
-
ctx.scale(this.pixelRatio, this.pixelRatio);
|
|
540
|
-
|
|
541
|
-
if (this.tooltipBodyDOM.style.overflowY === 'auto') {
|
|
542
|
-
boxPadding.r += SCROLL_WIDTH;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
x += boxPadding.l;
|
|
546
|
-
y += boxPadding.t;
|
|
547
|
-
|
|
548
|
-
ctx.font = this.getFontStyle('contents');
|
|
549
|
-
|
|
550
|
-
const seriesList = [];
|
|
551
|
-
seriesKeys.forEach((seriesName) => {
|
|
552
|
-
seriesList.push({
|
|
553
|
-
data: items[seriesName].data,
|
|
554
|
-
color: items[seriesName].color,
|
|
555
|
-
name: items[seriesName].name,
|
|
556
|
-
});
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
if (opt.sortByValue) {
|
|
560
|
-
seriesList.sort((a, b) => {
|
|
561
|
-
let prev = a.data.o;
|
|
562
|
-
let next = b.data.o;
|
|
563
|
-
|
|
564
|
-
if (prev === null || prev === undefined) {
|
|
565
|
-
prev = a.data.y;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
if (next === null || next === undefined) {
|
|
569
|
-
next = b.data.y;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
return next - prev;
|
|
573
|
-
});
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
this.setTooltipDOMStyle(opt);
|
|
577
|
-
|
|
578
|
-
let textLineCnt = 1;
|
|
579
|
-
for (let ix = 0; ix < seriesList.length; ix++) {
|
|
580
|
-
const gdata = seriesList[ix].data;
|
|
581
|
-
const color = seriesList[ix].color;
|
|
582
|
-
const name = seriesList[ix].name;
|
|
583
|
-
const valueText = gdata.formatted;
|
|
584
|
-
|
|
585
|
-
let itemX = x + 4;
|
|
586
|
-
let itemY = y + (textLineCnt * textHeight);
|
|
587
|
-
itemX += Util.aliasPixel(itemX);
|
|
588
|
-
itemY += Util.aliasPixel(itemY);
|
|
589
|
-
|
|
590
|
-
ctx.beginPath();
|
|
591
|
-
|
|
592
|
-
if (typeof color !== 'string') {
|
|
593
|
-
ctx.fillStyle = Canvas.createGradient(
|
|
594
|
-
ctx,
|
|
595
|
-
false,
|
|
596
|
-
{ x: itemX - 4, y: itemY, w: 12, h: -12 },
|
|
597
|
-
color,
|
|
598
|
-
);
|
|
599
|
-
} else {
|
|
600
|
-
ctx.fillStyle = color;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
const curTooltipInfo = {
|
|
604
|
-
id: hitInfo.hitId,
|
|
605
|
-
name: seriesList[ix].name,
|
|
606
|
-
value: valueText,
|
|
607
|
-
dataId: seriesList[ix].dataId,
|
|
608
|
-
};
|
|
609
|
-
|
|
610
|
-
// 1. Draw series color
|
|
611
|
-
this.drawSeriesColorShape(ctx, opt.colorShape, { x: itemX, y: itemY });
|
|
612
|
-
|
|
613
|
-
// 2. Draw series name
|
|
614
|
-
ctx.fillStyle = typeof opt.fontColor.label === 'function' ? opt.fontColor.label(curTooltipInfo) : opt.fontColor.label ?? opt.fontColor;
|
|
615
|
-
|
|
616
|
-
const seriesNameSpaceWidth = opt.maxWidth - Math.round(ctx.measureText(maxValue).width)
|
|
617
|
-
- boxPadding.l - boxPadding.r - seriesColorMarginRight - VALUE_MARGIN;
|
|
618
|
-
const xPos = itemX + seriesColorMarginRight;
|
|
619
|
-
const yPos = itemY;
|
|
620
|
-
|
|
621
|
-
if (seriesNameSpaceWidth > ctx.measureText(name).width) { // draw normally
|
|
622
|
-
ctx.fillText(name, xPos, yPos);
|
|
623
|
-
} else if (opt.textOverflow === 'wrap') { // draw with wrap
|
|
624
|
-
let line = '';
|
|
625
|
-
let yPosWithWrap = yPos;
|
|
626
|
-
|
|
627
|
-
for (let jx = 0; jx < name.length; jx++) {
|
|
628
|
-
const char = name[jx];
|
|
629
|
-
const temp = `${line}${char}`;
|
|
630
|
-
|
|
631
|
-
if (ctx.measureText(temp).width > seriesNameSpaceWidth) {
|
|
632
|
-
ctx.fillText(line, xPos, yPosWithWrap);
|
|
633
|
-
line = char;
|
|
634
|
-
textLineCnt += 1;
|
|
635
|
-
yPosWithWrap += textHeight;
|
|
636
|
-
} else {
|
|
637
|
-
line = temp;
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
ctx.fillText(line, xPos, yPosWithWrap);
|
|
641
|
-
} else { // draw with ellipsis
|
|
642
|
-
const shortSeriesName = Util.truncateLabelWithEllipsis(name, seriesNameSpaceWidth, ctx);
|
|
643
|
-
ctx.fillText(shortSeriesName, xPos, yPos);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
ctx.save();
|
|
647
|
-
|
|
648
|
-
// 3. Draw value
|
|
649
|
-
ctx.textAlign = 'right';
|
|
650
|
-
ctx.fillStyle = typeof opt.fontColor.value === 'function' ? opt.fontColor.value(curTooltipInfo) : opt.fontColor.value ?? opt.fontColor;
|
|
651
|
-
ctx.fillText(valueText, this.tooltipDOM.offsetWidth - boxPadding.r, itemY);
|
|
652
|
-
ctx.restore();
|
|
653
|
-
ctx.closePath();
|
|
654
|
-
|
|
655
|
-
// 4. add lineSpacing
|
|
656
|
-
y += LINE_SPACING;
|
|
657
|
-
textLineCnt += 1;
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
ctx.restore();
|
|
661
|
-
},
|
|
662
|
-
|
|
663
|
-
setCustomTooltipLayoutPosition(hitInfo, e) {
|
|
664
|
-
const mouseX = e.pageX;
|
|
665
|
-
const mouseY = e.pageY;
|
|
666
|
-
|
|
667
|
-
const customTooltipEl = this.tooltipDOM.getElementsByClassName('ev-chart-tooltip-custom')?.[0];
|
|
668
|
-
if (!customTooltipEl && !this.tooltipDOM) {
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
this.tooltipDOM.style.display = 'block';
|
|
673
|
-
const contentsWidth = customTooltipEl.offsetWidth;
|
|
674
|
-
const contentsHeight = customTooltipEl.offsetHeight;
|
|
675
|
-
|
|
676
|
-
this.tooltipDOM.style.height = 'auto';
|
|
677
|
-
this.tooltipBodyDOM.style.height = `${contentsHeight + 6}px`;
|
|
678
|
-
|
|
679
|
-
const bodyWidth = document.body.clientWidth;
|
|
680
|
-
const bodyHeight = document.body.clientHeight;
|
|
681
|
-
const tooltipDOMSize = this.tooltipDOM?.getBoundingClientRect();
|
|
682
|
-
const distanceMouseAndTooltip = 20;
|
|
683
|
-
const maximumPosX = bodyWidth - contentsWidth - distanceMouseAndTooltip;
|
|
684
|
-
const maximumPosY = bodyHeight - tooltipDOMSize?.height - distanceMouseAndTooltip;
|
|
685
|
-
const expectedPosX = mouseX + distanceMouseAndTooltip;
|
|
686
|
-
const expectedPosY = mouseY + distanceMouseAndTooltip;
|
|
687
|
-
const reversedPosX = mouseX - contentsWidth - distanceMouseAndTooltip;
|
|
688
|
-
const reversedPosY = mouseY - tooltipDOMSize?.height - distanceMouseAndTooltip;
|
|
689
|
-
this.tooltipDOM.style.left = expectedPosX > maximumPosX
|
|
690
|
-
? `${reversedPosX}px`
|
|
691
|
-
: `${expectedPosX}px`;
|
|
692
|
-
this.tooltipDOM.style.top = expectedPosY > maximumPosY
|
|
693
|
-
? `${reversedPosY}px`
|
|
694
|
-
: `${expectedPosY}px`;
|
|
695
|
-
},
|
|
696
|
-
|
|
697
|
-
/**
|
|
698
|
-
* Draw User Custom Tooltip (tooltip > formatter > html)
|
|
699
|
-
* call "formatter > html" and append to tooltip DOM
|
|
700
|
-
* @param hitInfoItems
|
|
701
|
-
*/
|
|
702
|
-
drawCustomTooltip(hitInfoItems) {
|
|
703
|
-
const opt = this.options?.tooltip;
|
|
704
|
-
if (opt.formatter?.html) {
|
|
705
|
-
this.tooltipDOM.innerHTML = '';
|
|
706
|
-
|
|
707
|
-
const seriesList = [];
|
|
708
|
-
Object.keys(hitInfoItems).forEach((sId) => {
|
|
709
|
-
seriesList.push({
|
|
710
|
-
sId,
|
|
711
|
-
data: hitInfoItems[sId].data,
|
|
712
|
-
color: hitInfoItems[sId].color,
|
|
713
|
-
name: hitInfoItems[sId].name,
|
|
714
|
-
dataId: hitInfoItems[sId].id,
|
|
715
|
-
index: hitInfoItems[sId].index,
|
|
716
|
-
});
|
|
717
|
-
});
|
|
718
|
-
|
|
719
|
-
const userCustomTooltipBody = Util.htmlToElement(opt?.formatter?.html((seriesList)));
|
|
720
|
-
if (userCustomTooltipBody) {
|
|
721
|
-
this.tooltipDOM.appendChild(userCustomTooltipBody);
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
this.tooltipDOM.style.overflowY = 'hidden';
|
|
725
|
-
this.tooltipDOM.style.backgroundColor = opt.backgroundColor;
|
|
726
|
-
this.tooltipDOM.style.border = `1px solid ${opt.borderColor}`;
|
|
727
|
-
this.tooltipDOM.style.color = opt.fontColor?.title ?? opt.fontColor;
|
|
728
|
-
}
|
|
729
|
-
},
|
|
730
|
-
|
|
731
|
-
/**
|
|
732
|
-
* set style properties on tooltip DOM
|
|
733
|
-
* @param tooltipOptions
|
|
734
|
-
*/
|
|
735
|
-
setTooltipDOMStyle(tooltipOptions) {
|
|
736
|
-
this.tooltipDOM.style.overflowY = 'hidden';
|
|
737
|
-
this.tooltipDOM.style.backgroundColor = tooltipOptions.backgroundColor;
|
|
738
|
-
this.tooltipDOM.style.border = `1px solid ${tooltipOptions.borderColor}`;
|
|
739
|
-
this.tooltipDOM.style.color = tooltipOptions.fontColor?.title ?? tooltipOptions.fontColor;
|
|
740
|
-
|
|
741
|
-
this.tooltipHeaderDOM.style.fontSize = `${tooltipOptions.fontSize.title}px`;
|
|
742
|
-
this.tooltipHeaderDOM.style.fontFamily = this.getFontStyle('title');
|
|
743
|
-
|
|
744
|
-
if (tooltipOptions.useShadow) {
|
|
745
|
-
const shadowColor = `rgba(0, 0, 0, ${tooltipOptions.shadowOpacity})`;
|
|
746
|
-
this.tooltipDOM.style.boxShadow = `2px 2px 2px ${shadowColor}`;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
this.tooltipDOM.style.display = 'block';
|
|
750
|
-
},
|
|
751
|
-
|
|
752
|
-
/**
|
|
753
|
-
* Draw graph item highlight
|
|
754
|
-
* @param {object} hitInfo mousemove callback
|
|
755
|
-
* @param {object} ctx overlayCanvas context
|
|
756
|
-
*
|
|
757
|
-
* @returns {undefined}
|
|
758
|
-
*/
|
|
759
|
-
drawItemsHighlight(hitInfo, ctx) {
|
|
760
|
-
Object.keys(hitInfo.items).forEach((sId) => {
|
|
761
|
-
const series = this.seriesList[sId];
|
|
762
|
-
series.itemHighlight(hitInfo.items[sId], ctx, hitInfo.items[sId].index);
|
|
763
|
-
|
|
764
|
-
if (Util.isDoughnutHole(series.type)) {
|
|
765
|
-
this.drawDoughnutHole(ctx);
|
|
766
|
-
}
|
|
767
|
-
});
|
|
768
|
-
},
|
|
769
|
-
|
|
770
|
-
/**
|
|
771
|
-
* Draw chart indicator with mousemove
|
|
772
|
-
* @param {object} offset mousemove callback
|
|
773
|
-
* @param {string} color indicator color
|
|
774
|
-
*
|
|
775
|
-
* @returns {undefined}
|
|
776
|
-
*/
|
|
777
|
-
drawIndicator(offset, color) {
|
|
778
|
-
const ctx = this.overlayCtx;
|
|
779
|
-
const [offsetX, offsetY] = offset;
|
|
780
|
-
const graphPos = {
|
|
781
|
-
x1: this.chartRect.x1 + this.labelOffset.left,
|
|
782
|
-
x2: this.chartRect.x2 - this.labelOffset.right,
|
|
783
|
-
y1: this.chartRect.y1 + this.labelOffset.top,
|
|
784
|
-
y2: this.chartRect.y2 - this.labelOffset.bottom,
|
|
785
|
-
};
|
|
786
|
-
const mouseXIp = 15; // mouseInterpolation - 더 넓은 범위에서 감지
|
|
787
|
-
const mouseYIp = 15; // Y축도 동일하게 증가
|
|
788
|
-
const options = this.options;
|
|
789
|
-
|
|
790
|
-
if (offsetX >= (graphPos.x1 - mouseXIp) && offsetX <= (graphPos.x2 + mouseXIp)
|
|
791
|
-
&& offsetY >= (graphPos.y1 - mouseYIp) && offsetY <= (graphPos.y2 + mouseYIp)) {
|
|
792
|
-
ctx.beginPath();
|
|
793
|
-
ctx.save();
|
|
794
|
-
ctx.strokeStyle = color;
|
|
795
|
-
ctx.lineWidth = 1;
|
|
796
|
-
|
|
797
|
-
if (options.indicator?.segments) {
|
|
798
|
-
ctx.setLineDash(options.indicator.segments);
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
if (options.horizontal) {
|
|
802
|
-
ctx.moveTo(graphPos.x1, offsetY + 0.5);
|
|
803
|
-
ctx.lineTo(graphPos.x2, offsetY + 0.5);
|
|
804
|
-
} else {
|
|
805
|
-
ctx.moveTo(offsetX + 0.5, graphPos.y1);
|
|
806
|
-
ctx.lineTo(offsetX + 0.5, graphPos.y2);
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
ctx.stroke();
|
|
810
|
-
ctx.restore();
|
|
811
|
-
ctx.closePath();
|
|
812
|
-
}
|
|
813
|
-
},
|
|
814
|
-
/**
|
|
815
|
-
* Get hovered axis label with mousemove
|
|
816
|
-
* @param {object} offset mousemove callback
|
|
817
|
-
*
|
|
818
|
-
* @returns {number | null} hovered axis label
|
|
819
|
-
*/
|
|
820
|
-
getTimeLabel(offset) {
|
|
821
|
-
const options = this.options;
|
|
822
|
-
|
|
823
|
-
if (
|
|
824
|
-
options.syncHover === false
|
|
825
|
-
|| (!options.horizontal && !options.axesX.every(({ type }) => type === 'time'))
|
|
826
|
-
|| (options.horizontal && !options.axesY.every(({ type }) => type === 'time'))) {
|
|
827
|
-
return null;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
const fromTime = +this.data.labels?.[0];
|
|
831
|
-
const toTime = +this.data.labels?.[this.data.labels.length - 1];
|
|
832
|
-
if (fromTime == null || toTime == null) {
|
|
833
|
-
return null;
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
const [offsetX, offsetY] = offset;
|
|
837
|
-
const graphPos = {
|
|
838
|
-
x1: this.chartRect.x1 + this.labelOffset.left,
|
|
839
|
-
x2: this.chartRect.x2 - this.labelOffset.right,
|
|
840
|
-
y1: this.chartRect.y1 + this.labelOffset.top,
|
|
841
|
-
y2: this.chartRect.y2 - this.labelOffset.bottom,
|
|
842
|
-
};
|
|
843
|
-
|
|
844
|
-
if (options.horizontal) {
|
|
845
|
-
const chartHeight = graphPos.y2 - graphPos.y1;
|
|
846
|
-
const hoverYAxis = offsetY - graphPos.y1;
|
|
847
|
-
const hoverTime = ((hoverYAxis * (toTime - fromTime)) / chartHeight) + fromTime;
|
|
848
|
-
return Math.round(hoverTime);
|
|
849
|
-
}
|
|
850
|
-
const chartWidth = graphPos.x2 - graphPos.x1;
|
|
851
|
-
const hoverXAxis = offsetX - graphPos.x1;
|
|
852
|
-
const hoverTime = ((hoverXAxis * (toTime - fromTime)) / chartWidth) + fromTime;
|
|
853
|
-
return Math.round(hoverTime);
|
|
854
|
-
},
|
|
855
|
-
/**
|
|
856
|
-
* Draw chart indicator with other grouped chart's mousemove
|
|
857
|
-
* @param {object} hoveredLabel chart direction and hovered axis label
|
|
858
|
-
*
|
|
859
|
-
* @returns {undefined}
|
|
860
|
-
*/
|
|
861
|
-
drawSyncedIndicator({ horizontal, label, mousePosition, dataLabel }) {
|
|
862
|
-
if (!this._canDrawSyncedIndicator(horizontal, mousePosition)) {
|
|
863
|
-
return;
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
if (dataLabel) {
|
|
867
|
-
this.drawSyncedIndicatorForTooltip({ dataLabel, mousePosition });
|
|
868
|
-
return;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
const fromTime = +this.data.labels?.[0];
|
|
872
|
-
const toTime = +this.data.labels?.[this.data.labels.length - 1];
|
|
873
|
-
const [clientX, clientY] = mousePosition;
|
|
874
|
-
const { top, bottom, left, right } = this.chartDOM.getBoundingClientRect();
|
|
875
|
-
|
|
876
|
-
const isHoveredChart = inRange(clientX, left, right) && inRange(clientY, bottom, top);
|
|
877
|
-
if (isHoveredChart) {
|
|
878
|
-
return;
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
this.overlayClear();
|
|
882
|
-
const graphPos = {
|
|
883
|
-
x1: this.chartRect.x1 + this.labelOffset.left,
|
|
884
|
-
x2: this.chartRect.x2 - this.labelOffset.right,
|
|
885
|
-
y1: this.chartRect.y1 + this.labelOffset.top,
|
|
886
|
-
y2: this.chartRect.y2 - this.labelOffset.bottom,
|
|
887
|
-
};
|
|
888
|
-
|
|
889
|
-
if (horizontal) {
|
|
890
|
-
const chartHeight = graphPos.y2 - graphPos.y1;
|
|
891
|
-
const offsetY = (chartHeight * (label - fromTime)) / (toTime - fromTime) + graphPos.y1;
|
|
892
|
-
this.drawIndicator([graphPos.x2, offsetY], this.options.indicator.color);
|
|
893
|
-
} else {
|
|
894
|
-
const chartWidth = graphPos.x2 - graphPos.x1;
|
|
895
|
-
const offsetX = (chartWidth * (label - fromTime)) / (toTime - fromTime) + graphPos.x1;
|
|
896
|
-
this.drawIndicator([offsetX, graphPos.y2], this.options.indicator.color);
|
|
897
|
-
}
|
|
898
|
-
},
|
|
899
|
-
|
|
900
|
-
_canDrawSyncedIndicator(horizontal, mousePosition) {
|
|
901
|
-
if (!mousePosition || !!horizontal !== !!this.options.horizontal) {
|
|
902
|
-
return false;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
return this._isTimeBasedSyncEnabled(horizontal) && this._hasValidTimeRange();
|
|
906
|
-
},
|
|
907
|
-
|
|
908
|
-
_isTimeBasedSyncEnabled(horizontal) {
|
|
909
|
-
if (this.options.syncHover === false) return false;
|
|
910
|
-
if (!this.data?.labels?.length) return false;
|
|
911
|
-
|
|
912
|
-
const timeAxes = horizontal
|
|
913
|
-
? this.options.axesY.every(({ type }) => type === 'time')
|
|
914
|
-
: this.options.axesX.every(({ type }) => type === 'time');
|
|
915
|
-
|
|
916
|
-
return timeAxes;
|
|
917
|
-
},
|
|
918
|
-
|
|
919
|
-
_hasValidTimeRange() {
|
|
920
|
-
const fromTime = +this.data.labels?.[0];
|
|
921
|
-
const toTime = +this.data.labels?.[this.data.labels.length - 1];
|
|
922
|
-
return fromTime != null && toTime != null;
|
|
923
|
-
},
|
|
924
|
-
|
|
925
|
-
/**
|
|
926
|
-
* 제공된 dataLabel과 일치하는 Label이 있다면 indicator를 그림
|
|
927
|
-
* @param {object} dataLabel data label
|
|
928
|
-
* @param {object} mousePosition mouse position
|
|
929
|
-
*
|
|
930
|
-
* @returns {undefined}
|
|
931
|
-
*/
|
|
932
|
-
drawSyncedIndicatorForTooltip({ dataLabel, mousePosition }) {
|
|
933
|
-
if (!this.data?.labels || !dataLabel) {
|
|
934
|
-
return;
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
const matchingLabelIndex = this.data.labels.findIndex(
|
|
938
|
-
label => label?.valueOf() === dataLabel?.valueOf(),
|
|
939
|
-
);
|
|
940
|
-
if (matchingLabelIndex === -1) {
|
|
941
|
-
this.overlayClear();
|
|
942
|
-
return;
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
const { horizontal } = this.options;
|
|
946
|
-
const { top, bottom, left, right } = this.chartDOM.getBoundingClientRect();
|
|
947
|
-
const isHoveredChart = inRange(mousePosition[0], left, right)
|
|
948
|
-
&& inRange(mousePosition[1], bottom, top);
|
|
949
|
-
if (isHoveredChart) {
|
|
950
|
-
return;
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
this.overlayClear();
|
|
954
|
-
|
|
955
|
-
const graphPos = {
|
|
956
|
-
x1: this.chartRect.x1 + this.labelOffset.left,
|
|
957
|
-
x2: this.chartRect.x2 - this.labelOffset.right,
|
|
958
|
-
y1: this.chartRect.y1 + this.labelOffset.top,
|
|
959
|
-
y2: this.chartRect.y2 - this.labelOffset.bottom,
|
|
960
|
-
};
|
|
961
|
-
|
|
962
|
-
const labelsCount = this.data.labels.length;
|
|
963
|
-
let indicatorPosition;
|
|
964
|
-
|
|
965
|
-
if (horizontal) {
|
|
966
|
-
const chartHeight = graphPos.y2 - graphPos.y1;
|
|
967
|
-
// CategoryMode인 경우 라벨들이 균등 간격으로 배치됨
|
|
968
|
-
const isCategoryMode = this.options.axesY?.some(axis => axis.categoryMode);
|
|
969
|
-
const positionY = isCategoryMode
|
|
970
|
-
? graphPos.y1 + (chartHeight * (matchingLabelIndex + 0.5)) / labelsCount
|
|
971
|
-
: graphPos.y1 + (chartHeight * matchingLabelIndex) / (labelsCount - 1);
|
|
972
|
-
indicatorPosition = [graphPos.x2, positionY];
|
|
973
|
-
} else {
|
|
974
|
-
const chartWidth = graphPos.x2 - graphPos.x1;
|
|
975
|
-
// CategoryMode인 경우 라벨들이 균등 간격으로 배치됨
|
|
976
|
-
const isCategoryMode = this.options.axesX?.some(axis => axis.categoryMode);
|
|
977
|
-
const positionX = isCategoryMode
|
|
978
|
-
? graphPos.x1 + (chartWidth * (matchingLabelIndex + 0.5)) / labelsCount
|
|
979
|
-
: graphPos.x1 + (chartWidth * matchingLabelIndex) / (labelsCount - 1);
|
|
980
|
-
indicatorPosition = [positionX, graphPos.y2];
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
this.drawIndicator(indicatorPosition, this.options.indicator.color);
|
|
984
|
-
},
|
|
985
|
-
|
|
986
|
-
/**
|
|
987
|
-
* Clear tooltip canvas
|
|
988
|
-
*
|
|
989
|
-
* @returns {undefined}
|
|
990
|
-
*/
|
|
991
|
-
tooltipClear() {
|
|
992
|
-
this.clearRectRatio = (this.pixelRatio < 1) ? this.pixelRatio : 1;
|
|
993
|
-
|
|
994
|
-
this.tooltipCtx.clearRect(0, 0, this.tooltipCanvas.width / this.clearRectRatio,
|
|
995
|
-
this.tooltipCanvas.height / this.clearRectRatio);
|
|
996
|
-
|
|
997
|
-
this.tooltipDOM.style.display = 'none';
|
|
998
|
-
},
|
|
999
|
-
|
|
1000
|
-
/**
|
|
1001
|
-
* Order series list by groups
|
|
1002
|
-
* @param {array} sKeys series list that is hit by mouse cursor. (not all of series)
|
|
1003
|
-
*
|
|
1004
|
-
* @returns {array} ordered series list by groups
|
|
1005
|
-
*/
|
|
1006
|
-
alignSeriesList(sKeys) {
|
|
1007
|
-
const groups = this.data.groups;
|
|
1008
|
-
const seriesList = this.seriesList;
|
|
1009
|
-
const result = [];
|
|
1010
|
-
|
|
1011
|
-
groups.forEach((group) => {
|
|
1012
|
-
group.slice().reverse().forEach((sId) => {
|
|
1013
|
-
const series = seriesList[sId];
|
|
1014
|
-
|
|
1015
|
-
if (series && series.showLegend && sKeys.includes(sId)) {
|
|
1016
|
-
result.push(sId);
|
|
1017
|
-
}
|
|
1018
|
-
});
|
|
1019
|
-
});
|
|
1020
|
-
|
|
1021
|
-
Object.keys(seriesList).forEach((sId) => {
|
|
1022
|
-
const series = seriesList[sId];
|
|
1023
|
-
|
|
1024
|
-
if (!series.isExistGrp && series.showLegend && sKeys.includes(sId)) {
|
|
1025
|
-
result.push(sId);
|
|
1026
|
-
}
|
|
1027
|
-
});
|
|
1028
|
-
|
|
1029
|
-
return result;
|
|
1030
|
-
},
|
|
1031
|
-
|
|
1032
|
-
tooltipDestroy() {
|
|
1033
|
-
if (this.tooltipDOM) {
|
|
1034
|
-
this.tooltipDOM.remove();
|
|
1035
|
-
this.tooltipDOM = null;
|
|
1036
|
-
}
|
|
1037
|
-
this.isInitTooltip = false;
|
|
1038
|
-
},
|
|
1039
|
-
};
|
|
1040
|
-
|
|
1041
|
-
export default modules;
|