Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 78121ce

Browse filesBrowse files
committed
fix: fix somme issues when scroll view not fitting all screen
1 parent 17c971b commit 78121ce
Copy full SHA for 78121ce

File tree

6 files changed

+147
-126
lines changed
Filter options

6 files changed

+147
-126
lines changed

‎README.md

Copy file name to clipboardExpand all lines: README.md
+15-3
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,31 @@ A react-native hook that handle auto-scroll when input is focused and behind key
55
## Installation
66

77
```sh
8-
npm install react-native-use-input-scroll-handler
8+
yarn add react-native-use-input-scroll-handler
99
```
1010

1111
## Usage
1212

1313
```js
14-
import UseInputScrollHandler from "react-native-use-input-scroll-handler";
14+
import useInputScrollHandler from "react-native-use-input-scroll-handler";
1515

1616
// ...
1717

18-
const result = await UseInputScrollHandler.multiply(3, 7);
18+
const { scrollHandler } = useInputScrollHandler();
19+
20+
<ScrollView {...scrollHandler}>
1921
```
2022

23+
## API
24+
25+
Custom options can be passed to this hook.
26+
27+
| **Option** | **Type** | **Description** |
28+
| --------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------- |
29+
| `ref` | `MutableRefObject` | Use this if you need to use your own scroll view ref. |
30+
| `extraScrollHeight` | `number` | Adds an extra offset to the keyboard. Useful if you want to stick elements above the keyboard. |
31+
| `keyboardOpeningTime` | `number` | Sets the delay time before scrolling to new position, default is 250 |
32+
2133
## Contributing
2234

2335
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.

‎example/src/App.tsx

Copy file name to clipboardExpand all lines: example/src/App.tsx
+41-46
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,59 @@
11
import * as React from 'react';
2-
3-
import { StyleSheet, ScrollView, View, TextInput } from 'react-native';
2+
import { StyleSheet, ScrollView, SafeAreaView } from 'react-native';
43
import useInputScrollHandler from 'react-native-use-input-scroll-handler';
54

5+
import Input from './atoms/Input';
6+
67
export default function App() {
78
const { scrollHandler } = useInputScrollHandler();
89

910
return (
10-
<View style={styles.container}>
11-
<ScrollView style={styles.container} {...scrollHandler}>
12-
<TextInput placeholder="Test" />
13-
<TextInput placeholder="Test" />
14-
<TextInput placeholder="Test" />
15-
<TextInput placeholder="Test" />
16-
<TextInput placeholder="Test" />
17-
<TextInput placeholder="Test" />
18-
<TextInput placeholder="Test" />
19-
<TextInput placeholder="Test" />
20-
<TextInput placeholder="Test" />
21-
<TextInput placeholder="Test" />
22-
<TextInput placeholder="Test" />
23-
<TextInput placeholder="Test" />
24-
<TextInput placeholder="Test" />
25-
<TextInput placeholder="Test" />
26-
<TextInput placeholder="Test" />
27-
<TextInput placeholder="Test" />
28-
<TextInput placeholder="Test" />
29-
<TextInput placeholder="Test" />
30-
<TextInput placeholder="Test" />
31-
<TextInput placeholder="Test" />
32-
<TextInput placeholder="Test" />
33-
<TextInput placeholder="Test" />
34-
<TextInput placeholder="Test" />
35-
<TextInput placeholder="Test" />
36-
<TextInput placeholder="Test" />
37-
<TextInput placeholder="Test" />
38-
<TextInput placeholder="Test" />
39-
<TextInput placeholder="Test" />
40-
<TextInput placeholder="Test" />
41-
<TextInput placeholder="Test" />
42-
<TextInput placeholder="Test" />
43-
<TextInput placeholder="Test" />
44-
<TextInput placeholder="Test" />
45-
<TextInput placeholder="Test" />
46-
<TextInput placeholder="Test" />
47-
<TextInput placeholder="Test" />
48-
<TextInput placeholder="Test" />
49-
<TextInput placeholder="Test" />
50-
<TextInput placeholder="Test" />
51-
<TextInput placeholder="Test" />
11+
<SafeAreaView style={styles.container}>
12+
<ScrollView style={styles.scrollView} {...scrollHandler}>
13+
<Input placeholder="Test" />
14+
<Input placeholder="Test" />
15+
<Input placeholder="Test" />
16+
<Input placeholder="Test" />
17+
<Input placeholder="Test" />
18+
<Input placeholder="Test" />
19+
<Input placeholder="Test" />
20+
<Input placeholder="Test" />
21+
<Input placeholder="Test" />
22+
<Input placeholder="Test" />
23+
<Input placeholder="Test" />
24+
<Input placeholder="Test" />
25+
<Input placeholder="Test" />
26+
<Input placeholder="Test" />
27+
<Input placeholder="Test" />
28+
<Input placeholder="Test" />
29+
<Input placeholder="Test" />
30+
<Input placeholder="Test" />
31+
<Input placeholder="Test" />
32+
<Input placeholder="Test" />
33+
<Input placeholder="Test" />
34+
<Input placeholder="Test" />
35+
<Input placeholder="Test" />
36+
<Input placeholder="Test" />
37+
<Input placeholder="Test" />
38+
<Input placeholder="Test" />
39+
<Input placeholder="Test" />
40+
<Input placeholder="Test" />
41+
<Input placeholder="Test" />
42+
<Input placeholder="Test" />
43+
<Input placeholder="Test" />
44+
<Input placeholder="Test" />
5245
</ScrollView>
53-
<View style={styles.footer} />
54-
</View>
46+
</SafeAreaView>
5547
);
5648
}
5749

