| import { useRef, useState, type ReactNode } from 'react'
|
| import { createPortal } from 'react-dom'
|
|
|
|
|
|
|
|
|
|
|
| export function Tooltip({ text, children, side = 'top', className }: {
|
| text: string
|
| children: ReactNode
|
| side?: 'top' | 'bottom'
|
| className?: string
|
| }) {
|
| const ref = useRef<HTMLSpanElement>(null)
|
| const [coords, setCoords] = useState<{ x: number; y: number } | null>(null)
|
|
|
| function show() {
|
| const el = ref.current
|
| if (!el) return
|
| const r = el.getBoundingClientRect()
|
| const half = 116
|
| const x = Math.min(Math.max(r.left + r.width / 2, half + 8), window.innerWidth - half - 8)
|
| setCoords({ x, y: side === 'top' ? r.top : r.bottom })
|
| }
|
| const hide = () => setCoords(null)
|
|
|
| return (
|
| <span
|
| ref={ref}
|
| className={className ?? 'inline-flex'}
|
| onMouseEnter={show}
|
| onMouseLeave={hide}
|
| onFocus={show}
|
| onBlur={hide}
|
| >
|
| {children}
|
| {coords && createPortal(
|
| <span
|
| role="tooltip"
|
| style={{
|
| position: 'fixed',
|
| left: coords.x,
|
| top: side === 'top' ? coords.y - 8 : coords.y + 8,
|
| transform: side === 'top' ? 'translate(-50%, -100%)' : 'translate(-50%, 0)',
|
| zIndex: 9999,
|
| }}
|
| className="pointer-events-none w-56 rounded-lg bg-foreground px-2.5 py-1.5 text-xs leading-snug text-background shadow-md"
|
| >
|
| {text}
|
| </span>,
|
| document.body,
|
| )}
|
| </span>
|
| )
|
| }
|
|
|