UNPKG

react-native-actionsheet

Version:

Cross platform ActionSheet. This component implements a custom ActionSheet and provides the same way to drawing it on the defferent platforms(iOS and Android). Actually, In order to keep the best effect, it still uses the ActionSheetIOS on iOS.

211 lines (188 loc) 5.55 kB
import React from 'react' import { Text, View, Dimensions, Modal, TouchableHighlight, Animated, ScrollView, Easing } from 'react-native' import * as utils from './utils' import styles2 from './styles' const WARN_COLOR = '#FF3B30' const MAX_HEIGHT = Dimensions.get('window').height * 0.7 class ActionSheet extends React.Component { static defaultProps = { tintColor: '#007AFF', buttonUnderlayColor: '#F4F4F4', onPress: () => {}, styles: {} } constructor (props) { super(props) this.scrollEnabled = false this.translateY = this._calculateHeight(props) this.state = { visible: false, sheetAnim: new Animated.Value(this.translateY) } } componentWillReceiveProps (nextProps) { this.translateY = this._calculateHeight(nextProps) } get styles () { const { styles } = this.props const obj = {} Object.keys(styles2).forEach((key) => { const arr = [styles2[key]] if (styles[key]) { arr.push(styles[key]) } obj[key] = arr }) return obj } 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 (utils.isset(cancelButtonIndex)) { this.hide(cancelButtonIndex) } } _showSheet = () => { Animated.timing(this.state.sheetAnim, { toValue: 0, duration: 250, easing: Easing.out(Easing.ease) }).start() } _hideSheet (callback) { Animated.timing(this.state.sheetAnim, { toValue: this.translateY, duration: 200 }).start(callback) } /** * elements: titleBox, messageBox, buttonBox, cancelButtonBox * box size: height, marginTop, marginBottom */ _calculateHeight (props) { const styles = this.styles const getHeight = (name) => { const style = styles[name][styles[name].length - 1] let h = 0 ;['height', 'marginTop', 'marginBottom'].forEach((attrName) => { if (typeof style[attrName] !== 'undefined') { h += style[attrName] } }) return h } let height = 0 if (props.title) height += getHeight('titleBox') if (props.message) height += getHeight('messageBox') if (utils.isset(props.cancelButtonIndex)) { height += getHeight('cancelButtonBox') height += (props.options.length - 1) * getHeight('buttonBox') } else { height += props.options.length * getHeight('buttonBox') } if (height > MAX_HEIGHT) { this.scrollEnabled = true height = MAX_HEIGHT } else { this.scrollEnabled = false } return height } _renderTitle () { const { title } = this.props const styles = this.styles if (!title) return null return ( <View style={styles.titleBox}> {React.isValidElement(title) ? title : ( <Text style={styles.titleText}>{title}</Text> )} </View> ) } _renderMessage () { const { message } = this.props const styles = this.styles if (!message) return null return ( <View style={styles.messageBox}> {React.isValidElement(message) ? message : ( <Text style={styles.messageText}>{message}</Text> )} </View> ) } _renderCancelButton () { const { options, cancelButtonIndex } = this.props if (!utils.isset(cancelButtonIndex)) return null return this._createButton(options[cancelButtonIndex], cancelButtonIndex) } _createButton (title, index) { const styles = this.styles const { buttonUnderlayColor, cancelButtonIndex, destructiveButtonIndex, tintColor } = this.props const fontColor = destructiveButtonIndex === index ? WARN_COLOR : tintColor const buttonBoxStyle = cancelButtonIndex === index ? styles.cancelButtonBox : styles.buttonBox return ( <TouchableHighlight key={index} activeOpacity={1} underlayColor={buttonUnderlayColor} style={buttonBoxStyle} onPress={() => this.hide(index)} > {React.isValidElement(title) ? title : ( <Text style={[styles.buttonText, {color: fontColor}]}>{title}</Text> )} </TouchableHighlight> ) } _renderOptions () { const { cancelButtonIndex } = this.props return this.props.options.map((title, index) => { return cancelButtonIndex === index ? null : this._createButton(title, index) }) } render () { const styles = this.styles const { visible, sheetAnim } = this.state return ( <Modal visible={visible} animationType='none' transparent onRequestClose={this._cancel} > <View style={[styles.wrapper]}> <Text style={[styles.overlay]} onPress={this._cancel} /> <Animated.View style={[ styles.body, { height: this.translateY, transform: [{ translateY: sheetAnim }] } ]} > {this._renderTitle()} {this._renderMessage()} <ScrollView scrollEnabled={this.scrollEnabled}>{this._renderOptions()}</ScrollView> {this._renderCancelButton()} </Animated.View> </View> </Modal> ) } } export default ActionSheet