@@ -31,12 +31,12 @@ import {
3131 useGlobalListeners ,
3232 useSyncRef
3333} from '@react-aria/utils' ;
34+ import { createSyntheticEvent , preventFocus , setEventTarget } from './utils' ;
3435import { disableTextSelection , restoreTextSelection } from './textSelection' ;
3536import { DOMAttributes , FocusableElement , PressEvent as IPressEvent , PointerType , PressEvents , RefObject } from '@react-types/shared' ;
3637import { flushSync } from 'react-dom' ;
3738import { 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' ;
4040
4141export interface PressProps extends PressEvents {
4242 /** 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 {
170170 onPressStart,
171171 onPressEnd,
172172 onPressUp,
173+ onClick,
173174 isDisabled,
174175 isPressed : isPressedProp ,
175176 preventFocusOnPress,
@@ -295,6 +296,23 @@ export function usePress(props: PressHookProps): PressResult {
295296 }
296297 } ) ;
297298
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+
298316 let pressProps = useMemo ( ( ) => {
299317 let state = ref . current ;
300318 let pressProps : DOMAttributes = {
@@ -362,11 +380,13 @@ export function usePress(props: PressHookProps): PressResult {
362380 let stopPressStart = triggerPressStart ( e , 'virtual' ) ;
363381 let stopPressUp = triggerPressUp ( e , 'virtual' ) ;
364382 let stopPressEnd = triggerPressEnd ( e , 'virtual' ) ;
383+ triggerClick ( e ) ;
365384 shouldStopPropagation = stopPressStart && stopPressUp && stopPressEnd ;
366385 } else if ( state . isPressed && state . pointerType !== 'keyboard' ) {
367386 let pointerType = state . pointerType || ( e . nativeEvent as PointerEvent ) . pointerType as PointerType || 'virtual' ;
368387 shouldStopPropagation = triggerPressEnd ( createEvent ( e . currentTarget , e ) , pointerType , true ) ;
369388 state . isOverTarget = false ;
389+ triggerClick ( e ) ;
370390 cancel ( e ) ;
371391 }
372392
@@ -385,7 +405,11 @@ export function usePress(props: PressHookProps): PressResult {
385405 }
386406
387407 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+ }
389413 removeAllGlobalListeners ( ) ;
390414
391415 // 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 {
723747 if ( touch && isOverTarget ( touch , e . currentTarget ) && state . pointerType != null ) {
724748 triggerPressUp ( createTouchEvent ( state . target ! , e ) , state . pointerType ) ;
725749 shouldStopPropagation = triggerPressEnd ( createTouchEvent ( state . target ! , e ) , state . pointerType ) ;
750+ triggerSyntheticClick ( e . nativeEvent , state . target ! ) ;
726751 } else if ( state . isOverTarget && state . pointerType != null ) {
727752 shouldStopPropagation = triggerPressEnd ( createTouchEvent ( state . target ! , e ) , state . pointerType , false ) ;
728753 }
@@ -784,7 +809,9 @@ export function usePress(props: PressHookProps): PressResult {
784809 cancelOnPointerExit ,
785810 triggerPressEnd ,
786811 triggerPressStart ,
787- triggerPressUp
812+ triggerPressUp ,
813+ triggerClick ,
814+ triggerSyntheticClick
788815 ] ) ;
789816
790817 // Remove user-select: none in case component unmounts immediately after pressStart
0 commit comments