import React, { Component } from 'react' import PropTypes from 'prop-types' import { View, Text, Animated, Dimensions, TouchableOpacity } from 'react-native' import { Route, Redirect } from 'react-router' import { Link } from 'react-router-native' const rootStoredLocations = {} class Stack extends Component { state = { previousProps: null, currentProps: null } animation = new Animated.Value(0) static getDerivedStateFromProps = (props, state) => { if (!state.currentProps) { return { currentProps: props } } const isLocationChanged = props.location !== state.currentProps.location if (isLocationChanged) { return { previousProps: state.currentProps, currentProps: props } } return null } componentDidUpdate(prevProps, prevState) { const previousProps = prevState.previousProps if (previousProps) { const { animation } = this animation.setValue(0) Animated.timing(animation, { toValue: 1, duration: 300 }).start(({ finished }) => { this.setState({ previousProps: null }) }) } } render() { const { width, height } = Dimensions.get('window') const { direction } = this.props const animating = this.state.previousProps const bothProps = [this.props] if (animating) { bothProps.push(this.state.previousProps) } return ( {bothProps.map((props, index, arr) => ( 1 && index === 0 ? [0, 1] : index === 1 ? [1, 0] : [1, 1] }), flexDirection: 'row', alignItems: 'center', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} > {props.parentLocation ? props.backButton :  } {props.title} ))} {bothProps.map((props, index, arr) => ( 1 ? index === 0 && direction === 'down' ? [width + 10, 0] : index === 1 && direction === 'down' ? [0, -100] : index === 0 && direction === 'up' ? [-100, 0] : index === 1 && direction === 'up' ? [0, width + 10] : [0, 0] : [0, 0] }), zIndex: arr.length > 1 ? index === 0 && direction === 'down' ? 1 : index === 1 && direction === 'down' ? 0 : index === 0 && direction === 'up' ? 0 : index === 1 && direction === 'up' ? 1 : 1 : 1, position: 'absolute', width, height, top: 0, shadowColor: '#000000', shadowOpacity: 0.25, shadowRadius: 10, opacity: this.animation.interpolate({ inputRange: [0, 1], outputRange: arr.length > 1 ? index === 0 && direction === 'down' ? [1, 1] : index === 1 && direction === 'down' ? [1, 0.5] : index === 0 && direction === 'up' ? [0.5, 1] : index === 1 && direction === 'up' ? [1, 1] : [1, 1] : [1, 1] }) }} > {props.content} ))} ) } } Stack.propTypes = { title: PropTypes.any, content: PropTypes.any, backButton: PropTypes.any, parentLocation: PropTypes.any, location: PropTypes.any } const StackContext = React.createContext('stackContext') class StackRootContainer extends Component { state = { title: null, content: null, parentLocation: null, backButton: null, direction: null } getChildContext() { return { stack: { push: ({ direction, title, content, parentLocation }) => { this.setState({ direction, title, content, parentLocation, backButton: ( < ) }) } } } } componentWillUnmount() { rootStoredLocations[this.props.pattern] = this.props.location } render() { const { title, content, backButton, parentLocation, direction } = this.state const { children, location } = this.props return ( {children} ) } } StackRootContainer.childContextTypes = { stack: PropTypes.any } StackRootContainer.propTypes = { children: PropTypes.node, location: PropTypes.object } class StackContainer extends Component { getChildContext() { return { stack: { ...this.context.stack, parentLocation: this.initialLocation } } } componentDidMount() { this.initialLocation = { ...this.props.location, pathname: this.props.location.pathname } this.pushToStack('down') } componentDidUpdate(prevProps) { const becameActive = this.props.isExact === true && prevProps.isExact === false if (becameActive) { this.pushToStack('up') } } pushToStack(direction) { const { isExact, renderTitle, renderContent, renderChild, ...rest } = this.props if (isExact) { this.context.stack.push({ title: renderTitle(rest), content: renderContent(rest), parentLocation: this.context.stack.parentLocation, direction }) } } render() { const { isExact, renderTitle, renderContent, renderChild, ...rest } = this.props return isExact ? null : renderChild ? renderChild(rest) : null } } StackContainer.contextTypes = { stack: PropTypes.any } StackContainer.childContextTypes = { stack: PropTypes.any } class RedirectStack extends Component { componentDidMount() { delete rootStoredLocations[this.props.pattern] } render() { return } } class StackMatch extends Component { render() { const { isRoot, pattern, ...rest } = this.props return ( isRoot ? ( rootStoredLocations[pattern] ? ( ) : ( ) ) : ( ) } /> ) } } StackMatch.propTypes = { pattern: PropTypes.string.isRequired, renderTitle: PropTypes.any, renderContent: PropTypes.any, renderChild: PropTypes.any } export class StackScene extends Component { render() { return this.props.children } } export default StackMatch