5850
const styles = StyleSheet.create({
5951
container: {
6052
flex: 1,
6153
},
54+
scrollView: {
55+
flex: 1,
56+
},
6257
box: {
6358
width: 60,
6459
height: 60,

‎example/src/atoms/Input.tsx

Copy file name to clipboard
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React, { ComponentProps } from 'react';
2+
import { TextInput, StyleSheet } from 'react-native';
3+
4+
const styles = StyleSheet.create({
5+
input: {
6+
height: 40,
7+
borderWidth: 1,
8+
borderColor: 'grey',
9+
borderRadius: 5,
10+
padding: 10,
11+
marginHorizontal: 16,
12+
marginBottom: 8,
13+
},
14+
});
15+
16+
interface Props extends ComponentProps<typeof TextInput> {}
17+
18+
const Input = ({ style, ...rest }: Props) => (
19+
<TextInput {...rest} style={[styles.input, style]} />
20+
);
21+
22+
export default Input;

‎src/__tests__/useLayout.test.ts

Copy file name to clipboardExpand all lines: src/__tests__/useLayout.test.ts
-14
This file was deleted.

‎src/index.ts

Copy file name to clipboardExpand all lines: src/index.ts
+69-47
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useRef, useEffect, useState } from 'react';
1+
import { useRef, useEffect, useState, MutableRefObject } from 'react';
22
import {
33
ScrollView,
44
UIManager,
@@ -14,8 +14,6 @@ import {
1414
useWindowDimensions,
1515
} from 'react-native';
1616

17-
import { useLayout } from './utils/useLayout';
18-
1917
const _keyboardWillShow: KeyboardEventName = Platform.select({
2018
ios: 'keyboardWillShow',
2119
android: 'keyboardDidShow',
@@ -28,26 +26,23 @@ const _keyboardWillHide: KeyboardEventName = Platform.select({
2826
});
2927

3028
export interface Options {
29+
ref?: MutableRefObject<ScrollView>;
3130
extraScrollHeight?: number;
3231
keyboardOpeningTime?: number;
3332
}
3433

3534
export default function useInputScrollHandler(options: Options = {}) {
3635
// Refs
37-
const ref = useRef<ScrollView>(null);
36+
const ref = useRef<ScrollView>(options.ref?.current ?? null);
3837
const offset = useRef<NativeScrollPoint>();
3938
const handledOffset = useRef<NativeScrollPoint>();
4039

4140
// Window
4241
const screen = useWindowDimensions();
4342

44-
// Layout
45-
const { onLayout, ...layout } = useLayout();
46-
4743
// Variables
4844
const extraScrollHeight = options?.extraScrollHeight ?? 0;
4945
const keyboardOpeningTime = options?.keyboardOpeningTime ?? 250;
50-
const bottomSpace = screen.height - (layout.y + layout.height);
5146

5247
// States
5348
const [keyboardSpace, setKeyboardSpace] = useState(0);
@@ -59,23 +54,26 @@ export default function useInputScrollHandler(options: Options = {}) {
5954
return responder;
6055
};
6156

62-
const scrollToFocusedInput = (reactNode: any) => {
57+
const scrollToFocusedInput = (reactNode: any, additionalOffset: number) => {
6358
setTimeout(() => {
6459
const responder = getScrollResponder();
6560

6661
responder &&
6762
responder.scrollResponderScrollNativeHandleToKeyboard(
6863
reactNode,
69-
extraScrollHeight,
70-
true
64+
additionalOffset + extraScrollHeight,
65+
false
7166
);
7267
}, keyboardOpeningTime);
7368
};
7469

75-
const scrollToFocusedInputWithNodeHandle = (nodeID: number) => {
70+
const scrollToFocusedInputWithNodeHandle = (
71+
nodeID: number,
72+
additionalOffset: number
73+
) => {
7674
const reactNode = findNodeHandle(nodeID);
7775

78-
scrollToFocusedInput(reactNode);
76+
scrollToFocusedInput(reactNode, additionalOffset);
7977
};
8078

8179
const handleOnScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
@@ -90,47 +88,72 @@ export default function useInputScrollHandler(options: Options = {}) {
9088
: TextInput.State.currentlyFocusedField();
9189
const responder = getScrollResponder();
9290

93-
setKeyboardSpace(
94-
e.endCoordinates.height + (options.extraScrollHeight ?? 0) - bottomSpace
95-
);
96-
9791
if (!currentlyFocusedField || !responder) {
9892
return;
9993
}
10094

101-
// @ts-ignore
102-
UIManager.viewIsDescendantOf(
103-
currentlyFocusedField,
104-
responder.getInnerViewNode(),
105-
(isAncestor: boolean) => {
106-
if (isAncestor) {
107-
UIManager.measureInWindow(
108-
currentlyFocusedField,
109-
(_x: number, y: number, _width: number, height: number) => {
110-
const textInputBottomPosition = y + height;
111-
const keyboardPosition = e.endCoordinates.screenY;
112-
const totalExtraHeight = options.extraScrollHeight ?? 0;
113-
114-
if (Platform.OS === 'ios') {
115-
if (
116-
textInputBottomPosition >
117-
keyboardPosition - totalExtraHeight
118-
) {
119-
scrollToFocusedInputWithNodeHandle(currentlyFocusedField);
95+
handledOffset.current = offset.current;
96+
97+
// Calculate scroll view position
98+
UIManager.measure(
99+
responder.getScrollableNode(),
100+
(
101+
_scrollX: number,
102+
_scrollY: number,
103+
_scrollWidth: number,
104+
scrollHeight: number,
105+
_scrollPageX: number,
106+
scrollPageY: number
107+
) => {
108+
const bottomSpace = screen.height - (scrollPageY + scrollHeight);
109+
110+
setKeyboardSpace(
111+
e.endCoordinates.height + extraScrollHeight - bottomSpace
112+
);
113+
114+
// @ts-ignore
115+
UIManager.viewIsDescendantOf(
116+
currentlyFocusedField,
117+
responder.getInnerViewNode(),
118+
(isAncestor: boolean) => {
119+
if (!isAncestor) {
120+
return;
121+
}
122+
123+
UIManager.measureInWindow(
124+
currentlyFocusedField,
125+
(
126+
_inputX: number,
127+
inputY: number,
128+
_inputWidth: number,
129+
inputHeight: number
130+
) => {
131+
const textInputBottomPosition = inputY + inputHeight;
132+
const keyboardPosition = e.endCoordinates.screenY;
133+
const totalExtraHeight = extraScrollHeight;
134+
135+
if (Platform.OS === 'ios') {
136+
if (
137+
textInputBottomPosition >
138+
keyboardPosition - totalExtraHeight
139+
) {
140+
scrollToFocusedInputWithNodeHandle(
141+
currentlyFocusedField,
142+
scrollPageY
143+
);
144+
}
145+
} else {
146+
responder.scrollTo({
147+
y: (offset.current?.y ?? 0) + extraScrollHeight,
148+
animated: true,
149+
});
120150
}
121-
} else {
122-
responder.scrollTo({
123-
y: (offset.current?.y ?? 0) + extraScrollHeight,
124-
animated: true,
125-
});
126151
}
127-
}
128-
);
129-
}
152+
);
153+
}
154+
);
130155
}
131156
);
132-
133-
handledOffset.current = offset.current;
134157
};
135158

136159
const handleResetKeyboardSpace: KeyboardEventListener = () => {
@@ -165,7 +188,6 @@ export default function useInputScrollHandler(options: Options = {}) {
165188
contentContainerStyle: { paddingBottom: keyboardSpace },
166189
onScroll: handleOnScroll,
167190
scrollEventThrottle: 16,
168-
onLayout,
169191
},
170192
keyboardSpace,
171193
};

‎src/utils/useLayout.ts

Copy file name to clipboardExpand all lines: src/utils/useLayout.ts
-16
This file was deleted.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.