Last active
September 4, 2017 17:36
-
-
Save erictraut/fb83e7f39bea07158731c9b20f9d10cd to your computer and use it in GitHub Desktop.
ReactXP component that implements a toast notification view that slides in from the top of the screen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* ToastView.tsx | |
* | |
* A component that dsplays system toast measages. | |
*/ | |
import RX = require('reactxp'); | |
import ToastService, { ToastMessage, ToastMessageParams } from '../services/ToastService'; | |
interface ToastViewProps extends RX.CommonProps { } | |
interface ToastViewState { | |
currentMessage?: ToastMessage; | |
} | |
const _defaultToastTimeout = 3000; | |
const _toastHeight = 30; | |
const _marginBottom = 10; | |
const _containerHeight = 100 + _toastHeight; | |
const _styles = { | |
containerRounded: RX.Styles.createViewStyle({ | |
position: 'absolute', | |
bottom: 0, | |
left: 0, | |
right: 0, | |
flexDirection: 'row', | |
height: _containerHeight, | |
justifyContent: 'center', | |
alignItems: 'flex-end' | |
}), | |
barButton: RX.Styles.createButtonStyle({ | |
bottom: 0, | |
left: 0, | |
right: 0, | |
height: _toastHeight | |
}), | |
contentContainer: RX.Styles.createViewStyle({ | |
flexDirection: 'row', | |
alignItems: 'center', | |
justifyContent: 'center', | |
paddingHorizontal: 25, | |
height: _toastHeight, | |
backgroundColor: 'blue', | |
borderRadius: _toastHeight / 2 | |
}) | |
}; | |
abstract class ToastView extends RX.Component<ToastViewProps, ToastViewState> { | |
private _hideToastTimerId: number; | |
private _button: RX.Button; | |
private _verticalAnimatedValue = new RX.Animated.Value(_containerHeight); | |
private _verticalAnimatedStyle = RX.Styles.createAnimatedViewStyle({ | |
transform: [{ | |
translateY: this._verticalAnimatedValue | |
}] | |
}); | |
private _recalcDisplay(message: ToastMessage): void { | |
let newState: ToastViewState = { | |
currentMessage: message | |
}; | |
this.setState(newState, () => { | |
this._showToast(0); | |
}); | |
} | |
protected _buildState(props: ToastViewProps, initialBuild: boolean): ToastViewState { | |
let newState: ToastViewState = {}; | |
if (initialBuild) { | |
newState.currentMessage = null; | |
} | |
return newState; | |
} | |
componentWillMount() { | |
super.componentWillMount(); | |
ToastService.showToastEvent.subscribe(this._onShowToast); | |
} | |
componentWillUnmount() { | |
super.componentWillUnmount(); | |
ToastService.showToastEvent.unsubscribe(this._onShowToast); | |
} | |
render(): JSX.Element { | |
// Bail, if there is not a valid current message. | |
if (!this.state.currentMessage) { | |
return null; | |
} | |
const params = this.state.currentMessage.params; | |
return ( | |
<RX.View | |
style={ _styles.containerRounded } | |
ignorePointerEvents={ true } | |
> | |
<RX.Animated.View style={ this._verticalAnimatedStyle }> | |
<RX.Button | |
ref={ this._onButtonRef } | |
style={ _styles.barButton } | |
onPress={ this._onTapDismiss } | |
> | |
<RX.View style={ _styles.contentContainer }> | |
<RX.Text> | |
{ this.state.currentMessage.params.textMessage } | |
</RX.Text> | |
</RX.View> | |
</RX.Button> | |
</RX.Animated.View> | |
</RX.View> | |
); | |
} | |
private _onShowToast = (message: ToastMessage) => { | |
if (this.isComponentMounted()) { | |
this._recalcDisplay(message); | |
} | |
}; | |
private _onTapDismiss = () => { | |
const params: ToastMessageParams = this.state.currentMessage ? this.state.currentMessage.params : null; | |
if (params && params.onPress) { | |
params.onPress(); | |
} | |
clearTimeout(this._hideToastTimerId); | |
this._hideToast(0); | |
}; | |
private _showToast(delay: number) { | |
const message = this.state.currentMessage; | |
if (!message) { | |
return; | |
} | |
const toValue = -1 * (this.state.currentMessage.params.bottomMargin ? | |
this.state.currentMessage.params.bottomMargin : _marginBottom); | |
setTimeout(() => { | |
if (this.isComponentMounted()) { | |
RX.Animated.timing(this._verticalAnimatedValue, { | |
toValue: toValue, | |
easing: RX.Animated.Easing.Out(), | |
duration: 250, | |
isInteraction: false | |
}).start(); | |
} | |
}, delay); | |
} | |
private _hideToast(delay: number) { | |
this._hideToastTimerId = setTimeout(() => { | |
if (this.isComponentMounted()) { | |
RX.Animated.timing(this._verticalAnimatedValue, { | |
toValue: _containerHeight, | |
easing: RX.Animated.Easing.Out(), | |
duration: 200, | |
isInteraction: false | |
}).start(); | |
} | |
}, delay); | |
} | |
private _onButtonRef = (button: RX.Button) => { | |
this._button = button; | |
} | |
} | |
export = ToastView; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment