Skip to content

Instantly share code, notes, and snippets.

@iremlopsum
Created April 25, 2019 15:02
Show Gist options
  • Save iremlopsum/0f3e977da7d1f8aa65fa2104e8d4923f to your computer and use it in GitHub Desktop.
Save iremlopsum/0f3e977da7d1f8aa65fa2104e8d4923f to your computer and use it in GitHub Desktop.
import React, { PureComponent } from 'react'
import { View, StyleSheet, TouchableOpacity, Animated, Dimensions, Easing, ActivityIndicator } from 'react-native'
import { Text, Emoji } from '@components'
const { width: ww, height: wh } = Dimensions.get('screen')
class ConfirmationActionSheet extends PureComponent {
state = {
active: false
}
backdropAnimatedValue = new Animated.Value(0)
contentAnimatedValue = new Animated.Value(0)
componentWillReceiveProps = nextProps => {
const shouldShowModal = !this.props.data && nextProps.data
const shouldHideModal = this.props.data && !nextProps.data
if (shouldShowModal) {
this.setState({ active: true })
}
if (shouldHideModal) {
this.animateModalClosed()
}
}
componentDidUpdate = (prevProps, prevState) => {
const shouldShowModal = !prevState.active && this.state.active
const shouldShowLoader = !prevProps.loading && this.props.loading
const shouldShowSuccess = !prevProps.success && this.props.success
if (shouldShowModal) {
this.animateModalOpen()
}
if (shouldShowLoader) {
this.animateToShowLoader()
}
if (shouldShowSuccess) {
this.animateToSuccess()
}
}
animateModalOpen = () => {
Animated.parallel([
Animated.timing(this.backdropAnimatedValue, {
toValue: 1,
duration: 300,
useNativeDriver: true,
easing: Easing.bezier(0.54, 0.02, 0.25, 0.85)
}),
Animated.timing(this.contentAnimatedValue, {
toValue: 1,
duration: 400,
useNativeDriver: true,
easing: Easing.bezier(0.54, 0.02, 0.25, 0.85)
})
]).start()
}
animateModalClosed = () => {
Animated.parallel([
Animated.timing(this.backdropAnimatedValue, {
toValue: 0,
duration: 300,
useNativeDriver: true,
easing: Easing.bezier(0.54, 0.02, 0.25, 0.85)
}),
Animated.timing(this.contentAnimatedValue, {
toValue: 0,
duration: 400,
useNativeDriver: true,
easing: Easing.bezier(0.54, 0.02, 0.25, 0.85)
})
]).start(() => this.setState({ active: false }))
}
animateToShowLoader = () => {
Animated.timing(this.contentAnimatedValue, {
toValue: 2,
duration: 250,
useNativeDriver: true,
easing: Easing.bezier(0.54, 0.02, 0.25, 0.85)
}).start()
}
animateToSuccess = () => {
Animated.timing(this.contentAnimatedValue, {
toValue: 3,
duration: 250,
useNativeDriver: true,
easing: Easing.bezier(0.54, 0.02, 0.25, 0.85)
}).start(() => setTimeout(this.props.closeConfirmationSheet, 2000))
}
renderBackdrop() {
const { closeConfirmationSheet } = this.props
const opacity = this.backdropAnimatedValue.interpolate({
inputRange: [ 0, 1 ],
outputRange: [ 0, 0.7 ]
})
return (
<Animated.View style={styles.backdropContainer(opacity)}>
<TouchableOpacity onPress={closeConfirmationSheet} style={styles.touchableBackdrop} />
</Animated.View>
)
}
renderLoader() {
const { loading } = this.props
const pointerEvents = loading ? 'auto' : 'none'
const opacity = this.contentAnimatedValue.interpolate({
inputRange: [ 0, 1, 2, 3 ],
outputRange: [ 0, 0, 1, 1 ]
})
return (
<Animated.View pointerEvents={pointerEvents} style={styles.loadingContainer(opacity)}>
<ActivityIndicator size="large" color="#333ddd" />
</Animated.View>
)
}
renderSuccess() {
const { success } = this.props
const pointerEvents = success ? 'auto' : 'none'
const opacity = this.contentAnimatedValue.interpolate({
inputRange: [ 0, 1, 2, 3 ],
outputRange: [ 0, 0, 0, 1 ]
})
return (
<Animated.View pointerEvents={pointerEvents} style={styles.successContainer(opacity)}>
<Text type="title">Success</Text>
</Animated.View>
)
}
renderContent() {
const { closeConfirmationSheet, primaryAction } = this.props
const opacity = this.contentAnimatedValue.interpolate({
inputRange: [ 0, 1, 2, 3 ],
outputRange: [ 0, 1, 1, 1 ]
})
const translateY = this.contentAnimatedValue.interpolate({
inputRange: [ 0, 1, 2, 3 ],
outputRange: [ wh / 2, 0, 0, 0 ]
})
const scale = this.contentAnimatedValue.interpolate({
inputRange: [ 0, 1, 2, 3 ],
outputRange: [ 1.2, 1, 1, 1 ]
})
return (
<Animated.View style={styles.contentContainer(opacity, translateY, scale)}>
<Emoji name="wondering" size={64} />
<Text type="description" color="#595858" style={styles.questionLabel}>
Do you really want to cancel Amazon Dash Replenishment?
</Text>
<TouchableOpacity onPress={primaryAction} style={styles.primaryButton}>
<Text type="secondaryTitle" color="white">
Yes
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={closeConfirmationSheet} style={styles.secondaryButton}>
<Text type="secondaryTitle" color="#004494">
no
</Text>
</TouchableOpacity>
{this.renderLoader()}
{this.renderSuccess()}
</Animated.View>
)
}
render() {
const { active } = this.state
if (!active) return false
return (
<View style={styles.container}>
{this.renderBackdrop()}
{this.renderContent()}
</View>
)
}
}
export default ConfirmationActionSheet
const styles = StyleSheet.create({
touchableBackdrop: { ...StyleSheet.absoluteFill },
backdropContainer: opacity => ({
...StyleSheet.absoluteFill,
backgroundColor: 'black',
opacity: opacity
}),
loadingContainer: opacity => ({
...StyleSheet.absoluteFill,
opacity: opacity,
borderTopRightRadius: 16,
borderTopLeftRadius: 16,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center'
}),
successContainer: opacity => ({
...StyleSheet.absoluteFill,
opacity: opacity,
borderTopRightRadius: 16,
borderTopLeftRadius: 16,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center'
}),
secondaryButton: {
width: 135,
height: 40,
marginTop: 15,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'transparent'
},
primaryButton: {
borderRadius: 8,
width: 135,
height: 40,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#004494'
},
questionLabel: {
marginTop: 42,
marginBottom: 90,
textAlign: 'center'
},
contentContainer: (opacity, translateY, scale) => ({
opacity: opacity,
transform: [ { translateY }, { scale } ],
elevation: 5,
position: 'absolute',
padding: 42,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'white',
borderTopRightRadius: 16,
borderTopLeftRadius: 16,
alignItems: 'center'
}),
container: { ...StyleSheet.absoluteFill }
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment