text: import PropTypes from 'prop-types';
  import React, { PureComponent } from 'react';
  import {
    Text,
    View,
    FlatList,
    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';
  
  import DropdownItem from '../item';
  import styles from './styles';
  
  export default class Dropdown extends PureComponent {
    static defaultProps = {
      hitSlop: { top: 6, right: 4, bottom: 6, left: 4 },
  
      disabled: false,
  
      data: [],
  
      valueExtractor: ({ value } = {}, index) => value,
      labelExtractor: ({ label } = {}, index) => label,
      propsExtractor: () => null,
  
      absoluteRTLLayout: false,
  
      dropdownOffset: {
        top: 32,
        left: 0,
      },
  
      dropdownMargins: {
        min: 8,
        max: 16,
      },
  
      rippleCentered: false,
      rippleSequential: true,
  
      rippleInsets: {
        top: 16,
        right: 0,
        bottom: -8,
        left: 0,
      },
  
      rippleOpacity: 0.54,
      shadeOpacity: 0.12,
  
      rippleDuration: 400,
      animationDuration: 225,
  
      fontSize: 16,
  
      textColor: 'rgba(0, 0, 0, .87)',
      itemColor: 'rgba(0, 0, 0, .54)',
      baseColor: 'rgba(0, 0, 0, .38)',
  
      itemCount: 4,
      itemPadding: 8,
  
      supportedOrientations: [
        'portrait',
        'portrait-upside-down',
        'landscape',
        'landscape-left',
        'landscape-right',
      ],
  
      useNativeDriver: false,
    };
  
    static propTypes = {
      ...TouchableWithoutFeedback.propTypes,
  
      disabled: PropTypes.bool,
  
      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,
      }),
  
      dropdownPosition: PropTypes.number,
  
      rippleColor: PropTypes.string,
      rippleCentered: PropTypes.bool,
      rippleSequential: PropTypes.bool,
  
      rippleInsets: PropTypes.shape({
        top: PropTypes.number,
        right: PropTypes.number,
        bottom: PropTypes.number,
        left: PropTypes.number,
      }),
  
      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,
  
      itemCount: PropTypes.number,
      itemPadding: PropTypes.number,
  
      onLayout: PropTypes.func,
      onFocus: PropTypes.func,
      onBlur: PropTypes.func,
      onChangeText: PropTypes.func,
  
      renderBase: PropTypes.func,
      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) {
      super(props);
  
      this.onPress = this.onPress.bind(this);
      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.focus = this.onPress;
  
      let { value } = this.props;
  
      this.mounted = false;
      this.focused = false;
  
      this.state = {
        opacity: new Animated.Value(0),
        selected: -1,
        modal: false,
        value,
      };
    }
  
    componentWillReceiveProps({ value }) {
      if (value !== this.props.value) {
        this.setState({ value });
      }
    }
  
    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() {
      return true;
  }
  
    componentDidMount() {
      this.mounted = true;
    }
  
    componentWillUnmount() {
      this.mounted = false;
    }
  
    onPress(event) {
      let {
        data,
        disabled,
        onFocus,
        itemPadding,
        rippleDuration,
        dropdownOffset,
        dropdownMargins: { min: minMargin, max: maxMargin },
        animationDuration,
        absoluteRTLLayout,
        useNativeDriver,
      } = this.props;
  
      if (disabled) {
        return;
      }
  
      let itemCount = data.length;
      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);
      }
  
      if (!itemCount) {
        return;
      }
  
      this.focused = true;
  
      if ('function' === typeof onFocus) {
        onFocus();
      }
  
      let dimensions = Dimensions.get('window');
  
      this.container.measureInWindow((x, y, containerWidth, containerHeight) => {
        let { opacity } = this.state;
  
        /* Adjust coordinates for relative layout in RTL locale */
        if (I18nManager.isRTL && !absoluteRTLLayout) {
          x = dimensions.width - (x + containerWidth);
        }
  
        let delay = Math.max(0, rippleDuration - animationDuration - (Date.now() - timestamp));
        let selected = this.selectedIndex();
  
        let leftInset;
        let left = x
          + dropdownOffset.left
          - maxMargin;
  
        if (left > minMargin) {
          leftInset = maxMargin;
        } else {
          left = minMargin;
          leftInset = minMargin;
        }
  
        let right = x + containerWidth + maxMargin;
        let rightInset;
  
        if (dimensions.width - right > minMargin) {
          rightInset = maxMargin;
        } else {
          right = dimensions.width - minMargin;
          rightInset = minMargin;
        }
  
        let top = y
          + dropdownOffset.top
          - itemPadding;
  
        this.setState({
          modal: true,
          width: right - left,
          top,
          left,
          leftInset,
          rightInset,
          selected,
        });
  
        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);
                  }
                }
              });
          }
        }), delay);
      });
    }
  
    onClose(value = this.state.value) {
      let { onBlur, animationDuration, useNativeDriver } = this.props;
      let { opacity } = this.state;
  
      Animated
        .timing(opacity, {
          duration: animationDuration,
          toValue: 0,
          useNativeDriver,
        })
        .start(() => {
          this.focused = false;
  
          if ('function' === typeof onBlur) {
            onBlur();
          }
  
          if (this.mounted) {
            this.setState({ value, modal: false });
          }
        });
    }
  
    onSelect(index) {
      let {
        data,
        valueExtractor,
        onChangeText,
        animationDuration,
        rippleDuration,
      } = this.props;
  
      let value = valueExtractor(data[index], index);
      let delay = Math.max(0, rippleDuration - animationDuration);
  
      if ('function' === typeof onChangeText) {
        onChangeText(value, index, data);
      }
  
      setTimeout(() => this.onClose(value), delay);
    }
  
    onLayout(event) {
      let { onLayout } = this.props;
  
      if ('function' === typeof onLayout) {
        onLayout(event);
      }
    }
  
    value() {
      let { value } = this.state;
  
      return value;
    }
  
    selectedIndex() {
      let { value } = this.state;
      let { data, valueExtractor } = this.props;
  
      return data
        .findIndex((item, index) => null != item && value === valueExtractor(item, index));
    }
  
    selectedItem() {
      let { data } = this.props;
  
      return data[this.selectedIndex()];
    }
  
    isFocused() {
      return this.focused;
    }
  
    itemSize() {
      let { fontSize, itemPadding } = this.props;
  
      return Math.ceil(fontSize * 1.5 + itemPadding * 2);
    }
  
    visibleItemCount() {
      let { data, itemCount } = this.props;
  
      return Math.min(data.length, itemCount);
    }
  
    tailItemCount() {
      return Math.max(this.visibleItemCount() - 2, 0);
    }
  
    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 index = this.selectedIndex();
      let title;
  
      if (~index) {
        title = labelExtractor(data[index], index);
      }
  
      if (null == title) {
        title = value;
      }
  
      if ('function' === typeof renderBase) {
        return renderBase({ ...props, title, value, renderAccessory });
      }
  
      title = null == title || 'string' === typeof title?
        title:
        String(title);
  
      return (
        <TextField
          label=''
          labelHeight={dropdownOffset.top - Platform.select({ ios: 1, android: 2 })}
  
          {...props}
  
          value={title}
          editable={false}
          onChangeText={undefined}
          renderAccessory={renderAccessory}
        />
      );
    }
  
    renderRipple() {
      let {
        baseColor,
        rippleColor = baseColor,
        rippleOpacity,
        rippleDuration,
        rippleCentered,
        rippleSequential,
      } = this.props;
  
      let { bottom, ...insets } = this.rippleInsets();
      let style = {
        ...insets,
  
        height: this.itemSize() - bottom,
        position: 'absolute',
      };
  
      return (
        <Ripple
          style={style}
          rippleColor={rippleColor}
          rippleDuration={rippleDuration}
          rippleOpacity={rippleOpacity}
          rippleCentered={rippleCentered}
          rippleSequential={rippleSequential}
          ref={this.updateRippleRef}
        />
      );
    }
  
    renderAccessory() {
      let { baseColor: backgroundColor } = this.props;
      let triangleStyle = { backgroundColor };
  
      return (
        <View style={styles.accessory}>
          <View style={styles.triangleContainer}>
            <View style={[styles.triangle, triangleStyle]} />
          </View>
        </View>
      );
    }
  
    renderItem({ item, index }) {
      if (null == item) {
        return null;
      }
  
      let { selected, leftInset, rightInset } = this.state;
  
      let {
        valueExtractor,
        labelExtractor,
        propsExtractor,
        textColor,
        itemColor,
        baseColor,
        selectedItemColor = textColor,
        disabledItemColor = baseColor,
        fontSize,
        itemTextStyle,
        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,
        };
  
      let value = valueExtractor(item, index);
      let label = labelExtractor(item, index);
  
      let title = null == label?
        value:
        label;
  
      let color = disabled?
        disabledItemColor:
        ~selected?
          index === selected?
            selectedItemColor:
            itemColor:
          selectedItemColor;
  
      let textStyle = { color, fontSize };
  
      props.style = [
        style,
        {
          height: this.itemSize(),
          paddingLeft: leftInset,
          paddingRight: rightInset,
        },
      ];
  
      return (
        <DropdownItem index={index} {...props}>
          <Text style={[styles.item, itemTextStyle, textStyle]} numberOfLines={1}>
            {title}
          </Text>
        </DropdownItem>
      );
    }
  
    render() {
      let {
        renderBase,
        renderAccessory,
        containerStyle,
        overlayStyle: overlayStyleOverrides,
        pickerStyle: pickerStyleOverrides,
  
        rippleInsets,
        rippleOpacity,
        rippleCentered,
        rippleSequential,
  
        hitSlop,
        pressRetentionOffset,
        testID,
        nativeID,
        accessible,
        accessibilityLabel,
  
        supportedOrientations,
  
        ...props
      } = this.props;
  
      let {
        data,
        disabled,
        itemPadding,
        dropdownPosition,
      } = props;
  
      let { left, top, width, opacity, selected, modal } = this.state;
  
      let itemCount = data.length;
      let visibleItemCount = this.visibleItemCount();
      let tailItemCount = this.tailItemCount();
      let itemSize = this.itemSize();
  
      let height = 2 * itemPadding + itemSize * visibleItemCount;
      let translateY = -itemPadding;
  
      if (null == dropdownPosition) {
        switch (selected) {
          case -1:
            translateY -= 1 === itemCount? 0 : itemSize;
            break;
  
          case 0:
            break;
  
          default:
            if (selected >= itemCount - tailItemCount) {
              translateY -= itemSize * (visibleItemCount - (itemCount - selected));
            } else {
              translateY -= itemSize;
            }
        }
      } else {
        if (dropdownPosition < 0) {
          translateY -= itemSize * (visibleItemCount + dropdownPosition);
        } else {
          translateY -= itemSize * dropdownPosition;
        }
      }
  
      let overlayStyle = { opacity };
  
      let pickerStyle = {
        width,
        height,
        top,
        left,
        transform: [{ translateY }],
      };
  
      let touchableProps = {
        disabled,
        hitSlop,
        pressRetentionOffset,
        onPress: this.onPress,
        testID,
        nativeID,
        accessible,
        accessibilityLabel,
      };
  
      return (
        <View onLayout={this.onLayout} ref={this.updateContainerRef} style={containerStyle}>
          <TouchableWithoutFeedback {...touchableProps}>
            <View pointerEvents='box-only'>
              {this.renderBase(props)}
              {this.renderRipple()}
            </View>
          </TouchableWithoutFeedback>
  
          <Modal
            visible={modal}
            transparent={true}
            onRequestClose={this.blur}
            supportedOrientations={supportedOrientations}
          >
            <Animated.View
              style={[styles.overlay, overlayStyle, overlayStyleOverrides]}
              onStartShouldSetResponder={() => true}
              onResponderRelease={this.blur}
            >
              <View
                style={[styles.picker, pickerStyle, pickerStyleOverrides]}
                onStartShouldSetResponder={() => true}
              >
                <FlatList
                  ref={this.updateScrollRef}
                  data={data}
                  style={styles.scroll}
                  renderItem={this.renderItem}
                  keyExtractor={this.keyExtractor}
                  scrollEnabled={visibleItemCount < itemCount}
                  contentContainerStyle={styles.scrollContainer}
                />
              </View>
            </Animated.View>
          </Modal>
        </View>
      );
    }
  }
  