react-native-dropdown-autocomplete-revised
Version:
[npm-badge]: https://img.shields.io/npm/v/react-native-dropdown-autocomplete.svg?colorA=6b7c93&colorB=5ab1b8&style=flat-square [npm-url]: https://www.npmjs.com/package/react-native-dropdown-autocomplete [npm-downloads]: https://img.shields.io/npm/dt/react
259 lines (239 loc) • 7.15 kB
JavaScript
import React, { Component, Fragment } from 'react'
import { findNodeHandle, ActivityIndicator, TextInput, View, ScrollView } from 'react-native'
import { string, bool, number, func } from 'prop-types'
import Dropdown from '../Dropdown'
import { capitalizeFirstLetter } from '../../utils/string'
import { styles } from './Autocomplete.styles'
import { get } from '../../utils/api'
import { WAIT_INTERVAL, NO_DATA } from '../../constants/Autocomplete'
import { theme } from '../../constants/Theme'
import locales from '../../constants/Locales'
class Autocomplete extends Component {
constructor(props) {
super(props)
this.state = {
inputValue: props.initialValue || '',
loading: false,
filteredItems: [],
}
this.mounted = false
this.timer = null
this.dropdown = React.createRef()
this.container = React.createRef()
this.setItem = this.setItem.bind(this)
this.triggerChange = this.triggerChange.bind(this)
this.handleInputChange = this.handleInputChange.bind(this)
this.handleBlur = this.handleBlur.bind(this)
this.promisifySetState = this.promisifySetState.bind(this)
// this.clearInput = this.clearInput.bind(this);
}
handleInputChange(text) {
const { onChangeText, minimumCharactersCount, waitInterval } = this.props
if (onChangeText) {
onChangeText(text)
}
clearTimeout(this.timer)
this.setState({ inputValue: text })
if (text.length > minimumCharactersCount) {
this.setState(
{
loading: true,
},
() => {
if (this.mounted) {
this.timer = setTimeout(this.triggerChange, waitInterval)
}
},
)
} else {
this.setState({ loading: false })
}
}
promisifySetState(state) {
return this.mounted && new Promise((resolve) => this.setState(state, () => resolve()))
}
async triggerChange() {
const { inputValue, items } = this.state
const { fetchData, fetchDataUrl, valueExtractor } = this.props
if (fetchData) {
try {
const response = await fetchData(inputValue)
if (response.length && this.mounted) {
this.setState({ items: response, loading: false })
} else {
this.setState({ items: [NO_DATA], loading: false })
}
if (this.dropdown.current) {
this.dropdown.current.onPress(this.container)
}
} catch (error) {
throw new Error(error)
}
} else if (fetchDataUrl) {
try {
const response = await get(fetchDataUrl, { search: inputValue })
if (response.length && this.mounted) {
this.setState({ items: response, loading: false })
} else {
this.setState({ items: [NO_DATA], loading: false })
}
if (this.dropdown.current) {
this.dropdown.current.onPress(this.container)
}
} catch (error) {
throw new Error(error)
}
} else {
const filteredItems = items.filter((item) => {
return valueExtractor(item).toLowerCase().search(inputValue.toLowerCase()) !== -1
})
if (filteredItems.length && this.mounted) {
await this.promisifySetState({
filteredItems,
loading: false,
})
} else {
await this.promisifySetState({
filteredItems: [NO_DATA],
loading: false,
})
}
if (this.dropdown.current) {
this.dropdown.current.onPress(this.container)
}
}
}
setItem(value) {
const { index, handleSelectItem, valueExtractor, resetOnSelect, inputValueExtractor } =
this.props
handleSelectItem(value, index)
if (resetOnSelect) {
this.setState({ inputValue: '' })
} else {
const capitalizedValue = capitalizeFirstLetter(
inputValueExtractor ? inputValueExtractor(value) : valueExtractor(value),
)
this.setState({ inputValue: capitalizedValue })
}
}
// clearInput() {
// this.setState({inputValue: ""});
// }
componentDidMount() {
const { data } = this.props
this.mounted = true
if (data) {
this.setState({ items: data })
}
}
componentWillUnmount() {
clearTimeout(this.timer)
this.mounted = false
}
handleBlur() {
clearTimeout(this.timer)
this.setState({ loading: false })
if (this.dropdown.current) {
// this.dropdown.current.close();
}
}
getData() {
return this.props.data || this.state.items
}
render() {
const { inputValue, items, loading, filteredItems } = this.state
const {
placeholder,
scrollToInput,
renderIcon,
inputContainerStyle,
inputStyle,
spinnerStyle,
spinnerSize,
listHeader,
autoCorrect,
keyboardType,
spinnerColor,
placeholderColor,
data,
// disableFullscreenUI,
...dropdownProps
} = this.props
return (
<Fragment>
<View style={[styles.inputContainerStyle, inputContainerStyle]}>
{renderIcon && renderIcon()}
<TextInput
ref={(ref) => {
this.container = ref
}}
onBlur={(event) => this.handleBlur(event)}
style={[styles.input, inputStyle]}
placeholder={placeholder}
placeholderTextColor={placeholderColor || theme.textSecondary}
value={inputValue}
autoCorrect={autoCorrect}
keyboardType={keyboardType}
onChangeText={(text) => this.handleInputChange(text)}
onFocus={(event) => {
if (scrollToInput) {
scrollToInput(findNodeHandle(event.target))
}
}}
/>
{loading && (
<ActivityIndicator
style={[styles.spinner, spinnerStyle]}
size={spinnerSize}
color={spinnerColor || theme.primary}
/>
)}
</View>
{this.getData() && this.getData().length > 0 && (
<Dropdown
ref={this.dropdown}
dropdownPosition={0}
data={this.getData()}
listHeader={listHeader}
inputValue={inputValue}
onChangeValue={this.setItem}
{...dropdownProps}
/>
)}
</Fragment>
)
}
}
Autocomplete.defaultProps = {
placeholder: locales.components.Autocomplete.placeholder,
spinnerSize: 'small',
autoCorrect: false,
keyboardType: 'default',
minimumCharactersCount: 2,
highlightText: true,
waitInterval: WAIT_INTERVAL,
resetOnSelect: false,
}
Autocomplete.propTypes = {
placeholder: string,
spinnerSize: string,
listHeader: string,
placeholderColor: string,
fetchDataUrl: string,
minimumCharactersCount: number,
waitInterval: number,
highlightText: bool,
rightContent: bool,
autoCorrect: bool,
keyboardType: string,
resetOnSelect: bool,
valueExtractor: func,
renderIcon: func,
scrollToInput: func,
handleSelectItem: func.isRequired,
onDropdownClose: func,
onDropdownShow: func,
rightTextExtractor: func,
fetchData: func,
}
export default Autocomplete