@@ -8,7 +8,14 @@ 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 {
12
+ AnchorCloseEvents ,
13
+ AnchorOpenEvents ,
14
+ GlobalCloseEvents ,
15
+ IPosition ,
16
+ ITooltip ,
17
+ PlacesType ,
18
+ } from './TooltipTypes'
12
19
13
20
const Tooltip = ( {
14
21
// props
@@ -34,6 +41,9 @@ const Tooltip = ({
34
41
closeOnEsc = false ,
35
42
closeOnScroll = false ,
36
43
closeOnResize = false ,
44
+ openEvents,
45
+ closeEvents,
46
+ globalCloseEvents,
37
47
style : externalStyles ,
38
48
position,
39
49
afterShow,
@@ -68,7 +78,49 @@ const Tooltip = ({
68
78
const [ anchorsBySelect , setAnchorsBySelect ] = useState < HTMLElement [ ] > ( [ ] )
69
79
const mounted = useRef ( false )
70
80
81
+ /**
82
+ * @todo Update when deprecated stuff gets removed.
83
+ */
71
84
const shouldOpenOnClick = openOnClick || events . includes ( 'click' )
85
+ const hasClickEvent =
86
+ shouldOpenOnClick || openEvents ?. click || openEvents ?. dblclick || openEvents ?. mousedown
87
+ const actualOpenEvents : AnchorOpenEvents = openEvents
88
+ ? { ...openEvents }
89
+ : {
90
+ mouseenter : true ,
91
+ focus : true ,
92
+ click : false ,
93
+ dblclick : false ,
94
+ mousedown : false ,
95
+ }
96
+ if ( ! openEvents && shouldOpenOnClick ) {
97
+ Object . assign ( actualOpenEvents , {
98
+ mouseenter : false ,
99
+ focus : false ,
100
+ click : true ,
101
+ } )
102
+ }
103
+ const actualCloseEvents : AnchorCloseEvents = closeEvents
104
+ ? { ...closeEvents }
105
+ : {
106
+ mouseleave : true ,
107
+ blur : true ,
108
+ click : false ,
109
+ }
110
+ if ( ! closeEvents && shouldOpenOnClick ) {
111
+ Object . assign ( actualCloseEvents , {
112
+ mouseleave : false ,
113
+ blur : false ,
114
+ } )
115
+ }
116
+ const actualGlobalCloseEvents : GlobalCloseEvents = globalCloseEvents
117
+ ? { ...globalCloseEvents }
118
+ : {
119
+ escape : closeOnEsc || false ,
120
+ scroll : closeOnScroll || false ,
121
+ resize : closeOnResize || false ,
122
+ clickOutsideAnchor : hasClickEvent || false ,
123
+ }
72
124
73
125
/**
74
126
* useLayoutEffect runs before useEffect,
@@ -248,13 +300,6 @@ const Tooltip = ({
248
300
lastFloatPosition . current = mousePosition
249
301
}
250
302
251
- const handleClickTooltipAnchor = ( event ?: Event ) => {
252
- handleShowTooltip ( event )
253
- if ( delayHide ) {
254
- handleHideTooltipDelayed ( )
255
- }
256
- }
257
-
258
303
const handleClickOutsideAnchors = ( event : MouseEvent ) => {
259
304
const anchorById = document . querySelector < HTMLElement > ( `[id='${ anchorId } ']` )
260
305
const anchors = [ anchorById , ...anchorsBySelect ]
@@ -353,13 +398,13 @@ const Tooltip = ({
353
398
const anchorScrollParent = getScrollParent ( activeAnchor )
354
399
const tooltipScrollParent = getScrollParent ( tooltipRef . current )
355
400
356
- if ( closeOnScroll ) {
401
+ if ( actualGlobalCloseEvents . scroll ) {
357
402
window . addEventListener ( 'scroll' , handleScrollResize )
358
403
anchorScrollParent ?. addEventListener ( 'scroll' , handleScrollResize )
359
404
tooltipScrollParent ?. addEventListener ( 'scroll' , handleScrollResize )
360
405
}
361
406
let updateTooltipCleanup : null | ( ( ) => void ) = null
362
- if ( closeOnResize ) {
407
+ if ( actualGlobalCloseEvents . resize ) {
363
408
window . addEventListener ( 'resize' , handleScrollResize )
364
409
} else if ( activeAnchor && tooltipRef . current ) {
365
410
updateTooltipCleanup = autoUpdate (
@@ -380,29 +425,63 @@ const Tooltip = ({
380
425
}
381
426
handleShow ( false )
382
427
}
383
-
384
- if ( closeOnEsc ) {
428
+ if ( actualGlobalCloseEvents . escape ) {
385
429
window . addEventListener ( 'keydown' , handleEsc )
386
430
}
387
431
432
+ if ( actualGlobalCloseEvents . clickOutsideAnchor ) {
433
+ window . addEventListener ( 'click' , handleClickOutsideAnchors )
434
+ }
435
+
388
436
const enabledEvents : { event : string ; listener : ( event ?: Event ) => void } [ ] = [ ]
389
437
390
- if ( shouldOpenOnClick ) {
391
- window . addEventListener ( 'click' , handleClickOutsideAnchors )
392
- enabledEvents . push ( { event : 'click' , listener : handleClickTooltipAnchor } )
393
- } else {
394
- enabledEvents . push (
395
- { event : 'mouseenter' , listener : debouncedHandleShowTooltip } ,
396
- { event : 'mouseleave' , listener : debouncedHandleHideTooltip } ,
397
- { event : 'focus' , listener : debouncedHandleShowTooltip } ,
398
- { event : 'blur' , listener : debouncedHandleHideTooltip } ,
399
- )
400
- if ( float ) {
401
- enabledEvents . push ( {
402
- event : 'mousemove' ,
403
- listener : handleMouseMove ,
404
- } )
438
+ const handleClickOpenTooltipAnchor = ( event ?: Event ) => {
439
+ if ( show ) {
440
+ return
405
441
}
442
+ handleShowTooltip ( event )
443
+ }
444
+ const handleClickCloseTooltipAnchor = ( ) => {
445
+ if ( ! show ) {
446
+ return
447
+ }
448
+ handleHideTooltip ( )
449
+ }
450
+
451
+ const regularEvents = [ 'mouseenter' , 'mouseleave' , 'focus' , 'blur' ]
452
+ const clickEvents = [ 'click' , 'dblclick' , 'mousedown' , 'mouseup' ]
453
+
454
+ Object . entries ( actualOpenEvents ) . forEach ( ( [ event , enabled ] ) => {
455
+ if ( ! enabled ) {
456
+ return
457
+ }
458
+ if ( regularEvents . includes ( event ) ) {
459
+ enabledEvents . push ( { event, listener : debouncedHandleShowTooltip } )
460
+ } else if ( clickEvents . includes ( event ) ) {
461
+ enabledEvents . push ( { event, listener : handleClickOpenTooltipAnchor } )
462
+ } else {
463
+ // never happens
464
+ }
465
+ } )
466
+
467
+ Object . entries ( actualCloseEvents ) . forEach ( ( [ event , enabled ] ) => {
468
+ if ( ! enabled ) {
469
+ return
470
+ }
471
+ if ( regularEvents . includes ( event ) ) {
472
+ enabledEvents . push ( { event, listener : debouncedHandleHideTooltip } )
473
+ } else if ( clickEvents . includes ( event ) ) {
474
+ enabledEvents . push ( { event, listener : handleClickCloseTooltipAnchor } )
475
+ } else {
476
+ // never happens
477
+ }
478
+ } )
479
+
480
+ if ( float ) {
481
+ enabledEvents . push ( {
482
+ event : 'mousemove' ,
483
+ listener : handleMouseMove ,
484
+ } )
406
485
}
407
486
408
487
const handleMouseEnterTooltip = ( ) => {
@@ -413,7 +492,9 @@ const Tooltip = ({
413
492
handleHideTooltip ( )
414
493
}
415
494
416
- if ( clickable && ! shouldOpenOnClick ) {
495
+ if ( clickable && ! hasClickEvent ) {
496
+ // used to keep the tooltip open when hovering content.
497
+ // not needed if using click events.
417
498
tooltipRef . current ?. addEventListener ( 'mouseenter' , handleMouseEnterTooltip )
418
499
tooltipRef . current ?. addEventListener ( 'mouseleave' , handleMouseLeaveTooltip )
419
500
}
@@ -425,23 +506,23 @@ const Tooltip = ({
425
506
} )
426
507
427
508
return ( ) => {
428
- if ( closeOnScroll ) {
509
+ if ( actualGlobalCloseEvents . scroll ) {
429
510
window . removeEventListener ( 'scroll' , handleScrollResize )
430
511
anchorScrollParent ?. removeEventListener ( 'scroll' , handleScrollResize )
431
512
tooltipScrollParent ?. removeEventListener ( 'scroll' , handleScrollResize )
432
513
}
433
- if ( closeOnResize ) {
514
+ if ( actualGlobalCloseEvents . resize ) {
434
515
window . removeEventListener ( 'resize' , handleScrollResize )
435
516
} else {
436
517
updateTooltipCleanup ?.( )
437
518
}
438
- if ( shouldOpenOnClick ) {
519
+ if ( actualGlobalCloseEvents . clickOutsideAnchor ) {
439
520
window . removeEventListener ( 'click' , handleClickOutsideAnchors )
440
521
}
441
- if ( closeOnEsc ) {
522
+ if ( actualGlobalCloseEvents . escape ) {
442
523
window . removeEventListener ( 'keydown' , handleEsc )
443
524
}
444
- if ( clickable && ! shouldOpenOnClick ) {
525
+ if ( clickable && ! hasClickEvent ) {
445
526
tooltipRef . current ?. removeEventListener ( 'mouseenter' , handleMouseEnterTooltip )
446
527
tooltipRef . current ?. removeEventListener ( 'mouseleave' , handleMouseLeaveTooltip )
447
528
}
@@ -461,8 +542,11 @@ const Tooltip = ({
461
542
rendered ,
462
543
anchorRefs ,
463
544
anchorsBySelect ,
464
- closeOnEsc ,
465
- events ,
545
+ // the effect uses the `actual*Events` objects, but this should work
546
+ openEvents ,
547
+ closeEvents ,
548
+ globalCloseEvents ,
549
+ shouldOpenOnClick ,
466
550
] )
467
551
468
552
useEffect ( ( ) => {
0 commit comments