FE-599: added passport-form (#4)

This commit is contained in:
Alexandra Usacheva 2018-05-11 14:59:03 +03:00 committed by GitHub
parent c62825ee8e
commit 24d58cc71b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
136 changed files with 4175 additions and 2291 deletions

View File

@ -1,5 +1,6 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const rules = require('./common-rules');
@ -40,6 +41,12 @@ module.exports = {
new HtmlWebpackPlugin({
template: './src/app/index.html',
filename: 'app.html'
})
}),
new CopyWebpackPlugin(
[
{from: './src/app/assets/icons', to: './assets/icons'}
],
{debug: 'warning'}
)
]
};

View File

@ -38,6 +38,17 @@ const rules = [
fallback: 'style-loader'
})
},
{
test: /\.(jpeg|jpg)$/,
use: [{
loader: 'file-loader',
options: {
name: '[hash:8].[ext]',
outputPath: './assets/',
mimetype: 'mimetype=image/jpeg',
}
}]
},
{
test: /\.(woff|woff2)$/,
use: [{

4644
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,10 +19,12 @@
},
"homepage": "https://github.com/rbkmoney/wallet-utils#readme",
"devDependencies": {
"@types/classnames": "~2.2.3",
"@types/ismobilejs": "~0.4.1",
"@types/lodash-es": "~4.17.0",
"@types/ramda": "~0.25.24",
"@types/react": "~16.3.11",
"@types/react-addons-css-transition-group": "~15.0.4",
"@types/react-dom": "~16.0.5",
"cache-loader": "~1.2.2",
"copy-webpack-plugin": "~4.5.1",
@ -50,12 +52,13 @@
"write-file-webpack-plugin": "~4.2.0"
},
"dependencies": {
"classnames": "~2.2.5",
"ismobilejs": "~0.4.1",
"lodash-es": "~4.17.8",
"ramda": "~0.25.0",
"react": "~16.3.2",
"react-addons-css-transition-group": "~15.6.2",
"react-dom": "~16.3.2",
"redux-form": "~7.3.0",
"redux-saga": "~0.16.0"
}
}

View File

@ -0,0 +1,5 @@
<svg width="13" height="9" xmlns="http://www.w3.org/2000/svg">
<g id="icon" stroke-width="2" stroke="#85D335" fill="none" fill-rule="evenodd">
<path d="M11.65 1.35L5.1 7.9M1.46 3.5L6.4 7.77 1.46 3.5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 230 B

View File

@ -0,0 +1,5 @@
<svg width="9" height="15" xmlns="http://www.w3.org/2000/svg">
<g id="icon" stroke-width="2" stroke="#9013FE" fill="none" fill-rule="evenodd">
<path d="M8 1L1 8.3M1 7l7 7-7-7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 206 B

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" xmlns="http://www.w3.org/2000/svg">
<path id="icon" d="M9 7.59L1.93.5.5 1.93 7.6 9 .5 16.07l1.42 1.42L9 10.4l7.07 7.08 1.42-1.42L10.4 9l7.08-7.07L16.07.5 9 7.6z" fill="#FFF" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 235 B

View File

@ -0,0 +1,3 @@
<svg width="17" height="17" xmlns="http://www.w3.org/2000/svg">
<path id="icon" d="M8.5 7.3l-6-6-1.2 1.2 6 6-6 6 1.2 1.2 6-6 6 6 1.2-1.2-6-6 6-6-1.2-1.2z" stroke="red" fill-rule="nonzero" fill="red"/>
</svg>

After

Width:  |  Height:  |  Size: 211 B

View File

@ -0,0 +1 @@
export * from './layout';

View File

@ -0,0 +1,19 @@
@import '../../styles/responsive';
.layout {
position: relative;
height: 100%;
min-height: 100%;
width: 100%;
@include responsive(sm) {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
height: auto;
padding: 45px 0;
box-sizing: border-box;
}
}

View File

@ -0,0 +1 @@
export const layout: string;

View File

@ -0,0 +1,12 @@
import * as React from 'react';
import { layout } from './layout.scss';
import { Overlay } from './overlay';
import { ModalContainer } from './modal-container';
export const App: React.SFC = () => (
<div className={layout}>
<Overlay/>
<ModalContainer/>
</div>
);

View File

@ -0,0 +1,120 @@
@import '../../../../styles/variables';
@import '../../../../styles/responsive';
$local-white: rgba(255, 255, 255, .6);
.footer {
padding: 30px 25px;
@include responsive(sm) {
padding: 15px 0;
}
}
.safe_payment_container {
@include responsive(sm) {
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
}
}
.safe_payment {
display: flex;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
margin: 0 0 15px;
.label {
font-family: 'Roboto', sans-serif;
font-size: 11px;
font-weight: 900;
color: $lightest-purple;
letter-spacing: 2px;
line-height: 15px;
text-transform: uppercase;
margin: 0;
@include responsive(sm) {
color: $local-white;
}
}
.secure_icon {
margin-right: 8px;
position: relative;
top: -2px;
path {
stroke: $lightest-purple2;
}
rect {
fill: $lightest-purple2;
}
@include responsive(sm) {
path {
stroke: $local-white;
}
rect {
fill: $local-white;
}
}
}
}
.safe_logos {
display: flex;
align-items: center;
margin-bottom: 25px;
svg {
margin-right: 25px;
}
.align_fix {
position: relative;
top: 4px;
}
.fill_icons {
fill: $lightest-purple2;
@include responsive(sm) {
fill: $local-white;
}
}
}
.copyright {
font-family: 'Roboto', sans-serif;
font-size: 13px;
font-weight: 400;
font-style: italic;
color: $lightest-purple2;
letter-spacing: 0;
line-height: 18px;
margin: 0 0 20px;
@include responsive(sm) {
color: $local-white;
max-width: 680px;
}
}
.offer {
display: inline-block;
font-family: 'Roboto', sans-serif;
font-weight: 900;
text-transform: uppercase;
text-decoration: none;
font-size: 11px;
color: $lightest-purple2;
letter-spacing: 2px;
line-height: 15px;
padding: 0;
margin: 0;
hr {
border-color: $lightest-purple2;
border-bottom: 0;
padding: 0;
margin: 0 0 3px;
}
@include responsive(sm) {
color: $local-white;
hr {
border-color: $local-white;
}
}
}

View File

@ -0,0 +1,10 @@
export const footer: string;
export const safe_payment_container: string;
export const safe_payment: string;
export const label: string;
export const safe_logos: string;
export const copyright: string;
export const offer: string;
export const fill_icons: string;
export const align_fix: string;
export const secure_icon: string;

View File

@ -0,0 +1,46 @@
import * as React from 'react';
import {
footer,
safe_payment_container,
safe_payment,
secure_icon,
label,
safe_logos,
fill_icons,
align_fix,
copyright
} from './footer.scss';
import { SecureIcon } from './secure-icon';
import { PciDssIcon } from './pci-dss-icon';
import { McIcon } from './mc-icon';
import { VisaIcon } from './visa-icon';
const cardIcons = () => (
<div className={safe_payment_container}>
<div className={safe_payment}>
<SecureIcon className={secure_icon}/>
<p className={label}>Безопасная оплата с RBKmoney</p>
</div>
<div className={safe_logos}>
<VisaIcon fillStyle={fill_icons}/>
<McIcon className={align_fix} fillStyle={fill_icons}/>
<PciDssIcon className={align_fix} fillStyle={fill_icons}/>
</div>
</div>
);
const isCardBinding = false;
export const Footer: React.SFC = () => (
<footer className={footer}>
{isCardBinding ?
cardIcons()
: null
}
<p className={copyright}>
© 2008-2018 RBKmoney | НКО "ЭПС" (ООО). Лицензия Банка России 3509-К, выдана 11 февраля 2013 г.
Персональные данные защищены в соответствии с требованиями Федерального закона 152-ФЗ «О персональных
данных» от 27.07.2006 г.
</p>
</footer>
);

View File

@ -0,0 +1 @@
export * from './footer';

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,16 @@
import * as React from 'react';
import { StylizedSvgIcon } from './stylized-svg-icon';
export const PciDssIcon: React.SFC<StylizedSvgIcon> = (props) => (
<svg className={props.className} width='63' height='24'>
<g className={props.fillStyle} fill='none' fillRule='evenodd'>
<path className={props.fillStyle} d='M42.75 13.63l.61.18c-.04.17-.1.32-.2.43-.08.12-.2.2-.32.26-.14.06-.3.1-.5.1-.25 0-.46-.04-.61-.12a.96.96 0 0 1-.4-.37 1.27 1.27 0 0 1-.18-.7c0-.37.1-.65.3-.85.2-.2.48-.3.85-.3.29 0 .51.05.68.17.16.11.28.29.36.53l-.62.13a.4.4 0 0 0-.4-.32.41.41 0 0 0-.37.2.8.8 0 0 0-.1.44c0 .25.04.43.12.52.08.1.18.14.32.14s.24-.04.3-.11a.7.7 0 0 0 .16-.33zm.87-.2c0-.37.1-.66.3-.87.21-.2.5-.3.87-.3.38 0 .67.1.87.3.2.2.31.48.31.85 0 .26-.04.48-.13.64a.95.95 0 0 1-.39.4c-.17.1-.38.14-.63.14-.26 0-.47-.04-.64-.12a.96.96 0 0 1-.4-.39c-.11-.17-.16-.4-.16-.66zm.7 0c0 .22.04.39.13.49.08.1.2.15.34.15.16 0 .27-.05.35-.15.09-.1.13-.27.13-.52 0-.22-.04-.37-.13-.47a.44.44 0 0 0-.35-.15.43.43 0 0 0-.34.15c-.09.1-.13.27-.13.5zm2.02-1.14h.93l.35 1.38.36-1.38h.92v2.26h-.57v-1.72l-.45 1.72h-.52l-.44-1.72v1.72h-.58V12.3zm3 0h1.17c.26 0 .45.06.58.18.12.12.19.3.19.52a.7.7 0 0 1-.21.53c-.14.13-.35.2-.63.2h-.39v.83h-.7V12.3zm.71.97h.17c.14 0 .23-.03.29-.07.05-.05.08-.11.08-.18a.25.25 0 0 0-.07-.18c-.05-.05-.14-.08-.27-.08h-.2v.5zm1.6-.97h.7V14h1.1v.56h-1.8V12.3zm2.14 0h.7v2.26h-.7V12.3zm2.6 1.89h-.8l-.1.37h-.72l.85-2.26h.77l.85 2.26h-.73l-.11-.37zm-.14-.49l-.25-.81-.25.81h.5zm1.22-1.4h.66l.86 1.25V12.3h.66v2.26h-.66l-.85-1.24v1.24h-.67V12.3zm2.49 0h2.13v.56h-.71v1.7h-.7v-1.7h-.72v-.56z' fill='#FFF'/>
<path className={props.fillStyle} d='M34.23 15.9l2.05-.63-.67-1.45c-.49.6-.98 1.37-1.38 2.08zm-11.2 1.26c.12.63.44 1.3.88 1.93L8.2 23.94.02 2.12 29.5.48l4.03 8.78c-.46.62-1.83 2.47-3.12 4.29V7.72h-3.65v7.82a4.07 4.07 0 0 0-1.55-.66v-.39c0-.03-.02-.04-.05-.02 0 0-.4.31-2.13.48-.49.05-.72.02-.9 0-2.46-.41-2.58-2.18-2.58-2.18V12a.9.9 0 0 1 0-.1s.17-1.9 2.58-2.1h.9c1.07.14 1.92.66 1.92.66.03.01.05 0 .05-.03V8.05a.1.1 0 0 0-.05-.08s-.48-.3-1.94-.39c-.08-.04-1.35-.05-1.7 0-5.32.42-5.52 4.19-5.52 4.36v.91c0 .12 0 3.97 5.51 4.31.54.04 1.6 0 1.7 0h.03zM28.6 6.83c1.23 0 2.23-.93 2.23-2.07 0-1.14-1-2.06-2.23-2.06-1.24 0-2.24.92-2.24 2.06 0 1.14 1 2.07 2.24 2.07zM5.65 17.03c0 .03.03.06.06.06h3.56c.03 0 .05-.03.05-.06v-2.71c0-.03.03-.05.06-.05 0 0 5.68.4 5.68-3.34 0-2.94-3.57-3.27-4.74-3.21H5.71a.05.05 0 0 0-.06.05v9.26z' fill='#FFF' fillRule='nonzero'/>
<path className={props.fillStyle} d='M28.69 21.58c.3 0 .52 0 .9-.16 1.3-.67 5.66-11.12 10.27-14.33a.41.41 0 0 0 .09-.08c.03-.05.03-.09.03-.09s0-.22-.68-.22c-4.1-.1-8.37 8.32-10.61 11.65-.03.04-.19 0-.19 0s-1.5-1.74-2.8-2.4a.81.81 0 0 0-.33-.05c-.1 0-.71.12-1 .41-.33.35-.33.54-.32.96 0 .04.02.18.06.25.32.54 1.78 2.5 2.97 3.6.19.12.47.46 1.6.46z' fill='#FFF' fillRule='nonzero'/>
<g fill='#FFF'>
<path className={props.fillStyle} d='M41.07 3.98h3.38c.67 0 1.2.09 1.62.27.4.18.75.43 1.01.77.27.34.47.73.59 1.17.12.45.18.92.18 1.42 0 .78-.1 1.39-.27 1.82-.18.43-.43.8-.75 1.08-.32.3-.66.49-1.02.58-.5.14-.95.2-1.36.2h-3.38V3.98zm2.27 1.65v4h.56c.48 0 .82-.05 1.02-.16.2-.1.36-.28.47-.54.11-.27.17-.69.17-1.27 0-.78-.13-1.31-.38-1.6-.26-.28-.68-.43-1.27-.43h-.57zm5.3 3.24l2.18-.13c.04.35.14.61.28.8.24.3.57.44 1 .44.33 0 .58-.07.76-.23a.68.68 0 0 0 .26-.52.67.67 0 0 0-.25-.51c-.17-.15-.55-.3-1.16-.43-1-.22-1.72-.51-2.14-.88a1.78 1.78 0 0 1-.64-1.42c0-.37.1-.73.32-1.06.22-.34.55-.6 1-.8.44-.18 1.04-.28 1.8-.28.95 0 1.67.18 2.16.52.5.35.8.9.88 1.66l-2.14.13c-.06-.33-.18-.57-.36-.72a1.16 1.16 0 0 0-.76-.23c-.26 0-.46.06-.6.17a.51.51 0 0 0-.2.4c0 .13.06.23.17.32.1.1.36.19.76.27 1 .22 1.71.43 2.14.65.43.22.75.49.94.81.2.32.3.68.3 1.08a2.45 2.45 0 0 1-1.49 2.2c-.47.2-1.06.3-1.78.3-1.25 0-2.12-.23-2.6-.71a2.83 2.83 0 0 1-.82-1.83zM55.87 8.87l2.16-.13c.05.35.15.61.3.8.23.3.56.44 1 .44.32 0 .57-.07.75-.23a.68.68 0 0 0 .26-.52.67.67 0 0 0-.25-.51c-.17-.15-.56-.3-1.17-.43-1-.22-1.7-.51-2.13-.88a1.78 1.78 0 0 1-.65-1.42c0-.37.11-.73.33-1.06.22-.34.55-.6 1-.8.43-.18 1.04-.28 1.8-.28.95 0 1.67.18 2.16.52.5.35.79.9.88 1.66l-2.14.13c-.06-.33-.18-.57-.36-.72a1.16 1.16 0 0 0-.76-.23c-.26 0-.46.06-.6.17a.51.51 0 0 0-.2.4c0 .13.06.23.17.32.1.1.36.19.76.27 1 .22 1.7.43 2.14.65.43.22.74.49.94.81.2.32.3.68.3 1.08a2.45 2.45 0 0 1-1.49 2.2c-.47.2-1.06.3-1.78.3-1.25 0-2.12-.23-2.6-.71a2.83 2.83 0 0 1-.82-1.83z'/>
</g>
<path d='M9.26 12.2V9.75h.87s1.27.06 1.37.9c.02.07.01.48 0 .5-.17 1-1.27 1.05-1.27 1.05h-.97z' fill='#D8D8D8'/>
</g>
</svg>
);

View File

@ -0,0 +1,11 @@
import * as React from 'react';
import { StylizedSvgIcon } from './stylized-svg-icon';
export const SecureIcon: React.SFC<StylizedSvgIcon> = (props) => (
<svg className={props.className} width='8' height='11'>
<g fill='none' fillRule='evenodd'>
<path d='M1 4.5a.5.5 0 0 0-.5.5v5a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 .5-.5V5a.5.5 0 0 0-.5-.5h-.5V3C6.5 1.6195 5.3807.5003 4 .5003 2.6195.5002 1.5003 1.6194 1.5003 3V4.5H1z'/>
<rect fillRule='evenodd' x='1' y='4' width='6' height='6'/>
</g>
</svg>
);

View File

@ -0,0 +1,4 @@
export interface StylizedSvgIcon {
className?: string;
fillStyle?: string;
}

View File

@ -0,0 +1,12 @@
import * as React from 'react';
import { StylizedSvgIcon } from './stylized-svg-icon';
export const VisaIcon: React.SFC<StylizedSvgIcon> = (props) => (
<svg className={props.className} width='59' height='32'>
<g className={props.fillStyle} fill='#FFF' fillRule='evenodd'>
<path d='M50.27 21.32a19 19 0 0 1-.32 1.8c-.56-.17-1.2-.45-2.12-.4-.54.04-1.18.24-1.2.71 0 .53.9.84 1.39 1.06.9.41 1.85.96 1.93 2.08.06.85-.36 1.6-.83 2.03-1.25 1.13-4.03 1.25-5.93.53.12-.61.23-1.23.32-1.86.74.3 1.75.7 2.85.58.5-.06 1.12-.35 1.1-.84-.02-.6-1.2-.97-1.79-1.28-.69-.37-1.47-.97-1.51-1.9-.03-.62.22-1.2.55-1.59.98-1.16 3.5-1.58 5.56-.92M27.74 9.5h2.53l-1.56 9.44h-2.53l1.56-9.44M21.18 11.38l-.4 2.37h.74c.71 0 1.39-.54 1.5-1.19.11-.65-.38-1.18-1.1-1.18h-.74zM18.96 9.5h3.88c2.47 0 3.1 1.37 2.84 3-.23 1.36-1.25 2.27-2.57 2.59l2.46 3.85h-3.18l-1.85-3.7-.61 3.7H17.4l1.56-9.44zM39.31 9.5h2.54l-1.56 9.44h-2.54l1.56-9.44M43.77 9.5h5.57L49 11.55h-3.03l-.27 1.62h2.88l-.33 2.05h-2.89l-.27 1.67h3.03l-.34 2.05H42.2l1.56-9.44M11.88 9.5h5.57l-.34 2.05h-3.03l-.27 1.62h2.88l-.34 2.05h-2.88l-.27 1.67h3.03l-.34 2.05h-5.57l1.56-9.44M32.2 9.5h5.62l-.34 2.05h-3.1l-.26 1.62h2.81l-.34 2.05h-2.81l-.62 3.72h-2.53L32.2 9.5M53.03 11.55h.52c1.94 0 2.7 1.32 2.49 2.58a3.14 3.14 0 0 1-3.22 2.76h-.67l.88-5.34zm-2.2-2.05h3.3c3.94 0 4.84 2.86 4.53 4.72-.44 2.67-2.92 4.72-6.09 4.72h-3.3l1.56-9.44zM0 8.22h4.69l.56 6.85C8.35 9.92 11.5 4.97 14.68.1h1.53a771.62 771.62 0 0 0-9.9 18.85H2.87l-.84-7.56C1.82 9.5 1.43 8.7 0 8.5v-.28'/>
<path d='M1.78 4.32l4 1.62.34 4.28h.3l3.63-5.9v-.3H1.77v.3M18.21 21.16h1.64l-.55 3.72c.47-.43 1.04-.7 1.68-.7 1.32 0 2.18 1.21 1.95 2.72-.23 1.5-1.48 2.72-2.8 2.72-.64 0-1.1-.28-1.42-.75l-.09.59h-1.64l1.23-8.3zm2.05 4.54c.6 0 1.02.53.92 1.2-.1.65-.67 1.18-1.28 1.18-.6 0-1.01-.53-.92-1.19.1-.66.67-1.19 1.28-1.19z'/>
<path d='M23.07 24.3h1.97l.88 2.64 1.7-2.64h1.91l-5 7.7h-1.9l2.11-3.25-1.67-4.45M39.31 29.45l1.45-8.3h2.35l-1.44 8.3h-2.36M37.34 21.14h2.48c-1.2 2.79-2.48 5.5-3.67 8.3h-2.53l-2.1-7.18a8.55 8.55 0 0 1 2.35 1.93l.03.04c.4.5.7 1.03.89 1.6l-.04-.19v.01l.25 1.1c.83-1.82 1.53-3.77 2.34-5.6zm-4.45 0c.53 0 .96.24 1.07.83l.83 3.87c-.73-2.32-3.54-4.1-5.76-4.52v-.18h3.86zM54.64 23.44l-1.2 3.08h1.94l-.74-3.08zm3.58 6.04h-2.16c-.14-.4-.18-.89-.32-1.28H52.8l-.55 1.28h-2.49l3.22-7.1c.14-.32.4-.74.5-.93.17-.29.8-.3 1.07-.3h1.79l1.88 8.33z'/>
</g>
</svg>
);

View File

@ -0,0 +1 @@
export * from './modal-container';

View File

@ -0,0 +1,33 @@
@import '../../../styles/responsive';
@import '../../../styles/animations';
.appear {
@include responsive(sm) {
animation: popup .75s;
}
}
.enter {
background: transparent;
}
.leave {
@include responsive(sm) {
animation: popout 1s;
}
}
.container {
height: 100%;
position: relative;
footer {
display: none;
}
@include responsive(sm) {
footer {
display: block;
}
}
}

View File

@ -0,0 +1,4 @@
export const appear: string;
export const enter: string;
export const leave: string;
export const container: string;

View File

@ -0,0 +1,33 @@
import * as React from 'react';
import * as ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import { appear, leave, enter, container } from './modal-container.scss';
import { Modal } from './modal';
import { Footer } from './footer';
import { Close } from '../modal-container/modal/close';
export const ModalContainer: React.SFC = () => (
<ReactCSSTransitionGroup
component='div'
transitionName={{enter, appear, leave}}
transitionEnterTimeout={950}
transitionLeaveTimeout={950}
transitionAppearTimeout={950}
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}>
<div className={container}>
<ReactCSSTransitionGroup
component='div'
transitionName='interactionAnimation'
transitionEnterTimeout={1000}
transitionLeaveTimeout={500}>
<div>
<Close/>
<Modal/>
<Footer/>
</div>
</ReactCSSTransitionGroup>
</div>
</ReactCSSTransitionGroup>
);

