diff --git a/Examples/Movies/SearchScreen.js b/Examples/Movies/SearchScreen.js index 057336649b3..d45ddf1a29e 100644 --- a/Examples/Movies/SearchScreen.js +++ b/Examples/Movies/SearchScreen.js @@ -299,7 +299,7 @@ var SearchScreen = React.createClass({ onEndReached={this.onEndReached} automaticallyAdjustContentInsets={false} keyboardDismissMode="on-drag" - keyboardShouldPersistTaps={true} + keyboardShouldPersistTaps="handled" showsVerticalScrollIndicator={false} />; diff --git a/Examples/UIExplorer/js/UIExplorerExampleList.js b/Examples/UIExplorer/js/UIExplorerExampleList.js index 47772a63f7f..b9af139c34a 100644 --- a/Examples/UIExplorer/js/UIExplorerExampleList.js +++ b/Examples/UIExplorer/js/UIExplorerExampleList.js @@ -82,7 +82,7 @@ class UIExplorerExampleList extends React.Component { renderRow={this._renderExampleRow.bind(this)} renderSectionHeader={this._renderSectionHeader} enableEmptySections={true} - keyboardShouldPersistTaps={true} + keyboardShouldPersistTaps="handled" automaticallyAdjustContentInsets={false} keyboardDismissMode="on-drag" /> diff --git a/Examples/UIExplorer/js/UIExplorerPage.js b/Examples/UIExplorer/js/UIExplorerPage.js index c29e5bfb61e..c2f75858b0d 100644 --- a/Examples/UIExplorer/js/UIExplorerPage.js +++ b/Examples/UIExplorer/js/UIExplorerPage.js @@ -35,13 +35,11 @@ var UIExplorerTitle = require('./UIExplorerTitle'); class UIExplorerPage extends React.Component { props: { - keyboardShouldPersistTaps?: boolean, noScroll?: boolean, noSpacer?: boolean, }; static propTypes = { - keyboardShouldPersistTaps: React.PropTypes.bool, noScroll: React.PropTypes.bool, noSpacer: React.PropTypes.bool, }; @@ -55,7 +53,7 @@ class UIExplorerPage extends React.Component { ContentWrapper = (ScrollView: ReactClass); // $FlowFixMe found when converting React.createClass to ES6 wrapperProps.automaticallyAdjustContentInsets = !this.props.title; - wrapperProps.keyboardShouldPersistTaps = true; + wrapperProps.keyboardShouldPersistTaps = 'handled'; wrapperProps.keyboardDismissMode = 'interactive'; } var title = this.props.title ? diff --git a/Libraries/Components/ScrollResponder.js b/Libraries/Components/ScrollResponder.js index e3ead883791..6b739ebebda 100644 --- a/Libraries/Components/ScrollResponder.js +++ b/Libraries/Components/ScrollResponder.js @@ -18,6 +18,7 @@ var ReactNative = require('ReactNative'); var Subscribable = require('Subscribable'); var TextInputState = require('TextInputState'); var UIManager = require('UIManager'); +var warning = require('fbjs/lib/warning'); var { getInstanceFromNode } = require('ReactNativeComponentTree'); var { ScrollViewManager } = require('NativeModules'); @@ -134,7 +135,7 @@ var ScrollResponderMixin = { // - 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). + // tap-to-dismiss mode (this.props.keyboardShouldPersistTaps !== 'always'). observedScrollSinceBecomingResponder: false, becameResponderWhileAnimating: false, }; @@ -172,7 +173,14 @@ var ScrollResponderMixin = { * true. * */ - scrollResponderHandleStartShouldSetResponder: function(): boolean { + scrollResponderHandleStartShouldSetResponder: function(e: Event): boolean { + var currentlyFocusedTextInput = TextInputState.currentlyFocusedField(); + + if (this.props.keyboardShouldPersistTaps === 'handled' && + currentlyFocusedTextInput != null && + e.target !== currentlyFocusedTextInput) { + return true; + } return false; }, @@ -190,7 +198,10 @@ var ScrollResponderMixin = { scrollResponderHandleStartShouldSetResponderCapture: function(e: Event): boolean { // First see if we want to eat taps while the keyboard is up var currentlyFocusedTextInput = TextInputState.currentlyFocusedField(); - if (!this.props.keyboardShouldPersistTaps && + var {keyboardShouldPersistTaps} = this.props; + var keyboardNeverPersistTaps = !keyboardShouldPersistTaps || + keyboardShouldPersistTaps === 'never'; + if (keyboardNeverPersistTaps && currentlyFocusedTextInput != null && !isTagInstanceOfTextInput(e.target)) { return true; @@ -250,7 +261,8 @@ var ScrollResponderMixin = { // By default scroll views will unfocus a textField // if another touch occurs outside of it var currentlyFocusedTextInput = TextInputState.currentlyFocusedField(); - if (!this.props.keyboardShouldPersistTaps && + if (this.props.keyboardShouldPersistTaps !== true && + this.props.keyboardShouldPersistTaps !== 'always' && currentlyFocusedTextInput != null && e.target !== currentlyFocusedTextInput && !this.state.observedScrollSinceBecomingResponder && @@ -481,6 +493,13 @@ var ScrollResponderMixin = { * The `keyboardWillShow` is called before input focus. */ componentWillMount: function() { + var {keyboardShouldPersistTaps} = this.props; + warning( + typeof keyboardShouldPersistTaps !== 'boolean', + `'keyboardShouldPersistTaps={${keyboardShouldPersistTaps}}' is deprecated. ` + + `Use 'keyboardShouldPersistTaps="${keyboardShouldPersistTaps ? "always" : "never"}"' instead` + ); + this.keyboardWillOpenTo = null; this.additionalScrollOffset = 0; this.addListenerOn(Keyboard, 'keyboardWillShow', this.scrollResponderKeyboardWillShow); diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index 3f39492a4fd..a53ff84ec9f 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -192,12 +192,18 @@ const ScrollView = React.createClass({ 'on-drag', ]), /** - * When false, tapping outside of the focused text input when the keyboard - * is up dismisses the keyboard. When true, the keyboard will not dismiss - * automatically, and the scroll view will not catch taps, but children of - * the scroll view can catch taps. The default value is false. + * Determines when the keyboard should stay visible after a tap. + * + * - 'never' (the default), tapping outside of the focused text input when the keyboard + * is up dismisses the keyboard. When this happens, children won't receive the tap. + * - 'always', the keyboard will not dismiss automatically, and the scroll view will not + * catch taps, but children of the scroll view can catch taps. + * - 'handled', the keyboard will not dismiss automatically when the tap was handled by + * a children, (or captured by an ancestor). + * - false, deprecated, use 'never' instead + * - true, deprecated, use 'always' instead */ - keyboardShouldPersistTaps: PropTypes.bool, + keyboardShouldPersistTaps: PropTypes.oneOf(['always', 'never', 'handled', false, true]), /** * The maximum allowed zoom scale. The default value is 1.0. * @platform ios