Fix search typeahead performance with absolute overlay

Instead of toggling display:none/flex on the typeahead and content panels
(which causes heavy unmount/remount cycles), the content (SearchScreenInner)
now stays always mounted and the typeahead overlay is absolutely positioned
on top with conditional rendering.

Added reanimated FadeInUp/FadeOutDown animations (native only via native()
wrapper) for a smooth transition. Added accessibility props
(accessibilityViewIsModal, aria-modal, accessibilityRole) so screen readers
properly treat the overlay as a modal list.

https://claude.ai/code/session_01BhE3QBg6XZwXTwgTbQP9L9
This commit is contained in:
Claude
2026-03-22 19:31:35 +00:00
committed by Samuel Newman
parent f1764f1d61
commit 09833572be
+33 -31
View File
@@ -12,6 +12,7 @@ import {
View,
type ViewStyle,
} from 'react-native'
import Animated, {FadeInUp, FadeOutDown} from 'react-native-reanimated'
import {Trans, useLingui} from '@lingui/react/macro'
import {useFocusEffect, useNavigation, useRoute} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'
@@ -32,7 +33,7 @@ import {
type Params,
parseSearchQuery,
} from '#/screens/Search/utils'
import {atoms as a, tokens, useBreakpoints, useTheme, web} from '#/alf'
import {atoms as a, native, tokens, useBreakpoints, useTheme, web} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {SearchInput} from '#/components/forms/SearchInput'
import * as Layout from '#/components/Layout'
@@ -418,36 +419,7 @@ export function SearchScreenShell({
</Layout.Center>
</View>
<View
style={{
display: showAutocomplete && !fixedParams ? 'flex' : 'none',
flex: 1,
}}>
{searchText.length > 0 ? (
<AutocompleteResults
isAutocompleteFetching={isAutocompleteFetching}
autocompleteData={autocompleteData}
searchText={searchText}
onSubmit={onSubmit('autocomplete')}
onResultPress={onAutocompleteResultPress}
onProfileClick={handleProfileClick}
/>
) : (
<SearchHistory
searchHistory={termHistory}
selectedProfiles={accountHistoryProfiles?.profiles || []}
onItemClick={handleHistoryItemClick}
onProfileClick={handleProfileClick}
onRemoveItemClick={deleteSearchHistoryItem}
onRemoveProfileClick={deleteProfileHistoryItem}
/>
)}
</View>
<View
style={{
display: showAutocomplete ? 'none' : 'flex',
flex: 1,
}}>
<View style={[a.flex_1, a.relative]}>
<SearchScreenInner
key={params.lang}
activeTab={activeTab}
@@ -457,6 +429,36 @@ export function SearchScreenShell({
headerHeight={headerHeight}
focusSearchInput={focusSearchInput}
/>
{showAutocomplete && !fixedParams && (
<Animated.View
entering={native(FadeInUp.duration(200))}
exiting={native(FadeOutDown.duration(150))}
style={[a.absolute, a.inset_0, t.atoms.bg]}
accessibilityViewIsModal
accessibilityRole="list"
aria-modal>
{searchText.length > 0 ? (
<AutocompleteResults
isAutocompleteFetching={isAutocompleteFetching}
autocompleteData={autocompleteData}
searchText={searchText}
onSubmit={onSubmit('autocomplete')}
onResultPress={onAutocompleteResultPress}
onProfileClick={handleProfileClick}
/>
) : (
<SearchHistory
searchHistory={termHistory}
selectedProfiles={accountHistoryProfiles?.profiles || []}
onItemClick={handleHistoryItemClick}
onProfileClick={handleProfileClick}
onRemoveItemClick={deleteSearchHistoryItem}
onRemoveProfileClick={deleteProfileHistoryItem}
/>
)}
</Animated.View>
)}
</View>
</Layout.Screen>
)