diff --git a/package-lock.json b/package-lock.json index 937a670..2aac94d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1216,6 +1216,12 @@ "loader-utils": "^1.1.0" } }, + "@types/classnames": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.8.tgz", + "integrity": "sha512-3UrLzPnz8u+MMXuJTF++389IfLSQUbl5F3ry9WCxva0BKG5H/oo5NuPRXk+HrpPU1+5pVHSWhnVWRzIaFQ7QuQ==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -1251,12 +1257,28 @@ "integrity": "sha512-/OMMBnjVtDuwX1tg2pkYVSqRIDSmNTnvVvmvP/2xiMAAWf4a5+JozrApCrO4WCAILmXVxfNoQ3E+0HJbNpFVGg==", "dev": true }, + "@types/prop-types": { + "version": "15.7.1", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.1.tgz", + "integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==", + "dev": true + }, "@types/q": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", "dev": true }, + "@types/react": { + "version": "16.8.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.18.tgz", + "integrity": "sha512-lUXdKzRqWR4FebR5tGHkLCqnvQJS4fdXKCBrNGGbglqZg2gpU+J82pMONevQODUotATs9fc9k66bx3/St8vReg==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, "@types/tapable": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.2.tgz", @@ -4085,6 +4107,11 @@ } } }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, "clean-css": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", @@ -4890,6 +4917,12 @@ "cssom": "0.3.x" } }, + "csstype": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.4.tgz", + "integrity": "sha512-lAJUJP3M6HxFXbqtGRc0iZrdyeN+WzOWeY0q/VnFzI+kqVrYIzC7bWlKqCW7oCIdzoPkvfp82EVvrTlQ8zsWQg==", + "dev": true + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -7306,8 +7339,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -7350,8 +7382,7 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", @@ -7362,8 +7393,7 @@ "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -7480,8 +7510,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -7493,7 +7522,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7508,7 +7536,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -7516,14 +7543,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -7542,7 +7567,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -7623,8 +7647,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -7636,7 +7659,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -7722,8 +7744,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -7759,7 +7780,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -7779,7 +7799,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -7823,14 +7842,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, diff --git a/package.json b/package.json index 9ede333..0ffd460 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,14 @@ ], "dependencies": { "react": "16.8.6", - "react-dom": "16.8.6" + "react-dom": "16.8.6", + "classnames": "latest" }, "devDependencies": { "@hellroot/eslint-config": "1.8.0", "@hellroot/stylelint-config": "1.1.0", + "@types/classnames": "2.2.8", + "@types/react": "16.8.18", "eslint": "5.12.0", "npm-run-all": "4.1.5", "prettier": "1.17.0", diff --git a/public/index.html b/public/index.html index 9a8ef8f..5d57a79 100644 --- a/public/index.html +++ b/public/index.html @@ -8,7 +8,7 @@ content="width=device-width, initial-scale=1, shrink-to-fit=no" /> - React App + Яндекс.Почта diff --git a/src/app/app.css b/src/app/app.css deleted file mode 100644 index 1c4d511..0000000 --- a/src/app/app.css +++ /dev/null @@ -1,27 +0,0 @@ -.app { - text-align: center; -} - -.app-header { - display: flex; - min-height: 100vh; - flex-direction: column; - align-items: center; - justify-content: center; - background-color: #282c34; - color: #fff; - font-size: calc(10px + 2vmin); -} - -.app-link { - color: #61dafb; -} - -@keyframes app-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/app/app.jsx b/src/app/app.jsx deleted file mode 100644 index f759eed..0000000 --- a/src/app/app.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, { Component } from 'react'; - -import './app.css'; - -export class App extends Component { - render() { - return ( -
-
-

- Edit src/app/app.jsx and save to reload. -

- - Learn React - -
-
- ); - } -} diff --git a/src/app/app.module.css b/src/app/app.module.css new file mode 100644 index 0000000..21d4fcd --- /dev/null +++ b/src/app/app.module.css @@ -0,0 +1,29 @@ +@font-face { + font-family: YandexSansBold; + /*src: url(fonts/YandexSansDisplay-Bold.ttf);*/ +} + +@font-face { + font-family: YandexSansThin; + /*src: url(/src/app/fonts/YandexSansDisplay-Thin.ttf);*/ +} + +@font-face { + font-family: HelveticaNeue; + /*src: url(/src/app/fonts/HelveticaNeueCyr-Medium.otf);*/ +} + +@font-face { + font-family: HelveticaNeueLight; + /*src: url(/src/app/fonts/HelveticaNeueCyr-Light.otf);*/ +} + +body { + position: relative; + + min-width: 750px; + max-width: 1600px; + padding: 0; + margin: 0 auto; + background: #e5eaf0; +} diff --git a/src/app/app.test.jsx b/src/app/app.test.jsx deleted file mode 100644 index 81be6fa..0000000 --- a/src/app/app.test.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -import { App } from './app'; - -it('renders without crashing', () => { - const div = document.createElement('div'); - - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); -}); diff --git a/src/app/app.tsx b/src/app/app.tsx new file mode 100644 index 0000000..40fb9c9 --- /dev/null +++ b/src/app/app.tsx @@ -0,0 +1,31 @@ +import React, { Component } from 'react'; + +import styles from './app.module.css'; +import { Header } from './structure/header/header'; +import { Menu } from './structure/menu/menu'; +import { MainContent } from './structure/main-content/mainContent'; + +export interface LetterType { + key: string; + id: string; + // classList: string[]; + text: string[]; + author : string; + topic : string; + date : string; + isChecked: boolean; + isVisible: boolean; +} + + +export class App extends Component { + render() { + return ( +
+
+ + +
+ ); + } +} diff --git a/src/app/fonts/HelveticaNeueCyr-Light.otf b/src/app/fonts/HelveticaNeueCyr-Light.otf new file mode 100755 index 0000000..7ced49a Binary files /dev/null and b/src/app/fonts/HelveticaNeueCyr-Light.otf differ diff --git a/src/app/fonts/HelveticaNeueCyr-Medium.otf b/src/app/fonts/HelveticaNeueCyr-Medium.otf new file mode 100755 index 0000000..a2cde16 Binary files /dev/null and b/src/app/fonts/HelveticaNeueCyr-Medium.otf differ diff --git a/src/app/fonts/YandexSansDisplay-Bold.ttf b/src/app/fonts/YandexSansDisplay-Bold.ttf new file mode 100755 index 0000000..d901f4b Binary files /dev/null and b/src/app/fonts/YandexSansDisplay-Bold.ttf differ diff --git a/src/app/fonts/YandexSansDisplay-Thin.ttf b/src/app/fonts/YandexSansDisplay-Thin.ttf new file mode 100755 index 0000000..3b8e196 Binary files /dev/null and b/src/app/fonts/YandexSansDisplay-Thin.ttf differ diff --git a/src/app/index.js b/src/app/index.ts similarity index 100% rename from src/app/index.js rename to src/app/index.ts diff --git a/src/app/structure/header/header.module.css b/src/app/structure/header/header.module.css new file mode 100644 index 0000000..2d68080 --- /dev/null +++ b/src/app/structure/header/header.module.css @@ -0,0 +1,36 @@ +/*Start SearchInput*/ + +.header { + margin-top: 7px; + margin-bottom: 18px; + margin-left: 22px; +} + +.header__lines, +.header__mainLogo { + margin: 0; + float: left; +} + +.header__lines { + margin-top: 12px; + float: left; +} + +.header__line { + width: 20px; + height: 3px; + margin-top: 4px; + background-color: #000000; +} + +.header__mainLogo { + margin-top: 7px; + margin-left: 11px; +} + +.clearfix:after { + display: table; + clear: both; + content: ''; +} diff --git a/src/app/structure/header/header.tsx b/src/app/structure/header/header.tsx new file mode 100644 index 0000000..8f43b6d --- /dev/null +++ b/src/app/structure/header/header.tsx @@ -0,0 +1,24 @@ +import React, { Component } from 'react'; + +import styles from './header.module.css'; +import logo from './images/logo.png'; +import classNames from 'classnames'; +import { SearchInput } from './searchInput/searchInput'; + +export class Header extends Component { + render() { + return ( +
+
+
+
+
+
+
+ logo +
+ +
+ ); + } +} diff --git a/src/app/structure/header/images/logo.png b/src/app/structure/header/images/logo.png new file mode 100644 index 0000000..e79b98c Binary files /dev/null and b/src/app/structure/header/images/logo.png differ diff --git a/src/app/structure/header/images/reset.png b/src/app/structure/header/images/reset.png new file mode 100644 index 0000000..ab907a0 Binary files /dev/null and b/src/app/structure/header/images/reset.png differ diff --git a/src/app/structure/header/searchInput/searchInput.module.css b/src/app/structure/header/searchInput/searchInput.module.css new file mode 100644 index 0000000..9904a40 --- /dev/null +++ b/src/app/structure/header/searchInput/searchInput.module.css @@ -0,0 +1,32 @@ +.header__search { + width: 37%; + height: 32px; + margin-top: 11px; + margin-left: 114px; + background: #f2f5f8; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2); + float: left; +} + +.header__input { + display: inline-block; + width: calc(100% - 50px); + height: 26px; + margin-left: 16px; + + border: none; + background: none; + outline: none; + vertical-align: center; +} + +.header__button { + width: 12px; + height: 12px; + padding: 0; + margin: 10px; + + border: none; + background: #f2f5f8; + float: right; +} diff --git a/src/app/structure/header/searchInput/searchInput.tsx b/src/app/structure/header/searchInput/searchInput.tsx new file mode 100644 index 0000000..b6d1ba2 --- /dev/null +++ b/src/app/structure/header/searchInput/searchInput.tsx @@ -0,0 +1,29 @@ +import React, { Component } from 'react'; + +import styles from './searchInput.module.css'; +import reset from '../images/reset.png'; + +export class SearchInput extends Component { + + private inputVal ?: HTMLInputElement; + + resetForm = () => { + this.inputVal!.value = ''; + }; + + render() { + return ( +
+ (this.inputVal = el.target)} + placeholder="Поиск" + className={styles.header__input} + /> + +
+ ); + } +} diff --git a/src/app/structure/main-content/all-functions/allFunctions.module.css b/src/app/structure/main-content/all-functions/allFunctions.module.css new file mode 100644 index 0000000..33ac520 --- /dev/null +++ b/src/app/structure/main-content/all-functions/allFunctions.module.css @@ -0,0 +1,37 @@ +.clearfix:after { + display: table; + clear: both; + content: ''; +} + +.check { + width: 16px; + height: 16px; + margin: 12px; + + border: 1px solid rgba(0, 0, 0, 0.15); + background-color: #ffffff; + border-radius: 3px; + float: left; +} + +.hiddenCheck { + display: none; +} + +.mainBlock__mailFunctions { + height: 40px; + + border-bottom: 2px solid rgba(0, 0, 0, 0.15); +} + +.mainBlock__allFunctions { + padding-top: 6px; + margin: 0; +} + +.mainBlock__func { + display: inline-block; + margin-left: 20px; + list-style: none; +} diff --git a/src/app/structure/main-content/all-functions/allFunctions.tsx b/src/app/structure/main-content/all-functions/allFunctions.tsx new file mode 100644 index 0000000..3174990 --- /dev/null +++ b/src/app/structure/main-content/all-functions/allFunctions.tsx @@ -0,0 +1,81 @@ +import React, { Component } from 'react'; + +import styles from './allFunctions.module.css'; +import { Button } from './button/button'; +import classNames from 'classnames'; + +interface IProps { + isLetterOpened : boolean; + isChecked : boolean; + newMailOnClick: () => void; + deleteLetter: () => void; + selectAll: () => void; +} + +export class AllFunctions extends Component { + constructor(props : IProps) { + super(props); + this.props = props; + } + + public readonly props: IProps; + + doNothing = () => {}; + + render() { + return ( +
+ { + if (!this.props.isLetterOpened) this.props.selectAll(); + }} + /> +
    +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