View File

@ -0,0 +1,21 @@
@import '../../../../../styles/variables';
@import '../../../../../styles/responsive';
.close {
display: none;
height: 22px;
width: 22px;
position: absolute;
right: -30px;
top: -27px;
cursor: pointer;
@include responsive(sm) {
display: block;
}
svg {
height: 22px;
width: 22px;
}
}

View File

@ -0,0 +1 @@
export const close: string;

View File

@ -0,0 +1,9 @@
import * as React from 'react';
import { close } from './close.scss';
import { Icon, IconType } from 'app/components/ui';
export const Close: React.SFC = () => (
<div className={close} >
<Icon icon={IconType.cross}/>
</div>
);

View File

@ -0,0 +1 @@
export * from './close';

View File

@ -0,0 +1,48 @@
@import '../../../../../../styles/variables';
.button {
text-align: center;
border-radius: 4px;
padding: 12px;
font-family: 'Roboto', sans-serif;
font-weight: 500;
font-size: 16px;
letter-spacing: 0;
line-height: 20px;
transition: all .3s;
cursor: pointer;
width: 100%;
outline: none;
&._default {
color: $purple;
background: $white;
border: 2px solid $purple;
&:hover {
border-color: $deep-purple;
}
&:active {
border-color: $deepest-purple;
outline: none;
}
}
&._primary {
color: $white;
background: $purple;
border: 2px solid $purple;
&:hover {
background: $deep-purple;
border-color: $deep-purple;
}
&:active {
background: $deepest-purple;
border-color: $deepest-purple;
outline: none;
}
}
}

