@@ -2,6 +2,7 @@ import React from "react";
22import {
33 Classes as BlueprintClasses ,
44 Popover as BlueprintPopover ,
5+ PopoverInteractionKind as InteractionKind ,
56 PopoverProps as BlueprintPopoverProps ,
67 Utils as BlueprintUtils ,
78} from "@blueprintjs/core" ;
@@ -38,8 +39,11 @@ export const ContextOverlay = ({
3839 ...otherPopoverProps
3940} : ContextOverlayProps ) => {
4041 const placeholderRef = React . useRef ( null ) ;
41- const eventMemory = React . useRef < undefined | "afterhover " | "afterfocus " > ( undefined ) ;
42+ const eventMemory = React . useRef < undefined | "mouseenter " | "focusin" | "click "> ( undefined ) ;
4243 const swapDelay = React . useRef < null | NodeJS . Timeout > ( null ) ;
44+ const interactionKind = React . useRef < InteractionKind > (
45+ otherPopoverProps [ "interactionKind" ] ?? InteractionKind . CLICK
46+ ) ;
4347 const swapDelayTime = 15 ;
4448 const [ placeholder , setPlaceholder ] = React . useState < boolean > (
4549 // use placeholder only for "simple" overlays without special states
@@ -50,36 +54,79 @@ export const ContextOverlay = ({
5054 usePlaceholder
5155 ) ;
5256
57+ const swap = ( ev : MouseEvent | globalThis . FocusEvent ) => {
58+ const waitForClick =
59+ interactionKind . current === InteractionKind . CLICK ||
60+ interactionKind . current === InteractionKind . CLICK_TARGET_ONLY
61+ ? true
62+ : false ;
63+
64+ if ( swapDelay . current ) {
65+ clearTimeout ( swapDelay . current ) ;
66+ }
67+ swapDelay . current = setTimeout (
68+ ( ) => {
69+ eventMemory . current = ev . type as "mouseenter" | "focusin" | "click" ;
70+ setPlaceholder ( false ) ;
71+ } ,
72+ // we delay the swap for hover/focus to prevent unwanted effects
73+ // (e.g. event hickup after replacing elements when it is not really necessary)
74+ waitForClick ? 0 : swapDelayTime
75+ ) ;
76+ } ;
77+
5378 React . useEffect ( ( ) => {
79+ const waitForClick =
80+ interactionKind . current === InteractionKind . CLICK ||
81+ interactionKind . current === InteractionKind . CLICK_TARGET_ONLY
82+ ? true
83+ : false ;
84+ const removeEvents = ( ) => {
85+ if ( placeholderRef . current ) {
86+ ( placeholderRef . current as HTMLElement ) . removeEventListener ( "click" , swap ) ;
87+ ( placeholderRef . current as HTMLElement ) . removeEventListener ( "mouseenter" , swap ) ;
88+ ( placeholderRef . current as HTMLElement ) . removeEventListener ( "focusin" , swap ) ;
89+ }
90+ return ;
91+ } ;
5492 if ( placeholderRef . current ) {
55- const swap = ( ev : MouseEvent | globalThis . FocusEvent ) => {
56- if ( swapDelay . current ) {
57- clearTimeout ( swapDelay . current ) ;
58- }
59- swapDelay . current = setTimeout ( ( ) => {
60- // we delay the swap to prevent unwanted effects
61- // (e.g. event hickup after replacing elements when it is not really necessary)
62- eventMemory . current = ev . type === "focusin" ? "afterfocus" : "afterhover" ;
63- setPlaceholder ( false ) ;
64- } , swapDelayTime ) ;
65- } ;
66- ( placeholderRef . current as HTMLElement ) . addEventListener ( "mouseenter" , swap ) ;
67- ( placeholderRef . current as HTMLElement ) . addEventListener ( "focusin" , swap ) ;
93+ removeEvents ( ) ; // remove events in case of interaction kind changed during existence
94+ if ( waitForClick ) {
95+ ( placeholderRef . current as HTMLElement ) . addEventListener ( "click" , swap ) ;
96+ } else {
97+ ( placeholderRef . current as HTMLElement ) . addEventListener ( "mouseenter" , swap ) ;
98+ ( placeholderRef . current as HTMLElement ) . addEventListener ( "focusin" , swap ) ;
99+ }
68100 return ( ) => {
69- if ( placeholderRef . current ) {
70- ( placeholderRef . current as HTMLElement ) . removeEventListener ( "mouseenter" , swap ) ;
71- ( placeholderRef . current as HTMLElement ) . removeEventListener ( "focusin" , swap ) ;
72- }
101+ removeEvents ( ) ;
73102 } ;
74103 }
75104 return ( ) => { } ;
76105 } , [ ! ! placeholderRef . current ] ) ;
77106
78107 const refocus = React . useCallback ( ( node ) => {
79- if ( eventMemory . current === "afterfocus" && node ) {
108+ if ( eventMemory . current && node ) {
80109 const target = node . targetRef . current . children [ 0 ] ;
81110 if ( target ) {
82- target . focus ( ) ;
111+ switch ( eventMemory . current ) {
112+ case "focusin" :
113+ target . focus ( ) ;
114+ break ;
115+ case "click" :
116+ target . click ( ) ;
117+ break ;
118+ case "mouseenter" :
119+ // re-check if the cursor is still over the element after swapping the placeholder before triggering the event to bubble up
120+ ( target as HTMLElement ) . addEventListener (
121+ "mouseover" ,
122+ ( ) => ( target as HTMLElement ) . dispatchEvent ( new MouseEvent ( "mouseover" , { bubbles : true } ) ) ,
123+ {
124+ capture : true ,
125+ once : true ,
126+ }
127+ ) ;
128+ break ;
129+ }
83130 }
84131 }
85132 } , [ ] ) ;
0 commit comments