Skip to content

Instantly share code, notes, and snippets.

@ryanflorence
Created December 6, 2016 19:20
Show Gist options
  • Save ryanflorence/80979a51aef8e356b4d79733bd780a61 to your computer and use it in GitHub Desktop.
Save ryanflorence/80979a51aef8e356b4d79733bd780a61 to your computer and use it in GitHub Desktop.
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>&nbsp;</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 }}>&lt;</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