View File

@ -0,0 +1,3 @@
export const button: string;
export const _default: string;
export const _primary: string;

View File

@ -0,0 +1,26 @@
import * as React from 'react';
import { MouseEventHandler } from 'react';
import * as cx from 'classnames';
import { button, _primary, _default } from './button.scss';
type ButtonType = 'primary' | 'default';
export interface ButtonProps {
style: ButtonType;
type?: 'submit';
children: React.ReactNode;
className?: string;
id?: string;
onClick?: MouseEventHandler<HTMLButtonElement>;
}
const getClass = (type: ButtonType) => type === 'primary' ? _primary : _default;
export const Button: React.SFC<ButtonProps> = (props) => (
<button type={props.type}
onClick={props.onClick}
className={cx(button, getClass(props.style), props.className)}
id={props.id}>
{props.children}
</button>
);

View File

@ -0,0 +1 @@
export * from './button';

View File

@ -0,0 +1,12 @@
import * as React from 'react';
import { Icon, IconType } from 'app/components/ui';
interface ChevronBackProps {
className: string;
}
export const ChevronBack: React.SFC<ChevronBackProps> = (props) => (
<div className={props.className}>
<Icon icon={IconType.chevronLeft}/>
</div>
);

View File

@ -0,0 +1 @@
export * from './chevron-back';

