import React, { useRef, useEffect } from 'react' import { createPortal } from 'react-dom' import type { ItemStack } from '../../types' import { useScale } from '../../context/ScaleContext' import { globalMouse } from '../../utils/globalMouse' import { ItemTooltipBody } from './ItemTooltipBody' import styles from './Tooltip.module.css' interface TooltipProps { item: ItemStack visible: boolean } export function Tooltip({ item, visible }: TooltipProps) { const { scale } = useScale() const ref = useRef(null) // Position the tooltip directly via DOM, bypassing React state updates. // Called on every mousemove and whenever visible/item/scale changes. const applyPosition = () => { const el = ref.current if (!el) return const tw = el.offsetWidth const th = el.offsetHeight const vw = window.innerWidth const vh = window.innerHeight const gapX = 12 const gapY = 2 let x = globalMouse.x + gapX let y = globalMouse.y - th - gapY // above cursor by default if (x + tw > vw - 4) x = globalMouse.x - tw - gapX if (y < 4) y = globalMouse.y + gapY // flip below if no space above if (y + th > vh - 4) y = vh - th - 4 el.style.left = `${x}px` el.style.top = `${y}px` el.style.visibility = 'visible' } useEffect(() => { if (!visible) return // Initial position immediately after mounting / becoming visible applyPosition() const onMove = () => applyPosition() window.addEventListener('mousemove', onMove, { passive: true }) return () => window.removeEventListener('mousemove', onMove) // eslint-disable-next-line react-hooks/exhaustive-deps }, [visible, item, scale]) if (!visible) return null const fs = Math.round(8 * scale) const pad = Math.round(3 * scale) const gap2 = Math.round(0.5 * scale) const tooltip = (
) // Portal to document.body so position:fixed works even inside transformed ancestors return createPortal(tooltip, document.body) }