-
Notifications
You must be signed in to change notification settings - Fork 2.9k
/
BaseAutoCompleteSuggestions.tsx
116 lines (106 loc) · 4.79 KB
/
BaseAutoCompleteSuggestions.tsx
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
import type {ReactElement} from 'react';
import React, {useCallback, useEffect, useRef} from 'react';
import {FlatList} from 'react-native-gesture-handler';
import Animated, {Easing, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
import ColorSchemeWrapper from '@components/ColorSchemeWrapper';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import CONST from '@src/CONST';
import type {AutoCompleteSuggestionsPortalProps} from './AutoCompleteSuggestionsPortal';
import type {RenderSuggestionMenuItemProps} from './types';
type ExternalProps<TSuggestion> = Omit<AutoCompleteSuggestionsPortalProps<TSuggestion>, 'left' | 'bottom'>;
function BaseAutoCompleteSuggestions<TSuggestion>({
highlightedSuggestionIndex = 0,
onSelect,
accessibilityLabelExtractor,
renderSuggestionMenuItem,
suggestions,
keyExtractor,
measuredHeightOfSuggestionRows,
}: ExternalProps<TSuggestion>) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const rowHeight = useSharedValue(0);
const prevRowHeightRef = useRef<number>(measuredHeightOfSuggestionRows);
const fadeInOpacity = useSharedValue(0);
const scrollRef = useRef<FlatList<TSuggestion>>(null);
/**
* Render a suggestion menu item component.
*/
const renderItem = useCallback(
({item, index}: RenderSuggestionMenuItemProps<TSuggestion>): ReactElement => (
<PressableWithFeedback
style={({hovered}) => StyleUtils.getAutoCompleteSuggestionItemStyle(highlightedSuggestionIndex, CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT, hovered, index)}
hoverDimmingValue={1}
onMouseDown={(e) => e.preventDefault()}
onPress={() => onSelect(index)}
onLongPress={() => {}}
accessibilityLabel={accessibilityLabelExtractor(item, index)}
>
{renderSuggestionMenuItem(item, index)}
</PressableWithFeedback>
),
[accessibilityLabelExtractor, renderSuggestionMenuItem, StyleUtils, highlightedSuggestionIndex, onSelect],
);
const innerHeight = CONST.AUTO_COMPLETE_SUGGESTER.SUGGESTION_ROW_HEIGHT * suggestions.length;
const animatedStyles = useAnimatedStyle(() => ({
opacity: fadeInOpacity.value,
...StyleUtils.getAutoCompleteSuggestionContainerStyle(rowHeight.value),
}));
useEffect(() => {
if (measuredHeightOfSuggestionRows === prevRowHeightRef.current) {
// eslint-disable-next-line react-compiler/react-compiler
fadeInOpacity.value = withTiming(1, {
duration: 70,
easing: Easing.inOut(Easing.ease),
});
rowHeight.value = measuredHeightOfSuggestionRows;
} else {
fadeInOpacity.value = 1;
rowHeight.value = withTiming(measuredHeightOfSuggestionRows, {
duration: 100,
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
});
}
prevRowHeightRef.current = measuredHeightOfSuggestionRows;
}, [suggestions.length, rowHeight, measuredHeightOfSuggestionRows, prevRowHeightRef, fadeInOpacity]);
useEffect(() => {
if (!scrollRef.current) {
return;
}
// When using cursor control (moving the cursor with the space bar on the keyboard) on Android, moving the cursor too fast may cause an error.
try {
scrollRef.current.scrollToIndex({index: highlightedSuggestionIndex, animated: true});
} catch (e) {
// eslint-disable-next-line no-console
}
}, [highlightedSuggestionIndex]);
return (
<Animated.View
style={[styles.autoCompleteSuggestionsContainer, animatedStyles]}
onPointerDown={(e) => {
if (DeviceCapabilities.hasHoverSupport()) {
return;
}
e.preventDefault();
}}
>
<ColorSchemeWrapper>
<FlatList
ref={scrollRef}
keyboardShouldPersistTaps="handled"
data={suggestions}
renderItem={renderItem}
keyExtractor={keyExtractor}
removeClippedSubviews={false}
showsVerticalScrollIndicator={innerHeight > rowHeight.value}
extraData={[highlightedSuggestionIndex, renderSuggestionMenuItem]}
/>
</ColorSchemeWrapper>
</Animated.View>
);
}
BaseAutoCompleteSuggestions.displayName = 'BaseAutoCompleteSuggestions';
export default BaseAutoCompleteSuggestions;