import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { TransitionMotion, spring } from 'react-motion';
import { Panel, PanelBody, PanelFooter, PanelHeader } from 'components/ui/Panel';
import { Form } from 'components/ui/form';
import MeasureHeight from 'components/MeasureHeight';
import { helpLinks as helpLinksStyles } from 'components/auth/helpLinks.scss';
import panelStyles from 'components/ui/panel.scss';
import icons from 'components/ui/icons.scss';
import authFlow from 'services/authFlow';
import { userShape } from 'components/user/User';
import * as actions from './actions';
const opacitySpringConfig = {stiffness: 300, damping: 20};
const transformSpringConfig = {stiffness: 500, damping: 50, precision: 0.5};
const changeContextSpringConfig = {stiffness: 500, damping: 20, precision: 0.5};
/**
* Definition of relation between contexts and panels
*
* Each sub-array is context. Each sub-array item is panel
*
* This definition declares animations between panels:
* - The animation between panels from different contexts will be along Y axe (height toggling)
* - The animation between panels from the same context will be along X axe (sliding)
* - Panel index defines the direction of X transition of both panels
* (e.g. the panel with lower index will slide from left side, and with greater from right side)
*/
const contexts = [
['login', 'password', 'forgotPassword', 'recoverPassword'],
['register', 'activation', 'resendActivation'],
['changePassword'],
['permissions']
];
if (process.env.NODE_ENV !== 'production') {
// test panel uniquenes between contexts
// TODO: it may be moved to tests in future
contexts.reduce((acc, context) => {
context.forEach((panel) => {
if (acc[panel]) {
throw new Error(`Panel ${panel} is already exists in context ${JSON.stringify(acc[panel])}`);
}
acc[panel] = context;
});
return acc;
}, {});
}
class PanelTransition extends Component {
static displayName = 'PanelTransition';
static propTypes = {
// context props
auth: PropTypes.shape({
error: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({
type: PropTypes.string,
payload: PropTypes.object
})]),
isLoading: PropTypes.bool,
login: PropTypes.shape({
login: PropTypes.string,
password: PropTypes.string
})
}).isRequired,
user: userShape.isRequired,
setError: PropTypes.func.isRequired,
clearErrors: PropTypes.func.isRequired,
resolve: PropTypes.func.isRequired,
reject: PropTypes.func.isRequired,
// local props
Title: PropTypes.element,
Body: PropTypes.element,
Footer: PropTypes.element,
Links: PropTypes.element,
children: PropTypes.element
};
static childContextTypes = {
auth: PropTypes.shape({
error: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({
type: PropTypes.string,
payload: PropTypes.object
})]),
login: PropTypes.shape({
login: PropTypes.string,
password: PropTypes.string
})
}),
user: userShape,
requestRedraw: PropTypes.func,
clearErrors: PropTypes.func,
resolve: PropTypes.func,
reject: PropTypes.func
};
state = {
contextHeight: 0,
panelId: this.props.Body && this.props.Body.type.panelId
};
getChildContext() {
return {
auth: this.props.auth,
user: this.props.user,
requestRedraw: () => this.setState({isHeightDirty: true}, () => this.setState({isHeightDirty: false})),
clearErrors: this.props.clearErrors,
resolve: this.props.resolve,
reject: this.props.reject
};
}
componentWillReceiveProps(nextProps) {
const nextPanel = nextProps.Body && nextProps.Body.type.panelId;
const prevPanel = this.props.Body && this.props.Body.type.panelId;
if (nextPanel !== prevPanel) {
const direction = this.getDirection(nextPanel, prevPanel);
const forceHeight = direction === 'Y' && nextPanel !== prevPanel ? 1 : 0;
this.props.clearErrors();
this.setState({
direction,
panelId: nextPanel,
prevPanelId: prevPanel,
forceHeight
});
if (forceHeight) {
setTimeout(() => {
this.setState({forceHeight: 0});
}, 100);
}
}
}
render() {
const {contextHeight, forceHeight} = this.state;
const {Title, Body, Footer, Links} = this.props;
if (this.props.children) {
return this.props.children;
} else if (!Title || !Body || !Footer || !Links) {
throw new Error('Title, Body, Footer and Links are required');
}
const {panelId, hasGoBack} = Body.type;
const formHeight = this.state[`formHeight${panelId}`] || 0;
// a hack to disable height animation on first render
const isHeightMeasured = this.isHeightMeasured;
this.isHeightMeasured = isHeightMeasured || formHeight > 0;
return (