View File

@ -0,0 +1,11 @@
import * as React from 'react';
import { Input } from '../../input';
export const Email: React.SFC = () => (
<Input
placeholder={'Email'}
mark={true}
type='email'
id='email-input'
/>
);

View File

@ -0,0 +1 @@
export * from './email';

View File

@ -0,0 +1,7 @@
export function validateEmail(value: any): boolean {
if (!value) {
return true;
}
const regExp = new RegExp(/^[A-z0-9._%+-]+@[A-z0-9.-]+\.[A-z]{2,63}$/i); // 2 to 63 because domain name can be from 2 characters to 63 characters only by RFC 1035
return !regExp.test(value.trim().toLowerCase());
}

View File

@ -0,0 +1 @@
export * from './email';

View File

@ -0,0 +1,44 @@
@import '../../../../../styles/variables';
@import '../../../../../styles/responsive';
.container {
padding: 0 5px;
@include responsive(sm) {
width: 360px;
padding: 0;
}
}
.form {
background: $white;
border-radius: 6px;
box-shadow: 0 2px 24px 0 rgba(0, 0, 0, 0.25);
padding: 30px 20px;
position: relative;
overflow: hidden;
transition: height .4s;
@include responsive(sm) {
padding: 30px;
min-height: auto;
}
&._error {
animation: shake .82s;
}
}
.animationFormContainer {
height: 100%;
position: relative;
form {
height: 100%;
width: 100%;
display: flex;
flex-wrap: nowrap;
flex-direction: column;
justify-content: space-between;
}
}

