import React, { useRef, cloneElement, useEffect } from "react"; import classnames from "classnames"; import { bem } from "../../utilities/bem"; import { useDisclosure, useKeyPress } from "../../hooks"; import { Popover, PopoverTooltipSharedProps } from "../Popover"; import { KEY_CODE } from "../../types"; export interface TooltipProps extends PopoverTooltipSharedProps { /** * Number of milliseconds to delay the disclosure of the tooltip */ delay?: number; } const cn = "Tooltip"; export const Tooltip = (props: TooltipProps) => { const { isVisible: isVisibleProp = false, children, referenceElement, placement = "bottom", className, delay = 200, style, ...rest } = props; const { isOpen, onOpen, onClose } = useDisclosure(isVisibleProp); const isHoveringReferenceRef = useRef(false); const isHoveringPopoverRef = useRef(false); const referenceTimoutRef = useRef(null); const popoverTimoutRef = useRef(null); useEffect(() => { if (isVisibleProp) { onOpen(); } }, [isVisibleProp, onOpen]); useEffect(() => { return () => { if (referenceTimoutRef.current) { clearTimeout(referenceTimoutRef.current); } if (popoverTimoutRef.current) { clearTimeout(popoverTimoutRef.current); } }; }); const close = () => { if (!isHoveringPopoverRef.current) { onClose(); } }; const handleMouseEnter = () => { isHoveringReferenceRef.current = true; if (!isOpen) { const referenceTimeout = setTimeout(() => { if (isHoveringReferenceRef.current) { onOpen(); } }, delay); referenceTimoutRef.current = referenceTimeout; } }; const handleMouseLeave = () => { isHoveringReferenceRef.current = false; if (isOpen) { const popoverTimeout = setTimeout(close, delay); popoverTimoutRef.current = popoverTimeout; } }; const handlePopoverMouseEnter = () => { if (!isHoveringPopoverRef.current) { isHoveringPopoverRef.current = true; } }; const handlePopoverMouseLeave = () => { isHoveringPopoverRef.current = false; if (isOpen) { const popoverTimeout = setTimeout(close, delay); popoverTimoutRef.current = popoverTimeout; } }; useKeyPress(KEY_CODE.ESC, () => { if (isOpen && !isVisibleProp) { close(); } }); const referenceElementWithMouseHandlers = !isVisibleProp ? cloneElement(referenceElement, { onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onFocus: handleMouseEnter, onBlur: handleMouseLeave, tabIndex: 0, }) : referenceElement; return ( {children} ); };