Очень грубо перенес текущие вьюхи на анимацию согласно прототипу

This commit is contained in:
SleepWalker 2016-01-31 14:59:38 +02:00
parent bea442c04b
commit 934afafce4
12 changed files with 510 additions and 326 deletions

View File

@ -20,6 +20,7 @@
"intl-messageformat": "^1.1.0",
"react": "^0.14.0",
"react-dom": "^0.14.3",
"react-height": "^2.0.3",
"react-helmet": "^2.3.1",
"react-intl": "^2.0.0-beta-2",
"react-motion": "^0.3.1",

View File

@ -11,7 +11,51 @@ import styles from './activation.scss';
import {helpLinks as helpLinksStyles} from './helpLinks.scss';
import messages from './Activation.messages';
export default class Activation extends Component {
export default function Activation() {
var Title = () => ( // TODO: separate component for PageTitle
<Message {...messages.accountActivationTitle}>
{(msg) => <span>{msg}<Helmet title={msg} /></span>}
</Message>
);
Title.goBack = '/register';
return {
Title,
Body: () => (
<div>
<div className={styles.description}>
<div className={styles.descriptionImage} />
<div className={styles.descriptionText}>
<Message {...messages.activationMailWasSent} values={{
email: (<b>erickskrauch@yandex.ru</b>)
}} />
</div>
</div>
<div className={styles.formRow}>
<Input color="blue" className={styles.activationCodeInput} placeholder={messages.enterTheCode} />
</div>
</div>
),
Footer: (props) => (
<button className={buttons.blue} onClick={(event) => {
event.preventDefault();
props.history.push('/oauth/permissions');
}}>
<Message {...messages.confirmEmail} />
</button>
),
Links: () => (
<a href="#">
<Message {...messages.didNotReceivedEmail} />
</a>
)
};
}
export class _Activation extends Component {
displayName = 'Activation';
render() {

View File

@ -24,10 +24,5 @@ export default defineMessages({
enterTheCode: {
id: 'enterTheCode',
defaultMessage: 'Enter the code from E-mail here'
},
didNotReceivedEmail: {
id: 'didNotReceivedEmail',
defaultMessage: 'Did not received E-mail?'
}
});

View File

@ -13,7 +13,40 @@ import messages from './Login.messages';
import {helpLinks as helpLinksStyles} from './helpLinks.scss';
import passwordMessages from './Password.messages';
class Login extends Component {
export default function Login() {
var context = {
onSubmit(event) {
event.preventDefault();
this.props.push('/password');
}
};
return {
Title: () => ( // TODO: separate component for PageTitle
<Message {...messages.loginTitle}>
{(msg) => <span>{msg}<Helmet title={msg} /></span>}
</Message>
),
Body: () => <Input icon="envelope" type="email" placeholder={messages.emailOrUsername} />,
Footer: (props) => (
<button className={buttons.green} onClick={(event) => {
event.preventDefault();
props.history.push('/password');
}}>
<Message {...messages.next} />
</button>
),
Links: () => (
<a href="#">
<Message {...passwordMessages.forgotPassword} />
</a>
)
};
}
class _Login extends Component {
displayName = 'Login';
render() {
@ -50,6 +83,6 @@ class Login extends Component {
}
export default connect(null, {
push: routeActions.push
})(Login);
// export connect(null, {
// push: routeActions.push
// })(Login);

View File

@ -0,0 +1,264 @@
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];
const heightSpringConfig = [200, 18];
const transformSpringConfig = [500, 20];
// TODO: сделать более быстрый фейд на горизонтальном скролле
export default class PanelTransition extends Component {
state = {
height: {}
};
componentWillReceiveProps() {
this.setState({
previousRoute: this.props.location
});
}
render() {
var {previousRoute, height} = this.state;
var {path, Title, Body, Footer, Links} = this.props;
return (
<TransitionMotion
styles={{
[path]: {
Title,
Body,
Footer,
Links,
hasBackButton: previousRoute && previousRoute.pathname === Title.type.goBack,
transformSpring: spring(0),
opacitySpring: spring(1)
},
common: {
heightSpring: spring(height[path] || 0, heightSpringConfig)
}
}}
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>
<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>
</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,
'/oauth/permissions': 1
};
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,
'/oauth/permissions': 1
};
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
}
});
};
onGoBack = (event) => {
event.preventDefault();
this.props.history.goBack();
};
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;
var {previousRoute} = this.state;
var next = this.props.path;
var prev = previousRoute && previousRoute.pathname;
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'
};
var direction = map[key];
if (direction === 'Y') {
transformSpring = Math.abs(transformSpring);
if (prev === key) {
transformSpring *= -1;
}
}
var style = {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
WebkitTransform: `translate${direction}(${transformSpring}%)`,
transform: `translate${direction}(${transformSpring}%)`,
opacity: opacitySpring
};
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>
);
}
}