+
+ ); + } +} diff --git a/src/app/structure/main-content/all-functions/button/button.module.css b/src/app/structure/main-content/all-functions/button/button.module.css new file mode 100644 index 0000000..65a6f25 --- /dev/null +++ b/src/app/structure/main-content/all-functions/button/button.module.css @@ -0,0 +1,13 @@ +.mainBlock__refFunc { + border: none; + background: none; + color: #cccccc; + font-family: "'HelveticaNeue'", sans-serif; + font-size: 13px; + font-weight: 500; + text-decoration: none; +} + +.mainBlock__refFunc:hover { + border-bottom: 2px solid #cccccc; +} diff --git a/src/app/structure/main-content/all-functions/button/button.tsx b/src/app/structure/main-content/all-functions/button/button.tsx new file mode 100644 index 0000000..4516f29 --- /dev/null +++ b/src/app/structure/main-content/all-functions/button/button.tsx @@ -0,0 +1,33 @@ +import React, { Component } from 'react'; + +import styles from './button.module.css'; + +interface IProps { + action : () => void; + id : string; + title : string; + isLetterOpened : boolean; +} +export class Button extends Component { + constructor(props : IProps ) { + super(props); + this.props = props; + } + + public readonly props: IProps; + + render() { + return ( + + ); + } +} diff --git a/src/app/structure/main-content/footer/footer.module.css b/src/app/structure/main-content/footer/footer.module.css new file mode 100644 index 0000000..2a868f3 --- /dev/null +++ b/src/app/structure/main-content/footer/footer.module.css @@ -0,0 +1,20 @@ +.footer { + position: absolute; + bottom: 0; + left: 0; + + width: 100%; + height: 26px; + padding-top: 5px; + + border-top: 2px solid rgba(140, 140, 140, 0.45); + background-color: white; + color: #9b9b9b; + font-family: "'HelveticaNeue'", sans-serif; + font-size: 0.7em; +} + +.footer__texts { + margin-right: 20px; + float: right; +} diff --git a/src/app/structure/main-content/footer/footer.tsx b/src/app/structure/main-content/footer/footer.tsx new file mode 100644 index 0000000..f56122b --- /dev/null +++ b/src/app/structure/main-content/footer/footer.tsx @@ -0,0 +1,18 @@ +import React, { Component } from 'react'; + +import styles from './footer.module.css'; +import { FooterText } from './footerText/footerText'; + +export class Footer extends Component { + render() { + return ( +
+
+ + + © 2001—2019, Яндекс +
+
+ ); + } +} diff --git a/src/app/structure/main-content/footer/footerText/footerText.module.css b/src/app/structure/main-content/footer/footerText/footerText.module.css new file mode 100644 index 0000000..f9d6c77 --- /dev/null +++ b/src/app/structure/main-content/footer/footerText/footerText.module.css @@ -0,0 +1,5 @@ +.footer__ref { + margin-right: 10px; + color: #9b9b9b; + text-decoration: none; +} diff --git a/src/app/structure/main-content/footer/footerText/footerText.tsx b/src/app/structure/main-content/footer/footerText/footerText.tsx new file mode 100644 index 0000000..149b15a --- /dev/null +++ b/src/app/structure/main-content/footer/footerText/footerText.tsx @@ -0,0 +1,22 @@ +import React, { Component } from 'react'; + +import styles from './footerText.module.css'; + +interface IProps { + id :string; + text : string; +} + +export class FooterText extends Component { + constructor(props : IProps) { + super(props); + + } + render() { + return ( + + {this.props.text} + + ); + } +} diff --git a/src/app/structure/main-content/letters/letter-page/img/reset.png b/src/app/structure/main-content/letters/letter-page/img/reset.png new file mode 100644 index 0000000..ab907a0 Binary files /dev/null and b/src/app/structure/main-content/letters/letter-page/img/reset.png differ diff --git a/src/app/structure/main-content/letters/letter-page/letterPage.module.css b/src/app/structure/main-content/letters/letter-page/letterPage.module.css new file mode 100644 index 0000000..e5e8816 --- /dev/null +++ b/src/app/structure/main-content/letters/letter-page/letterPage.module.css @@ -0,0 +1,25 @@ +.mainBlock__letterContent { + position: absolute; + top: 42px; + + overflow: scroll; + width: 100%; + height: calc(100% - 70px); + padding: 0; + background: #ffffff; + color: #000000; + float: left; + font-family: "'HelveticaNeue'", sans-serif; + font-size: 0.8em; + font-weight: 700; +} + +.mainBlock__paragraph { + padding: 10px; + margin-top: 10px; +} + +.close { + padding: 15px; + float: right; +} diff --git a/src/app/structure/main-content/letters/letter-page/letterPage.tsx b/src/app/structure/main-content/letters/letter-page/letterPage.tsx new file mode 100644 index 0000000..dcd6a72 --- /dev/null +++ b/src/app/structure/main-content/letters/letter-page/letterPage.tsx @@ -0,0 +1,32 @@ +import React, { Component } from 'react'; + +import styles from './letterPage.module.css'; +import reset from './img/reset.png'; + +interface IProps { + closeLetter : () => void; + text : string[]; +} + +export class LetterPage extends Component { + constructor(props : IProps) { + super(props); + } + + render() { + return ( +
+ reset { + this.props.closeLetter(); + }} + /> +

