ALF text input for generic search input (#5511)

* alf text input for generic search input

* clearer ref naming

* Adjust props and definition

* Migrate props

* Migrate props

* Migrate props

* Replace on search screen

* rm old props

---------

Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
Samuel Newman
2024-10-01 00:53:11 +03:00
committed by GitHub
parent b4941d8556
commit 2129f69fa0
8 changed files with 129 additions and 253 deletions
-124
View File
@@ -1,124 +0,0 @@
import React from 'react'
import {
StyleProp,
StyleSheet,
TextInput,
TouchableOpacity,
View,
ViewStyle,
} from 'react-native'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {HITSLOP_10} from 'lib/constants'
import {MagnifyingGlassIcon} from 'lib/icons'
import {useTheme} from 'lib/ThemeContext'
import {usePalette} from 'lib/hooks/usePalette'
import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'
interface Props {
query: string
setIsInputFocused?: (v: boolean) => void
onChangeQuery: (v: string) => void
onPressCancelSearch: () => void
onSubmitQuery: () => void
style?: StyleProp<ViewStyle>
}
export interface SearchInputRef {
focus?: () => void
}
export const SearchInput = React.forwardRef<SearchInputRef, Props>(
function SearchInput(
{
query,
setIsInputFocused,
onChangeQuery,
onPressCancelSearch,
onSubmitQuery,
style,
},
ref,
) {
const theme = useTheme()
const pal = usePalette('default')
const {_} = useLingui()
const textInput = React.useRef<TextInput>(null)
const onPressCancelSearchInner = React.useCallback(() => {
onPressCancelSearch()
textInput.current?.blur()
}, [onPressCancelSearch, textInput])
React.useImperativeHandle(ref, () => ({
focus: () => textInput.current?.focus(),
blur: () => textInput.current?.blur(),
}))
return (
<View style={[pal.viewLight, styles.container, style]}>
<MagnifyingGlassIcon style={[pal.icon, styles.icon]} size={21} />
<TextInput
testID="searchTextInput"
ref={textInput}
placeholder={_(msg`Search`)}
placeholderTextColor={pal.colors.textLight}
selectTextOnFocus
returnKeyType="search"
value={query}
style={[pal.text, styles.input]}
keyboardAppearance={theme.colorScheme}
onFocus={() => setIsInputFocused?.(true)}
onBlur={() => setIsInputFocused?.(false)}
onChangeText={onChangeQuery}
onSubmitEditing={onSubmitQuery}
accessibilityRole="search"
accessibilityLabel={_(msg`Search`)}
accessibilityHint=""
autoCorrect={false}
autoCapitalize="none"
/>
{query ? (
<TouchableOpacity
onPress={onPressCancelSearchInner}
accessibilityRole="button"
accessibilityLabel={_(msg`Clear search query`)}
accessibilityHint=""
hitSlop={HITSLOP_10}>
<FontAwesomeIcon
icon="xmark"
size={16}
style={pal.textLight as FontAwesomeIconStyle}
/>
</TouchableOpacity>
) : undefined}
</View>
)
},
)
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
borderRadius: 30,
paddingHorizontal: 12,
paddingVertical: 8,
},
icon: {
marginRight: 6,
alignSelf: 'center',
},
input: {
flex: 1,
fontSize: 17,
minWidth: 0, // overflow mitigation for firefox
},
cancelBtn: {
paddingLeft: 10,
},
})
+20 -19
View File
@@ -6,6 +6,12 @@ import {useLingui} from '@lingui/react'
import {useFocusEffect} from '@react-navigation/native'
import debounce from 'lodash.debounce'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {ComposeIcon2} from '#/lib/icons'
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
import {cleanError} from '#/lib/strings/errors'
import {s} from '#/lib/styles'
import {isNative, isWeb} from '#/platform/detection'
import {
SavedFeedItem,
@@ -16,25 +22,19 @@ import {
import {useSession} from '#/state/session'
import {useSetMinimalShellMode} from '#/state/shell'
import {useComposerControls} from '#/state/shell/composer'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {ComposeIcon2} from 'lib/icons'
import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
import {cleanError} from 'lib/strings/errors'
import {s} from 'lib/styles'
import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
import {FAB} from 'view/com/util/fab/FAB'
import {SearchInput} from 'view/com/util/forms/SearchInput'
import {TextLink} from 'view/com/util/Link'
import {List} from 'view/com/util/List'
import {FeedFeedLoadingPlaceholder} from 'view/com/util/LoadingPlaceholder'
import {Text} from 'view/com/util/text/Text'
import {ViewHeader} from 'view/com/util/ViewHeader'
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
import {FAB} from '#/view/com/util/fab/FAB'
import {TextLink} from '#/view/com/util/Link'
import {List} from '#/view/com/util/List'
import {FeedFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
import {Text} from '#/view/com/util/text/Text'
import {ViewHeader} from '#/view/com/util/ViewHeader'
import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed'
import {NoSavedFeedsOfAnyType} from '#/screens/Feeds/NoSavedFeedsOfAnyType'
import {atoms as a, useTheme} from '#/alf'
import {Divider} from '#/components/Divider'
import * as FeedCard from '#/components/FeedCard'
import {SearchInput} from '#/components/forms/SearchInput'
import {IconCircle} from '#/components/IconCircle'
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
import {FilterTimeline_Stroke2_Corner0_Rounded as FilterTimeline} from '#/components/icons/FilterTimeline'
@@ -481,11 +481,12 @@ export function FeedsScreen(_props: Props) {
<FeedsAboutHeader />
<View style={{paddingHorizontal: 12, paddingBottom: 4}}>
<SearchInput
query={query}
onChangeQuery={onChangeQuery}
onPressCancelSearch={onPressCancelSearch}
onSubmitQuery={onSubmitQuery}
setIsInputFocused={onChangeSearchFocus}
value={query}
onChangeText={onChangeQuery}
onClearText={onPressCancelSearch}
onSubmitEditing={onSubmitQuery}
onFocus={() => onChangeSearchFocus(true)}
onBlur={() => onChangeSearchFocus(false)}
/>
</View>
</>
+11 -84
View File
@@ -62,12 +62,10 @@ import {makeSearchQuery, parseSearchQuery} from '#/screens/Search/utils'
import {atoms as a, useBreakpoints, useTheme as useThemeNew, web} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as FeedCard from '#/components/FeedCard'
import * as TextField from '#/components/forms/TextField'
import {SearchInput} from '#/components/forms/SearchInput'
import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDown} from '#/components/icons/Chevron'
import {MagnifyingGlass2_Stroke2_Corner0_Rounded as MagnifyingGlass} from '#/components/icons/MagnifyingGlass2'
import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
import {SettingsGear2_Stroke2_Corner0_Rounded as Gear} from '#/components/icons/SettingsGear2'
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
function Loader() {
const pal = usePalette('default')
@@ -864,15 +862,16 @@ export function SearchScreen(
<ButtonIcon icon={Menu} size="lg" />
</Button>
)}
<SearchInputBox
textInput={textInput}
searchText={searchText}
showAutocomplete={showAutocomplete}
onFocus={onSearchInputFocus}
onChangeText={onChangeText}
onSubmit={onSubmit}
onPressClearQuery={onPressClearQuery}
/>
<View style={[a.flex_1]}>
<SearchInput
ref={textInput}
value={searchText}
onFocus={onSearchInputFocus}
onChangeText={onChangeText}
onClearText={onPressClearQuery}
onSubmitEditing={onSubmit}
/>
</View>
{showFiltersButton && (
<Button
onPress={() => setShowFilters(!showFilters)}
@@ -961,78 +960,6 @@ export function SearchScreen(
)
}
let SearchInputBox = ({
textInput,
searchText,
showAutocomplete,
onFocus,
onChangeText,
onSubmit,
onPressClearQuery,
}: {
textInput: React.RefObject<TextInput>
searchText: string
showAutocomplete: boolean
onFocus: () => void
onChangeText: (text: string) => void
onSubmit: () => void
onPressClearQuery: () => void
}): React.ReactNode => {
const {_} = useLingui()
const t = useThemeNew()
return (
<View style={[a.flex_1]}>
<TextField.Root>
<TextField.Icon icon={MagnifyingGlass} />
<TextField.Input
inputRef={textInput}
label={_(msg`Search`)}
value={searchText}
placeholder={_(msg`Search`)}
returnKeyType="search"
onChangeText={onChangeText}
onSubmitEditing={onSubmit}
onFocus={onFocus}
keyboardAppearance={t.scheme}
selectTextOnFocus={isNative}
autoFocus={false}
accessibilityRole="search"
autoCorrect={false}
autoComplete="off"
autoCapitalize="none"
/>
</TextField.Root>
{showAutocomplete && searchText.length > 0 && (
<View
style={[
a.absolute,
a.z_10,
a.my_auto,
a.inset_0,
a.justify_center,
a.pr_sm,
{left: 'auto'},
]}>
<Button
testID="searchTextInputClearBtn"
onPress={onPressClearQuery}
label={_(msg`Clear search query`)}
hitSlop={HITSLOP_10}
size="tiny"
shape="round"
variant="ghost"
color="secondary">
<ButtonIcon icon={X} size="sm" />
</Button>
</View>
)}
</View>
)
}
SearchInputBox = React.memo(SearchInputBox)
let AutocompleteResults = ({
isAutocompleteFetching,
autocompleteData,
+5 -5
View File
@@ -25,11 +25,11 @@ import {s} from '#/lib/styles'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete'
import {precacheProfile} from '#/state/queries/profile'
import {SearchInput} from '#/view/com/util/forms/SearchInput'
import {Link} from '#/view/com/util/Link'
import {Text} from '#/view/com/util/text/Text'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a} from '#/alf'
import {SearchInput} from '#/components/forms/SearchInput'
let SearchLinkCard = ({
label,
@@ -184,10 +184,10 @@ export function DesktopSearch() {
return (
<View style={[styles.container, pal.view]}>
<SearchInput
query={query}
onChangeQuery={onChangeText}
onPressCancelSearch={onPressCancelSearch}
onSubmitQuery={onSubmit}
value={query}
onChangeText={onChangeText}
onClearText={onPressCancelSearch}
onSubmitEditing={onSubmit}
/>
{query !== '' && isActive && moderationOpts && (
<View style={[pal.view, pal.borderDark, styles.resultsContainer]}>