import * as React from 'react' ; import { composeEventHandlers } from '@radix-ui/primitive' ; import { useComposedRefs } from '@radix-ui/react-compose-refs' ; import { createContextScope } from '@radix-ui/react-context' ; import { useControllableState } from '@radix-ui/react-use-controllable-state' ; import { useEscapeKeydown } from '@radix-ui/react-use-escape-keydown' ; import { usePrevious } from '@radix-ui/react-use-previous' ; import { useRect } from '@radix-ui/react-use-rect' ; import { Presence } from '@radix-ui/react-presence' ; import { Primitive } from '@radix-ui/react-primitive' ; import * as PopperPrimitive from '@radix-ui/react-popper' ; import { createPopperScope } from '@radix-ui/react-popper' ; import { Portal } from '@radix-ui/react-portal' ; import { Slottable } from '@radix-ui/react-slot' ; import * as VisuallyHiddenPrimitive from '@radix-ui/react-visually-hidden' ; import { useId } from '@radix-ui/react-id' ; import type * as Radix from '@radix-ui/react-primitive' ; import type { Scope } from '@radix-ui/react-context' ; type ScopedProps < P = { } > = P & { __scopeTooltip ? : Scope ; } ; const [ createTooltipContext , createTooltipScope ] = createContextScope ( 'Tooltip' , [ createPopperScope , ] ) ; const usePopperScope = createPopperScope ( ) ; const PROVIDER_NAME = 'TooltipProvider' ; const DEFAULT_DELAY_DURATION = 700 ; const TOOLTIP_OPEN = 'tooltip.open' ; type TooltipProviderContextValue = { isOpenDelayed : boolean ; delayDuration : number ; onOpen ( ) : void ; onClose ( ) : void ; } ; const [ TooltipProviderContextProvider , useTooltipProviderContext ] = createTooltipContext < TooltipProviderContextValue > ( PROVIDER_NAME , { isOpenDelayed : true , delayDuration : DEFAULT_DELAY_DURATION , onOpen : ( ) => { } , onClose : ( ) => { } , } ) ; interface TooltipProviderProps { defaultValue delayDuration ? : number ; defaultValue skipDelayDuration ? : number ; children : React . ReactNode ; } const TooltipProvider = ( props ) => { const { __scopeTooltip , delayDuration = DEFAULT_DELAY_DURATION , skipDelayDuration = 300 , children , } = props ; const [ isOpenDelayed , setIsOpenDelayed ] = React . useState ( true ) ; const skipDelayTimerRef = React . useRef ( 0 ) ; React . useEffect ( ( ) => { const skipDelayTimer = skipDelayTimerRef . current ; return ( ) => window . clearTimeout ( skipDelayTimer ) ; } , [ ] ) ; return ( < TooltipProviderContextProvider scope = { __scopeTooltip } isOpenDelayed = { isOpenDelayed } delayDuration = { delayDuration } onOpen = { React . useCallback ( ( ) => { window . clearTimeout ( skipDelayTimerRef . current ) ; setIsOpenDelayed ( false ) ; } , [ ] ) } onClose = { React . useCallback ( ( ) => { window . clearTimeout ( skipDelayTimerRef . current ) ; skipDelayTimerRef . current = window . setTimeout ( ( ) => setIsOpenDelayed ( true ) , skipDelayDuration ) ; } , [ skipDelayDuration ] ) } > { children } < /TooltipProviderContextProvider>); } ; TooltipProvider . displayName = PROVIDER_NAME ; const TOOLTIP_NAME = 'Tooltip' ; type TooltipContextValue = { contentId : string ; open : boolean ; stateAttribute : 'closed' | 'delayed-open' | 'instant-open' ; trigger : TooltipTriggerElement | null ; onTriggerChange ( trigger ) : void ; onTriggerEnter ( ) : void ; onOpen ( ) : void ; onClose ( ) : void ; } ; const [ TooltipContextProvider , useTooltipContext ] = createTooltipContext < TooltipContextValue > ( TOOLTIP_NAME ) ; interface TooltipProps { open ? : boolean ; defaultOpen ? : boolean ; onOpenChange ? : ( open : boolean ) => void ; defaultValue delayDuration ? : number ; children ? : React . ReactNode ; } const Tooltip = ( props ) => { const { __scopeTooltip , children , open : openProp , defaultOpen = false , onOpenChange , delayDuration : delayDurationProp , } = props ; const context = useTooltipProviderContext ( TOOLTIP_NAME , __scopeTooltip ) ; const popperScope = usePopperScope ( __scopeTooltip ) ; const [ trigger , setTrigger ] = React . useState < HTMLButtonElement | null > ( null ) ; const contentId = useId ( ) ; const openTimerRef = React . useRef ( 0 ) ; const delayDuration = delayDurationProp ?? context . delayDuration ; const wasOpenDelayedRef = React . useRef ( false ) ; const { onOpen , onClose } = context ; const [ open = false , setOpen ] = useControllableState ( { prop : openProp , defaultProp : defaultOpen , onChange : ( open ) => { if ( open ) { document . dispatchEvent ( new CustomEvent ( TOOLTIP_OPEN ) ) ; onOpen ( ) ; } onOpenChange ?. ( open ) ; } , } ) ; const stateAttribute = React . useMemo ( ( ) => { return open ? ( wasOpenDelayedRef . current ? 'delayed-open' : 'instant-open' ) : 'closed' ; } , [ open ] ) ; const handleOpen = React . useCallback ( ( ) => { window . clearTimeout ( openTimerRef . current ) ; wasOpenDelayedRef . current = false ; setOpen ( true ) ; } , [ setOpen ] ) ; const handleDelayedOpen = React . useCallback ( ( ) => { window . clearTimeout ( openTimerRef . current ) ; openTimerRef . current = window . setTimeout ( ( ) => { wasOpenDelayedRef . current = true ; setOpen ( true ) ; } , delayDuration ) ; } , [ delayDuration , setOpen ] ) ; React . useEffect ( ( ) => { return ( ) => window . clearTimeout ( openTimerRef . current ) ; } , [ ] ) ; return ( < PopperPrimitive . Root { ... popperScope } > < TooltipContextProvider scope = { __scopeTooltip } contentId = { contentId } open = { open } stateAttribute = { stateAttribute } trigger = { trigger } onTriggerChange = { setTrigger } onTriggerEnter = { React . useCallback ( ( ) => { if ( context . isOpenDelayed ) handleDelayedOpen ( ) ; else handleOpen ( ) ; } , [ context . isOpenDelayed , handleDelayedOpen , handleOpen ] ) } onOpen = { React . useCallback ( handleOpen , [ handleOpen ] ) } onClose = { React . useCallback ( ( ) => { window . clearTimeout ( openTimerRef . current ) ; setOpen ( false ) ; onClose ( ) ; } , [ setOpen , onClose ] ) } > { children } < /TooltipContextProvider> < /PopperPrimitive.Root>); } ; Tooltip . displayName = TOOLTIP_NAME ; const TRIGGER_NAME = 'TooltipTrigger' ; type TooltipTriggerElement = React . ElementRef < typeof Primitive . button > ; type PrimitiveButtonProps = Radix . ComponentPropsWithoutRef < typeof Primitive . button > ; interface TooltipTriggerProps extends PrimitiveButtonProps { } const TooltipTrigger = React . forwardRef < TooltipTriggerElement , TooltipTriggerProps > ( ( props , forwardedRef ) => { const { __scopeTooltip , ... triggerProps } = props ; const context = useTooltipContext ( TRIGGER_NAME , __scopeTooltip ) ; const popperScope = usePopperScope ( __scopeTooltip ) ; const composedTriggerRef = useComposedRefs ( forwardedRef , context . onTriggerChange ) ; const isMouseDownRef = React . useRef ( false ) ; const handleMouseUp = React . useCallback ( ( ) => ( isMouseDownRef . current = false ) , [ ] ) ; React . useEffect ( ( ) => { return ( ) => document . removeEventListener ( 'mouseup' , handleMouseUp ) ; } , [ handleMouseUp ] ) ; return ( < PopperPrimitive . Anchor asChild { ... popperScope } > < Primitive . button aria - describedby = { context . open ? context . contentId : undefined } data - state = { context . stateAttribute } { ... triggerProps } ref = { composedTriggerRef } onMouseEnter = { composeEventHandlers ( props . onMouseEnter , context . onTriggerEnter ) } onMouseLeave = { composeEventHandlers ( props . onMouseLeave , context . onClose ) } onMouseDown = { composeEventHandlers ( props . onMouseDown , ( ) => { context . onClose ( ) ; isMouseDownRef . current = true ; document . addEventListener ( 'mouseup' , handleMouseUp , { once : true } ) ; } ) } onFocus = { composeEventHandlers ( props . onFocus , ( ) => { if ( ! isMouseDownRef . current ) context . onOpen ( ) ; } ) } onBlur = { composeEventHandlers ( props . onBlur , context . onClose ) } onClick = { composeEventHandlers ( props . onClick , context . onClose ) } / > < /PopperPrimitive.Anchor>); } ) ; TooltipTrigger . displayName = TRIGGER_NAME ; const CONTENT_NAME = 'TooltipContent' ; type TooltipContentElement = TooltipContentImplElement ; interface TooltipContentProps extends TooltipContentImplProps { /**
     * Used to force mounting when more control is needed. Useful when
     * controlling animation with React animation libraries.
     */ forceMount ? : true ; } const TooltipContent = React . forwardRef < TooltipContentElement , TooltipContentProps > ( ( props , forwardedRef ) => { const { forceMount , ... contentProps } = props ; const context = useTooltipContext ( CONTENT_NAME , props . __scopeTooltip ) ; return ( < Presence present = { forceMount || context . open } > < TooltipContentImpl ref = { forwardedRef } { ... contentProps } / > < /Presence>); } ) ; type TooltipContentImplElement = React . ElementRef < typeof PopperPrimitive . Content > ; type PopperContentProps = Radix . ComponentPropsWithoutRef < typeof PopperPrimitive . Content > ; interface TooltipContentImplProps extends PopperContentProps { /**
     * A more descriptive label for accessibility purpose
     */ 'aria-label' ? : string ; /**
     * Whether the Tooltip should render in a Portal
     * (default: `true`)
     */ portalled ? : boolean ; } const TooltipContentImpl = React . forwardRef < TooltipContentImplElement , TooltipContentImplProps > ( ( props , forwardedRef ) => { const { __scopeTooltip , children , 'aria-label' : ariaLabel , portalled = true , ... contentProps } = props ; const context = useTooltipContext ( CONTENT_NAME , __scopeTooltip ) ; const popperScope = usePopperScope ( __scopeTooltip ) ; const PortalWrapper = portalled ? Portal : React . Fragment ; const { onClose } = context ; useEscapeKeydown ( ( ) => onClose ( ) ) ; React . useEffect ( ( ) => { document . addEventListener ( TOOLTIP_OPEN , onClose ) ; return ( ) => document . removeEventListener ( TOOLTIP_OPEN , onClose ) ; } , [ onClose ] ) ; return ( < PortalWrapper > < CheckTriggerMoved __scopeTooltip = { __scopeTooltip } / > < PopperPrimitive . Content data - state = { context . stateAttribute } { ... popperScope } { ... contentProps } ref = { forwardedRef } style = { { ... contentProps . style , [ '--radix-tooltip-content-transform-origin' as any ] : 'var(--radix-popper-transform-origin)' , } } > < Slottable > { children } < /Slottable> < VisuallyHiddenPrimitive . Root id = { context . contentId } role = "tooltip" > { ariaLabel || children } < /VisuallyHiddenPrimitive.Root> < /PopperPrimitive.Content> < /PortalWrapper>); } ) ; TooltipContent . displayName = CONTENT_NAME ; const ARROW_NAME = 'TooltipArrow' ; type TooltipArrowElement = React . ElementRef < typeof PopperPrimitive . Arrow > ; type PopperArrowProps = Radix . ComponentPropsWithoutRef < typeof PopperPrimitive . Arrow > ; interface TooltipArrowProps extends PopperArrowProps { } const TooltipArrow = React . forwardRef < TooltipArrowElement , TooltipArrowProps > ( ( props , forwardedRef ) => { const { __scopeTooltip , ... arrowProps } = props ; const popperScope = usePopperScope ( __scopeTooltip ) ; return < PopperPrimitive . Arrow { ... popperScope } { ... arrowProps } ref = { forwardedRef } / > ; } ) ; TooltipArrow . displayName = ARROW_NAME ; function CheckTriggerMoved ( props ) { const { __scopeTooltip } = props ; const context = useTooltipContext ( 'CheckTriggerMoved' , __scopeTooltip ) ; const triggerRect = useRect ( context . trigger ) ; const triggerLeft = triggerRect ?. left ; const previousTriggerLeft = usePrevious ( triggerLeft ) ; const triggerTop = triggerRect ?. top ; const previousTriggerTop = usePrevious ( triggerTop ) ; const handleClose = context . onClose ; React . useEffect ( ( ) => { const hasTriggerMoved = ( previousTriggerLeft !== undefined && previousTriggerLeft !== triggerLeft ) || ( previousTriggerTop !== undefined && previousTriggerTop !== triggerTop ) ; if ( hasTriggerMoved ) { handleClose ( ) ; } } , [ handleClose , previousTriggerLeft , previousTriggerTop , triggerLeft , triggerTop ] ) ; return null ; } const Provider = TooltipProvider ; const Root = Tooltip ; const Trigger = TooltipTrigger ; const Content = TooltipContent ; const Arrow = TooltipArrow ; export { createTooltipScope , TooltipProvider , Tooltip , TooltipTrigger , TooltipContent , TooltipArrow , Provider , Root , Trigger , Content , Arrow , } ; export type { TooltipProps , TooltipTriggerProps , TooltipContentProps , TooltipArrowProps } ;