@@ -14,6 +14,10 @@ import { useTimeout } from 'flavours/glitch/hooks/useTimeout';
1414const offset = [ - 12 , 4 ] as OffsetValue ;
1515const enterDelay = 750 ;
1616const leaveDelay = 150 ;
17+ // Only open the card if the mouse was moved within this time,
18+ // to avoid triggering the card without intentional mouse movement
19+ // (e.g. when content changed underneath the mouse cursor)
20+ const activeMovementThreshold = 150 ;
1721const popperConfig = { strategy : 'fixed' } as UsePopperOptions ;
1822
1923const isHoverCardAnchor = ( element : HTMLElement ) =>
@@ -23,10 +27,10 @@ export const HoverCardController: React.FC = () => {
2327 const [ open , setOpen ] = useState ( false ) ;
2428 const [ accountId , setAccountId ] = useState < string | undefined > ( ) ;
2529 const [ anchor , setAnchor ] = useState < HTMLElement | null > ( null ) ;
26- const isUsingTouchRef = useRef ( false ) ;
2730 const cardRef = useRef < HTMLDivElement | null > ( null ) ;
2831 const [ setLeaveTimeout , cancelLeaveTimeout ] = useTimeout ( ) ;
2932 const [ setEnterTimeout , cancelEnterTimeout , delayEnterTimeout ] = useTimeout ( ) ;
33+ const [ setMoveTimeout , cancelMoveTimeout ] = useTimeout ( ) ;
3034 const [ setScrollTimeout ] = useTimeout ( ) ;
3135 const location = useLocation ( ) ;
3236
@@ -43,6 +47,8 @@ export const HoverCardController: React.FC = () => {
4347
4448 useEffect ( ( ) => {
4549 let isScrolling = false ;
50+ let isUsingTouch = false ;
51+ let isActiveMouseMovement = false ;
4652 let currentAnchor : HTMLElement | null = null ;
4753 let currentTitle : string | null = null ;
4854
@@ -64,7 +70,7 @@ export const HoverCardController: React.FC = () => {
6470 const handleTouchStart = ( ) => {
6571 // Keeping track of touch events to prevent the
6672 // hover card from being displayed on touch devices
67- isUsingTouchRef . current = true ;
73+ isUsingTouch = true ;
6874 } ;
6975
7076 const handleMouseEnter = ( e : MouseEvent ) => {
@@ -76,13 +82,14 @@ export const HoverCardController: React.FC = () => {
7682 return ;
7783 }
7884
79- // Bail out if a touch is active
80- if ( isUsingTouchRef . current ) {
85+ // Bail out if we're scrolling, a touch is active,
86+ // or if there was no active mouse movement
87+ if ( isScrolling || ! isActiveMouseMovement || isUsingTouch ) {
8188 return ;
8289 }
8390
8491 // We've entered an anchor
85- if ( ! isScrolling && isHoverCardAnchor ( target ) ) {
92+ if ( isHoverCardAnchor ( target ) ) {
8693 cancelLeaveTimeout ( ) ;
8794
8895 currentAnchor ?. removeAttribute ( 'aria-describedby' ) ;
@@ -97,10 +104,7 @@ export const HoverCardController: React.FC = () => {
97104 }
98105
99106 // We've entered the hover card
100- if (
101- ! isScrolling &&
102- ( target === currentAnchor || target === cardRef . current )
103- ) {
107+ if ( target === currentAnchor || target === cardRef . current ) {
104108 cancelLeaveTimeout ( ) ;
105109 }
106110 } ;
@@ -139,10 +143,17 @@ export const HoverCardController: React.FC = () => {
139143 } ;
140144
141145 const handleMouseMove = ( ) => {
142- if ( isUsingTouchRef . current ) {
143- isUsingTouchRef . current = false ;
146+ if ( isUsingTouch ) {
147+ isUsingTouch = false ;
144148 }
149+
145150 delayEnterTimeout ( enterDelay ) ;
151+
152+ cancelMoveTimeout ( ) ;
153+ isActiveMouseMovement = true ;
154+ setMoveTimeout ( ( ) => {
155+ isActiveMouseMovement = false ;
156+ } , activeMovementThreshold ) ;
146157 } ;
147158
148159 document . body . addEventListener ( 'touchstart' , handleTouchStart , {
@@ -186,6 +197,8 @@ export const HoverCardController: React.FC = () => {
186197 setOpen ,
187198 setAccountId ,
188199 setAnchor ,
200+ setMoveTimeout ,
201+ cancelMoveTimeout ,
189202 ] ) ;
190203
191204 return (
0 commit comments