{this.props.text}

+
+ ); + } +} diff --git a/src/app/structure/main-content/letters/letters-list/letter/icons/ebay.png b/src/app/structure/main-content/letters/letters-list/letter/icons/ebay.png new file mode 100644 index 0000000..c9edef4 Binary files /dev/null and b/src/app/structure/main-content/letters/letters-list/letter/icons/ebay.png differ diff --git a/src/app/structure/main-content/letters/letters-list/letter/icons/facebook.png b/src/app/structure/main-content/letters/letters-list/letter/icons/facebook.png new file mode 100644 index 0000000..2d7290c Binary files /dev/null and b/src/app/structure/main-content/letters/letters-list/letter/icons/facebook.png differ diff --git a/src/app/structure/main-content/letters/letters-list/letter/icons/live.png b/src/app/structure/main-content/letters/letters-list/letter/icons/live.png new file mode 100644 index 0000000..19f8a27 Binary files /dev/null and b/src/app/structure/main-content/letters/letters-list/letter/icons/live.png differ diff --git a/src/app/structure/main-content/letters/letters-list/letter/icons/reddit.png b/src/app/structure/main-content/letters/letters-list/letter/icons/reddit.png new file mode 100644 index 0000000..a297ce9 Binary files /dev/null and b/src/app/structure/main-content/letters/letters-list/letter/icons/reddit.png differ diff --git a/src/app/structure/main-content/letters/letters-list/letter/icons/twitter.png b/src/app/structure/main-content/letters/letters-list/letter/icons/twitter.png new file mode 100644 index 0000000..41e07c1 Binary files /dev/null and b/src/app/structure/main-content/letters/letters-list/letter/icons/twitter.png differ diff --git a/src/app/structure/main-content/letters/letters-list/letter/icons/yandex.png b/src/app/structure/main-content/letters/letters-list/letter/icons/yandex.png new file mode 100644 index 0000000..df4059c Binary files /dev/null and b/src/app/structure/main-content/letters/letters-list/letter/icons/yandex.png differ diff --git a/src/app/structure/main-content/letters/letters-list/letter/icons/youtube.png b/src/app/structure/main-content/letters/letters-list/letter/icons/youtube.png new file mode 100644 index 0000000..f06f858 Binary files /dev/null and b/src/app/structure/main-content/letters/letters-list/letter/icons/youtube.png differ diff --git a/src/app/structure/main-content/letters/letters-list/letter/letter.module.css b/src/app/structure/main-content/letters/letters-list/letter/letter.module.css new file mode 100644 index 0000000..2a4ca4a --- /dev/null +++ b/src/app/structure/main-content/letters/letters-list/letter/letter.module.css @@ -0,0 +1,113 @@ +.check { + width: 16px; + height: 16px; + margin: 12px; + + border: 1px solid rgba(0, 0, 0, 0.15); + background-color: #ffffff; + border-radius: 3px; + float: left; +} + +.mainBlock__topic { + overflow: hidden; + width: 30%; + min-width: 30px; + height: 18px; + margin-top: 11px; + margin-left: 2%; + color: #000000; + float: left; + font-family: "'HelveticaNeue'", sans-serif; + font-size: 0.8em; +} + +.mainBlock__letter { + height: 40px; + + border-bottom: 2px solid #e2e2e2; + list-style: none; +} + +.mainBlock__img { + height: 30px; + margin-top: 5px; + margin-left: 2%; + float: left; + text-align: center; +} + +.mainBlock__mailFrom { + overflow: hidden; + width: 29%; + height: 18px; + margin: 10px; + float: left; + font-family: "'HelveticaNeue'", sans-serif; + font-size: 0.8em; +} + +.mainBlock__mailNotRead { + width: 10px; + height: 10px; + margin-top: 15px; + margin-left: 10px; + background: #6287bd; + border-radius: 50%; + float: left; +} + +.hidden { + visibility: hidden; +} + +.boldText { + font-weight: 700; +} + +.mainBlock__date { + overflow: hidden; + width: 6%; + height: 15px; + margin-top: 12px; + margin-right: 2%; + color: #9b9b9b; + float: right; + font-family: "'HelveticaNeue'", sans-serif; + font-size: 11px; +} + +.animationInsert { + animation: inserting 0.5s linear; +} + +.animationDelete { + animation: deleting 0.6s linear; +} + +@keyframes inserting { + 0% { + transform: scale(0.5); + } + 100% { + transform: scale(1); + } +} + +@keyframes down { + 0% { + transform: translateY(-40px); + } + 100% { + opacity: 1; + } +} + +@keyframes deleting { + 0% { + transform: scale(1); + } + 100% { + transform: scale(0.5); + } +} diff --git a/src/app/structure/main-content/letters/letters-list/letter/letter.tsx b/src/app/structure/main-content/letters/letters-list/letter/letter.tsx new file mode 100644 index 0000000..60f2db2 --- /dev/null +++ b/src/app/structure/main-content/letters/letters-list/letter/letter.tsx @@ -0,0 +1,65 @@ +import React, { Component } from 'react'; + +import styles from './letter.module.css'; +import logo1 from './icons/ebay.png'; +import logo2 from './icons/yandex.png'; +import logo3 from './icons/live.png'; +import logo4 from './icons/facebook.png'; +import logo5 from './icons/twitter.png'; +import logo6 from './icons/reddit.png'; +import logo7 from './icons/youtube.png'; +import { LetterType } from '../../../../../app'; +import classNames from 'classnames'; + +interface IProps { + key : string; + letterStates: LetterType; + isChecked: boolean; + openLetter: (text: string[]) => void; + onCheckboxChange: (id: string) => void; +} + +export class Letter extends Component { + constructor(props: IProps) { + super(props); + + this.firms = { + ebay: logo1, + yandex: logo2, + live: logo3, + facebook: logo4, + twitter: logo5, + reddit: logo6, + youtube: logo7 + }; + } + + private readonly firms: { [id: string]: string }; + + render() { + return ( +
  • +
    + this.props.onCheckboxChange(this.props.letterStates.id)} + checked={this.props.isChecked} + /> +
    this.props.openLetter(this.props.letterStates.text)}> +
    + {this.props.letterStates.author} +
    + {this.props.letterStates.author} +
    + {this.props.letterStates.topic} + +
    +
    +
  • + ); + } +} diff --git a/src/app/structure/main-content/letters/letters-list/letterList.tsx b/src/app/structure/main-content/letters/letters-list/letterList.tsx new file mode 100644 index 0000000..26be550 --- /dev/null +++ b/src/app/structure/main-content/letters/letters-list/letterList.tsx @@ -0,0 +1,39 @@ +import React, { Component } from 'react'; + +import styles from './lettersList.module.css'; +import { Letter } from './letter/letter'; +import { LetterType } from '../../../../app'; + +interface IProps { + letters : LetterType[]; + openLetter : (text : string[]) => void; + onCheckboxChange : (id : string) => void; + checkedLetters: { [id: string]: boolean }; +} +export class LetterList extends Component { + constructor(props : IProps){ + super(props); + this.props = props; + } + + public readonly props: IProps; + + render() { + return ( +
      + {this.props.letters.map(letter => { + if (letter.isVisible) + return ( + + ); + })} +
    + ); + } +} diff --git a/src/app/structure/main-content/letters/letters-list/lettersList.module.css b/src/app/structure/main-content/letters/letters-list/lettersList.module.css new file mode 100644 index 0000000..72af51c --- /dev/null +++ b/src/app/structure/main-content/letters/letters-list/lettersList.module.css @@ -0,0 +1,10 @@ +.mainBlock__allLetters { + overflow: scroll; + height: calc(100vh - 14px); + padding: 0; + margin: 0; +} + +.allLetterDown { + animation: down 0.6s linear; +} diff --git a/src/app/structure/main-content/letters/letters.tsx b/src/app/structure/main-content/letters/letters.tsx new file mode 100644 index 0000000..4ad08d0 --- /dev/null +++ b/src/app/structure/main-content/letters/letters.tsx @@ -0,0 +1,37 @@ +import React, { Component } from 'react'; + +import { LetterPage } from './letter-page/letterPage'; +import { LetterList } from './letters-list/letterList'; +import { LetterType } from '../../../app'; + +interface IProps { + isLetterOpened : boolean; + openedLetterText : string[]; + closeLetter : () => void; + openLetter : (text : string[]) => void; + onCheckboxChange : (id : string) => void; + letters : LetterType[]; + checkedLetters: { [id: string]: boolean }; +} + +export class Letters extends Component { + constructor(props : IProps){ + super(props); + this.props = props; + } + + public readonly props: IProps; + + render() { + return this.props.isLetterOpened ? ( + + ) : ( + + ); + } +} diff --git a/src/app/structure/main-content/mainContent.module.css b/src/app/structure/main-content/mainContent.module.css new file mode 100644 index 0000000..6672331 --- /dev/null +++ b/src/app/structure/main-content/mainContent.module.css @@ -0,0 +1,14 @@ +.mainBlock { + position: relative; + + width: calc(100% - 230px); + min-width: 515px; + min-height: 300px; + padding: 0; + margin: 0 20px 14px 22px; + + background-color: #ffffff; + border-radius: 3px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.34); + float: right; +} diff --git a/src/app/structure/main-content/mainContent.tsx b/src/app/structure/main-content/mainContent.tsx new file mode 100644 index 0000000..29de842 --- /dev/null +++ b/src/app/structure/main-content/mainContent.tsx @@ -0,0 +1,146 @@ +import React, { Component } from 'react'; + +import styles from './mainContent.module.css'; +import { Letters } from './letters/letters'; +import { AllFunctions } from './all-functions/allFunctions'; +import { generateNewLetter, randomInt } from './scripts/generator'; +import { Footer } from './footer/footer'; +import { LetterType } from '../../app'; + +const LETTERS_ON_PAGE = 30; + +interface IState { + isLetterOpened: false; + openedLetterText: string[]; + letters: LetterType[]; + isAllChecked: boolean; + checkedLetters: { [id: string]: boolean }; +} + +export class MainContent extends Component { + public constructor(props : any) { + super(props); + + this.state = { + isLetterOpened: false, + openedLetterText: [], + letters: [], + isAllChecked: false, + checkedLetters: {} + }; + + this.onCheckboxChange.bind(this); + this.selectAll.bind(this); + this.newMail.bind(this); + this.deleteLetter.bind(this); + this.getRandomLetter.bind(this); + this.openLetter.bind(this); + this.closeLetter.bind(this); + + setTimeout(this.getRandomLetter, 100); + } + + public readonly state: IState; + + public onCheckboxChange = (id: string) => { + this.setState((prevState : IState)=> { + const newCheckedLetters : { [id: string]: boolean } = prevState.checkedLetters; + newCheckedLetters[id] = !newCheckedLetters[id]; + return { + isAllChecked: false, + checkedLetters: newCheckedLetters + }; + }); + }; + + public newMail = () => { + const newLetter : LetterType = generateNewLetter(); + this.setState((prevState : IState)=> { + const newCheckedLetters = prevState.checkedLetters; + const newLetters = prevState.letters; + newCheckedLetters[newLetter.id] = false; + for (let i = newLetters.length - 1; i >= LETTERS_ON_PAGE - 1; i--) { + newLetters[i].isVisible = false; + newLetters[i].isChecked = false; + newCheckedLetters[newLetters[i].key] = false; + } + return { + letters: [newLetter, ...newLetters], + checkedLetters: newCheckedLetters, + isAllChecked: false + }; + }); + }; + + private getRandomLetter = () => { + const t = randomInt(10, 300000) + 300000; + this.newMail(); + setTimeout(this.getRandomLetter, t); + }; + + public selectAll = () => { + this.setState((prevState : IState) => { + const newCheckedLetters = prevState.checkedLetters; + prevState.letters.forEach(letter => { + if (letter.isVisible) { + newCheckedLetters[letter.key] = !prevState.isAllChecked; + } + }); + return { + isAllChecked: !prevState.isAllChecked, + checkedLetters: newCheckedLetters + }; + }); + }; + + public deleteLetter = () => { + this.setState((prevState : IState) => { + const newLetters = prevState.letters.filter(letter => !prevState.checkedLetters[letter.key]); + for (let i = 0; i < Math.min(newLetters.length, LETTERS_ON_PAGE); i++) { + newLetters[i].isVisible = true; + } + return { + letters: newLetters, + isAllChecked: false + }; + }); + }; + + public openLetter = (text : string[]) => { + this.setState({ + isLetterOpened: true, + openedLetterText: text + }); + }; + + public closeLetter = () => { + this.setState({ + isLetterOpened: false, + openedLetterText: null + }); + }; + + render() { + return ( +
    + + +
    +
    + ); + } +} diff --git a/src/app/structure/main-content/scripts/generator.ts b/src/app/structure/main-content/scripts/generator.ts new file mode 100644 index 0000000..fc79de9 --- /dev/null +++ b/src/app/structure/main-content/scripts/generator.ts @@ -0,0 +1,119 @@ +const companies = ['ebay', 'facebook', 'live', 'yandex', 'live', 'reddit', 'twitter', 'youtube']; +const topics = [ + 'Внимание!', + 'Добро пожаловать!', + 'Обновление', + 'Получите приз!', + 'Восстановление аккаунта', + 'Ищем сотрудников', + 'Спасибо за отзыв' +]; +const hello = ['Здравствуйте!', 'Добрый день!', 'Привет!', 'Приветствую!', 'Салют!']; +const word1 = ['Я', 'Меня зовут', 'Это']; +const firstName = ['Виталий', 'Андрей', 'Владимир', 'Алексей', 'Артём', 'Антон']; +const secondName = [ + 'Соболев', + 'Чиркин', + 'Борисов', + 'Орехов', + 'Гаврилов', + 'Иванов', + 'Сергеев', + 'Онегин' +]; +const phrase1 = [ + 'Так вышло, что', + 'Нам стало известно, что', + 'Сообщаем вам, что', + 'Как вы могли заметить,', + 'С сегоднящнего дня' +]; +const nouns1 = ['эксперт', 'редактор', 'программист', 'рабочий']; +const verbs = [ + 'взломал', + 'проверил', + 'удалил', + 'исправил', + 'закрыл', + 'заметил', + 'пометил', + 'пересмотрел', + 'передал' +]; +const nouns2 = ['счет', 'аккаунт', 'пароль', 'кабинет']; +const adjectives = [ + 'идеальный', + 'прямой', + 'обратный', + 'наш', + 'постоянный', + 'великолепный', + 'исключительный', + 'личный', + 'ваш' +]; + +let counter = 0; + +export function randomInt(min: number, max : number) { + return Math.floor((max - min) * Math.random() + min); +} + +function randFromList(list : any) { + return list[randomInt(0, list.length)]; +} + +const randomDate = () => { + const m = randomInt(1, 12); + let day = randomInt(1, 30); + if (m === 2) day = Math.min(28, day); + const month = [ + 'янв', + 'фев', + 'мар', + 'апр', + 'май', + 'июн', + 'июл', + 'авг', + 'сен', + 'окт', + 'ноя', + 'дек' + ]; + return `${String(day)} ${String(month[m - 1])}`; +}; + +function generateText(sender:string) { + const textContent = [ + `${randFromList(hello)} ${randFromList(word1)} ${randFromList(firstName)} ${randFromList( + secondName + )}, глава компании ${sender.toUpperCase()}.` + ]; + textContent.push( + `${randFromList(phrase1)} ${randFromList(adjectives)} ${randFromList(nouns1)} ${randFromList( + verbs + )} ${randFromList(adjectives)} ${randFromList(nouns2)}.` + ); + return textContent; +} + +export const generateNewLetter = () => { + counter++; + const id = counter; + const author = randFromList(companies); + const text = generateText(author); + const topic = randFromList(topics); + const date = randomDate(); + + return { + key: `id${id}`, + id: `id${id}`, + text: text, + author : author, + topic : topic, + date : date, + isChecked: false, + isVisible: true + }; +}; diff --git a/src/app/structure/menu/menu.module.css b/src/app/structure/menu/menu.module.css new file mode 100644 index 0000000..a826262 --- /dev/null +++ b/src/app/structure/menu/menu.module.css @@ -0,0 +1,53 @@ +.menu { + width: 147px; + max-width: 170px; + margin-left: 22px; + float: left; +} + +.menu__toWrite { + width: 147px; + height: 32px; + + border: none; + background-color: #6287bd; + border-radius: 3px; + color: #ffffff; + font-family: "'HelveticaNeue'", sans-serif; + font-size: 12px; +} + +.menu__toWrite:hover { + box-shadow: 2px 2px rgba(0, 0, 0, 0.6); +} + +.menu__bar { + padding: 0; +} + +.menu__action { + width: 147px; + height: 22px; + margin-top: 2px; + border-radius: 3px; + list-style: none; +} + +.menu__textRef { + display: block; + padding-left: 10px; + color: #555555; + font-family: "'HelveticaNeue'", sans-serif; + font-size: 11px; + font-weight: 500; + line-height: 22px; + text-decoration: none; +} + +.menu__action:hover { + background-color: #cdd6e4; +} + +.menu__textRef:hover { + font-weight: 700; +} diff --git a/src/app/structure/menu/menu.tsx b/src/app/structure/menu/menu.tsx new file mode 100644 index 0000000..d16a1ce --- /dev/null +++ b/src/app/structure/menu/menu.tsx @@ -0,0 +1,35 @@ +import React, { Component } from 'react'; + +import styles from './menu.module.css'; + +const actions = [ + { title: 'Входящие', fragment: 'inbox' }, + { title: 'Отправленные', fragment: 'sent' }, + { title: 'Удалённые', fragment: 'deleted' }, + { title: 'Спам', fragment: 'spam' }, + { title: 'Черновики', fragment: 'drafts' }, + { title: 'Создать папку', fragment: 'createdir' } +]; + +export class Menu extends Component { + render() { + return ( +
    + + +
    + ); + } +}