/** * Svelte action: show a custom tooltip after a 500ms hover delay. * Uses a positioned div instead of the native `title` attribute — * native tooltips only appear on mousemove, not on mouseenter, which * causes a "hover then wiggle" requirement that feels broken. * * If text is empty, does nothing. */ const TOOLTIP_CLASS = 'hu-tooltip'; // Shared tooltip element — one per page, repositioned as needed. let tooltipEl: HTMLDivElement | null = null; function getTooltipEl(): HTMLDivElement { if (!tooltipEl) { tooltipEl = document.createElement('div'); tooltipEl.className = TOOLTIP_CLASS; document.body.appendChild(tooltipEl); } return tooltipEl; } export function tooltip(node: HTMLElement, text: string) { let timer: ReturnType | null = null; let currentText = text; function show(e: MouseEvent) { if (!currentText) return; const el = getTooltipEl(); el.textContent = currentText; // Position near cursor, clamped to viewport const x = Math.min(e.clientX + 12, window.innerWidth - el.offsetWidth - 8); const y = e.clientY + 20; el.style.left = `${x}px`; el.style.top = `${y}px`; el.classList.add('hu-tooltip--visible'); } function enter(e: MouseEvent) { if (!currentText) return; timer = setTimeout(() => show(e), 500); } function move(e: MouseEvent) { // Keep tooltip near cursor while hovering if (!tooltipEl?.classList.contains('hu-tooltip--visible')) return; const el = getTooltipEl(); const x = Math.min(e.clientX + 12, window.innerWidth - el.offsetWidth - 8); const y = e.clientY + 20; el.style.left = `${x}px`; el.style.top = `${y}px`; } function leave() { if (timer !== null) { clearTimeout(timer); timer = null; } tooltipEl?.classList.remove('hu-tooltip--visible'); } node.addEventListener('mouseenter', enter); node.addEventListener('mousemove', move); node.addEventListener('mouseleave', leave); return { update(newText: string) { currentText = newText; if (tooltipEl?.classList.contains('hu-tooltip--visible')) { tooltipEl.textContent = newText; } }, destroy() { if (timer !== null) clearTimeout(timer); node.removeEventListener('mouseenter', enter); node.removeEventListener('mousemove', move); node.removeEventListener('mouseleave', leave); tooltipEl?.classList.remove('hu-tooltip--visible'); } }; }