11import { Platform } from 'react-native' ;
22
33import { type ActionType } from '../../ActionType' ;
4+ import type { ButtonEvent } from '../../specs/RNGestureHandlerButtonNativeComponent' ;
45import { State } from '../../State' ;
56import type { NativeHandlerData } from '../../v3/hooks/gestures/native/NativeTypes' ;
67import type { HandlerData } from '../../v3/types' ;
@@ -17,6 +18,7 @@ import type {
1718} from '../interfaces' ;
1819import { NativeGestureRole } from '../interfaces' ;
1920import type { GestureHandlerDelegate } from '../tools/GestureHandlerDelegate' ;
21+ import { ButtonEventName , dispatchButtonEvent } from '../tools/ButtonEvents' ;
2022import {
2123 dispatchGestureLifecycleEvent ,
2224 GestureLifecycleEvent ,
@@ -39,6 +41,12 @@ export default class NativeViewGestureHandler extends GestureHandler {
3941
4042 private lastActiveHandlerData : HandlerData < NativeHandlerData > | null = null ;
4143
44+ private hasLongPressHandler = false ;
45+ private longPressDuration = - 1 ;
46+ private longPressDetected = false ;
47+ private lastEventWasInside = false ;
48+ private pendingLongPress : ReturnType < typeof setTimeout > | null = null ;
49+
4250 public constructor (
4351 delegate : GestureHandlerDelegate < unknown , IGestureHandler >
4452 ) {
@@ -90,6 +98,12 @@ export default class NativeViewGestureHandler extends GestureHandler {
9098 if ( config . yieldsToContinuousGestures !== undefined ) {
9199 this . yieldsToContinuousGestures = config . yieldsToContinuousGestures ;
92100 }
101+ if ( config . hasLongPressHandler !== undefined ) {
102+ this . hasLongPressHandler = config . hasLongPressHandler ;
103+ }
104+ if ( config . longPressDuration !== undefined ) {
105+ this . longPressDuration = config . longPressDuration ;
106+ }
93107
94108 const view = this . delegate . view as HTMLElement ;
95109 this . restoreViewStyles ( view ) ;
@@ -165,9 +179,33 @@ export default class NativeViewGestureHandler extends GestureHandler {
165179 }
166180 }
167181
168- protected override onPointerLeave ( ) : void {
182+ protected override onPointerLeave ( event : AdaptedEvent ) : void {
183+ this . tracker . track ( event ) ;
184+
185+ if (
186+ this . role === NativeGestureRole . Button &&
187+ ! this . shouldCancelWhenOutside &&
188+ this . lastEventWasInside
189+ ) {
190+ this . dispatchButtonEvent ( ButtonEventName . PressOut ) ;
191+ this . clearLongPressTimer ( ) ;
192+ }
193+
169194 if ( this . state === State . BEGAN || this . state === State . ACTIVE ) {
170- this . cancel ( ) ;
195+ super . onPointerLeave ( event ) ;
196+ }
197+ }
198+
199+ protected override onPointerEnter ( event : AdaptedEvent ) : void {
200+ this . tracker . track ( event ) ;
201+ super . onPointerEnter ( event ) ;
202+
203+ if (
204+ this . role === NativeGestureRole . Button &&
205+ ( this . state === State . BEGAN || this . state === State . ACTIVE ) &&
206+ ! this . lastEventWasInside
207+ ) {
208+ this . dispatchButtonEvent ( ButtonEventName . PressIn ) ;
171209 }
172210 }
173211
@@ -234,6 +272,7 @@ export default class NativeViewGestureHandler extends GestureHandler {
234272 }
235273
236274 public override detach ( ) : void {
275+ this . clearLongPressTimer ( ) ;
237276 super . detach ( ) ;
238277 this . role = null ;
239278 }
@@ -282,6 +321,88 @@ export default class NativeViewGestureHandler extends GestureHandler {
282321 ) ;
283322 }
284323
324+ protected override onStateChange ( newState : State ) : void {
325+ if ( this . role !== NativeGestureRole . Button ) {
326+ return ;
327+ }
328+
329+ if ( newState === State . BEGAN ) {
330+ if ( ! this . getButtonEventData ( ) . pointerInside ) {
331+ return ;
332+ }
333+
334+ this . dispatchButtonEvent ( ButtonEventName . PressIn ) ;
335+ this . longPressDetected = false ;
336+
337+ if ( this . hasLongPressHandler && this . longPressDuration >= 0 ) {
338+ this . pendingLongPress = setTimeout ( ( ) => {
339+ this . pendingLongPress = null ;
340+ this . longPressDetected = true ;
341+ this . dispatchButtonEvent ( ButtonEventName . LongPress ) ;
342+ } , this . longPressDuration ) ;
343+ }
344+ return ;
345+ }
346+
347+ if (
348+ newState !== State . END &&
349+ newState !== State . FAILED &&
350+ newState !== State . CANCELLED
351+ ) {
352+ return ;
353+ }
354+
355+ const endedInside = this . lastEventWasInside ;
356+
357+ if ( endedInside ) {
358+ this . dispatchButtonEvent ( ButtonEventName . PressOut ) ;
359+ }
360+ this . clearLongPressTimer ( ) ;
361+
362+ if ( newState === State . END && ! this . longPressDetected && endedInside ) {
363+ this . dispatchButtonEvent ( ButtonEventName . Press ) ;
364+ }
365+
366+ this . dispatchButtonEvent ( ButtonEventName . InteractionFinished ) ;
367+ this . longPressDetected = false ;
368+ }
369+
370+ private dispatchButtonEvent ( name : ButtonEventName ) : void {
371+ if ( name === ButtonEventName . PressIn ) {
372+ this . lastEventWasInside = true ;
373+ } else if ( name === ButtonEventName . PressOut ) {
374+ this . lastEventWasInside = false ;
375+ }
376+
377+ dispatchButtonEvent (
378+ this . delegate . view as HTMLElement | null ,
379+ name ,
380+ this . getButtonEventData ( )
381+ ) ;
382+ }
383+
384+ private getButtonEventData ( ) : ButtonEvent {
385+ const absolute = this . tracker . getAbsoluteCoordsAverage ( ) ;
386+ const relative = this . tracker . getRelativeCoordsAverage ( ) ;
387+
388+ return {
389+ pointerInside : this . delegate . isPointerInBounds ( absolute ) ,
390+ x : relative . x ,
391+ y : relative . y ,
392+ absoluteX : absolute . x ,
393+ absoluteY : absolute . y ,
394+ numberOfPointers : this . tracker . trackedPointersCount ,
395+ pointerType : this . pointerType ,
396+ } ;
397+ }
398+
399+ private clearLongPressTimer ( ) : void {
400+ if ( this . pendingLongPress !== null ) {
401+ clearTimeout ( this . pendingLongPress ) ;
402+ this . pendingLongPress = null ;
403+ }
404+ }
405+
285406 protected override transformNativeEvent ( ) : Record < string , unknown > {
286407 const absolute = this . tracker . getAbsoluteCoordsAverage ( ) ;
287408 const relative = this . tracker . getRelativeCoordsAverage ( ) ;
@@ -317,7 +438,15 @@ export default class NativeViewGestureHandler extends GestureHandler {
317438 }
318439
319440 public override reset ( ) : void {
441+ this . clearLongPressTimer ( ) ;
320442 super . reset ( ) ;
321443 this . lastActiveHandlerData = null ;
444+ this . lastEventWasInside = false ;
445+ this . longPressDetected = false ;
446+ }
447+
448+ public override onDestroy ( ) : void {
449+ this . clearLongPressTimer ( ) ;
450+ super . onDestroy ( ) ;
322451 }
323452}
0 commit comments