1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
const tooltip = (element: HTMLElement) => {
let tooltipDiv: HTMLDivElement | null = null;
const offset = 10;
const tooltipTransitionTime = 200;
const tooltipHideDelay = 10;
const debounceDelay = 100;
let hideTimeout: number | null = null;
let debounceTimer: number | null = null;
const createTooltip = () => {
if (!tooltipDiv) {
tooltipDiv = document.createElement('div');
tooltipDiv.style.position = 'absolute';
tooltipDiv.style.zIndex = '1000';
tooltipDiv.style.opacity = '0';
tooltipDiv.style.transition = `opacity ${tooltipTransitionTime}ms ease-in-out, top 0.3s ease, left 0.3s ease`;
tooltipDiv.style.pointerEvents = 'none';
tooltipDiv.style.whiteSpace = 'nowrap';
tooltipDiv.classList.add('card');
tooltipDiv.classList.add('card-small');
document.body.appendChild(tooltipDiv);
}
};
const updateTooltipPosition = (x: number, y: number) => {
if (tooltipDiv) {
const tooltipWidth = tooltipDiv.offsetWidth;
const tooltipHeight = tooltipDiv.offsetHeight;
let top = y - tooltipHeight - offset;
let left = x - tooltipWidth / 2;
if (left < 0) left = offset;
if (left + tooltipWidth > window.innerWidth) left = window.innerWidth - tooltipWidth - offset;
if (top < 0) top = y + offset;
tooltipDiv.style.left = `${left}px`;
tooltipDiv.style.top = `${top}px`;
}
};
const showTooltip = (content: string, x: number, y: number) => {
if (hideTimeout !== null) {
clearTimeout(hideTimeout);
hideTimeout = null;
}
createTooltip();
if (tooltipDiv) {
tooltipDiv.innerHTML = content;
updateTooltipPosition(x, y);
setTimeout(() => {
if (tooltipDiv) tooltipDiv.style.opacity = '1';
}, 10);
}
};
const hideTooltip = () => {
hideTimeout = window.setTimeout(() => {
if (tooltipDiv) {
tooltipDiv.style.opacity = '0';
setTimeout(() => {
if (tooltipDiv) {
document.body.removeChild(tooltipDiv);
tooltipDiv = null;
}
}, tooltipTransitionTime);
}
}, tooltipHideDelay);
};
const handleMouseEnter = (event: MouseEvent) => {
const title = element.getAttribute('title');
if (title) {
element.removeAttribute('title');
showTooltip(title, event.pageX, event.pageY);
}
};
const handleMouseMove = (event: MouseEvent) => {
if (debounceTimer !== null) clearTimeout(debounceTimer);
debounceTimer = window.setTimeout(() => {
if (tooltipDiv && tooltipDiv.style.opacity === '1')
updateTooltipPosition(event.pageX, event.pageY);
}, debounceDelay);
};
const handleMouseLeave = () => {
element.setAttribute('title', tooltipDiv ? tooltipDiv.textContent || '' : '');
hideTooltip();
};
element.addEventListener('mouseenter', handleMouseEnter);
element.addEventListener('mousemove', handleMouseMove);
element.addEventListener('mouseleave', handleMouseLeave);
return {
destroy() {
element.removeEventListener('mouseenter', handleMouseEnter);
element.removeEventListener('mousemove', handleMouseMove);
element.removeEventListener('mouseleave', handleMouseLeave);
if (hideTimeout !== null) clearTimeout(hideTimeout);
if (debounceTimer !== null) clearTimeout(debounceTimer);
if (tooltipDiv && document.body.contains(tooltipDiv)) {
document.body.removeChild(tooltipDiv);
tooltipDiv = null;
}
}
};
};
export default tooltip;
|