UNPKG

hcmobile-sdk

Version:

mobile-sdk

220 lines (195 loc) 6.71 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Text, View, StyleSheet, Dimensions, Modal, TouchableHighlight, Animated, ScrollView } from 'react-native'; import styles, { btnStyle, sheetStyle, hairlineWidth } from './styles'; const TITLE_H = 40; const MESSAGE_H = 40; const CANCEL_MARGIN = 6; const BUTTON_H = 50 + hairlineWidth; const WARN_COLOR = '#ff3b30'; const MAX_HEIGHT = Dimensions.get('window').height * 0.7; class ActionSheet extends Component { constructor (props) { super(props); this.scrollEnabled = false; this.translateY = this._calculateHeight(props); this.state = { visible: false, sheetAnim: new Animated.Value(this.translateY) }; this._cancel = this._cancel.bind(this); } componentWillReceiveProps (nextProps) { this.translateY = this._calculateHeight(nextProps); } show () { this.setState({visible: true}); this._showSheet(); } hide (index) { this._hideSheet(() => { this.setState({visible: false}); this.props.onPress(index); }); } _cancel () { const { cancelButtonIndex } = this.props; // 保持和 ActionSheetIOS 一致, // 未设置 cancelButtonIndex 时,点击背景不隐藏 ActionSheet if (cancelButtonIndex > -1) { this.hide(cancelButtonIndex); } } _showSheet () { Animated.timing(this.state.sheetAnim, { toValue: 0, duration: 250 }).start(); } _hideSheet (callback) { Animated.timing(this.state.sheetAnim, { toValue: this.translateY, duration: 150 }).start(callback || function () {}); } _calculateHeight (props) { let count = props.options.length; let height = BUTTON_H * count + CANCEL_MARGIN; if (props.title) height += TITLE_H; if (props.message) height += MESSAGE_H; if (height > MAX_HEIGHT) { this.scrollEnabled = true; return MAX_HEIGHT; } else { this.scrollEnabled = false; return height; } } _renderTitle () { const title = this.props.title; if (!title) { return null; } if (React.isValidElement(title)) { return ( <View style={sheetStyle.title}>{title}</View> ); } return ( <View style={sheetStyle.title}> <Text style={sheetStyle.titleText}>{title}</Text> </View> ); } _renderMessage () { const message = this.props.message; if (!message) { return null; } if (React.isValidElement(message)) { return ( <View style={sheetStyle.message}>{message}</View> ); } return ( <View style={sheetStyle.message}> <Text style={sheetStyle.titleText}>{message}</Text> </View> ); } _renderCancelButton () { let {options, cancelButtonIndex, tintColor} = this.props; if (cancelButtonIndex > -1 && options[cancelButtonIndex]) { return ( <TouchableHighlight activeOpacity={1} underlayColor="#f4f4f4" style={[btnStyle.wrapper, {marginTop: 6}]} onPress={this._cancel} > <Text style={[btnStyle.title, {fontWeight: '700', color: tintColor}]}>{options[cancelButtonIndex]}</Text> </TouchableHighlight> ); } else { return null; } } _createButton (title, fontColor, index, style) { let titleNode = null; if (React.isValidElement(title)) { titleNode = title; } else { titleNode = <Text style={[btnStyle.title, {color: fontColor}]}>{title}</Text>; } return ( <TouchableHighlight key={index} activeOpacity={1} underlayColor="#f4f4f4" style={[btnStyle.wrapper, style || {}]} onPress={this.hide.bind(this, index)} > {titleNode} </TouchableHighlight> ); } _renderOptions () { let {options, tintColor, cancelButtonIndex, destructiveButtonIndex} = this.props; return options.map((title, index) => { let fontColor = destructiveButtonIndex === index ? WARN_COLOR : tintColor; return index === cancelButtonIndex ? null : this._createButton(title, fontColor, index); }); } render () { const { cancelButtonIndex } = this.props; const { visible, sheetAnim } = this.state; return ( <Modal visible={visible} transparent={true} animationType="none" onRequestClose={this._cancel} > <View style={sheetStyle.wrapper}> <Text style={styles.overlay} onPress={this._cancel}></Text> <Animated.View style={[sheetStyle.bd, {height: this.translateY, transform: [{translateY: sheetAnim}]}]} > {this._renderTitle()} {this._renderMessage()} <ScrollView scrollEnabled={this.scrollEnabled} contentContainerStyle={sheetStyle.options}> {this._renderOptions()} </ScrollView> {this._renderCancelButton()} </Animated.View> </View> </Modal> ); } } ActionSheet.propTypes = { title: PropTypes.oneOfType([ PropTypes.string, PropTypes.element]), message: PropTypes.oneOfType([ PropTypes.string, PropTypes.element]), options: PropTypes.arrayOf((propVal, key, componentName, location, propFullName) => { if (typeof propVal[key] !== 'string' && !React.isValidElement(propVal[key])) { return new Error( 'Invalid prop `' + propFullName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }), tintColor: PropTypes.string, cancelButtonIndex: PropTypes.number, destructiveButtonIndex: PropTypes.number, onPress: PropTypes.func }; ActionSheet.defaultProps = { tintColor: '#007aff', onPress: () => {} }; export default ActionSheet;