turash/bugulma/frontend/components/ui/Tooltip.tsx

106 lines
2.8 KiB
TypeScript

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<NodeJS.Timeout>();
useEffect(() => {
if (isVisible && !disabled) {
timeoutRef.current = setTimeout(() => {
setShowTooltip(true);
}, delay);
} else {
setShowTooltip(false);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
}
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, [isVisible, delay, disabled]);
if (disabled) {
return <>{children}</>;
}
return (
<div
className="relative inline-block"
onMouseEnter={() => setIsVisible(true)}
onMouseLeave={() => {
setIsVisible(false);
setShowTooltip(false);
}}
onFocus={() => setIsVisible(true)}
onBlur={() => {
setIsVisible(false);
setShowTooltip(false);
}}
>
{children}
{showTooltip && (
<div
className={clsx(
'absolute z-50 px-2 py-1',
'text-xs text-foreground bg-background',
'border rounded-md shadow-lg',
'whitespace-nowrap',
'animate-in fade-in-0 zoom-in-95 duration-200',
positionClasses[position],
className
)}
role="tooltip"
>
{content}
<div
className={clsx(
'absolute border-4',
arrowClasses[position]
)}
/>
</div>
)}
</div>
);
};