import React, { useEffect, useRef, useState } from 'react'; import { clsx } from 'clsx'; export interface TooltipProps { content: React.ReactNode; children: React.ReactNode; position?: 'top' | 'bottom' | 'left' | 'right'; delay?: number; className?: string; disabled?: boolean; } const positionClasses = { top: 'bottom-full left-1/2 -translate-x-1/2 mb-2', bottom: 'top-full left-1/2 -translate-x-1/2 mt-2', left: 'right-full top-1/2 -translate-y-1/2 mr-2', right: 'left-full top-1/2 -translate-y-1/2 ml-2', }; const arrowClasses = { top: 'top-full left-1/2 -translate-x-1/2 border-t-background border-l-transparent border-r-transparent border-b-transparent', bottom: 'bottom-full left-1/2 -translate-x-1/2 border-b-background border-l-transparent border-r-transparent border-t-transparent', left: 'left-full top-1/2 -translate-y-1/2 border-l-background border-t-transparent border-b-transparent border-r-transparent', right: 'right-full top-1/2 -translate-y-1/2 border-r-background border-t-transparent border-b-transparent border-l-transparent', }; /** * Tooltip component */ export const Tooltip = ({ content, children, position = 'top', delay = 200, className, disabled = false, }: TooltipProps) => { const [isVisible, setIsVisible] = useState(false); const [showTooltip, setShowTooltip] = useState(false); const timeoutRef = useRef(); const isVisibleRef = useRef(isVisible); // Keep ref in sync with state useEffect(() => { isVisibleRef.current = isVisible; }, [isVisible]); // Handle tooltip visibility with proper cleanup useEffect(() => { // Clear any existing timeout if (timeoutRef.current) { clearTimeout(timeoutRef.current); timeoutRef.current = undefined; } if (isVisible && !disabled) { // Set up delayed show timeoutRef.current = setTimeout(() => { // Only show if still visible when timeout fires if (isVisibleRef.current) { setShowTooltip(true); } }, delay); } else { // Hide immediately when not visible or disabled setShowTooltip(false); } return () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); timeoutRef.current = undefined; } }; }, [isVisible, delay, disabled]); if (disabled) { return <>{children}; } return (
setIsVisible(true)} onMouseLeave={() => { setIsVisible(false); setShowTooltip(false); }} onFocus={() => setIsVisible(true)} onBlur={() => { setIsVisible(false); setShowTooltip(false); }} > {children} {showTooltip && (
{content}
)}
); };