diff --git a/src/components/dropdown/index.js b/src/components/dropdown/index.js
index b1e65a1f..16ae5930 100644
--- a/src/components/dropdown/index.js
+++ b/src/components/dropdown/index.js
@@ -1,17 +1,6 @@
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
-import {
- Text,
- View,
- FlatList,
- Animated,
- Modal,
- TouchableWithoutFeedback,
- Dimensions,
- Platform,
- ViewPropTypes,
- I18nManager,
-} from 'react-native';
+import { Text, View, ScrollView, Animated, Modal, TouchableWithoutFeedback, Dimensions, Platform, ViewPropTypes, I18nManager, Keyboard } from 'react-native';
import Ripple from 'react-native-material-ripple';
import { TextField } from 'react-native-material-textfield';
@@ -28,15 +17,9 @@ export default class Dropdown extends PureComponent {
valueExtractor: ({ value } = {}, index) => value,
labelExtractor: ({ label } = {}, index) => label,
- propsExtractor: () => null,
absoluteRTLLayout: false,
- dropdownOffset: {
- top: 32,
- left: 0,
- },
-
dropdownMargins: {
min: 8,
max: 16,
@@ -55,9 +38,7 @@ export default class Dropdown extends PureComponent {
rippleOpacity: 0.54,
shadeOpacity: 0.12,
- rippleDuration: 400,
animationDuration: 225,
-
fontSize: 16,
textColor: 'rgba(0, 0, 0, .87)',
@@ -67,15 +48,9 @@ export default class Dropdown extends PureComponent {
itemCount: 4,
itemPadding: 8,
- supportedOrientations: [
- 'portrait',
- 'portrait-upside-down',
- 'landscape',
- 'landscape-left',
- 'landscape-right',
- ],
+ labelHeight: 32,
- useNativeDriver: false,
+ supportedOrientations: ['portrait', 'portrait-upside-down', 'landscape', 'landscape-left', 'landscape-right'],
};
static propTypes = {
@@ -83,24 +58,15 @@ export default class Dropdown extends PureComponent {
disabled: PropTypes.bool,
- value: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.number,
- ]),
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
data: PropTypes.arrayOf(PropTypes.object),
valueExtractor: PropTypes.func,
labelExtractor: PropTypes.func,
- propsExtractor: PropTypes.func,
absoluteRTLLayout: PropTypes.bool,
- dropdownOffset: PropTypes.shape({
- top: PropTypes.number.isRequired,
- left: PropTypes.number.isRequired,
- }),
-
dropdownMargins: PropTypes.shape({
min: PropTypes.number.isRequired,
max: PropTypes.number.isRequired,
@@ -108,7 +74,6 @@ export default class Dropdown extends PureComponent {
dropdownPosition: PropTypes.number,
- rippleColor: PropTypes.string,
rippleCentered: PropTypes.bool,
rippleSequential: PropTypes.bool,
@@ -122,15 +87,12 @@ export default class Dropdown extends PureComponent {
rippleOpacity: PropTypes.number,
shadeOpacity: PropTypes.number,
- rippleDuration: PropTypes.number,
animationDuration: PropTypes.number,
-
fontSize: PropTypes.number,
textColor: PropTypes.string,
itemColor: PropTypes.string,
selectedItemColor: PropTypes.string,
- disabledItemColor: PropTypes.string,
baseColor: PropTypes.string,
itemTextStyle: Text.propTypes.style,
@@ -138,6 +100,8 @@ export default class Dropdown extends PureComponent {
itemCount: PropTypes.number,
itemPadding: PropTypes.number,
+ labelHeight: PropTypes.number,
+
onLayout: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
@@ -147,12 +111,9 @@ export default class Dropdown extends PureComponent {
renderAccessory: PropTypes.func,
containerStyle: (ViewPropTypes || View.propTypes).style,
- overlayStyle: (ViewPropTypes || View.propTypes).style,
pickerStyle: (ViewPropTypes || View.propTypes).style,
supportedOrientations: PropTypes.arrayOf(PropTypes.string),
-
- useNativeDriver: PropTypes.bool,
};
constructor(props) {
@@ -162,17 +123,12 @@ export default class Dropdown extends PureComponent {
this.onClose = this.onClose.bind(this);
this.onSelect = this.onSelect.bind(this);
this.onLayout = this.onLayout.bind(this);
-
this.updateRippleRef = this.updateRef.bind(this, 'ripple');
this.updateContainerRef = this.updateRef.bind(this, 'container');
this.updateScrollRef = this.updateRef.bind(this, 'scroll');
-
this.renderAccessory = this.renderAccessory.bind(this);
- this.renderItem = this.renderItem.bind(this);
-
- this.keyExtractor = this.keyExtractor.bind(this);
- this.blur = () => this.onClose();
+ this.blur = this.onClose;
this.focus = this.onPress;
let { value } = this.props;
@@ -194,12 +150,24 @@ export default class Dropdown extends PureComponent {
}
}
+ componentWillMount() {
+ const handler = (e) => {
+ if (this.state.modal) {
+ this.onPress(null);
+ }
+ };
+ this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', handler);
+ this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', handler);
+ }
+
componentDidMount() {
this.mounted = true;
}
componentWillUnmount() {
this.mounted = false;
+ this.keyboardDidShowListener.remove();
+ this.keyboardDidHideListener.remove();
}
onPress(event) {
@@ -207,13 +175,12 @@ export default class Dropdown extends PureComponent {
data,
disabled,
onFocus,
+ labelHeight,
itemPadding,
- rippleDuration,
- dropdownOffset,
dropdownMargins: { min: minMargin, max: maxMargin },
+ dropdownPosition,
animationDuration,
absoluteRTLLayout,
- useNativeDriver,
} = this.props;
if (disabled) {
@@ -221,12 +188,13 @@ export default class Dropdown extends PureComponent {
}
let itemCount = data.length;
+ let visibleItemCount = this.visibleItemCount();
+ let tailItemCount = this.tailItemCount();
let timestamp = Date.now();
if (null != event) {
/* Adjust event location */
event.nativeEvent.locationY -= this.rippleInsets().top;
- event.nativeEvent.locationX -= this.rippleInsets().left;
/* Start ripple directly from event */
this.ripple.startRipple(event);
@@ -252,13 +220,40 @@ export default class Dropdown extends PureComponent {
x = dimensions.width - (x + containerWidth);
}
- let delay = Math.max(0, rippleDuration - animationDuration - (Date.now() - timestamp));
+ let delay = Math.max(0, animationDuration - (Date.now() - timestamp));
let selected = this.selectedIndex();
+ let offset = 0;
+
+ if (itemCount > visibleItemCount) {
+ if (null == dropdownPosition) {
+ switch (selected) {
+ case -1:
+ break;
+
+ case 0:
+ case 1:
+ break;
+
+ default:
+ if (selected >= itemCount - tailItemCount) {
+ offset = this.itemSize() * (itemCount - visibleItemCount);
+ } else {
+ offset = this.itemSize() * (selected - 1);
+ }
+ }
+ } else {
+ if (~selected) {
+ if (dropdownPosition < 0) {
+ offset = this.itemSize() * (selected - visibleItemCount - dropdownPosition);
+ } else {
+ offset = this.itemSize() * (selected - dropdownPosition);
+ }
+ }
+ }
+ }
+ let left = x - maxMargin;
let leftInset;
- let left = x
- + dropdownOffset.left
- - maxMargin;
if (left > minMargin) {
leftInset = maxMargin;
@@ -277,9 +272,7 @@ export default class Dropdown extends PureComponent {
rightInset = minMargin;
}
- let top = y
- + dropdownOffset.top
- - itemPadding;
+ let top = y + Platform.select({ ios: 1, android: 2 }) + labelHeight - itemPadding;
this.setState({
modal: true,
@@ -291,70 +284,60 @@ export default class Dropdown extends PureComponent {
selected,
});
- setTimeout((() => {
+ setTimeout(() => {
if (this.mounted) {
- this.resetScrollOffset();
-
- Animated
- .timing(opacity, {
- duration: animationDuration,
- toValue: 1,
- useNativeDriver,
- })
- .start(() => {
- if (this.mounted && 'ios' === Platform.OS) {
- let { flashScrollIndicators } = this.scroll || {};
-
- if ('function' === typeof flashScrollIndicators) {
- flashScrollIndicators.call(this.scroll);
- }
+ if (this.scroll) {
+ this.scroll.scrollTo({ x: 0, y: offset, animated: false });
+ }
+
+ Animated.timing(opacity, {
+ duration: animationDuration,
+ toValue: 1,
+ }).start(() => {
+ if (this.mounted && 'ios' === Platform.OS) {
+ let { flashScrollIndicators } = this.scroll || {};
+
+ if ('function' === typeof flashScrollIndicators) {
+ flashScrollIndicators.call(this.scroll);
}
- });
+ }
+ });
}
- }), delay);
+ }, delay);
});
}
- onClose(value = this.state.value) {
- let { onBlur, animationDuration, useNativeDriver } = this.props;
+ onClose() {
+ let { onBlur, animationDuration } = this.props;
let { opacity } = this.state;
- Animated
- .timing(opacity, {
- duration: animationDuration,
- toValue: 0,
- useNativeDriver,
- })
- .start(() => {
- this.focused = false;
-
- if ('function' === typeof onBlur) {
- onBlur();
- }
+ Animated.timing(opacity, {
+ duration: animationDuration,
+ toValue: 0,
+ }).start(() => {
+ this.focused = false;
- if (this.mounted) {
- this.setState({ value, modal: false });
- }
- });
+ if ('function' === typeof onBlur) {
+ onBlur();
+ }
+
+ if (this.mounted) {
+ this.setState({ modal: false });
+ }
+ });
}
onSelect(index) {
- let {
- data,
- valueExtractor,
- onChangeText,
- animationDuration,
- rippleDuration,
- } = this.props;
-
+ let { data, valueExtractor, onChangeText, animationDuration } = this.props;
let value = valueExtractor(data[index], index);
- let delay = Math.max(0, rippleDuration - animationDuration);
+
+ this.setState({ value });
if ('function' === typeof onChangeText) {
onChangeText(value, index, data);
}
- setTimeout(() => this.onClose(value), delay);
+ setTimeout(this.onClose, animationDuration);
}
onLayout(event) {
@@ -375,8 +358,7 @@ export default class Dropdown extends PureComponent {
let { value } = this.state;
let { data, valueExtractor } = this.props;
- return data
- .findIndex((item, index) => null != item && value === valueExtractor(item, index));
+ return data.findIndex((item, index) => null != item && value === valueExtractor(item, index));
}
selectedItem() {
@@ -392,7 +374,7 @@ export default class Dropdown extends PureComponent {
itemSize() {
let { fontSize, itemPadding } = this.props;
- return Math.ceil(fontSize * 1.5 + itemPadding * 2);
+ return fontSize * 1.5 + itemPadding * 2;
}
visibleItemCount() {
@@ -406,127 +388,41 @@ export default class Dropdown extends PureComponent {
}
rippleInsets() {
- let {
- top = 16,
- right = 0,
- bottom = -8,
- left = 0,
- } = this.props.rippleInsets || {};
+ let { top = 16, right = 0, bottom = -8, left = 0 } = this.props.rippleInsets || {};
return { top, right, bottom, left };
}
- resetScrollOffset() {
- let { selected } = this.state;
- let { data, dropdownPosition } = this.props;
-
- let offset = 0;
- let itemCount = data.length;
- let itemSize = this.itemSize();
- let tailItemCount = this.tailItemCount();
- let visibleItemCount = this.visibleItemCount();
-
- if (itemCount > visibleItemCount) {
- if (null == dropdownPosition) {
- switch (selected) {
- case -1:
- break;
-
- case 0:
- case 1:
- break;
-
- default:
- if (selected >= itemCount - tailItemCount) {
- offset = itemSize * (itemCount - visibleItemCount);
- } else {
- offset = itemSize * (selected - 1);
- }
- }
- } else {
- let index = selected - dropdownPosition;
-
- if (dropdownPosition < 0) {
- index -= visibleItemCount;
- }
-
- index = Math.max(0, index);
- index = Math.min(index, itemCount - visibleItemCount);
-
- if (~selected) {
- offset = itemSize * index;
- }
- }
- }
-
- if (this.scroll) {
- this.scroll.scrollToOffset({ offset, animated: false });
- }
- }
-
updateRef(name, ref) {
this[name] = ref;
}
- keyExtractor(item, index) {
- let { valueExtractor } = this.props;
-
- return `${index}-${valueExtractor(item, index)}`;
- }
-
renderBase(props) {
let { value } = this.state;
- let {
- data,
- renderBase,
- labelExtractor,
- dropdownOffset,
- renderAccessory = this.renderAccessory,
- } = this.props;
+ let { data, renderBase, labelExtractor, renderAccessory = this.renderAccessory } = this.props;
let index = this.selectedIndex();
- let title;
+ let label;
if (~index) {
- title = labelExtractor(data[index], index);
+ label = labelExtractor(data[index], index);
}
- if (null == title) {
- title = value;
+ if (null == label) {
+ label = value;
}
if ('function' === typeof renderBase) {
- return renderBase({ ...props, title, value, renderAccessory });
+ return renderBase({ ...props, label, value, renderAccessory });
}
- title = null == title || 'string' === typeof title?
- title:
- String(title);
-
- return (
-
- );
+ return ;
}
renderRipple() {
- let {
- baseColor,
- rippleColor = baseColor,
- rippleOpacity,
- rippleDuration,
- rippleCentered,
- rippleSequential,
- } = this.props;
+ let { baseColor, animationDuration, rippleOpacity, rippleCentered, rippleSequential } = this.props;
let { bottom, ...insets } = this.rippleInsets();
let style = {
@@ -536,17 +432,7 @@ export default class Dropdown extends PureComponent {
position: 'absolute',
};
- return (
-
- );
+ return ;
}
renderAccessory() {
@@ -562,79 +448,47 @@ export default class Dropdown extends PureComponent {
);
}
- renderItem({ item, index }) {
- if (null == item) {
- return null;
- }
-
+ renderItems() {
let { selected, leftInset, rightInset } = this.state;
- let {
- valueExtractor,
- labelExtractor,
- propsExtractor,
- textColor,
- itemColor,
+ let { data, valueExtractor, labelExtractor, textColor, itemColor, selectedItemColor = textColor, baseColor, fontSize, itemTextStyle, animationDuration, rippleOpacity, shadeOpacity } = this.props;
+
+ let props = {
baseColor,
- selectedItemColor = textColor,
- disabledItemColor = baseColor,
fontSize,
- itemTextStyle,
+ animationDuration,
rippleOpacity,
- rippleDuration,
shadeOpacity,
- } = this.props;
-
- let props = propsExtractor(item, index);
-
- let { style, disabled }
- = props
- = {
- rippleDuration,
- rippleOpacity,
- rippleColor: baseColor,
-
- shadeColor: baseColor,
- shadeOpacity,
-
- ...props,
-
- onPress: this.onSelect,
- };
+ onPress: this.onSelect,
+ style: {
+ height: this.itemSize(),
+ paddingLeft: leftInset,
+ paddingRight: rightInset,
+ },
+ };
- let value = valueExtractor(item, index);
- let label = labelExtractor(item, index);
+ return data.map((item, index) => {
+ if (null == item) {
+ return null;
+ }
- let title = null == label?
- value:
- label;
+ let value = valueExtractor(item, index);
+ let label = labelExtractor(item, index);
- let color = disabled?
- disabledItemColor:
- ~selected?
- index === selected?
- selectedItemColor:
- itemColor:
- selectedItemColor;
+ let title = null == label ? value : label;
- let textStyle = { color, fontSize };
+ let color = ~selected ? (index === selected ? selectedItemColor : itemColor) : selectedItemColor;
- props.style = [
- style,
- {
- height: this.itemSize(),
- paddingLeft: leftInset,
- paddingRight: rightInset,
- },
- ];
+ let style = { color, fontSize };
- return (
-
-
- {title}
-
-
- );
+ return (
+
+
+ {title}
+
+
+ );
+ });
}
render() {
@@ -642,7 +496,6 @@ export default class Dropdown extends PureComponent {
renderBase,
renderAccessory,
containerStyle,
- overlayStyle: overlayStyleOverrides,
pickerStyle: pickerStyleOverrides,
rippleInsets,
@@ -662,27 +515,29 @@ export default class Dropdown extends PureComponent {
...props
} = this.props;
- let {
- data,
- disabled,
- itemPadding,
- dropdownPosition,
- } = props;
+ let { data, disabled, itemPadding, dropdownPosition } = props;
let { left, top, width, opacity, selected, modal } = this.state;
+ let dimensions = Dimensions.get('window');
+
let itemCount = data.length;
let visibleItemCount = this.visibleItemCount();
let tailItemCount = this.tailItemCount();
let itemSize = this.itemSize();
+ let overlayStyle = {
+ width: dimensions.width,
+ height: dimensions.height,
+ };
+
let height = 2 * itemPadding + itemSize * visibleItemCount;
let translateY = -itemPadding;
if (null == dropdownPosition) {
switch (selected) {
case -1:
- translateY -= 1 === itemCount? 0 : itemSize;
+ translateY -= 1 === itemCount ? 0 : itemSize;
break;
case 0:
@@ -703,13 +558,12 @@ export default class Dropdown extends PureComponent {
}
}
- let overlayStyle = { opacity };
-
let pickerStyle = {
width,
height,
top,
left,
+ opacity,
transform: [{ translateY }],
};
@@ -727,38 +581,22 @@ export default class Dropdown extends PureComponent {
return (
-
+
{this.renderBase(props)}
{this.renderRipple()}
-
- true}
- onResponderRelease={this.blur}
- >
- true}
- >
-
+
+
+
+
+
+ {this.renderItems()}
+
+
-
+
);