import { debounce } from "./dom-utils.js"; import { emitEvent, listenTo } from "./events.js"; const tooltips = {}; let tooltipsCounter = 0; let lastCoordinates = null; let suppressTooltips = false; let activeTooltipTarget = null; export function initializeTooltips() { document.querySelectorAll("[data-retain-tooltip-hover]").forEach(elem => elem.addEventListener("mousemove", e => { if (lastCoordinates !== null) { e.stopPropagation(); lastCoordinates = { x: e.pageX, y: e.pageY }; emitEvent("tooltips:show"); } })); window.addEventListener("mousemove", () => { if (lastCoordinates !== null) { emitEvent("tooltips:hide"); } lastCoordinates = null; }); window.addEventListener("mousemove", debounce(e => { lastCoordinates = { x: e.pageX, y: e.pageY }; emitEvent("tooltips:show"); }, 500)); window.addEventListener("wheel", () => { if (lastCoordinates !== null) { setTimeout(() => emitEvent("tooltips:show"), 200); } }); listenTo("tooltips:show", showTooltip); listenTo("tooltips:hide", hideTooltip); document.addEventListener("keydown", e => { if (e.key === "T") { suppressTooltips = !suppressTooltips; } }); } export function createTooltip(data) { const id = tooltipsCounter; tooltips[id] = data; tooltipsCounter++; return id; } export function getTooltip(id) { return tooltips[id]; } export function deleteTooltip(id) { delete tooltips[id]; } export function positionAsTooltip(mainElement, tooltipElement, { horizontal = "left", vertical = "bottom" } = {}) { const targetRect = mainElement.getBoundingClientRect(); const domRect = tooltipElement.getBoundingClientRect(); const windowWidth = window.innerWidth, windowHeight = window.innerHeight; tooltipElement.style.left = `${Math.min(targetRect[horizontal], windowWidth - domRect.width)}px`; tooltipElement.style.top = `${Math.min(targetRect[vertical], windowHeight - domRect.height)}px`; } function showTooltip() { if (suppressTooltips) return; if (!lastCoordinates) return; if (activeTooltipTarget) hideTooltip(); const target = document.elementFromPoint(lastCoordinates.x, lastCoordinates.y); if (!target) return; if (!target.dataset.tooltip) return; const dom = createTooltipDom(target.dataset.tooltip); if (!dom) return; const targetRect = target.getBoundingClientRect(); dom.style.left = `${targetRect.left}px`; dom.style.top = `${targetRect.bottom}px`; target.appendChild(dom); positionAsTooltip(target, dom); activeTooltipTarget = target; } function hideTooltip() { if (suppressTooltips) return; if (!activeTooltipTarget) return; const tooltip = activeTooltipTarget.querySelector(".tooltip"); if (!tooltip) return; activeTooltipTarget.removeChild(tooltip); activeTooltipTarget = null; } function createTooltipDom(id) { const data = tooltips[id]; if (!Array.isArray(data)) { console.warn("tooltip %s is not an array: %o", id, data); return null; } const div = document.createElement("div"); div.classList.add("tooltip"); for (const row of data) { const rowElem = document.createElement("div"); if (typeof row === "string") { rowElem.innerText = row; } else { // TODO: support rich tooltips throw row; } div.appendChild(rowElem); } return div; }