diff --git a/components/ModalDropdown.js b/components/ModalDropdown.js index ace1472..f71f2d1 100644 --- a/components/ModalDropdown.js +++ b/components/ModalDropdown.js @@ -1,441 +1,434 @@ /** * Created by sohobloo on 16/9/13. + * Modifiedd by vitorizkiimanda on 19/01/10. */ 'use strict'; -import React, { - Component, -} from 'react'; +// https://github.com/sohobloo/react-native-modal-dropdown +import React, { Component } from 'react'; import { - StyleSheet, - Dimensions, - View, - Text, - TouchableWithoutFeedback, - TouchableNativeFeedback, - TouchableOpacity, - TouchableHighlight, - Modal, - ActivityIndicator, + StyleSheet, + Dimensions, + View, + Text, + TouchableWithoutFeedback, + TouchableNativeFeedback, + TouchableOpacity, + TouchableHighlight, + Modal, + ActivityIndicator, + ListView, } from 'react-native'; -import ListView from "deprecated-react-native-listview"; +// import ListView from 'deprecated-react-native-listview'; import PropTypes from 'prop-types'; const TOUCHABLE_ELEMENTS = [ - 'TouchableHighlight', - 'TouchableOpacity', - 'TouchableWithoutFeedback', - 'TouchableNativeFeedback' + 'TouchableHighlight', + 'TouchableOpacity', + 'TouchableWithoutFeedback', + 'TouchableNativeFeedback', ]; export default class ModalDropdown extends Component { - static propTypes = { - disabled: PropTypes.bool, - scrollEnabled: PropTypes.bool, - defaultIndex: PropTypes.number, - defaultValue: PropTypes.string, - options: PropTypes.array, - - accessible: PropTypes.bool, - animated: PropTypes.bool, - showsVerticalScrollIndicator: PropTypes.bool, - keyboardShouldPersistTaps: PropTypes.string, - - style: PropTypes.oneOfType([PropTypes.number, PropTypes.object, PropTypes.array]), - textStyle: PropTypes.oneOfType([PropTypes.number, PropTypes.object, PropTypes.array]), - dropdownStyle: PropTypes.oneOfType([PropTypes.number, PropTypes.object, PropTypes.array]), - dropdownTextStyle: PropTypes.oneOfType([PropTypes.number, PropTypes.object, PropTypes.array]), - dropdownTextHighlightStyle: PropTypes.oneOfType([PropTypes.number, PropTypes.object, PropTypes.array]), - - adjustFrame: PropTypes.func, - renderRow: PropTypes.func, - renderSeparator: PropTypes.func, - renderButtonText: PropTypes.func, - - onDropdownWillShow: PropTypes.func, - onDropdownWillHide: PropTypes.func, - onSelect: PropTypes.func - }; - - static defaultProps = { - disabled: false, - scrollEnabled: true, - defaultIndex: -1, - defaultValue: 'Please select...', - options: null, - animated: true, - showsVerticalScrollIndicator: true, - keyboardShouldPersistTaps: 'never' - }; - - constructor(props) { - super(props); - - this._button = null; - this._buttonFrame = null; - this._nextValue = null; - this._nextIndex = null; - - this.state = { - accessible: !!props.accessible, - loading: !props.options, - showDropdown: false, - buttonText: props.defaultValue, - selectedIndex: props.defaultIndex - }; - } - - componentWillReceiveProps(nextProps) { - let {buttonText, selectedIndex} = this.state; - const {defaultIndex, defaultValue, options} = nextProps; - buttonText = this._nextValue == null ? buttonText : this._nextValue; - selectedIndex = this._nextIndex == null ? selectedIndex : this._nextIndex; - if (selectedIndex < 0) { - selectedIndex = defaultIndex; - if (selectedIndex < 0) { - buttonText = defaultValue; - } - } - this._nextValue = null; - this._nextIndex = null; - - this.setState({ - loading: !options, - buttonText, - selectedIndex - }); - } - - render() { - return ( - - {this._renderButton()} - {this._renderModal()} - - ); - } - - _updatePosition(callback) { - if (this._button && this._button.measure) { - this._button.measure((fx, fy, width, height, px, py) => { - this._buttonFrame = {x: px, y: py, w: width, h: height}; - callback && callback(); - }); - } - } - - show() { - this._updatePosition(() => { - this.setState({ - showDropdown: true - }); - }); - } - - hide() { - this.setState({ - showDropdown: false - }); - } - - select(idx) { - const {defaultValue, options, defaultIndex, renderButtonText} = this.props; - - let value = defaultValue; - if (idx == null || !options || idx >= options.length) { - idx = defaultIndex; - } - - if (idx >= 0) { - value = renderButtonText ? renderButtonText(options[idx]) : options[idx].toString(); - } - - this._nextValue = value; - this._nextIndex = idx; - - this.setState({ - buttonText: value, - selectedIndex: idx - }); - } - - _renderButton() { - const {disabled, accessible, children, textStyle} = this.props; - const {buttonText} = this.state; - - return ( - this._button = button} - disabled={disabled} - accessible={accessible} - onPress={this._onButtonPress} - > - { - children || - ( - - - {buttonText} - - - ) - } - - ); - } - - _onButtonPress = () => { - const {onDropdownWillShow} = this.props; - if (!onDropdownWillShow || - onDropdownWillShow() !== false) { - this.show(); - } - }; - - _renderModal() { - const {animated, accessible, dropdownStyle} = this.props; - const {showDropdown, loading} = this.state; - if (showDropdown && this._buttonFrame) { - const frameStyle = this._calcPosition(); - const animationType = animated ? 'fade' : 'none'; - return ( - - - - - {loading ? this._renderLoading() : this._renderDropdown()} - - - - - ); - } - } - - _calcPosition() { - const {dropdownStyle, style, adjustFrame} = this.props; - - const dimensions = Dimensions.get('window'); - const windowWidth = dimensions.width; - const windowHeight = dimensions.height; - - const dropdownHeight = (dropdownStyle && StyleSheet.flatten(dropdownStyle).height) || - StyleSheet.flatten(styles.dropdown).height; - - const bottomSpace = windowHeight - this._buttonFrame.y - this._buttonFrame.h; - const rightSpace = windowWidth - this._buttonFrame.x; - const showInBottom = bottomSpace >= dropdownHeight || bottomSpace >= this._buttonFrame.y; - const showInLeft = rightSpace >= this._buttonFrame.x; - - const positionStyle = { - height: dropdownHeight, - top: showInBottom ? this._buttonFrame.y + this._buttonFrame.h : Math.max(0, this._buttonFrame.y - dropdownHeight), - }; - - if (showInLeft) { - positionStyle.left = this._buttonFrame.x; - } else { - const dropdownWidth = (dropdownStyle && StyleSheet.flatten(dropdownStyle).width) || - (style && StyleSheet.flatten(style).width) || -1; - if (dropdownWidth !== -1) { - positionStyle.width = dropdownWidth; - } - positionStyle.right = rightSpace - this._buttonFrame.w; - } - - return adjustFrame ? adjustFrame(positionStyle) : positionStyle; - } - - _onRequestClose = () => { - const {onDropdownWillHide} = this.props; - if (!onDropdownWillHide || - onDropdownWillHide() !== false) { - this.hide(); - } - }; - - _onModalPress = () => { - const {onDropdownWillHide} = this.props; - if (!onDropdownWillHide || - onDropdownWillHide() !== false) { - this.hide(); - } - }; - - _renderLoading() { - return ( - - ); - } - - _renderDropdown() { - const {scrollEnabled, renderSeparator, showsVerticalScrollIndicator, keyboardShouldPersistTaps} = this.props; - return ( - - ); - } - - get _dataSource() { - const {options} = this.props; - const ds = new ListView.DataSource({ - rowHasChanged: (r1, r2) => r1 !== r2 - }); - return ds.cloneWithRows(options); - } - - _renderRow = (rowData, sectionID, rowID, highlightRow) => { - const {renderRow, dropdownTextStyle, dropdownTextHighlightStyle, accessible} = this.props; - const {selectedIndex} = this.state; - const key = `row_${rowID}`; - const highlighted = rowID == selectedIndex; - const row = !renderRow ? - ( - {rowData} - ) : - renderRow(rowData, rowID, highlighted); - const preservedProps = { - key, - accessible, - onPress: () => this._onRowPress(rowData, sectionID, rowID, highlightRow), - }; - if (TOUCHABLE_ELEMENTS.find(name => name == row.type.displayName)) { - const props = {...row.props}; - props.key = preservedProps.key; - props.onPress = preservedProps.onPress; - const {children} = row.props; - switch (row.type.displayName) { - case 'TouchableHighlight': { - return ( - - {children} - - ); - } - case 'TouchableOpacity': { - return ( - - {children} - - ); - } - case 'TouchableWithoutFeedback': { - return ( - - {children} - - ); - } - case 'TouchableNativeFeedback': { - return ( - - {children} - - ); - } - default: - break; - } - } - return ( - - {row} - - ); - }; - - _onRowPress(rowData, sectionID, rowID, highlightRow) { - const {onSelect, renderButtonText, onDropdownWillHide} = this.props; - if (!onSelect || onSelect(rowID, rowData) !== false) { - highlightRow(sectionID, rowID); - const value = renderButtonText && renderButtonText(rowData) || rowData.toString(); - this._nextValue = value; - this._nextIndex = rowID; - this.setState({ - buttonText: value, - selectedIndex: rowID - }); - } - if (!onDropdownWillHide || onDropdownWillHide() !== false) { - this.setState({ - showDropdown: false - }); - } - } - - _renderSeparator = (sectionID, rowID, adjacentRowHighlighted) => { - const key = `spr_${rowID}`; - return ( - - ); - }; + static propTypes = { + disabled: PropTypes.bool, + scrollEnabled: PropTypes.bool, + defaultIndex: PropTypes.number, + defaultValue: PropTypes.string, + options: PropTypes.array, + + accessible: PropTypes.bool, + animated: PropTypes.bool, + showsVerticalScrollIndicator: PropTypes.bool, + keyboardShouldPersistTaps: PropTypes.string, + + style: PropTypes.oneOfType([PropTypes.number, PropTypes.object, PropTypes.array]), + textStyle: PropTypes.oneOfType([PropTypes.number, PropTypes.object, PropTypes.array]), + dropdownStyle: PropTypes.oneOfType([PropTypes.number, PropTypes.object, PropTypes.array]), + dropdownTextStyle: PropTypes.oneOfType([PropTypes.number, PropTypes.object, PropTypes.array]), + dropdownTextHighlightStyle: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.object, + PropTypes.array, + ]), + + adjustFrame: PropTypes.func, + renderRow: PropTypes.func, + renderSeparator: PropTypes.func, + renderButtonText: PropTypes.func, + + onDropdownWillShow: PropTypes.func, + onDropdownWillHide: PropTypes.func, + onSelect: PropTypes.func, + }; + + static defaultProps = { + disabled: false, + scrollEnabled: true, + defaultIndex: -1, + defaultValue: 'Please select...', + options: null, + animated: true, + showsVerticalScrollIndicator: true, + keyboardShouldPersistTaps: 'never', + }; + + constructor(props) { + super(props); + + this._button = null; + this._buttonFrame = null; + this._nextValue = null; + this._nextIndex = null; + + this.state = { + accessible: !!props.accessible, + loading: !props.options, + showDropdown: false, + buttonText: props.defaultValue, + selectedIndex: props.defaultIndex, + }; + } + + componentWillReceiveProps(nextProps) { + let { buttonText, selectedIndex } = this.state; + const { defaultIndex, defaultValue, options } = nextProps; + buttonText = this._nextValue == null ? buttonText : this._nextValue; + selectedIndex = this._nextIndex == null ? selectedIndex : this._nextIndex; + if (selectedIndex < 0) { + selectedIndex = defaultIndex; + if (selectedIndex < 0) { + buttonText = defaultValue; + } + } + this._nextValue = null; + this._nextIndex = null; + + this.setState({ + loading: !options, + buttonText, + selectedIndex, + }); + } + + render() { + return ( + + {this._renderButton()} + {this._renderModal()} + + ); + } + + _updatePosition(callback) { + if (this._button && this._button.measure) { + this._button.measure((fx, fy, width, height, px, py) => { + this._buttonFrame = { x: px, y: py, w: width, h: height }; + callback && callback(); + }); + } + } + + show() { + this._updatePosition(() => { + this.setState({ + showDropdown: true, + }); + }); + } + + hide() { + this.setState({ + showDropdown: false, + }); + } + + select(idx) { + const { defaultValue, options, defaultIndex, renderButtonText } = this.props; + + let value = defaultValue; + if (idx == null || !options || idx >= options.length) { + idx = defaultIndex; + } + + if (idx >= 0) { + value = renderButtonText ? renderButtonText(options[idx]) : options[idx].toString(); + } + + this._nextValue = value; + this._nextIndex = idx; + + this.setState({ + buttonText: value, + selectedIndex: idx, + }); + } + + _renderButton() { + const { disabled, accessible, children, textStyle } = this.props; + const { buttonText } = this.state; + + return ( + (this._button = button)} + disabled={disabled} + accessible={accessible} + onPress={this._onButtonPress} + > + {children || ( + + + {buttonText} + + + )} + + ); + } + + _onButtonPress = () => { + const { onDropdownWillShow } = this.props; + if (!onDropdownWillShow || onDropdownWillShow() !== false) { + this.show(); + } + }; + + _renderModal() { + const { animated, accessible, dropdownStyle } = this.props; + const { showDropdown, loading } = this.state; + if (showDropdown && this._buttonFrame) { + const frameStyle = this._calcPosition(); + const animationType = animated ? 'fade' : 'none'; + return ( + + + + + {loading ? this._renderLoading() : this._renderDropdown()} + + + + + ); + } + } + + _calcPosition() { + const { dropdownStyle, style, adjustFrame } = this.props; + + const dimensions = Dimensions.get('window'); + const windowWidth = dimensions.width; + const windowHeight = dimensions.height; + + const dropdownHeight = + (dropdownStyle && StyleSheet.flatten(dropdownStyle).height) || + StyleSheet.flatten(styles.dropdown).height; + + const bottomSpace = windowHeight - this._buttonFrame.y - this._buttonFrame.h; + const rightSpace = windowWidth - this._buttonFrame.x; + const showInBottom = bottomSpace >= dropdownHeight || bottomSpace >= this._buttonFrame.y; + const showInLeft = rightSpace >= this._buttonFrame.x; + + const positionStyle = { + height: dropdownHeight, + top: showInBottom + ? this._buttonFrame.y + this._buttonFrame.h + : Math.max(0, this._buttonFrame.y - dropdownHeight), + }; + + if (showInLeft) { + positionStyle.left = this._buttonFrame.x; + } else { + const dropdownWidth = + (dropdownStyle && StyleSheet.flatten(dropdownStyle).width) || + (style && StyleSheet.flatten(style).width) || + -1; + if (dropdownWidth !== -1) { + positionStyle.width = dropdownWidth; + } + positionStyle.right = rightSpace - this._buttonFrame.w; + } + + return adjustFrame ? adjustFrame(positionStyle) : positionStyle; + } + + _onRequestClose = () => { + const { onDropdownWillHide } = this.props; + if (!onDropdownWillHide || onDropdownWillHide() !== false) { + this.hide(); + } + }; + + _onModalPress = () => { + const { onDropdownWillHide } = this.props; + if (!onDropdownWillHide || onDropdownWillHide() !== false) { + this.hide(); + } + }; + + _renderLoading() { + return ; + } + + _renderDropdown() { + const { + scrollEnabled, + renderSeparator, + showsVerticalScrollIndicator, + keyboardShouldPersistTaps, + } = this.props; + return ( + + ); + } + + get _dataSource() { + const { options } = this.props; + const ds = new ListView.DataSource({ + rowHasChanged: (r1, r2) => r1 !== r2, + }); + return ds.cloneWithRows(options); + } + + _renderRow = (rowData, sectionID, rowID, highlightRow) => { + const { renderRow, dropdownTextStyle, dropdownTextHighlightStyle, accessible } = this.props; + const { selectedIndex } = this.state; + const key = `row_${rowID}`; + const highlighted = rowID == selectedIndex; + const row = !renderRow ? ( + + {rowData} + + ) : ( + renderRow(rowData, rowID, highlighted) + ); + const preservedProps = { + key, + accessible, + onPress: () => this._onRowPress(rowData, sectionID, rowID, highlightRow), + }; + if (TOUCHABLE_ELEMENTS.find(name => name == row.type.displayName)) { + const props = { ...row.props }; + props.key = preservedProps.key; + props.onPress = preservedProps.onPress; + const { children } = row.props; + switch (row.type.displayName) { + case 'TouchableHighlight': { + return {children}; + } + case 'TouchableOpacity': { + return {children}; + } + case 'TouchableWithoutFeedback': { + return {children}; + } + case 'TouchableNativeFeedback': { + return {children}; + } + default: + break; + } + } + return {row}; + }; + + _onRowPress(rowData, sectionID, rowID, highlightRow) { + const { onSelect, renderButtonText, onDropdownWillHide } = this.props; + if (!onSelect || onSelect(rowID, rowData) !== false) { + highlightRow(sectionID, rowID); + const value = (renderButtonText && renderButtonText(rowData)) || rowData.toString(); + this._nextValue = value; + this._nextIndex = rowID; + this.setState({ + buttonText: value, + selectedIndex: rowID, + }); + } + if (!onDropdownWillHide || onDropdownWillHide() !== false) { + this.setState({ + showDropdown: false, + }); + } + } + + _renderSeparator = (sectionID, rowID, adjacentRowHighlighted) => { + const key = `spr_${rowID}`; + return ; + }; } const styles = StyleSheet.create({ - button: { - justifyContent: 'center' - }, - buttonText: { - fontSize: 12 - }, - modal: { - flexGrow: 1 - }, - dropdown: { - position: 'absolute', - height: (33 + StyleSheet.hairlineWidth) * 5, - borderWidth: StyleSheet.hairlineWidth, - borderColor: 'lightgray', - borderRadius: 2, - backgroundColor: 'white', - justifyContent: 'center' - }, - loading: { - alignSelf: 'center' - }, - list: { - //flexGrow: 1, - }, - rowText: { - paddingHorizontal: 6, - paddingVertical: 10, - fontSize: 11, - color: 'gray', - backgroundColor: 'white', - textAlignVertical: 'center' - }, - highlightedRowText: { - color: 'black' - }, - separator: { - height: StyleSheet.hairlineWidth, - backgroundColor: 'lightgray' - } + button: { + justifyContent: 'center', + }, + buttonText: { + fontSize: 12, + }, + modal: { + flexGrow: 1, + }, + dropdown: { + position: 'absolute', + borderWidth: StyleSheet.hairlineWidth, + borderColor: 'lightgray', + borderRadius: 2, + backgroundColor: 'white', + justifyContent: 'center', + }, + loading: { + alignSelf: 'center', + }, + list: { + // flexGrow: 1, + }, + rowText: { + paddingHorizontal: 6, + paddingVertical: 10, + fontSize: 11, + color: 'gray', + backgroundColor: 'white', + textAlignVertical: 'center', + }, + highlightedRowText: { + color: 'black', + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: 'lightgray', + }, });