View File

@ -0,0 +1,5 @@
export const container: string;
export const form: string;
export const _error: string;
export const text: string;
export const animationFormContainer: string;

View File

@ -0,0 +1,21 @@
import * as React from 'react';
import * as cx from 'classnames';
import * as ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import { container, form, animationFormContainer, _error } from './form-container.scss';
import { PassportForm } from './passport-form';
export const FormContainer: React.SFC = () => (
<div className={container}>
<div className={cx(form, {[_error]: false})}
style={{height: 629}}> {/* TODO: высота должна задаваться в зависимости от стейта */}
<ReactCSSTransitionGroup
component='div'
className={animationFormContainer}
transitionName={'slideDirection'}
transitionEnterTimeout={550}
transitionLeaveTimeout={550}>
<PassportForm/>
</ReactCSSTransitionGroup>
</div>
</div>
);

View File

@ -0,0 +1,6 @@
.formGroup {
display: flex;
flex-wrap: nowrap;
flex-direction: row;
margin-bottom: 10px;
}

View File

@ -0,0 +1 @@
export const formGroup: string;

View File

@ -0,0 +1,33 @@
@import '../../../../../../styles/variables';
@import '../../../../../../styles/responsive';
.header {
margin-bottom: 20px;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
position: relative;
.back_btn {
display: none;
@include responsive(sm) {
display: flex;
}
}
}
.title {
font-family: 'Roboto', sans-serif;
font-weight: 500;
font-size: 16px;
color: $deep-gray;
letter-spacing: 0;
line-height: 20px;
width: 100%;
@include responsive(sm) {
text-align: center;
}
}

