import type { ReactNode } from "react"; import type { ButtonProps as AriaButtonProps, TooltipProps as AriaTooltipProps, TooltipTriggerComponentProps as AriaTooltipTriggerComponentProps, } from "react-aria-components"; import { Button as AriaButton, OverlayArrow as AriaOverlayArrow, Tooltip as AriaTooltip, TooltipTrigger as AriaTooltipTrigger } from "react-aria-components"; import { cx } from "@/utils/cx"; interface TooltipProps extends AriaTooltipTriggerComponentProps, Omit { /** * The title of the tooltip. */ title: ReactNode; /** * The description of the tooltip. */ description?: ReactNode; /** * Whether to show the arrow on the tooltip. * * @default false */ arrow?: boolean; /** * Delay in milliseconds before the tooltip is shown. * * @default 300 */ delay?: number; } export const Tooltip = ({ title, description, children, arrow = false, delay = 300, closeDelay = 0, trigger, isDisabled, isOpen, defaultOpen, offset = 6, crossOffset, placement = "top", onOpenChange, ...tooltipProps }: TooltipProps) => { const isTopOrBottomLeft = ["top left", "top end", "bottom left", "bottom end"].includes(placement); const isTopOrBottomRight = ["top right", "top start", "bottom right", "bottom start"].includes(placement); // Set negative cross offset for left and right placement to visually balance the tooltip. const calculatedCrossOffset = isTopOrBottomLeft ? -12 : isTopOrBottomRight ? 12 : 0; return ( {children} cx(isEntering && "ease-out animate-in", isExiting && "ease-in animate-out")} > {({ isEntering, isExiting }) => (
{title} {description && {description}} {arrow && ( )}
)}
); }; interface TooltipTriggerProps extends AriaButtonProps {} export const TooltipTrigger = ({ children, className, ...buttonProps }: TooltipTriggerProps) => { return ( cx("h-max w-max outline-hidden", typeof className === "function" ? className(values) : className)}> {children} ); };