feat: tooltip viewport clamping — shifts minimally to keep bounding box in view
This commit is contained in:
@@ -12,6 +12,26 @@ function getTooltipEl(): HTMLDivElement {
|
|||||||
return tooltipEl;
|
return tooltipEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clamp(val: number, min: number, max: number) {
|
||||||
|
return Math.min(Math.max(val, min), max);
|
||||||
|
}
|
||||||
|
|
||||||
|
function positionTooltip(el: HTMLDivElement, cx: number, cy: number) {
|
||||||
|
const margin = 6;
|
||||||
|
// Start with ideal position: just right of and below cursor
|
||||||
|
let x = cx + 12;
|
||||||
|
let y = cy + 16;
|
||||||
|
// Measure after content is set (element is in DOM, opacity-hidden is fine)
|
||||||
|
const w = el.offsetWidth;
|
||||||
|
const h = el.offsetHeight;
|
||||||
|
// Shift left/right only as much as needed
|
||||||
|
x = clamp(x, margin, window.innerWidth - w - margin);
|
||||||
|
// Shift up/down only as much as needed
|
||||||
|
y = clamp(y, margin, window.innerHeight - h - margin);
|
||||||
|
el.style.left = `${x}px`;
|
||||||
|
el.style.top = `${y}px`;
|
||||||
|
}
|
||||||
|
|
||||||
export function tooltip(node: HTMLElement, text: string) {
|
export function tooltip(node: HTMLElement, text: string) {
|
||||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||||
let currentText = text;
|
let currentText = text;
|
||||||
@@ -22,8 +42,8 @@ export function tooltip(node: HTMLElement, text: string) {
|
|||||||
if (!currentText) return;
|
if (!currentText) return;
|
||||||
const el = getTooltipEl();
|
const el = getTooltipEl();
|
||||||
el.textContent = currentText;
|
el.textContent = currentText;
|
||||||
el.style.left = `${mouseX + 12}px`;
|
// Position before making visible — offsetWidth/Height valid since el is in DOM
|
||||||
el.style.top = `${mouseY + 16}px`;
|
positionTooltip(el, mouseX, mouseY);
|
||||||
el.classList.add(TOOLTIP_VISIBLE_CLASS);
|
el.classList.add(TOOLTIP_VISIBLE_CLASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,8 +58,7 @@ export function tooltip(node: HTMLElement, text: string) {
|
|||||||
mouseX = e.clientX;
|
mouseX = e.clientX;
|
||||||
mouseY = e.clientY;
|
mouseY = e.clientY;
|
||||||
if (!tooltipEl?.classList.contains(TOOLTIP_VISIBLE_CLASS)) return;
|
if (!tooltipEl?.classList.contains(TOOLTIP_VISIBLE_CLASS)) return;
|
||||||
tooltipEl.style.left = `${mouseX + 12}px`;
|
positionTooltip(tooltipEl, mouseX, mouseY);
|
||||||
tooltipEl.style.top = `${mouseY + 16}px`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function leave() {
|
function leave() {
|
||||||
@@ -56,6 +75,7 @@ export function tooltip(node: HTMLElement, text: string) {
|
|||||||
currentText = newText;
|
currentText = newText;
|
||||||
if (tooltipEl?.classList.contains(TOOLTIP_VISIBLE_CLASS)) {
|
if (tooltipEl?.classList.contains(TOOLTIP_VISIBLE_CLASS)) {
|
||||||
tooltipEl.textContent = newText;
|
tooltipEl.textContent = newText;
|
||||||
|
positionTooltip(tooltipEl, mouseX, mouseY);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroy() {
|
destroy() {
|
||||||
|
|||||||
Reference in New Issue
Block a user