From 48a788b9eab8477afe322419d2e39806b1f369ce Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Fri, 16 Dec 2022 17:49:17 +0100 Subject: [PATCH] fix: scroll to TextInput onOpen --- package.json | 2 +- src/index.js | 33 +++++++++++++++++++++++++++++++++ yarn.lock | 8 ++++---- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4ba12a73..c39623b3 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "lodash.isequal": "^4.5.0" }, "devDependencies": { + "@react-native-picker/picker": ">=2.1.0", "@types/react-native": "^0.60.22", "babel-jest": "^23.6.0", "babel-preset-react-native": "^4.0.1", @@ -42,7 +43,6 @@ "react": "16.6.1", "react-dom": "^16.6.1", "react-native": "0.57.7", - "@react-native-picker/picker": ">=2.1.0", "react-test-renderer": "^16.6.1" }, "peerDependencies": { diff --git a/src/index.js b/src/index.js index bcbc93e8..5861855d 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,11 @@ import PropTypes from 'prop-types'; import isEqual from 'lodash.isequal'; import { Picker } from '@react-native-picker/picker'; import { defaultStyles } from './styles'; +import { Dimensions } from 'react-native'; + +// Measuring the modal before rendering is not working reliably, so we need to hardcode the height +// This height was tested thoroughly on several iPhone Models (from iPhone 8 to 14 Pro) +const IOS_MODAL_HEIGHT = 262; export default class RNPickerSelect extends PureComponent { static propTypes = { @@ -31,6 +36,8 @@ export default class RNPickerSelect extends PureComponent { onOpen: PropTypes.func, useNativeAndroidPickerStyle: PropTypes.bool, fixAndroidTouchableBug: PropTypes.bool, + scrollViewRef: PropTypes.any, + scrollViewContentOffsetY: PropTypes.number, // Custom Modal props (iOS only) doneText: PropTypes.string, @@ -137,6 +144,7 @@ export default class RNPickerSelect extends PureComponent { this.onValueChange = this.onValueChange.bind(this); this.onOrientationChange = this.onOrientationChange.bind(this); this.setInputRef = this.setInputRef.bind(this); + this.scrollToInput = this.scrollToInput.bind(this); this.togglePicker = this.togglePicker.bind(this); this.renderInputAccessoryView = this.renderInputAccessoryView.bind(this); } @@ -214,12 +222,37 @@ export default class RNPickerSelect extends PureComponent { return {}; } + scrollToInput() { + if ( + this.props.scrollViewRef == null || + this.props.scrollViewContentOffsetY == null || + this.inputRef == null + ) { + return; + } + + this.inputRef.measureInWindow((_x, y, _width, height) => { + // Bottom y-position of TextInput on screen + const textInputBottomY = y + height; + // Top y-position of picker modal on screen + const modalY = Dimensions.get('window').height - IOS_MODAL_HEIGHT; + + // If TextInput is below picker modal, scroll up + if (textInputBottomY > modalY) { + this.props.scrollViewRef.current.scrollTo({ + y: textInputBottomY - modalY + this.props.scrollViewContentOffsetY, + }); + } + }); + } + triggerOpenCloseCallbacks() { const { onOpen, onClose } = this.props; const { showPicker } = this.state; if (!showPicker && onOpen) { onOpen(); + this.scrollToInput(); } if (showPicker && onClose) { diff --git a/yarn.lock b/yarn.lock index 57aff63b..8c6cfe18 100644 --- a/yarn.lock +++ b/yarn.lock @@ -780,10 +780,10 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" -"@react-native-picker/picker@^1.8.3": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-1.8.3.tgz#fcbf969a4add749fc37ef064a5eb55eadc93db39" - integrity sha512-zfr8k9L5BJVN7fIrmrto1cCptZjkGoiKWeZTsCR+XormQnWj0Tqrv0S9Ni3SvdT5JZ2OAQ9H+edMRSUvrAxwQA== +"@react-native-picker/picker@>=2.1.0": + version "2.4.8" + resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-2.4.8.tgz#a1a21f3d6ecadedbc3f0b691a444ddd7baa081f8" + integrity sha512-5NQ5XPo1B03YNqKFrV6h9L3CQaHlB80wd4ETHUEABRP2iLh7FHLVObX2GfziD+K/VJb8G4KZcZ23NFBFP1f7bg== "@types/minimatch@^3.0.3": version "3.0.3"