1
- import React , { useEffect , useState , useRef , useCallback } from 'react'
1
+ import React , { useEffect , useState , useRef , useCallback , useImperativeHandle } from 'react'
2
2
import { autoUpdate } from '@floating-ui/dom'
3
3
import classNames from 'classnames'
4
4
import debounce from 'utils/debounce'
@@ -8,10 +8,11 @@ import { getScrollParent } from 'utils/get-scroll-parent'
8
8
import { computeTooltipPosition } from 'utils/compute-positions'
9
9
import coreStyles from './core-styles.module.css'
10
10
import styles from './styles.module.css'
11
- import type { IPosition , ITooltip , PlacesType } from './TooltipTypes'
11
+ import type { IPosition , ITooltip , PlacesType , TooltipImperativeOpenOptions } from './TooltipTypes'
12
12
13
13
const Tooltip = ( {
14
14
// props
15
+ forwardRef,
15
16
id,
16
17
className,
17
18
classNameArrow,
@@ -58,6 +59,9 @@ const Tooltip = ({
58
59
const [ inlineArrowStyles , setInlineArrowStyles ] = useState ( { } )
59
60
const [ show , setShow ] = useState ( false )
60
61
const [ rendered , setRendered ] = useState ( false )
62
+ const [ imperativeOptions , setImperativeOptions ] = useState < TooltipImperativeOpenOptions | null > (
63
+ null ,
64
+ )
61
65
const wasShowing = useRef ( false )
62
66
const lastFloatPosition = useRef < IPosition | null > ( null )
63
67
/**
@@ -149,6 +153,7 @@ const Tooltip = ({
149
153
if ( show ) {
150
154
afterShow ?.( )
151
155
} else {
156
+ setImperativeOptions ( null )
152
157
afterHide ?.( )
153
158
}
154
159
} , [ show ] )
@@ -274,6 +279,9 @@ const Tooltip = ({
274
279
}
275
280
276
281
const handleClickOutsideAnchors = ( event : MouseEvent ) => {
282
+ if ( ! show ) {
283
+ return
284
+ }
277
285
const anchorById = document . querySelector < HTMLElement > ( `[id='${ anchorId } ']` )
278
286
const anchors = [ anchorById , ...anchorsBySelect ]
279
287
if ( anchors . some ( ( anchor ) => anchor ?. contains ( event . target as HTMLElement ) ) ) {
@@ -293,9 +301,10 @@ const Tooltip = ({
293
301
const debouncedHandleShowTooltip = debounce ( handleShowTooltip , 50 , true )
294
302
const debouncedHandleHideTooltip = debounce ( handleHideTooltip , 50 , true )
295
303
const updateTooltipPosition = useCallback ( ( ) => {
296
- if ( position ) {
304
+ const actualPosition = imperativeOptions ?. position ?? position
305
+ if ( actualPosition ) {
297
306
// if `position` is set, override regular and `float` positioning
298
- handleTooltipPosition ( position )
307
+ handleTooltipPosition ( actualPosition )
299
308
return
300
309
}
301
310
@@ -349,6 +358,7 @@ const Tooltip = ({
349
358
offset ,
350
359
positionStrategy ,
351
360
position ,
361
+ imperativeOptions ?. position ,
352
362
float ,
353
363
] )
354
364
@@ -484,7 +494,7 @@ const Tooltip = ({
484
494
] )
485
495
486
496
useEffect ( ( ) => {
487
- let selector = anchorSelect ?? ''
497
+ let selector = imperativeOptions ?. anchorSelect ?? anchorSelect ?? ''
488
498
if ( ! selector && id ) {
489
499
selector = `[data-tooltip-id='${ id } ']`
490
500
}
@@ -584,7 +594,7 @@ const Tooltip = ({
584
594
return ( ) => {
585
595
documentObserver . disconnect ( )
586
596
}
587
- } , [ id , anchorSelect , activeAnchor ] )
597
+ } , [ id , anchorSelect , imperativeOptions ?. anchorSelect , activeAnchor ] )
588
598
589
599
useEffect ( ( ) => {
590
600
updateTooltipPosition ( )
@@ -628,7 +638,7 @@ const Tooltip = ({
628
638
} , [ ] )
629
639
630
640
useEffect ( ( ) => {
631
- let selector = anchorSelect
641
+ let selector = imperativeOptions ?. anchorSelect ?? anchorSelect
632
642
if ( ! selector && id ) {
633
643
selector = `[data-tooltip-id='${ id } ']`
634
644
}
@@ -642,9 +652,34 @@ const Tooltip = ({
642
652
// warning was already issued in the controller
643
653
setAnchorsBySelect ( [ ] )
644
654
}
645
- } , [ id , anchorSelect ] )
655
+ } , [ id , anchorSelect , imperativeOptions ?. anchorSelect ] )
656
+
657
+ const actualContent = imperativeOptions ?. content ?? content
658
+ const canShow = Boolean ( ! hidden && actualContent && show && Object . keys ( inlineStyles ) . length > 0 )
646
659
647
- const canShow = ! hidden && content && show && Object . keys ( inlineStyles ) . length > 0
660
+ useImperativeHandle ( forwardRef , ( ) => ( {
661
+ open : ( options ) => {
662
+ if ( options ?. anchorSelect ) {
663
+ try {
664
+ document . querySelector ( options . anchorSelect )
665
+ } catch {
666
+ if ( ! process . env . NODE_ENV || process . env . NODE_ENV !== 'production' ) {
667
+ // eslint-disable-next-line no-console
668
+ console . warn ( `[react-tooltip] "${ options . anchorSelect } " is not a valid CSS selector` )
669
+ }
670
+ return
671
+ }
672
+ }
673
+ setImperativeOptions ( options ?? null )
674
+ handleShow ( true )
675
+ } ,
676
+ close : ( ) => {
677
+ handleShow ( false )
678
+ } ,
679
+ activeAnchor,
680
+ place : actualPlacement ,
681
+ isOpen : rendered && canShow ,
682
+ } ) )
648
683
649
684
return rendered ? (
650
685
< WrapperElement
@@ -671,7 +706,7 @@ const Tooltip = ({
671
706
} }
672
707
ref = { tooltipRef }
673
708
>
674
- { content }
709
+ { actualContent }
675
710
< WrapperElement
676
711
className = { classNames (
677
712
'react-tooltip-arrow' ,
0 commit comments