Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-native-dropdown-picker

Package Overview
Dependencies
Maintainers
1
Versions
100
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-native-dropdown-picker - npm Package Compare versions

Comparing version 2.1.8 to 3.0.1-beta.0

8

package.json
{
"name": "react-native-dropdown-picker",
"version": "2.1.8",
"description": "A picker (dropdown) for react native which supports both Android & iOS.",
"version": "3.0.1-beta.0",
"description": "A single or multiple, searchable item picker (dropdown) component for react native which supports both Android & iOS.",
"main": "src/index.js",

@@ -14,2 +14,6 @@ "repository": {

"drop-down",
"multiple",
"single",
"placeholder",
"searchable",
"menu",

@@ -16,0 +20,0 @@ "context",

# React native dropdown picker
A picker (dropdown) component for react native which supports both Android & iOS.
# React native dropdown picker v3
A single or multiple, searchable item picker (dropdown) component for react native which supports both Android & iOS.
## Caution (incompatibility)
**x < 3.0.0 Versions are incompatible with the current version.**
**It's required to follow the docs in order to upgrade the package to v3.x**
## Changelog
+ Added multiple items feature.
+ Added searchable items feature.
+ Removed `defaultIndex` property.
+ Removed `defaultNull` property.
+ The `defaultValue` is state-friendly.
## Getting Started
![Screenshot](https://raw.githubusercontent.com/hossein-zare/react-native-dropdown-picker/master/screenshots/1.png)
![Screenshot](https://raw.githubusercontent.com/hossein-zare/react-native-dropdown-picker/master/screenshots/2.png)
![Screenshot](https://raw.githubusercontent.com/hossein-zare/react-native-dropdown-picker/3.x/screenshots/1.png)
![Screenshot](https://raw.githubusercontent.com/hossein-zare/react-native-dropdown-picker/3.x/screenshots/2.png)

@@ -18,42 +31,64 @@ ### Installation

### Basic Usage
First of all import the package.
The first step is to import the package.
```javascript
import DropDownPicker from 'react-native-dropdown-picker';
```
Render the component.
Read the docs to avoid mixing up props.
#### Single
Select a single item.
```javascript
this.state = {
country: 'uk'
}
<DropDownPicker
items={[
{label: 'Item 1', value: 'item1'},
{label: 'Item 2', value: 'item2'},
{label: 'UK', value: 'uk'},
{label: 'France', value: 'france'},
]}
defaultValue="item1"
defaultValue={this.state.country}
containerStyle={{height: 40}}
style={{backgroundColor: '#fafafa'}}
dropDownStyle={{backgroundColor: '#fafafa'}}
onChangeItem={item => console.log(item.label, item.value)}
onChangeItem={item => this.setState({
country: item.value
})}
/>
```
#### borderRadius
The only thing you have to avoid is `borderRadius`. All the corners must be set separately.
#### Multiple
Select multiple items.
```javascript
this.state = {
countries: ['uk']
}
```javascript
style={{
borderTopLeftRadius: 10, borderTopRightRadius: 10,
borderBottomLeftRadius: 10, borderBottomRightRadius: 10
}}
dropDownStyle={{
borderBottomLeftRadius: 20, borderBottomRightRadius: 20
}}
<DropDownPicker
items={[
{label: 'UK', value: 'uk'},
{label: 'France', value: 'france'},
]}
multiple={true}
multipleText="%d items have been selected."
min={0}
max={10}
defaultValue={this.state.countries}
containerStyle={{height: 40}}
onChangeItem={item => this.setState({
countries: item // an array of the selected items
})}
/>
```
#### zIndex conflicts
A lot of users use the `containerStyle` property to style the picker which results in unexpected behaviors like untouchable scrollviews.
### Searchable items
Search for specific items.
The `style` and `dropDownStyle` properties must be used instead.
```javascript
searchable={true}
searchablePlaceholder="Search..."
searchableError="Not Found"
```
Use the `containerStyle` prop to adjust the outer part of the picker such as `margin`, `width`, `height`, `flex`, ...
### Default item

@@ -63,3 +98,3 @@ You may want to select one of the items as default.

**Use one of these ways:**
1. Add `selected: true` to the object.
1. Add `selected: true` to the object. **(This method is not state-friendly!)**

@@ -69,129 +104,29 @@ ```javascript

{label: 'Item 1', value: 'item1'},
{label: 'Item 2', value: 'item2', selected: true},
{label: 'Item 2', value: 'item2', selected: true, disabled: true},
]}
```
2. The `defaultIndex` property.
2. The `defaultValue` property.
```javascript
defaultIndex={1}
defaultValue="uk" // Single
defaultValue=["uk"] // Multiple
```
3. The `defaultValue` property.
```javascript
defaultValue="item2"
```
### Placeholder
You may want to have a placeholder while the default value is null.
You may want to have a placeholder while the default value is null or an empty array.
Add the following properties to the component.
```javascript
this.state = {
data: null, // Single
data: [] // Multiple
}
...
defaultNull
defaultValue={this.state.data}
placeholder="Select an item"
...
```
#### Dynamic placeholder
Take note of the `defaultNull` property.
```javascript
state = {
item: null
}
<DropDownPicker
items={[
{label: 'Item 1', value: 1},
{label: 'Item 2', value: 2}
]}
defaultNull={this.state.item === null}
placeholder="Select an item"
placeholderStyle={{fontWeight: 'bold'}}
onChangeItem={(item)=> {
this.setState({
item: item.value
});
}}
dropDownMaxHeight={240}
/>
```
In some cases you're going to create two or more pickers which are linked together.
Think of a country picker and city picker, whenever you're changing the country, the city picker should be reset and show the placeholder.
```javascript
import React from 'react';
export default class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
country: null,
city: null,
cities: []
};
}
changeCountry(item) {
let city = null;
let cities;
switch (item.value) {
case 'fr':
cities = [
{label: 'Paris', value: 'paris'}
];
break;
case 'es':
cities = [
{label: 'Madrid', value: 'madrid'}
];
break;
}
this.setState({
city,
cities
});
}
changeCity(item) {
this.setState({
city: item.value
});
}
render() {
return (
<>
<DropDownPicker
items={[
{label: 'France', value: 'fr'},
{label: 'Spain', value: 'es'},
]}
defaultNull={this.state.country === null}
placeholder="Select your country"
containerStyle={{height: 40}}
onChangeItem={item => this.changeCountry(item)}
/>
<DropDownPicker
items={this.state.cities}
defaultNull={this.state.city === null}
placeholder="Select your city"
containerStyle={{height: 40}}
onChangeItem={item => this.changeCity(item)}
/>
</>
);
}
}
```
### Dropdown Overflow
Adding borders to the component will separate or overflow elements. to solve this issue you just need to add `marginTop` to the `dropDownStyle` and specify the value which fits your component well.
```javascript
dropDownStyle={{marginTop: 2}}
```
### Styling the component
You have 9 options to style the component.
You have 10 options to style the component.
1. The `style` property.

@@ -207,3 +142,3 @@

```javacript
dropDownStyle={{backgroundColor: '#fafafa}}
dropDownStyle={{backgroundColor: '#fafafa'}}
```

@@ -253,2 +188,103 @@

```
10. The `searchableStyle` property.
Additional styles for the `TextInput`
```javacript
searchableStyle={{backgroundColor: '#dfdfdf'}}
```
### FAQ
#### Multiple pickers and the open dropdown issue
Clicking on another picker doesn't close the other pickers?
This can be fixed with the help of state.
```javascript
this.state = {
itemA: null,
isVisibleA: false,
itemB: null,
isVisibleB: false
}
changeVisibility(state) {
this.setState({
isVisibleA: false,
isVisibleB: false,
...state
});
}
// Picker A
<DropDownPicker
items={[
{label: 'UK', value: 'uk'},
{label: 'France', value: 'france'},
]}
defaultValue={this.state.itemA}
containerStyle={{height: 40}}
isVisible={this.state.isVisibleA}
onOpen={() => this.changeVisibility({
isVisibleA: true
})}
onClose={() => this.setState({
isVisibleA: false
})}
onChangeItem={item => this.setState({
itemA: item.value
})}
/>
// Picker B
<DropDownPicker
items={[
{label: 'UK', value: 'uk'},
{label: 'France', value: 'france'},
]}
defaultValue={this.state.itemB}
containerStyle={{height: 40}}
isVisible={this.state.isVisibleB}
onOpen={() => this.changeVisibility({
isVisibleB: true
})}
onClose={() => this.setState({
isVisibleB: false
})}
onChangeItem={item => this.setState({
itemB: item.value
})}
/>
```
#### borderRadius
The only thing you have to avoid is `borderRadius`. All the corners must be set separately.
```javascript
style={{
borderTopLeftRadius: 10, borderTopRightRadius: 10,
borderBottomLeftRadius: 10, borderBottomRightRadius: 10
}}
dropDownStyle={{
borderBottomLeftRadius: 20, borderBottomRightRadius: 20
}}
```
#### zIndex conflicts
A lot of users use the `containerStyle` property to style the picker which results in unexpected behaviors like untouchable scrollviews.
The `style` and `dropDownStyle` properties must be used instead.
Use the `containerStyle` prop to adjust the outer part of the picker such as `margin`, `width`, `height`, `flex`, ...
#### Dropdown Overflow
Adding borders to the component will separate or overflow elements. to solve this issue you just need to add `marginTop` to the `dropDownStyle` and specify the value which fits your component well.
```javascript
dropDownStyle={{marginTop: 2}}
```
### Props

@@ -258,6 +294,4 @@ |Name|Description|Type|Default|Required

|**`items`**|The items for the component.|`array`||Yes
|`defaultIndex`|The index of the default item.|`number`|`0`|No
|`defaultValue`|The value of the default item.|`any`||No
|`defaultNull`|This sets the choice to null which should be used with `placeholder`|`bool`|`true`|No
|`placeholder`|Default text to be shown to the user which must be used with `defaultNull`|`string`|'Select an item'|No
|`defaultValue`|The value of the default item. (If `multiple={true}`, it takes an array of pre-selected values: `['uk']`)|`any`||No
|`placeholder`|Default text to be shown to the user when `defaultValue={null}` or `defaultValue={[]}`|`string`|'Select an item'|No
|`dropDownMaxHeight`|Height of the dropdown box.|`number`|`150`|No

@@ -276,6 +310,17 @@ |`style`|Additional styles for the picker.|`object`|`{}`|No

|`showArrow`|An option to show/hide the arrow.|`bool`|`true`|No
|`customArrowUp`|Customize the arrow-up.|`jsx`|`null`|No
|`customArrowDown`|Customize the arrow-down.|`jsx`|`null`|No
|`customArrowUp`|Customize the arrow-up.|`jsx`|`(size, color) => ...`|No
|`customArrowDown`|Customize the arrow-down.|`jsx`|`(size, color) => ...`|No
|`customTickIcon`|Customize the tick icon for multiple item picker.|`jsx`|`() => ...`|No
|`zIndex`|This property specifies the stack order of the component.|`number`|`5000`|No
|`disabled`|This disables the component.|`bool`|`false`|No
|`onChangeItem`|Callback which returns `item` and `index`. The `item` is the selected object.|`function`||No
|`disabled`|Disables the component.|`bool`|`false`|No
|`isVisible`|Open or close the dropdown box.|`bool`|`false`|No
|`multiple`|If set to true selecting multiple items is possible.|`bool`|`false`|No
|`multipleText`|a Text to inform the user how many items have been selected.|`string`|`%d items have been selected`|No
|`min`|minimum number of items.|`number`|`0`|No
|`max`|maximum number of items.|`number`|`10000000`|No
|`searchable`|Shows a `TextInput` to search for specific items.|`bool`|`false`|No
|`searchableStyle`|Additional styles for the `TextInput`.|`object`|`{}`|No
|`searchableError`|Shows a message when nothing found.|`string`|`Not Found`|No
|`onOpen`|Fires when you open the picker.|`func`|`() => {}`|No
|`onClose`|Fires when you close the picker.|`func`|`() => {}`|No
|`onChangeItem`|Callback which returns `item` and `index`. The `item` is the selected object or an array of the selected values.|`func`||No

@@ -8,3 +8,4 @@ import React from 'react';

ScrollView,
Platform
Platform,
TextInput
} from 'react-native';

@@ -21,21 +22,35 @@ import PropTypes from 'prop-types';

let choice;
if (props.defaultNull || (props.hasOwnProperty('defaultValue') && props.defaultValue === null)) {
choice = this.null();
} else if (props.defaultValue) {
choice = props.items.find(item => item.value === props.defaultValue);
} else if (props.items.filter(item => item.hasOwnProperty('selected') && item.selected === true).length > 0) {
choice = props.items.filter(item => item.hasOwnProperty('selected') && item.selected === true)[0];
} else if (props.items.length > 0) {
choice = props.items[props.defaultIndex ?? 0];
let items = [];
if (! props.multiple) {
if (props.defaultValue) {
choice = props.items.find(item => item.value === props.defaultValue);
} else if (props.items.filter(item => item.hasOwnProperty('selected') && item.selected === true).length > 0) {
choice = props.items.filter(item => item.hasOwnProperty('selected') && item.selected === true)[0];
} else {
choice = this.null();
}
} else {
choice = this.null();
if (props.defaultValue && Array.isArray(props.defaultValue) && props.defaultValue.length > 0) {
props.defaultValue.forEach((value, index) => {
items.push(
props.items.find(item => item.value === value)
)
});
} else if (props.items.filter(item => item.hasOwnProperty('selected') && item.selected === true).length > 0) {
items = props.items.filter((item, index) => item.hasOwnProperty('selected') && item.selected === true);
}
}
this.state = {
choice: {
choice: props.multiple ? items : {
label: choice.label,
value: choice.value
},
visible: false,
searchableText: null,
isVisible: props.isVisible,
props: {
multiple: props.multiple,
defaultValue: props.defaultValue,
isVisible: props.isVisible
}
};

@@ -45,12 +60,60 @@ }

static getDerivedStateFromProps(props, state) {
if (props.defaultNull === true) {
// Change default value (! multiple)
if (! state.props.multiple && props.defaultValue !== state.props.defaultValue) {
const { label, value } = props.defaultValue === null ? {
label: null,
value: null
} : props.items.find(item => item.value === props.defaultValue);
return {
choice: {
label: null,
value: null
label, value
},
visible: props.disabled ? false : state.visible,
};
props: {
...state.props,
defaultValue: props.defaultValue
}
}
}
// Change default value (multiple)
if (state.props.multiple && JSON.stringify(props.defaultValue) !== JSON.stringify(state.props.defaultValue)) {
let items = [];
if (props.defaultValue && Array.isArray(props.defaultValue) && props.defaultValue.length > 0) {
props.defaultValue.forEach((value, index) => {
items.push(
props.items.find(item => item.value === value)
)
});
}
return {
choice: items,
props: {
...state.props,
defaultValue: props.defaultValue
}
}
}
// Change visibility
if (props.isVisible !== state.props.isVisible) {
return {
isVisible: props.isVisible,
props: {
...state.props,
isVisible: props.isVisible
}
}
}
// Change disability
if (props.disabled !== state.props.disabled) {
return {
props: {
...state.props,
disabled: props.disabled
}
}
}
return null;

@@ -68,3 +131,11 @@ }

this.setState({
visible: ! this.state.visible
isVisible: ! this.state.isVisible,
}, () => {
const isVisible = this.state.isVisible;
if (isVisible) {
this.props.onOpen();
} else {
this.props.onClose();
}
});

@@ -74,14 +145,39 @@ }

select(item, index) {
this.setState({
choice: {
label: item.label,
value: item.value
},
visible: false
});
const { multiple } = this.state.props;
if (! multiple) {
this.setState({
choice: {
label: item.label,
value: item.value
},
isVisible: false,
props: {
...this.state.props,
isVisible: false
}
});
this.props.defaultNull = false;
// onChangeItem callback
this.props.onChangeItem(item, index);
} else {
let choice = [...this.state.choice];
const exists = choice.findIndex(i => i.label === item.label && i.value === item.value);
// onChangeItem callback
this.props.onChangeItem(item, index);
if (exists > -1 && choice.length > this.props.min) {
choice = choice.filter(i => i.label !== item.label && i.value !== item.value);
} else if (exists === -1 && choice.length < this.props.max) {
choice.push(item);
}
this.setState({
choice
});
// onChangeItem callback
this.props.onChangeItem(choice.map(i => i.value));
}
// onClose callback (! multiple)
if (! multiple)
this.props.onClose();
}

@@ -95,19 +191,54 @@

getItems() {
if (this.state.searchableText) {
const text = this.state.searchableText.toLowerCase();
return this.props.items.filter((item) => {
return item.label && (item.label.toLowerCase()).indexOf(text) > -1;
});
}
return this.props.items;
}
getNumberOfItems() {
return this.props.multipleText.replace('%d', this.state.choice.length);
}
render() {
const { defaultNull, placeholder, disabled } = this.props;
const isPlaceholderActive = (defaultNull) && this.state.choice.label === null;
const { multiple, disabled } = this.state.props;
const { placeholder } = this.props;
const isPlaceholderActive = this.state.choice.label === null;
const label = isPlaceholderActive ? (placeholder) : this.state.choice.label;
const placeholderStyle = isPlaceholderActive && this.props.placeholderStyle;
const opacity = disabled ? 0.5 : 1;
const items = this.getItems();
return (
<View style={[this.props.containerStyle, {
...Platform.select({
ios: {
zIndex: this.props.zIndex
}
})
...(Platform.OS !== 'android' && {
zIndex: this.props.zIndex
})
}]}>
<TouchableOpacity onLayout={(event) => { this.getLayout(event.nativeEvent.layout) }} disabled={disabled} onPress={() => this.toggle()} activeOpacity={1} style={[styles.dropDown, this.props.style, this.state.visible && styles.noBottomRadius, {flexDirection: 'row', flex: 1}]}>
<TouchableOpacity
onLayout={(event) => this.getLayout(event.nativeEvent.layout)}
disabled={disabled}
onPress={() => this.toggle()}
activeOpacity={1}
style={[
styles.dropDown,
this.props.style,
this.state.isVisible && styles.noBottomRadius, {
flexDirection: 'row', flex: 1
}
]}
>
<View style={[styles.dropDownDisplay]}>
<Text style={[this.props.labelStyle, placeholderStyle, {opacity}]}>{label}</Text>
<Text style={[this.props.labelStyle, placeholderStyle, {opacity}]}>
{multiple ? (
this.state.choice.length > 0 ? this.getNumberOfItems() : placeholder
) : label}
</Text>
</View>

@@ -118,6 +249,6 @@ {this.props.showArrow && (

{
! this.state.visible ? (
this.props.customArrowUp ?? <Feather name="chevron-down" size={this.props.arrowSize} color={this.props.arrowColor} />
! this.state.isVisible ? (
this.props.customArrowDown(this.props.arrowSize, this.props.arrowColor)
) : (
this.props.customArrowDown ?? <Feather name="chevron-up" size={this.props.arrowSize} color={this.props.arrowColor} />
this.props.customArrowUp(this.props.arrowSize, this.props.arrowColor)
)

@@ -129,18 +260,65 @@ }

</TouchableOpacity>
<View style={[styles.dropDown, styles.dropDownBox, this.props.dropDownStyle, ! this.state.visible && styles.hidden, {
top: this.state.top,
maxHeight: this.props.dropDownMaxHeight,
zIndex: this.props.zIndex
}]}>
<ScrollView style={{width: '100%', zIndex: this.props.zIndex}} nestedScrollEnabled={true}>
<View style={[
styles.dropDown,
styles.dropDownBox,
this.props.dropDownStyle,
! this.state.isVisible && styles.hidden, {
top: this.state.top,
maxHeight: this.props.dropDownMaxHeight,
zIndex: this.props.zIndex
}
]}>
{
this.props.searchable && (
<View style={{width: '100%', flexDirection: 'row'}}>
<TextInput
style={[styles.input, this.props.searchableStyle]}
defaultValue={this.state.searchableText}
placeholder={this.props.searchablePlaceholder}
onChangeText={(text) => {
this.setState({
searchableText: text
})
}}
/>
</View>
)
}
<ScrollView style={{width: '100%'}} nestedScrollEnabled={true}>
{
this.props.items.map((item, index) => (
<TouchableOpacity key={index} onPress={() => this.select(item, index)} style={[styles.dropDownItem, this.props.itemStyle, (
this.state.choice.value === item.value && this.props.activeItemStyle
)]}>
items.length > 0 ? items.map((item, index) => (
<TouchableOpacity
key={index}
onPress={() => this.select(item, index)}
style={[styles.dropDownItem, this.props.itemStyle, (
this.state.choice.value === item.value && this.props.activeItemStyle
), {
opacity: item?.disabled || false === true ? 0.3 : 1,
...(
multiple && {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between'
}
)
}]}
disabled={item?.disabled || false === true}
>
<Text style={[this.props.labelStyle,
this.state.choice.value === item.value && this.props.activeLabelStyle
]}>{item.label}</Text>
{
multiple && this.state.choice.findIndex(i => i.label === item.label && i.value === item.value) > -1 && (
this.props.customTickIcon()
)
}
</TouchableOpacity>
))
)) : (
<Text style={[styles.notFound, {
fontFamily: this.props.labelStyle?.fontFamily
}]}>
{this.props.searchableError}
</Text>
)
}

@@ -155,3 +333,2 @@ </ScrollView>

DropDownPicker.defaultProps = {
defaultNull: false,
placeholder: 'Select an item',

@@ -171,6 +348,18 @@ dropDownMaxHeight: 150,

arrowSize: 15,
customArrowUp: null,
customArrowDown: null,
customArrowUp: (size, color) => <Feather name="chevron-up" size={size} color={color} />,
customArrowDown: (size, color) => <Feather name="chevron-down" size={size} color={color} />,
customTickIcon: () => <Feather name="check" size={15} />,
zIndex: 5000,
disabled: false,
searchable: false,
searchablePlaceholder: 'Search for an item',
searchableError: 'Not Found',
searchableStyle: {},
isVisible: false,
multiple: false,
multipleText: '%d items have been selected',
min: 0,
max: 10000000,
onOpen: () => {},
onClose: () => {},
onChangeItem: () => {},

@@ -181,5 +370,3 @@ };

items: PropTypes.array.isRequired,
defaultIndex: PropTypes.number,
defaultValue: PropTypes.any,
defaultNull: PropTypes.bool,
placeholder: PropTypes.string,

@@ -198,6 +385,18 @@ dropDownMaxHeight: PropTypes.number,

arrowSize: PropTypes.number,
customArrowUp: PropTypes.any,
customArrowDown: PropTypes.any,
customArrowUp: PropTypes.func,
customArrowDown: PropTypes.func,
customTickIcon: PropTypes.func,
zIndex: PropTypes.number,
disabled: PropTypes.bool,
searchable: PropTypes.bool,
searchablePlaceholder: PropTypes.string,
searchableError: PropTypes.string,
searchableStyle: PropTypes.object,
isVisible: PropTypes.bool,
multiple: PropTypes.bool,
multipleText: PropTypes.string,
min: PropTypes.number,
max: PropTypes.number,
onOpen: PropTypes.func,
onClose: PropTypes.func,
onChangeItem: PropTypes.func

@@ -250,2 +449,10 @@ };

},
input: {
flex: 1,
borderColor: '#dfdfdf',
borderBottomWidth: 1,
paddingHorizontal: 0,
paddingVertical: 8,
marginBottom: 10,
},
hidden: {

@@ -258,2 +465,8 @@ position: 'relative',

borderBottomRightRadius: 0,
},
notFound: {
textAlign: 'center',
color: 'grey',
marginVertical: 10,
marginBottom: 15
}

@@ -260,0 +473,0 @@ });

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc