185 lines
4.9 KiB
TypeScript
185 lines
4.9 KiB
TypeScript
import { useRef, useEffect } from 'react'
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
TextInput,
|
|
Pressable,
|
|
} from 'react-native'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { LeftArrowIcon, CloseIcon } from '@/components/icon'
|
|
|
|
interface SearchBarProps {
|
|
searchText: string
|
|
onSearchTextChange: (text: string) => void
|
|
onSearch: (text: string) => void
|
|
onBack: () => void
|
|
placeholder?: string
|
|
autoFocus?: boolean
|
|
inputRef?: React.RefObject<TextInput | null>
|
|
showBackButton?: boolean
|
|
showSearchButton?: boolean
|
|
readOnly?: boolean
|
|
onInputPress?: () => void
|
|
onClearPress?: () => void
|
|
marginBottom?: number
|
|
}
|
|
|
|
export default function SearchBar({
|
|
searchText,
|
|
onSearchTextChange,
|
|
onSearch,
|
|
onBack,
|
|
placeholder,
|
|
autoFocus = false,
|
|
inputRef: externalInputRef,
|
|
showBackButton = true,
|
|
showSearchButton = true,
|
|
readOnly = false,
|
|
onInputPress,
|
|
onClearPress,
|
|
marginBottom = 20,
|
|
}: SearchBarProps) {
|
|
const { t } = useTranslation()
|
|
const internalInputRef = useRef<TextInput>(null)
|
|
const inputRef = externalInputRef || internalInputRef
|
|
const defaultPlaceholder = placeholder || t('searchBar.placeholder')
|
|
|
|
useEffect(() => {
|
|
if (autoFocus) {
|
|
const timer = setTimeout(() => {
|
|
inputRef.current?.focus()
|
|
}, 100)
|
|
return () => clearTimeout(timer)
|
|
}
|
|
}, [autoFocus, inputRef])
|
|
|
|
const handleClear = () => {
|
|
if (onClearPress) {
|
|
onClearPress()
|
|
} else {
|
|
onSearchTextChange('')
|
|
}
|
|
}
|
|
|
|
const inputContainer = (
|
|
<>
|
|
<TextInput
|
|
ref={inputRef}
|
|
style={styles.searchInput}
|
|
value={searchText}
|
|
onChangeText={readOnly ? undefined : onSearchTextChange}
|
|
placeholder={defaultPlaceholder}
|
|
placeholderTextColor="#ABABAB"
|
|
underlineColorAndroid="transparent"
|
|
selectionColor="#F5F5F5"
|
|
autoCorrect={false}
|
|
autoCapitalize="none"
|
|
editable={!readOnly}
|
|
pointerEvents={readOnly ? 'none' : 'auto'}
|
|
onSubmitEditing={() => {
|
|
if (searchText.trim()) {
|
|
onSearch(searchText.trim())
|
|
}
|
|
}}
|
|
/>
|
|
{searchText.length > 0 && (
|
|
<Pressable
|
|
onPress={(e) => {
|
|
if (readOnly && onInputPress) {
|
|
e.stopPropagation()
|
|
}
|
|
handleClear()
|
|
}}
|
|
style={styles.clearButton}
|
|
>
|
|
<CloseIcon />
|
|
</Pressable>
|
|
)}
|
|
</>
|
|
)
|
|
|
|
return (
|
|
<View style={[styles.topBar, { marginBottom }]}>
|
|
{showBackButton && (
|
|
<Pressable onPress={onBack}>
|
|
<LeftArrowIcon />
|
|
</Pressable>
|
|
)}
|
|
|
|
{readOnly && onInputPress ? (
|
|
<Pressable
|
|
style={styles.searchInputContainer}
|
|
onPress={onInputPress}
|
|
>
|
|
{inputContainer}
|
|
</Pressable>
|
|
) : (
|
|
<View style={styles.searchInputContainer}>
|
|
{inputContainer}
|
|
</View>
|
|
)}
|
|
{showSearchButton && (
|
|
<Pressable
|
|
style={styles.searchButton}
|
|
onPress={() => {
|
|
if (searchText.trim()) {
|
|
onSearch(searchText.trim())
|
|
}
|
|
}}
|
|
>
|
|
<Text style={styles.searchButtonText}>{t('searchBar.button')}</Text>
|
|
</Pressable>
|
|
)}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
topBar: {
|
|
height: 44,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
paddingHorizontal: 12,
|
|
marginTop: 8,
|
|
marginBottom: 20,
|
|
},
|
|
searchInputContainer: {
|
|
flex: 1,
|
|
marginLeft: 4,
|
|
marginRight: 12,
|
|
position: 'relative',
|
|
height: 40,
|
|
},
|
|
searchInput: {
|
|
flex: 1,
|
|
color: '#F5F5F5',
|
|
fontSize: 14,
|
|
height: 40,
|
|
paddingVertical: 8,
|
|
paddingLeft: 12,
|
|
paddingRight: 36,
|
|
borderRadius: 100,
|
|
backgroundColor: '#16181B',
|
|
},
|
|
clearButton: {
|
|
position: 'absolute',
|
|
right: 8,
|
|
top: 0,
|
|
bottom: 0,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
padding: 4,
|
|
},
|
|
searchButton: {
|
|
paddingHorizontal: 0,
|
|
paddingVertical: 8,
|
|
},
|
|
searchButtonText: {
|
|
color: '#F5F5F5',
|
|
fontSize: 14,
|
|
fontWeight: '400',
|
|
},
|
|
})
|
|
|