From 552c60192172f6ec503181c060c08bbc5cbcc5a4 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 25 Nov 2016 05:30:05 -0800 Subject: [PATCH] Don't dismiss keyboard when tapping another text input Summary: When using text inputs inside a ScrollView with `keyboardShouldPersistTaps=false` (default behavior) tapping another text input dismisses the keyboard instead of keeping it open and focusing the new text input which I think is the better and expected behavior. See #10628 for more discussion about that. Note that this affects nothing but the behavior with text inputs unlike #10628. cc satya164 MaxLap ericvicenti Closes https://github.com/facebook/react-native/pull/10887 Differential Revision: D4178474 Pulled By: ericvicenti fbshipit-source-id: 0c62ea2fac0017d559d1f8674b0a686a5e1b3d2d --- Libraries/Components/ScrollResponder.js | 14 ++++++++++++-- Libraries/ReactNative/requireNativeComponent.js | 4 ++-- .../react/uimanager/TouchTargetHelper.java | 14 +++++++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Libraries/Components/ScrollResponder.js b/Libraries/Components/ScrollResponder.js index e3049071b..26c3630a3 100644 --- a/Libraries/Components/ScrollResponder.js +++ b/Libraries/Components/ScrollResponder.js @@ -19,6 +19,7 @@ var Subscribable = require('Subscribable'); var TextInputState = require('TextInputState'); var UIManager = require('UIManager'); +var { getInstanceFromNode } = require('ReactNativeComponentTree'); var { ScrollViewManager } = require('NativeModules'); var invariant = require('fbjs/lib/invariant'); @@ -112,6 +113,15 @@ type State = { }; type Event = Object; +function isTagInstanceOfTextInput(tag) { + var instance = getInstanceFromNode(tag); + return instance && instance.viewConfig && ( + instance.viewConfig.uiViewClassName === 'AndroidTextInput' || + instance.viewConfig.uiViewClassName === 'RCTTextView' || + instance.viewConfig.uiViewClassName === 'RCTTextField' + ); +} + var ScrollResponderMixin = { mixins: [Subscribable.Mixin], scrollResponderMixinGetInitialState: function(): State { @@ -172,7 +182,7 @@ var ScrollResponderMixin = { * 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 + * - Tapping anywhere that is not a text input, while the keyboard is * up (which should dismiss the keyboard). * * Invoke this from an `onStartShouldSetResponderCapture` event. @@ -182,7 +192,7 @@ var ScrollResponderMixin = { var currentlyFocusedTextInput = TextInputState.currentlyFocusedField(); if (!this.props.keyboardShouldPersistTaps && currentlyFocusedTextInput != null && - e.target !== currentlyFocusedTextInput) { + !isTagInstanceOfTextInput(e.target)) { return true; } return this.scrollResponderIsAnimating(); diff --git a/Libraries/ReactNative/requireNativeComponent.js b/Libraries/ReactNative/requireNativeComponent.js index 903f3b239..098d024bf 100644 --- a/Libraries/ReactNative/requireNativeComponent.js +++ b/Libraries/ReactNative/requireNativeComponent.js @@ -101,7 +101,7 @@ function requireNativeComponent( return createReactNativeComponentClass(viewConfig); } -var TypeToDifferMap = { +const TypeToDifferMap = { // iOS Types CATransform3D: matricesDiffer, CGPoint: pointsDiffer, @@ -115,7 +115,7 @@ function processColorArray(colors: []): [] { return colors && colors.map(processColor); } -var TypeToProcessorMap = { +const TypeToProcessorMap = { // iOS Types CGColor: processColor, CGColorArray: processColorArray, diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java index 4ef74fafc..6243edd19 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java @@ -200,6 +200,18 @@ public class TouchTargetHelper { float eventCoords[], View view) { PointerEvents pointerEvents = view instanceof ReactPointerEventsView ? ((ReactPointerEventsView) view).getPointerEvents() : PointerEvents.AUTO; + + // Views that are disabled should never be the target of pointer events. However, their children + // can be because some views (SwipeRefreshLayout) use enabled but still have children that can + // be valid targets. + if (!view.isEnabled()) { + if (pointerEvents == PointerEvents.AUTO) { + pointerEvents = PointerEvents.BOX_NONE; + } else if (pointerEvents == PointerEvents.BOX_ONLY) { + pointerEvents = PointerEvents.NONE; + } + } + if (pointerEvents == PointerEvents.NONE) { // This view and its children can't be the target return null; @@ -209,7 +221,7 @@ public class TouchTargetHelper { return view; } else if (pointerEvents == PointerEvents.BOX_NONE) { - // This view can't be the target, but its children might + // This view can't be the target, but its children might. if (view instanceof ViewGroup) { View targetView = findTouchTargetView(eventCoords, (ViewGroup) view); if (targetView != view) {