diff --git a/packages/stack/src/navigators/createStackNavigator.tsx b/packages/stack/src/navigators/createStackNavigator.tsx index 6676ce86..6b3579bd 100644 --- a/packages/stack/src/navigators/createStackNavigator.tsx +++ b/packages/stack/src/navigators/createStackNavigator.tsx @@ -1,5 +1,5 @@ +import * as React from 'react'; import { StackRouter, createNavigator } from '@react-navigation/core'; -import { createKeyboardAwareNavigator } from '@react-navigation/native'; import { Platform } from 'react-native'; import StackView from '../views/Stack/StackView'; import { @@ -8,6 +8,7 @@ import { NavigationProp, Screen, } from '../types'; +import KeyboardManager from '../views/KeyboardManager'; function createStackNavigator( routeConfigMap: { @@ -26,13 +27,19 @@ function createStackNavigator( ) { const router = StackRouter(routeConfigMap, stackConfig); - // Create a navigator with StackView as the view - let Navigator = createNavigator(StackView, router, stackConfig); - if (!stackConfig.disableKeyboardHandling && Platform.OS !== 'web') { - Navigator = createKeyboardAwareNavigator(Navigator, stackConfig); + if (stackConfig.disableKeyboardHandling || Platform.OS === 'web') { + return createNavigator(StackView, router, stackConfig); } - return Navigator; + return createNavigator( + navigatorProps => ( + + {props => } + + ), + router, + stackConfig + ); } export default createStackNavigator; diff --git a/packages/stack/src/views/KeyboardManager.tsx b/packages/stack/src/views/KeyboardManager.tsx new file mode 100644 index 00000000..01721bb4 --- /dev/null +++ b/packages/stack/src/views/KeyboardManager.tsx @@ -0,0 +1,52 @@ +import * as React from 'react'; +import { TextInput, Keyboard } from 'react-native'; + +type Props = { + children: (props: { + onPageChangeStart: () => void; + onPageChangeConfirm: () => void; + onPageChangeCancel: () => void; + }) => React.ReactNode; +}; + +export default class KeyboardManager extends React.Component { + // Numeric id of the previously focused text input + // When a gesture didn't change the tab, we can restore the focused input with this + private previouslyFocusedTextInput: number | null = null; + + private handlePageChangeStart = () => { + const input = TextInput.State.currentlyFocusedField(); + + // When a page change begins, blur the currently focused input + TextInput.State.blurTextInput(input); + + // Store the id of this input so we can refocus it if change was cancelled + this.previouslyFocusedTextInput = input; + }; + + private handlePageChangeConfirm = () => { + Keyboard.dismiss(); + + // Cleanup the ID on successful page change + this.previouslyFocusedTextInput = null; + }; + + private handlePageChangeCancel = () => { + // The page didn't change, we should restore the focus of text input + const input = this.previouslyFocusedTextInput; + + if (input) { + TextInput.State.focusTextInput(input); + } + + this.previouslyFocusedTextInput = null; + }; + + render() { + return this.props.children({ + onPageChangeStart: this.handlePageChangeStart, + onPageChangeConfirm: this.handlePageChangeConfirm, + onPageChangeCancel: this.handlePageChangeCancel, + }); + } +} diff --git a/packages/stack/src/views/Stack/Stack.tsx b/packages/stack/src/views/Stack/Stack.tsx index ddafc88f..3e520a31 100755 --- a/packages/stack/src/views/Stack/Stack.tsx +++ b/packages/stack/src/views/Stack/Stack.tsx @@ -46,13 +46,9 @@ type Props = { renderHeader: (props: HeaderContainerProps) => React.ReactNode; renderScene: (props: { route: Route }) => React.ReactNode; headerMode: HeaderMode; - onTransitionStart?: ( - current: { index: number }, - previous: { index: number } - ) => void; - onGestureBegin?: () => void; - onGestureCanceled?: () => void; - onGestureEnd?: () => void; + onPageChangeStart?: () => void; + onPageChangeConfirm?: () => void; + onPageChangeCancel?: () => void; }; type State = { @@ -258,20 +254,10 @@ export default class Stack extends React.Component { })); }; - private handleTransitionStart = ({ - route, - current, - previous, - }: { - route: Route; - current: { index: number }; - previous: { index: number }; - }) => { - const { onTransitionStart, descriptors } = this.props; + private handleTransitionStart = ({ route }: { route: Route }) => { + const { descriptors } = this.props; const descriptor = descriptors[route.key]; - onTransitionStart && onTransitionStart(current, previous); - descriptor && descriptor.options.onTransitionStart && descriptor.options.onTransitionStart(); @@ -300,9 +286,9 @@ export default class Stack extends React.Component { renderHeader, renderScene, headerMode, - onGestureBegin, - onGestureCanceled, - onGestureEnd, + onPageChangeStart, + onPageChangeConfirm, + onPageChangeCancel, } = this.props; const { scenes, layout, progress, floatingHeaderHeights } = this.state; @@ -386,9 +372,9 @@ export default class Stack extends React.Component { cardShadowEnabled={cardShadowEnabled} cardStyle={cardStyle} gesturesEnabled={index !== 0 && getGesturesEnabled({ route })} - onGestureBegin={onGestureBegin} - onGestureCanceled={onGestureCanceled} - onGestureEnd={onGestureEnd} + onPageChangeStart={onPageChangeStart} + onPageChangeConfirm={onPageChangeConfirm} + onPageChangeCancel={onPageChangeCancel} gestureResponseDistance={gestureResponseDistance} floatingHeaderHeight={floatingHeaderHeights[route.key]} hasCustomHeader={header === null} diff --git a/packages/stack/src/views/Stack/StackItem.tsx b/packages/stack/src/views/Stack/StackItem.tsx index 69904b6f..7a2cec83 100644 --- a/packages/stack/src/views/Stack/StackItem.tsx +++ b/packages/stack/src/views/Stack/StackItem.tsx @@ -33,15 +33,11 @@ type Props = TransitionPreset & { onOpenRoute: (props: { route: Route }) => void; onCloseRoute: (props: { route: Route }) => void; onGoBack: (props: { route: Route }) => void; - onTransitionStart?: (props: { - route: Route; - current: { index: number }; - previous: { index: number }; - }) => void; + onTransitionStart?: (props: { route: Route }) => void; onTransitionEnd?: (props: { route: Route }) => void; - onGestureBegin?: () => void; - onGestureCanceled?: () => void; - onGestureEnd?: () => void; + onPageChangeStart?: () => void; + onPageChangeConfirm?: () => void; + onPageChangeCancel?: () => void; gestureResponseDistance?: { vertical?: number; horizontal?: number; @@ -68,15 +64,21 @@ export default class StackItem extends React.PureComponent { }; private handleTransitionStart = ({ closing }: { closing: boolean }) => { - const { index, scene, onTransitionStart, onGoBack } = this.props; + const { + scene, + onTransitionStart, + onPageChangeConfirm, + onPageChangeCancel, + onGoBack, + } = this.props; - onTransitionStart && - onTransitionStart({ - route: scene.route, - previous: { index: closing ? index - 1 : index }, - current: { index }, - }); + if (closing) { + onPageChangeConfirm && onPageChangeConfirm(); + } else { + onPageChangeCancel && onPageChangeCancel(); + } + onTransitionStart && onTransitionStart({ route: scene.route }); closing && onGoBack({ route: scene.route }); }; @@ -96,9 +98,8 @@ export default class StackItem extends React.PureComponent { cardShadowEnabled, cardStyle, gesturesEnabled, - onGestureBegin, - onGestureCanceled, - onGestureEnd, + onPageChangeStart, + onPageChangeCancel, gestureResponseDistance, floatingHeaderHeight, hasCustomHeader, @@ -129,9 +130,8 @@ export default class StackItem extends React.PureComponent { shadowEnabled={cardShadowEnabled} gesturesEnabled={gesturesEnabled} onTransitionStart={this.handleTransitionStart} - onGestureBegin={onGestureBegin} - onGestureCanceled={onGestureCanceled} - onGestureEnd={onGestureEnd} + onGestureBegin={onPageChangeStart} + onGestureCanceled={onPageChangeCancel} gestureResponseDistance={gestureResponseDistance} transitionSpec={transitionSpec} styleInterpolator={cardStyleInterpolator} diff --git a/packages/stack/src/views/Stack/StackView.tsx b/packages/stack/src/views/Stack/StackView.tsx index 8888fb98..393e85c6 100644 --- a/packages/stack/src/views/Stack/StackView.tsx +++ b/packages/stack/src/views/Stack/StackView.tsx @@ -16,13 +16,9 @@ type Props = { navigation: NavigationProp; descriptors: SceneDescriptorMap; navigationConfig: NavigationStackConfig; - onTransitionStart?: ( - current: { index: number }, - previous: { index: number } - ) => void; - onGestureBegin?: () => void; - onGestureCanceled?: () => void; - onGestureEnd?: () => void; + onPageChangeStart?: () => void; + onPageChangeConfirm?: () => void; + onPageChangeCancel?: () => void; screenProps?: unknown; }; @@ -275,10 +271,9 @@ class StackView extends React.Component { const { navigation, navigationConfig, - onTransitionStart, - onGestureBegin, - onGestureCanceled, - onGestureEnd, + onPageChangeStart, + onPageChangeConfirm, + onPageChangeCancel, } = this.props; const { mode = 'card', ...config } = navigationConfig; @@ -298,10 +293,9 @@ class StackView extends React.Component { onGoBack={this.handleGoBack} onOpenRoute={this.handleOpenRoute} onCloseRoute={this.handleCloseRoute} - onTransitionStart={onTransitionStart} - onGestureBegin={onGestureBegin} - onGestureCanceled={onGestureCanceled} - onGestureEnd={onGestureEnd} + onPageChangeStart={onPageChangeStart} + onPageChangeConfirm={onPageChangeConfirm} + onPageChangeCancel={onPageChangeCancel} renderHeader={this.renderHeader} renderScene={this.renderScene} headerMode={headerMode}