-
-
Save littlepsylo/2b04e4fc554d015327ab66cbf5e5b1b6 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
import React, { Component, PropTypes } from 'react' | |
import { | |
Text, | |
View, | |
TouchableHighlight, | |
ScrollView, | |
StatusBar, | |
Dimensions, | |
Animated | |
} from 'react-native' | |
import Match from 'react-router/Match' | |
import Redirect from 'react-router/Redirect' | |
import Nav from './Nav' | |
const { any, node } = PropTypes | |
class StackContainer extends Component { | |
static contextTypes = { | |
stack: any | |
} | |
static childContextTypes = { | |
stack: any | |
} | |
initialLocation = { | |
...this.props.location, | |
pathname: this.props.pathname | |
} | |
getChildContext() { | |
return { | |
stack: { | |
...this.context.stack, | |
parentLocation: this.initialLocation | |
} | |
} | |
} | |
componentWillMount() { | |
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 | |
} | |
} | |
class AnimatedStack extends React.Component { | |
static propTypes = { | |
title: any, | |
content: any, | |
backButton: any, | |
parentLocation: any, | |
location: any | |
} | |
state = { previousProps: null } | |
animation = new Animated.Value(0) | |
componentWillReceiveProps(nextProps) { | |
if (nextProps.location !== this.props.location) { | |
this.setState({ | |
previousProps: this.props | |
}, () => { | |
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 ( | |
<View pointerEvents={animating ? 'none' : 'auto'} style={{ flex: 1 }}> | |
<View style={{ zIndex: 1, backgroundColor: '#f0f0f0', borderBottomColor: '#ccc', borderBottomWidth: 1, height: 40, alignItems: 'center' }}> | |
{bothProps.map((props, index, arr) => ( | |
<Animated.View | |
key={props.location.pathname} | |
style={{ | |
opacity: this.animation.interpolate({ | |
inputRange: [ 0, 1 ], | |
outputRange: arr.length > 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 | |
}} | |
> | |
<View style={{ width: 30 }}> | |
{props.parentLocation ? props.backButton : <Text> </Text>} | |
</View> | |
<View style={{ flex: 1 }}> | |
{props.title} | |
</View> | |
<View style={{ width: 30 }}/> | |
</Animated.View> | |
))} | |
</View> | |
<View style={{ flex: 1, backgroundColor: '#ccc' }}> | |
{bothProps.map((props, index, arr) => ( | |
<Animated.View key={props.location.pathname} style={{ | |
left: this.animation.interpolate({ | |
inputRange: [ 0, 1 ], | |
outputRange: arr.length > 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} | |
</Animated.View> | |
))} | |
</View> | |
</View> | |
) | |
} | |
} | |
const rootStoredLocations = {} | |
class StackRootContainer extends Component { | |
static childContextTypes = { | |
stack: any, | |
} | |
static propTypes = { | |
children: PropTypes.node, | |
location: PropTypes.object | |
} | |
state = { | |
title: null, | |
content: null, | |
backLocation: null, | |
backButton: null, | |
direction: null | |
} | |
getChildContext() { | |
return { | |
stack: { | |
push: ({ direction, title, content, parentLocation }) => { | |
this.setState({ | |
direction, | |
title, | |
content, | |
parentLocation, | |
backButton: ( | |
<Nav replace={true} to={parentLocation}> | |
<Text style={{ padding: 10 }}><</Text> | |
</Nav> | |
) | |
}) | |
} | |
} | |
} | |
} | |
componentWillUnmount() { | |
rootStoredLocations[this.props.pattern] = this.props.location | |
} | |
render() { | |
const { title, content, backButton, parentLocation, direction } = this.state | |
const { children, location } = this.props | |
const { width, height } = Dimensions.get('window') | |
return ( | |
<View style={{ flex: 1 }}> | |
<AnimatedStack | |
title={title} | |
content={content} | |
backButton={backButton} | |
parentLocation={parentLocation} | |
direction={direction} | |
location={location} | |
/> | |
{children} | |
</View> | |
) | |
} | |
} | |
class RedirectStack extends Component { | |
componentWillMount() { | |
delete rootStoredLocations[this.props.pattern] | |
} | |
render() { | |
return <Redirect to={this.props.to}/> | |
} | |
} | |
class StackMatch extends Component { | |
static propTypes = { | |
pattern: any, | |
isRoot: any, | |
renderTitle: any, | |
renderContent: any, | |
renderChild: any | |
} | |
render() { | |
const { isRoot, pattern, ...rest } = this.props | |
return ( | |
<Match pattern={pattern} render={(props) => ( | |
isRoot ? ( | |
rootStoredLocations[pattern] ? ( | |
<RedirectStack pattern={pattern} to={rootStoredLocations[pattern]}/> | |
) : ( | |
<StackRootContainer pattern={pattern} location={props.location}> | |
<StackContainer {...rest} {...props}/> | |
</StackRootContainer> | |
) | |
) : ( | |
<StackContainer {...rest} {...props}/> | |
) | |
)}/> | |
) | |
} | |
} | |
export default StackMatch |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment