react-native-gesture-handler
Version:
Declarative API exposing native platform touch and gesture system to React Native
125 lines (110 loc) • 6.79 kB
JavaScript
;
// This piece of magic traverses the gesture tree and populates `waitFor` and `simultaneousHandlers`
// arrays for each gesture. It traverses the tree recursively using DFS.
// `waitFor` and `simultaneousHandlers` are global data structures that will be populated into each gesture.
// For `waitFor` we need array as order of the gestures matters.
// For `simultaneousHandlers` we use Set as the order doesn't matter.
import { tagMessage } from '../../utils';
import { isComposedGesture, prepareRelations } from '../hooks/utils/relationUtils';
import { ComposedGestureName } from '../types';
// The tree consists of ComposedGestures and NativeGestures. NativeGestures are always leaf nodes.
export const traverseAndConfigureRelations = (node, relationsByTag, simultaneousHandlers, waitFor = []) => {
// If we are in the leaf node, we want to fill gesture relations arrays with current
// waitFor and simultaneousHandlers. We also want to record the resulting relations for
// this handler so the caller can push them to the native side.
if (!isComposedGesture(node)) {
node.gestureRelations = prepareRelations(node.config, node.handlerTag);
node.gestureRelations.simultaneousHandlers.push(...simultaneousHandlers);
node.gestureRelations.waitFor.push(...waitFor);
relationsByTag.set(node.handlerTag, {
waitFor: node.gestureRelations.waitFor,
simultaneousHandlers: node.gestureRelations.simultaneousHandlers,
blocksHandlers: node.gestureRelations.blocksHandlers
});
return;
}
// If we are in the composed gesture, we want to traverse its children.
node.gestures.forEach(child => {
// If child is composed gesture, we have to correctly fill `waitFor` and `simultaneousHandlers`.
if (isComposedGesture(child)) {
// We have to update `simultaneousHandlers` before traversing the child (going top-down).
// Simultaneous is an all-to-all relation - it needs to be prepared when entering the node.
// Exclusive is a one-to-many relation - gesture depends on the preceding ones and not on itself - it should be prepared when leaving the node (bottom-up).
// If we go from a non-simultaneous gesture to a simultaneous gesture,
// we add the tags of the simultaneous gesture to the `simultaneousHandlers`.
// This way when we traverse the child, we already have the tags of the simultaneous gestures
if (node.type !== ComposedGestureName.Simultaneous && child.type === ComposedGestureName.Simultaneous) {
child.handlerTags.forEach(handlerTag => simultaneousHandlers.add(handlerTag));
}
// If we go from a simultaneous gesture to a non-simultaneous gesture,
// we remove the tags of the child gestures from the `simultaneousHandlers`,
// as those are not simultaneous with each other.
if (node.type === ComposedGestureName.Simultaneous && child.type !== ComposedGestureName.Simultaneous) {
child.handlerTags.forEach(handlerTag => simultaneousHandlers.delete(handlerTag));
}
// We will keep the current length of `waitFor` to reset it to previous state
// after traversing the child.
const length = waitFor.length;
// We traverse the child, passing the current `waitFor` and `simultaneousHandlers`.
traverseAndConfigureRelations(child, relationsByTag, simultaneousHandlers, waitFor);
// After traversing the child, we need to update `waitFor` and `simultaneousHandlers`
// If we go back from a simultaneous gesture to a non-simultaneous gesture,
// we want to delete the tags of the simultaneous gesture from the `simultaneousHandlers` -
// those gestures are not simultaneous with each other anymore.
if (child.type === ComposedGestureName.Simultaneous && node.type !== ComposedGestureName.Simultaneous) {
node.handlerTags.forEach(handlerTag => simultaneousHandlers.delete(handlerTag));
}
// If we go back from a non-simultaneous gesture to a simultaneous gesture,
// we want to add the tags of the simultaneous gesture to the `simultaneousHandlers`,
// as those gestures are simultaneous with other children of the current node.
if (child.type !== ComposedGestureName.Simultaneous && node.type === ComposedGestureName.Simultaneous) {
node.handlerTags.forEach(handlerTag => simultaneousHandlers.add(handlerTag));
}
// If we go back to an exclusive gesture, we want to add the tags of the child gesture to the `waitFor` array.
// This will allow us to pass exclusive gesture tags to the right subtree of the current node.
if (node.type === ComposedGestureName.Exclusive) {
child.handlerTags.forEach(handlerTag => waitFor.push(handlerTag));
}
// If we go back from an exclusive gesture to a non-exclusive gesture, we want to reset the `waitFor` array
// to the previous state, siblings of the exclusive gesture are not exclusive with it. Since we use `push` method to
// add tags to the `waitFor` array, we can override `length` property to reset it to the previous state.
if (child.type === ComposedGestureName.Exclusive && node.type !== ComposedGestureName.Exclusive) {
waitFor.length = length;
}
}
// This means that child is a leaf node.
else {
// We don't want to mark gesture as simultaneous with itself, so we remove its tag from the set.
const hasRemovedTag = simultaneousHandlers.delete(child.handlerTag);
traverseAndConfigureRelations(child, relationsByTag, simultaneousHandlers, waitFor);
if (hasRemovedTag) {
simultaneousHandlers.add(child.handlerTag);
}
// In the leaf node, we only care about filling `waitFor` array.
if (node.type === ComposedGestureName.Exclusive) {
waitFor.push(child.handlerTag);
}
}
});
};
export function configureRelations(gesture) {
const relationsByTag = new Map();
if (isComposedGesture(gesture)) {
const simultaneousHandlers = new Set(gesture.externalSimultaneousHandlers);
// If root is simultaneous, we want to add its tags to the set
if (gesture.type === ComposedGestureName.Simultaneous) {
gesture.handlerTags.forEach(handlerTag => simultaneousHandlers.add(handlerTag));
}
traverseAndConfigureRelations(gesture, relationsByTag, simultaneousHandlers);
} else {
relationsByTag.set(gesture.handlerTag, gesture.gestureRelations);
}
return relationsByTag;
}
export function ensureNativeDetectorComponent(NativeDetectorComponent) {
if (!NativeDetectorComponent) {
throw new Error(tagMessage('Gesture expects to run on the UI thread, but failed to create the Reanimated NativeDetector.'));
}
}
export const EMPTY_SET = new Set();
//# sourceMappingURL=utils.js.map