@@ -9,6 +9,7 @@ import useId from 'rc-util/lib/hooks/useId';
9
9
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect' ;
10
10
import isMobile from 'rc-util/lib/isMobile' ;
11
11
import * as React from 'react' ;
12
+ import { flushSync } from 'react-dom' ;
12
13
import type { TriggerContextProps } from './context' ;
13
14
import TriggerContext from './context' ;
14
15
import useAction from './hooks/useAction' ;
@@ -307,10 +308,14 @@ export function generateTrigger(
307
308
openRef . current = mergedOpen ;
308
309
309
310
const internalTriggerOpen = useEvent ( ( nextOpen : boolean ) => {
310
- if ( mergedOpen !== nextOpen ) {
311
- setMergedOpen ( nextOpen ) ;
312
- onPopupVisibleChange ?.( nextOpen ) ;
313
- }
311
+ // Enter or Pointer will both trigger open state change
312
+ // We only need take one to avoid duplicated change event trigger
313
+ flushSync ( ( ) => {
314
+ if ( mergedOpen !== nextOpen ) {
315
+ setMergedOpen ( nextOpen ) ;
316
+ onPopupVisibleChange ?.( nextOpen ) ;
317
+ }
318
+ } ) ;
314
319
} ) ;
315
320
316
321
// Trigger for delay
@@ -354,7 +359,9 @@ export function generateTrigger(
354
359
0 , 0 ,
355
360
] ) ;
356
361
357
- const setMousePosByEvent = ( event : React . MouseEvent ) => {
362
+ const setMousePosByEvent = (
363
+ event : Pick < React . MouseEvent , 'clientX' | 'clientY' > ,
364
+ ) => {
358
365
setMousePos ( [ event . clientX , event . clientY ] ) ;
359
366
} ;
360
367
@@ -463,21 +470,23 @@ export function generateTrigger(
463
470
hideAction ,
464
471
) ;
465
472
466
- // Util wrapper for trigger action
467
- const wrapperAction = (
473
+ /**
474
+ * Util wrapper for trigger action
475
+ */
476
+ function wrapperAction < Event extends React . SyntheticEvent > (
468
477
eventName : string ,
469
478
nextOpen : boolean ,
470
479
delay ?: number ,
471
- preEvent ?: ( event : any ) => void ,
472
- ) => {
480
+ preEvent ?: ( event : Event ) => void ,
481
+ ) {
473
482
cloneProps [ eventName ] = ( event : any , ...args : any [ ] ) => {
474
483
preEvent ?.( event ) ;
475
484
triggerOpen ( nextOpen , delay ) ;
476
485
477
486
// Pass to origin
478
487
originChildProps [ eventName ] ?.( event , ...args ) ;
479
488
} ;
480
- } ;
489
+ }
481
490
482
491
// ======================= Action: Click ========================
483
492
const clickToShow = showActions . has ( 'click' ) ;
@@ -521,9 +530,23 @@ export function generateTrigger(
521
530
let onPopupMouseLeave : VoidFunction ;
522
531
523
532
if ( hoverToShow ) {
524
- wrapperAction ( 'onMouseEnter' , true , mouseEnterDelay , ( event ) => {
525
- setMousePosByEvent ( event ) ;
526
- } ) ;
533
+ // Compatible with old browser which not support pointer event
534
+ wrapperAction < React . MouseEvent > (
535
+ 'onMouseEnter' ,
536
+ true ,
537
+ mouseEnterDelay ,
538
+ ( event ) => {
539
+ setMousePosByEvent ( event ) ;
540
+ } ,
541
+ ) ;
542
+ wrapperAction < React . PointerEvent > (
543
+ 'onPointerEnter' ,
544
+ true ,
545
+ mouseEnterDelay ,
546
+ ( event ) => {
547
+ setMousePosByEvent ( event ) ;
548
+ } ,
549
+ ) ;
527
550
onPopupMouseEnter = ( ) => {
528
551
// Only trigger re-open when popup is visible
529
552
if ( mergedOpen || inMotion ) {
@@ -542,6 +565,7 @@ export function generateTrigger(
542
565
543
566
if ( hoverToHide ) {
544
567
wrapperAction ( 'onMouseLeave' , false , mouseLeaveDelay ) ;
568
+ wrapperAction ( 'onPointerLeave' , false , mouseLeaveDelay ) ;
545
569
onPopupMouseLeave = ( ) => {
546
570
triggerOpen ( false , mouseLeaveDelay ) ;
547
571
} ;
0 commit comments