Root responder for global interception of touch events in RN
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Wrapper for creating Global (RootContainer) PanResponder | |
* Responder events are bubbled up by the View hierarchy, but sometimes it's needed to "move" | |
* touch handling to a sibling component. Instead we're creating Root Responder to capture the touch and | |
* delegate the callbacks to any component that wants to listen to it. | |
* | |
* Any component in the App can hook into the Root Responder lifecycle callbacks | |
* For capturing of the Root Responder the *Capture callbacks should return true | |
* | |
* Only one component at a time can be attached to Root Responder | |
* As soon as another one hooks into the callbacks, the old component will stop receiving events | |
*/ | |
import {PanResponder} from 'react-native'; | |
import {isString, isFunction} from 'lodash'; | |
/** | |
* Default callback always returns false so none of touch events gets captured | |
*/ | |
const defaultCallback = () => false; | |
let onStartShouldSetPanResponder = defaultCallback; | |
let onMoveShouldSetPanResponder = defaultCallback; | |
let onPanResponderMove = defaultCallback; | |
let onPanResponderRelease = defaultCallback; | |
export const CALLBACK_TYPES = { | |
ON_START_CAPTURE: 'ON_START_CAPTURE', | |
ON_MOVE_CAPTURE: 'ON_MOVE_CAPTURE', | |
ON_MOVE: 'ON_MOVE', | |
ON_RELEASE: 'ON_RELEASE' | |
}; | |
/** | |
* Every callback needs to be wrapped into an arrow function to keep reference in PanResponder | |
* But still to be able to swap the actual callback on a fly | |
*/ | |
const panResponder = PanResponder.create({ | |
onStartShouldSetPanResponderCapture: (event, gestureState) => onStartShouldSetPanResponder(event, gestureState), | |
onMoveShouldSetPanResponderCapture: (event, gestureState) => onMoveShouldSetPanResponder(event, gestureState), | |
onPanResponderMove: (event, gestureState) => onPanResponderMove(event, gestureState), | |
onPanResponderRelease: (event, gestureState) => onPanResponderRelease(event, gestureState), | |
onPanResponderTerminate: (event, gestureState) => onPanResponderRelease(event, gestureState) | |
}); | |
export const getPanHandlers = () => panResponder.panHandlers; | |
export const setCallback = (callback, type) => { | |
if (!isString(type)) { | |
throw new Error(`Root PanResponder callback type is not a String: ${JSON.stringify(type)}`); | |
} | |
if (!isFunction(callback)) { | |
throw new Error('Root PanResponder callback is not a Function'); | |
} | |
switch (type) { | |
case CALLBACK_TYPES.ON_START_CAPTURE: | |
onStartShouldSetPanResponder = callback; | |
break; | |
case CALLBACK_TYPES.ON_MOVE_CAPTURE: | |
onMoveShouldSetPanResponder = callback; | |
break; | |
case CALLBACK_TYPES.ON_MOVE: | |
onPanResponderMove = callback; | |
break; | |
case CALLBACK_TYPES.ON_RELEASE: | |
onPanResponderRelease = callback; | |
break; | |
default: | |
break; | |
} | |
}; | |
export const clearAllCallbacks = () => { | |
onStartShouldSetPanResponder = defaultCallback; | |
onMoveShouldSetPanResponder = defaultCallback; | |
onPanResponderMove = defaultCallback; | |
onPanResponderRelease = defaultCallback; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment