11'use client' ;
22
3- import type { ResponderEvent } from '@react-universal/core' ;
4- import { styled , useOwnerState } from '@react-universal/core' ;
5- import { runIfFunction } from '@react-universal/utils' ;
3+ import { styled , useOwnerState , usePressEvents } from '@react-universal/core' ;
4+ import { composeEventHandlers , runIfFunction } from '@react-universal/utils' ;
65import { useComposedRefs } from '@tamagui/compose-refs' ;
76import { useRef , useState } from 'react' ;
8- import type {
9- BlurEvent as RNBlurEvent ,
10- FocusEvent as RNFocusEvent ,
11- MouseEvent as RNMouseEvent ,
12- } from 'react-native' ;
7+ import type { BlurEvent as RNBlurEvent , FocusEvent as RNFocusEvent } from 'react-native' ;
138import { View } from '../View' ;
14- import type { ButtonMethods , ButtonOwnerState , ButtonProps } from './Button.types' ;
9+ import type { ButtonOwnerState , ButtonProps } from './Button.types' ;
1510
1611const ButtonRoot = styled ( View , {
1712 name : 'Button' ,
1813 slot : 'Root' ,
1914 shouldForwardProp : ( prop ) => prop !== 'ownerState' && prop !== 'theme' && prop !== 'sx' ,
2015} ) < {
2116 disabled ?: boolean ;
22- onMouseDown ?: React . MouseEventHandler < HTMLElement > ;
2317 ownerState : ButtonOwnerState ;
2418 type ?: 'button' | 'submit' | 'reset' ;
2519} > ( {
@@ -39,19 +33,33 @@ const ButtonRoot = styled(View, {
3933 } ,
4034} ) ;
4135
42- export const Button : React . FC < ButtonProps & { ref ?: React . Ref < HTMLElement & ButtonMethods > } > = ( {
36+ export const Button : React . FC < ButtonProps & { ref ?: React . Ref < HTMLElement > } > = ( {
4337 as : _as ,
4438 children,
39+ delayLongPress,
4540 disabled = false ,
4641 href,
4742 onBlur,
43+ onClick,
44+ onResponderTerminationRequest,
45+ onContextMenu,
4846 onFocus,
4947 onFocusVisible,
48+ onResponderTerminate,
49+ onResponderMove,
50+ onStartShouldSetResponder,
51+ onMouseEnter,
52+ onMouseLeave,
5053 onHoverIn,
5154 onHoverOut,
5255 onKeyDown,
53- onKeyUp ,
56+ onLongPress ,
5457 onPress,
58+ onPressIn,
59+ onPressMove,
60+ onResponderRelease,
61+ onPressOut,
62+ onResponderGrant,
5563 ref,
5664 style,
5765 tabIndex,
@@ -68,16 +76,18 @@ export const Button: React.FC<ButtonProps & { ref?: React.Ref<HTMLElement & Butt
6876 setFocusVisible ( false ) ;
6977 }
7078
71- const handleMouseEnter = ( event : RNMouseEvent ) => {
79+ const handleMouseEnter = ( event : React . MouseEvent < HTMLElement > ) => {
7280 setHovered ( true ) ;
7381 onHoverIn ?.( event ) ;
82+ onMouseEnter ?.( event ) ;
7483 } ;
7584
76- const handleMouseLeave = ( event : RNMouseEvent ) => {
85+ const handleMouseLeave = ( event : React . MouseEvent < HTMLElement > ) => {
7786 if ( focusVisible ) {
7887 event . preventDefault ( ) ;
7988 }
8089 onHoverOut ?.( event ) ;
90+ onMouseLeave ?.( event ) ;
8191 } ;
8292
8393 const handleBlur = ( event : RNBlurEvent ) => {
@@ -102,77 +112,16 @@ export const Button: React.FC<ButtonProps & { ref?: React.Ref<HTMLElement & Butt
102112 onFocus ?.( event ) ;
103113 } ;
104114
105- const isNativeButton = ( ) =>
106- hostRef . current ?. tagName === 'BUTTON' ||
107- ( hostRef . current ?. tagName === 'A' && ( hostRef . current as HTMLAnchorElement ) . href ) ;
108-
109- const handleClick = ( event : ResponderEvent ) => {
110- if ( ! disabled ) {
111- onPress ?.( event ) ;
112- }
113- } ;
114-
115- const handleMouseDown = ( ) => {
116- if ( ! disabled ) {
117- setPressed ( true ) ;
118- document . addEventListener (
119- 'mouseup' ,
120- ( ) => {
121- setPressed ( false ) ;
122- } ,
123- { once : true } ,
124- ) ;
125- }
126- } ;
127-
128- const handleKeyDown = ( event : React . KeyboardEvent < HTMLElement > ) => {
129- onKeyDown ?.( event ) ;
130-
131- if ( event . defaultPrevented ) {
132- return ;
133- }
134-
135- if ( event . target === event . currentTarget && ! isNativeButton ( ) && event . key === ' ' ) {
136- event . preventDefault ( ) ;
137- }
138-
139- if ( event . target === event . currentTarget && event . key === ' ' && ! disabled ) {
140- setPressed ( true ) ;
141- }
142-
143- // Keyboard accessibility for non interactive elements
144- if (
145- event . target === event . currentTarget &&
146- ! isNativeButton ( ) &&
147- event . key === 'Enter' &&
148- ! disabled
149- ) {
150- event . preventDefault ( ) ;
151- onPress ?.( event as any ) ;
152- }
153- } ;
154-
155- const handleKeyUp = ( event : React . KeyboardEvent < HTMLElement > ) => {
156- // calling preventDefault in keyUp on a <button> will not dispatch a click event if Space is pressed
157- // https://codesandbox.io/p/sandbox/button-keyup-preventdefault-dn7f0
158-
159- if ( event . target === event . currentTarget ) {
160- setPressed ( false ) ;
161- }
162-
163- onKeyUp ?.( event ) ;
164-
165- // Keyboard accessibility for non interactive elements
166- if (
167- event . target === event . currentTarget &&
168- ! isNativeButton ( ) &&
169- ! disabled &&
170- event . key === ' ' &&
171- ! event . defaultPrevented
172- ) {
173- onPress ?.( event as any ) ;
174- }
175- } ;
115+ const pressEventHandlers = usePressEvents ( hostRef , {
116+ delayLongPress,
117+ disabled,
118+ onLongPress,
119+ onPress,
120+ onPressChange : setPressed ,
121+ onPressIn,
122+ onPressMove,
123+ onPressOut,
124+ } ) ;
176125
177126 const handleRef = useComposedRefs < any > ( hostRef , ref ) ;
178127
@@ -193,13 +142,30 @@ export const Button: React.FC<ButtonProps & { ref?: React.Ref<HTMLElement & Butt
193142 style = { runIfFunction ( style , { focusVisible, hovered, pressed } ) }
194143 type = { href == null ? ( type ?? 'button' ) : undefined }
195144 onBlur = { handleBlur }
196- onClick = { handleClick }
145+ onClick = { composeEventHandlers ( pressEventHandlers . onClick , onClick ) }
146+ onContextMenu = { composeEventHandlers ( pressEventHandlers . onContextMenu , onContextMenu ) }
197147 onFocus = { handleFocus }
198- onKeyDown = { handleKeyDown }
199- onKeyUp = { handleKeyUp }
200- onMouseDown = { handleMouseDown }
148+ onKeyDown = { composeEventHandlers ( pressEventHandlers . onKeyDown , onKeyDown ) }
201149 onMouseEnter = { handleMouseEnter }
202150 onMouseLeave = { handleMouseLeave }
151+ onResponderGrant = { composeEventHandlers ( pressEventHandlers . onResponderGrant , onResponderGrant ) }
152+ onResponderMove = { composeEventHandlers ( pressEventHandlers . onResponderMove , onResponderMove ) }
153+ onResponderRelease = { composeEventHandlers (
154+ pressEventHandlers . onResponderRelease ,
155+ onResponderRelease ,
156+ ) }
157+ onResponderTerminate = { composeEventHandlers (
158+ pressEventHandlers . onResponderTerminate ,
159+ onResponderTerminate ,
160+ ) }
161+ onResponderTerminationRequest = { composeEventHandlers (
162+ pressEventHandlers . onResponderTerminationRequest ,
163+ onResponderTerminationRequest ,
164+ ) }
165+ onStartShouldSetResponder = { composeEventHandlers (
166+ pressEventHandlers . onStartShouldSetResponder ,
167+ onStartShouldSetResponder ,
168+ ) }
203169 { ...props }
204170 >
205171 { runIfFunction ( children , { focusVisible, hovered, pressed } ) }
0 commit comments