├── .eslintignore
├── .env
├── src
├── react-app-env.d.ts
├── index.tsx
├── hooks
│ ├── index.tsx
│ └── Menu
│ │ └── index.tsx
├── setupTests.ts
├── App.tsx
├── layout
│ ├── index.tsx
│ └── styles.ts
├── styles
│ └── global.ts
├── components
│ ├── Header
│ │ ├── index.tsx
│ │ └── styles.ts
│ ├── Main
│ │ ├── index.tsx
│ │ └── styles.ts
│ └── Menu
│ │ ├── styles.ts
│ │ └── index.tsx
└── assets
│ └── logo.svg
├── public
├── logo.png
└── index.html
├── .github
├── logo.png
└── printScreen.png
├── .gitignore
├── tsconfig.json
├── .eslintrc.json
├── package.json
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | SKIP_PREFLIGHT_CHECK=true
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lucas-eduardo/youtube-clone/HEAD/public/logo.png
--------------------------------------------------------------------------------
/.github/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lucas-eduardo/youtube-clone/HEAD/.github/logo.png
--------------------------------------------------------------------------------
/.github/printScreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lucas-eduardo/youtube-clone/HEAD/.github/printScreen.png
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import App from './App';
5 |
6 | ReactDOM.render( , document.getElementById('root'));
7 |
--------------------------------------------------------------------------------
/src/hooks/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { MenuProvider } from './Menu';
4 |
5 | const AppProvider: React.FC = ({ children }) => (
6 | {children}
7 | );
8 |
9 | export default AppProvider;
10 |
--------------------------------------------------------------------------------
/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import AppContext from './hooks';
4 |
5 | import Layout from './layout';
6 |
7 | import GlobalStyle from './styles/global';
8 |
9 | function App() {
10 | return (
11 |
12 |
13 |
14 |
15 | );
16 | }
17 |
18 | export default App;
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/layout/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { useMenu } from '../hooks/Menu';
4 |
5 | import Header from '../components/Header';
6 | import Menu from '../components/Menu';
7 | import Main from '../components/Main';
8 |
9 | import { Wrapper } from './styles';
10 |
11 | const Layout: React.FC = () => {
12 | const { openMenu } = useMenu();
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default Layout;
24 |
--------------------------------------------------------------------------------
/src/layout/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Wrapper = styled.div`
4 | display: grid;
5 |
6 | grid-template-columns: 24rem auto;
7 | grid-template-rows: 5.6rem auto;
8 |
9 | grid-template-areas:
10 | 'HEADER HEADER'
11 | 'MENU MAIN';
12 |
13 | height: 100vh;
14 |
15 | &.openMenu {
16 | grid-template-columns: 0 100vw;
17 | }
18 |
19 | @media screen and (max-width: 525px) {
20 | grid-template-columns: 0 auto;
21 |
22 | &.openMenu {
23 | grid-template-columns: 100vw 0;
24 | }
25 | }
26 | `;
27 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "react"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2020": true
5 | },
6 | "extends": [
7 | "plugin:react/recommended",
8 | "standard",
9 | "prettier/@typescript-eslint",
10 | "plugin:prettier/recommended"
11 | ],
12 | "parser": "@typescript-eslint/parser",
13 | "parserOptions": {
14 | "ecmaFeatures": {
15 | "jsx": true
16 | },
17 | "ecmaVersion": 11,
18 | "sourceType": "module"
19 | },
20 | "plugins": [
21 | "react",
22 | "@typescript-eslint",
23 | "prettier"
24 | ],
25 | "rules": {
26 | "prettier/prettier": "error",
27 | "react/no-unescaped-entities": "off",
28 | "react/prop-types": "off"
29 | }
30 | }
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 | YouTube
12 |
13 |
14 |
15 |
16 | You need to enable JavaScript to run this app.
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/hooks/Menu/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { createContext, useContext, useState, useCallback } from 'react';
2 |
3 | interface MenuContextData {
4 | openMenu: boolean;
5 | clickMenu: () => void;
6 | }
7 |
8 | const MenuContext = createContext({} as MenuContextData);
9 |
10 | const MenuProvider: React.FC = ({ children }) => {
11 | const [openMenu, setOpenMenu] = useState(false);
12 |
13 | const clickMenu = useCallback(() => {
14 | setOpenMenu(oldState => !oldState);
15 | }, []);
16 |
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | };
23 |
24 | function useMenu(): MenuContextData {
25 | return useContext(MenuContext);
26 | }
27 |
28 | export { MenuProvider, useMenu };
29 |
--------------------------------------------------------------------------------
/src/styles/global.ts:
--------------------------------------------------------------------------------
1 | import { createGlobalStyle } from 'styled-components';
2 |
3 | export default createGlobalStyle`
4 | * {
5 | margin: 0;
6 | padding: 0;
7 | outline: 0;
8 | box-sizing: border-box;
9 | }
10 |
11 | html {
12 | font-size: 62.5%;
13 | background-color: #f9f9f9;
14 | }
15 |
16 | body, input, select, textarea, button {
17 | font-size: 1rem;
18 | font-family: 'Roboto', Arial, sans-serif;
19 | -webkit-appearance: none;
20 | -webkit-font-smoothing: antialiased;
21 | }
22 |
23 | a {
24 | text-decoration: none;
25 | }
26 |
27 | button {
28 | cursor: pointer;
29 | background-color: transparent;
30 | border: 0;
31 | }
32 |
33 | #root {
34 | --primary: #606060;
35 | --secondary: #909090;
36 |
37 | --gray: #ccc;
38 | --gray83: #d3d3d3;
39 | --gray93: #eee;
40 |
41 | --white: #fff;
42 | --white97: #f8f8f8;
43 |
44 | --black: #030303;
45 | --black20: #333;
46 | --lightBlack: rgba(0, 0, 0, 0.1);
47 |
48 | --red: #f00;
49 | }
50 | `;
51 |
--------------------------------------------------------------------------------
/src/components/Header/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | MdMenu,
4 | MdSearch,
5 | MdVideoCall,
6 | MdApps,
7 | MdNotifications,
8 | } from 'react-icons/md';
9 |
10 | import { useMenu } from '../../hooks/Menu';
11 |
12 | import logo from '../../assets/logo.svg';
13 |
14 | import {
15 | Wrapper,
16 | Navigation,
17 | Search,
18 | Actions,
19 | ButtonAction,
20 | Profile,
21 | ProfileNotImage,
22 | } from './styles';
23 |
24 | const Header: React.FC = () => {
25 | const { clickMenu } = useMenu();
26 |
27 | return (
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | );
65 | };
66 |
67 | export default Header;
68 |
--------------------------------------------------------------------------------
/src/components/Main/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import {
4 | Wrapper,
5 | Title,
6 | GridVideo,
7 | ThumbVideo,
8 | Thumb,
9 | ThumbVideoHeader,
10 | ImageChannel,
11 | ImageNotChannel,
12 | ThumbVideoInfo,
13 | ThumbVideoInfoTitle,
14 | ThumbVideoInfoNameChannel,
15 | ThumbVideoInfoView,
16 | ThumbVideoInfoTime,
17 | } from './styles';
18 |
19 | const Main: React.FC = () => {
20 | return (
21 |
22 | Recomendados
23 |
24 |
25 | {Array.from(Array(15).keys()).map(item => (
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Não Decore Códigos, Início da Carreira, Speech to Text,
36 | Xamarin, Tema da Rocket no VS Code | #PR 09
37 |
38 |
39 |
40 | Rocketseat
41 |
42 |
43 | 2,7 mil visualizações
44 |
45 | há 1 hora
46 |
47 |
48 |
49 | ))}
50 |
51 |
52 | );
53 | };
54 |
55 | export default Main;
56 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "clone-youtube",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^4.2.4",
7 | "@testing-library/react": "^9.3.2",
8 | "@testing-library/user-event": "^7.1.2",
9 | "@types/jest": "^24.0.0",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^16.9.0",
12 | "@types/react-dom": "^16.9.0",
13 | "react": "^16.13.1",
14 | "react-dom": "^16.13.1",
15 | "react-icons": "^3.10.0",
16 | "react-scripts": "3.4.1",
17 | "styled-components": "^5.1.1",
18 | "typescript": "~3.7.2"
19 | },
20 | "scripts": {
21 | "start": "NODE_OPTIONS='--openssl-legacy-provider' react-scripts start",
22 | "build": "NODE_OPTIONS='--openssl-legacy-provider' react-scripts build",
23 | "test": "NODE_OPTIONS='--openssl-legacy-provider' react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": "react-app"
28 | },
29 | "prettier": {
30 | "singleQuote": true,
31 | "trailingComma": "all",
32 | "arrowParens": "avoid"
33 | },
34 | "browserslist": {
35 | "production": [
36 | ">0.2%",
37 | "not dead",
38 | "not op_mini all"
39 | ],
40 | "development": [
41 | "last 1 chrome version",
42 | "last 1 firefox version",
43 | "last 1 safari version"
44 | ]
45 | },
46 | "devDependencies": {
47 | "@types/react-icons": "^3.0.0",
48 | "@types/styled-components": "^5.1.0",
49 | "@typescript-eslint/eslint-plugin": "^3.4.0",
50 | "@typescript-eslint/parser": "^3.4.0",
51 | "eslint": "^7.3.1",
52 | "eslint-config-prettier": "^6.11.0",
53 | "eslint-config-standard": "^14.1.1",
54 | "eslint-plugin-import": "^2.21.2",
55 | "eslint-plugin-node": "^11.1.0",
56 | "eslint-plugin-prettier": "^3.1.4",
57 | "eslint-plugin-promise": "^4.2.1",
58 | "eslint-plugin-react": "^7.20.0",
59 | "eslint-plugin-standard": "^4.0.1",
60 | "prettier": "^2.0.5"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | ## 💻 Sobre o clone
26 |
27 | O clone da aplicação do youtube foi desenvolvimento apenas para fins de estudos e de desafio próprio.
28 |
29 | Link: https://www.ui-clone-youtube.lucaseduardo.app.br
30 |
31 | ---
32 |
33 | ## 🎨 Clone final
34 |
35 |
36 |
37 |
38 |
39 | ---
40 |
41 | ## 🛠 Tecnologia
42 |
43 | Foi utilizado o [ReactJS][reactjs] utilizando o CRA [TypeScript][typescript].
44 |
45 | #### Pré-requisitos
46 |
47 | Antes de começar, você vai precisar ter instalado em sua máquina as seguintes ferramentas:
48 | [Git](https://git-scm.com), [Node.js][nodejs] e [Yarn][yarn].
49 | Além disto é bom ter um editor para trabalhar com o código como [VSCode][vscode]
50 |
51 | ### 🧭 Rodando a aplicação
52 |
53 | ```bash
54 | # Clone este repositório
55 | $ git clone https://github.com/lucas-eduardo/youtube-clone
56 |
57 | # Acesse a pasta do projeto no seu terminal/cmd
58 | $ cd youtube-clone
59 |
60 | # Instale as dependências
61 | $ yarn install
62 |
63 | # Execute a aplicação em modo de desenvolvimento
64 | $ yarn start
65 |
66 | # A aplicação será aberta na porta:3000 - acesse http://localhost:3000
67 | ```
68 |
69 | [nodejs]: https://nodejs.org/
70 | [typescript]: https://www.typescriptlang.org/
71 | [expo]: https://expo.io/
72 | [reactjs]: https://reactjs.org
73 | [rn]: https://facebook.github.io/react-native/
74 | [yarn]: https://yarnpkg.com/
75 | [vscode]: https://code.visualstudio.com/
76 | [license]: https://opensource.org/licenses/MIT
77 | [rc]: https://rocketseat.com.br
78 |
--------------------------------------------------------------------------------
/src/components/Header/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Wrapper = styled.header`
4 | grid-area: HEADER;
5 |
6 | display: flex;
7 | align-items: center;
8 | justify-content: space-between;
9 |
10 | background-color: var(--white);
11 |
12 | padding: 0 1.6rem;
13 | `;
14 |
15 | export const Navigation = styled.div`
16 | width: 100%;
17 | max-width: 185px;
18 |
19 | > button {
20 | width: 40px;
21 | height: 40px;
22 |
23 | margin-right: 1.6rem;
24 |
25 | color: var(--primary);
26 |
27 | svg {
28 | width: 24px;
29 | height: 24px;
30 | }
31 | }
32 |
33 | > img {
34 | width: 80px;
35 | height: 24px;
36 | }
37 |
38 | @media screen and (max-width: 525px) {
39 | > img {
40 | display: none;
41 | }
42 | }
43 | `;
44 |
45 | export const Search = styled.div`
46 | display: flex;
47 |
48 | width: 100%;
49 | max-width: 728px;
50 |
51 | form {
52 | display: flex;
53 | }
54 |
55 | form,
56 | input {
57 | width: 100%;
58 | height: 30px;
59 | }
60 |
61 | input {
62 | padding: 0.2rem 0.6rem;
63 | border: 1px solid var(--gray);
64 | border-right: none;
65 | border-radius: 2px 0 0 2px;
66 | box-shadow: inset 0 1px 2px var(--gray93);
67 | color: var(--black);
68 | font-size: 1.6rem;
69 | font-weight: 400;
70 | line-height: 2.4rem;
71 | }
72 |
73 | button {
74 | display: flex;
75 | align-items: center;
76 | justify-content: center;
77 |
78 | width: 65px;
79 | height: 30px;
80 | border: 1px solid var(--gray83);
81 | background-color: var(--white97);
82 | border-radius: 0 2px 2px 0;
83 |
84 | svg {
85 | width: 20px;
86 | height: 20px;
87 | color: var(--black20);
88 | }
89 | }
90 |
91 | @media screen and (max-width: 525px) {
92 | display: none;
93 | }
94 | `;
95 |
96 | export const Actions = styled.div`
97 | display: flex;
98 | align-items: center;
99 | justify-content: flex-end;
100 |
101 | width: 225px;
102 | height: 40px;
103 | `;
104 |
105 | export const ButtonAction = styled.button`
106 | display: flex;
107 | align-items: center;
108 | justify-content: center;
109 |
110 | width: 40px;
111 | height: 40px;
112 |
113 | padding: 0.8rem;
114 |
115 | color: var(--primary);
116 |
117 | svg {
118 | width: 24px;
119 | height: 24px;
120 | }
121 |
122 | & + button {
123 | margin-left: 0.8rem;
124 | }
125 |
126 | @media screen and (max-width: 525px) {
127 | display: none;
128 | }
129 | `;
130 |
131 | export const Profile = styled.button`
132 | display: flex;
133 | align-items: center;
134 | justify-content: center;
135 |
136 | width: 60px;
137 | height: 40px;
138 | `;
139 |
140 | export const ProfileNotImage = styled.div`
141 | width: 32px;
142 | height: 32px;
143 |
144 | background-color: var(--primary);
145 | border-radius: 50%;
146 | `;
147 |
--------------------------------------------------------------------------------
/src/components/Main/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Wrapper = styled.main`
4 | grid-area: MAIN;
5 |
6 | display: flex;
7 | flex-direction: column;
8 |
9 | margin: 3.6rem 0 0 2.4rem;
10 | padding: 1.2rem 0;
11 |
12 | overflow-y: scroll;
13 |
14 | ::-webkit-scrollbar {
15 | width: 8px;
16 | }
17 | ::-webkit-scrollbar-thumb {
18 | background-color: rgba(0, 0, 0, 0.2);
19 | height: 56px;
20 | }
21 | ::-webkit-scrollbar-track {
22 | background-color: transparent;
23 | }
24 | `;
25 |
26 | export const Title = styled.span`
27 | color: var(--black);
28 |
29 | font-size: 2rem;
30 | font-weight: 500;
31 | line-height: 2.4rem;
32 | `;
33 |
34 | export const GridVideo = styled.div`
35 | display: grid;
36 |
37 | grid-template-columns: repeat(auto-fill, 250px);
38 | grid-gap: 4rem 1rem;
39 |
40 | margin-top: 1.6rem;
41 |
42 | @media screen and (max-width: 525px) {
43 | align-self: center;
44 | }
45 | `;
46 |
47 | export const ThumbVideo = styled.div``;
48 |
49 | export const Thumb = styled.div`
50 | width: 250px;
51 | height: 140px;
52 |
53 | background-color: var(--primary);
54 | `;
55 |
56 | export const ThumbVideoHeader = styled.div`
57 | margin-top: 1.2rem;
58 |
59 | display: flex;
60 | `;
61 |
62 | export const ImageChannel = styled.div`
63 | margin-right: 1.2rem;
64 | `;
65 |
66 | export const ImageNotChannel = styled.div`
67 | width: 36px;
68 | height: 36px;
69 |
70 | border-radius: 50%;
71 |
72 | background-color: var(--primary);
73 | `;
74 |
75 | export const ThumbVideoInfo = styled.div`
76 | display: flex;
77 | flex-direction: column;
78 |
79 | padding-right: 2.4rem;
80 | `;
81 |
82 | export const ThumbVideoInfoTitle = styled.h3`
83 | font-size: 1.4rem;
84 | font-weight: 500;
85 | line-height: 2rem;
86 |
87 | max-height: 4rem;
88 |
89 | margin-bottom: 0.6rem;
90 |
91 | color: var(--black);
92 |
93 | display: -webkit-box;
94 |
95 | overflow: hidden;
96 | text-overflow: ellipsis;
97 | white-space: normal;
98 | -webkit-line-clamp: 2;
99 | -webkit-box-orient: vertical;
100 | `;
101 |
102 | export const ThumbVideoInfoNameChannel = styled.span`
103 | font-size: 1.4rem;
104 | line-height: 1.8rem;
105 |
106 | color: var(--primary);
107 |
108 | overflow: hidden;
109 | text-overflow: ellipsis;
110 | white-space: nowrap;
111 | `;
112 |
113 | export const ThumbVideoInfoView = styled.span`
114 | font-size: 1.4rem;
115 | line-height: 1.8rem;
116 |
117 | color: var(--primary);
118 |
119 | overflow: hidden;
120 | text-overflow: ellipsis;
121 | white-space: nowrap;
122 |
123 | &:after {
124 | content: '•';
125 | margin: 0 0.4rem;
126 | }
127 | `;
128 |
129 | export const ThumbVideoInfoTime = styled.span`
130 | font-size: 1.4rem;
131 | line-height: 1.8rem;
132 |
133 | color: var(--primary);
134 |
135 | overflow: hidden;
136 | text-overflow: ellipsis;
137 | white-space: nowrap;
138 | `;
139 |
--------------------------------------------------------------------------------
/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
19 |
25 |
27 |
30 |
33 |
34 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/components/Menu/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Wrapper = styled.menu`
4 | grid-area: MENU;
5 |
6 | display: flex;
7 | flex-direction: column;
8 |
9 | background-color: var(--white);
10 |
11 | padding: 1.2rem 0;
12 |
13 | overflow-y: hidden;
14 |
15 | &:hover {
16 | overflow-y: scroll;
17 |
18 | ::-webkit-scrollbar {
19 | width: 8px;
20 | }
21 | ::-webkit-scrollbar-thumb {
22 | background-color: rgba(0, 0, 0, 0.2);
23 | height: 56px;
24 | }
25 | ::-webkit-scrollbar-track {
26 | background-color: transparent;
27 | }
28 | }
29 |
30 | @media screen and (max-width: 525px) {
31 | overflow-y: scroll;
32 |
33 | &.openMenu {
34 | animation: teste 1s;
35 |
36 | @keyframes teste {
37 | from {
38 | opacity: 0;
39 | }
40 | to {
41 | opacity: 1;
42 | }
43 | }
44 | }
45 | }
46 | `;
47 |
48 | export const List = styled.div`
49 | h3 {
50 | color: var(--primary);
51 | font-size: 1.4rem;
52 | font-weight: 500;
53 | letter-spacing: 0.007px;
54 | padding: 0.8rem 2.4rem;
55 | text-transform: uppercase;
56 | }
57 |
58 | ul {
59 | li {
60 | display: block;
61 |
62 | width: 100%;
63 |
64 | color: var(--black);
65 |
66 | > div {
67 | display: flex;
68 | align-items: center;
69 |
70 | cursor: pointer;
71 |
72 | width: 100%;
73 | min-height: 40px;
74 |
75 | padding: 0 2.4rem;
76 |
77 | font-size: 1.4rem;
78 |
79 | line-height: 1.8rem;
80 |
81 | color: var(--black);
82 |
83 | div {
84 | width: 24px;
85 | height: 24px;
86 | margin-right: 2.4rem;
87 |
88 | svg {
89 | width: 24px;
90 | height: 24px;
91 | color: var(--primary);
92 | }
93 | }
94 |
95 | span {
96 | overflow: hidden;
97 | white-space: nowrap;
98 | text-overflow: ellipsis;
99 | }
100 | }
101 |
102 | transition: background-color 0.2s;
103 |
104 | &:hover {
105 | background-color: rgba(0, 0, 0, 0.05);
106 | }
107 |
108 | &.active {
109 | background-color: var(--lightBlack);
110 |
111 | > div {
112 | font-weight: 500;
113 |
114 | svg {
115 | color: var(--red);
116 | }
117 | }
118 | }
119 | }
120 | }
121 |
122 | & + & {
123 | border-top: 1px solid var(--lightBlack);
124 | margin-top: 1.2rem;
125 | padding-top: 1.2rem;
126 | }
127 | `;
128 |
129 | export const Footer = styled.footer`
130 | border-top: 1px solid var(--lightBlack);
131 | margin-top: 1.2rem;
132 | padding-top: 1.2rem;
133 |
134 | div {
135 | padding: 1.6rem 2.4rem 0;
136 |
137 | > span {
138 | display: inline-block;
139 | text-decoration: none;
140 | white-space: nowrap;
141 | font-size: 1.3rem;
142 | font-weight: 500;
143 | line-height: 1.8rem;
144 | color: var(--primary);
145 | cursor: pointer;
146 |
147 | &:not(:last-child) {
148 | margin-right: 8px;
149 | }
150 | }
151 | }
152 | `;
153 |
154 | export const AvatarChannel = styled.div`
155 | width: 24px;
156 | height: 24px;
157 |
158 | border-radius: 50%;
159 |
160 | background-color: var(--primary);
161 |
162 | margin-right: 2.4rem;
163 | `;
164 |
165 | export const Copyright = styled.div`
166 | padding: 1.6rem 2.4rem;
167 |
168 | p {
169 | color: var(--secondary);
170 | font-size: 1.3rem;
171 | line-height: 1.8rem;
172 | }
173 | `;
174 |
--------------------------------------------------------------------------------
/src/components/Menu/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | MdHome,
4 | MdWhatshot,
5 | MdSubscriptions,
6 | MdVideoLibrary,
7 | MdHistory,
8 | MdSlideshow,
9 | MdWatchLater,
10 | MdThumbUp,
11 | MdKeyboardArrowDown,
12 | MdSettings,
13 | MdHelp,
14 | } from 'react-icons/md';
15 | import {
16 | AiFillYoutube,
17 | AiFillFlag,
18 | AiFillExclamationCircle,
19 | } from 'react-icons/ai';
20 | import { GiFilmStrip } from 'react-icons/gi';
21 | import { IoLogoGameControllerB } from 'react-icons/io';
22 | import { RiRecordCircleLine } from 'react-icons/ri';
23 |
24 | import { useMenu } from '../../hooks/Menu';
25 |
26 | import { Wrapper, List, AvatarChannel, Footer, Copyright } from './styles';
27 |
28 | const Menu: React.FC = () => {
29 | const { openMenu } = useMenu();
30 |
31 | return (
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
{' '}
40 |
Início
41 |
42 |
43 |
44 |
45 |
46 |
47 |
{' '}
48 |
Em alta
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
{' '}
57 |
Inscrições
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
{' '}
70 |
Biblioteca
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
{' '}
79 |
Histórico
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
{' '}
88 |
Seus vídeos
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
{' '}
97 |
Assistir mais tarde
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
{' '}
106 |
Vídeos marcados com "Gostei"
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
{' '}
115 |
Mostrar mais
116 |
117 |
118 |
119 |
120 |
121 |
122 | Inscrições
123 |
124 |
125 |
126 |
127 |
{' '}
130 |
Full Cycle
131 |
132 |
133 |
134 |
135 |
136 |
{' '}
139 |
Rocketseat
140 |
141 |
142 |
143 |
144 |
145 |
146 | Mais do YouTube
147 |
148 |
149 |
150 |
151 |
{' '}
154 |
YouTube Premium
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
{' '}
163 |
YouTube Filmes
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
{' '}
172 |
Jogos
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
{' '}
181 |
Ao vivo
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
{' '}
194 |
Configurações
195 |
196 |
197 |
198 |
199 |
200 |
{' '}
203 |
Histórico de denúncias
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
{' '}
212 |
Ajuda
213 |
214 |
215 |
216 |
217 |
218 |
{' '}
221 |
Enviar feedback
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 | Sobre
230 | Imprensa
231 | Direitos autorais
232 | Criadores de conteúdo
233 | Publicidade
234 | Desenvolvedores
235 |
236 |
237 |
238 | Termos
239 | Privacidade
240 | Política e Segurança
241 | Testar os novos recursos
242 |
243 |
244 |
245 | © 2020 Google LLC
246 |
247 |
248 |
249 | );
250 | };
251 |
252 | export default Menu;
253 |
--------------------------------------------------------------------------------