forked from Expensify/App
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.tsx
More file actions
133 lines (118 loc) · 4.84 KB
/
index.tsx
File metadata and controls
133 lines (118 loc) · 4.84 KB
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
import {useIsFocused} from '@react-navigation/native';
import {FlashList} from '@shopify/flash-list';
import React, {useCallback, useEffect, useMemo, useRef} from 'react';
import type {NativeSyntheticEvent} from 'react-native';
import Animated from 'react-native-reanimated';
import type {ExtendedTargetedEvent, SearchListItem} from '@components/SelectionListWithSections/types';
import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import {isMobileChrome} from '@libs/Browser';
import {addKeyDownPressListener, removeKeyDownPressListener} from '@libs/KeyboardShortcut/KeyDownPressListener';
import CONST from '@src/CONST';
import type BaseSearchListProps from './types';
const AnimatedFlashListComponent = Animated.createAnimatedComponent(FlashList<SearchListItem>);
function BaseSearchList({
data,
columns,
renderItem,
onSelectRow,
keyExtractor,
onScroll,
ref,
scrollToIndex,
onEndReached,
onEndReachedThreshold,
ListFooterComponent,
onViewableItemsChanged,
onLayout,
contentContainerStyle,
flattenedItemsLength,
newTransactions,
selectedTransactions,
customCardNames,
}: BaseSearchListProps) {
const hasKeyBeenPressed = useRef(false);
const isFocused = useIsFocused();
const setHasKeyBeenPressed = useCallback(() => {
if (hasKeyBeenPressed.current) {
return;
}
// We need to track whether a key has been pressed to enable focus syncing only if a key has been pressed.
// This is to avoid the default behavior of web showing blue border on click of items after a page refresh.
hasKeyBeenPressed.current = true;
}, []);
const [focusedIndex, setFocusedIndex] = useArrowKeyFocusManager({
initialFocusedIndex: -1,
maxIndex: flattenedItemsLength - 1,
isActive: isFocused,
onFocusedIndexChange: (index: number) => {
scrollToIndex?.(index);
},
...(!hasKeyBeenPressed.current && {setHasKeyBeenPressed}),
isFocused,
});
const renderItemWithKeyboardFocus = useCallback(
({item, index}: {item: SearchListItem; index: number}) => {
const isItemFocused = focusedIndex === index;
const onFocus = (event: NativeSyntheticEvent<ExtendedTargetedEvent>) => {
// Prevent unexpected scrolling on mobile Chrome after the context menu closes by ignoring programmatic focus not triggered by direct user interaction.
if (isMobileChrome() && event.nativeEvent) {
if (!event.nativeEvent.sourceCapabilities) {
return;
}
// Ignore the focus if it's caused by a touch event on mobile chrome.
// For example, a long press will trigger a focus event on mobile chrome
if (event.nativeEvent.sourceCapabilities.firesTouchEvents) {
return;
}
}
setFocusedIndex(index);
};
return renderItem(item, index, isItemFocused, onFocus);
},
[focusedIndex, renderItem, setFocusedIndex],
);
const selectFocusedOption = useCallback(() => {
const focusedItem = data.at(focusedIndex);
if (!focusedItem) {
return;
}
onSelectRow(focusedItem);
}, [data, focusedIndex, onSelectRow]);
useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ENTER, selectFocusedOption, {
captureOnInputs: true,
shouldBubble: false,
shouldPreventDefault: false,
isActive: isFocused && focusedIndex >= 0,
shouldStopPropagation: true,
});
useEffect(() => {
addKeyDownPressListener(setHasKeyBeenPressed);
return () => removeKeyDownPressListener(setHasKeyBeenPressed);
}, [setHasKeyBeenPressed]);
const extraData = useMemo(
() => [focusedIndex, columns, newTransactions, selectedTransactions, customCardNames],
[focusedIndex, columns, newTransactions, selectedTransactions, customCardNames],
);
return (
<AnimatedFlashListComponent
data={data}
renderItem={renderItemWithKeyboardFocus}
keyExtractor={keyExtractor}
onScroll={onScroll}
showsVerticalScrollIndicator
ref={ref}
extraData={extraData}
onEndReached={onEndReached}
onEndReachedThreshold={onEndReachedThreshold}
ListFooterComponent={ListFooterComponent}
onViewableItemsChanged={onViewableItemsChanged}
onLayout={onLayout}
removeClippedSubviews
drawDistance={1000}
contentContainerStyle={contentContainerStyle}
maintainVisibleContentPosition={{disabled: true}}
/>
);
}
export default BaseSearchList;