Skip to content

Instantly share code, notes, and snippets.

@janicduplessis
Created April 23, 2020 20:16
Show Gist options
  • Save janicduplessis/cc9f66cfbfbaa1a093789016f2990998 to your computer and use it in GitHub Desktop.
Save janicduplessis/cc9f66cfbfbaa1a093789016f2990998 to your computer and use it in GitHub Desktop.
import * as React from 'react';
import {
EmitterSubscription,
Keyboard,
KeyboardEvent,
LayoutAnimation,
Platform,
Dimensions,
ScreenRect,
} from 'react-native';
import { Box, BoxProps } from '../Box';
function subscribeToKeyboardEvents(
onKeyboardChange: (e: KeyboardEvent) => void,
): { remove: () => void } {
let subscriptions: EmitterSubscription[];
if (Platform.OS === 'ios') {
subscriptions = [
Keyboard.addListener('keyboardWillChangeFrame', onKeyboardChange),
];
} else {
subscriptions = [
Keyboard.addListener('keyboardDidHide', onKeyboardChange),
Keyboard.addListener('keyboardDidShow', onKeyboardChange),
];
}
return {
remove: () => {
subscriptions.forEach((s) => s.remove());
},
};
}
/**
* Manages the current value of the keyboard frame. This is useful in the
* case the keyboard avoiding view is mounted while the keyboard is already
* opened.
*/
class KeyboardManager {
currentFrame: ScreenRect = {
screenX: 0,
screenY: Dimensions.get('window').height,
width: 0,
height: 0,
};
constructor() {
subscribeToKeyboardEvents(this._handleKeyboardEvent);
}
_handleKeyboardEvent = (event: KeyboardEvent) => {
this.currentFrame = event.endCoordinates;
};
}
const globalKeyboardManager = new KeyboardManager();
function getVisibleHeight(endCoordinates: ScreenRect) {
return Dimensions.get('window').height - endCoordinates.screenY;
}
type KeyboardAvoidingViewProps = BoxProps & {
enabled?: boolean;
};
/**
* Simpler and hopefully less buggy version of RN KeyboardAvoidingView.
* To work better it must assume the view covers the bottom of the screen.
*/
export function KeyboardAvoidingView({
enabled = true,
...others
}: KeyboardAvoidingViewProps) {
const [bottom, setBottom] = React.useState(
getVisibleHeight(globalKeyboardManager.currentFrame),
);
const lastBottomRef = React.useRef(bottom);
lastBottomRef.current = bottom;
React.useLayoutEffect(() => {
const onKeyboardChange = (event: KeyboardEvent) => {
const { duration, easing, endCoordinates } = event;
const visibleHeight = getVisibleHeight(endCoordinates);
if (lastBottomRef.current === visibleHeight) {
return;
}
if (duration && easing) {
LayoutAnimation.configureNext({
duration: duration > 10 ? duration : 10,
update: {
duration: duration > 10 ? duration : 10,
type: LayoutAnimation.Types[easing] || 'keyboard',
},
});
}
setBottom(visibleHeight);
};
const subscription = subscribeToKeyboardEvents(onKeyboardChange);
return () => {
subscription.remove();
};
}, []);
// TODO: Either remove padding props or add padding to the bottom state.
return <Box {...others} pb={`${enabled ? bottom : 0}px`} />;
}
@todorone
Copy link

@janicduplessis I wonder, what's in Box? 😛

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment