import { useState, cloneElement } from 'react'; import { useFloating, autoUpdate, offset, flip, shift, useHover, useFocus, useDismiss, useRole, useInteractions, type Placement, } from '@floating-ui/react'; import { motion, AnimatePresence } from 'framer-motion'; import { slideX, slideY, mix, fade } from '@/lib/motion'; import styles from './tooltip.module.css'; interface TooltipProps { children: JSX.Element; content: string; hideDelay?: number; placement?: Placement; showDelay?: number; } export function Tooltip({ children, content, hideDelay = 100, placement = 'top', showDelay = 500, }: TooltipProps) { const [isTooltipOpen, setIsTooltipOpen] = useState(false); const { context, floatingStyles, placement: computedPlacement, refs, } = useFloating({ middleware: [offset(12), flip(), shift({ padding: 8 })], onOpenChange: setIsTooltipOpen, open: isTooltipOpen, placement: placement, whileElementsMounted: autoUpdate, }); const hover = useHover(context, { delay: showDelay, move: false, restMs: hideDelay, }); const focus = useFocus(context); const dismiss = useDismiss(context); const role = useRole(context, { role: 'tooltip' }); const { getFloatingProps, getReferenceProps } = useInteractions([ hover, focus, dismiss, role, ]); const slide = { bottom: slideY(-5), left: slideX(5), right: slideX(-5), top: slideY(5), }[ computedPlacement.includes('-') ? computedPlacement.split('-')[0] : computedPlacement ]; const variants = mix(fade(), slide!); const textVariants = fade(); return ( <> {cloneElement( children, getReferenceProps({ ref: refs.setReference, ...children.props }), )} {isTooltipOpen && (
{content}
)}
); }