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 ( -
-
-

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

- - Learn React - -
-
- ); - } -} 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 ( +
+ cross +
+ ); + } +} 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 ( +
+ unread +
+ ); + } + + 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 ( +
+
+ Yandex +
+
+ Yandex +
+
+ ); + } + + 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 ( +
+ Yandex.Mail +
+ ); + } +} 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 ( +
+ Cross +
+ ); + } +} 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" + ] +}