import { useState, useRef, cloneElement, isValidElement, type ReactElement, type ReactNode, } from 'react' import { useFloating, useHover, useFocus, useDismiss, useRole, useInteractions, offset, flip, shift, arrow, FloatingArrow, FloatingPortal, autoUpdate, type Placement, } from '@floating-ui/react' import { cn } from '@/lib/utils' export interface TooltipProps { content: ReactNode placement?: Placement delay?: number | { open?: number; close?: number } children: ReactElement className?: string } export function Tooltip({ content, placement = 'top', delay = { open: 400, close: 0 }, children, className, }: TooltipProps) { const [open, setOpen] = useState(false) const arrowRef = useRef(null) const { refs, floatingStyles, context } = useFloating({ open, onOpenChange: setOpen, placement, whileElementsMounted: autoUpdate, middleware: [ offset(8), flip({ fallbackAxisSideDirection: 'start' }), shift({ padding: 8 }), arrow({ element: arrowRef }), ], }) const hover = useHover(context, { delay }) const focus = useFocus(context) const dismiss = useDismiss(context) const role = useRole(context, { role: 'tooltip' }) const { getReferenceProps, getFloatingProps } = useInteractions([ hover, focus, dismiss, role, ]) if (!isValidElement(children)) return children return ( <> {cloneElement(children, { ref: refs.setReference, ...getReferenceProps(), } as Record)} {open && (
{content}
)} ) }