react-native-gesture-handler
Version:
Declarative API exposing native platform touch and gesture system to React Native
160 lines (142 loc) • 5.64 kB
text/typescript
import type { GestureRef, GestureType } from './gesture';
import { BaseGesture, Gesture } from './gesture';
function extendRelation(
currentRelation: GestureRef[] | undefined,
extendWith: GestureType[]
) {
if (currentRelation === undefined) {
return [...extendWith];
} else {
return [...currentRelation, ...extendWith];
}
}
/**
* @deprecated `ComposedGesture` is deprecated and will be removed in the future. Please use `useCompetingGestures` instead.
*/
export class ComposedGesture extends Gesture {
protected gestures: Gesture[] = [];
protected simultaneousGestures: GestureType[] = [];
protected requireGesturesToFail: GestureType[] = [];
constructor(...gestures: Gesture[]) {
super();
this.gestures = gestures;
}
protected prepareSingleGesture(
gesture: Gesture,
simultaneousGestures: GestureType[],
requireGesturesToFail: GestureType[]
) {
if (gesture instanceof BaseGesture) {
// Capture the relations defined directly on the gesture before composition
// extends them, then always rebuild from that snapshot. Otherwise, when the
// gesture is stable (e.g. wrapped in `useMemo`) but the composition is
// recreated on every render, the relations would keep accumulating
// references to gestures from previous renders, leaking memory (see #3763).
// We keep the original references (instead of collapsing them to handler
// tags) so relations can still be re-resolved after a remount, such as a
// `react-freeze` unfreeze (see #4238).
gesture.relationsSnapshot ??= {
simultaneousWith: gesture.config.simultaneousWith,
requireToFail: gesture.config.requireToFail,
};
const newConfig = { ...gesture.config };
// No need to extend `blocksHandlers` here, because it's not changed in composition.
// The same effect is achieved by reversing the order of 2 gestures in `Exclusive`
newConfig.simultaneousWith = extendRelation(
gesture.relationsSnapshot.simultaneousWith,
simultaneousGestures
);
newConfig.requireToFail = extendRelation(
gesture.relationsSnapshot.requireToFail,
requireGesturesToFail
);
gesture.config = newConfig;
} else if (gesture instanceof ComposedGesture) {
gesture.simultaneousGestures = simultaneousGestures;
gesture.requireGesturesToFail = requireGesturesToFail;
gesture.prepare();
}
}
prepare() {
for (const gesture of this.gestures) {
this.prepareSingleGesture(
gesture,
this.simultaneousGestures,
this.requireGesturesToFail
);
}
}
initialize() {
for (const gesture of this.gestures) {
gesture.initialize();
}
}
toGestureArray(): GestureType[] {
return this.gestures.flatMap((gesture) => gesture.toGestureArray());
}
}
/**
* @deprecated `SimultaneousGesture` is deprecated and will be removed in the future. Please use `useSimultaneousGestures` instead.
*/
export class SimultaneousGesture extends ComposedGesture {
override prepare() {
// This piece of magic works something like this:
// for every gesture in the array
const simultaneousArrays = this.gestures.map((gesture) =>
// we take the array it's in
this.gestures
// and make a copy without it
.filter((x) => x !== gesture)
// then we flatmap the result to get list of raw (not composed) gestures
// this way we don't make the gestures simultaneous with themselves, which is
// important when the gesture is `ExclusiveGesture` - we don't want to make
// exclusive gestures simultaneous
.flatMap((x) => x.toGestureArray())
);
for (let i = 0; i < this.gestures.length; i++) {
this.prepareSingleGesture(
this.gestures[i],
simultaneousArrays[i],
this.requireGesturesToFail
);
}
}
}
/**
* @deprecated `ExclusiveGesture` is deprecated and will be removed in the future. Please use `useExclusiveGestures` instead.
*/
export class ExclusiveGesture extends ComposedGesture {
override prepare() {
// Transforms the array of gestures into array of grouped raw (not composed) gestures
// i.e. [gesture1, gesture2, ComposedGesture(gesture3, gesture4)] -> [[gesture1], [gesture2], [gesture3, gesture4]]
const gestureArrays = this.gestures.map((gesture) =>
gesture.toGestureArray()
);
let requireToFail: GestureType[] = [];
for (let i = 0; i < this.gestures.length; i++) {
this.prepareSingleGesture(
this.gestures[i],
this.simultaneousGestures,
this.requireGesturesToFail.concat(requireToFail)
);
// Every group gets to wait for all groups before it
requireToFail = requireToFail.concat(gestureArrays[i]);
}
}
}
/**
* @deprecated `ComposedGestureType` is deprecated and will be removed in the future. Please use `ComposedGesture` instead.
*/
export type ComposedGestureType = InstanceType<typeof ComposedGesture>;
/**
* @deprecated `RaceGestureType` is deprecated and will be removed in the future. Please use `ComposedGesture` instead.
*/
export type RaceGestureType = ComposedGestureType;
/**
* @deprecated `SimultaneousGestureType` is deprecated and will be removed in the future. Please use `ComposedGesture` instead.
*/
export type SimultaneousGestureType = InstanceType<typeof SimultaneousGesture>;
/**
* @deprecated `ExclusiveGestureType` is deprecated and will be removed in the future. Please use `ComposedGesture` instead.
*/
export type ExclusiveGestureType = InstanceType<typeof ExclusiveGesture>;