View File

@ -0,0 +1,2 @@
export const title: string;
export const header: string;

View File

@ -0,0 +1,14 @@
import * as React from 'react';
import { title, header } from './header.scss';
export interface HeaderProps {
title: string;
}
export const Header: React.SFC<HeaderProps> = (props: HeaderProps) => (
<div className={header}>
<div className={title}>
{props.title}
</div>
</div>
);

View File

@ -0,0 +1 @@
export * from './header';

View File

@ -0,0 +1,2 @@
export * from './form-container';
export * from './common-fields';

View File

@ -0,0 +1 @@
export * from './input';

View File

@ -0,0 +1,125 @@
@import '../../../../../../styles/variables';
@import '../../../../../../styles/animations';
@import '../../../../../../styles/responsive';
.container {
position: relative;
width: 100%;
&:nth-child(2) {
margin-left: 10px;
}
.checkmark,
.errorCross {
display: flex;
position: absolute;
right: 0;
top: 0;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
}
.checkmark {
height: 9px;
width: 13px;
margin: 19px 15px 0 19px;
}
.errorCross {
height: 18px;
width: 18px;
margin: 15px 15px 0 19px;
transform: scale(.7);
}
&._hasError {
input {
border-color: red;
}
}
}
//.icon {
// position: absolute;
// top: 0;
// left: 0;
// margin: 15px 0 15px 15px;
// width: 19px;
// height: 18px;
// display: flex;
// flex-direction: row;
// flex-wrap: nowrap;
// align-items: center;
// //opacity: .75;
// opacity: 1;
//}
.input {
margin: 0;
width: 100%;
box-sizing: border-box;
border-radius: 3px;
border: 1px solid $gray;
box-shadow: 0 0 4px 0 $white;
font-family: 'Roboto', sans-serif;
font-weight: 500;
font-size: 16px;
color: $deep-gray;
letter-spacing: 0;
line-height: 20px;
//padding: 14px 13px 12px 40px; padding with icons
padding: 14px 13px 12px 13px;
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
transition: border-color .3s;
&[type=date] {
padding: 11px 30px 10px 13px;
}
&::-webkit-input-placeholder { /* Chrome/Opera/Safari */
color: $light-gray;
font-family: 'Roboto', sans-serif;
}
&::-moz-placeholder { /* Firefox 19+ */
color: $light-gray;
font-family: 'Roboto', sans-serif;
}
&:-ms-input-placeholder { /* IE 10+ */
color: $light-gray;
font-family: 'Roboto', sans-serif;
}
&:-moz-placeholder { /* Firefox 18- */
color: $light-gray;
font-family: 'Roboto', sans-serif;
}
&:focus {
border-color: $light-blue;
box-shadow: 0 0 4px 0 $light-blue;
outline: $light-blue solid 2px;
outline-offset: -1px;
}
&.mark {
//padding: 13px 30px 13px 40px; padding with icons
padding: 13px 30px 12px 13px;
&[type=date] {
padding: 11px 30px 10px 13px;
}
}
}
.appear {
animation: fadein .5s;
}
.enter {
animation: fadein .5s;
}
.leave {
animation: fadeout .5s;
}

View File

@ -0,0 +1,10 @@
export const container: string;
export const checkmark: string;
export const errorCross: string;
export const _hasError: string;
export const icon: string;
export const input: string;
export const mark: string;
export const appear: string;
export const enter: string;
export const leave: string;

View File

@ -0,0 +1,26 @@
import * as React from 'react';
import * as styles from './input.scss';
import * as cx from 'classnames';
import { Marks } from './marks';
export interface InputProps {
placeholder?: string;
mark?: boolean; // TODO mark always true
className?: string;
type?: 'text' | 'number' | 'value' | 'tel' | 'email' | 'password' | 'date';
id?: string;
}
export const Input: React.SFC<InputProps> = (props) => (
<div className={cx(styles.container, props.className, {
[styles._hasError]: false
})}>
<input
className={cx(styles.input, {[styles.mark]: props.mark})}
placeholder={props.placeholder}
type={props.type}
id={props.id}
/>
{props.mark ? <Marks active={false} pristine={false} error={false}/> : false}
</div>
);

View File

@ -0,0 +1 @@
export * from './marks';

View File

@ -0,0 +1,26 @@
import * as React from 'react';
import * as ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import { appear, enter, leave, checkmark, errorCross } from '../input.scss';
import { Icon, IconType } from 'app/components/ui';
interface MarksProps {
active: boolean;
pristine: boolean;
error: boolean;
}
export const Marks: React.SFC<MarksProps> = (props) => (
<ReactCSSTransitionGroup
component='div'
transitionName={{ appear, enter, leave }}
transitionEnterTimeout={450}
transitionLeaveTimeout={450}
transitionAppearTimeout={450}
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
>
{!props.active && !props.error && !props.pristine ? <Icon className={checkmark} icon={IconType.checkmark}/> : false}
{!props.active && props.error ? <Icon className={errorCross} icon={IconType.redCross}/> : false}
</ReactCSSTransitionGroup>
);

View File

@ -0,0 +1,14 @@
import * as React from 'react';
import { Input } from '../../../input';
import { formGroup } from '../../../forms.scss';
export const BirthDate: React.SFC = () => (
<div className={formGroup}>
<Input
placeholder='Дата рождения'
mark={true}
id='passport-birthdate-input'
type='date'
/>
</div>
);

View File

@ -0,0 +1 @@
export * from './birth-date';

View File

@ -0,0 +1,13 @@
import * as React from 'react';
import { Input } from '../../../input';
import { formGroup } from '../../../forms.scss';
export const BirthPlace: React.SFC = () => (
<div className={formGroup}>
<Input
placeholder='Место рождения'
mark={true}
id='passport-birthplace-input'
/>
</div>
);

View File

@ -0,0 +1 @@
export * from './birth-place';

