import * as React from 'react'; import { Animated, Component, Styles, Types, UserInterface, View } from 'reactxp'; import { ITransitionGroupProps } from '../transitions'; interface IProps extends ITransitionGroupProps { children: React.ReactNode; } interface IState { previousChildren?: React.ReactNode; childrenAnimation?: Types.AnimatedViewStyleRuleSet; previousChildrenAnimation?: Types.AnimatedViewStyleRuleSet; dimensions: Types.Dimensions; isAnimating: boolean; } const dimensions = UserInterface.measureWindow(); const styles = { animationDefaultStyle: Styles.createViewStyle({ position: 'absolute', width: dimensions.width, height: dimensions.height, }), allowPointerEventsStyle: Styles.createViewStyle({ // @ts-ignore pointerEvents: 'auto', }), transitionContainerStyle: Styles.createViewStyle({ width: dimensions.width, height: dimensions.height, }), }; export default class TransitionContainer extends Component { public state: IState = { dimensions: UserInterface.measureWindow(), isAnimating: false, }; public UNSAFE_componentWillReceiveProps(nextProps: IProps) { switch (nextProps.name) { case 'slide-up': this.slideUpTransition(nextProps); break; case 'slide-down': this.slideDownTransition(nextProps); break; case 'push': this.pushTransition(nextProps); break; case 'pop': this.popTransition(nextProps); break; default: break; } } public onFinishedAnimation = (_result: Types.Animated.EndResult) => { this.setState({ childrenAnimation: styles.allowPointerEventsStyle, previousChildren: null, isAnimating: false, }); }; public slideUpTransition(nextProps: IProps) { const currentTranslationValue = Animated.createValue(this.state.dimensions.height); this.setState( { previousChildren: this.props.children, childrenAnimation: Styles.createAnimatedViewStyle({ // @ts-ignore zIndex: 1, transform: [{ translateY: currentTranslationValue }], }), previousChildrenAnimation: Styles.createAnimatedViewStyle({ // @ts-ignore zIndex: 0, transform: [{ translateY: Animated.createValue(0) }], }), isAnimating: true, }, () => { Animated.timing(currentTranslationValue, { toValue: 0, easing: Animated.Easing.InOut(), duration: nextProps.duration, }).start(this.onFinishedAnimation); }, ); } public slideDownTransition(nextProps: IProps) { const previousTranslationValue = Animated.createValue(0); this.setState( { previousChildren: this.props.children, childrenAnimation: Styles.createAnimatedViewStyle({ // @ts-ignore zIndex: 0, transform: [{ translateY: Animated.createValue(0) }], }), previousChildrenAnimation: Styles.createAnimatedViewStyle({ // @ts-ignore zIndex: 1, transform: [{ translateY: previousTranslationValue }], }), isAnimating: true, }, () => { Animated.timing(previousTranslationValue, { toValue: this.state.dimensions.height, easing: Animated.Easing.InOut(), duration: nextProps.duration, }).start(this.onFinishedAnimation); }, ); } public pushTransition(nextProps: IProps) { const currentTranslationValue = Animated.createValue(this.state.dimensions.width); const previousTranslationValue = Animated.createValue(0); this.setState( { previousChildren: this.props.children, childrenAnimation: Styles.createAnimatedViewStyle({ // @ts-ignore zIndex: 1, transform: [{ translateX: currentTranslationValue }], }), previousChildrenAnimation: Styles.createAnimatedViewStyle({ // @ts-ignore zIndex: 0, transform: [{ translateX: previousTranslationValue }], }), isAnimating: true, }, () => { const compositeAnimation = Animated.parallel([ Animated.timing(currentTranslationValue, { toValue: 0, easing: Animated.Easing.InOut(), duration: nextProps.duration, }), Animated.timing(previousTranslationValue, { toValue: -this.state.dimensions.width / 2, easing: Animated.Easing.InOut(), duration: nextProps.duration, }), ]); compositeAnimation.start(this.onFinishedAnimation); }, ); } public popTransition(nextProps: IProps) { const currentTranslationValue = Animated.createValue(-this.state.dimensions.width / 2); const previousTranslationValue = Animated.createValue(0); this.setState( { previousChildren: this.props.children, childrenAnimation: Styles.createAnimatedViewStyle({ // @ts-ignore zIndex: 0, transform: [{ translateX: currentTranslationValue }], }), previousChildrenAnimation: Styles.createAnimatedViewStyle({ // @ts-ignore zIndex: 1, transform: [{ translateX: previousTranslationValue }], }), isAnimating: true, }, () => { const compositeAnimation = Animated.parallel([ Animated.timing(currentTranslationValue, { toValue: 0, easing: Animated.Easing.InOut(), duration: nextProps.duration, }), Animated.timing(previousTranslationValue, { toValue: this.state.dimensions.width, easing: Animated.Easing.InOut(), duration: nextProps.duration, }), ]); compositeAnimation.start(this.onFinishedAnimation); }, ); } public render() { const { children } = this.props; const { isAnimating, previousChildren, childrenAnimation, previousChildrenAnimation, } = this.state; return ( {previousChildren && ( {previousChildren} )} {children} ); } } function getChildKey(child?: React.ReactNode): string | number | undefined { return child && typeof child === 'object' && 'key' in child && (typeof child.key === 'string' || typeof child.key === 'number') ? child.key : undefined; }