1
- import { useRef , useEffect , useState } from 'react' ;
1
+ import { useRef , useEffect , useState , MutableRefObject } from 'react' ;
2
2
import {
3
3
ScrollView ,
4
4
UIManager ,
@@ -14,8 +14,6 @@ import {
14
14
useWindowDimensions ,
15
15
} from 'react-native' ;
16
16
17
- import { useLayout } from './utils/useLayout' ;
18
-
19
17
const _keyboardWillShow : KeyboardEventName = Platform . select ( {
20
18
ios : 'keyboardWillShow' ,
21
19
android : 'keyboardDidShow' ,
@@ -28,26 +26,23 @@ const _keyboardWillHide: KeyboardEventName = Platform.select({
28
26
} ) ;
29
27
30
28
export interface Options {
29
+ ref ?: MutableRefObject < ScrollView > ;
31
30
extraScrollHeight ?: number ;
32
31
keyboardOpeningTime ?: number ;
33
32
}
34
33
35
34
export default function useInputScrollHandler ( options : Options = { } ) {
36
35
// Refs
37
- const ref = useRef < ScrollView > ( null ) ;
36
+ const ref = useRef < ScrollView > ( options . ref ?. current ?? null ) ;
38
37
const offset = useRef < NativeScrollPoint > ( ) ;
39
38
const handledOffset = useRef < NativeScrollPoint > ( ) ;
40
39
41
40
// Window
42
41
const screen = useWindowDimensions ( ) ;
43
42
44
- // Layout
45
- const { onLayout, ...layout } = useLayout ( ) ;
46
-
47
43
// Variables
48
44
const extraScrollHeight = options ?. extraScrollHeight ?? 0 ;
49
45
const keyboardOpeningTime = options ?. keyboardOpeningTime ?? 250 ;
50
- const bottomSpace = screen . height - ( layout . y + layout . height ) ;
51
46
52
47
// States
53
48
const [ keyboardSpace , setKeyboardSpace ] = useState ( 0 ) ;
@@ -59,23 +54,26 @@ export default function useInputScrollHandler(options: Options = {}) {
59
54
return responder ;
60
55
} ;
61
56
62
- const scrollToFocusedInput = ( reactNode : any ) => {
57
+ const scrollToFocusedInput = ( reactNode : any , additionalOffset : number ) => {
63
58
setTimeout ( ( ) => {
64
59
const responder = getScrollResponder ( ) ;
65
60
66
61
responder &&
67
62
responder . scrollResponderScrollNativeHandleToKeyboard (
68
63
reactNode ,
69
- extraScrollHeight ,
70
- true
64
+ additionalOffset + extraScrollHeight ,
65
+ false
71
66
) ;
72
67
} , keyboardOpeningTime ) ;
73
68
} ;
74
69
75
- const scrollToFocusedInputWithNodeHandle = ( nodeID : number ) => {
70
+ const scrollToFocusedInputWithNodeHandle = (
71
+ nodeID : number ,
72
+ additionalOffset : number
73
+ ) => {
76
74
const reactNode = findNodeHandle ( nodeID ) ;
77
75
78
- scrollToFocusedInput ( reactNode ) ;
76
+ scrollToFocusedInput ( reactNode , additionalOffset ) ;
79
77
} ;
80
78
81
79
const handleOnScroll = ( e : NativeSyntheticEvent < NativeScrollEvent > ) => {
@@ -90,47 +88,72 @@ export default function useInputScrollHandler(options: Options = {}) {
90
88
: TextInput . State . currentlyFocusedField ( ) ;
91
89
const responder = getScrollResponder ( ) ;
92
90
93
- setKeyboardSpace (
94
- e . endCoordinates . height + ( options . extraScrollHeight ?? 0 ) - bottomSpace
95
- ) ;
96
-
97
91
if ( ! currentlyFocusedField || ! responder ) {
98
92
return ;
99
93
}
100
94
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
+ } ) ;
120
150
}
121
- } else {
122
- responder . scrollTo ( {
123
- y : ( offset . current ?. y ?? 0 ) + extraScrollHeight ,
124
- animated : true ,
125
- } ) ;
126
151
}
127
- }
128
- ) ;
129
- }
152
+ ) ;
153
+ }
154
+ ) ;
130
155
}
131
156
) ;
132
-
133
- handledOffset . current = offset . current ;
134
157
} ;
135
158
136
159
const handleResetKeyboardSpace : KeyboardEventListener = ( ) => {
@@ -165,7 +188,6 @@ export default function useInputScrollHandler(options: Options = {}) {
165
188
contentContainerStyle : { paddingBottom : keyboardSpace } ,
166
189
onScroll : handleOnScroll ,
167
190
scrollEventThrottle : 16 ,
168
- onLayout,
169
191
} ,
170
192
keyboardSpace,
171
193
} ;
0 commit comments