View File

@ -0,0 +1,10 @@
@import '../../../../../../../../styles/variables';
.label {
font-family: 'Roboto', sans-serif;
font-weight: 300;
font-size: 16px;
color: $deep-gray;
letter-spacing: 0;
margin: 14px 0 14px 0;
}

View File

@ -0,0 +1 @@
export const label: string;

View File

@ -0,0 +1,11 @@
import * as React from 'react';
import { formGroup } from '../../../forms.scss';
import { label } from './gender.scss';
import { RadioInput } from '../../../radio-input';
export const Gender: React.SFC = () => (
<div className={formGroup}>
<span className={label}>Пол:</span>
<RadioInput values={[{name: 'gender', value: 'male', label: 'Муж'}, {name: 'gender', value: 'female', label: 'Жен'}]}/>
</div>
);

View File

@ -0,0 +1 @@
export * from './gender';

View File

@ -0,0 +1,9 @@
export * from './issuer';
export * from './issued';
export * from './passport-number';
export * from './birth-date';
export * from './birth-place';
export * from './gender';
export * from './name';
export * from './patronymic';
export * from './surname';

View File

@ -0,0 +1 @@
export * from './issued';

View File

@ -0,0 +1,14 @@
import * as React from 'react';
import { Input } from '../../../input';
import { formGroup } from '../../../forms.scss';
export const Issued: React.SFC = () => (
<div className={formGroup}>
<Input
placeholder='Когда выдан'
mark={true}
id='passport-issued-input'
type='date'
/>
</div>
);

View File

@ -0,0 +1 @@
export * from './issuer';

View File

@ -0,0 +1,13 @@
import * as React from 'react';
import { Input } from '../../../input';
import { formGroup } from '../../../forms.scss';
export const Issuer: React.SFC = () => (
<div className={formGroup}>
<Input
placeholder='Кем выдан'
mark={true}
id='passport-issuer-input'
/>
</div>
);

View File

@ -0,0 +1 @@
export * from './name';

View File

@ -0,0 +1,13 @@
import * as React from 'react';
import { Input } from '../../../input';
import { formGroup } from '../../../forms.scss';
export const Name: React.SFC = () => (
<div className={formGroup}>
<Input
placeholder='Имя'
mark={true}
id='passport-name-input'
/>
</div>
);

View File

@ -0,0 +1 @@
export * from './passport-number';

View File

@ -0,0 +1,13 @@
import * as React from 'react';
import { Input } from '../../../input';
import { formGroup } from '../../../forms.scss';
export const PassportNumber: React.SFC = () => (
<div className={formGroup}>
<Input
placeholder='Серия и номер'
mark={true}
id='passport-number-input'
/>
</div>
);

View File

@ -0,0 +1 @@
export * from './patronymic';

View File

@ -0,0 +1,13 @@
import * as React from 'react';
import { Input } from '../../../input';
import { formGroup } from '../../../forms.scss';
export const Patronymic: React.SFC = () => (
<div className={formGroup}>
<Input
placeholder='Отчество'
mark={true}
id='passport-patronymic-input'
/>
</div>
);

View File

@ -0,0 +1 @@
export * from './surname';

View File

@ -0,0 +1,13 @@
import * as React from 'react';
import { Input } from '../../../input';
import { formGroup } from '../../../forms.scss';
export const Surname: React.SFC = () => (
<div className={formGroup}>
<Input
placeholder='Фамилия'
mark={true}
id='passport-surname-input'
/>
</div>
);

View File

@ -0,0 +1 @@
export * from './passport-form';

View File

@ -0,0 +1,27 @@
import * as React from 'react';
import { Header } from '../header';
import { Issuer, Issued, PassportNumber, Surname, Name, Patronymic, Gender, BirthDate, BirthPlace } from './fields';
import { Button } from '../button';
export const PassportForm: React.SFC = () => (
<form>
<div>
<Header key='header' title='Паспорт'/>
<Surname/>
<Name/>
<Patronymic/>
<Gender/>
<BirthDate/>
<BirthPlace/>
<PassportNumber/>
<Issuer/>
<Issued/>
</div>
<Button
type='submit'
style='primary'
id='pay-btn'>
Далее
</Button>
</form>
);

View File

@ -0,0 +1 @@
export * from './radio-input';

View File

@ -0,0 +1,75 @@
@import '../../../../../../styles/variables';
@import '../../../../../../styles/animations';
@import '../../../../../../styles/responsive';
.container {
position: relative;
width: 100%;
display: flex;
font-family: 'Roboto', sans-serif;
font-weight: 300;
font-size: 16px;
color: $deep-gray;
letter-spacing: 0;
user-select: none;
&:nth-child(2) {
margin-left: 10px;
}
&._hasError {
input {
border-color: red;
}
}
}
.radio {
position: relative;
margin: 14px 15px 14px 0;
input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 18px;
width: 18px;
margin: 0;
&:checked ~ .checkmark {
border-color: $light-blue;
}
&:checked ~ .checkmark:after {
opacity: 1;
}
}
.checkmark {
position: absolute;
top: 0;
left: 0;
display: inline-block;
height: 15px;
width: 15px;
border: 2px solid $black;
border-radius: 3px;
cursor: pointer;
&:after {
display: block;
opacity: 0;
content: "";
position: absolute;
top: -1px;
left: 1px;
color: $light-blue;
transition: opacity .3s;
}
}
label {
padding-left: 25px;
cursor: pointer;
}
}

View File

@ -0,0 +1,4 @@
export const container: string;
export const _hasError: string;
export const radio: string;
export const checkmark: string;

View File

