UNPKG

react-native-mock-tmp-build

Version:

A fully mocked and test-friendly version of react native

509 lines (467 loc) 21.7 kB
var _ScrollViewManager=require('../NativeModules/ScrollViewManager');var _ScrollViewManager2=_interopRequireDefault(_ScrollViewManager); var _Platform=require('../plugins/Platform');var _Platform2=_interopRequireDefault(_Platform); var _Dimensions=require('../api/Dimensions');var _Dimensions2=_interopRequireDefault(_Dimensions); var _DeviceEventEmitter=require('../plugins/DeviceEventEmitter');var _DeviceEventEmitter2=_interopRequireDefault(_DeviceEventEmitter); var _react=require('react');var _react2=_interopRequireDefault(_react); var _invariant=require('invariant');var _invariant2=_interopRequireDefault(_invariant); var _warning=require('warning');var _warning2=_interopRequireDefault(_warning); var _TextInputState=require('../api/TextInputState');var _TextInputState2=_interopRequireDefault(_TextInputState); var _Subscribable=require('./Subscribable');var _Subscribable2=_interopRequireDefault(_Subscribable); var _UIManager=require('../NativeModules/UIManager');var _UIManager2=_interopRequireDefault(_UIManager);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{'default':obj};} /** * Mixin that can be integrated in order to handle scrolling that plays well * with `ResponderEventPlugin`. Integrate with your platform specific scroll * views, or even your custom built (every-frame animating) scroll views so that * all of these systems play well with the `ResponderEventPlugin`. * * iOS scroll event timing nuances: * =============================== * * * Scrolling without bouncing, if you touch down: * ------------------------------- * * 1. `onMomentumScrollBegin` (when animation begins after letting up) * ... physical touch starts ... * 2. `onTouchStartCapture` (when you press down to stop the scroll) * 3. `onTouchStart` (same, but bubble phase) * 4. `onResponderRelease` (when lifting up - you could pause forever before * lifting) * 5. `onMomentumScrollEnd` * * * Scrolling with bouncing, if you touch down: * ------------------------------- * * 1. `onMomentumScrollBegin` (when animation begins after letting up) * ... bounce begins ... * ... some time elapses ... * ... physical touch during bounce ... * 2. `onMomentumScrollEnd` (Makes no sense why this occurs first during bounce) * 3. `onTouchStartCapture` (immediately after `onMomentumScrollEnd`) * 4. `onTouchStart` (same, but bubble phase) * 5. `onTouchEnd` (You could hold the touch start for a long time) * 6. `onMomentumScrollBegin` (When releasing the view starts bouncing back) * * So when we receive an `onTouchStart`, how can we tell if we are touching * *during* an animation (which then causes the animation to stop)? The only way * to tell is if the `touchStart` occurred immediately after the * `onMomentumScrollEnd`. * * This is abstracted out for you, so you can just call this.scrollResponderIsAnimating() if * necessary * * `ScrollResponder` also includes logic for blurring a currently focused input * if one is focused while scrolling. The `ScrollResponder` is a natural place * to put this logic since it can support not dismissing the keyboard while * scrolling, unless a recognized "tap"-like gesture has occurred. * * The public lifecycle API includes events for keyboard interaction, responder * interaction, and scrolling (among others). The keyboard callbacks * `onKeyboardWill/Did/*` are *global* events, but are invoked on scroll * responder's props so that you can guarantee that the scroll responder's * internal state has been updated accordingly (and deterministically) by * the time the props callbacks are invoke. Otherwise, you would always wonder * if the scroll responder is currently in a state where it recognizes new * keyboard positions etc. If coordinating scrolling with keyboard movement, * *always* use these hooks instead of listening to your own global keyboard * events. * * Public keyboard lifecycle API: (props callbacks) * * Standard Keyboard Appearance Sequence: * * this.props.onKeyboardWillShow * this.props.onKeyboardDidShow * * `onScrollResponderKeyboardDismissed` will be invoked if an appropriate * tap inside the scroll responder's scrollable region was responsible * for the dismissal of the keyboard. There are other reasons why the * keyboard could be dismissed. * * this.props.onScrollResponderKeyboardDismissed * * Standard Keyboard Hide Sequence: * * this.props.onKeyboardWillHide * this.props.onKeyboardDidHide */ var IS_ANIMATING_TOUCH_START_THRESHOLD_MS=16; var ScrollResponderMixin={ mixins:[_Subscribable2['default'].Mixin], scrollResponderMixinGetInitialState:function(){function scrollResponderMixinGetInitialState(){ return{ isTouching:false, lastMomentumScrollBeginTime:0, lastMomentumScrollEndTime:0, // Reset to false every time becomes responder. This is used to: // - Determine if the scroll view has been scrolled and therefore should // refuse to give up its responder lock. // - Determine if releasing should dismiss the keyboard when we are in // tap-to-dismiss mode (!this.props.keyboardShouldPersistTaps). observedScrollSinceBecomingResponder:false, becameResponderWhileAnimating:false}; }return scrollResponderMixinGetInitialState;}(), /** * Invoke this from an `onScroll` event. */ scrollResponderHandleScrollShouldSetResponder:function(){function scrollResponderHandleScrollShouldSetResponder(){ return this.state.isTouching; }return scrollResponderHandleScrollShouldSetResponder;}(), /** * Merely touch starting is not sufficient for a scroll view to become the * responder. Being the "responder" means that the very next touch move/end * event will result in an action/movement. * * Invoke this from an `onStartShouldSetResponder` event. * * `onStartShouldSetResponder` is used when the next move/end will trigger * some UI movement/action, but when you want to yield priority to views * nested inside of the view. * * There may be some cases where scroll views actually should return `true` * from `onStartShouldSetResponder`: Any time we are detecting a standard tap * that gives priority to nested views. * * - If a single tap on the scroll view triggers an action such as * recentering a map style view yet wants to give priority to interaction * views inside (such as dropped pins or labels), then we would return true * from this method when there is a single touch. * * - Similar to the previous case, if a two finger "tap" should trigger a * zoom, we would check the `touches` count, and if `>= 2`, we would return * true. * */ scrollResponderHandleStartShouldSetResponder:function(){function scrollResponderHandleStartShouldSetResponder(){ return false; }return scrollResponderHandleStartShouldSetResponder;}(), /** * There are times when the scroll view wants to become the responder * (meaning respond to the next immediate `touchStart/touchEnd`), in a way * that *doesn't* give priority to nested views (hence the capture phase): * * - Currently animating. * - Tapping anywhere that is not the focused input, while the keyboard is * up (which should dismiss the keyboard). * * Invoke this from an `onStartShouldSetResponderCapture` event. */ scrollResponderHandleStartShouldSetResponderCapture:function(){function scrollResponderHandleStartShouldSetResponderCapture(e){ // First see if we want to eat taps while the keyboard is up var currentlyFocusedTextInput=_TextInputState2['default'].currentlyFocusedField(); if(!this.props.keyboardShouldPersistTaps&& currentlyFocusedTextInput!=null&& e.target!==currentlyFocusedTextInput){ return true; } return this.scrollResponderIsAnimating(); }return scrollResponderHandleStartShouldSetResponderCapture;}(), /** * Invoke this from an `onResponderReject` event. * * Some other element is not yielding its role as responder. Normally, we'd * just disable the `UIScrollView`, but a touch has already began on it, the * `UIScrollView` will not accept being disabled after that. The easiest * solution for now is to accept the limitation of disallowing this * altogether. To improve this, find a way to disable the `UIScrollView` after * a touch has already started. */ scrollResponderHandleResponderReject:function(){function scrollResponderHandleResponderReject(){ (0,_warning2['default'])(false,"ScrollView doesn't take rejection well - scrolls anyway"); }return scrollResponderHandleResponderReject;}(), /** * We will allow the scroll view to give up its lock iff it acquired the lock * during an animation. This is a very useful default that happens to satisfy * many common user experiences. * * - Stop a scroll on the left edge, then turn that into an outer view's * backswipe. * - Stop a scroll mid-bounce at the top, continue pulling to have the outer * view dismiss. * - However, without catching the scroll view mid-bounce (while it is * motionless), if you drag far enough for the scroll view to become * responder (and therefore drag the scroll view a bit), any backswipe * navigation of a swipe gesture higher in the view hierarchy, should be * rejected. */ scrollResponderHandleTerminationRequest:function(){function scrollResponderHandleTerminationRequest(){ return!this.state.observedScrollSinceBecomingResponder; }return scrollResponderHandleTerminationRequest;}(), /** * Invoke this from an `onTouchEnd` event. * * @param {SyntheticEvent} e Event. */ scrollResponderHandleTouchEnd:function(){function scrollResponderHandleTouchEnd(e){ var nativeEvent=e.nativeEvent; this.state.isTouching=nativeEvent.touches.length!==0; if(this.props.onTouchEnd){ this.props.onTouchEnd(e); } }return scrollResponderHandleTouchEnd;}(), /** * Invoke this from an `onResponderRelease` event. */ scrollResponderHandleResponderRelease:function(){function scrollResponderHandleResponderRelease(e){ if(this.props.onResponderRelease){ this.props.onResponderRelease(e); } // By default scroll views will unfocus a textField // if another touch occurs outside of it var currentlyFocusedTextInput=_TextInputState2['default'].currentlyFocusedField(); if(!this.props.keyboardShouldPersistTaps&& currentlyFocusedTextInput!=null&& e.target!==currentlyFocusedTextInput&& !this.state.observedScrollSinceBecomingResponder&& !this.state.becameResponderWhileAnimating){ if(this.props.onScrollResponderKeyboardDismissed){ this.props.onScrollResponderKeyboardDismissed(e); } _TextInputState2['default'].blurTextInput(currentlyFocusedTextInput); } }return scrollResponderHandleResponderRelease;}(), scrollResponderHandleScroll:function(){function scrollResponderHandleScroll(e){ this.state.observedScrollSinceBecomingResponder=true; if(this.props.onScroll){ this.props.onScroll(e); } }return scrollResponderHandleScroll;}(), /** * Invoke this from an `onResponderGrant` event. */ scrollResponderHandleResponderGrant:function(){function scrollResponderHandleResponderGrant(e){ this.state.observedScrollSinceBecomingResponder=false; if(this.props.onResponderGrant){ this.props.onResponderGrant(e); } this.state.becameResponderWhileAnimating=this.scrollResponderIsAnimating(); }return scrollResponderHandleResponderGrant;}(), /** * Unfortunately, `onScrollBeginDrag` also fires when *stopping* the scroll * animation, and there's not an easy way to distinguish a drag vs. stopping * momentum. * * Invoke this from an `onScrollBeginDrag` event. */ scrollResponderHandleScrollBeginDrag:function(){function scrollResponderHandleScrollBeginDrag(e){ if(this.props.onScrollBeginDrag){ this.props.onScrollBeginDrag(e); } }return scrollResponderHandleScrollBeginDrag;}(), /** * Invoke this from an `onScrollEndDrag` event. */ scrollResponderHandleScrollEndDrag:function(){function scrollResponderHandleScrollEndDrag(e){ if(this.props.onScrollEndDrag){ this.props.onScrollEndDrag(e); } }return scrollResponderHandleScrollEndDrag;}(), /** * Invoke this from an `onMomentumScrollBegin` event. */ scrollResponderHandleMomentumScrollBegin:function(){function scrollResponderHandleMomentumScrollBegin(e){ this.state.lastMomentumScrollBeginTime=Date.now(); if(this.props.onMomentumScrollBegin){ this.props.onMomentumScrollBegin(e); } }return scrollResponderHandleMomentumScrollBegin;}(), /** * Invoke this from an `onMomentumScrollEnd` event. */ scrollResponderHandleMomentumScrollEnd:function(){function scrollResponderHandleMomentumScrollEnd(e){ this.state.lastMomentumScrollEndTime=Date.now(); if(this.props.onMomentumScrollEnd){ this.props.onMomentumScrollEnd(e); } }return scrollResponderHandleMomentumScrollEnd;}(), /** * Invoke this from an `onTouchStart` event. * * Since we know that the `SimpleEventPlugin` occurs later in the plugin * order, after `ResponderEventPlugin`, we can detect that we were *not* * permitted to be the responder (presumably because a contained view became * responder). The `onResponderReject` won't fire in that case - it only * fires when a *current* responder rejects our request. * * @param {SyntheticEvent} e Touch Start event. */ scrollResponderHandleTouchStart:function(){function scrollResponderHandleTouchStart(e){ this.state.isTouching=true; if(this.props.onTouchStart){ this.props.onTouchStart(e); } }return scrollResponderHandleTouchStart;}(), /** * Invoke this from an `onTouchMove` event. * * Since we know that the `SimpleEventPlugin` occurs later in the plugin * order, after `ResponderEventPlugin`, we can detect that we were *not* * permitted to be the responder (presumably because a contained view became * responder). The `onResponderReject` won't fire in that case - it only * fires when a *current* responder rejects our request. * * @param {SyntheticEvent} e Touch Start event. */ scrollResponderHandleTouchMove:function(){function scrollResponderHandleTouchMove(e){ if(this.props.onTouchMove){ this.props.onTouchMove(e); } }return scrollResponderHandleTouchMove;}(), /** * A helper function for this class that lets us quickly determine if the * view is currently animating. This is particularly useful to know when * a touch has just started or ended. */ scrollResponderIsAnimating:function(){function scrollResponderIsAnimating(){ var now=Date.now(); var timeSinceLastMomentumScrollEnd=now-this.state.lastMomentumScrollEndTime; var isAnimating=timeSinceLastMomentumScrollEnd<IS_ANIMATING_TOUCH_START_THRESHOLD_MS|| this.state.lastMomentumScrollEndTime<this.state.lastMomentumScrollBeginTime; return isAnimating; }return scrollResponderIsAnimating;}(), /** * A helper function to scroll to a specific point in the scrollview. * This is currently used to help focus on child textviews, but this * can also be used to quickly scroll to any element we want to focus */ scrollResponderScrollTo:function(){function scrollResponderScrollTo(offsetX,offsetY){var animated=arguments.length>2&&arguments[2]!==undefined?arguments[2]:true; }return scrollResponderScrollTo;}(), /** * A helper function to zoom to a specific rect in the scrollview. * @param {object} rect Should have shape {x, y, width, height} * @param {bool} animated Specify whether zoom is instant or animated */ scrollResponderZoomTo:function(){function scrollResponderZoomTo(rect){var animated=arguments.length>1&&arguments[1]!==undefined?arguments[1]:true; if(_Platform2['default'].OS==='android'){ (0,_invariant2['default'])('zoomToRect is not implemented'); }else{ _ScrollViewManager2['default'].zoomToRect(_react2['default'].findNodeHandle(this),rect,animated); } }return scrollResponderZoomTo;}(), /** * This method should be used as the callback to onFocus in a TextInputs' * parent view. Note that any module using this mixin needs to return * the parent view's ref in getScrollViewRef() in order to use this method. * @param {any} nodeHandle The TextInput node handle * @param {number} additionalOffset The scroll view's top "contentInset". * Default is 0. * @param {bool} preventNegativeScrolling Whether to allow pulling the content * down to make it meet the keyboard's top. Default is false. */ scrollResponderScrollNativeHandleToKeyboard:function(){function scrollResponderScrollNativeHandleToKeyboard( nodeHandle, additionalOffset, preventNegativeScrollOffset){ this.additionalScrollOffset=additionalOffset||0; this.preventNegativeScrollOffset=!!preventNegativeScrollOffset; _UIManager2['default'].measureLayout( nodeHandle, _react2['default'].findNodeHandle(this.getInnerViewNode()), this.scrollResponderTextInputFocusError, this.scrollResponderInputMeasureAndScrollToKeyboard); }return scrollResponderScrollNativeHandleToKeyboard;}(), /** * The calculations performed here assume the scroll view takes up the entire * screen - even if has some content inset. We then measure the offsets of the * keyboard, and compensate both for the scroll view's "contentInset". * * @param {number} left Position of input w.r.t. table view. * @param {number} top Position of input w.r.t. table view. * @param {number} width Width of the text input. * @param {number} height Height of the text input. */ scrollResponderInputMeasureAndScrollToKeyboard:function(){function scrollResponderInputMeasureAndScrollToKeyboard(left,top,width,height){ var keyboardScreenY=_Dimensions2['default'].get('window').height; if(this.keyboardWillOpenTo){ keyboardScreenY=this.keyboardWillOpenTo.endCoordinates.screenY; } var scrollOffsetY=top-keyboardScreenY+height+this.additionalScrollOffset; // By default, this can scroll with negative offset, pulling the content // down so that the target component's bottom meets the keyboard's top. // If requested otherwise, cap the offset at 0 minimum to avoid content // shifting down. if(this.preventNegativeScrollOffset){ scrollOffsetY=Math.max(0,scrollOffsetY); } this.scrollResponderScrollTo(0,scrollOffsetY); this.additionalOffset=0; this.preventNegativeScrollOffset=false; }return scrollResponderInputMeasureAndScrollToKeyboard;}(), scrollResponderTextInputFocusError:function(){function scrollResponderTextInputFocusError(e){ console.error('Error measuring text field: ',e); }return scrollResponderTextInputFocusError;}(), /** * `componentWillMount` is the closest thing to a standard "constructor" for * React components. * * The `keyboardWillShow` is called before input focus. */ componentWillMount:function(){function componentWillMount(){ this.keyboardWillOpenTo=null; this.additionalScrollOffset=0; this.addListenerOn(_DeviceEventEmitter2['default'], 'keyboardWillShow',this.scrollResponderKeyboardWillShow); this.addListenerOn(_DeviceEventEmitter2['default'], 'keyboardWillHide',this.scrollResponderKeyboardWillHide); this.addListenerOn(_DeviceEventEmitter2['default'],'keyboardDidShow',this.scrollResponderKeyboardDidShow); this.addListenerOn(_DeviceEventEmitter2['default'],'keyboardDidHide',this.scrollResponderKeyboardDidHide); }return componentWillMount;}(), /** * Warning, this may be called several times for a single keyboard opening. * It's best to store the information in this method and then take any action * at a later point (either in `keyboardDidShow` or other). * * Here's the order that events occur in: * - focus * - willShow {startCoordinates, endCoordinates} several times * - didShow several times * - blur * - willHide {startCoordinates, endCoordinates} several times * - didHide several times * * The `ScrollResponder` providesModule callbacks for each of these events. * Even though any user could have easily listened to keyboard events * themselves, using these `props` callbacks ensures that ordering of events * is consistent - and not dependent on the order that the keyboard events are * subscribed to. This matters when telling the scroll view to scroll to where * the keyboard is headed - the scroll responder better have been notified of * the keyboard destination before being instructed to scroll to where the * keyboard will be. Stick to the `ScrollResponder` callbacks, and everything * will work. * * WARNING: These callbacks will fire even if a keyboard is displayed in a * different navigation pane. Filter out the events to determine if they are * relevant to you. (For example, only if you receive these callbacks after * you had explicitly focused a node etc). */ scrollResponderKeyboardWillShow:function(){function scrollResponderKeyboardWillShow(e){ this.keyboardWillOpenTo=e; if(this.props.onKeyboardWillShow){ this.props.onKeyboardWillShow(e); } }return scrollResponderKeyboardWillShow;}(), scrollResponderKeyboardWillHide:function(){function scrollResponderKeyboardWillHide(e){ this.keyboardWillOpenTo=null; if(this.props.onKeyboardWillHide){ this.props.onKeyboardWillHide(e); } }return scrollResponderKeyboardWillHide;}(), scrollResponderKeyboardDidShow:function(){function scrollResponderKeyboardDidShow(e){ // TODO(7693961): The event for DidShow is not available on iOS yet. // Use the one from WillShow and do not assign. if(e){ this.keyboardWillOpenTo=e; } if(this.props.onKeyboardDidShow){ this.props.onKeyoardDidShow(e); } }return scrollResponderKeyboardDidShow;}(), scrollResponderKeyboardDidHide:function(){function scrollResponderKeyboardDidHide(e){ this.keyboardWillOpenTo=null; if(this.props.onKeyboardDidHide){ this.props.onKeyboardDidHide(e); } }return scrollResponderKeyboardDidHide;}()}; var ScrollResponder={ Mixin:ScrollResponderMixin}; module.exports=ScrollResponder;