@@ -8,7 +8,14 @@ import { getScrollParent } from 'utils/get-scroll-parent'
88import { computeTooltipPosition } from 'utils/compute-positions'
99import coreStyles from './core-styles.module.css'
1010import 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'
1219
1320const Tooltip = ( {
1421 // props
@@ -34,6 +41,9 @@ const Tooltip = ({
3441 closeOnEsc = false ,
3542 closeOnScroll = false ,
3643 closeOnResize = false ,
44+ openEvents,
45+ closeEvents,
46+ globalCloseEvents,
3747 style : externalStyles ,
3848 position,
3949 afterShow,
@@ -68,7 +78,49 @@ const Tooltip = ({
6878 const [ anchorsBySelect , setAnchorsBySelect ] = useState < HTMLElement [ ] > ( [ ] )
6979 const mounted = useRef ( false )
7080
81+ /**
82+ * @todo Update when deprecated stuff gets removed.
83+ */
7184 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+ }
72124
73125 /**
74126 * useLayoutEffect runs before useEffect,
@@ -248,13 +300,6 @@ const Tooltip = ({
248300 lastFloatPosition . current = mousePosition
249301 }
250302
251- const handleClickTooltipAnchor = ( event ?: Event ) => {
252- handleShowTooltip ( event )
253- if ( delayHide ) {
254- handleHideTooltipDelayed ( )
255- }
256- }
257-
258303 const handleClickOutsideAnchors = ( event : MouseEvent ) => {
259304 const anchorById = document . querySelector < HTMLElement > ( `[id='${ anchorId } ']` )
260305 const anchors = [ anchorById , ...anchorsBySelect ]
@@ -353,13 +398,13 @@ const Tooltip = ({
353398 const anchorScrollParent = getScrollParent ( activeAnchor )
354399 const tooltipScrollParent = getScrollParent ( tooltipRef . current )
355400
356- if ( closeOnScroll ) {
401+ if ( actualGlobalCloseEvents . scroll ) {
357402 window . addEventListener ( 'scroll' , handleScrollResize )
358403 anchorScrollParent ?. addEventListener ( 'scroll' , handleScrollResize )
359404 tooltipScrollParent ?. addEventListener ( 'scroll' , handleScrollResize )
360405 }
361406 let updateTooltipCleanup : null | ( ( ) => void ) = null
362- if ( closeOnResize ) {
407+ if ( actualGlobalCloseEvents . resize ) {
363408 window . addEventListener ( 'resize' , handleScrollResize )
364409 } else if ( activeAnchor && tooltipRef . current ) {
365410 updateTooltipCleanup = autoUpdate (
@@ -380,29 +425,63 @@ const Tooltip = ({
380425 }
381426 handleShow ( false )
382427 }
383-
384- if ( closeOnEsc ) {
428+ if ( actualGlobalCloseEvents . escape ) {
385429 window . addEventListener ( 'keydown' , handleEsc )
386430 }
387431
432+ if ( actualGlobalCloseEvents . clickOutsideAnchor ) {
433+ window . addEventListener ( 'click' , handleClickOutsideAnchors )
434+ }
435+
388436 const enabledEvents : { event : string ; listener : ( event ?: Event ) => void } [ ] = [ ]
389437
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
405441 }
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+ } )
406485 }
407486
408487 const handleMouseEnterTooltip = ( ) => {
@@ -413,7 +492,9 @@ const Tooltip = ({
413492 handleHideTooltip ( )
414493 }
415494
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.
417498 tooltipRef . current ?. addEventListener ( 'mouseenter' , handleMouseEnterTooltip )
418499 tooltipRef . current ?. addEventListener ( 'mouseleave' , handleMouseLeaveTooltip )
419500 }
@@ -425,23 +506,23 @@ const Tooltip = ({
425506 } )
426507
427508 return ( ) => {
428- if ( closeOnScroll ) {
509+ if ( actualGlobalCloseEvents . scroll ) {
429510 window . removeEventListener ( 'scroll' , handleScrollResize )
430511 anchorScrollParent ?. removeEventListener ( 'scroll' , handleScrollResize )
431512 tooltipScrollParent ?. removeEventListener ( 'scroll' , handleScrollResize )
432513 }
433- if ( closeOnResize ) {
514+ if ( actualGlobalCloseEvents . resize ) {
434515 window . removeEventListener ( 'resize' , handleScrollResize )
435516 } else {
436517 updateTooltipCleanup ?.( )
437518 }
438- if ( shouldOpenOnClick ) {
519+ if ( actualGlobalCloseEvents . clickOutsideAnchor ) {
439520 window . removeEventListener ( 'click' , handleClickOutsideAnchors )
440521 }
441- if ( closeOnEsc ) {
522+ if ( actualGlobalCloseEvents . escape ) {
442523 window . removeEventListener ( 'keydown' , handleEsc )
443524 }
444- if ( clickable && ! shouldOpenOnClick ) {
525+ if ( clickable && ! hasClickEvent ) {
445526 tooltipRef . current ?. removeEventListener ( 'mouseenter' , handleMouseEnterTooltip )
446527 tooltipRef . current ?. removeEventListener ( 'mouseleave' , handleMouseLeaveTooltip )
447528 }
@@ -461,8 +542,11 @@ const Tooltip = ({
461542 rendered ,
462543 anchorRefs ,
463544 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 ,
466550 ] )
467551
468552 useEffect ( ( ) => {
0 commit comments