# react-native-autocomplete-dropdown

Dropdown Item picker with search and autocomplete (typeahead) functionality for react native

[![license](https://img.shields.io/github/license/onmotion/react-native-autocomplete-dropdown)](https://img.shields.io/github/license/onmotion/react-native-autocomplete-dropdown)
[![npm](https://img.shields.io/npm/v/react-native-autocomplete-dropdown.svg)](https://npmjs.com/package/react-native-autocomplete-dropdown)
[![npm](https://img.shields.io/npm/dm/react-native-autocomplete-dropdown.svg)](https://npmjs.com/package/react-native-autocomplete-dropdown)

> This is documentation for version 4.x, if you are looking docs for version 3.x, you can find it [here](https://github.com/onmotion/react-native-autocomplete-dropdown/blob/main/README%5E3.md)

<p style="text-align: center;" align="center">
    <img src="./screens/Example.png" width="500px" >
</p>

## Demo

<p style="text-align: center;" align="center">
    <img src="./screens/android.gif" width="280px" >
    <img src="./screens/ios.gif" width="280px" >
</p>

> Run expo snack demo [@onmotion/react-native-autocomplete-dropdown](https://snack.expo.dev/@onmotion/react-native-autocomplete-dropdown-v4)

or download the [Expo Go](https://expo.dev/go) app and scan the QR code below

<img src="./screens/expo-example-qr-v4.png" width="150">

## Nav

- [react-native-autocomplete-dropdown](#react-native-autocomplete-dropdown)
        - [Demo](#demo)
        - [Nav](#nav)
        - [Installation](#installation)
        - [Post-install Steps](#post-install-steps)
                - [iOS](#ios)
                - [Android](#android)
        - [Usage](#usage)
                - [Dataset item format](#dataset-item-format)
                - [Example with local Dataset](#example-with-local-dataset)
                - [Example with remote requested Dataset](#example-with-remote-requested-dataset)
        - [Playground](#playground)
        - [Options](#options)
        - [Usage with a Modal](#usage-with-a-modal)

## Installation

Run:

 or

 ```bash
 yarn add react-native-autocomplete-dropdown
 ```

 or for v3.x

 ```bash
 yarn add react-native-autocomplete-dropdown@3.1.5
 ```

## Post-install Steps

Make sure **react-native-svg** is installed. Follow the guide
<https://github.com/software-mansion/react-native-svg>

```bash
yarn add react-native-svg
```

### iOS

Run: `npx pod-install` for install `react-native-svg` dependency (if not installed yet).

### Android

No additional steps are necessary

## Usage

Wrap your root component in `AutocompleteDropdownContextProvider` from `react-native-autocomplete-dropdown` as you can see in [example](https://github.com/onmotion/react-native-autocomplete-dropdown/blob/main/example/App.tsx)

```js
<AutocompleteDropdownContextProvider>
    <AppRootOrWhatever/>
</AutocompleteDropdownContextProvider>
```

The dropdown position is relative to the `AutocompleteDropdownContextProvider`, so put this in the right place, it should cover all the screen/modal.

If you have a header component, you can pass an offset.  For example with react navigation

```js
//...
import { useHeaderHeight } from '@react-navigation/elements';
//...
const headerHeight = useHeaderHeight();
//...

<AutocompleteDropdownContextProvider headerOffset={headerHeight} >
    <AppRootOrWhatever/>
</AutocompleteDropdownContextProvider>
```

import the package

```js
import { AutocompleteDropdown } from 'react-native-autocomplete-dropdown';
```

### Dataset item format

`dataSet` property must be an **array of objects** or **null**. Object required keys are:

```js
{
    id: 'some uniq string id',
    title: 'list item title'
}
```

### Example with local Dataset

```js
const [selectedItem, setSelectedItem] = useState(null);

<AutocompleteDropdown
  clearOnFocus={false}
  closeOnBlur={true}
  closeOnSubmit={false}
  initialValue={{ id: '2' }} // or just '2'
  onSelectItem={setSelectedItem}
  dataSet={[
    { id: '1', title: 'Alpha' },
    { id: '2', title: 'Beta' },
    { id: '3', title: 'Gamma' },
  ]}
/>;
```

### Example with remote requested Dataset

```js
import React, { memo, useCallback, useRef, useState } from 'react'
import { Button, Dimensions, Text, View, Platform } from 'react-native'
import { AutocompleteDropdown } from 'react-native-autocomplete-dropdown'

export const RemoteDataSetExample2 = memo(() => {
  const [loading, setLoading] = useState(false)
  const [suggestionsList, setSuggestionsList] = useState(null)
  const [selectedItem, setSelectedItem] = useState(null)
  const dropdownController = useRef(null)

  const searchRef = useRef(null)

  const getSuggestions = useCallback(async q => {
    const filterToken = q.toLowerCase()
    console.log('getSuggestions', q)
    if (typeof q !== 'string' || q.length < 3) {
      setSuggestionsList(null)
      return
    }
    setLoading(true)
    const response = await fetch('https://jsonplaceholder.typicode.com/posts')
    const items = await response.json()
    const suggestions = items
      .filter(item => item.title.toLowerCase().includes(filterToken))
      .map(item => ({
        id: item.id,
        title: item.title,
      }))
    setSuggestionsList(suggestions)
    setLoading(false)
  }, [])

  const onClearPress = useCallback(() => {
    setSuggestionsList(null)
  }, [])

  const onOpenSuggestionsList = useCallback(isOpened => {}, [])

  return (
    <>
      <View
        style={[
          { flex: 1, flexDirection: 'row', alignItems: 'center' },
          Platform.select({ ios: { zIndex: 1 } }),
        ]}>
        <AutocompleteDropdown
          ref={searchRef}
          controller={controller => {
            dropdownController.current = controller
          }}
          // initialValue={'1'}
          direction={Platform.select({ ios: 'down' })}
          dataSet={suggestionsList}
          onChangeText={getSuggestions}
          onSelectItem={item => {
            item && setSelectedItem(item.id)
          }}
          debounce={600}
          suggestionsListMaxHeight={Dimensions.get('window').height * 0.4}
          onClear={onClearPress}
          //  onSubmit={(e) => onSubmitSearch(e.nativeEvent.text)}
          onOpenSuggestionsList={onOpenSuggestionsList}
          loading={loading}
          useFilter={false} // set false to prevent rerender twice
          textInputProps={{
            placeholder: 'Type 3+ letters (dolo...)',
            autoCorrect: false,
            autoCapitalize: 'none',
            style: {
              borderRadius: 25,
              backgroundColor: '#383b42',
              color: '#fff',
              paddingLeft: 18,
            },
          }}
          rightButtonsContainerStyle={{
            right: 8,
            height: 30,

            alignSelf: 'center',
          }}
          inputContainerStyle={{
            backgroundColor: '#383b42',
            borderRadius: 25,
          }}
          suggestionsListContainerStyle={{
            backgroundColor: '#383b42',
          }}
          containerStyle={{ flexGrow: 1, flexShrink: 1 }}
          renderItem={(item, text) => <Text style={{ color: '#fff', padding: 15 }}>{item.title}</Text>}
        //   ChevronIconComponent={<Feather name="chevron-down" size={20} color="#fff" />}
        //   ClearIconComponent={<Feather name="x-circle" size={18} color="#fff" />}
          inputHeight={50}
          showChevron={false}
          closeOnBlur={false}
          //  showClear={false}
        />
        <View style={{ width: 10 }} />
        <Button style={{ flexGrow: 0 }} title="Toggle" onPress={() => dropdownController.current.toggle()} />
      </View>
      <Text style={{ color: '#668', fontSize: 13 }}>Selected item id: {JSON.stringify(selectedItem)}</Text>
    </>
  )
})

```

More examples see at <https://github.com/onmotion/react-native-autocomplete-dropdown/tree/main/example>

## Playground

To play around with the examples, you can run the following commands

```bash
cd example
yarn install
yarn pods

yarn ios
yarn android
```

## Options

| Option                         | Description                                                                                                      | Type                | Default                                  |
|--------------------------------|------------------------------------------------------------------------------------------------------------------|---------------------|------------------------------------------|
| `dataSet`                      | Set of list items                                                                                                | array               | null                                     |
| `initialValue`                 | String (**id**) or object that contains **id**                                                                   | string \| object    | null                                     |
| `loading`                      | Loading state                                                                                                    | bool                | false                                    |
| `enableLoadingIndicator`       | Whether to show activity indicator on loading                                                                    | bool                | true                                     |
| `useFilter`                    | Whether to use local filter by dataSet (useful set to false for remote filtering to prevent rerender twice)      | bool                | true                                     |
| `showClear`                    | Show clear button                                                                                                | bool                | true                                     |
| `showChevron`                  | Show chevron (open/close) button                                                                                 | bool                | true                                     |
| `closeOnBlur`                  | Whether to close dropdown on blur                                                                                | bool                | false                                    |
| `closeOnSubmit`                | Whether to close dropdown on submit                                                                              | bool                | false                                    |
| `clearOnFocus`                 | Whether to clear typed text on focus                                                                             | bool                | true                                     |
| `caseSensitive`                | Whether to perform case-sensitive search                                                                         | bool                | false                                    |
| `ignoreAccents`                | Ignore diacritics                                                                                                | bool                | true                                     |
| `trimSearchText`               | Trim the searched text                                                                                           | bool                | true                                     |
| `editable`                     | Is textInput editable                                                                                            | bool                | true                                     |
| `debounce`                     | Wait **ms** before call `onChangeText`                                                                           | number              | 0                                        |
| `suggestionsListMaxHeight`     | Max height of dropdown                                                                                           | number              | 200                                      |
| `direction`                    | "up" or "down"                                                                                                   | string              | down + auto calculate                    |
| `matchFrom`                    | Whether match suggestions from start of titles or anywhere in the title. Possible values are "any" or "start"    | string              | any                                      |
| `bottomOffset`                 | For calculate dropdown direction (e.g., tabbar)                                                                  | number              | 0                                        |
| `theme`           | Color scheme. Possible values are "light" or "dark"  | string            |  OS's color scheme                                        |
| `onChangeText`                 | Event textInput onChangeText                                                                                     | function            |                                          |
| `onSelectItem`                 | Event onSelectItem                                                                                               | function            |                                          |
| `onOpenSuggestionsList`        | Event onOpenSuggestionsList                                                                                      | function            |                                          |
| `onChevronPress`               | Event onChevronPress                                                                                             | function            |                                          |
| `onClear`                      | Event on clear button press                                                                                      | function            |                                          |
| `onSubmit`                     | Event on submit KB button press                                                                                  | function            |                                          |
| `onBlur`                       | Event fired on text input blur                                                                                   | function            |                                          |
| `onFocus`                      | Event on focus text input                                                                                        | function            |                                          |
| `renderItem`                   | JSX for render item `(item, searchText) => JSX \| null` if return null then the element will not be displayed    | function            | item.title                               |
| `controller`                   | Return reference to module controller with methods **close, open, toggle, clear, setInputText, setItem**         | function            |                                          |
| `containerStyle`               |                                                                                                                  | ViewStyle           |                                          |
| `rightButtonsContainerStyle`   |                                                                                                                  | ViewStyle           |                                          |
| `suggestionsListContainerStyle`|                                                                                                                  | ViewStyle           |                                          |
| `suggestionsListTextStyle`     |                                                                                                                  | TextStyle           | styles of suggestions list text items    |
| `ChevronIconComponent`         |                                                                                                                  | React.Component     | Feather chevron icon                     |
| `ClearIconComponent`           |                                                                                                                  | React.Component     | Feather x icon                           |
| `EmptyResultComponent`         | Replace the default `Component` on empty result                                                                  | React.Component     |                                          |
| `InputComponent`               | Input element component                                                                                          | React.ComponentType | TextInput                                |
| `emptyResultText`              | Replace the default "Nothing found" text on empty result                                                         | string              | "Nothing found"                          |
| `textInputProps`               | Text input props                                                                                                 | TextInputProps      |                                          |
| `flatListProps`                | Props for `FlatList` component                                                                                   | FlatListProps       |                                          |


## Usage with a Modal

if you want to use the dropdown in a modal, you need to wrap the dropdown in another `AutocompleteDropdownContextProvider` inside the modal component

```js
    <Modal
        visible={opened}
        presentationStyle="formSheet"
        animationType="slide"
        onRequestClose={() => setOpened(false)}>
        <SafeAreaView style={{ flex: 1, backgroundColor: 'transparent' }}>
          <AutocompleteDropdownContextProvider>
            <View style={{ paddingHorizontal: 20, flex: 1, paddingTop: 20 }}>
              <AutocompleteDropdown {...props}/>
            </View>
          </AutocompleteDropdownContextProvider>
        </SafeAreaView>
    </Modal>
```
