diff --git a/README.md b/README.md
index 4cef61b..a0d2137 100644
--- a/README.md
+++ b/README.md
@@ -107,4 +107,4 @@ WebStorm включает в себя всё необходимое для ра
- [React](https://reactjs.org) 🇺🇸
- [React](https://ru.reactjs.org) 🇷🇺
- [Create React App](https://facebook.github.io/create-react-app/docs/getting-started) 🇺🇸
-- [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/basic-types.html) 🇺🇸
+- [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/basic-types.html) 🇺🇸
\ No newline at end of file
diff --git a/package.json b/package.json
index 9ede333..dde8140 100644
--- a/package.json
+++ b/package.json
@@ -9,8 +9,13 @@
"not op_mini all"
],
"dependencies": {
+ "@types/jest": "24.0.12",
+ "@types/node": "12.0.0",
+ "@types/react": "16.8.16",
+ "@types/react-dom": "16.8.4",
"react": "16.8.6",
- "react-dom": "16.8.6"
+ "react-dom": "16.8.6",
+ "typescript": "3.3.4000"
},
"devDependencies": {
"@hellroot/eslint-config": "1.8.0",
diff --git a/public/index.html b/public/index.html
index 9a8ef8f..fd7dc98 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,17 +1,13 @@
-
+
+
-
-
-
- React App
+ Yandex.Mail
+
-
-
+
diff --git a/src/app/app.css b/src/app/app.css
index 1c4d511..6b72ab5 100644
--- a/src/app/app.css
+++ b/src/app/app.css
@@ -1,27 +1,14 @@
-.app {
- text-align: center;
-}
+.main {
+ position: static;
-.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);
+ width: 800px;
+ height: 600px;
}
-.app-link {
- color: #61dafb;
+.light {
+ background-color: #e5eaf0;
}
-@keyframes app-logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
+.dark {
+ background-color: #061f2f;
}
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 (
-
- );
- }
-}
diff --git a/src/app/app.tsx b/src/app/app.tsx
new file mode 100644
index 0000000..f7d8603
--- /dev/null
+++ b/src/app/app.tsx
@@ -0,0 +1,210 @@
+import React, { Component } from 'react';
+
+import './app.css';
+import Menu from '../menu/menu';
+import Writebox from '../writebox/writebox';
+import Search from '../search/search';
+import Letters from '../letters/letters';
+import Logo from '../logo/logo';
+import ThemeSwitch from '../theme-switch/theme-switch';
+import { ILetter } from '../letters/__letter/letters__letter';
+import { contents, getRandomInt, rndAuthor, rndTheme } from '../letters/content';
+
+interface IState {
+ letters: ILetter[];
+ theme: string;
+ filter: string;
+ filterProcessingDisplay: boolean;
+}
+
+export class App extends Component<{}, IState> {
+ public constructor(props: {}) {
+ super(props);
+ this.state = {
+ theme: 'light',
+ filter: '',
+ filterProcessingDisplay: false,
+ letters: [
+ {
+ author: 'Яндекс.Паспорт',
+ theme: 'Доступ к аккаунту восстановлен',
+ date: '6 авг',
+ authorImage: Letters.createYandexAuthorImage(),
+ read: false,
+ checked: false,
+ display: true
+ },
+ {
+ author: 'Команда Яндекс.Почты',
+ theme: 'Как читать почту с мобильного',
+ date: '6 июл',
+ authorImage: Letters.createYandexAuthorImage(),
+ read: false,
+ checked: false,
+ display: true
+ },
+ {
+ author: 'Команда Яндекс.Почты',
+ theme: 'Как читать почту с мобильного',
+ date: '6 июл',
+ authorImage: Letters.createYandexAuthorImage(),
+ read: true,
+ checked: false,
+ display: true
+ },
+ {
+ author: 'Яндекс',
+ theme: 'Соберите всю почту в этот ящик',
+ date: '6 июл',
+ authorImage: Letters.createYandexAuthorImage(),
+ read: true,
+ checked: false,
+ display: true
+ }
+ ]
+ };
+ this.changeTheme = this.changeTheme.bind(this);
+ this.handleFilterChange = this.handleFilterChange.bind(this);
+ this.setFilter = this.setFilter.bind(this);
+ this.setFilterProcessingDisplay = this.setFilterProcessingDisplay.bind(this);
+ this.newLetter = this.newLetter.bind(this);
+ this.deleteOnclick = this.deleteOnclick.bind(this);
+ this.handleCheckbox = this.handleCheckbox.bind(this);
+ this.selectAll = this.selectAll.bind(this);
+ this.processFilter = this.processFilter.bind(this);
+ }
+
+ public changeTheme(checkbox: React.ChangeEvent): void {
+ console.log(this.state.theme);
+ console.log(checkbox.currentTarget);
+ console.log(checkbox.currentTarget.checked);
+ const b = checkbox.target.checked;
+ this.setState(() => {
+ if (b) {
+ console.log('kek');
+ return {
+ theme: 'dark'
+ };
+ }
+ return {
+ theme: 'light'
+ };
+ });
+ console.log(this.state.theme);
+ }
+
+ private handleFilterChange(event: React.ChangeEvent) {
+ console.log(event.target.value);
+ this.processFilter(event.target.value);
+ this.setState({});
+ }
+
+ private setFilter(s: string) {
+ this.setState({ filter: s });
+ }
+
+ public setFilterProcessingDisplay(display: boolean) {
+ console.log('app setFilterProcessingDisplay');
+ //this.setState({ filterProcessingDisplay: display });
+ }
+
+ private removeSearchTextInput(event: React.MouseEvent) {}
+
+ private processFilter(s: string) {
+ if (s === '') {
+ for (let i = 0; i < this.state.letters.length; i++) {
+ this.state.letters[i].display = true;
+ }
+ return;
+ }
+ for (let i = 0; i < this.state.letters.length; i++) {
+ this.state.letters[i].display = this.state.letters[i].theme
+ .toLowerCase()
+ .includes(s.toLowerCase());
+ }
+ }
+
+ public componentDidMount() {
+ let sum = 0;
+
+ for (let i = 1; i < 20; i++) {
+ sum += 10 * getRandomInt(1, 600);
+ setTimeout(this.newLetter, sum);
+ }
+ }
+
+ private handleCheckbox(checkbox: React.ChangeEvent, number: number) {
+ this.state.letters[number].checked = checkbox.target.checked;
+ this.setState(state => {
+ return state;
+ });
+ }
+
+ private selectAll(checkbox: React.ChangeEvent) {
+ for (let i = 0; i < this.state.letters.length; i++) {
+ this.state.letters[i].checked = checkbox.target.checked;
+ }
+ this.setState({});
+ }
+
+ private deleteOnclick() {
+ let kek = 0;
+ while (kek++ < 4) {
+ let hasAny = false;
+ for (let i = 0; i < this.state.letters.length; i++) {
+ if (this.state.letters[i].checked) {
+ this.state.letters.splice(i, 1);
+ hasAny = true;
+ break;
+ }
+ }
+ if (hasAny) {
+ continue;
+ }
+ break;
+ }
+ this.setState(state => {
+ return state;
+ });
+ }
+
+ private newLetter() {
+ const author = rndAuthor();
+ this.state.letters.unshift({
+ author,
+ theme: rndTheme(author),
+ date: '6 июл',
+ read: false,
+ checked: false,
+ authorImage: Letters.createYandexAuthorImage(),
+ display: true
+ });
+ this.setState({});
+ }
+
+ public render() {
+ return (
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/src/fonts/HelveticaNeueCyr-Bold.otf b/src/fonts/HelveticaNeueCyr-Bold.otf
new file mode 100644
index 0000000..383dca6
Binary files /dev/null and b/src/fonts/HelveticaNeueCyr-Bold.otf differ
diff --git a/src/fonts/HelveticaNeueCyr-Light.otf b/src/fonts/HelveticaNeueCyr-Light.otf
new file mode 100644
index 0000000..7ced49a
Binary files /dev/null and b/src/fonts/HelveticaNeueCyr-Light.otf differ
diff --git a/src/fonts/HelveticaNeueCyr-Medium.otf b/src/fonts/HelveticaNeueCyr-Medium.otf
new file mode 100644
index 0000000..a2cde16
Binary files /dev/null and b/src/fonts/HelveticaNeueCyr-Medium.otf differ
diff --git a/src/fonts/YandexSansText-Bold.ttf b/src/fonts/YandexSansText-Bold.ttf
new file mode 100644
index 0000000..e267b3f
Binary files /dev/null and b/src/fonts/YandexSansText-Bold.ttf differ
diff --git a/src/fonts/YandexSansText-Light.ttf b/src/fonts/YandexSansText-Light.ttf
new file mode 100644
index 0000000..875cb32
Binary files /dev/null and b/src/fonts/YandexSansText-Light.ttf differ
diff --git a/src/fonts/YandexSansText-Regular.ttf b/src/fonts/YandexSansText-Regular.ttf
new file mode 100644
index 0000000..990d16b
Binary files /dev/null and b/src/fonts/YandexSansText-Regular.ttf differ
diff --git a/src/images/Fill-154.png b/src/images/Fill-154.png
new file mode 100644
index 0000000..6aae6dd
Binary files /dev/null and b/src/images/Fill-154.png differ
diff --git a/src/images/Oval-9.png b/src/images/Oval-9.png
new file mode 100644
index 0000000..93c9360
Binary files /dev/null and b/src/images/Oval-9.png differ
diff --git a/src/images/Oval.png b/src/images/Oval.png
new file mode 100644
index 0000000..c12733c
Binary files /dev/null and b/src/images/Oval.png differ
diff --git a/src/images/Shape.png b/src/images/Shape.png
new file mode 100644
index 0000000..e79b98c
Binary files /dev/null and b/src/images/Shape.png differ
diff --git a/src/images/cross.png b/src/images/cross.png
new file mode 100644
index 0000000..e20bcc9
Binary files /dev/null and b/src/images/cross.png differ
diff --git a/src/images/nut.webp b/src/images/nut.webp
new file mode 100644
index 0000000..6cdb766
Binary files /dev/null and b/src/images/nut.webp differ
diff --git a/src/index.css b/src/index.css
index 2b6e525..45b8518 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,3 +1,37 @@
+@font-face {
+ font-family: 'Yandex Sans Light';
+ src: url(fonts/YandexSansText-Light.ttf);
+}
+
+@font-face {
+ font-family: 'Yandex Sans Bold';
+ src: url(fonts/YandexSansText-Bold.ttf);
+}
+
+@font-face {
+ font-family: 'Yandex Sans Regular';
+ src: url(fonts/YandexSansText-Regular.ttf);
+}
+
+@font-face {
+ font-family: 'Helvetica Neue Bold';
+ src: url(fonts/HelveticaNeueCyr-Bold.otf);
+}
+
+@font-face {
+ font-family: 'Helvetica Neue Medium';
+ src: url(fonts/HelveticaNeueCyr-Medium.otf);
+}
+
+@font-face {
+ font-family: 'Helvetica Neue Light';
+ src: url(fonts/HelveticaNeueCyr-Light.otf);
+}
+
+p {
+ overflow: hidden;
+}
+
body {
padding: 0;
margin: 0;
diff --git a/src/index.jsx b/src/index.jsx
index ffc72ee..5912260 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -6,3 +6,5 @@ import { App } from './app';
import './index.css';
ReactDOM.render(, document.getElementById('root'));
+
+// document.getElementById('root').newLetters();
diff --git a/src/letters/Letters.module.css b/src/letters/Letters.module.css
new file mode 100644
index 0000000..cb08c06
--- /dev/null
+++ b/src/letters/Letters.module.css
@@ -0,0 +1,23 @@
+.letters {
+ position: absolute;
+
+ width: 589px;
+ height: 530px;
+ margin-top: 56px;
+
+ margin-left: 191px;
+
+ box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.34);
+}
+
+#letters__footer-line {
+ margin-top: 499px;
+}
+
+.light {
+ background-color: #fff;
+}
+
+.dark {
+ background-color: #1e3543;
+}
diff --git a/src/letters/__article/Article.module.css b/src/letters/__article/Article.module.css
new file mode 100644
index 0000000..f3d2a5c
--- /dev/null
+++ b/src/letters/__article/Article.module.css
@@ -0,0 +1,31 @@
+.article {
+ position: absolute;
+
+ overflow: hidden;
+ width: inherit;
+ max-width: 589px;
+ height: 448px;
+ max-height: 448px;
+ margin-top: 41px;
+ margin-left: 0;
+}
+
+.articleTitle {
+ margin-left: 50px;
+}
+
+.light {
+ background-color: #fff;
+}
+
+.dark {
+ background-color: #1e3543;
+}
+
+.textWhite {
+ color: #ded8d5;
+}
+
+.textBlack {
+ color: #000;
+}
diff --git a/src/letters/__article/__cross/Cross.module.css b/src/letters/__article/__cross/Cross.module.css
new file mode 100644
index 0000000..4151331
--- /dev/null
+++ b/src/letters/__article/__cross/Cross.module.css
@@ -0,0 +1,6 @@
+.cross {
+ position: absolute;
+
+ margin-top: -80px;
+ margin-left: 570px;
+}
diff --git a/src/letters/__article/__cross/letters__cross.tsx b/src/letters/__article/__cross/letters__cross.tsx
new file mode 100644
index 0000000..dbe515b
--- /dev/null
+++ b/src/letters/__article/__cross/letters__cross.tsx
@@ -0,0 +1,18 @@
+import React, { Component } from 'react';
+
+import styles from './Cross.module.css';
+import cross from '../../../images/cross.png';
+
+interface IProps {
+ close(): void;
+}
+
+export default class LettersCross extends Component {
+ public render() {
+ return (
+
+
+
+ );
+ }
+}
diff --git a/src/letters/__article/letters__article.tsx b/src/letters/__article/letters__article.tsx
new file mode 100644
index 0000000..7256628
--- /dev/null
+++ b/src/letters/__article/letters__article.tsx
@@ -0,0 +1,39 @@
+import React, { Component } from 'react';
+
+import styles from './Article.module.css';
+import LettersCross from './__cross/letters__cross';
+
+interface IProps {
+ articleHeader: string;
+ articleContent: string;
+ close(): void;
+ theme: string;
+}
+
+export default class LettersArticle extends Component {
+ private getTheme() {
+ if (this.props.theme === 'light') {
+ return styles.light;
+ }
+ return styles.dark;
+ }
+
+ private getTextTheme() {
+ if (this.props.theme === 'light') {
+ return styles.textBlack;
+ }
+ return styles.textWhite;
+ }
+
+ public render() {
+ return (
+
+
+ {this.props.articleHeader}
+
+
{this.props.articleContent}
+
+
+ );
+ }
+}
diff --git a/src/letters/__footer/Footer.module.css b/src/letters/__footer/Footer.module.css
new file mode 100644
index 0000000..800ba5a
--- /dev/null
+++ b/src/letters/__footer/Footer.module.css
@@ -0,0 +1,16 @@
+.footer {
+ position: absolute;
+ right: 20px;
+ bottom: 9px;
+
+ color: #9b9b9b;
+ font-family: 'Helvetica Neue Medium', sans-serif;
+ font-size: 11px;
+}
+
+.footerText {
+ overflow: hidden;
+ max-width: 350px;
+ max-height: 11px;
+ margin-bottom: 2px;
+}
diff --git a/src/letters/__footer/letters__footer.tsx b/src/letters/__footer/letters__footer.tsx
new file mode 100644
index 0000000..4f17a9e
--- /dev/null
+++ b/src/letters/__footer/letters__footer.tsx
@@ -0,0 +1,16 @@
+import React, { Component } from 'react';
+
+import styles from './Footer.module.css';
+
+export default class LettersFooter extends Component {
+ public render() {
+ return (
+
+
+ Помощь и обратная связь Реклама
+ © 2001—2018, Яндекс
+
+
+ );
+ }
+}
diff --git a/src/letters/__letter/Letter.module.css b/src/letters/__letter/Letter.module.css
new file mode 100644
index 0000000..10f5e57
--- /dev/null
+++ b/src/letters/__letter/Letter.module.css
@@ -0,0 +1,125 @@
+.line {
+ position: absolute;
+
+ width: inherit;
+ height: 0;
+ margin-top: 41px;
+}
+
+.marker {
+ position: absolute;
+
+ width: 16px;
+ height: 16px;
+ margin-top: 14px;
+ margin-left: 20px;
+
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ background-color: #ffffff;
+ border-radius: 3px;
+ color: #fff;
+}
+
+.label {
+ width: inherit;
+}
+
+/* Single letter */
+
+.authorImage {
+ position: absolute;
+
+ width: 30px;
+ height: inherit;
+ margin-top: 6px;
+ margin-left: 46px;
+}
+
+.letter {
+ width: inherit;
+ height: 43px;
+ margin-top: 0;
+ margin-left: 0;
+}
+
+.yandexOval {
+ position: absolute;
+
+ margin-top: 0;
+ margin-left: 0;
+}
+
+.Ya {
+ position: absolute;
+
+ margin-top: 7px;
+ margin-left: 9px;
+}
+
+.author {
+ position: absolute;
+
+ max-width: 160px;
+ max-height: 13px;
+ margin-top: 17px;
+ margin-left: 86px;
+ color: #000;
+ font-size: 13px;
+}
+
+.textWhite {
+ color: #ded8d5;
+}
+
+.textBlack {
+ color: #000;
+}
+
+.unreadOval {
+ position: absolute;
+
+ margin-top: 12px;
+ margin-left: 261px;
+}
+
+.theme {
+ position: absolute;
+
+ max-width: 210px;
+ max-height: 13px;
+ margin-top: 17px;
+ margin-left: 281px;
+ font-size: 13px;
+}
+
+.theme:hover {
+ background-color: grey;
+}
+
+.read {
+ font-family: 'Helvetica Neue Light', sans-serif;
+}
+
+.unread {
+ font-family: 'Helvetica Neue Bold', sans-serif;
+}
+
+.date {
+ position: absolute;
+ right: 20px;
+
+ max-width: 50px;
+ max-height: 13px;
+ margin-top: 17px;
+ color: #9b9b9b;
+ font-family: 'Helvetica Neue Medium', sans-serif;
+ font-size: 13px;
+}
+
+.lineLight {
+ border: 1px solid #e2e2e2;
+}
+
+.lineDark {
+ border: 1px solid #555;
+}
diff --git a/src/letters/__letter/letters__letter.tsx b/src/letters/__letter/letters__letter.tsx
new file mode 100644
index 0000000..ef3fffc
--- /dev/null
+++ b/src/letters/__letter/letters__letter.tsx
@@ -0,0 +1,97 @@
+import React, { Component } from 'react';
+
+import styles from './Letter.module.css';
+import unread from '../../images/Oval.png';
+
+export interface ILetter {
+ author: string;
+ theme: string;
+ date: string;
+ read: boolean;
+ checked: boolean;
+ authorImage: JSX.Element;
+ display: boolean;
+}
+
+interface IProps extends ILetter {
+ styleTheme: string;
+ number: number;
+ handleCheckbox(checkbox: React.ChangeEvent, number: number): void;
+ open(number: number): void;
+}
+
+export default class Letter extends Component {
+ public constructor(props: IProps) {
+ super(props);
+ this.state = {};
+ this.open = this.open.bind(this);
+ this.handleCheckbox = this.handleCheckbox.bind(this);
+ }
+
+ private handleCheckbox(checkbox: React.ChangeEvent) {
+ this.props.handleCheckbox(checkbox, this.props.number);
+ }
+
+ private convertReadProp() {
+ if (this.props.read) {
+ return styles.read;
+ }
+ return styles.unread;
+ }
+
+ private unreadMarker() {
+ if (this.props.read) {
+ return null;
+ }
+ return (
+
+
+
+ );
+ }
+
+ private open() {
+ this.props.open(this.props.number);
+ }
+
+ private textTheme() {
+ if (this.props.styleTheme === 'light') {
+ return styles.textBlack;
+ }
+ return styles.textWhite;
+ }
+
+ private lineTheme() {
+ if (this.props.styleTheme === 'light') {
+ return styles.lineLight;
+ }
+ return styles.lineDark;
+ }
+
+ public render() {
+ return (
+
+
+
+
{this.props.author}
+
+
+ {this.props.theme}
+
+
+
{this.props.date}
+
+ {this.props.authorImage}
+
+ {this.unreadMarker()}
+
+
+
+ );
+ }
+}
diff --git a/src/letters/__list/List.module.css b/src/letters/__list/List.module.css
new file mode 100644
index 0000000..a6587c8
--- /dev/null
+++ b/src/letters/__list/List.module.css
@@ -0,0 +1,9 @@
+.list {
+ position: absolute;
+
+ overflow: hidden;
+ width: inherit;
+ height: 430px;
+ margin-top: 41px;
+ margin-left: 0;
+}
diff --git a/src/letters/__list/letters__list.tsx b/src/letters/__list/letters__list.tsx
new file mode 100644
index 0000000..bc90ff5
--- /dev/null
+++ b/src/letters/__list/letters__list.tsx
@@ -0,0 +1,44 @@
+import React, { Component } from 'react';
+
+import styles from './List.module.css';
+import Letter, { ILetter } from '../__letter/letters__letter';
+
+interface IProps {
+ theme: string;
+ letters: ILetter[];
+ handleCheckbox(checkbox: React.ChangeEvent, number: number): void;
+ open(number: number): void;
+}
+
+export default class LettersList extends Component {
+ private makeJSXLetters() {
+ const jsxLetters = [];
+ const letters = this.props.letters;
+ for (let i = 0; i < letters.length; i++) {
+ const l = letters[i];
+ if (!l.display) {
+ continue;
+ }
+ jsxLetters.push(
+
+ );
+ }
+ return jsxLetters;
+ }
+
+ public render() {
+ return React.createElement('div', { className: styles.list }, this.makeJSXLetters());
+ }
+}
diff --git a/src/letters/__opmenu/LettersOpmenu.module.css b/src/letters/__opmenu/LettersOpmenu.module.css
new file mode 100644
index 0000000..28f3fde
--- /dev/null
+++ b/src/letters/__opmenu/LettersOpmenu.module.css
@@ -0,0 +1,51 @@
+.opmenu {
+ position: absolute;
+
+ width: 540px;
+ height: 41px;
+ margin-left: 49px;
+}
+
+.opmenuItem {
+ overflow: hidden;
+ max-height: 13px;
+ margin-top: 15px;
+
+ color: #cccccc;
+ float: left;
+ font-family: 'Helvetica Neue Bold', sans-serif;
+ font-size: 13px;
+ font-weight: 500;
+}
+
+.forward {
+ max-width: 75px;
+ margin-left: 6px;
+}
+
+.delete {
+ max-width: 53px;
+ margin-left: 26px;
+}
+
+.opmenuItem:hover {
+ background-color: grey;
+}
+
+.spam {
+ max-width: 64px;
+ margin-left: 24px;
+}
+
+.read {
+ max-width: 71px;
+ margin-left: 25px;
+}
+
+.light {
+ color: #ccc;
+}
+
+.dark {
+ color: #777;
+}
diff --git a/src/letters/__opmenu/letters__opmenu.tsx b/src/letters/__opmenu/letters__opmenu.tsx
new file mode 100644
index 0000000..3aba177
--- /dev/null
+++ b/src/letters/__opmenu/letters__opmenu.tsx
@@ -0,0 +1,44 @@
+import React, { Component } from 'react';
+
+import styles from './LettersOpmenu.module.css';
+
+interface IProps {
+ theme: string;
+ deleteOnclick(): void;
+}
+
+export default class LettersOpmenu extends Component {
+ private getTheme() {
+ if (this.props.theme === 'light') {
+ return styles.light;
+ }
+ return styles.dark;
+ }
+
+ public render() {
+ return (
+
+ );
+ }
+}
diff --git a/src/letters/content.ts b/src/letters/content.ts
new file mode 100644
index 0000000..5ec3f3d
--- /dev/null
+++ b/src/letters/content.ts
@@ -0,0 +1,38 @@
+export function getRandomInt(min: number, max: number) {
+ return Math.floor(Math.random() * (max - min)) + min;
+}
+
+const authors = ['Яндекс', 'Google'];
+
+export function rndAuthor() {
+ return authors[getRandomInt(0, authors.length)];
+}
+
+export const themes = new Map();
+themes.set('Яндекс', ['Яндекс.Технологии', 'Яндекс.Музыка', 'Яндекс.Дзен']);
+themes.set('Google', ['Google Play', 'GDrive']);
+
+export const contents = new Map();
+
+contents.set('Яндекс.Технологии', 'Мы расскажем вам про Яндекс.Технологии');
+contents.set('Яндекс.Музыка', 'Подписывайтесь на Яндекс.Музыку');
+contents.set('Яндекс.Дзен', 'Умная лента от Яндекс позволит Вам оставаться в курсе новостей');
+contents.set('Google Play', 'Would you like to know more about Google Play?');
+contents.set('GDrive', 'Here is a simple tutorial to get started with GDrive');
+contents.set(
+ 'Доступ к аккаунту восстановлен',
+ 'Поздравляем! Доступ к аккаунту наконец-то восстановлен'
+);
+contents.set(
+ 'Как читать почту с мобильного',
+ 'Для начала нужно установить мобильное приложение Яндекс.Почты, затем...'
+);
+contents.set('Соберите всю почту в этот ящик', 'Чем больше человечество использует интернет');
+
+export function rndTheme(author: string) {
+ const concreteThemes = themes.get(author);
+ if (concreteThemes === undefined) {
+ return 'none';
+ }
+ return concreteThemes[getRandomInt(0, concreteThemes.length)];
+}
diff --git a/src/letters/letters.tsx b/src/letters/letters.tsx
new file mode 100644
index 0000000..b073a1c
--- /dev/null
+++ b/src/letters/letters.tsx
@@ -0,0 +1,152 @@
+import React, { Component } from 'react';
+
+import styles from './Letters.module.css';
+import letterStyles from './__letter/Letter.module.css';
+import LettersOpmenu from './__opmenu/letters__opmenu';
+import LettersList from './__list/letters__list';
+import LettersArticle from './__article/letters__article';
+import LettersFooter from './__footer/letters__footer';
+import { getRandomInt, rndAuthor, rndTheme, contents } from './content';
+import yandexOval from '../images/Oval-9.png';
+import ya from '../images/Fill-154.png';
+import { ILetter } from './__letter/letters__letter';
+
+interface IProps {
+ theme: string;
+ filter: string;
+ letters: ILetter[];
+ deleteOnclick(): void;
+ handleCheckbox(event: React.ChangeEvent, number: number): void;
+ setFilterProcessingDisplay(display: boolean): void;
+ selectAll(checkbox: React.ChangeEvent): void;
+}
+
+interface IState {
+ articleHeader: string;
+ articleContent: string;
+ mode: string;
+}
+
+export default class Letters extends Component {
+ public static createYandexAuthorImage() {
+ return (
+
+
+
+
+
+
+
+
+ );
+ }
+
+ public constructor(props: IProps) {
+ super(props);
+ this.state = {
+ articleHeader: 'Header',
+ articleContent: 'Content',
+ mode: 'letters'
+ };
+ this.open = this.open.bind(this);
+ this.close = this.close.bind(this);
+ }
+ //
+ // public shouldComponentUpdate(
+ // nextProps: Readonly,
+ // nextState: Readonly,
+ // nextContext: any
+ // ): boolean {
+ //
+ // boolean
+ //
+ // return (
+ // this.props.filter !== nextProps.filter ||
+ // this.props.theme !== nextProps.theme ||
+ // this.state !== nextState ||
+ // this.props.letters !== nextProps.letters
+ // );
+ // }
+ //
+ // public componentDidUpdate(
+ // prevProps: Readonly,
+ // prevState: Readonly,
+ // snapshot?: any
+ // ): void {
+ // this.props.setFilterProcessingDisplay(false);
+ // }
+
+ private createView() {
+ if (this.state.mode === 'article') {
+ return (
+
+ );
+ }
+ return (
+
+ );
+ }
+
+ public open(number: number): void {
+ const articleHeader = this.props.letters[number].theme;
+ const articleContentOrUndefined = contents.get(articleHeader);
+ let articleContent = 'none';
+ if (articleContentOrUndefined !== undefined) {
+ articleContent = articleContentOrUndefined;
+ }
+ this.props.letters[number].read = true;
+ this.setState({
+ articleHeader,
+ articleContent,
+ mode: 'article'
+ });
+ }
+
+ private close() {
+ this.setState({
+ mode: 'letters'
+ });
+ }
+
+ private getTheme() {
+ if (this.props.theme === 'light') {
+ return styles.light;
+ }
+ return styles.dark;
+ }
+
+ private lineTheme() {
+ if (this.props.theme === 'light') {
+ return letterStyles.lineLight;
+ }
+ return letterStyles.lineDark;
+ }
+
+ public render() {
+ return (
+
+
+
+
+ {this.createView()}
+
+
+
+ );
+ }
+}
diff --git a/src/logo/Logo.module.css b/src/logo/Logo.module.css
new file mode 100644
index 0000000..0f05ffd
--- /dev/null
+++ b/src/logo/Logo.module.css
@@ -0,0 +1,7 @@
+.logo {
+ position: absolute;
+ margin-top: 12px;
+ margin-left: 22px;
+ width: 184px;
+ height: 31px;
+}
diff --git a/src/logo/logo.tsx b/src/logo/logo.tsx
new file mode 100644
index 0000000..70ec0db
--- /dev/null
+++ b/src/logo/logo.tsx
@@ -0,0 +1,20 @@
+import React, { Component } from 'react';
+
+import styles from './Logo.module.css';
+import ThreeLines from '../three-lines/three-lines';
+import Name from '../name/name';
+
+interface IProps {
+ theme: string;
+}
+
+export default class Logo extends Component {
+ public render() {
+ return (
+
+
+
+
+ );
+ }
+}
diff --git a/src/menu/Menu.module.css b/src/menu/Menu.module.css
new file mode 100644
index 0000000..98ee85f
--- /dev/null
+++ b/src/menu/Menu.module.css
@@ -0,0 +1,67 @@
+.menuSection {
+ position: absolute;
+
+ width: 147px;
+ height: 126px;
+ margin-top: 96px;
+ margin-left: 22px;
+}
+
+.highlighted {
+ position: absolute;
+ z-index: 1;
+
+ width: 147px;
+ height: 22px;
+ margin-top: 5px;
+ margin-left: 0;
+ border-radius: 3px;
+}
+
+.menuItems {
+ position: absolute;
+ z-index: 2;
+
+ width: inherit;
+ height: inherit;
+}
+
+.menuItem {
+ z-index: 2;
+
+ max-width: 147px;
+ max-height: 14px;
+ margin-left: 10px;
+ font-family: 'Helvetica Neue', sans-serif;
+ font-size: 11px;
+ font-weight: 500;
+}
+
+.highlightedText {
+ z-index: 2;
+ font-weight: 700;
+}
+
+.dark {
+ color: #ddd;
+}
+
+.light {
+ color: #707070;
+}
+
+.highlightedDark {
+ background-color: #1e3543;
+}
+
+.highlightedLight {
+ background-color: #cdd6e4;
+}
+
+.highlightedTextLight {
+ color: #555;
+}
+
+.highlightedTextDark {
+ color: #ccc;
+}
diff --git a/src/menu/menu.tsx b/src/menu/menu.tsx
new file mode 100644
index 0000000..13af70d
--- /dev/null
+++ b/src/menu/menu.tsx
@@ -0,0 +1,59 @@
+import React, { Component } from 'react';
+
+import styles from './Menu.module.css';
+
+interface IProps {
+ theme: string;
+}
+
+export default class Menu extends Component {
+ private getTheme() {
+ if (this.props.theme === 'light') {
+ return styles.light;
+ }
+ return styles.dark;
+ }
+
+ private getHighlightedTheme() {
+ if (this.props.theme === 'light') {
+ return styles.highlightedLight;
+ }
+ return styles.highlightedDark;
+ }
+
+ private getHighlightedTextTheme() {
+ if (this.props.theme === 'light') {
+ return styles.highlightedTextLight;
+ }
+ return styles.highlightedTextDark;
+ }
+
+ public render() {
+ return (
+
+
+
+
+
+ Входящие
+
+
+ Отправленные
+
+
+ Удаленные
+
+
+ Спам
+
+
+ Черновики
+
+
+ Создать папку
+
+
+
+ );
+ }
+}
diff --git a/src/name/Name.module.css b/src/name/Name.module.css
new file mode 100644
index 0000000..7a2d4bb
--- /dev/null
+++ b/src/name/Name.module.css
@@ -0,0 +1,16 @@
+.name {
+ position: absolute;
+
+ width: 153px;
+ height: 31px;
+ margin-left: 53px;
+
+ color: #010101;
+
+ font-size: 26px;
+}
+
+
+.dark {
+ background-color: #999;
+}
diff --git a/src/name/name.tsx b/src/name/name.tsx
new file mode 100644
index 0000000..5fde324
--- /dev/null
+++ b/src/name/name.tsx
@@ -0,0 +1,25 @@
+import React, { Component } from 'react';
+
+import styles from './Name.module.css';
+import shape from '../images/Shape.png';
+
+interface IProps {
+ theme: string;
+}
+
+export default class Name extends Component {
+ private getTheme() {
+ if (this.props.theme === 'light') {
+ return styles.light;
+ }
+ return styles.dark;
+ }
+
+ public render() {
+ return (
+
+

+
+ );
+ }
+}
diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts
new file mode 100644
index 0000000..6431bc5
--- /dev/null
+++ b/src/react-app-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/src/search/Search.module.css b/src/search/Search.module.css
new file mode 100644
index 0000000..863f0dc
--- /dev/null
+++ b/src/search/Search.module.css
@@ -0,0 +1,34 @@
+.search {
+ position: absolute;
+
+ width: 301px;
+ height: 26px;
+ margin-top: 11px;
+
+ margin-left: 320px;
+ background-color: #fff;
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2);
+}
+
+
+.loader {
+ position: absolute;
+ margin-left: 250px;
+ margin-top: 8px;
+ border: 1.5px solid #ccc; /* Light grey */
+ border-top: 1.5px solid #3498db; /* Blue */
+ border-radius: 50%;
+ width: 12px;
+ height: 12px;
+ animation: spin 2s linear infinite;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+.none {
+ position: absolute;
+ display: none;
+}
diff --git a/src/search/__cross/Cross.module.css b/src/search/__cross/Cross.module.css
new file mode 100644
index 0000000..6266e0a
--- /dev/null
+++ b/src/search/__cross/Cross.module.css
@@ -0,0 +1,6 @@
+.cross {
+ position: absolute;
+
+ margin-top: 5px;
+ margin-left: 280px;
+}
diff --git a/src/search/__cross/search__cross.tsx b/src/search/__cross/search__cross.tsx
new file mode 100644
index 0000000..3a23f94
--- /dev/null
+++ b/src/search/__cross/search__cross.tsx
@@ -0,0 +1,18 @@
+import React, { Component } from 'react';
+
+import styles from './Cross.module.css';
+import cross from '../../images/cross.png';
+
+interface IProps {
+ removeSearchTextInput(event: React.MouseEvent): void;
+}
+
+export default class SearchCross extends Component {
+ public render() {
+ return (
+
+
+
+ );
+ }
+}
diff --git a/src/search/__suggestion/Suggestion.module.css b/src/search/__suggestion/Suggestion.module.css
new file mode 100644
index 0000000..c96268d
--- /dev/null
+++ b/src/search/__suggestion/Suggestion.module.css
@@ -0,0 +1,21 @@
+.suggestion {
+ position: absolute;
+
+ width: inherit;
+ height: inherit;
+
+ font-family: 'Yandex Sans Regular', sans-serif;
+ font-size: 15px;
+ font-weight: 400;
+}
+
+
+.dark {
+ color: #aaa;
+ background-color: #1e3543;
+ border: 1px solid #555555;
+}
+
+.light {
+ color: #707070;
+}
diff --git a/src/search/__suggestion/search__suggestion.tsx b/src/search/__suggestion/search__suggestion.tsx
new file mode 100644
index 0000000..841fcdf
--- /dev/null
+++ b/src/search/__suggestion/search__suggestion.tsx
@@ -0,0 +1,28 @@
+import React, { Component } from 'react';
+
+import styles from './Suggestion.module.css';
+
+interface IProps {
+ theme: string;
+ handleFilterChange(event: React.ChangeEvent): void;
+}
+
+export default class SearchSuggestion extends Component {
+ private getTheme() {
+ if (this.props.theme === 'light') {
+ return styles.light;
+ }
+ return styles.dark;
+ }
+
+ public render() {
+ return (
+
+ );
+ }
+}
diff --git a/src/search/search.tsx b/src/search/search.tsx
new file mode 100644
index 0000000..027da4d
--- /dev/null
+++ b/src/search/search.tsx
@@ -0,0 +1,41 @@
+import React, { Component } from 'react';
+
+import styles from './Search.module.css';
+import SearchSuggestion from './__suggestion/search__suggestion';
+import SearchCross from './__cross/search__cross';
+
+interface IProps {
+ theme: string;
+ handleFilterChange(event: React.ChangeEvent): void;
+ removeSearchTextInput(event: React.MouseEvent): void;
+ display: boolean;
+}
+
+export default class Search extends Component {
+ private getTheme() {
+ if (this.props.theme === 'light') {
+ return styles.light;
+ }
+ return styles.dark;
+ }
+
+ private createLoader() {
+ if (this.props.display) {
+ return ;
+ }
+ return ;
+ }
+
+ public render() {
+ return (
+
+
+ {this.createLoader()}
+
+
+ );
+ }
+}
diff --git a/src/theme-switch/ThemeSwitch.module.css b/src/theme-switch/ThemeSwitch.module.css
new file mode 100644
index 0000000..7901bc4
--- /dev/null
+++ b/src/theme-switch/ThemeSwitch.module.css
@@ -0,0 +1,69 @@
+:root {
+ --toggleActiveColor: #777;
+}
+
+/* The switch - the box around the slider */
+.switch {
+ position: absolute;
+ display: inline-block;
+ width: 50px;
+ height: 19px;
+
+ margin-left: 700px;
+ margin-top: 20px;
+}
+
+/* Hide default HTML checkbox */
+.switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+/* The slider */
+.slider {
+ position: absolute;
+ cursor: pointer;
+ top: -7px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #ccc;
+ -webkit-transition: 0.4s;
+ transition: 0.4s;
+}
+
+.slider:before {
+ position: absolute;
+ content: '';
+ height: 19px;
+ width: 19px;
+ left: 4px;
+ bottom: 4px;
+ background-color: white;
+ -webkit-transition: 0.4s;
+ transition: 0.4s;
+}
+
+input:checked + .slider {
+ background-color: var(--toggleActiveColor);
+}
+
+input:focus + .slider {
+ box-shadow: 0 0 1px var(--toggleActiveColor);
+}
+
+input:checked + .slider:before {
+ -webkit-transform: translateX(23px);
+ -ms-transform: translateX(23px);
+ transform: translateX(23px);
+}
+
+/* Rounded sliders */
+.slider.round {
+ border-radius: 19px;
+}
+
+.slider.round:before {
+ border-radius: 50%;
+}
diff --git a/src/theme-switch/theme-switch.tsx b/src/theme-switch/theme-switch.tsx
new file mode 100644
index 0000000..9aa3ee5
--- /dev/null
+++ b/src/theme-switch/theme-switch.tsx
@@ -0,0 +1,18 @@
+import React, { Component } from 'react';
+
+import styles from './ThemeSwitch.module.css';
+
+interface IProps {
+ changeTheme(checkbox: React.ChangeEvent
): void;
+}
+
+export default class ThemeSwitch extends Component {
+ public render() {
+ return (
+
+ );
+ }
+}
diff --git a/src/three-lines/ThreeLines.module.css b/src/three-lines/ThreeLines.module.css
new file mode 100644
index 0000000..fbae5a7
--- /dev/null
+++ b/src/three-lines/ThreeLines.module.css
@@ -0,0 +1,28 @@
+.lines {
+ position: absolute;
+
+ margin-right: 5px;
+ margin-top: 5px;
+
+ width: 20px;
+ height: 31px;
+}
+
+.line {
+ width: inherit;
+ height: 3px;
+ margin-top: 6px;
+ background-color: #000;
+}
+
+.first {
+ margin-top: 0;
+}
+
+.light {
+ background-color: #000;
+}
+
+.dark {
+ background-color: #eee;
+}
diff --git a/src/three-lines/three-lines.tsx b/src/three-lines/three-lines.tsx
new file mode 100644
index 0000000..8071ae1
--- /dev/null
+++ b/src/three-lines/three-lines.tsx
@@ -0,0 +1,28 @@
+import React, { Component } from 'react';
+
+import styles from './ThreeLines.module.css';
+
+interface IProps {
+ theme: string;
+}
+
+export default class ThreeLines extends Component {
+ private getTheme() {
+ if (this.props.theme === 'light') {
+ return styles.light;
+ }
+ return styles.dark;
+ }
+
+ public render() {
+ return (
+
+ );
+ }
+}
diff --git a/src/writebox/Writebox.module.css b/src/writebox/Writebox.module.css
new file mode 100644
index 0000000..2de5040
--- /dev/null
+++ b/src/writebox/Writebox.module.css
@@ -0,0 +1,34 @@
+.writebox {
+ position: absolute;
+
+ width: 147px;
+ height: 32px;
+ margin-top: 56px;
+ margin-left: 22px;
+
+ border-radius: 3px;
+}
+
+.dark {
+ background-color: #708090;
+}
+
+.light {
+ background-color: #6287bd;
+}
+
+.write {
+ position: absolute;
+
+ overflow: hidden;
+
+ max-width: 60px;
+ max-height: 11px;
+ margin-top: 9px;
+ margin-left: 46px;
+
+ color: #fff;
+ font-family: 'Helvetica Neue Medium', sans-serif;
+ font-size: 12px;
+ font-weight: 500;
+}
diff --git a/src/writebox/writebox.tsx b/src/writebox/writebox.tsx
new file mode 100644
index 0000000..9e195c1
--- /dev/null
+++ b/src/writebox/writebox.tsx
@@ -0,0 +1,25 @@
+import React, { Component } from 'react';
+
+import styles from './Writebox.module.css';
+
+interface IProps {
+ theme: string;
+}
+
+export default class Writebox extends Component {
+ private getTheme() {
+ if (this.props.theme === 'light') {
+ return styles.light;
+ }
+ return styles.dark;
+ }
+
+
+ public render() {
+ return (
+
+ );
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..0980b23
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "preserve"
+ },
+ "include": [
+ "src"
+ ]
+}