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; if (element.dataset.tooltipDisable === 'true') return; 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.style.zIndex = '1000'; 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;