├── .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 | 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 | 33 | Youtube 34 | 35 | 36 | 37 |
38 | 39 | 40 | 43 |
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 | YouTube Clone 3 |

4 | 5 |

6 | GitHub language count 7 | 8 | Repository size 9 | 10 | 11 | Made by Lucas Eduardo 12 | 13 | 14 | 15 | GitHub last commit 16 | 17 | 18 | License 19 | 20 | 21 | Stargazers 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 |
    128 | 129 |
    {' '} 130 | Full Cycle 131 |
    132 |
  • 133 | 134 |
  • 135 |
    136 |
    137 | 138 |
    {' '} 139 | Rocketseat 140 |
    141 |
  • 142 |
143 |
144 | 145 | 146 |

Mais do YouTube

147 | 148 |
    149 |
  • 150 |
    151 |
    152 | 153 |
    {' '} 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 |
    201 | 202 |
    {' '} 203 | Histórico de denúncias 204 |
    205 |
  • 206 | 207 |
  • 208 |
    209 |
    210 | 211 |
    {' '} 212 | Ajuda 213 |
    214 |
  • 215 | 216 |
  • 217 |
    218 |
    219 | 220 |
    {' '} 221 | Enviar feedback 222 |
    223 |
  • 224 |
225 |
226 | 227 | 248 |
249 | ); 250 | }; 251 | 252 | export default Menu; 253 | --------------------------------------------------------------------------------