evui 3.3.55 → 3.3.57

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.
@@ -0,0 +1,629 @@
1
+ import { defaultsDeep, isEqual, throttle } from 'lodash-es';
2
+ import { truthyNumber } from '@/common/utils';
3
+ import { AXIS_OPTION } from '../helpers/helpers.constant';
4
+ import { checkNullAndUndefined } from '../../../common/utils';
5
+
6
+ const module = {
7
+ /**
8
+ * init scrollbar information
9
+ */
10
+ initScrollbar() {
11
+ if (this.options.axesX?.[0]?.scrollbar?.use) {
12
+ this.initScrollbarInfo(this.options.axesX, 'x');
13
+ }
14
+
15
+ if (this.options.axesY?.[0]?.scrollbar?.use) {
16
+ this.initScrollbarInfo(this.options.axesY, 'y');
17
+ }
18
+ },
19
+
20
+ /**
21
+ * init scrollbar information with axis information
22
+ * @param axisOpt axis option
23
+ * @param dir axis direction (x | y)
24
+ */
25
+ initScrollbarInfo(axisOpt, dir) {
26
+ const scrollbarOpt = this.scrollbar[dir];
27
+
28
+ if (!scrollbarOpt.isInit) {
29
+ const merged = defaultsDeep({}, axisOpt?.[0]?.scrollbar, AXIS_OPTION.scrollbar);
30
+ Object.keys(merged).forEach((key) => {
31
+ scrollbarOpt[key] = merged[key];
32
+ });
33
+
34
+ scrollbarOpt.type = axisOpt?.[0]?.type;
35
+ scrollbarOpt.range = axisOpt?.[0]?.range || null;
36
+
37
+ this.createScrollbarLayout(dir);
38
+ this.createScrollbar(dir);
39
+ this.createScrollEvent(dir);
40
+ scrollbarOpt.isInit = true;
41
+ }
42
+ },
43
+
44
+ checkValidRange(dir) {
45
+ const scrollbarOpt = this.scrollbar[dir];
46
+ const axesType = scrollbarOpt.type;
47
+
48
+ if (scrollbarOpt.range?.length) {
49
+ const [min, max] = scrollbarOpt.range;
50
+
51
+ if (!(truthyNumber(min) && truthyNumber(max))) {
52
+ return true;
53
+ }
54
+
55
+ if (axesType === 'step') {
56
+ const labels = this.options.type === 'heatMap' ? this.data.labels[dir] : this.data.labels;
57
+ if (min < 0 || max > labels.length - 1) {
58
+ return true;
59
+ }
60
+ } else {
61
+ const minMax = this.minMax[dir]?.[0];
62
+ if (+min < +minMax.min || +max > +minMax.max) {
63
+ return true;
64
+ }
65
+ }
66
+ }
67
+
68
+ return false;
69
+ },
70
+
71
+ /**
72
+ * update scrollbar information
73
+ */
74
+ updateScrollbar(updateData) {
75
+ this.updateScrollbarInfo('x', updateData);
76
+ this.updateScrollbarInfo('y', updateData);
77
+ },
78
+
79
+ /**
80
+ * Updated scrollbar information with updated axis information
81
+ * @param dir axis direction (x | y)
82
+ * @param updateData is update data
83
+ */
84
+ updateScrollbarInfo(dir, updateData) {
85
+ const { axesX, axesY } = this.options;
86
+ const newOpt = dir === 'x' ? axesX : axesY;
87
+ if (!this.scrollbar[dir].isInit && newOpt?.[0]?.scrollbar?.use && newOpt?.[0]?.range) {
88
+ this.initScrollbarInfo(newOpt, dir);
89
+ return;
90
+ } else if (!newOpt?.[0].scrollbar?.use || checkNullAndUndefined(newOpt?.[0]?.range)) {
91
+ this.destroyScrollbar(dir);
92
+ return;
93
+ }
94
+
95
+ const axisOpt = dir === 'x' ? this.axesX : this.axesY;
96
+ const isUpdateAxesRange = !isEqual(newOpt?.[0]?.range, axisOpt?.[0]?.range);
97
+ if (isUpdateAxesRange || updateData) {
98
+ this.scrollbar[dir].range = newOpt?.[0]?.range || null;
99
+ }
100
+ this.scrollbar[dir].use = !!newOpt?.[0].scrollbar?.use;
101
+ },
102
+
103
+ /**
104
+ * update scrollbar position
105
+ */
106
+ updateScrollbarPosition() {
107
+ if (this.scrollbar.x?.use && this.scrollbar.x?.isInit) {
108
+ if (this.checkValidRange('x')) {
109
+ return;
110
+ }
111
+ this.setScrollbarPosition('x');
112
+ }
113
+
114
+ if (this.scrollbar.y?.use && this.scrollbar.y?.isInit) {
115
+ if (this.checkValidRange('y')) {
116
+ return;
117
+ }
118
+ this.setScrollbarPosition('y');
119
+ }
120
+ },
121
+
122
+ /**
123
+ * create scrollbar layout
124
+ * @param dir axis direction ('x' | 'y')
125
+ */
126
+ createScrollbarLayout(dir) {
127
+ const scrollbarOpt = this.scrollbar[dir];
128
+ scrollbarOpt.dom = document.createElement('div');
129
+ scrollbarOpt.dom.className = 'ev-chart-scrollbar';
130
+ scrollbarOpt.dom.dataset.type = 'scrollbar';
131
+
132
+ const containerDOM = document.createElement('div');
133
+ containerDOM.className = 'ev-chart-scrollbar-container';
134
+ containerDOM.dataset.type = dir;
135
+
136
+ scrollbarOpt.dom.appendChild(containerDOM);
137
+ this.wrapperDOM.appendChild(scrollbarOpt.dom);
138
+ },
139
+
140
+ /**
141
+ * create scrollbar
142
+ * @param dir axis direction ('x' | 'y')
143
+ */
144
+ createScrollbar(dir) {
145
+ const scrollbarOpt = this.scrollbar[dir];
146
+ const containerDOM = scrollbarOpt.dom.children[0];
147
+ this.createScrollbarTrack(containerDOM);
148
+ this.createScrollbarThumb(containerDOM);
149
+
150
+ if (scrollbarOpt.showButton) {
151
+ this.createScrollbarButton(containerDOM);
152
+ }
153
+ },
154
+
155
+ /**
156
+ * create scrollbar track
157
+ * @param containerDOM
158
+ */
159
+ createScrollbarTrack(containerDOM) {
160
+ const trackDOM = document.createElement('div');
161
+ trackDOM.className = 'ev-chart-scrollbar-track';
162
+ trackDOM.dataset.type = 'track';
163
+ containerDOM.appendChild(trackDOM);
164
+ },
165
+
166
+ /**
167
+ * create scrollbar thumb
168
+ * @param containerDOM
169
+ */
170
+ createScrollbarThumb(containerDOM) {
171
+ const thumbDOM = document.createElement('div');
172
+ thumbDOM.className = 'ev-chart-scrollbar-thumb';
173
+ thumbDOM.dataset.type = 'thumb';
174
+ containerDOM.appendChild(thumbDOM);
175
+ },
176
+
177
+ /**
178
+ * create scrollbar up, down button
179
+ * @param containerDOM
180
+ */
181
+ createScrollbarButton(containerDOM) {
182
+ const upBtnDOM = document.createElement('div');
183
+ upBtnDOM.className = 'ev-chart-scrollbar-button ev-chart-scrollbar-button-up';
184
+ upBtnDOM.dataset.type = 'button';
185
+ const iconUpBtn = document.createElement('i');
186
+ iconUpBtn.className = 'ev-icon ev-icon-triangle-up ev-chart-scrollbar-button-icon';
187
+ iconUpBtn.dataset.type = 'button-icon';
188
+ upBtnDOM.appendChild(iconUpBtn);
189
+
190
+ const downBtnDOM = document.createElement('div');
191
+ downBtnDOM.className = 'ev-chart-scrollbar-button ev-chart-scrollbar-button-down';
192
+ downBtnDOM.dataset.type = 'button';
193
+ const iconDownBtn = document.createElement('i');
194
+ iconDownBtn.className = 'ev-icon ev-icon-triangle-down ev-chart-scrollbar-button-icon';
195
+ iconDownBtn.dataset.type = 'button-icon';
196
+ downBtnDOM.appendChild(iconDownBtn);
197
+
198
+ containerDOM.appendChild(upBtnDOM);
199
+ containerDOM.appendChild(downBtnDOM);
200
+ },
201
+
202
+ /**
203
+ * set scrollbar position
204
+ * @param dir axis direction ('x' | 'y')
205
+ */
206
+ setScrollbarPosition(dir) {
207
+ const scrollbarOpt = this.scrollbar[dir];
208
+ if (!scrollbarOpt.use || !scrollbarOpt.range) {
209
+ return;
210
+ }
211
+
212
+ const scrollbarDOM = scrollbarOpt.dom;
213
+ const chartRect = this.chartRect;
214
+ const labelOffset = this.labelOffset;
215
+ const aPos = {
216
+ x1: chartRect.x1 + labelOffset.left,
217
+ x2: chartRect.x2 - labelOffset.right,
218
+ y1: chartRect.y1 + labelOffset.top,
219
+ y2: chartRect.y2 - labelOffset.bottom,
220
+ };
221
+
222
+ const titleHeight = this.options.title?.show ? this.options.title?.height : 0;
223
+ const isXScroll = dir === 'x';
224
+ const scrollHeight = isXScroll ? scrollbarOpt.height : scrollbarOpt.width;
225
+ const fullSize = isXScroll ? (aPos.x2 - aPos.x1) : (aPos.y2 - aPos.y1);
226
+ const buttonSize = scrollbarOpt.showButton ? scrollHeight : 0;
227
+ const trackSize = fullSize - (buttonSize * 2);
228
+ const thumbSize = this.getScrollbarThumbSize(dir, trackSize);
229
+
230
+ let scrollbarStyle = 'display: block;';
231
+ let scrollbarTrackStyle;
232
+ let scrollbarThumbStyle;
233
+ let upBtnStyle;
234
+ let downBtnStyle;
235
+ let commonBtnStyle = `width:${buttonSize}px;height:${buttonSize}px;`;
236
+ if (isXScroll) {
237
+ scrollbarStyle = `top: ${chartRect.y2 + titleHeight + labelOffset.top}px;`;
238
+ scrollbarStyle += `left: ${aPos.x1}px;`;
239
+ scrollbarStyle += `width: ${fullSize}px;`;
240
+ scrollbarStyle += ` height: ${scrollHeight}px;`;
241
+
242
+ scrollbarTrackStyle = 'top: 0;';
243
+ scrollbarTrackStyle += `left: ${buttonSize}px;`;
244
+ scrollbarTrackStyle += `width: ${trackSize}px;`;
245
+ scrollbarTrackStyle += 'height: 100%;';
246
+
247
+ scrollbarThumbStyle = `width: ${thumbSize.size}px;`;
248
+ scrollbarThumbStyle += 'height: 100%;';
249
+ scrollbarThumbStyle += `left: ${thumbSize.position + buttonSize}px`;
250
+
251
+ commonBtnStyle += 'transform:rotate(90deg);top: 0;';
252
+
253
+ upBtnStyle = `${commonBtnStyle}right:0;`;
254
+ downBtnStyle = `${commonBtnStyle}left:0;`;
255
+ } else {
256
+ scrollbarStyle = `top: ${aPos.y1 + titleHeight}px;`;
257
+ scrollbarStyle += `left: ${aPos.x2 + 10}px;`;
258
+ scrollbarStyle += `width: ${scrollHeight}px;`;
259
+ scrollbarStyle += `height: ${fullSize}px;`;
260
+
261
+ scrollbarTrackStyle = `top: ${buttonSize}px;`;
262
+ scrollbarTrackStyle += 'left: 0;';
263
+ scrollbarTrackStyle += 'width: 100%;';
264
+ scrollbarTrackStyle += `height: ${trackSize}px;`;
265
+
266
+ scrollbarThumbStyle = 'width: 100%;';
267
+ scrollbarThumbStyle += `height: ${thumbSize.size}px;`;
268
+ scrollbarThumbStyle += `bottom: ${thumbSize.position + buttonSize}px`;
269
+
270
+ commonBtnStyle += 'left:0;';
271
+ upBtnStyle = `${commonBtnStyle}top: 0;`;
272
+ downBtnStyle = `${commonBtnStyle}bottom: 0;`;
273
+ }
274
+ scrollbarDOM.style.cssText = scrollbarStyle;
275
+
276
+ const scrollbarTrackDOM = scrollbarDOM.getElementsByClassName('ev-chart-scrollbar-track');
277
+ scrollbarTrackDOM[0].style.cssText = scrollbarTrackStyle;
278
+ scrollbarTrackDOM[0].style.backgroundColor = scrollbarOpt.background;
279
+
280
+ const scrollbarThumbDOM = scrollbarDOM.getElementsByClassName('ev-chart-scrollbar-thumb');
281
+ scrollbarThumbDOM[0].style.cssText = scrollbarThumbStyle;
282
+ scrollbarThumbDOM[0].style.backgroundColor = scrollbarOpt.thumbStyle.background;
283
+ scrollbarThumbDOM[0].style.borderRadius = `${scrollbarOpt.thumbStyle.radius}px`;
284
+
285
+ if (scrollbarOpt.showButton) {
286
+ const upBtnDOM = scrollbarDOM.getElementsByClassName('ev-chart-scrollbar-button-up');
287
+ const endPosition = Math.floor(trackSize - thumbSize.size);
288
+ const upBtnOpacity = Math.floor(thumbSize.position) > endPosition ? 0.5 : 1;
289
+ upBtnDOM[0].style.cssText = `background-color: ${scrollbarOpt.background};${upBtnStyle}`;
290
+ upBtnDOM[0].style.opacity = upBtnOpacity;
291
+ upBtnDOM[0].children[0].style.display = 'block';
292
+ const downBtnDOM = scrollbarDOM.getElementsByClassName('ev-chart-scrollbar-button-down');
293
+ downBtnDOM[0].style.cssText = `background-color: ${scrollbarOpt.background};${downBtnStyle}`;
294
+ downBtnDOM[0].style.opacity = Math.floor(thumbSize.position) < 0 ? 0.5 : 1;
295
+ downBtnDOM[0].children[0].style.display = 'block';
296
+ }
297
+ },
298
+
299
+ /**
300
+ * get scrollbar thumb size
301
+ * @param dir axis direction ('x' | 'y')
302
+ * @param trackSize scrollbar track size
303
+ */
304
+ getScrollbarThumbSize(dir, trackSize) {
305
+ const scrollbarOpt = this.scrollbar[dir];
306
+ const [min, max] = scrollbarOpt.range;
307
+ const axesType = scrollbarOpt.type;
308
+
309
+ let thumbSize;
310
+ let steps;
311
+ let interval = 1;
312
+ let startValue = 0;
313
+ let thumbPosition = 0;
314
+ if (axesType === 'step') {
315
+ const labels = this.options.type === 'heatMap' ? this.data.labels[dir] : this.data.labels;
316
+ const range = (max - min) + 1;
317
+ steps = labels.length;
318
+
319
+ const intervalSize = trackSize / steps;
320
+ thumbSize = intervalSize * range;
321
+ thumbPosition = intervalSize * min;
322
+ } else {
323
+ const axisOpt = dir === 'x' ? this.axesX : this.axesY;
324
+ const minMax = this.minMax[dir]?.[0];
325
+ const graphRange = (+minMax.max) - (+minMax.min);
326
+ const range = (+max) - (+min);
327
+ if (axesType === 'time') {
328
+ interval = axisOpt?.[0]?.getInterval({
329
+ minValue: minMax.min,
330
+ maxValue: minMax.max,
331
+ maxSteps: this.labelRange[dir]?.[0]?.max,
332
+ });
333
+ }
334
+ steps = Math.ceil(graphRange / interval) + 1;
335
+ startValue = +minMax.min;
336
+
337
+ const intervalSize = trackSize / steps;
338
+ const count = (range / interval) + 1;
339
+ const point = (+min - startValue);
340
+ thumbSize = intervalSize * count;
341
+ thumbPosition = intervalSize * (point / interval);
342
+ }
343
+
344
+ scrollbarOpt.startValue = startValue;
345
+ scrollbarOpt.steps = steps;
346
+ scrollbarOpt.interval = interval;
347
+
348
+ return {
349
+ size: thumbSize,
350
+ position: thumbPosition,
351
+ };
352
+ },
353
+
354
+ /**
355
+ * get scrollbar containerDOM
356
+ * @param targetDOM event target dom
357
+ * @returns {HTMLElement|Element|*}
358
+ */
359
+ getScrollbarContainerDOM(targetDOM) {
360
+ const childTypes = ['track', 'thumb', 'button'];
361
+
362
+ const type = targetDOM.dataset.type;
363
+ if (childTypes.includes(type)) {
364
+ return targetDOM.parentElement;
365
+ } else if (type === 'button-icon') {
366
+ return targetDOM.parentElement.parentElement;
367
+ } else if (type === 'scrollbar') {
368
+ return targetDOM.getElementsByClassName('ev-chart-scrollbar-container')[0];
369
+ }
370
+
371
+ return targetDOM;
372
+ },
373
+
374
+ /**
375
+ * update scrollbar option range
376
+ * @param dir axis direction ('x' | 'y')
377
+ * @param isUp
378
+ */
379
+ updateScrollbarRange(dir, isUp) {
380
+ const scrollbarOpt = this.scrollbar[dir];
381
+ const { startValue, range, interval, steps } = scrollbarOpt;
382
+ const endValue = startValue + (interval * steps);
383
+ const axisOpt = dir === 'x' ? this.axesX[0] : this.axesY[0];
384
+ const [min, max] = range ?? [];
385
+
386
+ if (!truthyNumber(min) || !truthyNumber(max)) {
387
+ scrollbarOpt.range = axisOpt?.range || null;
388
+ }
389
+
390
+ let minValue;
391
+ let maxValue;
392
+ let isOutOfRange = false;
393
+ if (isUp) {
394
+ minValue = min + interval;
395
+ maxValue = max + interval;
396
+ isOutOfRange = maxValue >= endValue;
397
+ } else {
398
+ minValue = min - interval;
399
+ maxValue = max - interval;
400
+ isOutOfRange = minValue < startValue;
401
+ }
402
+
403
+ if (!isOutOfRange) {
404
+ scrollbarOpt.range = [minValue, maxValue];
405
+
406
+ this.update({
407
+ updateSeries: false,
408
+ updateSelTip: { update: false, keepDomain: false },
409
+ });
410
+ }
411
+ },
412
+
413
+ /**
414
+ * create scroll event
415
+ */
416
+ createScrollEvent() {
417
+ this.onScrollbarClick = (e) => {
418
+ e.preventDefault();
419
+
420
+ const type = e.target.dataset.type;
421
+ const containerDOM = this.getScrollbarContainerDOM(e.target);
422
+ const buttonTypes = ['button', 'button-icon'];
423
+ const dir = containerDOM.dataset.type;
424
+
425
+ let isUp;
426
+ if (buttonTypes.includes(type)) {
427
+ let buttonDOM;
428
+ if (type === 'button') {
429
+ buttonDOM = e.target;
430
+ } else if (type === 'button-icon') {
431
+ buttonDOM = e.target.parentElement;
432
+ }
433
+ isUp = buttonDOM.className.includes('up');
434
+ } else if (type === 'track') {
435
+ const thumbDOM = containerDOM.getElementsByClassName('ev-chart-scrollbar-thumb');
436
+ const { x, y } = thumbDOM[0].getBoundingClientRect();
437
+ const isXScroll = dir === 'x';
438
+ const clickPoint = isXScroll ? e.clientX : -e.clientY;
439
+ const thumbPosition = isXScroll ? x : -y;
440
+ isUp = (clickPoint > thumbPosition);
441
+ } else {
442
+ return;
443
+ }
444
+ this.updateScrollbarRange(dir, isUp);
445
+ };
446
+
447
+ this.onScrollbarDown = (e) => {
448
+ e.preventDefault();
449
+
450
+ if (e.target.dataset.type !== 'thumb') {
451
+ return;
452
+ }
453
+
454
+ const containerDOM = this.getScrollbarContainerDOM(e.target);
455
+ const dir = containerDOM.dataset.type;
456
+ const thumbDOM = containerDOM.getElementsByClassName('ev-chart-scrollbar-thumb');
457
+ const { x, y, height } = thumbDOM[0].getBoundingClientRect();
458
+ const scrollbarOpt = this.scrollbar[dir];
459
+ scrollbarOpt.scrolling = true;
460
+ if (dir === 'x') {
461
+ scrollbarOpt.pointInThumb = e.clientX - x;
462
+ } else {
463
+ scrollbarOpt.pointInThumb = y + height - e.clientY;
464
+ }
465
+
466
+ const scrollbarDOM = scrollbarOpt.dom;
467
+ scrollbarDOM.addEventListener('mousemove', this.onScrollbarMove);
468
+ scrollbarDOM.addEventListener('mouseup', this.onScrollbarUp);
469
+ };
470
+
471
+ const onScrollbarMove = (e) => {
472
+ this.scrolling(e);
473
+ };
474
+
475
+ this.onScrollbarMove = throttle(onScrollbarMove, 5);
476
+
477
+ this.onScrollbarUp = (e) => {
478
+ e.preventDefault();
479
+
480
+ this.stopScrolling(e);
481
+ };
482
+
483
+ this.onScrollbarLeave = (e) => {
484
+ e.preventDefault();
485
+
486
+ this.scrolling(e);
487
+ this.stopScrolling(e);
488
+ };
489
+
490
+ this.onScrollbarWheel = (e) => {
491
+ e.preventDefault();
492
+
493
+ this.updateScrollbarRange('y', e.deltaY < 0);
494
+ };
495
+
496
+ if (this.scrollbar.x.use && !this.scrollbar.x.isInit) {
497
+ const scrollbarXDOM = this.scrollbar.x.dom;
498
+ scrollbarXDOM.addEventListener('click', this.onScrollbarClick);
499
+ scrollbarXDOM.addEventListener('mousedown', this.onScrollbarDown);
500
+ scrollbarXDOM.addEventListener('mouseleave', this.onScrollbarLeave);
501
+ }
502
+
503
+ if (this.scrollbar.y.use && !this.scrollbar.y.isInit) {
504
+ const scrollbarYDOM = this.scrollbar.y.dom;
505
+ scrollbarYDOM.addEventListener('click', this.onScrollbarClick);
506
+ scrollbarYDOM.addEventListener('mousedown', this.onScrollbarDown);
507
+ scrollbarYDOM.addEventListener('mouseleave', this.onScrollbarLeave);
508
+ this.overlayCanvas?.addEventListener('wheel', this.onScrollbarWheel);
509
+ }
510
+ },
511
+
512
+ /**
513
+ * Update scroll information by move event
514
+ * @param e Event
515
+ */
516
+ scrolling(e) {
517
+ const containerDOM = this.getScrollbarContainerDOM(e.target);
518
+ const dir = containerDOM.dataset.type;
519
+ if (!this.scrollbar[dir].scrolling) {
520
+ return;
521
+ }
522
+
523
+ const {
524
+ steps, range, pointInThumb,
525
+ startValue, interval,
526
+ } = this.scrollbar[dir];
527
+
528
+ const trackDOM = containerDOM.getElementsByClassName('ev-chart-scrollbar-track');
529
+ const { x, y, width, height } = trackDOM[0].getBoundingClientRect();
530
+
531
+ const isXScroll = dir === 'x';
532
+ const sp = isXScroll ? x : y;
533
+ const trackSize = isXScroll ? width : height;
534
+ const intervalSize = trackSize / steps;
535
+ const endValue = (startValue + ((steps - 1) * interval));
536
+
537
+ let movePoint = isXScroll ? e.clientX : e.clientY;
538
+ if (movePoint < sp) {
539
+ movePoint = sp;
540
+ } else if (movePoint > sp + trackSize) {
541
+ movePoint = sp + trackSize;
542
+ }
543
+
544
+ let move;
545
+ if (isXScroll) {
546
+ move = movePoint - sp - pointInThumb;
547
+ } else {
548
+ move = (sp + trackSize) - movePoint - pointInThumb;
549
+ }
550
+
551
+ if (move <= 0) {
552
+ return;
553
+ }
554
+
555
+ let movedMin;
556
+ let movedMax;
557
+ const currValue = (Math.round(Math.abs(move) / intervalSize) * interval);
558
+ const [min, max] = range;
559
+ if (move > 0) {
560
+ const incrementValue = startValue + (currValue - +min);
561
+ movedMin = +min + incrementValue;
562
+ movedMax = movedMin + (+max - +min);
563
+ }
564
+
565
+ if (movedMin < startValue || movedMax > endValue) {
566
+ return;
567
+ }
568
+
569
+ this.scrollbar[dir].range = [movedMin, movedMax];
570
+ this.update({
571
+ updateSeries: false,
572
+ updateSelTip: { update: false, keepDomain: false },
573
+ });
574
+ },
575
+
576
+ /**
577
+ * init scrolling event
578
+ * @param e
579
+ */
580
+ stopScrolling(e) {
581
+ const containerDOM = this.getScrollbarContainerDOM(e.target);
582
+ const dir = containerDOM.dataset.type;
583
+ const scrollbarOpt = this.scrollbar[dir];
584
+
585
+ if (scrollbarOpt.scrolling) {
586
+ scrollbarOpt.scrolling = false;
587
+
588
+ const scrollbarDOM = scrollbarOpt.dom;
589
+ scrollbarDOM.removeEventListener('mousemove', this.onScrollbarMove, false);
590
+ scrollbarDOM.removeEventListener('mouseup', this.onScrollbarUp, false);
591
+ }
592
+ },
593
+
594
+ /**
595
+ * hide scrollbar dom
596
+ * @param dir axis direction ('x' | 'y')
597
+ */
598
+ hideScrollbar(dir) {
599
+ const scrollbarDOM = this.scrollbar[dir].dom;
600
+
601
+ if (!scrollbarDOM) {
602
+ return;
603
+ }
604
+
605
+ const scrollbarStyle = scrollbarDOM?.style;
606
+ scrollbarStyle.display = 'none';
607
+ scrollbarStyle.width = '0';
608
+ scrollbarStyle.height = '0';
609
+ },
610
+
611
+ /**
612
+ * destroy scrollbar dom
613
+ * @param dir axis direction ('x' | 'y')
614
+ */
615
+ destroyScrollbar(dir) {
616
+ const scrollbarXDOM = this.scrollbar[dir].dom;
617
+
618
+ if (scrollbarXDOM) {
619
+ scrollbarXDOM.remove();
620
+ this.scrollbar[dir] = { isInit: false };
621
+
622
+ if (dir === 'y') {
623
+ this.overlayCanvas?.removeEventListener('wheel', this.onScrollbarWheel, false);
624
+ }
625
+ }
626
+ },
627
+ };
628
+
629
+ export default module;
@@ -34,9 +34,11 @@ const modules = {
34
34
  this.tooltipDOM.style.display = 'none';
35
35
  this.setFontFamily();
36
36
 
37
- this.tooltipBodyDOM.appendChild(this.tooltipCanvas);
38
- this.tooltipDOM.appendChild(this.tooltipHeaderDOM);
39
- this.tooltipDOM.appendChild(this.tooltipBodyDOM);
37
+ if (!this.options.tooltip?.formatter?.dom) {
38
+ this.tooltipBodyDOM.appendChild(this.tooltipCanvas);
39
+ this.tooltipDOM.appendChild(this.tooltipHeaderDOM);
40
+ this.tooltipDOM.appendChild(this.tooltipBodyDOM);
41
+ }
40
42
 
41
43
  document.body.appendChild(this.tooltipDOM);
42
44
 
@@ -563,6 +565,72 @@ const modules = {
563
565
  ctx.restore();
564
566
  },
565
567
 
568
+ setCustomTooltipLayoutPosition(hitInfo, e) {
569
+ const mouseX = e.pageX;
570
+ const mouseY = e.pageY;
571
+
572
+ const customTooltipEl = document.getElementsByClassName('ev-chart-tooltip-custom')?.[0];
573
+ if (!customTooltipEl) {
574
+ return;
575
+ }
576
+
577
+ const contentsWidth = customTooltipEl.offsetWidth;
578
+ const contentsHeight = customTooltipEl.offsetHeight;
579
+
580
+ this.tooltipDOM.style.height = 'auto';
581
+ this.tooltipBodyDOM.style.height = `${contentsHeight + 6}px`;
582
+
583
+ const bodyWidth = document.body.clientWidth;
584
+ const bodyHeight = document.body.clientHeight;
585
+ const distanceMouseAndTooltip = 20;
586
+ const maximumPosX = bodyWidth - contentsWidth - distanceMouseAndTooltip;
587
+ const maximumPosY = bodyHeight - (TITLE_HEIGHT + contentsHeight) - distanceMouseAndTooltip;
588
+ const expectedPosX = mouseX + distanceMouseAndTooltip;
589
+ const expectedPosY = mouseY + distanceMouseAndTooltip;
590
+ const reversedPosX = mouseX - contentsWidth - distanceMouseAndTooltip;
591
+ const reversedPosY = mouseY - (TITLE_HEIGHT + contentsHeight) - distanceMouseAndTooltip;
592
+ this.tooltipDOM.style.left = expectedPosX > maximumPosX
593
+ ? `${reversedPosX}px`
594
+ : `${expectedPosX}px`;
595
+ this.tooltipDOM.style.top = expectedPosY > maximumPosY
596
+ ? `${reversedPosY}px`
597
+ : `${expectedPosY}px`;
598
+
599
+ this.tooltipDOM.style.display = 'block';
600
+ },
601
+
602
+ /**
603
+ * Draw User Custom Tooltip (tooltip > formatter > html)
604
+ * call "formatter > html" and append to tooltip DOM
605
+ * @param hitInfoItems
606
+ */
607
+ drawCustomTooltip(hitInfoItems) {
608
+ const opt = this.options?.tooltip;
609
+ if (opt.formatter?.html) {
610
+ this.tooltipDOM.innerHTML = '';
611
+
612
+ const seriesList = [];
613
+ Object.keys(hitInfoItems).forEach((sId) => {
614
+ seriesList.push({
615
+ sId,
616
+ data: hitInfoItems[sId].data,
617
+ color: hitInfoItems[sId].color,
618
+ name: hitInfoItems[sId].name,
619
+ });
620
+ });
621
+
622
+ const userCustomTooltipBody = Util.htmlToElement(opt?.formatter?.html((seriesList)));
623
+ if (userCustomTooltipBody) {
624
+ this.tooltipDOM.appendChild(userCustomTooltipBody);
625
+ }
626
+
627
+ this.tooltipDOM.style.overflowY = 'hidden';
628
+ this.tooltipDOM.style.backgroundColor = opt.backgroundColor;
629
+ this.tooltipDOM.style.border = `1px solid ${opt.borderColor}`;
630
+ this.tooltipDOM.style.color = opt.fontColor;
631
+ }
632
+ },
633
+
566
634
  /**
567
635
  * set style properties on tooltip DOM
568
636
  * @param tooltipOptions