@@ -31,12 +31,12 @@ import {
31
31
useGlobalListeners ,
32
32
useSyncRef
33
33
} from '@react-aria/utils' ;
34
+ import { createSyntheticEvent , preventFocus , setEventTarget } from './utils' ;
34
35
import { disableTextSelection , restoreTextSelection } from './textSelection' ;
35
36
import { DOMAttributes , FocusableElement , PressEvent as IPressEvent , PointerType , PressEvents , RefObject } from '@react-types/shared' ;
36
37
import { flushSync } from 'react-dom' ;
37
38
import { PressResponderContext } from './context' ;
38
- import { preventFocus } from './utils' ;
39
- import { TouchEvent as RTouchEvent , useContext , useEffect , useMemo , useRef , useState } from 'react' ;
39
+ import { MouseEvent as RMouseEvent , TouchEvent as RTouchEvent , useContext , useEffect , useMemo , useRef , useState } from 'react' ;
40
40
41
41
export interface PressProps extends PressEvents {
42
42
/** Whether the target is in a controlled press state (e.g. an overlay it triggers is open). */
@@ -170,6 +170,7 @@ export function usePress(props: PressHookProps): PressResult {
170
170
onPressStart,
171
171
onPressEnd,
172
172
onPressUp,
173
+ onClick,
173
174
isDisabled,
174
175
isPressed : isPressedProp ,
175
176
preventFocusOnPress,
@@ -295,6 +296,23 @@ export function usePress(props: PressHookProps): PressResult {
295
296
}
296
297
} ) ;
297
298
299
+ let triggerClick = useEffectEvent ( ( e : RMouseEvent < FocusableElement > ) => {
300
+ onClick ?.( e ) ;
301
+ } ) ;
302
+
303
+ let triggerSyntheticClick = useEffectEvent ( ( e : KeyboardEvent | TouchEvent , target : FocusableElement ) => {
304
+ // Some third-party libraries pass in onClick instead of onPress.
305
+ // Create a fake mouse event and trigger onClick as well.
306
+ // This matches the browser's native activation behavior for certain elements (e.g. button).
307
+ // https://html.spec.whatwg.org/#activation
308
+ // https://html.spec.whatwg.org/#fire-a-synthetic-pointer-event
309
+ if ( onClick ) {
310
+ let event = new MouseEvent ( 'click' , e ) ;
311
+ setEventTarget ( event , target ) ;
312
+ onClick ( createSyntheticEvent ( event ) ) ;
313
+ }
314
+ } ) ;
315
+
298
316
let pressProps = useMemo ( ( ) => {
299
317
let state = ref . current ;
300
318
let pressProps : DOMAttributes = {
@@ -362,11 +380,13 @@ export function usePress(props: PressHookProps): PressResult {
362
380
let stopPressStart = triggerPressStart ( e , 'virtual' ) ;
363
381
let stopPressUp = triggerPressUp ( e , 'virtual' ) ;
364
382
let stopPressEnd = triggerPressEnd ( e , 'virtual' ) ;
383
+ triggerClick ( e ) ;
365
384
shouldStopPropagation = stopPressStart && stopPressUp && stopPressEnd ;
366
385
} else if ( state . isPressed && state . pointerType !== 'keyboard' ) {
367
386
let pointerType = state . pointerType || ( e . nativeEvent as PointerEvent ) . pointerType as PointerType || 'virtual' ;
368
387
shouldStopPropagation = triggerPressEnd ( createEvent ( e . currentTarget , e ) , pointerType , true ) ;
369
388
state . isOverTarget = false ;
389
+ triggerClick ( e ) ;
370
390
cancel ( e ) ;
371
391
}
372
392
@@ -385,7 +405,11 @@ export function usePress(props: PressHookProps): PressResult {
385
405
}
386
406
387
407
let target = getEventTarget ( e ) ;
388
- triggerPressEnd ( createEvent ( state . target , e ) , 'keyboard' , nodeContains ( state . target , getEventTarget ( e ) ) ) ;
408
+ let wasPressed = nodeContains ( state . target , getEventTarget ( e ) ) ;
409
+ triggerPressEnd ( createEvent ( state . target , e ) , 'keyboard' , wasPressed ) ;
410
+ if ( wasPressed ) {
411
+ triggerSyntheticClick ( e , state . target ) ;
412
+ }
389
413
removeAllGlobalListeners ( ) ;
390
414
391
415
// If a link was triggered with a key other than Enter, open the URL ourselves.
@@ -723,6 +747,7 @@ export function usePress(props: PressHookProps): PressResult {
723
747
if ( touch && isOverTarget ( touch , e . currentTarget ) && state . pointerType != null ) {
724
748
triggerPressUp ( createTouchEvent ( state . target ! , e ) , state . pointerType ) ;
725
749
shouldStopPropagation = triggerPressEnd ( createTouchEvent ( state . target ! , e ) , state . pointerType ) ;
750
+ triggerSyntheticClick ( e . nativeEvent , state . target ! ) ;
726
751
} else if ( state . isOverTarget && state . pointerType != null ) {
727
752
shouldStopPropagation = triggerPressEnd ( createTouchEvent ( state . target ! , e ) , state . pointerType , false ) ;
728
753
}
@@ -784,7 +809,9 @@ export function usePress(props: PressHookProps): PressResult {
784
809
cancelOnPointerExit ,
785
810
triggerPressEnd ,
786
811
triggerPressStart ,
787
- triggerPressUp
812
+ triggerPressUp ,
813
+ triggerClick ,
814
+ triggerSyntheticClick
788
815
] ) ;
789
816
790
817
// Remove user-select: none in case component unmounts immediately after pressStart
0 commit comments