react-native-gesture-handler
Version:
Declarative API exposing native platform touch and gesture system to React Native
168 lines (151 loc) • 4.92 kB
text/typescript
import type { GestureTouchEvent } from '../../../handlers/gestureHandlerCommon';
import { CALLBACK_TYPE } from '../../../handlers/gestures/gesture';
import type { ReanimatedContext } from '../../../handlers/gestures/reanimatedWrapper';
import { State } from '../../../State';
import { TouchEventType } from '../../../TouchEventType';
import { tagMessage } from '../../../utils';
import type {
ChangeCalculatorType,
GestureCallbacks,
GestureEndEvent,
GestureEvent,
GestureHandlerEventWithHandlerData,
GestureStateChangeEventWithHandlerData,
GestureUpdateEventWithHandlerData,
} from '../../types';
import {
flattenAndFilterEvent,
isEventForHandlerWithTag,
maybeExtractNativeEvent,
runCallback,
touchEventTypeToCallbackType,
} from '../utils';
import { isStateChangeEvent, isTouchEvent } from '../utils/eventUtils';
function handleStateChangeEvent<
THandlerData,
TExtendedHandlerData extends THandlerData,
>(
eventWithData: GestureStateChangeEventWithHandlerData<
THandlerData | TExtendedHandlerData
>,
callbacks: GestureCallbacks<THandlerData, TExtendedHandlerData>,
context: ReanimatedContext<TExtendedHandlerData>,
fillInDefaultValues?: (event: GestureEvent<TExtendedHandlerData>) => void
) {
'worklet';
const { oldState, state } = eventWithData;
const event = flattenAndFilterEvent(eventWithData);
if (oldState === State.UNDETERMINED && state === State.BEGAN) {
runCallback(CALLBACK_TYPE.BEGAN, callbacks, event);
} else if (
(oldState === State.BEGAN || oldState === State.UNDETERMINED) &&
state === State.ACTIVE
) {
fillInDefaultValues?.(event as GestureEvent<TExtendedHandlerData>);
runCallback(CALLBACK_TYPE.START, callbacks, event);
} else if (
oldState !== state &&
(state === State.END || state === State.FAILED || state === State.CANCELLED)
) {
const canceled = state === State.FAILED || state === State.CANCELLED;
const endEvent: GestureEndEvent<THandlerData> = {
...event,
canceled,
};
if (oldState === State.ACTIVE) {
fillInDefaultValues?.(endEvent as GestureEndEvent<TExtendedHandlerData>);
runCallback<THandlerData, TExtendedHandlerData>(
CALLBACK_TYPE.END,
callbacks,
endEvent
);
}
runCallback<THandlerData, TExtendedHandlerData>(
CALLBACK_TYPE.FINALIZE,
callbacks,
endEvent
);
if (context) {
context.lastUpdateEvent = undefined;
}
}
}
export function handleUpdateEvent<
THandlerData,
TExtendedHandlerData extends THandlerData,
>(
eventWithData: GestureUpdateEventWithHandlerData<TExtendedHandlerData>,
handlers: GestureCallbacks<THandlerData, TExtendedHandlerData>,
changeEventCalculator: ChangeCalculatorType<TExtendedHandlerData> | undefined,
context: ReanimatedContext<TExtendedHandlerData>
) {
'worklet';
const eventWithChanges = changeEventCalculator
? changeEventCalculator(
eventWithData,
context ? context.lastUpdateEvent : undefined
)
: eventWithData;
const event = flattenAndFilterEvent(eventWithChanges);
// This should never happen, but since we don't want to call hooks conditionally, we have to mark
// context as possibly undefined to make TypeScript happy.
if (!context) {
throw new Error(tagMessage('Event handler context is not defined'));
}
runCallback(CALLBACK_TYPE.UPDATE, handlers, event);
context.lastUpdateEvent = eventWithData;
}
export function handleTouchEvent<
THandlerData,
TExtendedHandlerData extends THandlerData,
>(
event: GestureTouchEvent,
handlers: GestureCallbacks<THandlerData, TExtendedHandlerData>
) {
'worklet';
if (event.eventType !== TouchEventType.UNDETERMINED) {
runCallback(touchEventTypeToCallbackType(event.eventType), handlers, event);
}
}
export function eventHandler<
THandlerData,
TExtendedHandlerData extends THandlerData,
>(
handlerTag: number,
sourceEvent: GestureHandlerEventWithHandlerData<
THandlerData,
TExtendedHandlerData
>,
handlers: GestureCallbacks<THandlerData, TExtendedHandlerData>,
changeEventCalculator: ChangeCalculatorType<TExtendedHandlerData> | undefined,
jsContext: ReanimatedContext<TExtendedHandlerData>,
dispatchesAnimatedEvents: boolean,
fillInDefaultValues?: (event: GestureEvent<TExtendedHandlerData>) => void
) {
'worklet';
const eventWithData = maybeExtractNativeEvent(sourceEvent);
if (!isEventForHandlerWithTag(handlerTag, eventWithData)) {
return;
}
if (isStateChangeEvent(eventWithData)) {
handleStateChangeEvent(
eventWithData,
handlers,
jsContext,
fillInDefaultValues
);
return;
}
if (isTouchEvent(eventWithData)) {
handleTouchEvent(eventWithData, handlers);
return;
}
if (!dispatchesAnimatedEvents) {
handleUpdateEvent(
eventWithData,
handlers,
changeEventCalculator,
jsContext
);
}
}