UNPKG

react-native-ui-lib

Version:

[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct.svg)](https://stand-with-ukraine.pp.ua)

169 lines (144 loc) 5.11 kB
import _ from 'lodash'; import {Component} from 'react'; import PropTypes from 'prop-types'; import ReactNative, {DeviceEventEmitter, Keyboard} from 'react-native'; export default class KeyboardAwareBase extends Component { constructor(props) { super(props); this._bind('_onKeyboardWillShow', '_onKeyboardWillHide', '_addKeyboardEventListeners', '_removeKeyboardListeners', '_scrollToFocusedTextInput', '_onKeyboardAwareViewLayout', '_updateKeyboardAwareViewContentSize', 'scrollToBottom', 'scrollBottomOnNextSizeChange', '_onKeyboardAwareViewScroll'); this.contentSize = undefined; this.layoutSize = undefined; this.state = {keyboardHeight: 0}; this._addKeyboardEventListeners(); } static propTypes = { startScrolledToBottom: PropTypes.bool, scrollToBottomOnKBShow: PropTypes.bool, scrollToInputAdditionalOffset: PropTypes.number }; static defaultProps = { startScrolledToBottom: false, scrollToBottomOnKBShow: false, scrollToInputAdditionalOffset: 75 }; _bind(...methods) { methods.forEach(method => { this[method] = this[method].bind(this); }); } _addKeyboardEventListeners() { const KeyboardEventsObj = Keyboard || DeviceEventEmitter; this.keyboardEventListeners = [ KeyboardEventsObj.addListener('keyboardWillShow', this._onKeyboardWillShow), KeyboardEventsObj.addListener('keyboardWillHide', this._onKeyboardWillHide) ]; } _removeKeyboardListeners() { this.keyboardEventListeners.forEach(eventListener => eventListener.remove()); } componentDidMount() { if (this._keyboardAwareView && this.props.startScrolledToBottom) { this.scrollToBottom(false); setTimeout(() => this._keyboardAwareView.setNativeProps?.({opacity: 1}), 100); } } _onKeyboardAwareViewLayout(layoutEvent) { const layout = layoutEvent.nativeEvent.layout; this.layoutSize = layout; this._keyboardAwareView.contentOffset = {x: 0, y: 0}; this._updateKeyboardAwareViewContentSize(); } _onKeyboardAwareViewScroll(event) { this._keyboardAwareView.contentOffset = event.nativeEvent.contentOffset; this._updateKeyboardAwareViewContentSize(); if (this.props.onScroll) { this.props.onScroll(event); } } _updateKeyboardAwareViewContentSize(width, height) { let heightHasChanged = false; if (width && height) { if (this.contentSize && this.contentSize.height !== height) { // for FlatList heightHasChanged = true; } this.contentSize = {width, height}; } if (this._keyboardAwareView) { if (this.state.scrollBottomOnNextSizeChange || (this.props.startScrolledToBottom && heightHasChanged)) { this.scrollToBottom(); this.setState({scrollBottomOnNextSizeChange: false}); } } } componentWillUnmount() { this._removeKeyboardListeners(); } _scrollToFocusedTextInput() { if (this.props.getTextInputRefs) { const textInputRefs = this.props.getTextInputRefs(); textInputRefs.some((textInputRef) => { const isFocusedFunc = textInputRef.isFocused(); const isFocused = isFocusedFunc && typeof isFocusedFunc === 'function' ? isFocusedFunc() : isFocusedFunc; if (isFocused) { setTimeout(() => { this._keyboardAwareView .getScrollResponder() .scrollResponderScrollNativeHandleToKeyboard(ReactNative.findNodeHandle(textInputRef), this.props.scrollToInputAdditionalOffset, true); }, 0); } return isFocused; }); } } _onKeyboardWillShow(event) { this._scrollToFocusedTextInput(); const newKeyboardHeight = event.endCoordinates.height; if (this.state.keyboardHeight === newKeyboardHeight) { return; } this.setState({keyboardHeight: newKeyboardHeight}); if (this.props.scrollToBottomOnKBShow) { this.scrollToBottom(); } } _onKeyboardWillHide() { const keyboardHeight = this.state.keyboardHeight; this.setState({keyboardHeight: 0}); const hasYOffset = this._keyboardAwareView?.contentOffset?.y !== undefined; const yOffset = hasYOffset ? Math.max(this._keyboardAwareView.contentOffset.y - keyboardHeight, 0) : 0; this.scrollTo({x: 0, y: yOffset, animated: true}); } scrollBottomOnNextSizeChange() { this.setState({scrollBottomOnNextSizeChange: true}); } scrollToBottom(scrollAnimated = true) { if (this._keyboardAwareView) { if (!this.contentSize || !this.layoutSize) { setTimeout(() => this.scrollToBottom(scrollAnimated), 50); return; } this._keyboardAwareView.scrollToEnd({animated: scrollAnimated}); } } scrollTo(options) { if (this._keyboardAwareView) { if (this._keyboardAwareView.scrollTo) { this._keyboardAwareView.scrollTo(options); } else if (this._keyboardAwareView.scrollToOffset) { this._keyboardAwareView.scrollToOffset({...options, offset: _.get(options, 'y', 0)}); } } } }