import React, { forwardRef, ForwardedRef, ElementRef, ReactNode, useRef, useState, } from 'react' import { isText, noop } from '@aviato/utils' import { mergeRefs } from '~/hooks' import { styled } from '~/theme' import { Text, Popover, SharedPopoverProps } from '~/components' const StyledTooltip = styled('div', { position: 'relative', }) const TooltipContainer = styled('div', { background: '$CalloutMain', padding: '4px 8px', border: '1px solid $OtherDivider', color: '$TextPrimary', borderRadius: 4, }) const TextContainer = styled('div', { color: '$TextPrimary', }) export interface TooltipProps extends SharedPopoverProps { /** * Text or component to show when label is active */ label: ReactNode | string /** * Target child to wrap label display */ children: ReactNode /** * Control Tooltip state yourself */ opened?: boolean /** * Delay before showing tooltip */ delay?: number /** * Disable tooltip */ disabled?: boolean /** * Set specific tooltip width */ width?: number | 'auto' /** * Escape-hatch to allow pointer-events */ allowPointerEvents?: boolean /** * Use with multi-line tooltip text */ wrapLines?: boolean /** * Triggers when the mouse enters the target component bounding box */ onMouseEnter?: () => void /** * Triggers when the mouse leaves the target component bounding box */ onMouseLeave?: () => void /** * Forward a reference to the tooltip itself */ tooltipRef?: ForwardedRef /** * useEffect dependencies to force update tooltip position */ positionDependencies?: any[] /** * Whether to render the target element in a Portal */ disablePortal?: boolean } export const Tooltip = forwardRef< ElementRef, TooltipProps >((properties, forwardedRef) => { const { label, position = 'bottom', placement = 'center', delay = 0, opened, disabled = false, tooltipRef, onMouseLeave = noop, onMouseEnter = noop, width = 'auto', wrapLines = false, allowPointerEvents = false, children, ...remainingProps } = properties const timeoutRef = useRef() const [isOpen, setOpened] = useState(false) const isVisible = (typeof opened === 'boolean' ? opened : isOpen) && !disabled const [referenceElement, setReferenceElement] = useState(null) const handleOpen = () => { window.clearTimeout(timeoutRef.current) setOpened(true) } const handleClose = () => { if (delay !== 0) { timeoutRef.current = window.setTimeout(() => { setOpened(false) }, delay) } else { setOpened(false) } } const ChildVariant = isText(children) ? ( {children} ) : ( children ) return ( { handleOpen() onMouseEnter(event) }} onMouseLeave={(event) => { handleClose() onMouseLeave(event) }} onFocusCapture={handleOpen} onBlurCapture={handleClose} ref={mergeRefs(setReferenceElement, forwardedRef)} {...remainingProps} > {label} {ChildVariant} ) }) Tooltip.displayName = 'Tooltip'