2016-01-31 18:29:38 +05:30
|
|
|
import React, { Component } from 'react';
|
|
|
|
|
|
|
|
import { TransitionMotion, spring } from 'react-motion';
|
|
|
|
import ReactHeight from 'react-height';
|
|
|
|
|
|
|
|
import { Panel, PanelBody, PanelFooter, PanelHeader } from 'components/ui/Panel';
|
|
|
|
import {helpLinks as helpLinksStyles} from 'components/auth/helpLinks.scss';
|
|
|
|
import panelStyles from 'components/ui/panel.scss';
|
|
|
|
import icons from 'components/ui/icons.scss';
|
|
|
|
|
|
|
|
|
|
|
|
const opacitySpringConfig = [200, 20];
|
2016-02-06 14:33:51 +05:30
|
|
|
const transformSpringConfig = [500, 50];
|
2016-01-31 18:29:38 +05:30
|
|
|
|
|
|
|
// TODO: сделать более быстрый фейд на горизонтальном скролле
|
|
|
|
|
|
|
|
export default class PanelTransition extends Component {
|
|
|
|
state = {
|
2016-02-06 14:33:51 +05:30
|
|
|
height: {},
|
|
|
|
contextHeight: 0
|
2016-01-31 18:29:38 +05:30
|
|
|
};
|
|
|
|
|
2016-02-03 11:06:00 +05:30
|
|
|
componentWillReceiveProps(nextProps) {
|
|
|
|
var previousRoute = this.props.location;
|
|
|
|
|
|
|
|
var next = nextProps.path;
|
|
|
|
var prev = previousRoute && previousRoute.pathname;
|
|
|
|
|
|
|
|
var direction = this.getDirection(next, next, prev);
|
2016-02-06 14:33:51 +05:30
|
|
|
var forceHeight = direction === 'Y' && next !== prev ? 1 : 0;
|
2016-02-03 11:06:00 +05:30
|
|
|
|
2016-01-31 18:29:38 +05:30
|
|
|
this.setState({
|
2016-02-06 14:33:51 +05:30
|
|
|
direction,
|
2016-02-03 11:06:00 +05:30
|
|
|
forceHeight: forceHeight,
|
2016-01-31 18:29:38 +05:30
|
|
|
previousRoute: this.props.location
|
|
|
|
});
|
2016-02-02 23:32:00 +05:30
|
|
|
|
2016-02-03 11:06:00 +05:30
|
|
|
if (forceHeight) {
|
|
|
|
setTimeout(() => {
|
|
|
|
this.setState({forceHeight: 0});
|
|
|
|
}, 100);
|
|
|
|
}
|
2016-01-31 18:29:38 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2016-02-06 14:33:51 +05:30
|
|
|
var {previousRoute, height, contextHeight, forceHeight} = this.state;
|
2016-01-31 18:29:38 +05:30
|
|
|
|
2016-02-06 14:33:51 +05:30
|
|
|
const {path, Title, Body, Footer, Links} = this.props;
|
2016-01-31 18:29:38 +05:30
|
|
|
|
|
|
|
return (
|
|
|
|
<TransitionMotion
|
|
|
|
styles={{
|
|
|
|
[path]: {
|
|
|
|
Title,
|
|
|
|
Body,
|
|
|
|
Footer,
|
|
|
|
Links,
|
|
|
|
hasBackButton: previousRoute && previousRoute.pathname === Title.type.goBack,
|
2016-02-03 11:06:00 +05:30
|
|
|
transformSpring: spring(0, transformSpringConfig),
|
|
|
|
opacitySpring: spring(1, opacitySpringConfig)
|
2016-01-31 18:29:38 +05:30
|
|
|
},
|
|
|
|
common: {
|
2016-02-06 14:33:51 +05:30
|
|
|
heightSpring: spring(forceHeight || height[path] || 0, transformSpringConfig),
|
|
|
|
switchContextHeightSpring: spring(forceHeight || contextHeight, [500, 20])
|
2016-01-31 18:29:38 +05:30
|
|
|
}
|
|
|
|
}}
|
|
|
|
willEnter={this.willEnter}
|
|
|
|
willLeave={this.willLeave}
|
|
|
|
>
|
|
|
|
{(items) => {
|
|
|
|
var keys = Object.keys(items).filter((key) => key !== 'common');
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<Panel>
|
|
|
|
<PanelHeader>
|
|
|
|
<div style={{
|
|
|
|
position: 'relative',
|
|
|
|
height: '59px',
|
|
|
|
overflow: 'hidden'
|
|
|
|
}}>
|
|
|
|
{keys.map((key) => this.getHeader(key, items[key]))}
|
|
|
|
</div>
|
|
|
|
</PanelHeader>
|
2016-02-06 14:33:51 +05:30
|
|
|
<div style={{
|
|
|
|
overflow: 'hidden',
|
|
|
|
height: forceHeight ? items.common.switchContextHeightSpring : 'auto'
|
2016-01-31 18:29:38 +05:30
|
|
|
}}>
|
2016-02-06 14:33:51 +05:30
|
|
|
<ReactHeight onHeightReady={this.updateContextHeight}>
|
|
|
|
<PanelBody style={{
|
|
|
|
overflow: 'hidden'
|
|
|
|
}}>
|
|
|
|
<div style={{
|
|
|
|
position: 'relative',
|
|
|
|
height: previousRoute ? `${items.common.heightSpring}px` : `${height[path]}px`
|
|
|
|
}}>
|
|
|
|
{keys.map((key) => this.getBody(key, items[key]))}
|
|
|
|
</div>
|
|
|
|
</PanelBody>
|
|
|
|
<PanelFooter>
|
|
|
|
<div style={{
|
|
|
|
position: 'relative',
|
|
|
|
height: '50px',
|
|
|
|
overflow: 'hidden'
|
|
|
|
}}>
|
|
|
|
{keys.map((key) => this.getFooter(key, items[key]))}
|
|
|
|
</div>
|
|
|
|
</PanelFooter>
|
|
|
|
</ReactHeight>
|
|
|
|
</div>
|
2016-01-31 18:29:38 +05:30
|
|
|
</Panel>
|
|
|
|
<div className={helpLinksStyles} style={{position: 'relative', height: '20px'}}>
|
|
|
|
{keys.map((key) => this.getLinks(key, items[key]))}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}}
|
|
|
|
</TransitionMotion>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
willEnter = (key, styles) => {
|
|
|
|
var map = {
|
|
|
|
'/login': -1,
|
|
|
|
'/register': -1,
|
|
|
|
'/password': 1,
|
|
|
|
'/activation': 1,
|
2016-02-02 23:32:00 +05:30
|
|
|
'/oauth/permissions': -1
|
2016-01-31 18:29:38 +05:30
|
|
|
};
|
|
|
|
var sign = map[key];
|
|
|
|
|
|
|
|
return {
|
|
|
|
...styles,
|
|
|
|
transformSpring: spring(sign * 100, transformSpringConfig),
|
|
|
|
opacitySpring: spring(1, opacitySpringConfig)
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
willLeave = (key, styles) => {
|
|
|
|
var map = {
|
|
|
|
'/login': -1,
|
|
|
|
'/register': -1,
|
|
|
|
'/password': 1,
|
|
|
|
'/activation': 1,
|
2016-02-02 23:32:00 +05:30
|
|
|
'/oauth/permissions': -1
|
2016-01-31 18:29:38 +05:30
|
|
|
};
|
|
|
|
var sign = map[key];
|
|
|
|
|
|
|
|
return {
|
|
|
|
...styles,
|
|
|
|
transformSpring: spring(sign * 100, transformSpringConfig),
|
|
|
|
opacitySpring: spring(0, opacitySpringConfig)
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
updateHeight = (height) => {
|
|
|
|
this.setState({
|
|
|
|
height: {
|
|
|
|
...this.state.height,
|
|
|
|
[this.props.path]: height
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2016-02-06 14:33:51 +05:30
|
|
|
updateContextHeight = (height) => {
|
|
|
|
this.setState({
|
|
|
|
contextHeight: height
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2016-01-31 18:29:38 +05:30
|
|
|
onGoBack = (event) => {
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
|
|
this.props.history.goBack();
|
|
|
|
};
|
|
|
|
|
2016-02-06 14:33:51 +05:30
|
|
|
getDirection(key, next, prev) {
|
|
|
|
var not = (path) => prev !== path && next !== path;
|
|
|
|
|
|
|
|
var map = {
|
|
|
|
'/login': not('/password') ? 'Y' : 'X',
|
|
|
|
'/password': not('/login') ? 'Y' : 'X',
|
|
|
|
'/register': not('/activation') ? 'Y' : 'X',
|
|
|
|
'/activation': not('/register') ? 'Y' : 'X',
|
|
|
|
'/oauth/permissions': 'Y'
|
|
|
|
};
|
|
|
|
|
|
|
|
return map[key];
|
|
|
|
}
|
|
|
|
|
2016-01-31 18:29:38 +05:30
|
|
|
getHeader(key, props) {
|
|
|
|
var {hasBackButton, transformSpring, Title} = props;
|
|
|
|
|
|
|
|
var style = {
|
|
|
|
position: 'absolute',
|
|
|
|
top: 0,
|
|
|
|
left: 0,
|
|
|
|
width: '100%'
|
|
|
|
};
|
|
|
|
|
|
|
|
var scrollStyle = {
|
|
|
|
WebkitTransform: `translateY(${transformSpring}%)`,
|
|
|
|
transform: `translateY(${transformSpring}%)`
|
|
|
|
};
|
|
|
|
|
|
|
|
var sideScrollStyle = {
|
|
|
|
position: 'relative',
|
|
|
|
zIndex: 2,
|
|
|
|
WebkitTransform: `translateX(${-Math.abs(transformSpring)}%)`,
|
|
|
|
transform: `translateX(${-Math.abs(transformSpring)}%)`
|
|
|
|
};
|
|
|
|
|
|
|
|
var backButton = (
|
|
|
|
<button style={sideScrollStyle} onClick={this.onGoBack} className={panelStyles.headerControl}>
|
|
|
|
<span className={icons.arrowLeft} />
|
|
|
|
</button>
|
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div key={`header${key}`} style={style}>
|
|
|
|
{hasBackButton ? backButton : null}
|
|
|
|
<div style={scrollStyle}>
|
|
|
|
{Title}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
getBody(key, props) {
|
|
|
|
var {transformSpring, opacitySpring, Body} = props;
|
2016-02-06 14:33:51 +05:30
|
|
|
var {direction} = this.state;
|
2016-01-31 18:29:38 +05:30
|
|
|
|
2016-02-06 14:33:51 +05:30
|
|
|
var transform = {
|
|
|
|
WebkitTransform: `translate${direction}(${transformSpring}%)`,
|
|
|
|
transform: `translate${direction}(${transformSpring}%)`
|
|
|
|
};
|
2016-01-31 18:29:38 +05:30
|
|
|
|
|
|
|
|
2016-02-02 23:32:00 +05:30
|
|
|
var verticalOrigin = 'top';
|
2016-02-06 14:33:51 +05:30
|
|
|
if (direction === 'Y') {
|
2016-02-02 23:32:00 +05:30
|
|
|
verticalOrigin = 'bottom';
|
2016-02-06 14:33:51 +05:30
|
|
|
transform = {};
|
2016-01-31 18:29:38 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
var style = {
|
|
|
|
position: 'absolute',
|
2016-02-02 23:32:00 +05:30
|
|
|
[verticalOrigin]: 0,
|
2016-01-31 18:29:38 +05:30
|
|
|
left: 0,
|
|
|
|
width: '100%',
|
2016-02-06 14:33:51 +05:30
|
|
|
opacity: opacitySpring,
|
|
|
|
...transform
|
2016-01-31 18:29:38 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<ReactHeight key={`body${key}`} style={style} onHeightReady={this.updateHeight}>
|
|
|
|
{Body}
|
|
|
|
</ReactHeight>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
getFooter(key, props) {
|
|
|
|
var {opacitySpring, Footer} = props;
|
|
|
|
|
|
|
|
var style = {
|
|
|
|
position: 'absolute',
|
|
|
|
top: 0,
|
|
|
|
left: 0,
|
|
|
|
width: '100%',
|
|
|
|
opacity: opacitySpring
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div key={`footer${key}`} style={style}>
|
|
|
|
{Footer}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
getLinks(key, props) {
|
|
|
|
var {opacitySpring, Links} = props;
|
|
|
|
|
|
|
|
var style = {
|
|
|
|
position: 'absolute',
|
|
|
|
top: 0,
|
|
|
|
left: 0,
|
|
|
|
width: '100%',
|
|
|
|
opacity: opacitySpring
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div key={`links${key}`} style={style}>
|
|
|
|
{Links}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|