├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── manifest.json
└── index.html
├── imgs
├── Screenshot_1.jpg
├── Screenshot_2.jpg
└── Screenshot_3.jpg
├── src
├── index.js
├── components
│ ├── Header
│ │ ├── index.js
│ │ └── styles.css
│ ├── MovieRow
│ │ ├── styles.css
│ │ └── index.js
│ └── FeaturedMovie
│ │ ├── index.js
│ │ └── styles.css
├── App.css
├── App.js
└── Tmdb.js
├── README.md
├── .gitignore
└── package.json
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toandriottibertoni/react-netflix-clone-ui/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toandriottibertoni/react-netflix-clone-ui/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toandriottibertoni/react-netflix-clone-ui/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/imgs/Screenshot_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toandriottibertoni/react-netflix-clone-ui/HEAD/imgs/Screenshot_1.jpg
--------------------------------------------------------------------------------
/imgs/Screenshot_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toandriottibertoni/react-netflix-clone-ui/HEAD/imgs/Screenshot_2.jpg
--------------------------------------------------------------------------------
/imgs/Screenshot_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toandriottibertoni/react-netflix-clone-ui/HEAD/imgs/Screenshot_3.jpg
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Clone ui for study react
2 |
3 | by https://www.youtube.com/watch?v=tBweoUiMsDg
4 |
5 | data from https://api.themoviedb.org/3
6 |
7 | ## Get your API_KEY and config in Tmdb API_KEY
8 |
9 | ## imgs
10 | 
11 |
--------------------------------------------------------------------------------
/.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/components/Header/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './styles.css';
3 |
4 | export default ({black})=>{
5 | return(
6 |
7 |
8 |

9 |
10 |
11 |

12 |
13 |
14 | )
15 | }
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/Header/styles.css:
--------------------------------------------------------------------------------
1 | header{
2 | position: fixed;
3 | z-index: 999;
4 | top:0px;
5 | left: 0px;
6 | right:0px;
7 | height: 70px;
8 | display:flex;
9 | justify-content: space-between;
10 | align-items: center;
11 | padding: 0 30px;
12 | background: transparent;
13 | transition: all ease 0.5s;
14 | }
15 | header.black{
16 | background:#141414;
17 | }
18 | .header--logo{
19 | height: 25px;
20 | }
21 |
22 | .header--logo img{
23 | height: 100%;
24 | }
25 |
26 | .header--user{
27 | height: 35px;
28 | }
29 |
30 | .header--user img {
31 | height: 100%;
32 | border-radius: 3px;
33 | }
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
2 |
3 | * {
4 | box-sizing: border-box;
5 | }
6 |
7 | body{
8 | background-color: #111;
9 | color :#fff;
10 | margin: 0;
11 | font-family: 'Roboto', sans-serif;
12 | }
13 |
14 | .lists{
15 | margin-top: -150px;
16 | }
17 |
18 | footer{
19 | margin: 50px 0;
20 | text-align: center;
21 | }
22 |
23 | .loading{
24 | position: fixed;
25 | top:0;
26 | left:0;
27 | right: 0;
28 | bottom: 0;
29 | z-index: 99;
30 | background-color: #000;
31 | display:flex;
32 | justify-content: center;
33 | align-items: center;
34 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "netflix-clone-ui",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@material-ui/core": "^4.11.0",
7 | "@material-ui/icons": "^4.9.1",
8 | "@testing-library/jest-dom": "^4.2.4",
9 | "@testing-library/react": "^9.3.2",
10 | "@testing-library/user-event": "^7.1.2",
11 | "react": "^16.13.1",
12 | "react-dom": "^16.13.1",
13 | "react-scripts": "3.4.3"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject"
20 | },
21 | "eslintConfig": {
22 | "extends": "react-app"
23 | },
24 | "browserslist": {
25 | "production": [
26 | ">0.2%",
27 | "not dead",
28 | "not op_mini all"
29 | ],
30 | "development": [
31 | "last 1 chrome version",
32 | "last 1 firefox version",
33 | "last 1 safari version"
34 | ]
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/MovieRow/styles.css:
--------------------------------------------------------------------------------
1 | .movieRow{
2 | margin-bottom: 30px;
3 | }
4 |
5 | .movieRow h2 {
6 | margin: 0px 0px 0px 30px;
7 | }
8 |
9 | .movieRow--listarea {
10 | overflow-x : hidden;
11 | padding-left: 30px;
12 | }
13 |
14 | .movieRow--list {
15 | transition : all ease 0.2s;
16 | }
17 |
18 | .movieRow--item{
19 | display: inline-block;
20 | width:150px;
21 | cursor: pointer;
22 | }
23 |
24 | .movieRow--item img{
25 | width: 100%;
26 | transform: scale(0.9);
27 | transition : all ease 0.2s;
28 | }
29 |
30 |
31 | .movieRow--item img:hover{
32 | transform: scale(1);
33 | }
34 |
35 | .movieRow-left,
36 | .movieRow-right {
37 | position:absolute;
38 | width: 40px;
39 | height: 225px;
40 | background-color:rgba(0,0,0,0.6);
41 | z-index: 99;
42 | display: flex;
43 | align-items: center;
44 | justify-content: center;
45 | overflow: hidden;
46 | cursor:pointer;
47 | opacity: 0;
48 | transition : all ease 0.2s;
49 | }
50 |
51 | .movieRow-left{
52 | left: 0;
53 | }
54 |
55 | .movieRow-right{
56 | right: 0;
57 | }
58 |
59 | .movieRow:hover .movieRow-left,
60 | .movieRow:hover .movieRow-right{
61 | opacity: 1;
62 | }
63 |
64 |
65 | @media (max-width: 760px){
66 | .movieRow-left,
67 | .movieRow-right {
68 | opacity: 1;
69 | }
70 | }
71 |
72 |
73 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/components/MovieRow/index.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import './styles.css';
3 | import NavigateBeforeIcon from '@material-ui/icons/NavigateBefore';
4 | import NavigateNextIcon from '@material-ui/icons/NavigateNext';
5 |
6 | export default ({title, items}) =>{
7 |
8 | const [scrollX, setScrollX]= useState(0);
9 |
10 | const handleLeftArrow = () => {
11 | let x = scrollX + Math.round(window.innerWidth / 2);
12 | if(x > 0){
13 | x = 0;
14 | }
15 | setScrollX(x);
16 | }
17 | const handleRightArrow = () =>{
18 | let x = scrollX - Math.round(window.innerWidth / 2);
19 | let listW = items.results.length * 150;
20 | if(window.innerWidth - listW > x)
21 | {
22 | x = (window.innerWidth - listW) - 60;
23 | }
24 | setScrollX(x);
25 | }
26 |
27 | return(
28 |
29 |
{title}
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
42 | {items.results.length > 0 && items.results.map((item, key)=>(
43 |
44 |

45 |
46 | ))}
47 |
48 |
49 |
50 | );
51 | }
--------------------------------------------------------------------------------
/src/components/FeaturedMovie/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './styles.css';
3 |
4 | export default ({item}) => {
5 |
6 | let firstDate = new Date(item.first_air_date);
7 |
8 | let genres = [];
9 | for(let i in item.genres){
10 | genres.push(item.genres[i].name);
11 | }
12 |
13 | let descr = item.overview;
14 | if(descr.length > 200){
15 | descr = descr.substring(0, 200) + '...';
16 | }
17 |
18 |
19 | return (
20 |
25 |
26 |
27 |
{item.original_name}
28 |
29 |
{item.vote_average} pontos
30 |
{firstDate.getFullYear()}
31 |
{item.number_of_seasons} temporada{item.number_of_season !== 1 ? 's' : ''}
32 |
33 |
{descr}
34 |
38 |
Gêneros: {genres.join(', ')}
39 |
40 |
41 |
42 |
43 |
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/FeaturedMovie/styles.css:
--------------------------------------------------------------------------------
1 | .featured{
2 | height: 100vh;
3 | }
4 |
5 | .featured--vertical{
6 | width: inherit;
7 | height: inherit;
8 | background: linear-gradient(to top, #111 10%, transparent);
9 | }
10 |
11 | .featured--horizontal{
12 | width: inherit;
13 | height: inherit;
14 | background: linear-gradient(to right, #111 30%, transparent 70%);
15 | display: flex;
16 | flex-direction: column;
17 | justify-content: center;
18 | padding-left: 30px;
19 | padding-bottom: 150px;
20 | padding-top : 70px;
21 | }
22 |
23 | .featured--name{
24 | font-size: 60px;
25 | font-weight: bold;
26 | }
27 |
28 | .featured--info{
29 | font-size: 18px;
30 | font-weight: bold;
31 | margin-top: 15px;
32 | }
33 |
34 | .featured--points{
35 | color: #46d369
36 | }
37 |
38 | .featured--description{
39 | margin-top:15px;
40 | font-size: 20px;
41 | color: #999;
42 | max-width: 40%;
43 | }
44 |
45 | .featured--buttons{
46 | margin-top:15px;
47 | }
48 |
49 | .featured--mylistbutton,
50 | .featured--watchbutton{
51 | display: inline-block;
52 | font-size:20px;
53 | font-weight: bold;
54 | padding: 12px 25px;
55 | border-radius: 5px;
56 | text-decoration: none;
57 | margin-right: 10px;
58 | opacity: 1;
59 | transition :all ease 0.2s;
60 | }
61 |
62 | .featured--mylistbutton:hover,
63 | .featured--watchbutton:hover{
64 | opacity: 0.7;
65 | }
66 | .featured--watchbutton{
67 | background-color: #fff;
68 | color: #000;
69 | }
70 |
71 | .featured--mylistbutton{
72 | background-color: #333;
73 | color: #fff;
74 | }
75 |
76 | .featured--genres{
77 | margin-top: 15px;
78 | font-size: 18px;
79 | color: #999;
80 | }
81 |
82 | @media (max-width:760px){
83 | .featured{
84 | height: 90vh;
85 | }
86 | .featured--name{
87 | font-size:40px;
88 | }
89 | .featured--info{
90 | font-size:16px;
91 | }
92 | .featured--description{
93 | font-size:14px;
94 | max-width: 100%;
95 | margin-right: 30px;
96 | }
97 | .featured--mylistbutton,
98 | .featured--watchbutton{
99 | font-size:10px;
100 | }
101 | .featured--genres{
102 | font-size:14px;
103 | }
104 | }
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useState} from 'react';
2 | import './App.css';
3 | import Tmdb from './Tmdb';
4 | import MovieRow from './components/MovieRow';
5 | import FeaturedMovie from './components/FeaturedMovie';
6 | import Header from './components/Header';
7 |
8 | export default () =>{
9 |
10 | const [movieList, setMovieList] = useState([]);
11 | const [featuredData, setFeaturedData]= useState(null);
12 | const [blackHeader, setblackHeader]= useState(false);
13 |
14 | useEffect(()=>{
15 | const loadAll = async () => {
16 | let list = await Tmdb.getHomeList();
17 | setMovieList(list);
18 |
19 | let originals = list.filter(i=> i.slug === 'originals');
20 | let randonChosen = Math.floor(Math.random() * (originals[0].items.results.length - 1));
21 | let chosen = originals[0].items.results[randonChosen]
22 | let chosenInfo = await Tmdb.getMovieInfo(chosen.id, 'tv');
23 | setFeaturedData(chosenInfo);
24 | }
25 |
26 | loadAll();
27 | }, []);
28 |
29 | useEffect(()=>{
30 | const scrollListener = () =>{
31 | if(window.scrollY > 10){
32 | setblackHeader(true);
33 | }else{
34 | setblackHeader(false);
35 | }
36 | }
37 |
38 | window.addEventListener('scroll', scrollListener);
39 |
40 | return () =>{
41 | window.removeEventListener('scroll', scrollListener);
42 | }
43 | }, []);
44 |
45 | return(
46 |
47 |
48 |
49 |
50 | {featuredData &&
51 |
52 | }
53 |
54 |
55 | {movieList.map((item, key) =>(
56 |
57 | ))
58 |
59 | }
60 |
61 |
65 |
66 |
67 | {movieList.length <= 0 &&
68 |
69 |

70 |
71 | }
72 |
73 | )
74 | }
75 |
76 |
77 |
78 | // https://youtu.be/tBweoUiMsDg?t=8054
--------------------------------------------------------------------------------
/src/Tmdb.js:
--------------------------------------------------------------------------------
1 | const API_KEY = '';
2 | const API_BASE = 'https://api.themoviedb.org/3';
3 |
4 |
5 | const basicFecth = async (endpoint) =>{
6 | return (await fetch(`${API_BASE}${endpoint}`)).json();
7 | }
8 |
9 | export default {
10 | getHomeList : async () =>{
11 | return [
12 | {
13 | slug: 'originals',
14 | title : "Originais do Netflix",
15 | items : await basicFecth(`/discover/tv/?with_network=213&language=pt-BR&api_key=${API_KEY}`)
16 | },
17 | {
18 | slug: 'trending',
19 | title : "Recomendados para Você",
20 | items : await basicFecth(`/trending/all/week?language=pt-BR&api_key=${API_KEY}`)
21 | },
22 | {
23 | slug: 'toprated',
24 | title : "Em Alta",
25 | items : await basicFecth(`/movie/top_rated?&language=pt-BR&api_key=${API_KEY}`)
26 | },
27 | {
28 | slug: 'action',
29 | title : "Ação",
30 | items : await basicFecth(`/discover/movie?with_genres=28&language=pt-BR&api_key=${API_KEY}`)
31 | },
32 | {
33 | slug: 'comedy',
34 | title : "Comédia",
35 | items : await basicFecth(`/discover/movie?with_genres=35&language=pt-BR&api_key=${API_KEY}`)
36 | },
37 | {
38 | slug: 'horror',
39 | title : "Terror",
40 | items : await basicFecth(`/discover/movie?with_genres=27&language=pt-BR&api_key=${API_KEY}`)
41 | },
42 | {
43 | slug: 'romance',
44 | title : "Romance",
45 | items : await basicFecth(`/discover/movie?with_genres=10749&language=pt-BR&api_key=${API_KEY}`)
46 | },
47 | {
48 | slug: 'documentary',
49 | title : "Documentários",
50 | items : await basicFecth(`/discover/movie?with_genres=99&language=pt-BR&api_key=${API_KEY}`)
51 | },
52 | ]
53 | },
54 |
55 | getMovieInfo : async (movieId, type) =>{
56 | let info = {};
57 | if(movieId) {
58 | switch(type){
59 | case 'movei':
60 | info = await basicFecth(`/movie/${movieId}?language=pt-BR&api_key=${API_KEY}`);
61 | break;
62 | case 'tv':
63 | info = await basicFecth(`/tv/${movieId}?language=pt-BR&api_key=${API_KEY}`);
64 | break;
65 | default:
66 | info = null;
67 | break;
68 | }
69 | }
70 |
71 | return info;
72 | }
73 | }
--------------------------------------------------------------------------------