mirror of
https://github.com/elyby/emails-renderer.git
synced 2024-12-30 17:10:17 +05:30
Migrate project's code base to TypeScript
This commit is contained in:
parent
c2c920d5f7
commit
71021a52c9
115
package.json
115
package.json
@ -1,57 +1,62 @@
|
|||||||
{
|
{
|
||||||
"name": "email-renderer",
|
"name": "email-renderer",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+ssh://git@gitlab.com/elyby/email-renderer.git"
|
"url": "git+ssh://git@gitlab.com/elyby/email-renderer.git"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "SleepWalker <dev@udf.su>",
|
"author": "SleepWalker <dev@udf.su>",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://gitlab.com/elyby/email-renderer/issues"
|
"url": "https://gitlab.com/elyby/email-renderer/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://gitlab.com/elyby/email-renderer#README",
|
"homepage": "https://gitlab.com/elyby/email-renderer#README",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "webpack-dev-server --mode=development --progress --colors",
|
"start": "webpack-dev-server --mode=development --progress --colors",
|
||||||
"lint": "eslint ./src",
|
"lint": "eslint ./src",
|
||||||
"i18n:collect": "./scripts/i18n-collect/index.js",
|
"i18n:collect": "./scripts/i18n-collect/index.js",
|
||||||
"i18n:pull": "node scripts/i18n-crowdin/index.js pull",
|
"i18n:pull": "node scripts/i18n-crowdin/index.js pull",
|
||||||
"i18n:push": "node scripts/i18n-crowdin/index.js push",
|
"i18n:push": "node scripts/i18n-crowdin/index.js push",
|
||||||
"build": "rm -rf dist/ && webpack --mode=production --progress --colors"
|
"build": "rm -rf dist/ && webpack --mode=production --progress --colors"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^16.8.4",
|
"react": "^16.8.4",
|
||||||
"react-dom": "^16.8.4",
|
"react-dom": "^16.8.4",
|
||||||
"react-intl": "^2.0.0"
|
"react-intl": "^2.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.3.4",
|
"@babel/core": "^7.3.4",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.3.4",
|
"@babel/plugin-proposal-class-properties": "^7.3.4",
|
||||||
"@babel/plugin-proposal-export-default-from": "^7.2.0",
|
"@babel/plugin-proposal-export-default-from": "^7.2.0",
|
||||||
"@babel/preset-env": "^7.3.4",
|
"@babel/preset-env": "^7.3.4",
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
"babel-eslint": "^6.0.0",
|
"@babel/preset-typescript": "^7.3.3",
|
||||||
"babel-loader": "^8.0.5",
|
"@types/react": "^16.8.17",
|
||||||
"babel-preset-react-hot": "^1.0.5",
|
"@types/react-intl": "^2.3.17",
|
||||||
"eslint": "^3.1.1",
|
"@types/webpack-env": "^1.13.9",
|
||||||
"eslint-plugin-react": "^6.0.0",
|
"babel-eslint": "^6.0.0",
|
||||||
"extended-translations-loader": "file:webpack-utils/extended-translations-loader",
|
"babel-loader": "^8.0.5",
|
||||||
"file-loader": "^3.0.1",
|
"babel-preset-react-hot": "^1.0.5",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"eslint": "^3.1.1",
|
||||||
"i18n-collect": "file:scripts/i18n-collect",
|
"eslint-plugin-react": "^6.0.0",
|
||||||
"i18n-crowdin": "file:scripts/i18n-crowdin",
|
"extended-translations-loader": "file:webpack-utils/extended-translations-loader",
|
||||||
"intl-json-loader": "file:./webpack-utils/intl-json-loader",
|
"file-loader": "^3.0.1",
|
||||||
"json-loader": "^0.5.7",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"prop-types": "^15.7.2",
|
"i18n-collect": "file:scripts/i18n-collect",
|
||||||
"text2png-loader": "file:./webpack-utils/text2png-loader",
|
"i18n-crowdin": "file:scripts/i18n-crowdin",
|
||||||
"webpack": "^4.29.6",
|
"intl-json-loader": "file:./webpack-utils/intl-json-loader",
|
||||||
"webpack-cli": "^3.3.0",
|
"json-loader": "^0.5.7",
|
||||||
"webpack-dev-server": "^3.2.1"
|
"text2png-loader": "file:./webpack-utils/text2png-loader",
|
||||||
},
|
"typescript": "^3.4.5",
|
||||||
"engines": {
|
"webpack": "^4.29.6",
|
||||||
"node": ">=7.6.0"
|
"webpack-bundle-analyzer": "^3.3.2",
|
||||||
}
|
"webpack-cli": "^3.3.0",
|
||||||
|
"webpack-dev-server": "^3.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
import React from 'react';
|
import React, { FunctionComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||||
|
|
||||||
import { SUPPORTED_LANGUAGES, DEFAULT_LANGUAGE } from './params';
|
import { SUPPORTED_LANGUAGES, DEFAULT_LANGUAGE } from 'params';
|
||||||
|
|
||||||
import BaseLayout from 'components/BaseLayout';
|
import { BaseLayout } from 'components';
|
||||||
|
|
||||||
export default function App({type, payload = {}}) {
|
export interface Params {
|
||||||
let { locale } = payload;
|
type: string;
|
||||||
|
payload: {
|
||||||
|
locale: string;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const App: FunctionComponent<Params> = ({ type, payload: { locale, ...params } }) => {
|
||||||
if (!locale || SUPPORTED_LANGUAGES.indexOf(locale) === -1) {
|
if (!locale || SUPPORTED_LANGUAGES.indexOf(locale) === -1) {
|
||||||
locale = DEFAULT_LANGUAGE;
|
locale = DEFAULT_LANGUAGE;
|
||||||
}
|
}
|
||||||
@ -21,17 +25,13 @@ export default function App({type, payload = {}}) {
|
|||||||
const { default: Email } = require(`emails/${type}/index`);
|
const { default: Email } = require(`emails/${type}/index`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
// @ts-ignore have no idea why
|
||||||
<IntlProvider locale={locale} messages={messages}>
|
<IntlProvider locale={locale} messages={messages}>
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<Email {...payload} />
|
<Email {...params} />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
App.propTypes = {
|
|
||||||
type: PropTypes.string.isRequired,
|
|
||||||
payload: PropTypes.shape({
|
|
||||||
locale: PropTypes.string,
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default App;
|
@ -1,10 +1,10 @@
|
|||||||
import React from 'react';
|
import React, { FunctionComponent } from 'react';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
import { Table } from 'components/table';
|
import { Table } from 'components/table';
|
||||||
|
|
||||||
export default function BaseLayout(props) {
|
const BaseLayout: FunctionComponent = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<Table style={styles.body}>
|
<Table style={styles.body}>
|
||||||
<tr>
|
<tr>
|
||||||
@ -12,7 +12,7 @@ export default function BaseLayout(props) {
|
|||||||
|
|
||||||
</td>
|
</td>
|
||||||
<td style={styles.container}>
|
<td style={styles.container}>
|
||||||
{props.children}
|
{children}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
@ -20,4 +20,6 @@ export default function BaseLayout(props) {
|
|||||||
</tr>
|
</tr>
|
||||||
</Table>
|
</Table>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default BaseLayout;
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React, { FunctionComponent } from 'react';
|
||||||
|
|
||||||
export default function Html(props) {
|
const Html: FunctionComponent = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
@ -10,8 +10,10 @@ export default function Html(props) {
|
|||||||
<body style={{
|
<body style={{
|
||||||
margin: 0
|
margin: 0
|
||||||
}}>
|
}}>
|
||||||
{props.children}
|
{children}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default Html;
|
@ -1,15 +1,21 @@
|
|||||||
import React from 'react';
|
import React, { FunctionComponent, ReactElement } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { FormattedMessage as Message } from 'react-intl';
|
import { FormattedMessage as Message } from 'react-intl';
|
||||||
|
|
||||||
import { colors, green } from 'components/ui/colors';
|
import { Colors } from 'components/ui/colors';
|
||||||
import { Button, Input } from 'components/ui';
|
import { Button, Input } from 'components/ui';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import messages from './messages.intl.json';
|
import messages from './messages.intl.json';
|
||||||
|
|
||||||
export default function Code({code, link, label, color = green}) {
|
interface Props {
|
||||||
|
code: string;
|
||||||
|
link: string;
|
||||||
|
label: ReactElement;
|
||||||
|
color?: Colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Code: FunctionComponent<Props> = ({ code, link, label, color = 'green' }) => {
|
||||||
return (
|
return (
|
||||||
<div style={styles.codeWrapper}>
|
<div style={styles.codeWrapper}>
|
||||||
<div>
|
<div>
|
||||||
@ -28,11 +34,6 @@ export default function Code({code, link, label, color = green}) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
Code.propTypes = {
|
|
||||||
code: PropTypes.string.isRequired,
|
|
||||||
link: PropTypes.string.isRequired,
|
|
||||||
label: PropTypes.node.isRequired,
|
|
||||||
color: PropTypes.oneOf(Object.values(colors))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default Code;
|
@ -1,21 +0,0 @@
|
|||||||
export default {
|
|
||||||
codeWrapper: {
|
|
||||||
paddingTop: '20px',
|
|
||||||
textAlign: 'center'
|
|
||||||
},
|
|
||||||
confirmEmailButton: {
|
|
||||||
paddingLeft: '50px',
|
|
||||||
paddingRight: '50px'
|
|
||||||
},
|
|
||||||
or: {
|
|
||||||
fontSize: '12px',
|
|
||||||
paddingTop: '5px'
|
|
||||||
},
|
|
||||||
codeLabel: {
|
|
||||||
paddingTop: '1px',
|
|
||||||
fontSize: '16px'
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
paddingTop: '5px'
|
|
||||||
}
|
|
||||||
};
|
|
25
src/components/blocks/code/styles.ts
Normal file
25
src/components/blocks/code/styles.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { CSSProperties } from 'react';
|
||||||
|
|
||||||
|
const styles: { [key: string]: CSSProperties } = {
|
||||||
|
codeWrapper: {
|
||||||
|
paddingTop: '20px',
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
confirmEmailButton: {
|
||||||
|
paddingLeft: '50px',
|
||||||
|
paddingRight: '50px',
|
||||||
|
},
|
||||||
|
or: {
|
||||||
|
fontSize: '12px',
|
||||||
|
paddingTop: '5px',
|
||||||
|
},
|
||||||
|
codeLabel: {
|
||||||
|
paddingTop: '1px',
|
||||||
|
fontSize: '16px',
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
paddingTop: '5px',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default styles;
|
@ -1,5 +0,0 @@
|
|||||||
import Code from './code/Code';
|
|
||||||
|
|
||||||
export {
|
|
||||||
Code
|
|
||||||
};
|
|
1
src/components/blocks/index.ts
Normal file
1
src/components/blocks/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Code } from './code/Code';
|
@ -1,5 +0,0 @@
|
|||||||
import Html from './Html';
|
|
||||||
|
|
||||||
export {
|
|
||||||
Html
|
|
||||||
};
|
|
2
src/components/index.ts
Normal file
2
src/components/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { default as HTML } from './Html';
|
||||||
|
export { default as BaseLayout } from './BaseLayout';
|
@ -1,11 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import styles from './styles';
|
|
||||||
|
|
||||||
export default function Content(props) {
|
|
||||||
return (
|
|
||||||
<div style={styles.content}>
|
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
13
src/components/layout/content/Content.tsx
Normal file
13
src/components/layout/content/Content.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import React, { FunctionComponent } from 'react';
|
||||||
|
|
||||||
|
import styles from './styles';
|
||||||
|
|
||||||
|
const Content: FunctionComponent = ({ children }) => {
|
||||||
|
return (
|
||||||
|
<div style={styles.content}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Content;
|
@ -1,24 +0,0 @@
|
|||||||
import {green} from 'components/ui/colors';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
footer: {
|
|
||||||
borderTop: `10px solid ${green.color}`,
|
|
||||||
background: '#DDD8CE',
|
|
||||||
height: '135px'
|
|
||||||
},
|
|
||||||
footerText: {
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
paddingLeft: '30px',
|
|
||||||
fontSize: '13px',
|
|
||||||
color: '#7A7A7A'
|
|
||||||
},
|
|
||||||
footerLink: {
|
|
||||||
color: '#7A7A7A',
|
|
||||||
textDecoration: 'none',
|
|
||||||
borderBottom: '1px dashed #7A7A7A'
|
|
||||||
},
|
|
||||||
footerLogo: {
|
|
||||||
padding: '0 30px',
|
|
||||||
textAlign: 'center'
|
|
||||||
}
|
|
||||||
};
|
|
27
src/components/layout/footer/styles.ts
Normal file
27
src/components/layout/footer/styles.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { green } from 'components/ui/colors';
|
||||||
|
import { CSSProperties } from 'react';
|
||||||
|
|
||||||
|
const styles: { [key: string]: CSSProperties } = {
|
||||||
|
footer: {
|
||||||
|
borderTop: `10px solid ${green.base}`,
|
||||||
|
background: '#DDD8CE',
|
||||||
|
height: '135px',
|
||||||
|
},
|
||||||
|
footerText: {
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
paddingLeft: '30px',
|
||||||
|
fontSize: '13px',
|
||||||
|
color: '#7A7A7A',
|
||||||
|
},
|
||||||
|
footerLink: {
|
||||||
|
color: '#7A7A7A',
|
||||||
|
textDecoration: 'none',
|
||||||
|
borderBottom: '1px dashed #7A7A7A',
|
||||||
|
},
|
||||||
|
footerLogo: {
|
||||||
|
padding: '0 30px',
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default styles;
|
@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { FunctionComponent, ReactElement } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { FormattedMessage as Message } from 'react-intl';
|
import { FormattedMessage as Message } from 'react-intl';
|
||||||
|
|
||||||
import { Table } from 'components/table';
|
import { Table } from 'components/table';
|
||||||
@ -7,7 +6,12 @@ import { Table } from 'components/table';
|
|||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import messages from './messages.intl.json';
|
import messages from './messages.intl.json';
|
||||||
|
|
||||||
export default function Userbar({username, title}) {
|
interface Props {
|
||||||
|
username: string;
|
||||||
|
title: ReactElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Userbar: FunctionComponent<Props> = ({ username, title }) => {
|
||||||
return (
|
return (
|
||||||
<Table style={styles.headerImage}>
|
<Table style={styles.headerImage}>
|
||||||
<tr>
|
<tr>
|
||||||
@ -21,9 +25,6 @@ export default function Userbar({username, title}) {
|
|||||||
</tr>
|
</tr>
|
||||||
</Table>
|
</Table>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
Userbar.propTypes = {
|
|
||||||
username: PropTypes.string,
|
|
||||||
title: PropTypes.node
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default Userbar;
|
@ -1,16 +0,0 @@
|
|||||||
import background from './background.jpg';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
headerImage: {
|
|
||||||
height: '200px',
|
|
||||||
backgroundImage: `url(${background})`
|
|
||||||
},
|
|
||||||
headerTextContainer: {
|
|
||||||
color: '#fff',
|
|
||||||
textAlign: 'center',
|
|
||||||
verticalAlign: 'middle'
|
|
||||||
},
|
|
||||||
welcomeUsername: {
|
|
||||||
fontSize: '20px'
|
|
||||||
},
|
|
||||||
};
|
|
19
src/components/layout/header/styles.ts
Normal file
19
src/components/layout/header/styles.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import background from './background.jpg';
|
||||||
|
import { CSSProperties } from 'react';
|
||||||
|
|
||||||
|
const styles: { [key: string]: CSSProperties } = {
|
||||||
|
headerImage: {
|
||||||
|
height: '200px',
|
||||||
|
backgroundImage: `url(${background})`,
|
||||||
|
},
|
||||||
|
headerTextContainer: {
|
||||||
|
color: '#fff',
|
||||||
|
textAlign: 'center',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
},
|
||||||
|
welcomeUsername: {
|
||||||
|
fontSize: '20px',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default styles;
|
@ -1,11 +0,0 @@
|
|||||||
import Userbar from './userbar/Userbar';
|
|
||||||
import Header from './header/Header';
|
|
||||||
import Content from './content/Content';
|
|
||||||
import Footer from './footer/Footer';
|
|
||||||
|
|
||||||
export {
|
|
||||||
Userbar,
|
|
||||||
Header,
|
|
||||||
Content,
|
|
||||||
Footer
|
|
||||||
};
|
|
4
src/components/layout/index.ts
Normal file
4
src/components/layout/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export { default as Userbar } from './userbar/Userbar';
|
||||||
|
export { default as Header } from './header/Header';
|
||||||
|
export { default as Content } from './content/Content';
|
||||||
|
export { default as Footer } from './footer/Footer';
|
@ -1,26 +1,24 @@
|
|||||||
import React from 'react';
|
import React, { FunctionComponent } from 'react';
|
||||||
|
|
||||||
import { Table } from 'components/table';
|
import { Table } from 'components/table';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import logoImage from './logo.png';
|
import logoImage from './logo.png';
|
||||||
|
|
||||||
export default function Userbar() {
|
const Userbar: FunctionComponent = () => {
|
||||||
return (
|
return (
|
||||||
<Table style={styles.userbar}>
|
<Table style={styles.userbar}>
|
||||||
<tr>
|
<tr>
|
||||||
<td style={styles.marginColumn} />
|
<td style={styles.marginColumn} />
|
||||||
<td style={styles.logoColumn}>
|
<td style={styles.logoColumn}>
|
||||||
<a href="http://ely.by" style={styles.logo}>
|
<a href="http://ely.by" style={styles.logo}>
|
||||||
{/* TODO: здесь нужно динамически сформировать название, т.к. может быть Ёly.by */}
|
<img src={logoImage} alt="Ely.by" style={styles.logoImage} />
|
||||||
<img src={logoImage} alt="Ely.by" style={{
|
|
||||||
width: '65px',
|
|
||||||
verticalAlign: 'middle'
|
|
||||||
}} />
|
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
</tr>
|
</tr>
|
||||||
</Table>
|
</Table>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default Userbar;
|
@ -1,21 +0,0 @@
|
|||||||
import {green} from 'components/ui/colors';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
userbar: {
|
|
||||||
background: green.color,
|
|
||||||
height: '50px'
|
|
||||||
},
|
|
||||||
marginColumn: {
|
|
||||||
width: '20px'
|
|
||||||
},
|
|
||||||
logoColumn: {
|
|
||||||
width: '1%',
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
background: green.dark
|
|
||||||
},
|
|
||||||
logo: {
|
|
||||||
padding: '0 13px',
|
|
||||||
display: 'block',
|
|
||||||
lineHeight: '50px'
|
|
||||||
}
|
|
||||||
};
|
|
25
src/components/layout/userbar/styles.ts
Normal file
25
src/components/layout/userbar/styles.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { green } from 'components/ui/colors';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
userbar: {
|
||||||
|
background: green.base,
|
||||||
|
height: '50px',
|
||||||
|
},
|
||||||
|
marginColumn: {
|
||||||
|
width: '20px',
|
||||||
|
},
|
||||||
|
logoColumn: {
|
||||||
|
width: '1%',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
background: green.dark,
|
||||||
|
},
|
||||||
|
logo: {
|
||||||
|
padding: '0 13px',
|
||||||
|
display: 'block',
|
||||||
|
lineHeight: '50px',
|
||||||
|
},
|
||||||
|
logoImage: {
|
||||||
|
width: '65px',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
},
|
||||||
|
};
|
@ -3,9 +3,9 @@ export default {
|
|||||||
backgroundColor: '#EBE8E1',
|
backgroundColor: '#EBE8E1',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
fontFamily: 'Roboto, Arial, sans-serif',
|
fontFamily: 'Roboto, Arial, sans-serif',
|
||||||
color: '#444'
|
color: '#444',
|
||||||
},
|
},
|
||||||
container: {
|
container: {
|
||||||
width: '600px'
|
width: '600px',
|
||||||
}
|
},
|
||||||
};
|
};
|
@ -1,16 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import styles from './styles';
|
|
||||||
|
|
||||||
export default function Table(props) {
|
|
||||||
return (
|
|
||||||
<table cellPadding="0" cellSpacing="0" style={{
|
|
||||||
...styles.table,
|
|
||||||
...props.style
|
|
||||||
}}>
|
|
||||||
<tbody>
|
|
||||||
{props.children}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
}
|
|
22
src/components/table/Table.tsx
Normal file
22
src/components/table/Table.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React, { CSSProperties, FunctionComponent } from 'react';
|
||||||
|
|
||||||
|
import styles from './styles';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
style?: CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Table: FunctionComponent<Props> = ({ children, style }) => {
|
||||||
|
return (
|
||||||
|
<table cellPadding="0" cellSpacing="0" style={{
|
||||||
|
...styles.table,
|
||||||
|
...style
|
||||||
|
}}>
|
||||||
|
<tbody>
|
||||||
|
{children}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Table;
|
@ -1 +0,0 @@
|
|||||||
export Table from './Table';
|
|
1
src/components/table/index.ts
Normal file
1
src/components/table/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Table } from './Table';
|
@ -1,8 +0,0 @@
|
|||||||
export default {
|
|
||||||
table: {
|
|
||||||
borderCollapse: 'collapse',
|
|
||||||
msoTableLspace: '0pt',
|
|
||||||
msoTableRspace: '0pt',
|
|
||||||
width: '100%'
|
|
||||||
}
|
|
||||||
};
|
|
13
src/components/table/styles.ts
Normal file
13
src/components/table/styles.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { CSSProperties } from 'react';
|
||||||
|
|
||||||
|
const styles: { [key: string]: CSSProperties } = {
|
||||||
|
table: {
|
||||||
|
borderCollapse: 'collapse',
|
||||||
|
// @ts-ignore
|
||||||
|
msoTableLspace: '0pt',
|
||||||
|
msoTableRspace: '0pt',
|
||||||
|
width: '100%'
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default styles;
|
@ -1,34 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import styles from './styles';
|
|
||||||
|
|
||||||
import { colors, green } from 'components/ui/colors';
|
|
||||||
|
|
||||||
export default class Button extends Component {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
label: PropTypes.node.isRequired,
|
|
||||||
color: PropTypes.oneOf(Object.values(colors))
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
color: green
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {props} = this;
|
|
||||||
const {color, label} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{
|
|
||||||
...styles.button,
|
|
||||||
...styles[color],
|
|
||||||
...props.style
|
|
||||||
}}>
|
|
||||||
{label}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
26
src/components/ui/button/Button.tsx
Normal file
26
src/components/ui/button/Button.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import React, { CSSProperties, FunctionComponent, ReactElement } from 'react';
|
||||||
|
|
||||||
|
import { Colors, green } from 'components/ui/colors';
|
||||||
|
|
||||||
|
import styles from './styles';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
label: ReactElement;
|
||||||
|
style?: CSSProperties;
|
||||||
|
color?: Colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button: FunctionComponent<Props> = ({ label, style, color = 'green' }) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
...styles.button,
|
||||||
|
...styles[color],
|
||||||
|
...style,
|
||||||
|
}}>
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Button;
|
@ -1 +0,0 @@
|
|||||||
export Button from './Button';
|
|
1
src/components/ui/button/index.ts
Normal file
1
src/components/ui/button/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './Button';
|
@ -1,25 +0,0 @@
|
|||||||
import { colors } from 'components/ui/colors';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Color} color
|
|
||||||
* @return {{backgroundColor: *}}
|
|
||||||
*/
|
|
||||||
function generateColor(color) {
|
|
||||||
return {
|
|
||||||
backgroundColor: color.color
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = {
|
|
||||||
button: {
|
|
||||||
padding: '0 13px',
|
|
||||||
lineHeight: '50px',
|
|
||||||
display: 'inline-block'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.values(colors).forEach((color) => {
|
|
||||||
styles[color] = generateColor(color);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default styles;
|
|
27
src/components/ui/button/styles.ts
Normal file
27
src/components/ui/button/styles.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Color, Colors, colors } from 'components/ui/colors';
|
||||||
|
import { CSSProperties } from 'react';
|
||||||
|
|
||||||
|
function generateColor({ base }: Color): CSSProperties {
|
||||||
|
return {
|
||||||
|
backgroundColor: base,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type Styles = {
|
||||||
|
[key: string]: CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles: Styles = {
|
||||||
|
button: {
|
||||||
|
padding: '0 13px',
|
||||||
|
lineHeight: '50px',
|
||||||
|
display: 'inline-block',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(colors).forEach((colorName) => {
|
||||||
|
// TS has error when trying to recognize keys types, so we cast it manually
|
||||||
|
styles[colorName] = generateColor(colors[colorName as any as Colors]);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default styles;
|
@ -1,48 +0,0 @@
|
|||||||
export class Color {
|
|
||||||
|
|
||||||
constructor(name, base, light, dark) {
|
|
||||||
this._name = name;
|
|
||||||
this._base = base;
|
|
||||||
this._light = light;
|
|
||||||
this._dark = dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return this._name;
|
|
||||||
}
|
|
||||||
|
|
||||||
get color() {
|
|
||||||
return this._base;
|
|
||||||
}
|
|
||||||
|
|
||||||
get light() {
|
|
||||||
return this._light;
|
|
||||||
}
|
|
||||||
|
|
||||||
get dark() {
|
|
||||||
return this._dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const green = new Color('green', '#207e5c', '#379070', '#1a6449');
|
|
||||||
export const blue = new Color('blue', '#5b9aa9', '#71a6b2', '#568297');
|
|
||||||
export const darkBlue = new Color('darkBlue', '#28555b', '#3e6164', '#233d49');
|
|
||||||
export const violet = new Color('violet', '#6b5b8c', '#816795', '#66437a');
|
|
||||||
export const lightViolet = new Color('lightViolet', '#8b5d79', '#a16982', '#864567');
|
|
||||||
export const orange = new Color('orange', '#dd8650', '#f39259', '#d86e3e');
|
|
||||||
export const red = new Color('red', '#e66c69', '#e15457', '#fc7872');
|
|
||||||
|
|
||||||
export const colors = {
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
darkBlue,
|
|
||||||
violet,
|
|
||||||
lightViolet,
|
|
||||||
orange,
|
|
||||||
red
|
|
||||||
};
|
|
63
src/components/ui/colors.ts
Normal file
63
src/components/ui/colors.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
export type Colors =
|
||||||
|
| 'green'
|
||||||
|
| 'blue'
|
||||||
|
| 'darkBlue'
|
||||||
|
| 'violet'
|
||||||
|
| 'lightViolet'
|
||||||
|
| 'orange'
|
||||||
|
| 'red'
|
||||||
|
;
|
||||||
|
|
||||||
|
export interface Color {
|
||||||
|
base: string;
|
||||||
|
light: string;
|
||||||
|
dark: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const green: Color = {
|
||||||
|
base: '#207e5c',
|
||||||
|
light: '#379070',
|
||||||
|
dark: '#1a6449',
|
||||||
|
};
|
||||||
|
export const blue: Color = {
|
||||||
|
base: '#5b9aa9',
|
||||||
|
light: '#71a6b2',
|
||||||
|
dark: '#568297',
|
||||||
|
};
|
||||||
|
export const darkBlue: Color = {
|
||||||
|
base: '#28555b',
|
||||||
|
light: '#3e6164',
|
||||||
|
dark: '#233d49',
|
||||||
|
};
|
||||||
|
export const violet: Color = {
|
||||||
|
base: '#6b5b8c',
|
||||||
|
light: '#816795',
|
||||||
|
dark: '#66437a',
|
||||||
|
};
|
||||||
|
export const lightViolet: Color = {
|
||||||
|
base: '#8b5d79',
|
||||||
|
light: '#a16982',
|
||||||
|
dark: '#864567',
|
||||||
|
};
|
||||||
|
export const orange: Color = {
|
||||||
|
base: '#dd8650',
|
||||||
|
light: '#f39259',
|
||||||
|
dark: '#d86e3e',
|
||||||
|
};
|
||||||
|
export const red: Color = {
|
||||||
|
base: '#e66c69',
|
||||||
|
light: '#e15457',
|
||||||
|
dark: '#fc7872',
|
||||||
|
};
|
||||||
|
|
||||||
|
type ColorsMap = { [key in Colors]: Color };
|
||||||
|
|
||||||
|
export const colors: ColorsMap = {
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
darkBlue,
|
||||||
|
violet,
|
||||||
|
lightViolet,
|
||||||
|
orange,
|
||||||
|
red,
|
||||||
|
};
|
@ -1,32 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { colors, green } from 'components/ui/colors';
|
|
||||||
|
|
||||||
import styles from './styles';
|
|
||||||
|
|
||||||
export default class Input extends Component {
|
|
||||||
static propTypes = {
|
|
||||||
value: PropTypes.string.isRequired,
|
|
||||||
color: PropTypes.oneOf(Object.values(colors)),
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
color: green,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {props} = this;
|
|
||||||
const {value, color, style} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{
|
|
||||||
...styles.input,
|
|
||||||
...styles[color],
|
|
||||||
...style
|
|
||||||
}}>
|
|
||||||
{value}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
25
src/components/ui/input/Input.tsx
Normal file
25
src/components/ui/input/Input.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React, { CSSProperties, FunctionComponent } from 'react';
|
||||||
|
|
||||||
|
import { Colors } from 'components/ui/colors';
|
||||||
|
|
||||||
|
import styles from './styles';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
value: string;
|
||||||
|
color?: Colors;
|
||||||
|
style?: CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Input: FunctionComponent<Props> = ({ value, style, color = 'green' }) => {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
...styles.input,
|
||||||
|
...styles[color],
|
||||||
|
...style
|
||||||
|
}}>
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Input;
|
@ -1 +0,0 @@
|
|||||||
export Input from './Input';
|
|
1
src/components/ui/input/index.ts
Normal file
1
src/components/ui/input/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './Input';
|
@ -1,30 +0,0 @@
|
|||||||
import { colors } from 'components/ui/colors';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Color} color
|
|
||||||
* @return {{backgroundColor: *}}
|
|
||||||
*/
|
|
||||||
function generateColor(color) {
|
|
||||||
return {
|
|
||||||
borderColor: color.color,
|
|
||||||
color: color.dark
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = {
|
|
||||||
input: {
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
padding: '0 30px',
|
|
||||||
lineHeight: '50px',
|
|
||||||
fontSize: '18px',
|
|
||||||
display: 'inline-block',
|
|
||||||
border: '3px solid transparent',
|
|
||||||
color: '#444'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.values(colors).forEach((color) => {
|
|
||||||
styles[color] = generateColor(color);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default styles;
|
|
28
src/components/ui/input/styles.ts
Normal file
28
src/components/ui/input/styles.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { CSSProperties } from 'react';
|
||||||
|
import { Color, Colors, colors } from 'components/ui/colors';
|
||||||
|
|
||||||
|
const styles: { [key: string]: CSSProperties } = {
|
||||||
|
input: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
padding: '0 30px',
|
||||||
|
lineHeight: '50px',
|
||||||
|
fontSize: '18px',
|
||||||
|
display: 'inline-block',
|
||||||
|
border: '3px solid transparent',
|
||||||
|
color: '#444',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function generateColor({ base, dark }: Color): CSSProperties {
|
||||||
|
return {
|
||||||
|
borderColor: base,
|
||||||
|
color: dark,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(colors).forEach((color) => {
|
||||||
|
// TS has error when trying to recognize keys types, so we cast it manually
|
||||||
|
styles[color] = generateColor(colors[color as any as Colors]);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default styles;
|
@ -1,94 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
|
|
||||||
import App from 'App';
|
|
||||||
|
|
||||||
import List from './List';
|
|
||||||
|
|
||||||
import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from 'params';
|
|
||||||
|
|
||||||
const AVAILABLE_EMAILS = require.context('emails', true, /index\.js$/).keys().map((path) => path.split('/')[1]);
|
|
||||||
|
|
||||||
export default class DevApp extends Component {
|
|
||||||
state = {
|
|
||||||
locale: DEFAULT_LANGUAGE,
|
|
||||||
type: AVAILABLE_EMAILS[0],
|
|
||||||
fixture: 'default',
|
|
||||||
isMinimized: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
try {
|
|
||||||
const lastState = JSON.parse(localStorage.getItem('emailRendererState'));
|
|
||||||
lastState && this.setState(lastState);
|
|
||||||
} catch (err) {/* no state was saved */}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUpdate(nextProps, nextState) {
|
|
||||||
localStorage.setItem('emailRendererState', JSON.stringify(nextState));
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {locale, type, isMinimized} = this.state;
|
|
||||||
let {fixture} = this.state;
|
|
||||||
|
|
||||||
let fixturesAvailable = {};
|
|
||||||
try {
|
|
||||||
fixturesAvailable = require(`emails/${type}/fixtures`).default;
|
|
||||||
} catch (err) {/* no fixtures available */}
|
|
||||||
|
|
||||||
if (!fixturesAvailable[fixture]) {
|
|
||||||
fixture = 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
locale,
|
|
||||||
...(fixturesAvailable[fixture] || {})
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div style={isMinimized ? {
|
|
||||||
opacity: 0.4,
|
|
||||||
position: 'fixed'
|
|
||||||
} : {}}>
|
|
||||||
[<a href="#" style={{textDecoration: 'none', padding: '6px'}} onClick={this.onMinimizeToggle}>
|
|
||||||
{isMinimized ? '+' : '-'}
|
|
||||||
</a>]
|
|
||||||
|
|
||||||
<div style={isMinimized ? {display: 'none'} : {}}>
|
|
||||||
<List label="Lang"
|
|
||||||
items={SUPPORTED_LANGUAGES}
|
|
||||||
active={locale}
|
|
||||||
onChange={this.onLocaleChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<List label="Email"
|
|
||||||
items={AVAILABLE_EMAILS}
|
|
||||||
active={type}
|
|
||||||
onChange={this.onTypeChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<List label="Fixtures"
|
|
||||||
items={Object.keys(fixturesAvailable)}
|
|
||||||
active={fixture}
|
|
||||||
onChange={this.onFixtureChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<App type={type} payload={payload} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
onLocaleChange = (locale) => this.setState({locale});
|
|
||||||
onTypeChange = (type) => this.setState({type});
|
|
||||||
onFixtureChange = (fixture) => this.setState({fixture});
|
|
||||||
onMinimizeToggle = (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
isMinimized: !this.state.isMinimized
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
109
src/devTools/DevApp.tsx
Normal file
109
src/devTools/DevApp.tsx
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import React, { BaseSyntheticEvent, FunctionComponent, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from 'params';
|
||||||
|
|
||||||
|
import App from 'App';
|
||||||
|
|
||||||
|
import List from './List';
|
||||||
|
|
||||||
|
const AVAILABLE_TEMPLATES = require.context('emails', true, /index\.[jt]s$/).keys().map((path) => path.split('/')[1]);
|
||||||
|
|
||||||
|
interface LocalStorageState {
|
||||||
|
locale: string;
|
||||||
|
template: string;
|
||||||
|
fixture: string;
|
||||||
|
isMinimized: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DevApp: FunctionComponent = () => {
|
||||||
|
const [ locale, setLocale ] = useState(DEFAULT_LANGUAGE);
|
||||||
|
const [ template, setTemplate ] = useState(AVAILABLE_TEMPLATES[0]);
|
||||||
|
const [ fixture, setFixture ] = useState('default');
|
||||||
|
const [ isMinimized, setIsMinimized ] = useState(false);
|
||||||
|
|
||||||
|
// Load stored state from local storage on the first run
|
||||||
|
useEffect(() => {
|
||||||
|
let state: LocalStorageState;
|
||||||
|
try {
|
||||||
|
state = JSON.parse(localStorage.getItem('emailRendererState') || '');
|
||||||
|
if (!state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLocale(state.locale);
|
||||||
|
setTemplate(state.template);
|
||||||
|
setFixture(state.fixture);
|
||||||
|
setIsMinimized(state.isMinimized);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Store current state to the local storage when any param is changed
|
||||||
|
useEffect(() => {
|
||||||
|
const state: LocalStorageState = { locale, template, fixture, isMinimized };
|
||||||
|
localStorage.setItem('emailRendererState', JSON.stringify(state));
|
||||||
|
}, [locale, template, fixture, isMinimized]);
|
||||||
|
|
||||||
|
const availableFixtures = useMemo(() => {
|
||||||
|
try {
|
||||||
|
return require(`emails/${template}/fixtures`).default;
|
||||||
|
} catch (err) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}, [template]);
|
||||||
|
|
||||||
|
const payload = useMemo(() => ({
|
||||||
|
locale,
|
||||||
|
...(availableFixtures[fixture] || availableFixtures['default'] || {}),
|
||||||
|
}), [locale, availableFixtures, fixture]);
|
||||||
|
|
||||||
|
const onMinimizeClick = useCallback((event: BaseSyntheticEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setIsMinimized(!isMinimized);
|
||||||
|
}, [isMinimized, setIsMinimized]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div style={isMinimized ? {
|
||||||
|
opacity: 0.4,
|
||||||
|
position: 'fixed',
|
||||||
|
} : {}}>
|
||||||
|
[<a
|
||||||
|
href="#"
|
||||||
|
style={{
|
||||||
|
textDecoration: 'none',
|
||||||
|
padding: '6px',
|
||||||
|
}}
|
||||||
|
onClick={onMinimizeClick}
|
||||||
|
>
|
||||||
|
{isMinimized ? '+' : '-'}
|
||||||
|
</a>]
|
||||||
|
|
||||||
|
<div style={isMinimized ? {display: 'none'} : {}}>
|
||||||
|
<List label="Lang"
|
||||||
|
items={SUPPORTED_LANGUAGES}
|
||||||
|
active={locale}
|
||||||
|
onChange={setLocale}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<List label="Email"
|
||||||
|
items={AVAILABLE_TEMPLATES}
|
||||||
|
active={template}
|
||||||
|
onChange={setTemplate}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<List label="Fixtures"
|
||||||
|
items={Object.keys(availableFixtures)}
|
||||||
|
active={fixture}
|
||||||
|
onChange={setFixture}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<App type={template} payload={payload} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DevApp;
|
@ -1,31 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
export default function List({label, items, active, onChange}) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{label}:
|
|
||||||
{items.map((key) =>
|
|
||||||
<a href="#"
|
|
||||||
key={key}
|
|
||||||
onClick={(event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
onChange && onChange(key);
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
padding: '0 5px',
|
|
||||||
color: active === key ? 'red' : ''
|
|
||||||
}}
|
|
||||||
>{key}</a>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List.propTypes = {
|
|
||||||
label: PropTypes.string.isRequired,
|
|
||||||
items: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
||||||
active: PropTypes.string,
|
|
||||||
onChange: PropTypes.func
|
|
||||||
};
|
|
34
src/devTools/List.tsx
Normal file
34
src/devTools/List.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import React, { FunctionComponent } from 'react';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
label: string;
|
||||||
|
items: ReadonlyArray<string>;
|
||||||
|
active: string;
|
||||||
|
onChange: (item: string) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const List: FunctionComponent<Props> = ({ label, items, active, onChange = () => {} }) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{label}:
|
||||||
|
{items.map((item) =>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
key={item}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
onChange(item);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
padding: '0 5px',
|
||||||
|
color: active === item ? 'red' : '',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item}
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default List;
|
@ -1,3 +0,0 @@
|
|||||||
import DevApp from './DevApp';
|
|
||||||
|
|
||||||
export default DevApp;
|
|
1
src/devTools/index.ts
Normal file
1
src/devTools/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './DevApp';
|
@ -1,16 +1,20 @@
|
|||||||
import React from 'react';
|
import React, { FunctionComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { FormattedMessage as Message, FormattedHTMLMessage as HTMLMessage } from 'react-intl';
|
import { FormattedMessage as Message, FormattedHTMLMessage as HTMLMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Userbar, Header, Content, Footer } from 'components/layout';
|
import { Userbar, Header, Content, Footer } from 'components/layout';
|
||||||
import { Table } from 'components/table';
|
import { Table } from 'components/table';
|
||||||
import { Code } from 'components/blocks';
|
import { Code } from 'components/blocks';
|
||||||
import { lightViolet } from 'components/ui/colors';
|
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import messages from './messages.intl.json';
|
import messages from './messages.intl.json';
|
||||||
|
|
||||||
export default function ForgotPassword({username, link, code}) {
|
interface Props {
|
||||||
|
username: string;
|
||||||
|
link: string;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ForgotPassword: FunctionComponent<Props> = ({ username, link, code }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Userbar />
|
<Userbar />
|
||||||
@ -30,7 +34,7 @@ export default function ForgotPassword({username, link, code}) {
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<Code code={code} link={link} color={lightViolet} label={
|
<Code code={code} link={link} color="lightViolet" label={
|
||||||
<HTMLMessage {...messages.continue_image} />
|
<HTMLMessage {...messages.continue_image} />
|
||||||
} />
|
} />
|
||||||
</td>
|
</td>
|
||||||
@ -41,10 +45,6 @@ export default function ForgotPassword({username, link, code}) {
|
|||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
ForgotPassword.propTypes = {
|
|
||||||
username: PropTypes.string,
|
|
||||||
link: PropTypes.string,
|
|
||||||
code: PropTypes.string
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default ForgotPassword;
|
@ -2,6 +2,6 @@ export default {
|
|||||||
default: {
|
default: {
|
||||||
username: 'ErickSkrauch',
|
username: 'ErickSkrauch',
|
||||||
code: 'I7SP06BUTLLM8MA03O',
|
code: 'I7SP06BUTLLM8MA03O',
|
||||||
link: 'https://account.ely.by/activation/I7SP06BUTLLM8MA03O'
|
link: 'https://account.ely.by/activation/I7SP06BUTLLM8MA03O',
|
||||||
},
|
},
|
||||||
};
|
};
|
@ -1,3 +0,0 @@
|
|||||||
import ForgotPassword from './ForgotPassword';
|
|
||||||
|
|
||||||
export default ForgotPassword;
|
|
1
src/emails/forgotPassword/index.ts
Normal file
1
src/emails/forgotPassword/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './ForgotPassword';
|
@ -1,10 +1,9 @@
|
|||||||
export default {
|
export default {
|
||||||
contentCenterCell: {
|
contentCenterCell: {
|
||||||
textAlign: 'center'
|
textAlign: 'center',
|
||||||
},
|
},
|
||||||
|
|
||||||
paragraph: {
|
paragraph: {
|
||||||
fontSize: '16px',
|
fontSize: '16px',
|
||||||
lineHeight: '125%'
|
lineHeight: '125%',
|
||||||
},
|
},
|
||||||
};
|
};
|
@ -1,10 +1,8 @@
|
|||||||
import React from 'react';
|
import React, { FunctionComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { FormattedMessage as Message, FormattedHTMLMessage as HTMLMessage } from 'react-intl';
|
import { FormattedMessage as Message, FormattedHTMLMessage as HTMLMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Userbar, Header, Content, Footer } from 'components/layout';
|
import { Userbar, Header, Content, Footer } from 'components/layout';
|
||||||
import { Table } from 'components/table';
|
import { Table } from 'components/table';
|
||||||
import { blue } from 'components/ui/colors';
|
|
||||||
import { Code } from 'components/blocks';
|
import { Code } from 'components/blocks';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
@ -14,7 +12,13 @@ import violetManImage from './images/violetMan.png';
|
|||||||
import orangeManImage from './images/orangeMan.png';
|
import orangeManImage from './images/orangeMan.png';
|
||||||
import darkBlueManImage from './images/darkBlueMan.png';
|
import darkBlueManImage from './images/darkBlueMan.png';
|
||||||
|
|
||||||
export default function Register({username, link, code}) {
|
interface Props {
|
||||||
|
username: string;
|
||||||
|
link: string;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Register: FunctionComponent<Props> = ({ username, link, code }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Userbar />
|
<Userbar />
|
||||||
@ -34,7 +38,7 @@ export default function Register({username, link, code}) {
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<Code code={code} link={link} color={blue} label={
|
<Code code={code} link={link} color="blue" label={
|
||||||
<HTMLMessage {...messages.confirm_email_image} />
|
<HTMLMessage {...messages.confirm_email_image} />
|
||||||
} />
|
} />
|
||||||
</td>
|
</td>
|
||||||
@ -42,7 +46,7 @@ export default function Register({username, link, code}) {
|
|||||||
<tr>
|
<tr>
|
||||||
<td style={{
|
<td style={{
|
||||||
...styles.contentCenterCell,
|
...styles.contentCenterCell,
|
||||||
...styles.whatsNextText
|
...styles.whatsNextText,
|
||||||
}}>
|
}}>
|
||||||
<HTMLMessage {...messages.whats_next_image} />
|
<HTMLMessage {...messages.whats_next_image} />
|
||||||
</td>
|
</td>
|
||||||
@ -52,10 +56,7 @@ export default function Register({username, link, code}) {
|
|||||||
<Table>
|
<Table>
|
||||||
<tr>
|
<tr>
|
||||||
<td style={styles.todoItemIcon}>
|
<td style={styles.todoItemIcon}>
|
||||||
<img src={violetManImage} style={{
|
<img src={violetManImage} style={styles.todoItemIconImage} />
|
||||||
width: '25px',
|
|
||||||
verticalAlign: 'middle'
|
|
||||||
}} />
|
|
||||||
</td>
|
</td>
|
||||||
<td style={styles.todoItemContent}>
|
<td style={styles.todoItemContent}>
|
||||||
<HTMLMessage {...messages.choose_you_skin_image} />
|
<HTMLMessage {...messages.choose_you_skin_image} />
|
||||||
@ -75,10 +76,7 @@ export default function Register({username, link, code}) {
|
|||||||
<Table>
|
<Table>
|
||||||
<tr>
|
<tr>
|
||||||
<td style={styles.todoItemIcon}>
|
<td style={styles.todoItemIcon}>
|
||||||
<img src={orangeManImage} style={{
|
<img src={orangeManImage} style={styles.todoItemIconImage} />
|
||||||
width: '25px',
|
|
||||||
verticalAlign: 'middle'
|
|
||||||
}} />
|
|
||||||
</td>
|
</td>
|
||||||
<td style={styles.todoItemContent}>
|
<td style={styles.todoItemContent}>
|
||||||
<HTMLMessage {...messages.install_our_patch_image} />
|
<HTMLMessage {...messages.install_our_patch_image} />
|
||||||
@ -98,10 +96,7 @@ export default function Register({username, link, code}) {
|
|||||||
<Table>
|
<Table>
|
||||||
<tr>
|
<tr>
|
||||||
<td style={styles.todoItemIcon}>
|
<td style={styles.todoItemIcon}>
|
||||||
<img src={darkBlueManImage} style={{
|
<img src={darkBlueManImage} style={styles.todoItemIconImage} />
|
||||||
width: '25px',
|
|
||||||
verticalAlign: 'middle'
|
|
||||||
}} />
|
|
||||||
</td>
|
</td>
|
||||||
<td style={styles.todoItemContent}>
|
<td style={styles.todoItemContent}>
|
||||||
<HTMLMessage {...messages.useTLLauncher} />
|
<HTMLMessage {...messages.useTLLauncher} />
|
||||||
@ -122,10 +117,6 @@ export default function Register({username, link, code}) {
|
|||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
Register.propTypes = {
|
|
||||||
username: PropTypes.string,
|
|
||||||
link: PropTypes.string,
|
|
||||||
code: PropTypes.string,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default Register;
|
@ -2,12 +2,11 @@ export default {
|
|||||||
default: {
|
default: {
|
||||||
username: 'ErickSkrauch',
|
username: 'ErickSkrauch',
|
||||||
code: 'I7SP06BUTLLM8MA03O',
|
code: 'I7SP06BUTLLM8MA03O',
|
||||||
link: 'https://account.ely.by/activation/I7SP06BUTLLM8MA03O'
|
link: 'https://account.ely.by/activation/I7SP06BUTLLM8MA03O',
|
||||||
},
|
},
|
||||||
|
|
||||||
SleepWalker: {
|
SleepWalker: {
|
||||||
username: 'SleepWalker',
|
username: 'SleepWalker',
|
||||||
code: 'TLLM8MA03OI7SP06BU',
|
code: 'TLLM8MA03OI7SP06BU',
|
||||||
link: 'https://account.ely.by/activation/TLLM8MA03OI7SP06BU'
|
link: 'https://account.ely.by/activation/TLLM8MA03OI7SP06BU',
|
||||||
}
|
},
|
||||||
};
|
};
|
@ -1,3 +0,0 @@
|
|||||||
import Register from './Register';
|
|
||||||
|
|
||||||
export default Register;
|
|
1
src/emails/register/index.ts
Normal file
1
src/emails/register/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './Register';
|
@ -1,28 +0,0 @@
|
|||||||
export default {
|
|
||||||
contentCenterCell: {
|
|
||||||
textAlign: 'center'
|
|
||||||
},
|
|
||||||
|
|
||||||
paragraph: {
|
|
||||||
fontSize: '16px',
|
|
||||||
lineHeight: '125%'
|
|
||||||
},
|
|
||||||
|
|
||||||
whatsNextText: {
|
|
||||||
paddingTop: '30px'
|
|
||||||
},
|
|
||||||
|
|
||||||
todoItem: {
|
|
||||||
paddingTop: '30px'
|
|
||||||
},
|
|
||||||
todoItemIcon: {
|
|
||||||
width: '46px',
|
|
||||||
verticalAlign: 'top'
|
|
||||||
},
|
|
||||||
todoItemContent: {
|
|
||||||
verticalAlign: 'top'
|
|
||||||
},
|
|
||||||
todoItemText: {
|
|
||||||
paddingTop: '3px'
|
|
||||||
}
|
|
||||||
};
|
|
33
src/emails/register/styles.ts
Normal file
33
src/emails/register/styles.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { CSSProperties } from 'react';
|
||||||
|
|
||||||
|
const styles: { [key: string]: CSSProperties } = {
|
||||||
|
contentCenterCell: {
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
paragraph: {
|
||||||
|
fontSize: '16px',
|
||||||
|
lineHeight: '125%',
|
||||||
|
},
|
||||||
|
whatsNextText: {
|
||||||
|
paddingTop: '30px',
|
||||||
|
},
|
||||||
|
todoItem: {
|
||||||
|
paddingTop: '30px',
|
||||||
|
},
|
||||||
|
todoItemIcon: {
|
||||||
|
width: '46px',
|
||||||
|
verticalAlign: 'top',
|
||||||
|
},
|
||||||
|
todoItemIconImage: {
|
||||||
|
width: '25px',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
},
|
||||||
|
todoItemContent: {
|
||||||
|
verticalAlign: 'top',
|
||||||
|
},
|
||||||
|
todoItemText: {
|
||||||
|
paddingTop: '3px',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default styles;
|
12
src/i18n/module.d.ts
vendored
Normal file
12
src/i18n/module.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
declare module 'i18n/index.json' {
|
||||||
|
const content: {
|
||||||
|
[key: string]: {
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
englishName: string;
|
||||||
|
progress: number;
|
||||||
|
isReleased: boolean;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default content;
|
||||||
|
}
|
@ -12,7 +12,7 @@ if (process.env.NODE_ENV !== 'production') {
|
|||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<DevApp />,
|
<DevApp />,
|
||||||
document.getElementById('app')
|
document.getElementById('app'),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const ReactDOMServer = require('react-dom/server');
|
const ReactDOMServer = require('react-dom/server');
|
||||||
@ -20,7 +20,14 @@ if (process.env.NODE_ENV !== 'production') {
|
|||||||
const App = require('App').default;
|
const App = require('App').default;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
default(props) {
|
default(props: {
|
||||||
|
type: string;
|
||||||
|
payload: {
|
||||||
|
locale: string;
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
assetsHost?: string;
|
||||||
|
}) {
|
||||||
if (props.assetsHost) {
|
if (props.assetsHost) {
|
||||||
// noinspection JSUnresolvedVariable
|
// noinspection JSUnresolvedVariable
|
||||||
__webpack_public_path__ = props.assetsHost.replace(/\/*$/, '/'); // eslint-disable-line
|
__webpack_public_path__ = props.assetsHost.replace(/\/*$/, '/'); // eslint-disable-line
|
9
src/typings/file-loader.d.ts
vendored
Normal file
9
src/typings/file-loader.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
declare module "*.png" {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.jpg" {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
9
src/typings/json-loader.d.ts
vendored
Normal file
9
src/typings/json-loader.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
declare module '*.intl.json' {
|
||||||
|
const content: {
|
||||||
|
[key: string]: {
|
||||||
|
id: string;
|
||||||
|
defaultMessage: string;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default content;
|
||||||
|
}
|
24
tsconfig.json
Normal file
24
tsconfig.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"lib": [
|
||||||
|
"es7",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable"
|
||||||
|
],
|
||||||
|
"jsx": "preserve",
|
||||||
|
"declaration": false,
|
||||||
|
"skipLibCheck": false,
|
||||||
|
"noEmit": true,
|
||||||
|
"strict": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"baseUrl": "src",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src"
|
||||||
|
]
|
||||||
|
}
|
@ -4,6 +4,7 @@ const path = require('path');
|
|||||||
|
|
||||||
const { ContextReplacementPlugin } = require('webpack');
|
const { ContextReplacementPlugin } = require('webpack');
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
||||||
|
|
||||||
const SUPPORTED_LANGUAGES = Object.keys(require('./src/i18n/index.json'));
|
const SUPPORTED_LANGUAGES = Object.keys(require('./src/i18n/index.json'));
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ module.exports = (env, { mode = 'development' }) => {
|
|||||||
path.join(__dirname, 'src'),
|
path.join(__dirname, 'src'),
|
||||||
path.join(__dirname, 'node_modules'),
|
path.join(__dirname, 'node_modules'),
|
||||||
],
|
],
|
||||||
extensions: ['.js', '.jsx'],
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
},
|
},
|
||||||
|
|
||||||
resolveLoader: {
|
resolveLoader: {
|
||||||
@ -49,49 +50,47 @@ module.exports = (env, { mode = 'development' }) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
new ContextReplacementPlugin(
|
new ContextReplacementPlugin(/i18n/, new RegExp(`/(${SUPPORTED_LANGUAGES.join('|')})\\.json`)),
|
||||||
/i18n/, new RegExp(`/(${SUPPORTED_LANGUAGES.join('|')})\\.json`)
|
new ContextReplacementPlugin(/locale-data/, new RegExp(`/(${SUPPORTED_LANGUAGES.join('|')})\\.js`)),
|
||||||
),
|
|
||||||
new ContextReplacementPlugin(
|
|
||||||
/locale-data/, new RegExp(`/(${SUPPORTED_LANGUAGES.join('|')})\\.js`)
|
|
||||||
),
|
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: 'src/index.ejs',
|
template: 'src/index.ejs',
|
||||||
favicon: 'src/favicon.ico',
|
favicon: 'src/favicon.ico',
|
||||||
filename: 'index.html',
|
filename: 'index.html',
|
||||||
inject: false,
|
inject: false,
|
||||||
}),
|
}),
|
||||||
|
new BundleAnalyzerPlugin({
|
||||||
|
openAnalyzer: false,
|
||||||
|
generateStatsFile: true,
|
||||||
|
analyzerMode: isProduction ? 'static': 'server',
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
|
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.jsx?$/,
|
test: /\.[jt]sx?$/,
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
presets: [
|
presets: [
|
||||||
[
|
['@babel/preset-env', {
|
||||||
'@babel/preset-env',
|
targets: isProduction ? {
|
||||||
{
|
node: '8',
|
||||||
targets: isProduction ? {
|
} : {
|
||||||
node: '8',
|
browsers: [
|
||||||
} : {
|
'last 1 chrome version',
|
||||||
browsers: [
|
'last 1 firefox version',
|
||||||
'last 1 chrome version',
|
],
|
||||||
'last 1 firefox version',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
],
|
}],
|
||||||
[
|
['@babel/preset-react', {
|
||||||
'@babel/preset-react',
|
development: !isProduction,
|
||||||
{
|
}],
|
||||||
development: !isProduction,
|
['@babel/preset-typescript', {
|
||||||
},
|
jsx: true,
|
||||||
],
|
}],
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
'@babel/plugin-proposal-class-properties',
|
'@babel/plugin-proposal-class-properties',
|
||||||
|
Loading…
Reference in New Issue
Block a user