-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
index.ios.js
162 lines (138 loc) · 4.69 KB
/
index.ios.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/**
* External dependencies
*/
import { ScrollView, FlatList, useWindowDimensions } from 'react-native';
import Animated, {
useAnimatedScrollHandler,
useSharedValue,
} from 'react-native-reanimated';
/**
* WordPress dependencies
*/
import { useCallback, useEffect, useRef } from '@wordpress/element';
import { useThrottle } from '@wordpress/compose';
/**
* Internal dependencies
*/
import useTextInputOffset from './use-text-input-offset';
import useKeyboardOffset from './use-keyboard-offset';
import useScrollToTextInput from './use-scroll-to-text-input';
import useTextInputCaretPosition from './use-text-input-caret-position';
const AnimatedScrollView = Animated.createAnimatedComponent( ScrollView );
/**
* React component that provides a FlatList that is aware of the keyboard state and can scroll
* to the currently focused TextInput.
*
* @param {Object} props Component props.
* @param {number} props.extraScrollHeight Extra scroll height for the content.
* @param {Function} props.innerRef Function to pass the ScrollView ref to the parent component.
* @param {Function} props.onScroll Function to be called when the list is scrolled.
* @param {boolean} props.scrollEnabled Whether the list can be scrolled.
* @param {Object} props.scrollViewStyle Additional style for the ScrollView component.
* @param {boolean} props.shouldPreventAutomaticScroll Whether to prevent scrolling when there's a Keyboard offset set.
* @param {Object} props... Other props to pass to the FlatList component.
* @return {WPComponent} KeyboardAwareFlatList component.
*/
export const KeyboardAwareFlatList = ( {
extraScrollHeight,
innerRef,
onScroll,
scrollEnabled,
scrollViewStyle,
shouldPreventAutomaticScroll,
...props
} ) => {
const scrollViewRef = useRef();
const scrollViewMeasurements = useRef();
const scrollViewYOffset = useSharedValue( -1 );
const { height: windowHeight, width: windowWidth } = useWindowDimensions();
const isLandscape = windowWidth >= windowHeight;
const [ keyboardOffset ] = useKeyboardOffset(
scrollEnabled,
shouldPreventAutomaticScroll
);
const [ currentCaretData ] = useTextInputCaretPosition( scrollEnabled );
const [ getTextInputOffset ] = useTextInputOffset(
scrollEnabled,
scrollViewRef
);
const [ scrollToTextInputOffset ] = useScrollToTextInput(
extraScrollHeight,
keyboardOffset,
scrollEnabled,
scrollViewMeasurements,
scrollViewRef,
scrollViewYOffset
);
const onScrollToTextInput = useThrottle(
useCallback(
async ( caret ) => {
const textInputOffset = await getTextInputOffset( caret );
const hasTextInputOffset = textInputOffset !== null;
if ( hasTextInputOffset ) {
scrollToTextInputOffset( caret, textInputOffset );
}
},
[ getTextInputOffset, scrollToTextInputOffset ]
),
200,
{ leading: false }
);
useEffect( () => {
onScrollToTextInput( currentCaretData );
}, [ currentCaretData, onScrollToTextInput ] );
// When the orientation changes, the ScrollView measurements
// need to be re-calculated.
useEffect( () => {
scrollViewMeasurements.current = null;
}, [ isLandscape ] );
const scrollHandler = useAnimatedScrollHandler( {
onScroll: ( event ) => {
const { contentOffset } = event;
scrollViewYOffset.value = contentOffset.y;
onScroll( event );
},
} );
const measureScrollView = useCallback( () => {
if ( scrollViewRef.current ) {
const scrollRef = scrollViewRef.current.getNativeScrollRef();
scrollRef.measureInWindow( ( _x, y, width, height ) => {
scrollViewMeasurements.current = { y, width, height };
} );
}
}, [] );
const onContentSizeChange = useCallback( () => {
onScrollToTextInput( currentCaretData );
// Sets the first values when the content size changes.
if ( ! scrollViewMeasurements.current ) {
measureScrollView();
}
}, [ measureScrollView, onScrollToTextInput, currentCaretData ] );
const getRef = useCallback(
( ref ) => {
scrollViewRef.current = ref;
innerRef( ref );
},
[ innerRef ]
);
// Adds content insets when the keyboard is opened to have
// extra padding at the bottom.
const contentInset = { bottom: keyboardOffset };
const style = [ { flex: 1 }, scrollViewStyle ];
return (
<AnimatedScrollView
automaticallyAdjustContentInsets={ false }
contentInset={ contentInset }
keyboardShouldPersistTaps="handled"
onContentSizeChange={ onContentSizeChange }
onScroll={ scrollHandler }
ref={ getRef }
scrollEnabled={ scrollEnabled }
scrollEventThrottle={ 16 }
style={ style }
>
<FlatList { ...props } />
</AnimatedScrollView>
);
};
export default KeyboardAwareFlatList;