View File

@ -12,7 +12,62 @@ import styles from './password.scss';
import {helpLinks as helpLinksStyles} from './helpLinks.scss';
import messages from './Password.messages';
export default class Password extends Component {
export default function Password() {
var Title = () => ( // TODO: separate component for PageTitle
<Message {...messages.passwordTitle}>
{(msg) => <span>{msg}<Helmet title={msg} /></span>}
</Message>
);
Title.goBack = '/login';
return {
Title,
Body: () => (
<div>
<PanelBodyHeader type="error">
<Message {...messages.invalidPassword} />
<br/>
<Message {...messages.suggestResetPassword} values={{
link: (
<a href="#">
<Message {...messages.forgotYourPassword} />
</a>
)
}} />
</PanelBodyHeader>
<div className={styles.miniProfile}>
<div className={styles.avatar}>
{/*<img src="//lorempixel.com/g/90/90" />*/}
<span className={icons.user} />
</div>
<div className={styles.email}>
{/* На деле тут может быть и ник, в зависимости от того, что введут в 1 вьюху */}
erickskrauch@yandex.ru
</div>
</div>
<Input icon="key" type="password" placeholder={messages.accountPassword} />
<Checkbox label={<Message {...messages.rememberMe} />} />
</div>
),
Footer: (props) => (
<button className={buttons.green} onClick={(event) => {
event.preventDefault();
props.history.push('/oauth/permissions');
}}>
<Message {...messages.signInButton} />
</button>
),
Links: () => (
<a href="#">
<Message {...messages.forgotPassword} />
</a>
)
};
}
export class _Password extends Component {
displayName = 'Password';
render() {

View File

@ -11,7 +11,56 @@ import styles from './permissions.scss';
import {helpLinks as helpLinksStyles} from './helpLinks.scss';
import messages from './Permissions.messages';
export default class Permissions extends Component {
export default function Permissions() {
return {
Title: () => ( // TODO: separate component for PageTitle
<Message {...messages.permissionsTitle}>
{(msg) => <span>{msg}<Helmet title={msg} /></span>}
</Message>
),
Body: () => (
<div>
<PanelBodyHeader>
<div className={styles.authInfo}>
<div className={styles.authInfoAvatar}>
{/*<img src="//lorempixel.com/g/90/90" />*/}
<span className={icons.user} />
</div>
<div className={styles.authInfoTitle}>
<Message {...messages.youAuthorizedAs} />
</div>
<div className={styles.authInfoEmail}>
erickskrauch@yandex.ru
</div>
</div>
</PanelBodyHeader>
<div className={styles.permissionsContainer}>
<div className={styles.permissionsTitle}>
<Message {...messages.theAppNeedsAccess} />
</div>
<ul className={styles.permissionsList}>
<li>Authorization for Minecraft servers</li>
<li>Manage your skins directory and additional rows for multiline</li>
<li>Change the active skin</li>
<li>View your E-mail address</li>
</ul>
</div>
</div>
),
Footer: () => (
<button className={buttons.green}>
<Message {...messages.approve} />
</button>
),
Links: () => (
<a href="#">
<Message {...messages.decline} />
</a>
)
};
}
export class _Permissions extends Component {
displayName = 'Permissions';
render() {

View File

@ -11,7 +11,49 @@ import {helpLinks as helpLinksStyles} from './helpLinks.scss';
import messages from './Register.messages';
import activationMessages from './Activation.messages';
export default class Register extends Component {
export default function Register() {
return {
Title: () => ( // TODO: separate component for PageTitle
<Message {...messages.registerTitle}>
{(msg) => <span>{msg}<Helmet title={msg} /></span>}
</Message>
),
Body: () => (
<div>
<Input icon="user" color="blue" type="text" placeholder={messages.yourNickname} />
<Input icon="envelope" color="blue" type="email" placeholder={messages.yourEmail} />
<Input icon="key" color="blue" type="password" placeholder={messages.accountPassword} />
<Input icon="key" color="blue" type="password" placeholder={messages.repeatPassword} />
<Checkbox color="blue" label={
<Message {...messages.acceptRules} values={{
link: (
<a href="#">
<Message {...messages.termsOfService} />
</a>
)
}} />
} />
</div>
),
Footer: (props) => (
<button className={buttons.blue} onClick={(event) => {
event.preventDefault();
props.history.push('/activation');
}}>
<Message {...messages.signUpButton} />
</button>
),
Links: () => (
<a href="#">
<Message {...activationMessages.didNotReceivedEmail} />
</a>
)
};
}
export class _Register extends Component {
displayName = 'Register';
render() {

View File

@ -65,6 +65,10 @@ $bodyTopBottomPadding: 15px;
> * {
flex-grow: 1;
}
button { // TODO: добавленно временно, пока не устаканится лейаут панелек
width: 100%;
}
}
.bodyHeader {

View File

@ -15,6 +15,9 @@ export default class Userbar extends Component {
<Link to="/register" className={buttons.blue}>
<Message {...messages.register} />
</Link>
<Link to="/oauth/permissions" className={buttons.blue}>
Test oAuth
</Link>
</div>
);
}

View File

@ -1,14 +1,11 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { TransitionMotion, spring } from 'react-motion';
import AppInfo from 'components/auth/AppInfo';
import PanelTransition from 'components/auth/PanelTransition';
import styles from './auth.scss';
const springConfig = [200, 20];
class AuthPage extends Component {
displayName = 'AuthPage';
@ -18,320 +15,17 @@ class AuthPage extends Component {
description: `Лучший альтернативный лаунчер для Minecraft с большим количеством версий и их модификаций, а также возмоностью входа как с лицензионным аккаунтом, так и без него.`
};
var { path, children } = this.props;
return (
<div>
<div className={styles.sidebar}>
<AppInfo {...appInfo} />
</div>
<div className={styles.content}>
<TransitionMotion
willEnter={this.willEnter}
willLeave={this.willLeave}
styles={{
[path]: {
children,
x: spring(0, springConfig)
}
}}
>
{(items) => (
<div style={{position: 'relative', overflow: 'hidden', width: '100%', height: '500px'}}>
{Object.keys(items).map((path) => {
const {children, x} = items[path];
const style = {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
transform: `translateX(${x}%)`
};
return (
<div key={path} style={style}>
{children}
</div>
);
})}
</div>
)}
</TransitionMotion>
<TheDemo />
<PanelTransition {...this.props} />
</div>
</div>
);
}
willEnter(key, styles) {
return {
...styles,
x: spring(100, springConfig)
};
}
willLeave(key, styles) {
return {
...styles,
x: spring(-100, springConfig)
};
}
}
import { FormattedMessage as Message } from 'react-intl';
import Helmet from 'react-helmet';
import ReactHeight from 'react-height';
import panelStyles from 'components/ui/panel.scss';
import buttons from 'components/ui/buttons.scss';
import icons from 'components/ui/icons.scss';
import { Panel, PanelBody, PanelFooter, PanelHeader } from 'components/ui/Panel';
import { Input, Checkbox } from 'components/ui/Form';
import messages from 'components/auth/Login.messages';
import regMessages from 'components/auth/Register.messages';
import {helpLinks as helpLinksStyles} from 'components/auth/helpLinks.scss';
import passwordMessages from 'components/auth/Password.messages';
const opacitySpringConfig = [200, 20];
const heightSpringConfig = [200, 18];
const transformSpringConfig = [500, 20];
// TODO: сделать более быстрый фейд на горизонтальном скролле
class TheDemo extends Component {
state = {
isFirstPage: true,
height: {}
};
render() {
var {isFirstPage} = this.state;
var path = `page${isFirstPage ? '1' : '2'}`;
return (
<TransitionMotion
styles={{
[path]: {
isFirstPage,
transformSpring: spring(0),
opacitySpring: spring(1)
},
common: {
heightSpring: spring(this.state.height[path] || 0, heightSpringConfig)
}
}}
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>
<PanelBody style={{
overflow: 'hidden'
}}>
<div style={{
position: 'relative',
height: `${items.common.heightSpring}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>
</Panel>
<div className={helpLinksStyles} style={{position: 'relative', height: '20px'}}>
{keys.map((key) => this.getLinks(key, items[key]))}
</div>
</div>
);
}}
</TransitionMotion>
);
}
willEnter = (key, styles) => {
var sign = this.state.isFirstPage ? -1 : 1;
return {
...styles,
transformSpring: spring(sign * 100, transformSpringConfig),
opacitySpring: spring(1, opacitySpringConfig)
};
};
willLeave = (key, styles) => {
var sign = this.state.isFirstPage ? -1 : 1;
return {
...styles,
transformSpring: spring(sign * -100, transformSpringConfig),
opacitySpring: spring(0, opacitySpringConfig)
};
};
getBody(key, props) {
var {isFirstPage, transformSpring, opacitySpring} = props;
var style = {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
WebkitTransform: `translateX(${transformSpring}%)`,
transform: `translateX(${transformSpring}%)`,
opacity: opacitySpring
};
return (isFirstPage ? (
<ReactHeight key={`body${key}`} style={style} onHeightReady={this.updateHeight}>
<Input icon="envelope" type="email" placeholder={messages.emailOrUsername} />
</ReactHeight>
) : (
<ReactHeight key={`body${key}`} style={style} onHeightReady={this.updateHeight}>
<Input icon="user" color="blue" type="text" placeholder={regMessages.yourNickname} />
<Input icon="envelope" color="blue" type="email" placeholder={regMessages.yourEmail} />
<Input icon="key" color="blue" type="password" placeholder={regMessages.accountPassword} />
<Input icon="key" color="blue" type="password" placeholder={regMessages.repeatPassword} />
<Checkbox color="blue" label={
<Message {...regMessages.acceptRules} values={{
link: (
<a href="#">
<Message {...regMessages.termsOfService} />
</a>
)
}} />
} />
</ReactHeight>
));
}
updateHeight = (height) => {
this.setState({
height: {
...this.state.height,
[`page${this.state.isFirstPage ? '1' : '2'}`]: height
}
});
};
getFooter(key, props) {
var {isFirstPage, opacitySpring} = props;
var style = {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
opacity: opacitySpring
};
return (isFirstPage ? (
<button key={`footer${key}`} style={style} onClick={this.onSwitchViews} className={buttons.green}>
<Message {...messages.next} />
</button>
) : (
<button key={`footer${key}`} style={style} onClick={this.onSwitchViews} className={buttons.blue}>
<Message {...regMessages.signUpButton} />
</button>
));
}
getHeader(key, props) {
var {isFirstPage, transformSpring} = 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)}%)`
};
return (isFirstPage ? (
<div key={`header${key}`} style={style}>
<Message {...messages.loginTitle}>
{(msg) => <div style={scrollStyle}>{msg}<Helmet title={msg} /></div>}
</Message>
</div>
) : (
<div key={`header${key}`} style={style}>
<button style={sideScrollStyle} onClick={this.onSwitchViews} className={panelStyles.headerControl}>
<span className={icons.arrowLeft} />
</button>
<Message {...regMessages.registerTitle}>
{(msg) => <div style={scrollStyle}>{msg}<Helmet title={msg} /></div>}
</Message>
</div>
));
}
getLinks(key, props) {
var {isFirstPage, opacitySpring} = props;
var style = {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
opacity: opacitySpring
};
return (isFirstPage ? (
<div key={`links${key}`} style={style}>
<a href="#">
<Message {...passwordMessages.forgotPassword} />
</a>
</div>
) : (
<div key={`links${key}`} style={style}>
<a href="#">
{'test 123'}
</a>
</div>
));
}
onSwitchViews = (event) => {
event.preventDefault();
this.setState({
isFirstPage: !this.state.isFirstPage
});
};
}
export default connect((state) => ({

View File

@ -27,11 +27,11 @@ export default (
<IndexRoute component={IndexPage} onEnter={requireAuth} />
<Route path="auth" component={AuthPage}>
<Route path="/login" component={Login} />
<Route path="/password" component={Password} />
<Route path="/register" component={Register} />
<Route path="/activation" component={Activation} />
<Route path="/oauth/permissions" component={Permissions} />
<Route path="/login" components={new Login()} />
<Route path="/password" components={new Password()} />
<Route path="/register" components={new Register()} />
<Route path="/activation" components={new Activation()} />
<Route path="/oauth/permissions" components={new Permissions()} />
<Route path="/oauth/:id" component={Permissions} />
</Route>
</Route>