diff --git a/package.json b/package.json index 9ede333..046816d 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,13 @@ "not op_mini all" ], "dependencies": { + "@types/jest": "24.0.12", + "@types/node": "11.13.8", + "@types/react": "16.8.15", + "@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..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.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..ea8a15d --- /dev/null +++ b/src/app/app.tsx @@ -0,0 +1,9 @@ +import * as React from 'react'; + +import { Page } from './page/page'; + +export class App extends React.Component { + public render() { + return ; + } +} diff --git a/src/app/content/content.module.css b/src/app/content/content.module.css new file mode 100644 index 0000000..4facc39 --- /dev/null +++ b/src/app/content/content.module.css @@ -0,0 +1,37 @@ +.content { + display: inline-block; + width: calc(100% - 226px); + min-width: 574px; + min-height: 492px; + margin-right: 20px; + background-color: #ffffff; + border-radius: 3px; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.34); + font-family: HelveticaNeue, serif; +} + +.contentDark { + background-color: #555555; + border-radius: 3px; + box-shadow: 0 0 3px 3px #777777; + composes: content; +} + +.myCheckbox { + width: 16px; + height: 16px; + margin-top: 11px; + margin-left: 20px; + border: solid 1px rgba(0, 0, 0, 0.15); + background-color: #ffffff; + border-radius: 3px; + composes: myInput from '../page/page.module.css'; +} + +.myCheckbox:checked { + background-clip: content-box; + background-color: white; + background-image: url('../../images/check.png'); + background-size: 14px; + background-repeat: no-repeat; +} diff --git a/src/app/content/content.module.css.d.ts b/src/app/content/content.module.css.d.ts new file mode 100644 index 0000000..6e2d3a5 --- /dev/null +++ b/src/app/content/content.module.css.d.ts @@ -0,0 +1,3 @@ +export const content: string; +export const contentDark: string; +export const myCheckbox: string; diff --git a/src/app/content/content.tsx b/src/app/content/content.tsx new file mode 100644 index 0000000..cad0df1 --- /dev/null +++ b/src/app/content/content.tsx @@ -0,0 +1,104 @@ +import React from 'react'; + +import * as styles from './content.module.css'; +import * as pageStyles from '../page/page.module.css'; + +import { MessageMenu } from '../messageMenu/messageMenu'; +import { Letters } from '../letters/letters'; +import { Letter } from '../letter/letter'; +import { Footer } from '../footer/footer'; +import { ILetterType } from '../types/types'; + +interface IContentProps { + deleteMails: () => void; + letters: ILetterType[]; + selectAll: () => void; + isSelectAll: boolean; + checkboxChange: (id: string) => void; + checked: { [id: string]: boolean }; + text: string[]; + setText: (text: string[]) => void; + setRead: (id: string) => void; + removeAddAnimation: (id: string) => void; + removeDeleteAnimation: (id: string) => void; + theme: boolean; + searchText: string; +} + +interface IContentState { + letterIsVisible: boolean; +} + +export class Content extends React.Component { + public constructor(props: IContentProps) { + super(props); + + this.state = { + letterIsVisible: false, + }; + + this.showLetter = this.showLetter.bind(this); + this.closeLetter = this.closeLetter.bind(this); + this.getContentClass = this.getContentClass.bind(this); + this.getLineClass = this.getLineClass.bind(this); + } + + private showLetter() { + this.setState({ + letterIsVisible: true + }); + } + + private closeLetter() { + this.setState({ + letterIsVisible: false + }); + } + + private getContentClass() { + return !this.props.theme ? styles.content : styles.contentDark; + } + + private getLineClass() { + return !this.props.theme ? pageStyles.line : pageStyles.lineDark; + } + + public render() { + return ( +
+ + +
+ + + +
+
+ ); + } +} diff --git a/src/app/footer/footer.module.css b/src/app/footer/footer.module.css new file mode 100644 index 0000000..8d80f9f --- /dev/null +++ b/src/app/footer/footer.module.css @@ -0,0 +1,39 @@ +.footer { + width: 100%; + min-width: 574px; + + border-top: solid 2px #e2e2e2; + font-family: HelveticaNeue, serif; +} + +.footerDark { + border-top: solid 2px #222222; + composes: footer; +} + +.menuText { + font-size: 11px; + font-weight: normal; +} + +.menuLink { + composes: menuText; + display: inline-block; + margin-right: 20px; +} + +.menu { + padding: 0; + margin: 0; + float: right; + list-style-type: none; +} + +.link { + color: #bbbbbb; + composes: myLink from '../page/page.module.css'; +} + +.myCopy { + color: #bbbbbb; +} diff --git a/src/app/footer/footer.module.css.d.ts b/src/app/footer/footer.module.css.d.ts new file mode 100644 index 0000000..5a3c232 --- /dev/null +++ b/src/app/footer/footer.module.css.d.ts @@ -0,0 +1,7 @@ +export const footer: string; +export const menuLink: string; +export const menuText: string; +export const menu: string; +export const link: string; +export const myCopy: string; +export const footerDark: string; diff --git a/src/app/footer/footer.tsx b/src/app/footer/footer.tsx new file mode 100644 index 0000000..263a360 --- /dev/null +++ b/src/app/footer/footer.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; + +import * as styles from './footer.module.css'; + +interface IFooterProps { + theme: boolean; +} + +export class Footer extends React.Component { + public constructor(props: IFooterProps) { + super(props); + + this.getFooterClass = this.getFooterClass.bind(this); + } + + private getFooterClass() { + return !this.props.theme ? styles.footer : styles.footerDark; + } + + public render() { + return ( + + ); + } +} diff --git a/src/app/header/header.module.css b/src/app/header/header.module.css new file mode 100644 index 0000000..21fc43c --- /dev/null +++ b/src/app/header/header.module.css @@ -0,0 +1,181 @@ +.title { + display: inline-block; + width: 153px; + height: 31px; + margin-top: 12px; + margin-left: 11px; +} + +.logo { + height: 31px; +} + +.menu { + display: inline-block; + margin-top: 14px; + margin-left: 22px; + vertical-align: top; +} + +.navigation { + display: inline-block; + width: 181px; + margin-left: 19px; + font-family: HelveticaNeue, serif; + vertical-align: top; +} + +.header { + height: 56px; +} + +.rectangle { + width: 20px; + height: 2px; + margin-top: 5px; + background-color: #000000; +} + +.rectangleDark { + background-color: #ffffff; + composes: rectangle; +} + +.search { + color: white; + display: inline-block; + width: calc(100% - 499px); + min-width: 301px; + height: 32px; + margin-top: 11px; + margin-left: 114px; + background-color: #ffffff; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2); + opacity: 0.5; + vertical-align: top; +} + +.searchDark { + background-color: #555555; + box-shadow: inset 0 0 0 1px #333333; + opacity: 1; + composes: search; +} + +.searchInput { + display: inline-block; + width: calc(100% - 25px); + min-width: 276px; + height: 32px; + padding-left: 16px; + + border-style: none; + background: none; + color: #000000; + font-family: Yandex Sans, serif; + font-size: 15px; + line-height: 2.13; + outline: none; + vertical-align: middle; +} + +.searchInputDark { + color: #ffffff; + composes: searchInput; +} + +.searchInput::-webkit-search-cancel-button { + -webkit-appearance: none; +} + +.searchInput::-webkit-input-placeholder { + color: black; +} + +.searchInputDark::-webkit-search-cancel-button { + -webkit-appearance: none; +} + +.searchInputDark::-webkit-input-placeholder { + color: white; +} + +.searchCancelButton { + display: inline-block; + width: 9px; + height: 9px; + + border-style: none; + -webkit-appearance: none; + background: url(../../images/cross-symbol.png) no-repeat; + background-size: 9px; + cursor: pointer; + outline: none; + vertical-align: middle; +} + +.searchCancelButtonDark { + composes: searchCancelButton; + background: url(../../images/cross-symbol-dark.png) no-repeat; + background-size: 9px; +} + +.changeThemeButton { + width: 80px; + height: 32px; + + border: none; + -webkit-appearance: none; + border-radius: 3px; + cursor: pointer; + background-color: #333333; + outline: none; + display: inline-block; + vertical-align: top; + margin-top: 11px; + float: right; + margin-right: 26px; +} + +.changeThemeButtonText { + display: inline; + padding-top: 7px; + color: #ffffff; + font-size: 10px; + font-weight: 500; + text-align: center; +} + +.changeThemeButtonDark { + background-color: #6287bd; + composes: changeThemeButton; +} + +.spinner { + width: 100px; + height: 4px; + animation: move 2s linear infinite; + background-color: black; +} + +.spinnerDark { + background-color: white; + composes: spinner; +} + +.spinnerHidden { + display: none; +} + + +@keyframes move { + 0% { + margin-left: 0; + } + 50% { + margin-left: calc(100% - 100px); + } + 100% { + margin-left: 0; + } +} diff --git a/src/app/header/header.module.css.d.ts b/src/app/header/header.module.css.d.ts new file mode 100644 index 0000000..5f02a71 --- /dev/null +++ b/src/app/header/header.module.css.d.ts @@ -0,0 +1,19 @@ +export const title: string; +export const logo: string; +export const menu: string; +export const navigation: string; +export const header: string; +export const rectangle: string; +export const search: string; +export const searchInput: string; +export const searchCancelButton: string; +export const changeThemeButton: string; +export const changeThemeButtonText: string; +export const rectangleDark: string; +export const searchDark: string; +export const searchInputDark: string; +export const changeThemeButtonDark: string; +export const searchCancelButtonDark: string; +export const spinner: string; +export const spinnerDark: string; +export const spinnerHidden: string; diff --git a/src/app/header/header.tsx b/src/app/header/header.tsx new file mode 100644 index 0000000..ade11d1 --- /dev/null +++ b/src/app/header/header.tsx @@ -0,0 +1,109 @@ +import React from 'react'; + +import * as styles from './header.module.css'; + +const headerMail = require('../../images/header-mail.svg'); +const headerYandex = require('../../images/header-yandex.svg'); + +const headerMailDark = require('../../images/header-mail-dark.svg'); +const headerYandexDark = require('../../images/header-yandex-dark.svg'); + +interface IHeaderProps { + setSearchText: (text: string) => void; + changeTheme: () => void; + theme: boolean; + isSearch: boolean; +} + +export class Header extends React.Component { + public constructor(props: IHeaderProps) { + super(props); + + this.search = this.search.bind(this); + this.getHeaderYandex = this.getHeaderYandex.bind(this); + this.getHeaderMail = this.getHeaderMail.bind(this); + this.getRectangleClass = this.getRectangleClass.bind(this); + this.getSearchClass = this.getSearchClass.bind(this); + this.getSearchInputClass = this.getSearchInputClass.bind(this); + this.getSearchCancelButtonClass = this.getSearchCancelButtonClass.bind(this); + this.getChangeThemeButtonClass = this.getChangeThemeButtonClass.bind(this); + this.getSpinnerClass = this.getSpinnerClass.bind(this); + } + + private getHeaderYandex() { + return !this.props.theme ? headerYandex : headerYandexDark; + } + + private getHeaderMail() { + return !this.props.theme ? headerMail : headerMailDark; + } + + private getRectangleClass() { + return !this.props.theme ? styles.rectangle : styles.rectangleDark; + } + + private getSearchClass() { + return !this.props.theme ? styles.search : styles.searchDark; + } + + private getSearchInputClass() { + return !this.props.theme ? styles.searchInput : styles.searchInputDark; + } + + private getSearchCancelButtonClass() { + return !this.props.theme ? styles.searchCancelButton : styles.searchCancelButtonDark; + } + + private getChangeThemeButtonClass() { + return !this.props.theme ? styles.changeThemeButton : styles.changeThemeButtonDark; + } + + private search(event: React.ChangeEvent) { + this.props.setSearchText(event.target.value); + } + + private getSpinnerClass() { + if (this.props.isSearch) { + if (!this.props.theme) { + return styles.spinner; + } else { + return styles.spinnerDark; + } + } else { + return styles.spinnerHidden; + } + } + + public render() { + return ( +
+
+
+
+
+
+
+ logo + logo +
+
+ + +
+ ); + } +} diff --git a/src/app/letter/letter.module.css b/src/app/letter/letter.module.css new file mode 100644 index 0000000..f298aa1 --- /dev/null +++ b/src/app/letter/letter.module.css @@ -0,0 +1,44 @@ +.letter { + width: 100%; + min-width: 574px; + min-height: 415px; +} + +.hidden { + display: none; +} + +.closeImg { + display: inline-block; + width: 9px; + height: 9px; + margin-top: 15px; + margin-left: 10px; + opacity: 0.15; + vertical-align: top; +} + +.myArticle { + display: inline-block; + width: calc(100% - 50px); + min-width: 520px; + margin-left: 20px; + font-size: 13px; +} + +.text { + font-family: Arial, serif; + color: #000000; +} + +.textDark { + color: #ffffff; + composes: text; +} + +.image { + width: 140px; + margin-right: 1px; + float: left; + shape-outside: circle(50%); +} diff --git a/src/app/letter/letter.module.css.d.ts b/src/app/letter/letter.module.css.d.ts new file mode 100644 index 0000000..298dfae --- /dev/null +++ b/src/app/letter/letter.module.css.d.ts @@ -0,0 +1,7 @@ +export const letter: string; +export const closeImg: string; +export const myArticle: string; +export const image: string; +export const text: string; +export const hidden: string; +export const textDark: string; diff --git a/src/app/letter/letter.tsx b/src/app/letter/letter.tsx new file mode 100644 index 0000000..191f048 --- /dev/null +++ b/src/app/letter/letter.tsx @@ -0,0 +1,55 @@ +import * as React from 'react'; + +import * as styles from './letter.module.css'; + +interface ILetterProps { + text: string[]; + display: boolean; + closeLetter: () => void; + theme: boolean; +} + +const close: string = require('../../images/cross-symbol.png'); +const closeDark: string = require('../../images/cross-symbol-dark.png'); + +export class Letter extends React.Component { + public constructor(props: ILetterProps) { + super(props); + + this.makeClassName = this.makeClassName.bind(this); + this.getTextClass = this.getTextClass.bind(this); + this.getCloseImg = this.getCloseImg.bind(this); + } + + private getTextClass() { + return !this.props.theme ? styles.text : styles.textDark; + } + + private getCloseImg() { + return !this.props.theme ? close : closeDark; + } + + private readonly makeClassName = () => { + return this.props.display ? styles.letter : styles.hidden; + }; + + public render(): React.ReactNode { + const letter = []; + for (let i = 0; i < this.props.text.length; i++) { + letter.push(

{this.props.text[i]}

); + } + return ( + + ); + } +} diff --git a/src/app/letterHead/letterHead.module.css b/src/app/letterHead/letterHead.module.css new file mode 100644 index 0000000..43b15b6 --- /dev/null +++ b/src/app/letterHead/letterHead.module.css @@ -0,0 +1,145 @@ +.myCheckbox { + width: 16px; + height: 16px; + margin-top: 11px; + margin-left: 20px; + border: solid 1px rgba(0, 0, 0, 0.15); + background-color: #ffffff; + border-radius: 3px; + composes: myInput from '../page/page.module.css'; +} + +.myCheckbox:checked { + background-clip: content-box; + background-color: white; + background-image: url('../../images/check.png'); + background-size: 14px; + background-repeat: no-repeat; +} + +.letterHead { + min-width: 574px; + height: 40px; +} + +.hidden { + display: none; +} + +.link { + outline: none; + text-decoration: none; +} +.unread { + font-weight: bold; + composes: link; +} + +.authorImage { + display: inline-block; + width: 30px; + height: 30px; + margin-top: 5px; + margin-left: 10px; + border-radius: 50%; + vertical-align: top; +} + +.authorName { + display: inline-block; + width: 160px; + height: 40px; + margin-left: 10px; + color: #000000; + font-size: 13px; + vertical-align: top; +} + +.authorNameDark { + composes: authorName; + color: #ffffff; +} + +.read { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 50%; + vertical-align: super; +} + +.unread > .read { + background-color: #6287bd; +} + +.unreadDark { + composes: unread; +} + +.unreadDark > .read { + background-color: #cccccc; +} + +.text { + display: inline-block; + overflow: hidden; + width: calc(100% - 322px); + min-width: 240px; + height: 40px; + margin-left: 10px; + color: #000000; + font-size: 13px; + text-overflow: ellipsis; + vertical-align: top; + composes: myText from '../page/page.module.css'; +} + +.textDark { + color: #ffffff; + composes: text; +} + +.date { + display: inline-block; + height: 40px; + margin-right: 10px; + color: #9b9b9b; + font-size: 13px; + font-weight: normal; + text-align: right; + vertical-align: bottom; +} + + +.dateDark { + color: #bbbbbb; + composes: date; +} + +.animatedAddLine { + animation: add 1.5s linear; + composes: letterHead; +} + +.animatedDeleteLine { + animation: delete 1.5s linear; + composes: letterHead; +} + +@keyframes add { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes delete { + from { + opacity: 1; + } + to { + opacity: 0; + } +} diff --git a/src/app/letterHead/letterHead.module.css.d.ts b/src/app/letterHead/letterHead.module.css.d.ts new file mode 100644 index 0000000..dd53404 --- /dev/null +++ b/src/app/letterHead/letterHead.module.css.d.ts @@ -0,0 +1,16 @@ +export const unread: string; +export const myCheckbox: string; +export const letterHead: string; +export const link: string; +export const authorImage: string; +export const authorName: string; +export const read: string; +export const date: string; +export const animatedAddLine: string; +export const animatedDeleteLine: string; +export const text: string; +export const hidden: string; +export const authorNameDark: string; +export const unreadDark: string; +export const textDark: string; +export const dateDark: string; diff --git a/src/app/letterHead/letterHead.tsx b/src/app/letterHead/letterHead.tsx new file mode 100644 index 0000000..eb71811 --- /dev/null +++ b/src/app/letterHead/letterHead.tsx @@ -0,0 +1,131 @@ +import * as React from 'react'; + +import * as styles from './letterHead.module.css'; +import * as pageStyles from '../page/page.module.css'; + +interface ILetterHeadProps { + id: string; + authorName: string; + authorImage: string; + letterText: string[]; + headText: string; + isVisible: boolean; + isChecked: boolean; + checkboxChange: (id: string) => void; + setText: (text: string[]) => void; + addAnimation: boolean; + removeAddAnimation: (id: string) => void; + removeDeleteAnimation: (id: string) => void; + deleteAnimation: boolean; + isRead: boolean; + setRead: (id: string) => void; + showLetter: () => void; + headTagDate: string; + headDate: string; + theme: boolean; +} + +export class LetterHead extends React.Component { + public constructor(props: ILetterHeadProps) { + super(props); + + this.makeClassName = this.makeClassName.bind(this); + this.makeLinkClassName = this.makeLinkClassName.bind(this); + this.deleteAnimation = this.deleteAnimation.bind(this); + this.getAuthorNameClass = this.getAuthorNameClass.bind(this); + this.getTextClass = this.getTextClass.bind(this); + this.getDateClass = this.getDateClass.bind(this); + this.getLineClass = this.getLineClass.bind(this); + } + + private makeClassName() { + if (this.props.addAnimation) { + return styles.animatedAddLine; + } + + if (this.props.deleteAnimation) { + return styles.animatedDeleteLine; + } + + if (!this.props.isVisible) { + return styles.hidden; + } + + return styles.letterHead; + } + + private makeLinkClassName() { + if (this.props.isRead) { + if (!this.props.theme) { + return styles.unread; + } + return styles.unreadDark; + } + return styles.link; + } + + private deleteAnimation() { + if (this.props.addAnimation) { + this.props.removeAddAnimation(this.props.id); + } + if (this.props.deleteAnimation) { + this.props.removeDeleteAnimation(this.props.id); + } + } + + private getAuthorNameClass() { + return !this.props.theme ? styles.authorName : styles.authorNameDark; + } + + private getTextClass() { + return !this.props.theme ? styles.text : styles.textDark; + } + + private getDateClass() { + return !this.props.theme ? styles.date : styles.dateDark; + } + + private getLineClass() { + return !this.props.theme ? pageStyles.line : pageStyles.lineDark; + } + + public render() { + return ( +
  • + + { + this.props.setText(this.props.letterText); + this.props.showLetter(); + this.props.setRead(this.props.id); + }} + > + author logo +
    +

    {this.props.authorName}

    +
    +
  • + ); + } +} diff --git a/src/app/letters/letters.module.css b/src/app/letters/letters.module.css new file mode 100644 index 0000000..a953e60 --- /dev/null +++ b/src/app/letters/letters.module.css @@ -0,0 +1,12 @@ +.letters { + width: 100%; + min-height: 415px; + padding: 0; + margin: 0; + list-style-type: none; + vertical-align: top; +} + +.hidden { + display: none; +} diff --git a/src/app/letters/letters.module.css.d.ts b/src/app/letters/letters.module.css.d.ts new file mode 100644 index 0000000..87a75ba --- /dev/null +++ b/src/app/letters/letters.module.css.d.ts @@ -0,0 +1,2 @@ +export const letters: string; +export const hidden: string; diff --git a/src/app/letters/letters.tsx b/src/app/letters/letters.tsx new file mode 100644 index 0000000..1f79759 --- /dev/null +++ b/src/app/letters/letters.tsx @@ -0,0 +1,99 @@ +import * as React from 'react'; + +import * as styles from './letters.module.css'; + +import { LetterHead } from '../letterHead/letterHead'; +import { ILetterType } from '../types/types'; + +interface ILettersProps { + letters: ILetterType[]; + checkboxChange: (id: string) => void; + checked: { [id: string]: boolean }; + setText: (text: string[]) => void; + setRead: (id: string) => void; + removeAddAnimation: (id: string) => void; + removeDeleteAnimation: (id: string) => void; + display: boolean; + showLetter: () => void; + theme: boolean; + searchText: string; +} + +interface ILettersState { + worker: any; + searchFor: string; + filteredLetters: ILetterType[] | null; +} + +export class Letters extends React.Component { + public constructor(props: ILettersProps) { + super(props); + + this.state = { + worker: null, + searchFor: '', + filteredLetters: null + }; + + this.makeClassName = this.makeClassName.bind(this); + this.isLetterHasText = this.isLetterHasText.bind(this); + } + + private makeClassName() { + return this.props.display ? styles.letters : styles.hidden; + } + + private isLetterHasText = (text: string, letter: ILetterType) => { + if (text.length === 0) { + return true; + } + if ( + letter.headText.toLocaleUpperCase().indexOf(text.toLocaleUpperCase()) !== -1 || + letter.authorName.toLocaleUpperCase().indexOf(text.toLocaleUpperCase()) !== -1 + ) { + return true; + } + let f = false; + for (let i = 0; i < letter.letterText.length && !f; i++) { + if (letter.letterText[i].toLocaleUpperCase().indexOf(text.toLocaleUpperCase()) !== -1) { + f = true; + } + } + return f; + }; + + // eslint-disable-next-line react/sort-comp + private static filterLetters(letters: ILetterType[]) { + let newLetters: ILetterType[] = letters; + newLetters = newLetters.map((letter: ILetterType, index: number) => { + letter.isVisible = index < 30; + return letter; + }); + return newLetters; + } + + public render() { + return ( +
      + {this.props.letters.map((letter: ILetterType, index: number) => { + if (index < 30) { + return ( + + ); + } + })} +
    + ); + } +} diff --git a/src/app/messageMenu/messageMenu.module.css b/src/app/messageMenu/messageMenu.module.css new file mode 100644 index 0000000..cffc772 --- /dev/null +++ b/src/app/messageMenu/messageMenu.module.css @@ -0,0 +1,21 @@ +.link { + color: #cccccc; + composes: myLink from '../page/page.module.css'; +} + +.text { + display: inline-block; + height: 40px; + margin-left: 20px; + font-size: 13px; + font-weight: 500; +} + +.messageMenu { + display: inline-block; + height: 40px; + padding: 0; + margin: 0; + list-style-type: none; + vertical-align: top; +} diff --git a/src/app/messageMenu/messageMenu.module.css.d.ts b/src/app/messageMenu/messageMenu.module.css.d.ts new file mode 100644 index 0000000..7592251 --- /dev/null +++ b/src/app/messageMenu/messageMenu.module.css.d.ts @@ -0,0 +1,3 @@ +export const text: string; +export const link: string; +export const messageMenu: string; diff --git a/src/app/messageMenu/messageMenu.tsx b/src/app/messageMenu/messageMenu.tsx new file mode 100644 index 0000000..1fcda89 --- /dev/null +++ b/src/app/messageMenu/messageMenu.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; + +import * as styles from './messageMenu.module.css'; +import * as pageStyles from '../page/page.module.css'; + +interface IMessageMenuProps { + deleteMessages: () => void; +} + +export class MessageMenu extends React.Component { + public render() { + return ( + + ); + } +} diff --git a/src/app/page/nav.tsx b/src/app/page/nav.tsx new file mode 100644 index 0000000..cd07dfe --- /dev/null +++ b/src/app/page/nav.tsx @@ -0,0 +1,100 @@ +import * as React from 'react'; + +import * as pageStyles from './page.module.css'; + +interface INavProps { + newLetter: () => void; + theme: boolean; +} + +export class Nav extends React.Component { + public constructor(props: INavProps) { + super(props); + + this.getWriteButtonClass = this.getWriteButtonClass.bind(this); + this.getNavigationContentCurrentClass = this.getNavigationContentCurrentClass.bind(this); + this.getNavigationTextClass = this.getNavigationTextClass.bind(this); + this.getNavigationTextCurrentClass = this.getNavigationTextCurrentClass.bind(this); + this.getNavigationContentClass = this.getNavigationContentClass.bind(this); + } + + private getWriteButtonClass() { + return !this.props.theme + ? pageStyles.navigationWriteButton + : pageStyles.navigationWriteButtonDark; + } + + private getNavigationContentCurrentClass() { + return !this.props.theme + ? pageStyles.navigationContentCurrent + : pageStyles.navigationContentCurrentDark; + } + + private getNavigationTextClass() { + return !this.props.theme ? pageStyles.navigationText : pageStyles.navigationTextDark; + } + + private getNavigationTextCurrentClass() { + return !this.props.theme + ? pageStyles.navigationTextCurrent + : pageStyles.navigationTextCurrentDark; + } + + private getNavigationContentClass() { + return !this.props.theme ? pageStyles.navigationContent : pageStyles.navigationContentDark; + } + + public render() { + return ( + + ); + } +} diff --git a/src/app/page/page.module.css b/src/app/page/page.module.css new file mode 100644 index 0000000..fb1dfaa --- /dev/null +++ b/src/app/page/page.module.css @@ -0,0 +1,147 @@ +.page { + min-width: 800px; + min-height: 600px; +} + +.myText { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.navigation { + display: inline-block; + width: 181px; + + margin-left: 19px; + font-family: HelveticaNeue, serif; + vertical-align: top; +} + +.navigationWriteButton { + width: 147px; + height: 32px; + + border: none; + -webkit-appearance: none; + background-color: #6287bd; + border-radius: 3px; + cursor: pointer; + outline: none; +} + +.navigationWriteButtonDark { + background-color: #333333; + composes: navigationWriteButton; +} + +.navigationWriteText { + display: inline; + padding-top: 7px; + color: #ffffff; + font-size: 12px; + font-weight: 500; + text-align: center; +} + +.navigationContent { + width: 147px; + height: 22px; + margin-top: 0; +} + +.navigationContentDark { + composes: navigationContent; +} + +.navigationContentCurrent { + background-color: #cdd6e4; + border-radius: 3px; + composes: navigationContent; +} + +.navigationContentCurrentDark { + background-color: #666666; + composes: navigationContentCurrent; +} + +.navigationContent:hover { + width: 147px; + height: 22px; + margin-top: 0; + background-color: #cdd6e4; + border-radius: 3px; +} + +.navigationContentDark:hover { + width: 147px; + height: 22px; + margin-top: 0; + background-color: #666666; + border-radius: 3px; +} + +.navigationText { + composes: myText; + display: block; + width: 130px; + padding-top: 6px; + padding-left: 10px; + color: #707070; + font-size: 11px; + font-weight: 500; +} + +.navigationTextDark { + composes: navigationText; + color: #cccccc; +} + +.navigationTextCurrent { + color: black; + composes: navigationText; + font-weight: bold; +} + +.navigationTextCurrentDark { + color: #ffffff; + composes: navigationTextCurrent; +} + +.myInput { + display: inline-block; + -webkit-appearance: none; +} + +.line { + width: calc(100% - 2px); + min-width: 572px; + height: 0; + + border: solid 1px #e2e2e2; + background-color: #e5eaf0; +} + +.lineDark { + composes: line; + border: solid 1px #222222; + background-color: #222222; +} + +.navigationList { + padding: 0; + margin: 8px 0 0; + list-style-type: none; + vertical-align: top; +} + +.myLink { + outline: none; + text-decoration: none; +} + +.navigationLink { + outline: none; + text-decoration: none; + text-overflow: ellipsis; +} diff --git a/src/app/page/page.module.css.d.ts b/src/app/page/page.module.css.d.ts new file mode 100644 index 0000000..b6f84f8 --- /dev/null +++ b/src/app/page/page.module.css.d.ts @@ -0,0 +1,20 @@ +export const page: string; +export const navigation: string; +export const navigationWriteButton: string; +export const navigationWriteText: string; +export const navigationContent: string; +export const navigationContentCurrent: string; +export const navigationText: string; +export const navigationTextCurrent: string; +export const navigationLink: string; +export const navigationList: string; +export const myInput: string; +export const myText: string; +export const myLink: string; +export const line: string; +export const navigationWriteButtonDark: string; +export const navigationContentCurrentDark: string; +export const navigationContentDark: string; +export const navigationTextDark: string; +export const navigationTextCurrentDark: string; +export const lineDark: string; diff --git a/src/app/page/page.tsx b/src/app/page/page.tsx new file mode 100644 index 0000000..ee62099 --- /dev/null +++ b/src/app/page/page.tsx @@ -0,0 +1,345 @@ +import * as React from 'react'; + +import { Header } from '../header/header'; +import { Nav } from './nav'; +import { Content } from '../content/content'; +import { + genAuthorImage, + genAuthorName, + genHeadText, + genText, + getDate, + getHeadDate, + generateLetters, + MAX_LETTERS +} from '../scripts/scripts'; +import { ILetterType } from '../types/types'; +import styles from './page.module.css'; + +interface IMyState { + checked: { [id: string]: boolean }; + letters: ILetterType[]; + count: number; + isSelectAll: boolean; + text: string[]; + searchText: string; + theme: boolean; + searchFor: string; + worker: any; + filteredLetters: ILetterType[] | null; + isSearch: boolean; +} + +export class Page extends React.Component<{}, IMyState> { + public constructor(props: {}) { + super(props); + const { letters, checked } = generateLetters(); + this.state = { + checked, + letters, + count: 0, + isSelectAll: false, + text: [], + searchText: '', + theme: false, + searchFor: '', + worker: null, + filteredLetters: null, + isSearch: false + }; + + this.newLetter = this.newLetter.bind(this); + this.deleteMails = this.deleteMails.bind(this); + this.selectAll = this.selectAll.bind(this); + this.checkboxChange = this.checkboxChange.bind(this); + this.setText = this.setText.bind(this); + this.markRead = this.markRead.bind(this); + this.setRead = this.setRead.bind(this); + this.removeAddAnimation = this.removeAddAnimation.bind(this); + this.setSearchText = this.setSearchText.bind(this); + this.deleteLetter = this.deleteLetter.bind(this); + this.removeLetterById = this.removeLetterById.bind(this); + + this.last = 0; + + this.newMail = this.newMail.bind(this); + this.changeTheme = this.changeTheme.bind(this); + document.body.style.background = this.state.theme ? 'black' : '#e5eaf0'; + this.newMail(); + } + + public newLetter = () => { + console.log('1'); + const id: string = `letter-id-${this.state.count}`; + + this.setState((state: Readonly) => { + return { count: state.count + 1 }; + }); + + const authorName: string = genAuthorName(); + const authorImage: string = genAuthorImage(); + const headText: string = genHeadText(); + const letterText: string[] = genText(); + + const date: Date = new Date(); + const headTagDate: string = getDate(date); + const headDate: string = getHeadDate(date); + + const newChecked: { [id: string]: boolean } = this.state.checked; + newChecked[id] = false; + + const newLetter: ILetterType = { + id, + letterText, + authorName, + authorImage, + headText, + isChecked: false, + isVisible: true, + isRead: true, + addAnimation: true, + deleteAnimation: false, + headTagDate, + headDate + }; + + this.setState((state: IMyState) => { + return { + letters: [newLetter].concat(state.letters), + checked: newChecked + }; + }); + }; + + private readonly selectAll = () => { + const newChecked: { [id: string]: boolean } = this.state.checked; + + if (this.state.filteredLetters !== null) { + for (let i = 0; i < Math.min(this.state.filteredLetters.length, MAX_LETTERS); i++) { + newChecked[this.state.filteredLetters[i].id] = !this.state.isSelectAll; + } + } else { + for (let i = 0; i < Math.min(this.state.letters.length, MAX_LETTERS); i++) { + newChecked[this.state.letters[i].id] = !this.state.isSelectAll; + } + } + this.setState((state: IMyState) => { + return { + isSelectAll: !state.isSelectAll, + checked: newChecked + }; + }); + }; + + private readonly checkboxChange = (id: string) => { + const newChecked: { [id: string]: boolean } = this.state.checked; + newChecked[id] = !newChecked[id]; + this.setState({ + checked: newChecked + }); + }; + + private readonly deleteMails = () => { + const newLetters: ILetterType[] = this.state.letters.map(letter => { + const newLetter: ILetterType = letter; + if (this.state.checked[newLetter.id]) { + newLetter.deleteAnimation = true; + } + return newLetter; + }); + + this.setState({ + letters: newLetters, + isSelectAll: false + }); + + // setTimeout(this.makeDelete, 1500); + }; + + private readonly setText = (text: string[]) => { + this.setState({ + text + }); + }; + + private readonly markRead = (id: string, val: ILetterType) => { + const val1: ILetterType = val; + if (val1.id === id) { + val1.isRead = false; + } + return val1; + }; + + private readonly setRead = (id: string) => { + const newLetters: ILetterType[] = this.state.letters; + const letters1: ILetterType[] = newLetters.map(value => this.markRead(id, value)); + this.setState({ + letters: letters1 + }); + }; + + private readonly removeAddAnimation = (id: string) => { + const letters1: ILetterType[] = this.state.letters; + const newLetters: ILetterType[] = letters1.map(value => { + const tmp: ILetterType = value; + if (tmp.id === id) { + tmp.addAnimation = false; + } + return tmp; + }); + this.setState({ + letters: newLetters + }); + }; + + private isLetterHasText = (text: string, letter: ILetterType) => { + if (text.length === 0) { + return true; + } + if ( + letter.headText.toLocaleUpperCase().indexOf(text.toLocaleUpperCase()) !== -1 || + letter.authorName.toLocaleUpperCase().indexOf(text.toLocaleUpperCase()) !== -1 + ) { + return true; + } + let f = false; + for (let i = 0; i < letter.letterText.length && !f; i++) { + if (letter.letterText[i].toLocaleUpperCase().indexOf(text.toLocaleUpperCase()) !== -1) { + f = true; + } + } + return f; + }; + + private readonly setSearchText = (text: string) => { + this.setState({ + searchText: text + }); + }; + + private newMail() { + this.newLetter(); + const fiveMinute = 300000; + const maxTime = 600000; + const minTime = 10; + const time: number = Math.max( + fiveMinute - this.last, + Math.floor(Math.random() * (maxTime - minTime) + minTime) + ); + this.last = time; + setTimeout(this.newMail, time); + } + + private removeLetterById(letters: ILetterType[], id: string) { + let newLetters: ILetterType[] = letters; + newLetters = newLetters.filter((letter: ILetterType) => letter.id !== id); + return newLetters; + } + + private deleteLetter(id: string) { + this.setState((state: IMyState) => { + const newLetters: ILetterType[] = this.removeLetterById(state.letters, id); + let newFilteredLetters: ILetterType[] | null = null; + if (state.filteredLetters !== null) { + newFilteredLetters = this.removeLetterById(state.filteredLetters, id); + } + return { + letters: newLetters, + filteredLetters: newFilteredLetters + }; + }); + } + + private changeTheme() { + this.setState((state: IMyState) => { + document.body.style.background = state.theme ? '#e5eaf0' : 'black'; + return { theme: !state.theme }; + }); + } + + private last: number; + + public render() { + const searchText: string = this.state.searchText.toLocaleUpperCase(); + + if (searchText !== this.state.searchFor) { + if (this.state.searchText !== '') { + this.setState((state: IMyState) => { + if (state.worker) { + clearTimeout(state.worker); + } + + return { + searchFor: searchText, + worker: setTimeout(() => { + const lambdaWorker = (filtered: ILetterType[], from: number, size: number) => { + const to = Math.min(from + 30, size); + const newLetters = filtered.concat( + state.letters + .slice(from, to) + .filter((letter: ILetterType) => this.isLetterHasText(searchText, letter)) + ); + if (to === size) { + this.setState({ + filteredLetters: newLetters, + worker: null, + searchFor: searchText, + isSearch: false + }); + } else { + const worker = setTimeout(() => lambdaWorker(newLetters, to, size)); + this.setState({ + filteredLetters: newLetters, + worker, + searchFor: searchText + }); + } + }; + lambdaWorker([], 0, this.state.letters.length); + }, 500), + isSearch: true + }; + }); + } else { + this.setState((state: IMyState) => { + if (state.worker) { + clearTimeout(state.worker); + } + return { + filteredLetters: null, + worker: null, + searchFor: searchText, + isSearch: false + }; + }); + } + } + const letters = this.state.filteredLetters || this.state.letters; + return ( +
    +
    +
    + ); + } +} diff --git a/src/app/scripts/scripts.ts b/src/app/scripts/scripts.ts new file mode 100644 index 0000000..59dc73d --- /dev/null +++ b/src/app/scripts/scripts.ts @@ -0,0 +1,390 @@ +import { ILetterType } from '../types/types'; + +export const MAX_LETTERS = 30; + +const icon1: string = require('../../images/icons/1.png'); +const icon2: string = require('../../images/icons/2.png'); +const icon3: string = require('../../images/icons/3.jpg'); +const icon4: string = require('../../images/icons/4.jpg'); +const icon5: string = require('../../images/icons/5.png'); +const icon6: string = require('../../images/icons/6.png'); +const icon7: string = require('../../images/icons/7.png'); +const icon8: string = require('../../images/icons/8.png'); +const icon9: string = require('../../images/icons/9.png'); +const icon10: string = require('../../images/icons/9.jpg'); + +const phrases: string[] = [ + 'практика показывает, что', + 'структура организации', + 'одна маленькая строчка', + 'реторический вопрос', + 'текст продолжил', + 'свой путь', + 'непостижимые разновидности', + 'я совсем один', + 'так счастлив', + 'мой друг', + 'вдохнуть в рисунок', + 'выразить', + 'прильнув к земле', + 'не под силу', + 'после', + 'сердечным отношение', + 'подумал он', + 'нерегулярным питанием', + 'осмелился', + 'красный', + 'синий', + 'вверху', + 'живота', + 'с высоты', + 'с чистого листа', + 'хочешь я', + 'в глазва', + 'взгляну в', + 'твои глаза', + 'и слова', + 'припомню все', + 'и снова повторю', + 'кто тебе сказал', + 'ну', + 'кто тебе сказал', + 'кто придумал', + 'что тебя', + 'я', + 'не', + 'люблю', + 'я каждый жест', + 'каждый', + 'взгляд твой', + 'в душе берегу', + 'твой голос', + 'в серде моем', + 'звуччит звеня', + 'нет', + 'никогда', + 'я тебя', + 'разлюбить не смогу', + 'и', + 'ты люби', + 'ты всегда', + 'люби меня', + 'ты решилва', + 'все', + 'что было', + 'больше не вернешь', + 'сердце пусто', + 'вместо чувства', + 'в нем осталась', + 'ложь', + 'и казалось', + 'не осталось', + 'верного пути', + 'выбор сделан', + 'ты б хотела', + 'навсегда', + 'уйти', + 'навсегда уйти', + 'навсегда уйти', + 'и с чистого листа', + 'опять', + 'начнешь сначала', + 'звоню в', + 'последний раз', + 'а', + 'голос мой', + 'сотри', + 'и с чистого', + 'листа', + 'и снова все', + 'сначала', + 'закончилась', + 'про нас', + 'история любви', + 'история', + 'любви', + 'смелый', + 'как ветер', + 'свободный', + 'я делал все', + 'что душе угодно', + 'жил для себя', + 'год за годом', + 'крутой проявляя нрав', + 'сколько', + 'девченок хороших', + 'влюбилось в', + 'меня', + 'неосторожно', + 'всех сосчитать', + 'невозможно', + 'попробуй меня исправь', + 'и одна', + 'лишь ты', + 'много-много', + 'лет', + 'говорила нет', + 'ты одна', + 'ты такая', + 'я тебя знаю', + 'больше в мире таких', + 'таких не бывает', + 'все не то', + 'все не так', + 'ты мой друг', + 'я твой враг', + 'как же так', + 'все у нас', + 'с тоюой', + 'был', + 'апрель', + 'и в любви', + 'мы клялись', + 'но увы', + 'пролетел', + 'желтый лист', + 'по', + 'бульварам Москвы', + 'третье сентября', + 'день', + 'прощания', + 'день, когда', + 'горят', + 'костры рябин', + 'как костры горят', + 'обещяния', + 'день когда', + 'я совсем один', + 'я календарь', + 'переверну', + 'и снова третье сенятбря', + 'на фото я', + 'твое взгляну и', + 'снова тертье сентября', + 'но почему', + 'но почему', + 'расстаться все же', + 'нам пришлось', + 'но былов все', + 'у нас', + 'всерьез', + 'второго сентября' +]; + +const names: string[] = [ + 'Милослав', + 'Федосий', + 'Александр', + 'Самойло', + 'Кирьяк', + 'Фофан', + 'Аверьян', + 'Пантелеимон', + 'Игнат', + 'Прокопий', + 'Лев', + 'Лукьян', + 'Данила', + 'Филимон', + 'Акиндин', + 'Егор', + 'Панкратий', + 'Роман', + 'Абакум', + 'Мартын', + 'Еремей', + 'Гаврило', + 'Андрон', + 'Нафанаил', + 'Гаврила', + 'Федосий', + 'Прокофий', + 'Ипатий', + 'Аврамий', + 'Артемий', + 'Вавила', + 'Харлам', + 'Давыд', + 'Мордва' +]; + +const surnames: string[] = [ + 'Разумовский', + 'Вревский', + 'Ромодановский', + 'Скавронский', + 'Ржевский', + 'Урусов', + 'Хилков', + 'Татищев', + 'Нарышкин', + 'Бакаев', + 'Мещерский', + 'Херасков', + 'Шаховской', + 'Гершфельд', + 'Рабин', + 'Менакер', + 'Фукс', + 'Оппенгеймер', + 'Богораз', + 'Вольф', + 'Краузе', + 'Беккер', + 'Арендт', + 'Вагнер', + 'Гагин', + 'Корсак', + 'Сверчков', + 'Мухин', + 'Нигматуллин', + 'Беклемишев', + 'Великая' +]; + +const icons: string[] = [icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, icon9, icon10]; + +const months: string[] = [ + 'янв', + 'фев', + 'мар', + 'апр', + 'май', + 'июн', + 'июл', + 'авг', + 'сен', + 'окт', + 'ноя', + 'дек' +]; + +export function genText(): string[] { + const minParagraphCount = 1; + const maxParagraphCount = 5; + + const minSentenceCount = 1; + const maxSentenceCount = 10; + + const minPhraseCount = 2; + const maxPhraseCount = 20; + + const paragraphCount: number = Math.floor( + Math.random() * (maxParagraphCount - minParagraphCount) + minParagraphCount + ); + + const html: string[] = []; + for (let i = 0; i < paragraphCount; i++) { + let string = ''; + const sentenceCount: number = Math.floor( + Math.random() * (maxSentenceCount - minSentenceCount) + minSentenceCount + ); + for (let j = 0; j < sentenceCount; j++) { + const phraseCount: number = Math.floor( + Math.random() * (maxPhraseCount - minPhraseCount) + minPhraseCount + ); + for (let k = 0; k < phraseCount; k++) { + let phrase: string = phrases[Math.floor(Math.random() * (phrases.length - 1))]; + if (k === 0) { + phrase = phrase.charAt(0).toUpperCase() + phrase.substr(1, phrase.length - 1); + } + string += `${phrase} `; + } + string = string.substr(0, string.length - 1); + string += '. '; + } + html.push(string); + } + return html; +} + +export function genAuthorName(): string { + return `${surnames[Math.floor(Math.random() * (surnames.length - 1))]} ${ + names[Math.floor(Math.random() * (names.length - 1))] + }`; +} + +export function genAuthorImage(): string { + return icons[Math.floor(Math.random() * (icons.length - 1))]; +} + +export function genHeadText(): string { + const minPhraseCount = 2; + const maxPhraseCount = 20; + + let string = ''; + + const phraseCount: number = Math.floor( + Math.random() * (maxPhraseCount - minPhraseCount) + minPhraseCount + ); + for (let k = 0; k < phraseCount; k++) { + let phrase: string = phrases[Math.floor(Math.random() * (phrases.length - 1))]; + if (k === 0) { + phrase = phrase.charAt(0).toUpperCase() + phrase.substr(1, phrase.length - 1); + } + string += `${phrase} `; + } + string = string.substr(0, string.length - 1); + string += '. '; + + return string; +} + +export const getHeadDate = (date: Date) => { + const month = date.getMonth(); + const day = date.getDate(); + return `${day} ${months[month]}`; +}; + +const addZero = (x: number) => { + if (x < 10) { + return `0${x}`; + } + return x.toString(); +}; + +export const getDate = (date: Date) => { + const year = date.getFullYear(); + const month = date.getMonth(); + const day = date.getDate(); + const hours = date.getHours(); + const minutes = date.getMinutes(); + return `${year}-${addZero(month)}-${addZero(day)} ${addZero(hours)}:${addZero(minutes)}`; +}; + +export const generateLetters = () => { + let letters: ILetterType[] = []; + const checked: { [id: string]: boolean } = {}; + + for (let i = 0; i < 10000; i++) { + const id = `id${i}`; + const authorName: string = genAuthorName(); + const authorImage: string = genAuthorImage(); + const headText: string = genHeadText(); + const letterText: string[] = genText(); + + const date: Date = new Date(); + const headTagDate: string = getDate(date); + const headDate: string = getHeadDate(date); + checked[id] = false; + + const newLetter: ILetterType = { + id, + letterText, + authorName, + authorImage, + headText, + isChecked: false, + isVisible: true, + isRead: true, + addAnimation: true, + deleteAnimation: false, + headTagDate, + headDate + }; + + letters = [newLetter].concat(letters); + } + + return { letters, checked }; +}; diff --git a/src/app/types/types.ts b/src/app/types/types.ts new file mode 100644 index 0000000..ca6fba2 --- /dev/null +++ b/src/app/types/types.ts @@ -0,0 +1,14 @@ +export interface ILetterType { + id: string; + letterText: string[]; + authorName: string; + authorImage: string; + headText: string; + isChecked: boolean; + isVisible: boolean; + isRead: boolean; + addAnimation: boolean; + deleteAnimation: boolean; + headTagDate: string; + headDate: string; +} diff --git a/src/fonts/HelveticaNeueCyr-Bold.ttf b/src/fonts/HelveticaNeueCyr-Bold.ttf new file mode 100644 index 0000000..8dcfee9 Binary files /dev/null and b/src/fonts/HelveticaNeueCyr-Bold.ttf 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-Regular.eot b/src/fonts/YandexSansText-Regular.eot new file mode 100644 index 0000000..ce7d907 Binary files /dev/null and b/src/fonts/YandexSansText-Regular.eot differ diff --git a/src/images/check.png b/src/images/check.png new file mode 100644 index 0000000..0bd2b77 Binary files /dev/null and b/src/images/check.png differ diff --git a/src/images/cross-symbol-dark.png b/src/images/cross-symbol-dark.png new file mode 100644 index 0000000..c3d85d3 Binary files /dev/null and b/src/images/cross-symbol-dark.png differ diff --git a/src/images/cross-symbol.png b/src/images/cross-symbol.png new file mode 100644 index 0000000..7765268 Binary files /dev/null and b/src/images/cross-symbol.png differ diff --git a/src/images/header-mail-dark.svg b/src/images/header-mail-dark.svg new file mode 100644 index 0000000..5237141 --- /dev/null +++ b/src/images/header-mail-dark.svg @@ -0,0 +1,66 @@ + + +Created by EvoPdf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/images/header-mail.svg b/src/images/header-mail.svg new file mode 100644 index 0000000..fc9da9f --- /dev/null +++ b/src/images/header-mail.svg @@ -0,0 +1,66 @@ + + +Created by EvoPdf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/images/header-yandex-dark.svg b/src/images/header-yandex-dark.svg new file mode 100644 index 0000000..daee62c --- /dev/null +++ b/src/images/header-yandex-dark.svg @@ -0,0 +1,79 @@ + + +Created by EvoPdf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/images/header-yandex.svg b/src/images/header-yandex.svg new file mode 100644 index 0000000..69e2032 --- /dev/null +++ b/src/images/header-yandex.svg @@ -0,0 +1,79 @@ + + +Created by EvoPdf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/images/icons/1.png b/src/images/icons/1.png new file mode 100644 index 0000000..b54a914 Binary files /dev/null and b/src/images/icons/1.png differ diff --git a/src/images/icons/2.png b/src/images/icons/2.png new file mode 100644 index 0000000..51225ff Binary files /dev/null and b/src/images/icons/2.png differ diff --git a/src/images/icons/3.jpg b/src/images/icons/3.jpg new file mode 100644 index 0000000..108a275 Binary files /dev/null and b/src/images/icons/3.jpg differ diff --git a/src/images/icons/4.jpg b/src/images/icons/4.jpg new file mode 100644 index 0000000..d7a982f Binary files /dev/null and b/src/images/icons/4.jpg differ diff --git a/src/images/icons/5.png b/src/images/icons/5.png new file mode 100644 index 0000000..5db4d16 Binary files /dev/null and b/src/images/icons/5.png differ diff --git a/src/images/icons/6.png b/src/images/icons/6.png new file mode 100644 index 0000000..e3687ee Binary files /dev/null and b/src/images/icons/6.png differ diff --git a/src/images/icons/7.png b/src/images/icons/7.png new file mode 100644 index 0000000..a53067c Binary files /dev/null and b/src/images/icons/7.png differ diff --git a/src/images/icons/8.png b/src/images/icons/8.png new file mode 100644 index 0000000..1d1805f Binary files /dev/null and b/src/images/icons/8.png differ diff --git a/src/images/icons/9.jpg b/src/images/icons/9.jpg new file mode 100644 index 0000000..b33117d Binary files /dev/null and b/src/images/icons/9.jpg differ diff --git a/src/images/icons/9.png b/src/images/icons/9.png new file mode 100644 index 0000000..0ab5d21 Binary files /dev/null and b/src/images/icons/9.png differ diff --git a/src/images/icons/check.png b/src/images/icons/check.png new file mode 100644 index 0000000..0bd2b77 Binary files /dev/null and b/src/images/icons/check.png differ diff --git a/src/images/itmo.png b/src/images/itmo.png new file mode 100644 index 0000000..73a861f Binary files /dev/null and b/src/images/itmo.png differ diff --git a/src/images/ox.png b/src/images/ox.png new file mode 100644 index 0000000..c7b5da9 Binary files /dev/null and b/src/images/ox.png differ diff --git a/src/images/yandex.png b/src/images/yandex.png new file mode 100644 index 0000000..986765f Binary files /dev/null and b/src/images/yandex.png differ diff --git a/src/index.css b/src/index.css index 2b6e525..097218d 100644 --- a/src/index.css +++ b/src/index.css @@ -1,10 +1,30 @@ +@font-face { + font-family: Yandex Sans; + font-style: normal; + src: url(./fonts/YandexSansText-Regular.eot) format('eot'); +} + +@font-face { + font-family: HelveticaNeue; + src: url(./fonts/HelveticaNeueCyr-Light.otf) format('opentype'); +} + +@font-face { + font-family: HelveticaNeue; + font-style: normal; + font-weight: 500; + src: url(./fonts/HelveticaNeueCyr-Medium.otf) format('opentype'); +} + +@font-face { + font-family: HelveticaNeue; + font-style: normal; + font-weight: bold; + src: url(./fonts/HelveticaNeueCyr-Bold.ttf) format('truetype'); +} + body { - padding: 0; - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', - 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + background-color: black; } code { diff --git a/src/index.jsx b/src/index.tsx similarity index 61% rename from src/index.jsx rename to src/index.tsx index ffc72ee..3e29557 100644 --- a/src/index.jsx +++ b/src/index.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; import { App } from './app'; 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/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..77c567c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es6", + "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" + ] +}