56 lines
1.2 KiB
TypeScript
56 lines
1.2 KiB
TypeScript
import { type ReactNode, useRef, useState } from "react";
|
|
import { createPortal } from "react-dom";
|
|
|
|
interface TooltipProps {
|
|
content: string;
|
|
children: ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export function Tooltip({
|
|
content,
|
|
children,
|
|
className,
|
|
}: Readonly<TooltipProps>) {
|
|
const ref = useRef<HTMLSpanElement>(null);
|
|
const [pos, setPos] = useState<{ top: number; left: number } | null>(null);
|
|
|
|
function show() {
|
|
const el = ref.current;
|
|
if (!el) return;
|
|
const rect = el.getBoundingClientRect();
|
|
setPos({
|
|
top: rect.top - 4,
|
|
left: rect.left + rect.width / 2,
|
|
});
|
|
}
|
|
|
|
function hide() {
|
|
setPos(null);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<span
|
|
ref={ref}
|
|
onPointerEnter={show}
|
|
onPointerLeave={hide}
|
|
className={className ?? "inline-flex"}
|
|
>
|
|
{children}
|
|
</span>
|
|
{pos !== null &&
|
|
createPortal(
|
|
<div
|
|
role="tooltip"
|
|
className="pointer-events-none fixed z-[60] max-w-64 -translate-x-1/2 -translate-y-full rounded-md border border-border bg-background px-2.5 py-1.5 text-foreground text-xs leading-snug shadow-lg"
|
|
style={{ top: pos.top, left: pos.left }}
|
|
>
|
|
{content}
|
|
</div>,
|
|
document.body,
|
|
)}
|
|
</>
|
|
);
|
|
}
|