Skip to content
This repository has been archived by the owner on Nov 27, 2022. It is now read-only.

Commit

Permalink
feat: improve keyboard handling during tab change
Browse files Browse the repository at this point in the history
Currently the keyboard is dismissed only when a swipe gesture starts. This commit changes the behaviour so that:

- Keyboard is dismissed if the tab index changes
- When a gesture begines, keyboard is dismissed, but if the index didn't change at end of the gesture, it's restored

This is closer to iOS native behaviour and is based on previous work by [@skevy](https://github.com/skevy) in react-navgation:
react-navigation/react-navigation#3951

The old behaviour is left as the 'on-drag' option for backward compatibility.
  • Loading branch information
satya164 committed Jun 27, 2019
1 parent 3cfa23a commit ccfa884
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 17 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ Boolean indicating whether to remove invisible views (such as unfocused screens)

String indicating whether the keyboard gets dismissed in response to a drag gesture. Possible values are:

- `'on-drag'` (default): the keyboard is dismissed when a drag begins.
- `'auto'` (default): the keyboard is dismissed when the index changes.
- `'on-drag'`: the keyboard is dismissed when a drag begins.
- `'none'`: drags do not dismiss the keyboard.

##### `swipeEnabled`
Expand Down
69 changes: 55 additions & 14 deletions src/Pager.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { StyleSheet, Keyboard, I18nManager } from 'react-native';
import { StyleSheet, TextInput, Keyboard, I18nManager } from 'react-native';
import { PanGestureHandler, State } from 'react-native-gesture-handler';
import Animated, { Easing } from 'react-native-reanimated';
import memoize from './memoize';
Expand Down Expand Up @@ -215,6 +215,12 @@ export default class Pager<T extends Route> extends React.Component<Props<T>> {
// Remember to set it before transition needs to occur
private isSwipeGesture: Animated.Value<Binary> = new Value(FALSE);

// Track the index value when a swipe gesture has ended
// This lets us know if a gesture end triggered a tab switch or not
private indexAtSwipeEnd: Animated.Value<number> = new Value(
this.props.navigationState.index
);

// Mappings to some prop values
// We use them in animation calculations, so we need live animated nodes
private routesLength = new Value(this.props.navigationState.routes.length);
Expand Down Expand Up @@ -291,6 +297,10 @@ export default class Pager<T extends Route> extends React.Component<Props<T>> {
// It also needs to be reset right after componentDidUpdate fires
private pendingIndexValue: number | undefined = undefined;

// 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;

// Listeners for the entered screen
private enterListeners: Listener[] = [];

Expand All @@ -301,7 +311,7 @@ export default class Pager<T extends Route> extends React.Component<Props<T>> {
};

private jumpTo = (key: string) => {
const { navigationState } = this.props;
const { navigationState, keyboardDismissMode, onIndexChange } = this.props;

const index = navigationState.routes.findIndex(route => route.key === key);

Expand All @@ -311,7 +321,13 @@ export default class Pager<T extends Route> extends React.Component<Props<T>> {
if (navigationState.index === index) {
this.jumpToIndex(index);
} else {
this.props.onIndexChange(index);
onIndexChange(index);

// When the index changes, the focused input will no longer be in current tab
// So we should dismiss the keyboard
if (keyboardDismissMode === 'auto') {
Keyboard.dismiss();
}
}
};

Expand Down Expand Up @@ -471,19 +487,43 @@ export default class Pager<T extends Route> extends React.Component<Props<T>> {
// Listen to updates for this value only when it changes
// Without `onChange`, this will fire even if the value didn't change
// We don't want to call the listeners if the value didn't change
call([this.isSwiping], ([value]: readonly Binary[]) => {
const { keyboardDismissMode, onSwipeStart, onSwipeEnd } = this.props;

if (value === TRUE) {
onSwipeStart && onSwipeStart();

if (keyboardDismissMode === 'on-drag') {
Keyboard.dismiss();
call(
[this.isSwiping, this.indexAtSwipeEnd, this.index],
([isSwiping, indexAtSwipeEnd, currentIndex]: readonly number[]) => {
const { keyboardDismissMode, onSwipeStart, onSwipeEnd } = this.props;

if (isSwiping === TRUE) {
onSwipeStart && onSwipeStart();

if (keyboardDismissMode === 'auto') {
const input = TextInput.State.currentlyFocusedField();

// When a gesture begins, blur the currently focused input
TextInput.State.blurTextInput(input);

// Store the id of this input so we can refocus it if gesture was cancelled
this.previouslyFocusedTextInput = input;
} else if (keyboardDismissMode === 'on-drag') {
Keyboard.dismiss();
}
} else {
onSwipeEnd && onSwipeEnd();

if (keyboardDismissMode === 'auto') {
if (indexAtSwipeEnd === currentIndex) {
// The index didn't change, we should restore the focus of text input
const input = this.previouslyFocusedTextInput;

if (input) {
TextInput.State.focusTextInput(input);
}
}

this.previouslyFocusedTextInput = null;
}
}
} else {
onSwipeEnd && onSwipeEnd();
}
})
)
),
onChange(
this.nextIndex,
Expand Down Expand Up @@ -518,6 +558,7 @@ export default class Pager<T extends Route> extends React.Component<Props<T>> {
],
[
set(this.isSwiping, FALSE),
set(this.indexAtSwipeEnd, this.index),
this.transitionTo(
cond(
and(
Expand Down
2 changes: 1 addition & 1 deletion src/TabView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default class TabView<T extends Route> extends React.Component<
<TabBar {...props} />
),
renderLazyPlaceholder: () => null,
keyboardDismissMode: 'on-drag',
keyboardDismissMode: 'auto',
swipeEnabled: true,
lazy: false,
lazyPreloadDistance: 0,
Expand Down
2 changes: 1 addition & 1 deletion src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export type EventEmitterProps = {
};

export type PagerCommonProps = {
keyboardDismissMode: 'none' | 'on-drag';
keyboardDismissMode: 'none' | 'on-drag' | 'auto';
swipeEnabled: boolean;
swipeVelocityImpact?: number;
onSwipeStart?: () => void;
Expand Down

0 comments on commit ccfa884

Please sign in to comment.