react-native-smart-button
Version:
A smart button for React Native apps
314 lines (289 loc) • 9.37 kB
JavaScript
/*
* A smart button for react-native apps
* https://github.com/react-native-component/react-native-smart-button/
* Released under the MIT license
* Copyright (c) 2016 react-native-component <moonsunfall@aliyun.com>
*/
import React, {
PureComponent,
} from 'react'
import PropTypes from 'prop-types'
import {
View,
ViewPropTypes,
Text,
TouchableHighlight,
TouchableOpacity,
StyleSheet,
} from 'react-native'
import constants, {touchableTypes, SYSTEM_OPACITY, NOOP, EMPTY_OBJECT,} from './constants'
import Blur from './Blur'
const styles = StyleSheet.create({
text: {
color: '#007aff',
fontFamily: '.HelveticaNeueInterface-MediumP4',
fontSize: 17,
fontWeight: 'bold',
textAlign: 'center',
},
disabledText: {
color: '#dcdcdc',
},
touchContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
contentContainer: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
})
export default class Button extends PureComponent {
static constants = constants
static defaultProps = {
touchableType: touchableTypes.fade,
activeOpacity: SYSTEM_OPACITY,
loading: false,
disabled: false,
onPress: NOOP,
onPressIn: NOOP,
onPressOut: NOOP,
onLayout: NOOP,
renderLoadingComponent: NOOP,
}
static propTypes = {
testID: PropTypes.string,
touchableType: PropTypes.oneOf([
touchableTypes.highlight,
touchableTypes.fadeContent,
touchableTypes.blur,
touchableTypes.fade,
]),
activeOpacity: PropTypes.number,
underlayColor: PropTypes.string,
style: ViewPropTypes.style,
disabledStyle: ViewPropTypes.style,
textStyle: Text.propTypes.style,
disabledTextStyle: Text.propTypes.style,
onPressIn: PropTypes.func,
onPressOut: PropTypes.func,
onPress: PropTypes.func,
onLayout: PropTypes.func,
disabled: PropTypes.bool,
loading: PropTypes.bool,
renderLoadingComponent: PropTypes.func,
shadowOpacity: PropTypes.number,
shadowColor: PropTypes.string,
}
constructor(props) {
super(props)
this.state = {
pressIn: false,
disabled: props.disabled,
loading: props.loading,
}
this._boxDimension = EMPTY_OBJECT
}
render() {
const touchableProps = EMPTY_OBJECT
const {touchableType,} = this.props
touchableProps.onPress = this.props.onPress
touchableProps.onPressIn = this.props.onPressIn
touchableProps.onPressOut = this.props.onPressOut
let renderView = null
switch (touchableType) {
case touchableTypes.highlight:
renderView = this._renderHighlightView(touchableProps)
break
case touchableTypes.fadeContent:
renderView = this._renderFadeContentView(touchableProps)
break
case touchableTypes.blur: // experimental feature
renderView = this._renderBlurView(touchableProps)
break
case touchableTypes.fade:
default:
renderView = this._renderFadeView(touchableProps)
break
}
return renderView
}
componentWillReceiveProps(nextProps) {
const { disabled: nextDisabled, loading: nextLoading, } = nextProps
const { disabled, loading, } = this.props
const newState = {}
if (nextDisabled !== disabled) {
newState.disabled = nextDisabled
} else if (nextLoading !== loading) {
newState.loading = nextLoading
}
if (Object.keys(newState).length > 0) {
this.setState(newState)
}
}
_renderHighlightView(touchableProps) {
const reassignedProps = {
...touchableProps,
activeOpacity: 1,
underlayColor: this.props.underlayColor,
}
return (
<TouchableHighlight
onLayout={this.props.onLayout}
disabled={this.state.disabled || this.state.loading}
style={[
this.props.style,
this.state.disabled ? this.props.disabledStyle : null,
]}
{...reassignedProps}
testID={this.props.testID}
>
{this._renderChildren()}
</TouchableHighlight>
)
}
_renderFadeContentView(touchableProps) {
const reassignedProps = {
...touchableProps,
activeOpacity: this.props.activeOpacity,
}
return (
<View
onLayout={this.props.onLayout}
style={[
this.props.style,
this.state.disabled ? this.props.disabledStyle : null,
]}>
<TouchableOpacity
disabled={this.state.disabled || this.state.loading}
style={[styles.touchContainer,]}
{...reassignedProps}
testID={this.props.testID}
>
{this._renderChildren()}
</TouchableOpacity>
</View>
)
}
_renderBlurView(touchableProps) {
const reassignedProps = {
...touchableProps,
activeOpacity: this.props.activeOpacity,
onPressIn: this._onBlurPressIn,
onPressOut: this._onBlurPressOut,
}
return (
<View
onLayout={this._handleButtonLayout}
style={[
{
position: 'relative',
overflow: 'hidden',
},
this.props.style,
this.state.disabled ? this.props.disabledStyle : null,
]}>
<TouchableOpacity
disabled={this.state.disabled || this.state.loading}
style={styles.touchContainer}
{...reassignedProps}
testID={this.props.testID}
>
{this._renderChildren()}
</TouchableOpacity>
{this._renderBlur()}
</View>
)
}
_renderFadeView(touchableProps) {
const reassignedProps = {
...touchableProps,
activeOpacity: this.props.activeOpacity,
}
return (
<TouchableOpacity
onLayout={this.props.onLayout}
disabled={this.state.disabled || this.state.loading}
style={[
this.props.style,
this.state.disabled ? this.props.disabledStyle : null,
]}
{...reassignedProps}
testID={this.props.testID}
>
{this._renderChildren()}
</TouchableOpacity>
)
}
_renderBlur() {
return (
<Blur
show={this.state.pressIn}
// textDimension={this.textDimension}
containerDimension={this._boxDimension}
shadowOpacity={this.props.shadowOpacity}
shadowColor={this.props.shadowColor}
/>
)
}
_renderChildren() {
let childView = null
if (this.state.loading) {
childView = (
<View style={styles.contentContainer}>
{this.props.renderLoadingComponent()}
</View>
)
} else {
const children = React.Children.map(this.props.children, (child) => {
if (!React.isValidElement(child)) {
return (
<Text
// onLayout={this._onTextLayout}
style={[
styles.text,
this.props.textStyle,
this.state.disabled ? (this.props.disabledTextStyle || styles.disabledText) : null,
]}>
{child}
</Text>
)
}
return child
})
childView = (
<View style={styles.contentContainer}>
{children}
</View>
)
}
return childView
}
_onBlurPressIn = (e) => {
this.setState({
pressIn: true,
})
this.props.onPressIn(e)
}
_onBlurPressOut = (e) => {
this.setState({
pressIn: false,
})
this.props.onPressOut(e)
}
_handleButtonLayout = (e) => {
this._boxDimension = {
width: e.nativeEvent.layout.width,
height: e.nativeEvent.layout.height,
}
this.props.onLayout(e)
}
// _onTextLayout = (e) => {
// this.textDimension = {
// width: e.nativeEvent.layout.width,
// height: e.nativeEvent.layout.height,
// }
// }
}