import classNames from 'classnames'; import type { CSSProperties, KeyboardEvent, ReactNode } from 'react'; import { useRef, useState } from 'react'; import { generateId } from '../../lib/generateId'; import { Portal } from '../Portal'; export interface TooltipProps { children: ReactNode; content: ReactNode; tabIndex?: number; size?: 'md' | 'lg'; className?: string; } const hiddenStyles: CSSProperties = { left: -99999, top: -99999, visibility: 'hidden', pointerEvents: 'none', opacity: 0, }; export function Tooltip({ children, className, content, tabIndex, size = 'md' }: TooltipProps) { const [isOpen, setIsOpen] = useState(); const triggerRef = useRef(null); const tooltipRef = useRef(null); const showTimeout = useRef(undefined); const handleOpenImmediate = () => { if (triggerRef.current == null || tooltipRef.current == null) return; clearTimeout(showTimeout.current); setIsOpen(undefined); const triggerRect = triggerRef.current.getBoundingClientRect(); const tooltipRect = tooltipRef.current.getBoundingClientRect(); const docRect = document.documentElement.getBoundingClientRect(); const styles: CSSProperties = { bottom: docRect.height - triggerRect.top, left: Math.max(0, triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2), maxHeight: triggerRect.top, }; setIsOpen(styles); }; const handleOpen = () => { clearTimeout(showTimeout.current); showTimeout.current = setTimeout(handleOpenImmediate, 500); }; const handleClose = () => { clearTimeout(showTimeout.current); setIsOpen(undefined); }; const handleToggleImmediate = () => { if (isOpen) handleClose(); else handleOpenImmediate(); }; const handleKeyDown = (e: KeyboardEvent) => { if (isOpen && e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); handleClose(); } }; const id = useRef(`tooltip-${generateId()}`); return ( <> {/** biome-ignore lint/a11y/useSemanticElements: Needs to be usable in other buttons */} {children} ); } function Triangle({ className }: { className?: string }) { return ( Triangle ); }