Files
ragflow/web/src/components/ui/tooltip.tsx
chanx 1f47001c82 Fix: Optimize tooltips and I118n #3221 (#9744)
### What problem does this PR solve?

Fix: Optimize tooltips and I118n #3221

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

---------

Co-authored-by: Kevin Hu <kevinhu.sh@gmail.com>
2025-08-27 11:46:51 +08:00

134 lines
3.9 KiB
TypeScript

'use client';
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
import * as React from 'react';
import { cn } from '@/lib/utils';
import { Info } from 'lucide-react';
const TooltipProvider = TooltipPrimitive.Provider;
const Tooltip = TooltipPrimitive.Root;
const TooltipTrigger = TooltipPrimitive.Trigger;
const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 max-w-[20vw]',
className,
)}
{...props}
/>
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
export const FormTooltip = ({ tooltip }: { tooltip: React.ReactNode }) => {
return (
<Tooltip>
<TooltipTrigger tabIndex={-1}>
<Info className="size-3 ml-2" />
</TooltipTrigger>
<TooltipContent>
<p>{tooltip}</p>
</TooltipContent>
</Tooltip>
);
};
export interface AntToolTipProps {
title: React.ReactNode;
children: React.ReactNode;
placement?: 'top' | 'bottom' | 'left' | 'right';
trigger?: 'hover' | 'click' | 'focus';
className?: string;
}
export const AntToolTip: React.FC<AntToolTipProps> = ({
title,
children,
placement = 'top',
trigger = 'hover',
className,
}) => {
const [visible, setVisible] = React.useState(false);
const showTooltip = () => {
if (trigger === 'hover' || trigger === 'focus') {
setVisible(true);
}
};
const hideTooltip = () => {
if (trigger === 'hover' || trigger === 'focus') {
setVisible(false);
}
};
const toggleTooltip = () => {
if (trigger === 'click') {
setVisible(!visible);
}
};
const getPlacementClasses = () => {
switch (placement) {
case 'top':
return 'bottom-full left-1/2 transform -translate-x-1/2 mb-2';
case 'bottom':
return 'top-full left-1/2 transform -translate-x-1/2 mt-2';
case 'left':
return 'right-full top-1/2 transform -translate-y-1/2 mr-2';
case 'right':
return 'left-full top-1/2 transform -translate-y-1/2 ml-2';
default:
return 'bottom-full left-1/2 transform -translate-x-1/2 mb-2';
}
};
return (
<div className="inline-block relative">
<div
onMouseEnter={showTooltip}
onMouseLeave={hideTooltip}
onClick={toggleTooltip}
onFocus={showTooltip}
onBlur={hideTooltip}
>
{children}
</div>
{visible && title && (
<div
className={cn(
'absolute z-50 px-2.5 py-1.5 text-xs text-white bg-gray-800 rounded-sm shadow-sm whitespace-nowrap',
getPlacementClasses(),
className,
)}
>
{title}
<div
className={cn(
'absolute w-2 h-2 bg-gray-800',
placement === 'top' &&
'bottom-[-4px] left-1/2 transform -translate-x-1/2 rotate-45',
placement === 'bottom' &&
'top-[-4px] left-1/2 transform -translate-x-1/2 rotate-45',
placement === 'left' &&
'right-[-4px] top-1/2 transform -translate-y-1/2 rotate-45',
placement === 'right' &&
'left-[-4px] top-1/2 transform -translate-y-1/2 rotate-45',
)}
/>
</div>
)}
</div>
);
};