@ -0,0 +1,33 @@
import * as React from 'react';
import { container, _hasError, radio, checkmark } from './radio-input.scss';
import * as cx from 'classnames';
export class Radio {
readonly name: string;
readonly value: string;
readonly label: string;
}
export interface RadioInputProps {
className?: string;
values: ReadonlyArray<Radio>;
}
const renderButtons = (data: Radio, i: number) => (
<div className={radio} key={i}>
<label htmlFor={data.value}>
<input type='radio' name={data.name} value={data.value} id={data.value}/>
<span className={checkmark}/>
{data.label}
</label>
</div>
);
export const RadioInput: React.SFC<RadioInputProps> = (props) => (
<div className={cx(container, props.className, {
[_hasError]: false
})}>
{props.values.map(renderButtons)}
</div>
);

View File

@ -0,0 +1 @@
export * from './modal';

View File

@ -0,0 +1 @@
export * from './info';

View File

@ -0,0 +1,54 @@
@import '../../../../../styles/variables';
@import '../../../../../styles/responsive';
.info {
padding: 30px 25px;
@include responsive(sm) {
padding: 0;
width: 230px;
margin-right: 30px;
margin-top: 30px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
}
.action_name {
display: none;
font-family: 'Roboto', sans-serif;
font-weight: 500;
font-size: 16px;
color: $white;
letter-spacing: 0;
line-height: 20px;
margin: 0 0 30px;
@include responsive(sm) {
display: block;
}
}
.label {
font-family: 'Roboto', sans-serif;
font-weight: 900;
font-size: 11px;
color: $white;
opacity: .4;
letter-spacing: 2px;
line-height: 15px;
margin-bottom: 5px;
text-transform: uppercase;
}
.action_description {
font-family: 'Roboto', sans-serif;
font-weight: 500;
font-size: 16px;
color: $white;
letter-spacing: 0;
line-height: 20px;
margin-bottom: 20px;
}

View File

@ -0,0 +1,4 @@
export const info: string;
export const action_name: string;
export const label: string;
export const action_description: string;

View File

@ -0,0 +1,19 @@
import * as React from 'react';
import { info, action_name, label, action_description } from './info.scss';
const getIdentityChallengeTitle = () => 'Процедура идентификации';
const getCreatePaymentResourceTitle = () => 'Заведение инструмента вывода средств';
const getIdentityChallendeDescription = () => 'Необходимо ввести паспортные данные и номер СНИЛС';
export const Info: React.SFC = () => (
<div className={info}>
<div>
<h4 className={action_name} id='company-name-label'>{getIdentityChallengeTitle()}</h4>
<div>
<div className={label}>Описание</div>
<div className={action_description} id='product-description'>{getIdentityChallendeDescription()}</div>
</div>
</div>
</div>
);

View File

@ -0,0 +1 @@
export * from './mobile-header';

View File

@ -0,0 +1,55 @@
@import '../../../../../styles/variables';
@import '../../../../../styles/responsive';
.header {
display: flex;
flex-wrap: nowrap;
flex-direction: row;
height: 60px;
background-color: $white;
align-items: center;
padding: 0 25px;
justify-content: center;
@include responsive(sm) {
display: none;
}
}
.text {
position: relative;
font-family: 'Roboto', sans-serif;
font-weight: 500;
height: 20px;
font-size: 16px;
letter-spacing: 0;
line-height: 20px;
color: $deep-gray;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
width: 100%;
}
.back_btn {
display: flex;
height: 20px;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
cursor: pointer;
position: absolute;
left: 20px;
width: 20px;
z-index: 1;
@include responsive(sm) {
display: none;
}
svg {
height: 15px;
width: 9px;
}
}

View File

@ -0,0 +1,4 @@
export const header: string;
export const text: string;
export const _center: string;
export const back_btn: string;

View File

@ -0,0 +1,11 @@
import * as React from 'react';
import * as cx from 'classnames';
import { header, text, _center } from './mobile-header.scss';
export const MobileHeader: React.SFC = () => (
<header className={header}>
<div className={cx(text, {[_center]: true})}>
[props.initConfig.name]
</div>
</header>
);

View File

@ -0,0 +1,54 @@
@import '../../../../styles/responsive';
@import '../../../../styles/animations';
@import '../../../../styles/variables';
.appear {
@include responsive(sm) {
animation: rotatein 0s;
}
}
.leave {
@include responsive(sm) {
animation: rotateout 0s;
}
}
.form_container {
position: relative;
height: 100%;
min-height: 100%;
width: 100%;
background-image: linear-gradient(70deg, $light-purple 0%, $deep-blue 29%, $blue 59%, $lightest-blue 100%);
footer {
display: block;
}
@include responsive(sm) {
footer {
display: none;
}
}
@include responsive(sm) {
height: auto;
min-height: auto;
width: 680px;
border-radius: 6px;
display: flex;
flex-wrap: nowrap;
flex-direction: row;
padding: 30px;
box-sizing: border-box;
background-image: linear-gradient(90deg, $deep-blue 0%, $blue 39%, $lightest-blue 100%);
footer {
display: none;
}
}
&.with_shadow {
box-shadow: 0 15px 49px 0 $very-deep-blue;
}
}

View File

@ -0,0 +1,4 @@
export const appear: string;
export const leave: string;
export const form_container: string;
export const with_shadow: string;

View File

@ -0,0 +1,30 @@
import * as React from 'react';
import * as ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import * as cx from 'classnames';
import { appear, leave, form_container, with_shadow } from './modal.scss';
import { Footer } from '../footer';
import { MobileHeader } from './mobile-header';
import { FormContainer } from './form-container';
import { Info } from './info';
export const Modal: React.SFC = () => (
<ReactCSSTransitionGroup
component='div'
transitionName={{ appear, enter: null, leave }}
transitionEnterTimeout={1000}
transitionLeaveTimeout={1000}
transitionAppearTimeout={1000}
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}>
<div className={cx(form_container, {
[with_shadow]: false
})} id='form-container'>
<MobileHeader/>
<Info/>
<FormContainer/>
<Footer/>{/*For mobile*/}
</div>
</ReactCSSTransitionGroup>
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Some files were not shown because too many files have changed in this diff Show More