UNPKG

rn_supermap

Version:

rn_supermap 一款基于React-Native框架的移动应用开发工具。基于该开发工具,用户可以使用JavaScript开发语言,开发出在Android和IOS操作系统下运行的原生移动GIS应用,入门门槛低,一次开发,处处运行。

426 lines (383 loc) 11.9 kB
/********************************************************************************* Copyright © SuperMap. All rights reserved. Author: Zihao Wang E-mail: pridehao@gmail.com Description:下拉选框第三方组件 react-native-modal-dropdown(MIT协议)。 react-native-modal-dropdown: Author:sohobloo HomePage:https://github.com/sohobloo/react-native-modal-dropdown **********************************************************************************/ 'use strict'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { StyleSheet, Dimensions, View, Text, ListView, TouchableWithoutFeedback, TouchableNativeFeedback, TouchableOpacity, TouchableHighlight, Modal, ActivityIndicator, } from 'react-native'; const TOUCHABLE_ELEMENTS = ['TouchableHighlight', 'TouchableOpacity', 'TouchableWithoutFeedback', 'TouchableNativeFeedback']; export default class ModalDropdown extends Component { static propTypes = { disabled: PropTypes.bool, defaultIndex: PropTypes.number, defaultValue: PropTypes.string, options: PropTypes.array, accessible: PropTypes.bool, animated: PropTypes.bool, showsVerticalScrollIndicator: PropTypes.bool, keyboardShouldPersistTaps: PropTypes.oneOf(['always', 'never', 'handled', false, true]), 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, onDropdownWillShow: PropTypes.func, onDropdownWillHide: PropTypes.func, onSelect: PropTypes.func }; static defaultProps = { disabled: false, 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 = { disabled: props.disabled, accessible: !!props.accessible, loading: props.options === null || props.options === undefined, showDropdown: false, buttonText: props.defaultValue, selectedIndex: props.defaultIndex }; } componentWillReceiveProps(nextProps) { var buttonText = this._nextValue == null ? this.state.buttonText : this._nextValue.toString(); var selectedIndex = this._nextIndex == null ? this.state.selectedIndex : this._nextIndex; if (selectedIndex < 0) { selectedIndex = nextProps.defaultIndex; if (selectedIndex < 0) { buttonText = nextProps.defaultValue; } } this._nextValue = null; this._nextIndex = null; this.setState({ disabled: nextProps.disabled, loading: nextProps.options == null, buttonText: buttonText, selectedIndex: selectedIndex }); } render() { return ( <View {...this.props}> {this._renderButton()} {this._renderModal()} </View> ); } _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) { var value = this.props.defaultValue; if (idx == null || this.props.options == null || idx >= this.props.options.length) { idx = this.props.defaultIndex; } if (idx >= 0) { value = this.props.options[idx].toString(); } this._nextValue = value; this._nextIndex = idx; this.setState({ buttonText: value, selectedIndex: idx }); } _renderButton() { return ( <TouchableOpacity ref={button => this._button = button} disabled={this.props.disabled} accessible={this.props.accessible} onPress={this._onButtonPress.bind(this)}> { this.props.children || ( <View style={styles.button}> <Text style={[styles.buttonText, this.props.textStyle]} numberOfLines={1}> {this.state.buttonText} </Text> </View> ) } </TouchableOpacity> ); } _onButtonPress() { if (!this.props.onDropdownWillShow || this.props.onDropdownWillShow() !== false) { this.show(); } } _renderModal() { if (this.state.showDropdown && this._buttonFrame) { let frameStyle = this._calcPosition(); let animationType = this.props.animated ? 'fade' : 'none'; return ( <Modal animationType={animationType} transparent={true} onRequestClose={this._onRequestClose.bind(this)} supportedOrientations={['portrait', 'portrait-upside-down', 'landscape', 'landscape-left', 'landscape-right']}> <TouchableWithoutFeedback accessible={this.props.accessible} disabled={!this.state.showDropdown} onPress={this._onModalPress.bind(this)}> <View style={styles.modal}> <View style={[styles.dropdown, this.props.dropdownStyle, frameStyle]}> {this.state.loading ? this._renderLoading() : this._renderDropdown()} </View> </View> </TouchableWithoutFeedback> </Modal> ); } } _calcPosition() { let dimensions = Dimensions.get('window'); let windowWidth = dimensions.width; let windowHeight = dimensions.height; let dropdownHeight = (this.props.dropdownStyle && StyleSheet.flatten(this.props.dropdownStyle).height) || StyleSheet.flatten(styles.dropdown).height; let bottomSpace = windowHeight - this._buttonFrame.y - this._buttonFrame.h; let rightSpace = windowWidth - this._buttonFrame.x; let showInBottom = bottomSpace >= dropdownHeight || bottomSpace >= this._buttonFrame.y; let showInLeft = rightSpace >= this._buttonFrame.x; var style = { height: dropdownHeight, top: showInBottom ? this._buttonFrame.y + this._buttonFrame.h : Math.max(0, this._buttonFrame.y - dropdownHeight), }; if (showInLeft) { style.left = this._buttonFrame.x; } else { let dropdownWidth = (this.props.dropdownStyle && StyleSheet.flatten(this.props.dropdownStyle).width) || (this.props.style && StyleSheet.flatten(this.props.style).width) || -1; if (dropdownWidth !== -1) { style.width = dropdownWidth; } style.right = rightSpace - this._buttonFrame.w; } if (this.props.adjustFrame) { style = this.props.adjustFrame(style) || style; } return style; } _onRequestClose() { if (!this.props.onDropdownWillHide || this.props.onDropdownWillHide() !== false) { this.hide(); } } _onModalPress() { if (!this.props.onDropdownWillHide || this.props.onDropdownWillHide() !== false) { this.hide(); } } _renderLoading() { return ( <ActivityIndicator size='small'/> ); } _renderDropdown() { return ( <ListView style={styles.list} dataSource={this._dataSource} renderRow={this._renderRow.bind(this)} renderSeparator={this.props.renderSeparator || this._renderSeparator.bind(this)} automaticallyAdjustContentInsets={false} showsVerticalScrollIndicator={this.props.showsVerticalScrollIndicator} keyboardShouldPersistTaps={this.props.keyboardShouldPersistTaps} /> ); } get _dataSource() { let ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }); return ds.cloneWithRows(this.props.options); } _renderRow(rowData, sectionID, rowID, highlightRow) { let key = `row_${rowID}`; let highlighted = rowID == this.state.selectedIndex; let row = !this.props.renderRow ? (<Text style={[ styles.rowText, this.props.dropdownTextStyle, highlighted && styles.highlightedRowText, highlighted && this.props.dropdownTextHighlightStyle ]} > {rowData} </Text>) : this.props.renderRow(rowData, rowID, highlighted); let preservedProps = { key: key, accessible: this.props.accessible, onPress: () => this._onRowPress(rowData, sectionID, rowID, highlightRow), }; if (TOUCHABLE_ELEMENTS.find(name => name == row.type.displayName)) { var props = {...row.props}; props.key = preservedProps.key; props.onPress = preservedProps.onPress; switch (row.type.displayName) { case 'TouchableHighlight': { return ( <TouchableHighlight {...props}> {row.props.children} </TouchableHighlight> ); } break; case 'TouchableOpacity': { return ( <TouchableOpacity {...props}> {row.props.children} </TouchableOpacity> ); } break; case 'TouchableWithoutFeedback': { return ( <TouchableWithoutFeedback {...props}> {row.props.children} </TouchableWithoutFeedback> ); } break; case 'TouchableNativeFeedback': { return ( <TouchableNativeFeedback {...props}> {row.props.children} </TouchableNativeFeedback> ); } break; default: break; } } return ( <TouchableHighlight {...preservedProps}> {row} </TouchableHighlight> ); } _onRowPress(rowData, sectionID, rowID, highlightRow) { if (!this.props.onSelect || this.props.onSelect(rowID, rowData) !== false) { highlightRow(sectionID, rowID); this._nextValue = rowData; this._nextIndex = rowID; this.setState({ buttonText: rowData.toString(), selectedIndex: rowID }); } if (!this.props.onDropdownWillHide || this.props.onDropdownWillHide() !== false) { this.setState({ showDropdown: false }); } } _renderSeparator(sectionID, rowID, adjacentRowHighlighted) { let key = `spr_${rowID}`; return (<View style={styles.separator} key={key} />); } } 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' } });