1- import { useRef , useEffect , useState } from 'react' ;
1+ import { useRef , useEffect , useState , MutableRefObject } from 'react' ;
22import {
33 ScrollView ,
44 UIManager ,
@@ -14,8 +14,6 @@ import {
1414 useWindowDimensions ,
1515} from 'react-native' ;
1616
17- import { useLayout } from './utils/useLayout' ;
18-
1917const _keyboardWillShow : KeyboardEventName = Platform . select ( {
2018 ios : 'keyboardWillShow' ,
2119 android : 'keyboardDidShow' ,
@@ -28,26 +26,23 @@ const _keyboardWillHide: KeyboardEventName = Platform.select({
2826} ) ;
2927
3028export interface Options {
29+ ref ?: MutableRefObject < ScrollView > ;
3130 extraScrollHeight ?: number ;
3231 keyboardOpeningTime ?: number ;
3332}
3433
3534export 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 } ;
0 commit comments