├── arrays-block-buster ├── css │ └── styles.css ├── images │ └── logo.png ├── index.html └── js │ ├── app.js │ ├── filter.js │ ├── movies.js │ ├── normalize.js │ ├── recommended.js │ ├── render.js │ └── search.js ├── asignacion-desestructuracion ├── css │ └── styles.css ├── images │ ├── ash.jpg │ └── user.png └── index.html ├── block-buster ├── css │ └── styles.css ├── images │ └── logo.png ├── index.html └── src │ ├── actions │ └── index.js │ ├── api.js │ ├── components │ ├── actions.js │ ├── app.js │ ├── button.js │ ├── filters.js │ ├── form.js │ ├── header.js │ ├── input.js │ ├── movie-list.js │ ├── movie.js │ ├── search.js │ ├── select.js │ └── wrapper.js │ ├── constants.js │ ├── index.js │ ├── lib │ ├── react-dom.js │ ├── react │ │ ├── index.js │ │ └── src │ │ │ ├── React.js │ │ │ └── ReactElement.js │ └── styled-components.js │ ├── movies.js │ ├── normalize.js │ ├── reducers │ └── index.js │ ├── redux │ └── index.js │ └── store.js ├── modules ├── 01 │ ├── index.html │ └── module.js ├── 02 │ ├── css │ │ └── styles.css │ ├── index.html │ └── js │ │ ├── actions │ │ └── index.js │ │ ├── app.js │ │ ├── reducers │ │ └── index.js │ │ ├── redux │ │ └── index.js │ │ └── store.js ├── 03 │ ├── index.html │ └── js │ │ ├── app.js │ │ ├── components │ │ └── title.js │ │ ├── react-dom │ │ └── index.js │ │ ├── react │ │ └── index.js │ │ └── styled-components │ │ └── index.js ├── 04 │ ├── css │ │ └── styles.css │ ├── index.html │ └── js │ │ ├── app.js │ │ └── icecream │ │ ├── icream-machine.js │ │ ├── index.js │ │ └── validations.js └── 05 │ ├── css │ └── styles.css │ ├── images │ └── ps5.jpeg │ ├── index.html │ └── js │ ├── app.js │ └── player.js ├── parameter-defaults ├── css │ └── styles.css ├── images │ └── mac.png └── index.html ├── quemador-calorias ├── css │ └── styles.css └── index.html ├── react-create-element ├── components │ ├── app.js │ ├── user-styled.js │ ├── user.js │ └── wrapper.js ├── images │ └── ash.jpg ├── index.html ├── index.js ├── lib │ ├── react-dom.js │ ├── react │ │ ├── index.js │ │ └── src │ │ │ ├── React.js │ │ │ └── ReactElement.js │ └── styled-components.js └── styles.css ├── react-hooks ├── css │ └── styles.css ├── images │ ├── ash.jpg │ └── user.png └── index.html ├── react ├── components │ ├── app.js │ ├── user-styled.js │ ├── user.js │ └── wrapper.js ├── images │ └── ash.jpg ├── index.html ├── index.js ├── lib │ ├── react-dom.js │ ├── react.js │ └── styled-components.js └── styles.css ├── redux └── index.html ├── rest-spread ├── css │ └── styles.css └── index.html ├── shorthand-property-names ├── css │ └── styles.css ├── images │ └── mac.png └── index.html ├── styled-components └── index.html ├── tagged-templates └── index.html └── template-literals-expresiones └── index.html /arrays-block-buster/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 4 | } 5 | .wrapper { 6 | max-width: 1280px; 7 | margin: auto; 8 | } 9 | 10 | .header { 11 | background: #0e3fa9; 12 | margin-bottom: 2em; 13 | text-align: center; 14 | } 15 | .header img { 16 | width: 200px; 17 | position: relative; 18 | top: 20px; 19 | filter: drop-shadow(3px 3px 0 #f2a30c); 20 | } 21 | 22 | .actions { 23 | display: flex; 24 | gap: 21px; 25 | padding: 1em 0 2em; 26 | } 27 | .actions > * { 28 | position: relative; 29 | } 30 | .actions > *::after { 31 | content: "/"; 32 | color: #3f69ff; 33 | display: inline-flex; 34 | position: absolute; 35 | left: calc(100% + 10px); 36 | text-align: center; 37 | height: 100%; 38 | top: 0; 39 | display: flex; 40 | align-items: center; 41 | } 42 | .actions > *::after:last-child { 43 | display: none; 44 | } 45 | 46 | form { 47 | /* border: 1px solid red; */ 48 | min-width: 320px; 49 | display: flex; 50 | gap: .5em; 51 | } 52 | input { 53 | font-size: 1rem; 54 | font-family: system-ui; 55 | padding: .5em 1em; 56 | flex: 1; 57 | } 58 | select { 59 | font-size: 1rem; 60 | padding: 0 1em; 61 | border-radius: .5em; 62 | } 63 | button { 64 | font-size: 1rem; 65 | background: #3f69ff; 66 | padding: 1em; 67 | color: white; 68 | border: none; 69 | border-radius: .5em; 70 | } 71 | 72 | .movie-list { 73 | display: grid; 74 | grid-template-columns: repeat(auto-fit, 200px); 75 | justify-content: center; 76 | box-sizing: border-box; 77 | gap: 1em; 78 | } 79 | 80 | 81 | .movie { 82 | position: relative; 83 | transition: 1s; 84 | } 85 | .movie.recommended img { 86 | border: 2px solid goldenrod; 87 | box-sizing: border-box; 88 | perspective-origin: top; 89 | animation: recommended 1s forwards; 90 | } 91 | 92 | @keyframes recommended { 93 | to { 94 | transform: perspective(500px) rotate3d(0, -1, 0, 25deg); 95 | } 96 | } 97 | .movie.recommended .movie-rate { 98 | background-color: goldenrod; 99 | box-shadow: 0 0 0 2px goldenrod; 100 | } 101 | 102 | .movie-poster { 103 | max-width: 100%; 104 | width: 100%; 105 | border-radius: .7em; 106 | } 107 | 108 | .movie-title { 109 | font-weight: bold; 110 | } 111 | 112 | .movie-rate { 113 | position: absolute; 114 | left: 1em; 115 | top: 1em; 116 | background: #839eff; 117 | color: white; 118 | width: 40px; 119 | height: 40px; 120 | display: flex; 121 | justify-content: center; 122 | align-items: center; 123 | border-radius: 50%; 124 | border: 2px solid white; 125 | box-shadow: 0 0 0 2px #3f69ff; 126 | } 127 | -------------------------------------------------------------------------------- /arrays-block-buster/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonidasEsteban/curso-javascript-react/7a52f7d0bf55754815d4dfacdf049b4f7141d4d2/arrays-block-buster/images/logo.png -------------------------------------------------------------------------------- /arrays-block-buster/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BlockBuster 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 |
21 | 22 | 23 |
24 | 25 | 30 |
31 |
32 |
33 |
34 | 40 |
41 |
42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /arrays-block-buster/js/app.js: -------------------------------------------------------------------------------- 1 | import movies from './movies.js' 2 | import render from './render.js' 3 | import './recommended.js' 4 | import './search.js' 5 | import './filter.js' 6 | import './normalize.js' 7 | render(movies) -------------------------------------------------------------------------------- /arrays-block-buster/js/filter.js: -------------------------------------------------------------------------------- 1 | import { 2 | renderMovieListFromMap 3 | } from './render.js' 4 | 5 | import { 6 | movieList, 7 | all, 8 | popular as mostValued, 9 | notPopular as leastValued 10 | } from './normalize.js' 11 | 12 | filter.addEventListener('change', function () { 13 | switch (this.value) { 14 | case 'most-valued': 15 | return renderMovieListFromMap(mostValued, movieList) 16 | case 'least-valued': 17 | return renderMovieListFromMap(leastValued, movieList) 18 | default: 19 | return renderMovieListFromMap(all, movieList) 20 | } 21 | }) -------------------------------------------------------------------------------- /arrays-block-buster/js/movies.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | "popularity": 1616.326, 4 | "vote_count": 18, 5 | "video": false, 6 | "poster_path": "/ugZW8ocsrfgI95pnQ7wrmKDxIe.jpg", 7 | "id": 724989, 8 | "adult": false, 9 | "backdrop_path": "/86L8wqGMDbwURPni2t7FQ0nDjsH.jpg", 10 | "original_language": "en", 11 | "original_title": "Hard Kill", 12 | "genre_ids": [ 13 | 28, 14 | 53 15 | ], 16 | "title": "Hard Kill", 17 | "vote_average": 5.8, 18 | "overview": "The work of billionaire tech CEO Donovan Chalmers is so valuable that he hires mercenaries to protect it, and a terrorist group kidnaps his daughter just to get it.", 19 | "release_date": "2020-08-25" 20 | }, 21 | { 22 | "popularity": 1456.937, 23 | "vote_count": 159, 24 | "video": false, 25 | "poster_path": "/zVncJzXzwIO15YM1WilqYn30r54.jpg", 26 | "id": 718444, 27 | "adult": false, 28 | "backdrop_path": "/x4UkhIQuHIJyeeOTdcbZ3t3gBSa.jpg", 29 | "original_language": "en", 30 | "original_title": "Rogue", 31 | "genre_ids": [ 32 | 28 33 | ], 34 | "title": "Rogue", 35 | "vote_average": 6, 36 | "overview": "Battle-hardened O’Hara leads a lively mercenary team of soldiers on a daring mission: rescue hostages from their captors in remote Africa. But as the mission goes awry and the team is stranded, O’Hara’s squad must face a bloody, brutal encounter with a gang of rebels.", 37 | "release_date": "2020-08-20" 38 | }, 39 | { 40 | "popularity": 1171.542, 41 | "vote_count": 30, 42 | "video": false, 43 | "poster_path": "/9Rj8l6gElLpRL7Kj17iZhrT5Zuw.jpg", 44 | "id": 734309, 45 | "adult": false, 46 | "backdrop_path": "/7fvdg211A2L0mHddvzyArRuRalp.jpg", 47 | "original_language": "en", 48 | "original_title": "Santana", 49 | "genre_ids": [ 50 | 28 51 | ], 52 | "title": "Santana", 53 | "vote_average": 6.4, 54 | "overview": "Two brothers — one a narcotics agent and the other a general — finally discover the identity of the drug lord who murdered their parents decades ago. They may kill each other before capturing the bad guys.", 55 | "release_date": "2020-08-28" 56 | }, 57 | { 58 | "popularity": 1123.256, 59 | "vote_count": 108, 60 | "video": false, 61 | "poster_path": "/sMO1v5TUf8GOJHbJieDXsgWT2Ud.jpg", 62 | "id": 438396, 63 | "adult": false, 64 | "backdrop_path": "/qGZe9qTuydxyJYQ60XDtEckzLR8.jpg", 65 | "original_language": "es", 66 | "original_title": "Orígenes secretos", 67 | "genre_ids": [ 68 | 18, 69 | 53 70 | ], 71 | "title": "Unknown Origins", 72 | "vote_average": 6.2, 73 | "overview": "In Madrid, Spain, a mysterious serial killer ruthlessly murders his victims by recreating the first appearance of several comic book superheroes. Cosme, a veteran police inspector who is about to retire, works on the case along with the tormented inspector David Valentín and his own son Jorge Elías, a nerdy young man who owns a comic book store.", 74 | "release_date": "2020-08-28" 75 | }, 76 | { 77 | "popularity": 1066.224, 78 | "vote_count": 44, 79 | "video": false, 80 | "poster_path": "/n6hptKS7Y0ZjkYwbqKOK3jz9XAC.jpg", 81 | "id": 594328, 82 | "adult": false, 83 | "backdrop_path": "/lkeBhXGJFRlhI7cBWn8LQQAdZqK.jpg", 84 | "original_language": "en", 85 | "original_title": "Phineas and Ferb The Movie Candace Against the Universe", 86 | "genre_ids": [ 87 | 16, 88 | 35, 89 | 10402, 90 | 878, 91 | 10751, 92 | 10770 93 | ], 94 | "title": "Phineas and Ferb The Movie Candace Against the Universe", 95 | "vote_average": 7.1, 96 | "overview": "Phineas and Ferb travel across the galaxy to rescue their older sister Candace, who has been abducted by aliens and taken to a utopia in a far-off planet, free of her pesky little brothers.", 97 | "release_date": "2020-08-28" 98 | }, 99 | { 100 | "popularity": 1046.033, 101 | "vote_count": 1102, 102 | "video": false, 103 | "poster_path": "/TnOeov4w0sTtV2gqICqIxVi74V.jpg", 104 | "id": 605116, 105 | "adult": false, 106 | "backdrop_path": "/qVygtf2vU15L2yKS4Ke44U4oMdD.jpg", 107 | "original_language": "en", 108 | "original_title": "Project Power", 109 | "genre_ids": [ 110 | 28, 111 | 80, 112 | 878 113 | ], 114 | "title": "Project Power", 115 | "vote_average": 6.7, 116 | "overview": "An ex-soldier, a teen and a cop collide in New Orleans as they hunt for the source behind a dangerous new pill that grants users temporary superpowers.", 117 | "release_date": "2020-08-14" 118 | }, 119 | { 120 | "popularity": 958.038, 121 | "vote_count": 341, 122 | "video": false, 123 | "poster_path": "/sy6DvAu72kjoseZEjocnm2ZZ09i.jpg", 124 | "id": 581392, 125 | "adult": false, 126 | "backdrop_path": "/gEjNlhZhyHeto6Fy5wWy5Uk3A9D.jpg", 127 | "original_language": "ko", 128 | "original_title": "반도", 129 | "genre_ids": [ 130 | 28, 131 | 27, 132 | 53 133 | ], 134 | "title": "Peninsula", 135 | "vote_average": 7.2, 136 | "overview": "A soldier and his team battle hordes of post-apocalyptic zombies in the wastelands of the Korean Peninsula", 137 | "release_date": "2020-07-15" 138 | }, 139 | { 140 | "popularity": 859.648, 141 | "vote_count": 134, 142 | "video": false, 143 | "poster_path": "/tM4hht0LdY06UbuxGR4LjK6adCD.jpg", 144 | "id": 613504, 145 | "adult": false, 146 | "backdrop_path": "/dZJJDmiwp0W1NE74SY5WV00v0Ec.jpg", 147 | "original_language": "en", 148 | "original_title": "After We Collided", 149 | "genre_ids": [ 150 | 18, 151 | 10749 152 | ], 153 | "title": "After We Collided", 154 | "vote_average": 7.3, 155 | "overview": "Tessa finds herself struggling with her complicated relationship with Hardin; she faces a dilemma that could change their lives forever.", 156 | "release_date": "2020-09-02" 157 | }, 158 | { 159 | "popularity": 843.438, 160 | "vote_count": 855, 161 | "video": false, 162 | "poster_path": "/aKx1ARwG55zZ0GpRvU2WrGrCG9o.jpg", 163 | "id": 337401, 164 | "adult": false, 165 | "backdrop_path": "/xl5oCFLVMo4d4Pgxvrf8Jmc2IlA.jpg", 166 | "original_language": "en", 167 | "original_title": "Mulan", 168 | "genre_ids": [ 169 | 28, 170 | 12, 171 | 18, 172 | 14, 173 | 10752 174 | ], 175 | "title": "Mulan", 176 | "vote_average": 7.8, 177 | "overview": "When the Emperor of China issues a decree that one man per family must serve in the Imperial Chinese Army to defend the country from Huns, Hua Mulan, the eldest daughter of an honored warrior, steps in to take the place of her ailing father. She is spirited, determined and quick on her feet. Disguised as a man by the name of Hua Jun, she is tested every step of the way and must harness her innermost strength and embrace her true potential.", 178 | "release_date": "2020-09-04" 179 | }, 180 | { 181 | "popularity": 812.371, 182 | "vote_count": 54, 183 | "video": false, 184 | "poster_path": "/eDnHgozW8vfOaLHzfpHluf1GZCW.jpg", 185 | "id": 606234, 186 | "adult": false, 187 | "backdrop_path": "/u9YEh2xVAPVTKoaMNlB5tH6pXkm.jpg", 188 | "original_language": "en", 189 | "original_title": "Archive", 190 | "genre_ids": [ 191 | 878 192 | ], 193 | "title": "Archive", 194 | "vote_average": 6, 195 | "overview": "2038: George Almore is working on a true human-equivalent AI, and his latest prototype is almost ready. This sensitive phase is also the riskiest as he has a goal that must be hidden at all costs—being reunited with his dead wife.", 196 | "release_date": "2020-08-13" 197 | }, 198 | { 199 | "popularity": 804.043, 200 | "vote_count": 65, 201 | "video": false, 202 | "poster_path": "/i4kPwXPlM1iy8Jf3S1uuLuwqQAV.jpg", 203 | "id": 721452, 204 | "adult": false, 205 | "backdrop_path": "/riDrpqQtZpXGeiJdlmfcwwPH7nN.jpg", 206 | "original_language": "en", 207 | "original_title": "One Night in Bangkok", 208 | "genre_ids": [ 209 | 28, 210 | 53 211 | ], 212 | "title": "One Night in Bangkok", 213 | "vote_average": 7.2, 214 | "overview": "A hit man named Kai flies into Bangkok, gets a gun, and orders a cab. He offers a professional female driver big money to be his all-night driver. But when she realizes Kai is committing brutal murders at each stop, it's too late to walk away. Meanwhile, an offbeat police detective races to decode the string of slayings before more blood is spilled.", 215 | "release_date": "2020-08-25" 216 | }, 217 | { 218 | "popularity": 765.892, 219 | "vote_count": 214, 220 | "video": false, 221 | "poster_path": "/uGhQ2ZGBpzCj6wC5jUrybsZuPTI.jpg", 222 | "id": 539885, 223 | "adult": false, 224 | "backdrop_path": "/ekkuqt9Q2ad1F7xq2ZONP0oq36P.jpg", 225 | "original_language": "en", 226 | "original_title": "Ava", 227 | "genre_ids": [ 228 | 28, 229 | 80, 230 | 18, 231 | 53 232 | ], 233 | "title": "Ava", 234 | "vote_average": 6.1, 235 | "overview": "A black ops assassin is forced to fight for her own survival after a job goes dangerously wrong.", 236 | "release_date": "2020-08-06" 237 | }, 238 | { 239 | "popularity": 532.282, 240 | "vote_count": 68, 241 | "video": false, 242 | "poster_path": "/5pe30v0z4ucVgwh5nR439cCzwwO.jpg", 243 | "id": 632618, 244 | "adult": false, 245 | "backdrop_path": "/cVdYaAQmd5DZNdo0KFJruz7JpUs.jpg", 246 | "original_language": "es", 247 | "original_title": "Crímenes de familia", 248 | "genre_ids": [ 249 | 80, 250 | 18, 251 | 53 252 | ], 253 | "title": "The Crimes That Bind", 254 | "vote_average": 7, 255 | "overview": "When her son is accused of raping and trying to murder his ex-wife, Alicia embarks on a journey that will change her life forever.", 256 | "release_date": "2020-08-20" 257 | }, 258 | { 259 | "popularity": 509.318, 260 | "vote_count": 571, 261 | "video": false, 262 | "poster_path": "/jHo2M1OiH9Re33jYtUQdfzPeUkx.jpg", 263 | "id": 385103, 264 | "adult": false, 265 | "backdrop_path": "/fKtYXUhX5fxMxzQfyUcQW9Shik6.jpg", 266 | "original_language": "en", 267 | "original_title": "Scoob!", 268 | "genre_ids": [ 269 | 12, 270 | 16, 271 | 35, 272 | 10751 273 | ], 274 | "title": "Scoob!", 275 | "vote_average": 7.4, 276 | "overview": "In Scooby-Doo’s greatest adventure yet, see the never-before told story of how lifelong friends Scooby and Shaggy first met and how they joined forces with young detectives Fred, Velma, and Daphne to form the famous Mystery Inc. Now, with hundreds of cases solved, Scooby and the gang face their biggest, toughest mystery ever: an evil plot to unleash the ghost dog Cerberus upon the world. As they race to stop this global “dogpocalypse,” the gang discovers that Scooby has a secret legacy and an epic destiny greater than anyone ever imagined.", 277 | "release_date": "2020-07-08" 278 | }, 279 | { 280 | "popularity": 501.925, 281 | "vote_count": 94, 282 | "video": false, 283 | "poster_path": "/A11Ez4UkOE4Ysmtmur5Bho8qrGM.jpg", 284 | "id": 611395, 285 | "adult": false, 286 | "backdrop_path": "/qXACJOuyklS0BpvO8ALLkkrsv7W.jpg", 287 | "original_language": "zh", 288 | "original_title": "征途", 289 | "genre_ids": [ 290 | 28, 291 | 12, 292 | 14 293 | ], 294 | "title": "Double World", 295 | "vote_average": 6.9, 296 | "overview": "Keen to bring honor to his clan, young villager Dong Yilong embarks on a perilous journey to compete in a tournament that selects warriors for battle.", 297 | "release_date": "2020-07-24" 298 | }, 299 | { 300 | "popularity": 473.21, 301 | "vote_count": 375, 302 | "video": false, 303 | "poster_path": "/kPzcvxBwt7kEISB9O4jJEuBn72t.jpg", 304 | "id": 677638, 305 | "adult": false, 306 | "backdrop_path": "/pO1SnM5a1fEsYrFaVZW78Wb0zRJ.jpg", 307 | "original_language": "en", 308 | "original_title": "We Bare Bears: The Movie", 309 | "genre_ids": [ 310 | 12, 311 | 16, 312 | 35, 313 | 10751 314 | ], 315 | "title": "We Bare Bears: The Movie", 316 | "vote_average": 7.8, 317 | "overview": "When Grizz, Panda, and Ice Bear's love of food trucks and viral videos went out of hand, it catches the attention of Agent Trout from the National Wildlife Control, who pledges to restore the “natural order” by separating them forever. Chased away from their home, the Bears embark on an epic road trip as they seek refuge in Canada, with their journey being filled with new friends, perilous obstacles, and huge parties. The risky journey also forces the Bears to face how they first met and became brothers, in order to keep their family bond from splitting apart.", 318 | "release_date": "2020-06-30" 319 | }, 320 | { 321 | "popularity": 460.596, 322 | "vote_count": 113, 323 | "video": false, 324 | "poster_path": "/4V2nTPfeB59TcqJcUfQ9ziTi7VN.jpg", 325 | "id": 501979, 326 | "adult": false, 327 | "backdrop_path": "/oazPqs1z78LcIOFslbKtJLGlueo.jpg", 328 | "original_language": "en", 329 | "original_title": "Bill & Ted Face the Music", 330 | "genre_ids": [ 331 | 12, 332 | 35, 333 | 878 334 | ], 335 | "title": "Bill & Ted Face the Music", 336 | "vote_average": 6.5, 337 | "overview": "Yet to fulfill their rock and roll destiny, the now middle-aged best friends Bill and Ted set out on a new adventure when a visitor from the future warns them that only their song can save life as we know it. Along the way, they are helped by their daughters, a new batch of historical figures and a few music legends—to seek the song that will set their world right and bring harmony to the universe.", 338 | "release_date": "2020-08-27" 339 | }, 340 | { 341 | "popularity": 444.068, 342 | "vote_count": 65, 343 | "video": false, 344 | "poster_path": "/bhNHCeJDFDaB00A46AoCw2mggdE.jpg", 345 | "id": 601165, 346 | "adult": false, 347 | "backdrop_path": "/nxxODhq9I05Ze9uLONGvfDrzaUO.jpg", 348 | "original_language": "en", 349 | "original_title": "Legacy of Lies", 350 | "genre_ids": [ 351 | 28, 352 | 53 353 | ], 354 | "title": "Legacy of Lies", 355 | "vote_average": 6.1, 356 | "overview": "An ex-MI6 agent is thrown back into the world of espionage and high stakes to uncover the shocking truth about operations conducted by unknown secret services.", 357 | "release_date": "2020-08-06" 358 | }, 359 | { 360 | "popularity": 442.72, 361 | "vote_count": 13, 362 | "video": false, 363 | "poster_path": "/uvMjNLot0dG7CX4HZPme2WDkMmE.jpg", 364 | "id": 523849, 365 | "adult": false, 366 | "backdrop_path": "/AmTfxc3S22z7WWC7KAR3SPs70Bl.jpg", 367 | "original_language": "en", 368 | "original_title": "The Last Sharknado: It's About Time", 369 | "genre_ids": [ 370 | 28, 371 | 12, 372 | 35, 373 | 878, 374 | 10770 375 | ], 376 | "title": "The Last Sharknado: It's About Time", 377 | "vote_average": 6.2, 378 | "overview": "With much of America lying in ruins, the rest of the world braces for a global sharknado, Fin and his family must travel around the world to stop them.", 379 | "release_date": "2020-07-17" 380 | }, 381 | { 382 | "popularity": 438.189, 383 | "vote_count": 23, 384 | "video": false, 385 | "poster_path": "/q2lkJf1TAjImTHCEO7XvbqPtnPb.jpg", 386 | "id": 703134, 387 | "adult": false, 388 | "backdrop_path": "/j57oUw8LIYvjOl0zs3A1A1UqwKH.jpg", 389 | "original_language": "en", 390 | "original_title": "Infamous", 391 | "genre_ids": [ 392 | 80, 393 | 18, 394 | 53 395 | ], 396 | "title": "Infamous", 397 | "vote_average": 5.4, 398 | "overview": "Two young lovers rob their way across the southland, posting their exploits to social media, and gaining fame and followers as a result.", 399 | "release_date": "2020-06-12" 400 | } 401 | , 402 | { 403 | "popularity": 437.445, 404 | "vote_count": 799, 405 | "video": false, 406 | "poster_path": "/tI8ocADh22GtQFV28vGHaBZVb0U.jpg", 407 | "id": 475430, 408 | "adult": false, 409 | "backdrop_path": "/o0F8xAt8YuEm5mEZviX5pEFC12y.jpg", 410 | "original_language": "en", 411 | "original_title": "Artemis Fowl", 412 | "genre_ids": [ 413 | 28, 414 | 12, 415 | 14, 416 | 878, 417 | 10751 418 | ], 419 | "title": "Artemis Fowl", 420 | "vote_average": 5.8, 421 | "overview": "Artemis Fowl is a 12-year-old genius and descendant of a long line of criminal masterminds. He soon finds himself in an epic battle against a race of powerful underground fairies who may be behind his father's disappearance.", 422 | "release_date": "2020-06-12" 423 | }, 424 | { 425 | "popularity": 436.561, 426 | "vote_count": 1943, 427 | "video": false, 428 | "poster_path": "/cjr4NWURcVN3gW5FlHeabgBHLrY.jpg", 429 | "id": 547016, 430 | "adult": false, 431 | "backdrop_path": "/m0ObOaJBerZ3Unc74l471ar8Iiy.jpg", 432 | "original_language": "en", 433 | "original_title": "The Old Guard", 434 | "genre_ids": [ 435 | 28, 436 | 14 437 | ], 438 | "title": "The Old Guard", 439 | "vote_average": 7.3, 440 | "overview": "Four undying warriors who've secretly protected humanity for centuries become targeted for their mysterious powers just as they discover a new immortal.", 441 | "release_date": "2020-07-10" 442 | }, 443 | { 444 | "popularity": 435.283, 445 | "vote_count": 107, 446 | "video": false, 447 | "poster_path": "/e7ZsW5EbLbQwoGx0548KCmCAXA9.jpg", 448 | "id": 508570, 449 | "adult": false, 450 | "backdrop_path": "/fFdOJxmG2U7IYYlkFKtDk1nGPhF.jpg", 451 | "original_language": "en", 452 | "original_title": "The One and Only Ivan", 453 | "genre_ids": [ 454 | 35, 455 | 18, 456 | 10751 457 | ], 458 | "title": "The One and Only Ivan", 459 | "vote_average": 7.2, 460 | "overview": "Ivan is a 400-pound silverback gorilla who shares a communal habitat in a suburban shopping mall with Stella the elephant, Bob the dog, and various other animals. He has few memories of the jungle where he was captured, but when a baby elephant named Ruby arrives, it touches something deep within him. Ruby is recently separated from her family in the wild, which causes him to question his life, where he comes from and where he ultimately wants to be.", 461 | "release_date": "2020-08-21" 462 | }, 463 | { 464 | "popularity": 428.33, 465 | "vote_count": 139, 466 | "video": false, 467 | "poster_path": "/6Bbq8qQWpoApLZYWFFAuZ1r2gFw.jpg", 468 | "id": 618354, 469 | "adult": false, 470 | "backdrop_path": "/bazlsLkNuhy3IuhviepqvlMm2hV.jpg", 471 | "original_language": "en", 472 | "original_title": "Superman: Man of Tomorrow", 473 | "genre_ids": [ 474 | 28, 475 | 16, 476 | 878 477 | ], 478 | "title": "Superman: Man of Tomorrow", 479 | "vote_average": 7.3, 480 | "overview": "It’s the dawn of a new age of heroes, and Metropolis has just met its first. But as Daily Planet intern Clark Kent – working alongside reporter Lois Lane – secretly wields his alien powers of flight, super-strength and x-ray vision in the battle for good, there’s even greater trouble on the horizon.", 481 | "release_date": "2020-08-23" 482 | }, 483 | { 484 | "popularity": 427.083, 485 | "vote_count": 6, 486 | "video": false, 487 | "poster_path": "/o1WvNhoackad1QiAGRgjJCQ1Trj.jpg", 488 | "id": 724717, 489 | "adult": false, 490 | "backdrop_path": "/AbtsLdz1gUj2H1HJJ3TRaBOl8Ta.jpg", 491 | "original_language": "en", 492 | "original_title": "The 2nd", 493 | "genre_ids": [ 494 | 28 495 | ], 496 | "title": "The 2nd", 497 | "vote_average": 6.7, 498 | "overview": "Secret-service agent Vic Davis is on his way to pick up his estranged son, Sean, from his college campus when he finds himself in the middle of a high-stakes terrorist operation. His son's friend Erin Walton, the daughter of Supreme Court Justice Walton is the target, and this armed faction will stop at nothing to kidnap her and use her as leverage for a pending landmark legal case.", 499 | "release_date": "2020-09-01" 500 | }, 501 | { 502 | "popularity": 426.947, 503 | "vote_count": 7, 504 | "video": false, 505 | "poster_path": "/dKSN0oZCUSNcEd39MnySLYmpUiJ.jpg", 506 | "id": 735110, 507 | "adult": false, 508 | "backdrop_path": "/aahbYclKYfms6Utm5YHQOywsj9N.jpg", 509 | "original_language": "es", 510 | "original_title": "Fuego negro", 511 | "genre_ids": [ 512 | 28, 513 | 27, 514 | 9648, 515 | 53 516 | ], 517 | "title": "Dark Forces", 518 | "vote_average": 5, 519 | "overview": "In search of his sister, a renegade criminal seeks answers at a sordid hotel where he encounters a sinister guest and romances a mysterious waitress.", 520 | "release_date": "2020-08-21" 521 | }, 522 | { 523 | "popularity": 419.135, 524 | "vote_count": 124, 525 | "video": false, 526 | "poster_path": "/3eg0kGC2Xh0vhydJHO37Sp4cmMt.jpg", 527 | "id": 531499, 528 | "adult": false, 529 | "backdrop_path": "/zogWnCSztU8xvabaepQnAwsOtOt.jpg", 530 | "original_language": "en", 531 | "original_title": "The Tax Collector", 532 | "genre_ids": [ 533 | 28, 534 | 80, 535 | 18 536 | ], 537 | "title": "The Tax Collector", 538 | "vote_average": 6, 539 | "overview": "David Cuevas is a family man who works as a gangland tax collector for high ranking Los Angeles gang members. He makes collections across the city with his partner Creeper making sure people pay up or will see retaliation. An old threat returns to Los Angeles that puts everything David loves in harm’s way.", 540 | "release_date": "2020-08-07" 541 | }, 542 | { 543 | "popularity": 413.928, 544 | "id": 603119, 545 | "video": false, 546 | "vote_count": 85, 547 | "vote_average": 6.9, 548 | "title": "The Silencing", 549 | "release_date": "2020-07-18", 550 | "original_language": "en", 551 | "original_title": "The Silencing", 552 | "genre_ids": [ 553 | 80, 554 | 53, 555 | 28 556 | ], 557 | "backdrop_path": "/aSdp2uS0jWkqdhCeyhJuCoiLZji.jpg", 558 | "adult": false, 559 | "overview": "A reformed hunter becomes involved in a deadly game of cat and mouse when he and the local sheriff set out to track a vicious killer who may have kidnapped his daughter years ago.", 560 | "poster_path": "/dnN1ncxEOO1TY0gYL2FWxJqlhlL.jpg" 561 | }, 562 | { 563 | "popularity": 412.505, 564 | "vote_count": 4, 565 | "video": false, 566 | "poster_path": "/w6e0XZreiyW4mGlLRHEG8ipff7b.jpg", 567 | "id": 722603, 568 | "adult": false, 569 | "backdrop_path": "/m7QpUAeI2xTCJyAVl9J9z5dBTSb.jpg", 570 | "original_language": "en", 571 | "original_title": "Battlefield 2025", 572 | "genre_ids": [ 573 | 28, 574 | 27, 575 | 878 576 | ], 577 | "title": "Battlefield 2025", 578 | "vote_average": 5.3, 579 | "overview": "Weekend campers, an escaped convict, young lovers and a police officer experience a night of terror when a hostile visitor from another world descends on a small Arizona town.", 580 | "release_date": "2020-07-07" 581 | }, 582 | { 583 | "popularity": 391.98, 584 | "vote_count": 133, 585 | "video": false, 586 | "poster_path": "/vFIHbiy55smzi50RmF8LQjmpGcx.jpg", 587 | "id": 703771, 588 | "adult": false, 589 | "backdrop_path": "/owraiceOKtSOa3t8sp3wA9K2Ox6.jpg", 590 | "original_language": "en", 591 | "original_title": "Deathstroke: Knights & Dragons - The Movie", 592 | "genre_ids": [ 593 | 28, 594 | 16 595 | ], 596 | "title": "Deathstroke: Knights & Dragons - The Movie", 597 | "vote_average": 7, 598 | "overview": "Ten years ago, Slade Wilson-aka the super-assassin called Deathstroke-made a tragic mistake and his wife and son paid a terrible price. Now, a decade later, Wilson's family is threatened once again by the murderous Jackal and the terrorists of H.I.V.E. Can Deathstroke atone for the sins of the past-or will his family pay the ultimate price?", 599 | "release_date": "2020-08-04" 600 | }, 601 | { 602 | "popularity": 373.784, 603 | "vote_count": 5094, 604 | "video": false, 605 | "poster_path": "/y95lQLnuNKdPAzw9F9Ab8kJ80c3.jpg", 606 | "id": 38700, 607 | "adult": false, 608 | "backdrop_path": "/upUy2QhMZEmtypPW3PdieKLAHxh.jpg", 609 | "original_language": "en", 610 | "original_title": "Bad Boys for Life", 611 | "genre_ids": [ 612 | 28, 613 | 80, 614 | 53 615 | ], 616 | "title": "Bad Boys for Life", 617 | "vote_average": 7.3, 618 | "overview": "Marcus and Mike are forced to confront new threats, career changes, and midlife crises as they join the newly created elite team AMMO of the Miami police department to take down the ruthless Armando Armas, the vicious leader of a Miami drug cartel.", 619 | "release_date": "2020-01-15" 620 | }, 621 | { 622 | "popularity": 352.444, 623 | "vote_count": 228, 624 | "video": false, 625 | "poster_path": "/bKthjUmxjHjvJK8FktFfQdmwP12.jpg", 626 | "id": 703745, 627 | "adult": false, 628 | "backdrop_path": "/hIHtyIYgBqHybOgUdoAmveipuiO.jpg", 629 | "original_language": "en", 630 | "original_title": "Deep Blue Sea 3", 631 | "genre_ids": [ 632 | 28, 633 | 27, 634 | 878 635 | ], 636 | "title": "Deep Blue Sea 3", 637 | "vote_average": 6.2, 638 | "overview": "Dr. Emma Collins and her team are spending their third summer on the island of Little Happy studying the effect of climate change on the great white sharks who come to the nearby nursery every year to give birth. Along with the last two inhabitants of this former fishing village, their peaceful life is disrupted when a \"scientific\" team led by her ex-boyfriend and marine biologist Richard show up looking for three bull sharks who we soon learn aren't just any bull sharks.", 639 | "release_date": "2020-07-28" 640 | }, 641 | { 642 | "popularity": 349.588, 643 | "vote_count": 38, 644 | "video": false, 645 | "poster_path": "/xqbQtMffXwa3oprse4jiHBMBvdW.jpg", 646 | "id": 601844, 647 | "adult": false, 648 | "backdrop_path": "/qTrpw2ZUvN7ywUu1kieEsvNDrgQ.jpg", 649 | "original_language": "en", 650 | "original_title": "Becky", 651 | "genre_ids": [ 652 | 28, 653 | 27, 654 | 53 655 | ], 656 | "title": "Becky", 657 | "vote_average": 4.9, 658 | "overview": "A teenager's weekend at a lake house with her father takes a turn for the worse when a group of convicts wreaks havoc on their lives.", 659 | "release_date": "2020-07-23" 660 | }, 661 | { 662 | "popularity": 334.491, 663 | "vote_count": 1083, 664 | "video": false, 665 | "poster_path": "/kjMbDciooTbJPofVXgAoFjfX8Of.jpg", 666 | "id": 516486, 667 | "adult": false, 668 | "backdrop_path": "/xXBnM6uSTk6qqCf0SRZKXcga9Ba.jpg", 669 | "original_language": "en", 670 | "original_title": "Greyhound", 671 | "genre_ids": [ 672 | 28, 673 | 18, 674 | 10752 675 | ], 676 | "title": "Greyhound", 677 | "vote_average": 7.5, 678 | "overview": "A first-time captain leads a convoy of allied ships carrying thousands of soldiers across the treacherous waters of the “Black Pit” to the front lines of WW2. With no air cover protection for 5 days, the captain and his convoy must battle the surrounding enemy Nazi U-boats in order to give the allies a chance to win the war.", 679 | "release_date": "2020-06-19" 680 | }, 681 | { 682 | "popularity": 333.736, 683 | "vote_count": 5694, 684 | "video": false, 685 | "poster_path": "/h4VB6m0RwcicVEZvzftYZyKXs6K.jpg", 686 | "id": 495764, 687 | "adult": false, 688 | "backdrop_path": "/9xNOiv6DZZjH7ABoUUDP0ZynouU.jpg", 689 | "original_language": "en", 690 | "original_title": "Birds of Prey (and the Fantabulous Emancipation of One Harley Quinn)", 691 | "genre_ids": [ 692 | 28, 693 | 35, 694 | 80 695 | ], 696 | "title": "Birds of Prey (and the Fantabulous Emancipation of One Harley Quinn)", 697 | "vote_average": 7.2, 698 | "overview": "Harley Quinn joins forces with a singer, an assassin and a police detective to help a young girl who had a hit placed on her after she stole a rare diamond from a crime lord.", 699 | "release_date": "2020-02-05" 700 | }, 701 | { 702 | "popularity": 331.826, 703 | "vote_count": 364, 704 | "video": false, 705 | "poster_path": "/dqKqzcdhtJwOhjqe89RTURqILtl.jpg", 706 | "id": 514207, 707 | "adult": false, 708 | "backdrop_path": "/kUaE05cgWuuygBorOcBFNn2x3CO.jpg", 709 | "original_language": "ru", 710 | "original_title": "Вторжение", 711 | "genre_ids": [ 712 | 28, 713 | 18, 714 | 27, 715 | 878 716 | ], 717 | "title": "Invasion", 718 | "vote_average": 7.1, 719 | "overview": "After the fall of the alien ship, it took three years. The catastrophe turned the girl's life from Chertanovo and forever changed our view of the universe. It seems that this was the biggest test for all of us. But mankind does not yet know that very soon he will have to experience a new meeting.", 720 | "release_date": "2020-01-01" 721 | }, 722 | { 723 | "popularity": 309.216, 724 | "vote_count": 4, 725 | "video": false, 726 | "poster_path": "/pXv4qbWyj6ycMaWkK2LzlizZQjf.jpg", 727 | "id": 713825, 728 | "adult": false, 729 | "backdrop_path": "/tWxCVe4rQZa3BvR3tMT3t74oVTT.jpg", 730 | "original_language": "en", 731 | "original_title": "Robot Riot", 732 | "genre_ids": [ 733 | 28, 734 | 878 735 | ], 736 | "title": "Robot Riot", 737 | "vote_average": 3.8, 738 | "overview": "Unconscious soldiers are dropped into a testing site only to discover their memories have been wiped and that once docile machines are the new intelligence.", 739 | "release_date": "2020-06-12" 740 | }, 741 | { 742 | "popularity": 300.997, 743 | "vote_count": 124, 744 | "video": false, 745 | "poster_path": "/aVbqhqYtlxwEGihTEhewZAgDOCX.jpg", 746 | "id": 489326, 747 | "adult": false, 748 | "backdrop_path": "/dFB6Tiy3z2xRLbnEUB5ocApT5xG.jpg", 749 | "original_language": "en", 750 | "original_title": "Mortal", 751 | "genre_ids": [ 752 | 28, 753 | 14, 754 | 53 755 | ], 756 | "title": "Mortal", 757 | "vote_average": 6.8, 758 | "overview": "A young boy must discover the origins of his extraordinary powers before he is captured by authorities hell-bent on condemning him for an accidental murder.", 759 | "release_date": "2020-02-28" 760 | }, 761 | { 762 | "popularity": 294.997, 763 | "vote_count": 14643, 764 | "video": false, 765 | "poster_path": "/udDclJoHjfjb8Ekgsd4FDteOkCU.jpg", 766 | "id": 475557, 767 | "adult": false, 768 | "backdrop_path": "/n6bUvigpRFqSwmPp1m2YADdbRBc.jpg", 769 | "original_language": "en", 770 | "original_title": "Joker", 771 | "genre_ids": [ 772 | 80, 773 | 18, 774 | 53 775 | ], 776 | "title": "Joker", 777 | "vote_average": 8.2, 778 | "overview": "During the 1980s, a failed stand-up comedian is driven insane and turns to a life of crime and chaos in Gotham City while becoming an infamous psychopathic crime figure.", 779 | "release_date": "2019-10-02" 780 | }, 781 | { 782 | "popularity": 294.801, 783 | "vote_count": 152, 784 | "video": false, 785 | "poster_path": "/h7dZpJDORYs5c56dydbrLFkEXpE.jpg", 786 | "id": 723072, 787 | "adult": false, 788 | "backdrop_path": "/5TbtcmRySXPAEXBzwhiOYYDQmgv.jpg", 789 | "original_language": "en", 790 | "original_title": "Host", 791 | "genre_ids": [ 792 | 27 793 | ], 794 | "title": "Host", 795 | "vote_average": 6.9, 796 | "overview": "Six friends hire a medium to hold a séance via Zoom during lockdown — but they get far more than they bargained for as things quickly go wrong. When an evil spirit starts invading their homes, they begin to realize they might not survive the night.", 797 | "release_date": "2020-07-30" 798 | } 799 | ] 800 | -------------------------------------------------------------------------------- /arrays-block-buster/js/normalize.js: -------------------------------------------------------------------------------- 1 | import rawMoviesList from './movies.js' 2 | 3 | const movieList = rawMoviesList.reduce((list, movie) => { 4 | list.set(movie.id, movie) 5 | return list 6 | }, new Map()) 7 | 8 | 9 | const all = rawMoviesList.map(movie => movie.id) 10 | 11 | const popular = rawMoviesList.reduce((list, movie) => { 12 | if (movie.vote_average > 7) { 13 | list.push(movie.id) 14 | } 15 | return list 16 | }, []) 17 | 18 | const notPopular = rawMoviesList.reduce((list, movie) => { 19 | if (movie.vote_average <= 7) { 20 | list.push(movie.id) 21 | } 22 | return list 23 | }, []) 24 | 25 | export { 26 | movieList, 27 | all, 28 | popular, 29 | notPopular 30 | } -------------------------------------------------------------------------------- /arrays-block-buster/js/recommended.js: -------------------------------------------------------------------------------- 1 | import movies from './movies.js' 2 | import render from './render.js' 3 | 4 | const $button = window.recommended 5 | // const $button = document.querySelector('#recommended') 6 | // const $button = document.getElementById('recommended') 7 | 8 | $button.addEventListener('click', () => { 9 | render(setRecomendedMovies(movies)) 10 | }) 11 | 12 | function setRecomendedMovies(movies) { 13 | return movies.map((movie, index) => { 14 | return { ...movie, recommended: movie.vote_average > 7 } 15 | // if (movie.vote_average > 7) { 16 | // return { ...movie, recommended: true } 17 | // } 18 | // return movie 19 | }) 20 | // console.table(newMovies, ['title', 'vote_average', 'recommended']) 21 | } -------------------------------------------------------------------------------- /arrays-block-buster/js/render.js: -------------------------------------------------------------------------------- 1 | 2 | export function renderMovieListFromMap(list, map) { 3 | cleanMovieList() 4 | list.forEach(movieId => renderElement(map.get(movieId))) 5 | } 6 | 7 | function renderElement(movie) { 8 | const element = buildElement(movie) 9 | window.container.append(element) 10 | } 11 | 12 | function buildElement({ title, poster_path, vote_average, id, recommended }) { 13 | const template = ` 14 |
15 | 16 |

${title}

17 |

${id}

18 | ${vote_average} 19 |
20 | ` 21 | const movie = document.createElement('div') 22 | movie.innerHTML = template 23 | return movie.firstElementChild 24 | } 25 | 26 | function cleanMovieList() { 27 | window.container.innerHTML = '' 28 | } 29 | 30 | export default function renderMovieList(list) { 31 | cleanMovieList() 32 | // console.table(list, ['title', 'poster_path', 'vote_average', 'id']) 33 | list.forEach(renderElement) 34 | } -------------------------------------------------------------------------------- /arrays-block-buster/js/search.js: -------------------------------------------------------------------------------- 1 | import movies from './movies.js' 2 | import render from './render.js' 3 | 4 | const search = window['search-form'] 5 | 6 | search.addEventListener('submit', function (event) { 7 | event.preventDefault() 8 | const formData = new FormData(this) 9 | const query = formData.get('title') 10 | const movies = searchMovie(query) 11 | console.log(movies) 12 | if (movies) { 13 | return render(movies) 14 | } 15 | return alert('No enconramos tu película') 16 | }) 17 | 18 | function filterByTitle(title) { 19 | return movies.filter((movie) => { 20 | return movie.title.toLowerCase().includes(title.toLowerCase()) 21 | }) 22 | } 23 | 24 | function findById(id) { 25 | // return movies.filter((movie) => { 26 | return movies.find((movie) => { 27 | return movie.id === parseInt(id, 10) 28 | }) 29 | } 30 | 31 | function searchMovie(query) { 32 | if (isNaN(query)) { 33 | return filterByTitle(query) 34 | } 35 | return [findById(query)] 36 | // return findById(query) 37 | } 38 | 39 | -------------------------------------------------------------------------------- /asignacion-desestructuracion/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: system-ui, Helvetica, sans-serif; 4 | background-color: white; 5 | text-align: center; 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | min-height: 100vh; 10 | background: #f8f9fd; 11 | } 12 | .wrapper { 13 | max-width: 1020px; 14 | margin: auto; 15 | } 16 | h1 { 17 | font-size: 110px; 18 | line-height: 100px; 19 | } 20 | 21 | .button { 22 | color: white; 23 | background: #3f69ff; 24 | border: none; 25 | font-size: 1em; 26 | padding: .7em 2em; 27 | border-radius: .5em; 28 | font-weight: bold; 29 | cursor: pointer; 30 | } 31 | 32 | .profile { 33 | flex: 1; 34 | } 35 | 36 | .profile-content { 37 | height: 700px; 38 | background: url('../images/user.png') center top no-repeat; 39 | background-size: contain; 40 | max-width: 350px; 41 | margin: 1em auto; 42 | border-radius: 1em; 43 | box-shadow: 1px 1px 5px rgba(0,0,0,.1); 44 | } 45 | .user { 46 | padding-top: 30px; 47 | } 48 | img { 49 | border-radius: 50%; 50 | } 51 | .details { 52 | background: white; 53 | padding: 1px; 54 | font-size: 1.2em; 55 | } 56 | .social { 57 | /* margin-top: 50px; */ 58 | padding: 1em 0; 59 | font-weight: bold; 60 | background: #f8f9fd; 61 | display: flex; 62 | justify-content: space-evenly; 63 | } 64 | 65 | .bio { 66 | display: flex; 67 | background: white; 68 | } 69 | 70 | .city { 71 | background: white; 72 | margin: 0; 73 | padding: 1em; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /asignacion-desestructuracion/images/ash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonidasEsteban/curso-javascript-react/7a52f7d0bf55754815d4dfacdf049b4f7141d4d2/asignacion-desestructuracion/images/ash.jpg -------------------------------------------------------------------------------- /asignacion-desestructuracion/images/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonidasEsteban/curso-javascript-react/7a52f7d0bf55754815d4dfacdf049b4f7141d4d2/asignacion-desestructuracion/images/user.png -------------------------------------------------------------------------------- /asignacion-desestructuracion/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Destructuring 9 | 10 | 11 | 12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /block-buster/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 4 | } 5 | /* .wrapper { 6 | max-width: 1280px; 7 | margin: auto; 8 | } */ 9 | 10 | /* .header { 11 | background: #0e3fa9; 12 | margin-bottom: 2em; 13 | text-align: center; 14 | } 15 | .header img { 16 | width: 200px; 17 | position: relative; 18 | top: 20px; 19 | filter: drop-shadow(3px 3px 0 #f2a30c); 20 | } */ 21 | 22 | .actions { 23 | display: flex; 24 | gap: 21px; 25 | padding: 1em 0 2em; 26 | } 27 | .actions > * { 28 | position: relative; 29 | } 30 | .actions > *::after { 31 | content: "/"; 32 | color: #3f69ff; 33 | display: inline-flex; 34 | position: absolute; 35 | left: calc(100% + 10px); 36 | text-align: center; 37 | height: 100%; 38 | top: 0; 39 | display: flex; 40 | align-items: center; 41 | } 42 | .actions > *::after:last-child { 43 | display: none; 44 | } 45 | 46 | /* form { 47 | border: 1px solid red; 48 | min-width: 320px; 49 | display: flex; 50 | gap: .5em; 51 | } */ 52 | /* input { 53 | font-size: 1rem; 54 | font-family: system-ui; 55 | padding: .5em 1em; 56 | flex: 1; 57 | } */ 58 | /* select { 59 | font-size: 1rem; 60 | padding: 0 1em; 61 | border-radius: .5em; 62 | } */ 63 | /* button { 64 | font-size: 1rem; 65 | background: #3f69ff; 66 | padding: 1em; 67 | color: white; 68 | border: none; 69 | border-radius: .5em; 70 | } */ 71 | 72 | /* .movie-list { 73 | display: grid; 74 | grid-template-columns: repeat(auto-fit, 200px); 75 | justify-content: center; 76 | box-sizing: border-box; 77 | gap: 1em; 78 | } */ 79 | 80 | 81 | .movie { 82 | position: relative; 83 | transition: 1s; 84 | } 85 | .movie.recommended img { 86 | border: 2px solid goldenrod; 87 | box-sizing: border-box; 88 | perspective-origin: top; 89 | animation: recommended 1s forwards; 90 | } 91 | 92 | @keyframes recommended { 93 | to { 94 | transform: perspective(500px) rotate3d(0, -1, 0, 25deg); 95 | } 96 | } 97 | .movie.recommended .movie-rate { 98 | background-color: goldenrod; 99 | box-shadow: 0 0 0 2px goldenrod; 100 | } 101 | 102 | .movie-poster { 103 | max-width: 100%; 104 | width: 100%; 105 | border-radius: .7em; 106 | } 107 | 108 | .movie-title { 109 | font-weight: bold; 110 | } 111 | 112 | .movie-rate { 113 | position: absolute; 114 | left: 1em; 115 | top: 1em; 116 | background: #839eff; 117 | color: white; 118 | width: 40px; 119 | height: 40px; 120 | display: flex; 121 | justify-content: center; 122 | align-items: center; 123 | border-radius: 50%; 124 | border: 2px solid white; 125 | box-shadow: 0 0 0 2px #3f69ff; 126 | } 127 | -------------------------------------------------------------------------------- /block-buster/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonidasEsteban/curso-javascript-react/7a52f7d0bf55754815d4dfacdf049b4f7141d4d2/block-buster/images/logo.png -------------------------------------------------------------------------------- /block-buster/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BlockBuster 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /block-buster/src/actions/index.js: -------------------------------------------------------------------------------- 1 | export const ADD_MOVIES = 'ADD_MOVIES' 2 | export const SET_FILTER = 'SET_FILTER' 3 | export const SEARCH_MOVIE = 'SEARCH_MOVIE' -------------------------------------------------------------------------------- /block-buster/src/api.js: -------------------------------------------------------------------------------- 1 | import { API_KEY } from './constants.js' 2 | 3 | class API { 4 | constructor(API_KEY) { 5 | this.API_KEY = API_KEY 6 | } 7 | baseAPI = 'https://api.themoviedb.org/3/' 8 | get discoverMovie() { 9 | return `${this.baseAPI}discover/movie?api_key=${this.API_KEY}` 10 | } 11 | async moviePage(page) { 12 | const response = await fetch(`${this.discoverMovie}&page=${page}`) 13 | const data = await response.json() 14 | return data 15 | } 16 | } 17 | 18 | export default new API(API_KEY) -------------------------------------------------------------------------------- /block-buster/src/components/actions.js: -------------------------------------------------------------------------------- 1 | import { Component, createElement } from '../lib/react/index.js' 2 | import Wrapper from './wrapper.js' 3 | 4 | class Actions extends Component { 5 | render() { 6 | const { children } = this.props 7 | return Wrapper({ 8 | children: createElement('div', { 9 | class: 'actions', 10 | children, 11 | }) 12 | }) 13 | } 14 | } 15 | 16 | export default Actions 17 | -------------------------------------------------------------------------------- /block-buster/src/components/app.js: -------------------------------------------------------------------------------- 1 | import { Component } from '../lib/react/index.js' 2 | import styled from '../lib/styled-components.js' 3 | import Header from './header.js' 4 | import Actions from './actions.js' 5 | import Search from './search.js' 6 | import Filters from './filters.js' 7 | import MovieList from './movie-list.js' 8 | 9 | const AppStyled = styled.div`` 10 | 11 | class App extends Component { 12 | render() { 13 | return AppStyled({ 14 | children: [ 15 | new Header(), 16 | new Actions({ 17 | children: [ 18 | new Search(), 19 | new Filters(), 20 | ] 21 | }), 22 | new MovieList() 23 | ] 24 | }) 25 | } 26 | } 27 | 28 | export default App -------------------------------------------------------------------------------- /block-buster/src/components/button.js: -------------------------------------------------------------------------------- 1 | import styled from '../lib/styled-components.js' 2 | 3 | export default styled.button` 4 | font-size: 1rem; 5 | background: #3f69ff; 6 | padding: 1em; 7 | color: white; 8 | border: none; 9 | border-radius: .5em; 10 | ` -------------------------------------------------------------------------------- /block-buster/src/components/filters.js: -------------------------------------------------------------------------------- 1 | import { Component, createElement } from '../lib/react/index.js' 2 | import Select from './select.js' 3 | import store from '../store.js' 4 | import { SET_FILTER } from '../actions/index.js' 5 | 6 | class Filters extends Component { 7 | handleChange = (event) => { 8 | store.dispatch({ 9 | type: SET_FILTER, 10 | payload: event.target.value, 11 | }) 12 | } 13 | render() { 14 | return Select({ 15 | onChange: this.handleChange, 16 | children: [ 17 | createElement('option', { value: 'all'}, 'Todas'), 18 | createElement('option', { value: 'mostValued'}, 'Más valoradas'), 19 | createElement('option', { value: 'leastValued'}, 'Menos valoradas'), 20 | ] 21 | }) 22 | } 23 | } 24 | 25 | export default Filters 26 | 27 | -------------------------------------------------------------------------------- /block-buster/src/components/form.js: -------------------------------------------------------------------------------- 1 | import styled from '../lib/styled-components.js' 2 | 3 | export default styled.form` 4 | min-width: 320px; 5 | display: flex; 6 | gap: .5em; 7 | ` 8 | -------------------------------------------------------------------------------- /block-buster/src/components/header.js: -------------------------------------------------------------------------------- 1 | import { Component } from '../lib/react/index.js' 2 | import styled from '../lib/styled-components.js' 3 | import Wrapper from './wrapper.js' 4 | 5 | const HeaderStyled = styled.header` 6 | background: #0e3fa9; 7 | margin-bottom: 2em; 8 | text-align: center; 9 | ` 10 | 11 | const HeaderLogo = styled.img` 12 | width: 200px; 13 | position: relative; 14 | top: 20px; 15 | filter: drop-shadow(3px 3px 0 #f2a30c); 16 | ` 17 | 18 | class Header extends Component { 19 | render() { 20 | return HeaderStyled({ 21 | children: Wrapper({ 22 | children: HeaderLogo({ src: './images/logo.png' }) 23 | }) 24 | }) 25 | } 26 | } 27 | 28 | export default Header -------------------------------------------------------------------------------- /block-buster/src/components/input.js: -------------------------------------------------------------------------------- 1 | import styled from '../lib/styled-components.js' 2 | 3 | export default styled.input` 4 | font-size: 1rem; 5 | font-family: system-ui; 6 | padding: .5em 1em; 7 | flex: 1; 8 | ` -------------------------------------------------------------------------------- /block-buster/src/components/movie-list.js: -------------------------------------------------------------------------------- 1 | import { Component } from '../lib/react/index.js' 2 | import styled from '../lib/styled-components.js' 3 | import Wrapper from './wrapper.js' 4 | import Movie from './movie.js' 5 | import store from '../store.js' 6 | import api from '../api.js' 7 | import { ADD_MOVIES } from '../actions/index.js' 8 | // import movies from '../movies.js' 9 | 10 | const MovieListStyled = styled.section` 11 | display: grid; 12 | grid-template-columns: repeat(auto-fit, 200px); 13 | justify-content: center; 14 | box-sizing: border-box; 15 | gap: 1em; 16 | ` 17 | 18 | class MovieList extends Component { 19 | state = { 20 | page: 1, 21 | } 22 | getPage = async (page) => { 23 | const { results } = await api.moviePage(page) 24 | store.dispatch({ 25 | type: ADD_MOVIES, 26 | payload: results 27 | }) 28 | } 29 | handleIntersection = (entries) => { 30 | if(entries[0].isIntersecting) { 31 | this.getPage(this.state.page) 32 | this.setState({ 33 | page: this.state.page + 1 34 | }) 35 | } 36 | } 37 | componentDidMount() { 38 | // this.getPage(this.state.page) 39 | store.subscribe(() => { 40 | this.setState() 41 | }) 42 | const observer = new IntersectionObserver(this.handleIntersection) 43 | observer.observe(window.intersector) 44 | // debugger 45 | } 46 | render() { 47 | const state = store.getState() 48 | const movieListId = state.list[state.filter] 49 | const movieList = state.movieList 50 | console.log(state) 51 | return Wrapper({ 52 | children: MovieListStyled({ 53 | children: movieListId.map(id => new Movie(movieList.get(id))) 54 | }) 55 | }) 56 | } 57 | } 58 | 59 | export default MovieList 60 | -------------------------------------------------------------------------------- /block-buster/src/components/movie.js: -------------------------------------------------------------------------------- 1 | import { Component, createElement } from '../lib/react/index.js' 2 | 3 | class Movie extends Component { 4 | render() { 5 | const { poster_path, title, id, vote_average } = this.props 6 | return createElement('article', { 7 | class: `movie ${vote_average >= 7 ? 'recommended': ''}`, 8 | children: [ 9 | createElement('img', { 10 | class: 'movie-poster', 11 | src: `//image.tmdb.org/t/p/w220_and_h330_face${poster_path}` 12 | }), 13 | createElement('p', { 14 | class: 'movie-title', 15 | }, title), 16 | createElement('p', { 17 | class: 'movie-id', 18 | }, id), 19 | createElement('span', { 20 | class: 'movie-rate', 21 | }, vote_average) 22 | ] 23 | }) 24 | } 25 | } 26 | 27 | export default Movie -------------------------------------------------------------------------------- /block-buster/src/components/search.js: -------------------------------------------------------------------------------- 1 | import { Component, createElement } from '../lib/react/index.js' 2 | import Form from './form.js' 3 | import Input from './input.js' 4 | import Button from './button.js' 5 | import store from '../store.js' 6 | import { SEARCH_MOVIE, SET_FILTER } from '../actions/index.js' 7 | 8 | class Search extends Component { 9 | handleSubmit = (event) => { 10 | event.preventDefault() 11 | const formData = new FormData(event.target) 12 | const query = formData.get('title') 13 | if (query) { 14 | return store.dispatch({ 15 | type: SEARCH_MOVIE, 16 | payload: query 17 | }) 18 | } 19 | return store.dispatch({ 20 | type: SET_FILTER, 21 | payload: 'all' 22 | }) 23 | } 24 | render() { 25 | return Form({ 26 | onSubmit: this.handleSubmit, 27 | children: [ 28 | new Input({ 29 | placeholder: 'Escribe tu película favorita', 30 | name: 'title', 31 | type: 'text' 32 | }), 33 | new Button(null, 'Buscar') 34 | ] 35 | }) 36 | } 37 | } 38 | 39 | export default Search 40 | 41 | 42 | -------------------------------------------------------------------------------- /block-buster/src/components/select.js: -------------------------------------------------------------------------------- 1 | import styled from '../lib/styled-components.js' 2 | 3 | export default styled.select` 4 | font-size: 1rem; 5 | padding: 0 1em; 6 | border-radius: .5em; 7 | ` -------------------------------------------------------------------------------- /block-buster/src/components/wrapper.js: -------------------------------------------------------------------------------- 1 | import styled from '../lib/styled-components.js' 2 | 3 | const WrapperStyled = styled.div` 4 | max-width: 1280px; 5 | margin: auto; 6 | ` 7 | 8 | export default WrapperStyled 9 | -------------------------------------------------------------------------------- /block-buster/src/constants.js: -------------------------------------------------------------------------------- 1 | export const API_KEY = 'busca un api key :P' -------------------------------------------------------------------------------- /block-buster/src/index.js: -------------------------------------------------------------------------------- 1 | import { render } from './lib/react-dom.js' 2 | import App from './components/app.js' 3 | 4 | render(new App(), root) -------------------------------------------------------------------------------- /block-buster/src/lib/react-dom.js: -------------------------------------------------------------------------------- 1 | function render(element, container) { 2 | if (typeof element === 'string' || element instanceof Element) { 3 | return container.append(element) 4 | } 5 | function reRender(newChild) { 6 | container.replaceChild(newChild, childElement) 7 | childElement = newChild 8 | } 9 | element.update = reRender 10 | let childElement = element.build() 11 | container.append(childElement) 12 | element.componentDidMount() 13 | } 14 | 15 | 16 | export { 17 | render 18 | } -------------------------------------------------------------------------------- /block-buster/src/lib/react/index.js: -------------------------------------------------------------------------------- 1 | export { 2 | Component 3 | } from './src/React.js' 4 | 5 | export { 6 | createElement, 7 | createElement as jsx, 8 | } from './src/ReactElement.js' -------------------------------------------------------------------------------- /block-buster/src/lib/react/src/React.js: -------------------------------------------------------------------------------- 1 | class Component { 2 | constructor(props = {}, state = {}) { 3 | this.props = props 4 | this.state = state 5 | } 6 | update() {} 7 | #updater(){ 8 | this.update(this.render()) 9 | this.componentDidUpdate() 10 | } 11 | // se llama andes que se renderice el componente 12 | componentWillMount() { 13 | 14 | } 15 | componentDidMount() { 16 | 17 | } 18 | componentDidUpdate() { 19 | 20 | } 21 | setState(newState) { 22 | this.state = { 23 | ...this.state, 24 | ...newState 25 | } 26 | this.#updater() 27 | } 28 | build() { 29 | this.componentWillMount() 30 | return this.render() 31 | } 32 | } 33 | 34 | export { 35 | Component 36 | } -------------------------------------------------------------------------------- /block-buster/src/lib/react/src/ReactElement.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../react-dom.js' 2 | 3 | 4 | function renderChildren(children, container) { 5 | if (Array.isArray(children)) { 6 | return children.forEach(child => render(child, container)) 7 | } 8 | return render(children, container) 9 | } 10 | 11 | function setEvents(element, event, callback) { 12 | return element.addEventListener(event, callback) 13 | } 14 | 15 | function setProperties(prop, value, element) { 16 | // events support 17 | if(prop.startsWith('on')) { 18 | const event = prop.replace('on','').toLowerCase() 19 | return setEvents(element, event, value) 20 | } 21 | 22 | // support for children 23 | if (prop === 'children') { 24 | return renderChildren(value, element) 25 | } 26 | 27 | // support for attributes 28 | const attribute = value 29 | return element.setAttribute(prop, attribute) 30 | } 31 | 32 | export function createElement(type, props, content) { 33 | // Creando tipo de elemento 34 | const element = document.createElement(type) 35 | 36 | // Contenido 37 | if (content) { 38 | element.textContent = content 39 | } 40 | // Propiedades 41 | if (props) { 42 | Object.keys(props).forEach(prop => setProperties(prop, props[prop], element )) 43 | } 44 | 45 | // console.log(Object.keys(props)) 46 | 47 | return element 48 | } 49 | 50 | -------------------------------------------------------------------------------- /block-buster/src/lib/styled-components.js: -------------------------------------------------------------------------------- 1 | import { createElement } from './react/index.js' 2 | 3 | const styled = {} 4 | 5 | const elements = [ 6 | 'h1', 7 | 'p', 8 | 'div', 9 | 'img', 10 | 'article', 11 | 'footer', 12 | 'header', 13 | 'form', 14 | 'input', 15 | 'button', 16 | 'select', 17 | 'section', 18 | ] 19 | 20 | function buildStyles(strings, dynamicValues, props) { 21 | let style = strings.slice() 22 | dynamicValues.forEach((value, index) => { 23 | style[index] += value(props) 24 | }) 25 | 26 | return style.join('') 27 | } 28 | 29 | 30 | elements.forEach((tag) => { 31 | styled[tag] = function (strings, ...dynamicValues) { 32 | return function (props, content) { 33 | const style = buildStyles(strings, dynamicValues, props) 34 | return createElement(tag, { 35 | ...props, 36 | style, 37 | }, content) 38 | 39 | } 40 | } 41 | }) 42 | 43 | 44 | 45 | 46 | export default styled 47 | -------------------------------------------------------------------------------- /block-buster/src/movies.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | "popularity": 1616.326, 4 | "vote_count": 18, 5 | "video": false, 6 | "poster_path": "/ugZW8ocsrfgI95pnQ7wrmKDxIe.jpg", 7 | "id": 724989, 8 | "adult": false, 9 | "backdrop_path": "/86L8wqGMDbwURPni2t7FQ0nDjsH.jpg", 10 | "original_language": "en", 11 | "original_title": "Hard Kill", 12 | "genre_ids": [ 13 | 28, 14 | 53 15 | ], 16 | "title": "Hard Kill", 17 | "vote_average": 5.8, 18 | "overview": "The work of billionaire tech CEO Donovan Chalmers is so valuable that he hires mercenaries to protect it, and a terrorist group kidnaps his daughter just to get it.", 19 | "release_date": "2020-08-25" 20 | }, 21 | { 22 | "popularity": 1456.937, 23 | "vote_count": 159, 24 | "video": false, 25 | "poster_path": "/zVncJzXzwIO15YM1WilqYn30r54.jpg", 26 | "id": 718444, 27 | "adult": false, 28 | "backdrop_path": "/x4UkhIQuHIJyeeOTdcbZ3t3gBSa.jpg", 29 | "original_language": "en", 30 | "original_title": "Rogue", 31 | "genre_ids": [ 32 | 28 33 | ], 34 | "title": "Rogue", 35 | "vote_average": 6, 36 | "overview": "Battle-hardened O’Hara leads a lively mercenary team of soldiers on a daring mission: rescue hostages from their captors in remote Africa. But as the mission goes awry and the team is stranded, O’Hara’s squad must face a bloody, brutal encounter with a gang of rebels.", 37 | "release_date": "2020-08-20" 38 | }, 39 | { 40 | "popularity": 1171.542, 41 | "vote_count": 30, 42 | "video": false, 43 | "poster_path": "/9Rj8l6gElLpRL7Kj17iZhrT5Zuw.jpg", 44 | "id": 734309, 45 | "adult": false, 46 | "backdrop_path": "/7fvdg211A2L0mHddvzyArRuRalp.jpg", 47 | "original_language": "en", 48 | "original_title": "Santana", 49 | "genre_ids": [ 50 | 28 51 | ], 52 | "title": "Santana", 53 | "vote_average": 6.4, 54 | "overview": "Two brothers — one a narcotics agent and the other a general — finally discover the identity of the drug lord who murdered their parents decades ago. They may kill each other before capturing the bad guys.", 55 | "release_date": "2020-08-28" 56 | }, 57 | { 58 | "popularity": 1123.256, 59 | "vote_count": 108, 60 | "video": false, 61 | "poster_path": "/sMO1v5TUf8GOJHbJieDXsgWT2Ud.jpg", 62 | "id": 438396, 63 | "adult": false, 64 | "backdrop_path": "/qGZe9qTuydxyJYQ60XDtEckzLR8.jpg", 65 | "original_language": "es", 66 | "original_title": "Orígenes secretos", 67 | "genre_ids": [ 68 | 18, 69 | 53 70 | ], 71 | "title": "Unknown Origins", 72 | "vote_average": 6.2, 73 | "overview": "In Madrid, Spain, a mysterious serial killer ruthlessly murders his victims by recreating the first appearance of several comic book superheroes. Cosme, a veteran police inspector who is about to retire, works on the case along with the tormented inspector David Valentín and his own son Jorge Elías, a nerdy young man who owns a comic book store.", 74 | "release_date": "2020-08-28" 75 | }, 76 | { 77 | "popularity": 1066.224, 78 | "vote_count": 44, 79 | "video": false, 80 | "poster_path": "/n6hptKS7Y0ZjkYwbqKOK3jz9XAC.jpg", 81 | "id": 594328, 82 | "adult": false, 83 | "backdrop_path": "/lkeBhXGJFRlhI7cBWn8LQQAdZqK.jpg", 84 | "original_language": "en", 85 | "original_title": "Phineas and Ferb The Movie Candace Against the Universe", 86 | "genre_ids": [ 87 | 16, 88 | 35, 89 | 10402, 90 | 878, 91 | 10751, 92 | 10770 93 | ], 94 | "title": "Phineas and Ferb The Movie Candace Against the Universe", 95 | "vote_average": 7.1, 96 | "overview": "Phineas and Ferb travel across the galaxy to rescue their older sister Candace, who has been abducted by aliens and taken to a utopia in a far-off planet, free of her pesky little brothers.", 97 | "release_date": "2020-08-28" 98 | }, 99 | { 100 | "popularity": 1046.033, 101 | "vote_count": 1102, 102 | "video": false, 103 | "poster_path": "/TnOeov4w0sTtV2gqICqIxVi74V.jpg", 104 | "id": 605116, 105 | "adult": false, 106 | "backdrop_path": "/qVygtf2vU15L2yKS4Ke44U4oMdD.jpg", 107 | "original_language": "en", 108 | "original_title": "Project Power", 109 | "genre_ids": [ 110 | 28, 111 | 80, 112 | 878 113 | ], 114 | "title": "Project Power", 115 | "vote_average": 6.7, 116 | "overview": "An ex-soldier, a teen and a cop collide in New Orleans as they hunt for the source behind a dangerous new pill that grants users temporary superpowers.", 117 | "release_date": "2020-08-14" 118 | }, 119 | { 120 | "popularity": 958.038, 121 | "vote_count": 341, 122 | "video": false, 123 | "poster_path": "/sy6DvAu72kjoseZEjocnm2ZZ09i.jpg", 124 | "id": 581392, 125 | "adult": false, 126 | "backdrop_path": "/gEjNlhZhyHeto6Fy5wWy5Uk3A9D.jpg", 127 | "original_language": "ko", 128 | "original_title": "반도", 129 | "genre_ids": [ 130 | 28, 131 | 27, 132 | 53 133 | ], 134 | "title": "Peninsula", 135 | "vote_average": 7.2, 136 | "overview": "A soldier and his team battle hordes of post-apocalyptic zombies in the wastelands of the Korean Peninsula", 137 | "release_date": "2020-07-15" 138 | }, 139 | { 140 | "popularity": 859.648, 141 | "vote_count": 134, 142 | "video": false, 143 | "poster_path": "/tM4hht0LdY06UbuxGR4LjK6adCD.jpg", 144 | "id": 613504, 145 | "adult": false, 146 | "backdrop_path": "/dZJJDmiwp0W1NE74SY5WV00v0Ec.jpg", 147 | "original_language": "en", 148 | "original_title": "After We Collided", 149 | "genre_ids": [ 150 | 18, 151 | 10749 152 | ], 153 | "title": "After We Collided", 154 | "vote_average": 7.3, 155 | "overview": "Tessa finds herself struggling with her complicated relationship with Hardin; she faces a dilemma that could change their lives forever.", 156 | "release_date": "2020-09-02" 157 | }, 158 | { 159 | "popularity": 843.438, 160 | "vote_count": 855, 161 | "video": false, 162 | "poster_path": "/aKx1ARwG55zZ0GpRvU2WrGrCG9o.jpg", 163 | "id": 337401, 164 | "adult": false, 165 | "backdrop_path": "/xl5oCFLVMo4d4Pgxvrf8Jmc2IlA.jpg", 166 | "original_language": "en", 167 | "original_title": "Mulan", 168 | "genre_ids": [ 169 | 28, 170 | 12, 171 | 18, 172 | 14, 173 | 10752 174 | ], 175 | "title": "Mulan", 176 | "vote_average": 7.8, 177 | "overview": "When the Emperor of China issues a decree that one man per family must serve in the Imperial Chinese Army to defend the country from Huns, Hua Mulan, the eldest daughter of an honored warrior, steps in to take the place of her ailing father. She is spirited, determined and quick on her feet. Disguised as a man by the name of Hua Jun, she is tested every step of the way and must harness her innermost strength and embrace her true potential.", 178 | "release_date": "2020-09-04" 179 | }, 180 | { 181 | "popularity": 812.371, 182 | "vote_count": 54, 183 | "video": false, 184 | "poster_path": "/eDnHgozW8vfOaLHzfpHluf1GZCW.jpg", 185 | "id": 606234, 186 | "adult": false, 187 | "backdrop_path": "/u9YEh2xVAPVTKoaMNlB5tH6pXkm.jpg", 188 | "original_language": "en", 189 | "original_title": "Archive", 190 | "genre_ids": [ 191 | 878 192 | ], 193 | "title": "Archive", 194 | "vote_average": 6, 195 | "overview": "2038: George Almore is working on a true human-equivalent AI, and his latest prototype is almost ready. This sensitive phase is also the riskiest as he has a goal that must be hidden at all costs—being reunited with his dead wife.", 196 | "release_date": "2020-08-13" 197 | }, 198 | { 199 | "popularity": 804.043, 200 | "vote_count": 65, 201 | "video": false, 202 | "poster_path": "/i4kPwXPlM1iy8Jf3S1uuLuwqQAV.jpg", 203 | "id": 721452, 204 | "adult": false, 205 | "backdrop_path": "/riDrpqQtZpXGeiJdlmfcwwPH7nN.jpg", 206 | "original_language": "en", 207 | "original_title": "One Night in Bangkok", 208 | "genre_ids": [ 209 | 28, 210 | 53 211 | ], 212 | "title": "One Night in Bangkok", 213 | "vote_average": 7.2, 214 | "overview": "A hit man named Kai flies into Bangkok, gets a gun, and orders a cab. He offers a professional female driver big money to be his all-night driver. But when she realizes Kai is committing brutal murders at each stop, it's too late to walk away. Meanwhile, an offbeat police detective races to decode the string of slayings before more blood is spilled.", 215 | "release_date": "2020-08-25" 216 | }, 217 | { 218 | "popularity": 765.892, 219 | "vote_count": 214, 220 | "video": false, 221 | "poster_path": "/uGhQ2ZGBpzCj6wC5jUrybsZuPTI.jpg", 222 | "id": 539885, 223 | "adult": false, 224 | "backdrop_path": "/ekkuqt9Q2ad1F7xq2ZONP0oq36P.jpg", 225 | "original_language": "en", 226 | "original_title": "Ava", 227 | "genre_ids": [ 228 | 28, 229 | 80, 230 | 18, 231 | 53 232 | ], 233 | "title": "Ava", 234 | "vote_average": 6.1, 235 | "overview": "A black ops assassin is forced to fight for her own survival after a job goes dangerously wrong.", 236 | "release_date": "2020-08-06" 237 | }, 238 | { 239 | "popularity": 532.282, 240 | "vote_count": 68, 241 | "video": false, 242 | "poster_path": "/5pe30v0z4ucVgwh5nR439cCzwwO.jpg", 243 | "id": 632618, 244 | "adult": false, 245 | "backdrop_path": "/cVdYaAQmd5DZNdo0KFJruz7JpUs.jpg", 246 | "original_language": "es", 247 | "original_title": "Crímenes de familia", 248 | "genre_ids": [ 249 | 80, 250 | 18, 251 | 53 252 | ], 253 | "title": "The Crimes That Bind", 254 | "vote_average": 7, 255 | "overview": "When her son is accused of raping and trying to murder his ex-wife, Alicia embarks on a journey that will change her life forever.", 256 | "release_date": "2020-08-20" 257 | }, 258 | { 259 | "popularity": 509.318, 260 | "vote_count": 571, 261 | "video": false, 262 | "poster_path": "/jHo2M1OiH9Re33jYtUQdfzPeUkx.jpg", 263 | "id": 385103, 264 | "adult": false, 265 | "backdrop_path": "/fKtYXUhX5fxMxzQfyUcQW9Shik6.jpg", 266 | "original_language": "en", 267 | "original_title": "Scoob!", 268 | "genre_ids": [ 269 | 12, 270 | 16, 271 | 35, 272 | 10751 273 | ], 274 | "title": "Scoob!", 275 | "vote_average": 7.4, 276 | "overview": "In Scooby-Doo’s greatest adventure yet, see the never-before told story of how lifelong friends Scooby and Shaggy first met and how they joined forces with young detectives Fred, Velma, and Daphne to form the famous Mystery Inc. Now, with hundreds of cases solved, Scooby and the gang face their biggest, toughest mystery ever: an evil plot to unleash the ghost dog Cerberus upon the world. As they race to stop this global “dogpocalypse,” the gang discovers that Scooby has a secret legacy and an epic destiny greater than anyone ever imagined.", 277 | "release_date": "2020-07-08" 278 | }, 279 | { 280 | "popularity": 501.925, 281 | "vote_count": 94, 282 | "video": false, 283 | "poster_path": "/A11Ez4UkOE4Ysmtmur5Bho8qrGM.jpg", 284 | "id": 611395, 285 | "adult": false, 286 | "backdrop_path": "/qXACJOuyklS0BpvO8ALLkkrsv7W.jpg", 287 | "original_language": "zh", 288 | "original_title": "征途", 289 | "genre_ids": [ 290 | 28, 291 | 12, 292 | 14 293 | ], 294 | "title": "Double World", 295 | "vote_average": 6.9, 296 | "overview": "Keen to bring honor to his clan, young villager Dong Yilong embarks on a perilous journey to compete in a tournament that selects warriors for battle.", 297 | "release_date": "2020-07-24" 298 | }, 299 | { 300 | "popularity": 473.21, 301 | "vote_count": 375, 302 | "video": false, 303 | "poster_path": "/kPzcvxBwt7kEISB9O4jJEuBn72t.jpg", 304 | "id": 677638, 305 | "adult": false, 306 | "backdrop_path": "/pO1SnM5a1fEsYrFaVZW78Wb0zRJ.jpg", 307 | "original_language": "en", 308 | "original_title": "We Bare Bears: The Movie", 309 | "genre_ids": [ 310 | 12, 311 | 16, 312 | 35, 313 | 10751 314 | ], 315 | "title": "We Bare Bears: The Movie", 316 | "vote_average": 7.8, 317 | "overview": "When Grizz, Panda, and Ice Bear's love of food trucks and viral videos went out of hand, it catches the attention of Agent Trout from the National Wildlife Control, who pledges to restore the “natural order” by separating them forever. Chased away from their home, the Bears embark on an epic road trip as they seek refuge in Canada, with their journey being filled with new friends, perilous obstacles, and huge parties. The risky journey also forces the Bears to face how they first met and became brothers, in order to keep their family bond from splitting apart.", 318 | "release_date": "2020-06-30" 319 | }, 320 | { 321 | "popularity": 460.596, 322 | "vote_count": 113, 323 | "video": false, 324 | "poster_path": "/4V2nTPfeB59TcqJcUfQ9ziTi7VN.jpg", 325 | "id": 501979, 326 | "adult": false, 327 | "backdrop_path": "/oazPqs1z78LcIOFslbKtJLGlueo.jpg", 328 | "original_language": "en", 329 | "original_title": "Bill & Ted Face the Music", 330 | "genre_ids": [ 331 | 12, 332 | 35, 333 | 878 334 | ], 335 | "title": "Bill & Ted Face the Music", 336 | "vote_average": 6.5, 337 | "overview": "Yet to fulfill their rock and roll destiny, the now middle-aged best friends Bill and Ted set out on a new adventure when a visitor from the future warns them that only their song can save life as we know it. Along the way, they are helped by their daughters, a new batch of historical figures and a few music legends—to seek the song that will set their world right and bring harmony to the universe.", 338 | "release_date": "2020-08-27" 339 | }, 340 | { 341 | "popularity": 444.068, 342 | "vote_count": 65, 343 | "video": false, 344 | "poster_path": "/bhNHCeJDFDaB00A46AoCw2mggdE.jpg", 345 | "id": 601165, 346 | "adult": false, 347 | "backdrop_path": "/nxxODhq9I05Ze9uLONGvfDrzaUO.jpg", 348 | "original_language": "en", 349 | "original_title": "Legacy of Lies", 350 | "genre_ids": [ 351 | 28, 352 | 53 353 | ], 354 | "title": "Legacy of Lies", 355 | "vote_average": 6.1, 356 | "overview": "An ex-MI6 agent is thrown back into the world of espionage and high stakes to uncover the shocking truth about operations conducted by unknown secret services.", 357 | "release_date": "2020-08-06" 358 | }, 359 | { 360 | "popularity": 442.72, 361 | "vote_count": 13, 362 | "video": false, 363 | "poster_path": "/uvMjNLot0dG7CX4HZPme2WDkMmE.jpg", 364 | "id": 523849, 365 | "adult": false, 366 | "backdrop_path": "/AmTfxc3S22z7WWC7KAR3SPs70Bl.jpg", 367 | "original_language": "en", 368 | "original_title": "The Last Sharknado: It's About Time", 369 | "genre_ids": [ 370 | 28, 371 | 12, 372 | 35, 373 | 878, 374 | 10770 375 | ], 376 | "title": "The Last Sharknado: It's About Time", 377 | "vote_average": 6.2, 378 | "overview": "With much of America lying in ruins, the rest of the world braces for a global sharknado, Fin and his family must travel around the world to stop them.", 379 | "release_date": "2020-07-17" 380 | }, 381 | { 382 | "popularity": 438.189, 383 | "vote_count": 23, 384 | "video": false, 385 | "poster_path": "/q2lkJf1TAjImTHCEO7XvbqPtnPb.jpg", 386 | "id": 703134, 387 | "adult": false, 388 | "backdrop_path": "/j57oUw8LIYvjOl0zs3A1A1UqwKH.jpg", 389 | "original_language": "en", 390 | "original_title": "Infamous", 391 | "genre_ids": [ 392 | 80, 393 | 18, 394 | 53 395 | ], 396 | "title": "Infamous", 397 | "vote_average": 5.4, 398 | "overview": "Two young lovers rob their way across the southland, posting their exploits to social media, and gaining fame and followers as a result.", 399 | "release_date": "2020-06-12" 400 | } 401 | , 402 | { 403 | "popularity": 437.445, 404 | "vote_count": 799, 405 | "video": false, 406 | "poster_path": "/tI8ocADh22GtQFV28vGHaBZVb0U.jpg", 407 | "id": 475430, 408 | "adult": false, 409 | "backdrop_path": "/o0F8xAt8YuEm5mEZviX5pEFC12y.jpg", 410 | "original_language": "en", 411 | "original_title": "Artemis Fowl", 412 | "genre_ids": [ 413 | 28, 414 | 12, 415 | 14, 416 | 878, 417 | 10751 418 | ], 419 | "title": "Artemis Fowl", 420 | "vote_average": 5.8, 421 | "overview": "Artemis Fowl is a 12-year-old genius and descendant of a long line of criminal masterminds. He soon finds himself in an epic battle against a race of powerful underground fairies who may be behind his father's disappearance.", 422 | "release_date": "2020-06-12" 423 | }, 424 | { 425 | "popularity": 436.561, 426 | "vote_count": 1943, 427 | "video": false, 428 | "poster_path": "/cjr4NWURcVN3gW5FlHeabgBHLrY.jpg", 429 | "id": 547016, 430 | "adult": false, 431 | "backdrop_path": "/m0ObOaJBerZ3Unc74l471ar8Iiy.jpg", 432 | "original_language": "en", 433 | "original_title": "The Old Guard", 434 | "genre_ids": [ 435 | 28, 436 | 14 437 | ], 438 | "title": "The Old Guard", 439 | "vote_average": 7.3, 440 | "overview": "Four undying warriors who've secretly protected humanity for centuries become targeted for their mysterious powers just as they discover a new immortal.", 441 | "release_date": "2020-07-10" 442 | }, 443 | { 444 | "popularity": 435.283, 445 | "vote_count": 107, 446 | "video": false, 447 | "poster_path": "/e7ZsW5EbLbQwoGx0548KCmCAXA9.jpg", 448 | "id": 508570, 449 | "adult": false, 450 | "backdrop_path": "/fFdOJxmG2U7IYYlkFKtDk1nGPhF.jpg", 451 | "original_language": "en", 452 | "original_title": "The One and Only Ivan", 453 | "genre_ids": [ 454 | 35, 455 | 18, 456 | 10751 457 | ], 458 | "title": "The One and Only Ivan", 459 | "vote_average": 7.2, 460 | "overview": "Ivan is a 400-pound silverback gorilla who shares a communal habitat in a suburban shopping mall with Stella the elephant, Bob the dog, and various other animals. He has few memories of the jungle where he was captured, but when a baby elephant named Ruby arrives, it touches something deep within him. Ruby is recently separated from her family in the wild, which causes him to question his life, where he comes from and where he ultimately wants to be.", 461 | "release_date": "2020-08-21" 462 | }, 463 | { 464 | "popularity": 428.33, 465 | "vote_count": 139, 466 | "video": false, 467 | "poster_path": "/6Bbq8qQWpoApLZYWFFAuZ1r2gFw.jpg", 468 | "id": 618354, 469 | "adult": false, 470 | "backdrop_path": "/bazlsLkNuhy3IuhviepqvlMm2hV.jpg", 471 | "original_language": "en", 472 | "original_title": "Superman: Man of Tomorrow", 473 | "genre_ids": [ 474 | 28, 475 | 16, 476 | 878 477 | ], 478 | "title": "Superman: Man of Tomorrow", 479 | "vote_average": 7.3, 480 | "overview": "It’s the dawn of a new age of heroes, and Metropolis has just met its first. But as Daily Planet intern Clark Kent – working alongside reporter Lois Lane – secretly wields his alien powers of flight, super-strength and x-ray vision in the battle for good, there’s even greater trouble on the horizon.", 481 | "release_date": "2020-08-23" 482 | }, 483 | { 484 | "popularity": 427.083, 485 | "vote_count": 6, 486 | "video": false, 487 | "poster_path": "/o1WvNhoackad1QiAGRgjJCQ1Trj.jpg", 488 | "id": 724717, 489 | "adult": false, 490 | "backdrop_path": "/AbtsLdz1gUj2H1HJJ3TRaBOl8Ta.jpg", 491 | "original_language": "en", 492 | "original_title": "The 2nd", 493 | "genre_ids": [ 494 | 28 495 | ], 496 | "title": "The 2nd", 497 | "vote_average": 6.7, 498 | "overview": "Secret-service agent Vic Davis is on his way to pick up his estranged son, Sean, from his college campus when he finds himself in the middle of a high-stakes terrorist operation. His son's friend Erin Walton, the daughter of Supreme Court Justice Walton is the target, and this armed faction will stop at nothing to kidnap her and use her as leverage for a pending landmark legal case.", 499 | "release_date": "2020-09-01" 500 | }, 501 | { 502 | "popularity": 426.947, 503 | "vote_count": 7, 504 | "video": false, 505 | "poster_path": "/dKSN0oZCUSNcEd39MnySLYmpUiJ.jpg", 506 | "id": 735110, 507 | "adult": false, 508 | "backdrop_path": "/aahbYclKYfms6Utm5YHQOywsj9N.jpg", 509 | "original_language": "es", 510 | "original_title": "Fuego negro", 511 | "genre_ids": [ 512 | 28, 513 | 27, 514 | 9648, 515 | 53 516 | ], 517 | "title": "Dark Forces", 518 | "vote_average": 5, 519 | "overview": "In search of his sister, a renegade criminal seeks answers at a sordid hotel where he encounters a sinister guest and romances a mysterious waitress.", 520 | "release_date": "2020-08-21" 521 | }, 522 | { 523 | "popularity": 419.135, 524 | "vote_count": 124, 525 | "video": false, 526 | "poster_path": "/3eg0kGC2Xh0vhydJHO37Sp4cmMt.jpg", 527 | "id": 531499, 528 | "adult": false, 529 | "backdrop_path": "/zogWnCSztU8xvabaepQnAwsOtOt.jpg", 530 | "original_language": "en", 531 | "original_title": "The Tax Collector", 532 | "genre_ids": [ 533 | 28, 534 | 80, 535 | 18 536 | ], 537 | "title": "The Tax Collector", 538 | "vote_average": 6, 539 | "overview": "David Cuevas is a family man who works as a gangland tax collector for high ranking Los Angeles gang members. He makes collections across the city with his partner Creeper making sure people pay up or will see retaliation. An old threat returns to Los Angeles that puts everything David loves in harm’s way.", 540 | "release_date": "2020-08-07" 541 | }, 542 | { 543 | "popularity": 413.928, 544 | "id": 603119, 545 | "video": false, 546 | "vote_count": 85, 547 | "vote_average": 6.9, 548 | "title": "The Silencing", 549 | "release_date": "2020-07-18", 550 | "original_language": "en", 551 | "original_title": "The Silencing", 552 | "genre_ids": [ 553 | 80, 554 | 53, 555 | 28 556 | ], 557 | "backdrop_path": "/aSdp2uS0jWkqdhCeyhJuCoiLZji.jpg", 558 | "adult": false, 559 | "overview": "A reformed hunter becomes involved in a deadly game of cat and mouse when he and the local sheriff set out to track a vicious killer who may have kidnapped his daughter years ago.", 560 | "poster_path": "/dnN1ncxEOO1TY0gYL2FWxJqlhlL.jpg" 561 | }, 562 | { 563 | "popularity": 412.505, 564 | "vote_count": 4, 565 | "video": false, 566 | "poster_path": "/w6e0XZreiyW4mGlLRHEG8ipff7b.jpg", 567 | "id": 722603, 568 | "adult": false, 569 | "backdrop_path": "/m7QpUAeI2xTCJyAVl9J9z5dBTSb.jpg", 570 | "original_language": "en", 571 | "original_title": "Battlefield 2025", 572 | "genre_ids": [ 573 | 28, 574 | 27, 575 | 878 576 | ], 577 | "title": "Battlefield 2025", 578 | "vote_average": 5.3, 579 | "overview": "Weekend campers, an escaped convict, young lovers and a police officer experience a night of terror when a hostile visitor from another world descends on a small Arizona town.", 580 | "release_date": "2020-07-07" 581 | }, 582 | { 583 | "popularity": 391.98, 584 | "vote_count": 133, 585 | "video": false, 586 | "poster_path": "/vFIHbiy55smzi50RmF8LQjmpGcx.jpg", 587 | "id": 703771, 588 | "adult": false, 589 | "backdrop_path": "/owraiceOKtSOa3t8sp3wA9K2Ox6.jpg", 590 | "original_language": "en", 591 | "original_title": "Deathstroke: Knights & Dragons - The Movie", 592 | "genre_ids": [ 593 | 28, 594 | 16 595 | ], 596 | "title": "Deathstroke: Knights & Dragons - The Movie", 597 | "vote_average": 7, 598 | "overview": "Ten years ago, Slade Wilson-aka the super-assassin called Deathstroke-made a tragic mistake and his wife and son paid a terrible price. Now, a decade later, Wilson's family is threatened once again by the murderous Jackal and the terrorists of H.I.V.E. Can Deathstroke atone for the sins of the past-or will his family pay the ultimate price?", 599 | "release_date": "2020-08-04" 600 | }, 601 | { 602 | "popularity": 373.784, 603 | "vote_count": 5094, 604 | "video": false, 605 | "poster_path": "/y95lQLnuNKdPAzw9F9Ab8kJ80c3.jpg", 606 | "id": 38700, 607 | "adult": false, 608 | "backdrop_path": "/upUy2QhMZEmtypPW3PdieKLAHxh.jpg", 609 | "original_language": "en", 610 | "original_title": "Bad Boys for Life", 611 | "genre_ids": [ 612 | 28, 613 | 80, 614 | 53 615 | ], 616 | "title": "Bad Boys for Life", 617 | "vote_average": 7.3, 618 | "overview": "Marcus and Mike are forced to confront new threats, career changes, and midlife crises as they join the newly created elite team AMMO of the Miami police department to take down the ruthless Armando Armas, the vicious leader of a Miami drug cartel.", 619 | "release_date": "2020-01-15" 620 | }, 621 | { 622 | "popularity": 352.444, 623 | "vote_count": 228, 624 | "video": false, 625 | "poster_path": "/bKthjUmxjHjvJK8FktFfQdmwP12.jpg", 626 | "id": 703745, 627 | "adult": false, 628 | "backdrop_path": "/hIHtyIYgBqHybOgUdoAmveipuiO.jpg", 629 | "original_language": "en", 630 | "original_title": "Deep Blue Sea 3", 631 | "genre_ids": [ 632 | 28, 633 | 27, 634 | 878 635 | ], 636 | "title": "Deep Blue Sea 3", 637 | "vote_average": 6.2, 638 | "overview": "Dr. Emma Collins and her team are spending their third summer on the island of Little Happy studying the effect of climate change on the great white sharks who come to the nearby nursery every year to give birth. Along with the last two inhabitants of this former fishing village, their peaceful life is disrupted when a \"scientific\" team led by her ex-boyfriend and marine biologist Richard show up looking for three bull sharks who we soon learn aren't just any bull sharks.", 639 | "release_date": "2020-07-28" 640 | }, 641 | { 642 | "popularity": 349.588, 643 | "vote_count": 38, 644 | "video": false, 645 | "poster_path": "/xqbQtMffXwa3oprse4jiHBMBvdW.jpg", 646 | "id": 601844, 647 | "adult": false, 648 | "backdrop_path": "/qTrpw2ZUvN7ywUu1kieEsvNDrgQ.jpg", 649 | "original_language": "en", 650 | "original_title": "Becky", 651 | "genre_ids": [ 652 | 28, 653 | 27, 654 | 53 655 | ], 656 | "title": "Becky", 657 | "vote_average": 4.9, 658 | "overview": "A teenager's weekend at a lake house with her father takes a turn for the worse when a group of convicts wreaks havoc on their lives.", 659 | "release_date": "2020-07-23" 660 | }, 661 | { 662 | "popularity": 334.491, 663 | "vote_count": 1083, 664 | "video": false, 665 | "poster_path": "/kjMbDciooTbJPofVXgAoFjfX8Of.jpg", 666 | "id": 516486, 667 | "adult": false, 668 | "backdrop_path": "/xXBnM6uSTk6qqCf0SRZKXcga9Ba.jpg", 669 | "original_language": "en", 670 | "original_title": "Greyhound", 671 | "genre_ids": [ 672 | 28, 673 | 18, 674 | 10752 675 | ], 676 | "title": "Greyhound", 677 | "vote_average": 7.5, 678 | "overview": "A first-time captain leads a convoy of allied ships carrying thousands of soldiers across the treacherous waters of the “Black Pit” to the front lines of WW2. With no air cover protection for 5 days, the captain and his convoy must battle the surrounding enemy Nazi U-boats in order to give the allies a chance to win the war.", 679 | "release_date": "2020-06-19" 680 | }, 681 | { 682 | "popularity": 333.736, 683 | "vote_count": 5694, 684 | "video": false, 685 | "poster_path": "/h4VB6m0RwcicVEZvzftYZyKXs6K.jpg", 686 | "id": 495764, 687 | "adult": false, 688 | "backdrop_path": "/9xNOiv6DZZjH7ABoUUDP0ZynouU.jpg", 689 | "original_language": "en", 690 | "original_title": "Birds of Prey (and the Fantabulous Emancipation of One Harley Quinn)", 691 | "genre_ids": [ 692 | 28, 693 | 35, 694 | 80 695 | ], 696 | "title": "Birds of Prey (and the Fantabulous Emancipation of One Harley Quinn)", 697 | "vote_average": 7.2, 698 | "overview": "Harley Quinn joins forces with a singer, an assassin and a police detective to help a young girl who had a hit placed on her after she stole a rare diamond from a crime lord.", 699 | "release_date": "2020-02-05" 700 | }, 701 | { 702 | "popularity": 331.826, 703 | "vote_count": 364, 704 | "video": false, 705 | "poster_path": "/dqKqzcdhtJwOhjqe89RTURqILtl.jpg", 706 | "id": 514207, 707 | "adult": false, 708 | "backdrop_path": "/kUaE05cgWuuygBorOcBFNn2x3CO.jpg", 709 | "original_language": "ru", 710 | "original_title": "Вторжение", 711 | "genre_ids": [ 712 | 28, 713 | 18, 714 | 27, 715 | 878 716 | ], 717 | "title": "Invasion", 718 | "vote_average": 7.1, 719 | "overview": "After the fall of the alien ship, it took three years. The catastrophe turned the girl's life from Chertanovo and forever changed our view of the universe. It seems that this was the biggest test for all of us. But mankind does not yet know that very soon he will have to experience a new meeting.", 720 | "release_date": "2020-01-01" 721 | }, 722 | { 723 | "popularity": 309.216, 724 | "vote_count": 4, 725 | "video": false, 726 | "poster_path": "/pXv4qbWyj6ycMaWkK2LzlizZQjf.jpg", 727 | "id": 713825, 728 | "adult": false, 729 | "backdrop_path": "/tWxCVe4rQZa3BvR3tMT3t74oVTT.jpg", 730 | "original_language": "en", 731 | "original_title": "Robot Riot", 732 | "genre_ids": [ 733 | 28, 734 | 878 735 | ], 736 | "title": "Robot Riot", 737 | "vote_average": 3.8, 738 | "overview": "Unconscious soldiers are dropped into a testing site only to discover their memories have been wiped and that once docile machines are the new intelligence.", 739 | "release_date": "2020-06-12" 740 | }, 741 | { 742 | "popularity": 300.997, 743 | "vote_count": 124, 744 | "video": false, 745 | "poster_path": "/aVbqhqYtlxwEGihTEhewZAgDOCX.jpg", 746 | "id": 489326, 747 | "adult": false, 748 | "backdrop_path": "/dFB6Tiy3z2xRLbnEUB5ocApT5xG.jpg", 749 | "original_language": "en", 750 | "original_title": "Mortal", 751 | "genre_ids": [ 752 | 28, 753 | 14, 754 | 53 755 | ], 756 | "title": "Mortal", 757 | "vote_average": 6.8, 758 | "overview": "A young boy must discover the origins of his extraordinary powers before he is captured by authorities hell-bent on condemning him for an accidental murder.", 759 | "release_date": "2020-02-28" 760 | }, 761 | { 762 | "popularity": 294.997, 763 | "vote_count": 14643, 764 | "video": false, 765 | "poster_path": "/udDclJoHjfjb8Ekgsd4FDteOkCU.jpg", 766 | "id": 475557, 767 | "adult": false, 768 | "backdrop_path": "/n6bUvigpRFqSwmPp1m2YADdbRBc.jpg", 769 | "original_language": "en", 770 | "original_title": "Joker", 771 | "genre_ids": [ 772 | 80, 773 | 18, 774 | 53 775 | ], 776 | "title": "Joker", 777 | "vote_average": 8.2, 778 | "overview": "During the 1980s, a failed stand-up comedian is driven insane and turns to a life of crime and chaos in Gotham City while becoming an infamous psychopathic crime figure.", 779 | "release_date": "2019-10-02" 780 | }, 781 | { 782 | "popularity": 294.801, 783 | "vote_count": 152, 784 | "video": false, 785 | "poster_path": "/h7dZpJDORYs5c56dydbrLFkEXpE.jpg", 786 | "id": 723072, 787 | "adult": false, 788 | "backdrop_path": "/5TbtcmRySXPAEXBzwhiOYYDQmgv.jpg", 789 | "original_language": "en", 790 | "original_title": "Host", 791 | "genre_ids": [ 792 | 27 793 | ], 794 | "title": "Host", 795 | "vote_average": 6.9, 796 | "overview": "Six friends hire a medium to hold a séance via Zoom during lockdown — but they get far more than they bargained for as things quickly go wrong. When an evil spirit starts invading their homes, they begin to realize they might not survive the night.", 797 | "release_date": "2020-07-30" 798 | } 799 | ] 800 | -------------------------------------------------------------------------------- /block-buster/src/normalize.js: -------------------------------------------------------------------------------- 1 | function movieListAsMap(newList, oldList = new Map()) { 2 | return newList.reduce((list, movie) => { 3 | list.set(movie.id, movie) 4 | return list 5 | }, oldList) 6 | } 7 | 8 | 9 | function getAllIds(list, oldList = []) { 10 | return oldList.concat(list.map(movie => movie.id)) 11 | } 12 | 13 | function getMostValuedIds(list, oldList = []) { 14 | return list.reduce((list, movie) => { 15 | if (movie.vote_average >= 7) { 16 | list.push(movie.id) 17 | } 18 | return list 19 | }, oldList) 20 | } 21 | 22 | function getLeastValuedIds(list, oldList = []) { 23 | return list.reduce((list, movie) => { 24 | if (movie.vote_average < 7) { 25 | list.push(movie.id) 26 | } 27 | return list 28 | }, oldList) 29 | } 30 | 31 | 32 | export { 33 | movieListAsMap, 34 | getAllIds, 35 | getMostValuedIds, 36 | getLeastValuedIds, 37 | } -------------------------------------------------------------------------------- /block-buster/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { ADD_MOVIES, SET_FILTER, SEARCH_MOVIE } from '../actions/index.js' 2 | import { 3 | movieListAsMap, 4 | getAllIds, 5 | getLeastValuedIds, 6 | getMostValuedIds 7 | } from '../normalize.js' 8 | 9 | function filterByTitle(title, movies) { 10 | const list = [] 11 | movies.forEach((movie) => { 12 | if (movie.title.toLowerCase().includes(title.toLowerCase())) { 13 | list.push(movie.id) 14 | } 15 | }) 16 | return list 17 | } 18 | 19 | function findById(id, allIds) { 20 | const parseId = parseInt(id, 10) 21 | if (allIds.includes(parseId)) { 22 | return [parseId] 23 | } 24 | return allIds 25 | } 26 | 27 | function searchMovie(query, list, allIds) { 28 | if (isNaN(query)) { 29 | return filterByTitle(query, list) 30 | } 31 | return findById(query, allIds) 32 | } 33 | 34 | const reducer = (state, { type, payload }) => { 35 | switch (type) { 36 | case ADD_MOVIES: { 37 | const movieList = movieListAsMap(payload, state.movieList) 38 | const all = getAllIds(payload, state.list.all) 39 | const leastValued = getLeastValuedIds(payload, state.list.leastValued) 40 | const mostValued = getMostValuedIds(payload, state.list.mostValued) 41 | return { 42 | ...state, 43 | movieList, 44 | list: { 45 | ...state.list, 46 | all, 47 | leastValued, 48 | mostValued, 49 | } 50 | } 51 | } 52 | case SET_FILTER: 53 | return { 54 | ...state, 55 | filter: payload, 56 | } 57 | case SEARCH_MOVIE: 58 | return { 59 | ...state, 60 | filter: 'search', 61 | list: { 62 | ...state.list, 63 | search: searchMovie(payload, state.movieList, state.list.all) 64 | } 65 | } 66 | default: 67 | return state 68 | } 69 | } 70 | 71 | export default reducer -------------------------------------------------------------------------------- /block-buster/src/redux/index.js: -------------------------------------------------------------------------------- 1 | const createStore = (reducer, initialState) => { 2 | let state = initialState 3 | let updater = () => { } 4 | const getState = () => state 5 | 6 | const dispatch = (action) => { 7 | state = reducer(state, action) 8 | updater() 9 | } 10 | const subscribe = (listener) => { 11 | updater = listener 12 | } 13 | return { 14 | getState, 15 | dispatch, 16 | subscribe 17 | } 18 | } 19 | 20 | function combineReducers() { 21 | 22 | } 23 | 24 | export { 25 | createStore, 26 | combineReducers 27 | } 28 | 29 | -------------------------------------------------------------------------------- /block-buster/src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore } from './redux/index.js' 2 | import reducer from './reducers/index.js' 3 | import movies from './movies.js' 4 | import { 5 | movieListAsMap, 6 | getAllIds, 7 | getMostValuedIds, 8 | getLeastValuedIds, 9 | } from './normalize.js' 10 | 11 | const initialState = { 12 | movieList: movieListAsMap(movies), 13 | filter: 'all', 14 | list: { 15 | all: getAllIds(movies), 16 | mostValued: getMostValuedIds(movies), 17 | leastValued: getLeastValuedIds(movies), 18 | } 19 | } 20 | 21 | const store = createStore(reducer, initialState) 22 | 23 | export default store 24 | -------------------------------------------------------------------------------- /modules/01/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tu primer módulo 8 | 9 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /modules/01/module.js: -------------------------------------------------------------------------------- 1 | export default function sayHey(name) { 2 | return `hola ${name}` 3 | } 4 | 5 | // export default sayHey -------------------------------------------------------------------------------- /modules/02/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: system-ui, Helvetica, sans-serif; 4 | background-color: white; 5 | text-align: center; 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | min-height: 100vh; 10 | background: #8c53c6; 11 | } 12 | .wrapper { 13 | max-width: 1020px; 14 | margin: auto; 15 | } 16 | h1 { 17 | font-size: 70px; 18 | margin: 0; 19 | /* line-height: 100px; */ 20 | -webkit-text-stroke: 2px rgba(255,255,255,.7); 21 | } 22 | p { 23 | max-width: 32em; 24 | margin: 1em auto; 25 | } 26 | .burn { 27 | color: white; 28 | background: #00aef7; 29 | border: none; 30 | font-size: 5em; 31 | line-height: 0; 32 | border-radius: 50%; 33 | font-weight: bold; 34 | cursor: pointer; 35 | width: 150px; 36 | height: 150px; 37 | outline: 0; 38 | box-shadow: 10px 10px 10px 0 rgba(0,0,0,.5), -5px -5px 10px 0 rgba(255,255,255,.3); 39 | position: relative; 40 | } 41 | details { 42 | text-align: left; 43 | color: white; 44 | padding: 1em; 45 | cursor: pointer; 46 | line-height: 1.6; 47 | font-size: 1.2em; 48 | } 49 | .burn:before { 50 | border: 3px solid white; 51 | content: ''; 52 | background-color: #00aef7; 53 | z-index: -1; 54 | position: absolute; 55 | left: -20px; 56 | top: -20px; 57 | bottom: -20px; 58 | right: -20px; 59 | border-radius: 50%; 60 | } 61 | 62 | .burn:active { 63 | box-shadow: inset 10px 10px 10px 0 rgba(0,0,0,.5), inset -10px -10px 10px 0 rgba(255,255,255,.3); 64 | } 65 | 66 | .result { 67 | font-size: 3em; 68 | color: white; 69 | text-shadow: 5px 2px 0 red; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /modules/02/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Importar Funciones específicas 9 | 10 | 11 | 12 |
13 |
14 |
15 | 16 | ⭐️ explicación 17 | 18 |

El libro, Convert Anything to Calories, llegó a la conclusión de que, en promedio, una persona con un dedo 19 | indice 20 | de 21 | 10.8 cm^3 y de 11.7 gramos necesitará muuuchos clicks para conservar la línea. Se requieren 16.7 micromoles de 22 | adenosín 23 | trifosfato (la molécula que transporta energía entre las células) para mover un gramo del cuerpo por un 24 | segundo, 25 | y 26 | como 27 | hay 7.3 calorías en un mol (la medida de cantidad de una sustancia) de adenosín trifosfato, en teoría, cada 28 | click 29 | del 30 | mouse debería quemar 1.42 calorías. Aunque el cálculo asume el movimiento de todo el músculo, por lo que la 31 | cantidad 32 | real puede variar un poco.

33 |

Pero resulta que es sábado y tienes hambre. treinta minutos después te llega una pizza (1,835kCal) con una 34 | coca 35 | (97kCal), tendrías que hacer 1,360,563 clicks para quemar la cena. Y cómo es sábado seguramente involucrará 36 | unas cuantas 37 | bebidas alcohólicas: en un Gin & Tonic (120kCal) hay 84,507 clicks, y 131,690.14 por cada cerveza que 38 | consumas. En otras 39 | palabras, con 427 clicks en promedio por día, la computadora no es un buen ejercicio. 40 |

41 |
42 |

Quemador de calorías

43 |

44 | 45 |
46 |
47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /modules/02/js/actions/index.js: -------------------------------------------------------------------------------- 1 | export const BURN = 'BURN' -------------------------------------------------------------------------------- /modules/02/js/app.js: -------------------------------------------------------------------------------- 1 | import store from './store.js' 2 | import { BURN } from './actions/index.js' 3 | 4 | store.subscribe(() => { 5 | // console.log('ha cambiado algo en el store', store.getState()) 6 | window.result.textContent = `Haz quemado ${store.getState()} calorías` 7 | }) 8 | 9 | const burn = () => { 10 | store.dispatch({ 11 | type: BURN, 12 | payload: 1.42, 13 | }) 14 | } 15 | 16 | window.burn.addEventListener('click', burn) 17 | -------------------------------------------------------------------------------- /modules/02/js/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { BURN } from '../actions/index.js' 2 | 3 | const reducer = (state, { type, payload }) => { 4 | switch (type) { 5 | case BURN: 6 | return state + payload 7 | default: 8 | return state 9 | } 10 | } 11 | 12 | export default reducer -------------------------------------------------------------------------------- /modules/02/js/redux/index.js: -------------------------------------------------------------------------------- 1 | const createStore = (reducer, initialState) => { 2 | let state = initialState 3 | let updater = () => { } 4 | const getState = () => state 5 | 6 | const dispatch = (action) => { 7 | state = reducer(state, action) 8 | updater() 9 | } 10 | const subscribe = (listener) => { 11 | updater = listener 12 | } 13 | return { 14 | getState, 15 | dispatch, 16 | subscribe 17 | } 18 | } 19 | 20 | function combineReducers() { 21 | 22 | } 23 | 24 | export { 25 | createStore, 26 | combineReducers 27 | } 28 | 29 | -------------------------------------------------------------------------------- /modules/02/js/store.js: -------------------------------------------------------------------------------- 1 | import { createStore } from './redux/index.js' 2 | import reducer from './reducers/index.js' 3 | 4 | const store = createStore(reducer, 0) 5 | 6 | export default store 7 | -------------------------------------------------------------------------------- /modules/03/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Asignando alias a exports importadas 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /modules/03/js/app.js: -------------------------------------------------------------------------------- 1 | 2 | import { render } from './react-dom/index.js' 3 | import Title from './components/title.js' 4 | 5 | render(Title, window.container) -------------------------------------------------------------------------------- /modules/03/js/components/title.js: -------------------------------------------------------------------------------- 1 | import styled from '../styled-components/index.js' 2 | import { component as Component } from '../react/index.js' 3 | 4 | const TitleStyled = styled.h1` 5 | font-family: system-ui; 6 | color: orange; 7 | font-size: 50px; 8 | text-shadow: 2px 2px 0 black; 9 | line-height: 100vh; 10 | ` 11 | 12 | const props = { 13 | message: 'No Te Rindas', 14 | } 15 | 16 | const Title = Component` ${'message'} `(props) 17 | 18 | export default TitleStyled(Title) 19 | -------------------------------------------------------------------------------- /modules/03/js/react-dom/index.js: -------------------------------------------------------------------------------- 1 | function render(component, container) { 2 | container.innerHTML = component 3 | } 4 | 5 | function hydrate() { 6 | 7 | } 8 | 9 | export { 10 | render, 11 | hydrate, 12 | } -------------------------------------------------------------------------------- /modules/03/js/react/index.js: -------------------------------------------------------------------------------- 1 | function component(strings, ...dynamicValues) { 2 | return function (props) { 3 | let newContent = strings.slice() 4 | console.log(newContent) 5 | dynamicValues.forEach((value, index) => { 6 | newContent[index] += props[value] 7 | }) 8 | return newContent.join('') 9 | } 10 | } 11 | 12 | export { 13 | component 14 | } 15 | -------------------------------------------------------------------------------- /modules/03/js/styled-components/index.js: -------------------------------------------------------------------------------- 1 | const styled = { 2 | h1: function (styles) { 3 | return function (content) { 4 | return ` 5 |

6 | ${content} 7 |

8 | ` 9 | } 10 | }, 11 | h2: '', 12 | div: '', 13 | } 14 | 15 | export default styled -------------------------------------------------------------------------------- /modules/04/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: system-ui, Helvetica, sans-serif; 4 | background-color: white; 5 | text-align: center; 6 | min-height: 100vh; 7 | background: repeating-linear-gradient(-225deg, #d0d6f6, #d0d6f6 20px, white 20px, white 40px,#fdd1d0 40px, #fdd1d0 60px, white 60px, white 80px); 8 | } 9 | .wrapper { 10 | max-width: 1020px; 11 | margin: auto; 12 | } 13 | /* #d0d6f6 14 | white 15 | #d1d9fd */ 16 | h1 { 17 | font-size: 110px; 18 | line-height: 100px; 19 | } 20 | 21 | .hero-content { 22 | display: flex; 23 | /* border: 1px solid red; */ 24 | } 25 | .app { 26 | flex: 1; 27 | flex-direction: column; 28 | justify-content: center; 29 | /* align-items: center; */ 30 | font-size: 2em; 31 | text-align: left; 32 | } 33 | p { 34 | display: flex; 35 | justify-content: space-between; 36 | font-size: 2em; 37 | } 38 | .mac { 39 | height: 700px; 40 | flex: 1; 41 | background: url('../images/mac.png') center top no-repeat; 42 | background-size: contain; 43 | } 44 | 45 | .button { 46 | color: white; 47 | background: #3f69ff; 48 | border: none; 49 | font-size: 2em; 50 | padding: .7em 2em; 51 | border-radius: .5em; 52 | font-weight: bold; 53 | cursor: pointer; 54 | } 55 | 56 | 57 | .icrecream-grid { 58 | display: grid; 59 | grid-template-columns: auto-fit; 60 | grid-auto-flow: column; 61 | margin-bottom: 1em; 62 | } 63 | 64 | .icrecream-grid .flavor { 65 | height: 50px; 66 | } 67 | .flavor { 68 | transition: .3s; 69 | cursor: pointer; 70 | display: flex; 71 | align-items: center; 72 | justify-content: center; 73 | text-transform: capitalize; 74 | box-sizing: border-box; 75 | } 76 | .flavor.is-active { 77 | border: 5px dashed greenyellow; 78 | } 79 | .flavor:hover { 80 | z-index: 1; 81 | transform: scale(1.2); 82 | } 83 | .flavor:before { 84 | content: attr(data-flavor); 85 | color: white; 86 | font-weight: bold; 87 | text-shadow: 1px 1px 0 black; 88 | } 89 | 90 | [data-flavor="strawberry"] { 91 | background: red; 92 | } 93 | [data-flavor="chocolate"] { 94 | background: brown; 95 | } 96 | [data-flavor="vanilla"] { 97 | background: #f3e5ab; 98 | } 99 | [data-flavor="orange"] { 100 | background: orange; 101 | } 102 | [data-flavor="lemon"] { 103 | background: lime; 104 | } 105 | 106 | -------------------------------------------------------------------------------- /modules/04/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Side Effect 8 | 9 | 10 | 11 | 12 |
13 |

Icecream Machine

14 |

Elige los 2 sabores que más te gusten y haz tu orden

15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /modules/04/js/app.js: -------------------------------------------------------------------------------- 1 | import './icecream/index.js' -------------------------------------------------------------------------------- /modules/04/js/icecream/icream-machine.js: -------------------------------------------------------------------------------- 1 | import { isRequired } from './validations.js' 2 | 3 | function makeIcecream(flavor1 = isRequired('flavor1'), flavor2 = isRequired('flavor2')) { 4 | // console.log(flavor1) 5 | alert(`tienes un rico helado de ${flavor1.dataset.flavor} y ${flavor2.dataset.flavor}`) 6 | } 7 | 8 | export { 9 | makeIcecream 10 | } -------------------------------------------------------------------------------- /modules/04/js/icecream/index.js: -------------------------------------------------------------------------------- 1 | import { makeIcecream } from './icream-machine.js' 2 | 3 | const $flavors = document.querySelectorAll('.flavor') 4 | // console.log($flavors) 5 | $flavors.forEach($el => $el.addEventListener('click', selectFlavor)) 6 | function selectFlavor() { 7 | this.classList.toggle('is-active') 8 | } 9 | 10 | window.btn.addEventListener('click', () => { 11 | const $flavors = document.querySelectorAll('.flavor.is-active') 12 | console.log($flavors) 13 | try { 14 | makeIcecream($flavors[0], $flavors[1]) 15 | } catch (error) { 16 | alert('Aun no completa los sabores para preparar tu helado') 17 | } 18 | }) -------------------------------------------------------------------------------- /modules/04/js/icecream/validations.js: -------------------------------------------------------------------------------- 1 | function isRequired(param) { 2 | throw new Error(`${param} es requerido`) 3 | } 4 | 5 | export { 6 | isRequired 7 | } -------------------------------------------------------------------------------- /modules/05/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 3 | } 4 | 5 | .wrapper { 6 | max-width: 1042px; 7 | margin: 0 auto; 8 | } 9 | 10 | .hidden { 11 | display: none !important; 12 | } 13 | 14 | .video { 15 | position: relative; 16 | cursor: pointer; 17 | user-select: none; 18 | } 19 | .video-js { 20 | width: 100%; 21 | height: auto; 22 | aspect-ratio: 16/9; 23 | } 24 | .video::after { 25 | content: "►"; 26 | width: 100px; 27 | height: 100px; 28 | color: white; 29 | background: red; 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | position: absolute; 34 | left: 0; 35 | right: 0; 36 | margin: auto; 37 | top: 0; 38 | bottom: 0; 39 | border-radius: 50%; 40 | font-size: 1.3em; 41 | } 42 | 43 | .video:hover .video::after { 44 | transform: scale(1.1); 45 | } 46 | 47 | 48 | img { 49 | max-width: 100%; 50 | } -------------------------------------------------------------------------------- /modules/05/images/ps5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonidasEsteban/curso-javascript-react/7a52f7d0bf55754815d4dfacdf049b4f7141d4d2/modules/05/images/ps5.jpeg -------------------------------------------------------------------------------- /modules/05/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Dynamic Imports 12 | 13 | 14 | 15 |
16 |

Lanzamiento del PS5

17 |

18 | Ya es oficial. Sony acaba de desvelar el precio oficial de su nueva PlayStation 5: 499,99 euros para la 19 | PlayStation 5 20 | con lector de discos y 399,99 euros para la versión completamente digital. De esa forma, la compañía nipona deja 21 | el 22 | modelo más completo a la altura de la Xbox Series X, mientras que supera en precio al modelo más asequible de 23 | Microsoft, 24 | la Xbox Series S, que se queda en 299 euros. 25 |

26 |

Mira la transimisión en vivo

27 |
28 | 29 |
30 | 34 |
35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /modules/05/js/app.js: -------------------------------------------------------------------------------- 1 | video.addEventListener('click', function () { 2 | console.log('click') 3 | this.classList.add('hidden') 4 | import('./player.js').then(({ player }) => { 5 | // console.log(player.play()) 6 | setTimeout(() => { 7 | player.play() 8 | }, 100) 9 | }) 10 | }) -------------------------------------------------------------------------------- /modules/05/js/player.js: -------------------------------------------------------------------------------- 1 | import 'https://vjs.zencdn.net/7.8.4/video.js' 2 | 3 | console.log('he importado el player') 4 | 5 | ps5.classList.remove('hidden') 6 | 7 | const player = videojs('ps5', { 8 | controls: true, 9 | preload: 'auto', 10 | }) 11 | 12 | export { 13 | player 14 | } -------------------------------------------------------------------------------- /parameter-defaults/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: system-ui, Helvetica, sans-serif; 4 | background-color: white; 5 | text-align: center; 6 | min-height: 100vh; 7 | background: repeating-linear-gradient(-225deg, #d0d6f6, #d0d6f6 20px, white 20px, white 40px,#fdd1d0 40px, #fdd1d0 60px, white 60px, white 80px); 8 | } 9 | .wrapper { 10 | max-width: 1020px; 11 | margin: auto; 12 | } 13 | /* #d0d6f6 14 | white 15 | #d1d9fd */ 16 | h1 { 17 | font-size: 110px; 18 | line-height: 100px; 19 | } 20 | 21 | .hero-content { 22 | display: flex; 23 | /* border: 1px solid red; */ 24 | } 25 | .app { 26 | flex: 1; 27 | flex-direction: column; 28 | justify-content: center; 29 | /* align-items: center; */ 30 | font-size: 2em; 31 | text-align: left; 32 | } 33 | p { 34 | display: flex; 35 | justify-content: space-between; 36 | font-size: 2em; 37 | } 38 | .mac { 39 | height: 700px; 40 | flex: 1; 41 | background: url('../images/mac.png') center top no-repeat; 42 | background-size: contain; 43 | } 44 | 45 | .button { 46 | color: white; 47 | background: #3f69ff; 48 | border: none; 49 | font-size: 2em; 50 | padding: .7em 2em; 51 | border-radius: .5em; 52 | font-weight: bold; 53 | cursor: pointer; 54 | } 55 | 56 | 57 | .icrecream-grid { 58 | display: grid; 59 | grid-template-columns: auto-fit; 60 | grid-auto-flow: column; 61 | margin-bottom: 1em; 62 | } 63 | 64 | .icrecream-grid .flavor { 65 | height: 50px; 66 | } 67 | .flavor { 68 | transition: .3s; 69 | cursor: pointer; 70 | display: flex; 71 | align-items: center; 72 | justify-content: center; 73 | text-transform: capitalize; 74 | box-sizing: border-box; 75 | } 76 | .flavor.is-active { 77 | border: 5px dashed greenyellow; 78 | } 79 | .flavor:hover { 80 | z-index: 1; 81 | transform: scale(1.2); 82 | } 83 | .flavor:before { 84 | content: attr(data-flavor); 85 | color: white; 86 | font-weight: bold; 87 | text-shadow: 1px 1px 0 black; 88 | } 89 | 90 | [data-flavor="strawberry"] { 91 | background: red; 92 | } 93 | [data-flavor="chocolate"] { 94 | background: brown; 95 | } 96 | [data-flavor="vanilla"] { 97 | background: #f3e5ab; 98 | } 99 | [data-flavor="orange"] { 100 | background: orange; 101 | } 102 | [data-flavor="lemon"] { 103 | background: lime; 104 | } 105 | 106 | -------------------------------------------------------------------------------- /parameter-defaults/images/mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonidasEsteban/curso-javascript-react/7a52f7d0bf55754815d4dfacdf049b4f7141d4d2/parameter-defaults/images/mac.png -------------------------------------------------------------------------------- /parameter-defaults/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Paramater Defaults 8 | 9 | 10 | 11 | 12 |
13 |

Icecream Machine

14 |

Elige los 2 sabores que más te gusten y haz tu orden

15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /quemador-calorias/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: system-ui, Helvetica, sans-serif; 4 | background-color: white; 5 | text-align: center; 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | min-height: 100vh; 10 | background: #8c53c6; 11 | } 12 | .wrapper { 13 | max-width: 1020px; 14 | margin: auto; 15 | } 16 | h1 { 17 | font-size: 70px; 18 | margin: 0; 19 | /* line-height: 100px; */ 20 | -webkit-text-stroke: 2px rgba(255,255,255,.7); 21 | } 22 | p { 23 | max-width: 32em; 24 | margin: 1em auto; 25 | } 26 | .burn { 27 | color: white; 28 | background: #00aef7; 29 | border: none; 30 | font-size: 5em; 31 | line-height: 0; 32 | border-radius: 50%; 33 | font-weight: bold; 34 | cursor: pointer; 35 | width: 150px; 36 | height: 150px; 37 | outline: 0; 38 | box-shadow: 10px 10px 10px 0 rgba(0,0,0,.5), -5px -5px 10px 0 rgba(255,255,255,.3); 39 | position: relative; 40 | } 41 | details { 42 | text-align: left; 43 | color: white; 44 | padding: 1em; 45 | cursor: pointer; 46 | line-height: 1.6; 47 | font-size: 1.2em; 48 | } 49 | .burn:before { 50 | border: 3px solid white; 51 | content: ''; 52 | background-color: #00aef7; 53 | z-index: -1; 54 | position: absolute; 55 | left: -20px; 56 | top: -20px; 57 | bottom: -20px; 58 | right: -20px; 59 | border-radius: 50%; 60 | } 61 | 62 | .burn:active { 63 | box-shadow: inset 10px 10px 10px 0 rgba(0,0,0,.5), inset -10px -10px 10px 0 rgba(255,255,255,.3); 64 | } 65 | 66 | .result { 67 | font-size: 3em; 68 | color: white; 69 | text-shadow: 5px 2px 0 red; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /quemador-calorias/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Quemador de Calorías 9 | 10 | 11 | 12 |
13 |
14 |
15 | 16 | ⭐️ explicación 17 | 18 |

El libro, Convert Anything to Calories, llegó a la conclusión de que, en promedio, una persona con un dedo 19 | indice 20 | de 21 | 10.8 cm^3 y de 11.7 gramos necesitará muuuchos clicks para conservar la línea. Se requieren 16.7 micromoles de 22 | adenosín 23 | trifosfato (la molécula que transporta energía entre las células) para mover un gramo del cuerpo por un 24 | segundo, 25 | y 26 | como 27 | hay 7.3 calorías en un mol (la medida de cantidad de una sustancia) de adenosín trifosfato, en teoría, cada 28 | click 29 | del 30 | mouse debería quemar 1.42 calorías. Aunque el cálculo asume el movimiento de todo el músculo, por lo que la 31 | cantidad 32 | real puede variar un poco.

33 |

Pero resulta que es sábado y tienes hambre. treinta minutos después te llega una pizza (1,835kCal) con una 34 | coca 35 | (97kCal), tendrías que hacer 1,360,563 clicks para quemar la cena. Y cómo es sábado seguramente involucrará 36 | unas cuantas 37 | bebidas alcohólicas: en un Gin & Tonic (120kCal) hay 84,507 clicks, y 131,690.14 por cada cerveza que 38 | consumas. En otras 39 | palabras, con 427 clicks en promedio por día, la computadora no es un buen ejercicio. 40 |

41 |
42 |

Quemador de calorías

43 |

44 | 45 |
46 |
47 | 48 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /react-create-element/components/app.js: -------------------------------------------------------------------------------- 1 | import { Component, createElement } from '../lib/react/index.js' 2 | import User from './user.js' 3 | import Wrapper from './wrapper.js' 4 | import UserStyled from './user-styled.js' 5 | 6 | 7 | 8 | 9 | class App extends Component { 10 | render() { 11 | return createElement('div', { 12 | class: 'app', 13 | children: new Wrapper({ 14 | children: [ 15 | new User({ 16 | name: 'Ash', 17 | avatar: './images/ash.jpg', 18 | age: 10, 19 | }), 20 | new UserStyled({ 21 | name: 'Ash', 22 | avatar: './images/ash.jpg', 23 | age: 10, 24 | }) 25 | ] 26 | }) 27 | }) 28 | } 29 | } 30 | 31 | // ${new Wrapper({ 32 | // children: ` 33 | //

React.js ⭐️⭐️⭐️⭐️⭐️

34 | // ${new User({ 35 | // name: 'Ash', 36 | // avatar: './images/ash.jpg' 37 | // }).render()} 38 | // ${new UserStyled({ 39 | // name: 'Ash', 40 | // avatar: './images/ash.jpg' 41 | // }).render()} 42 | // ` 43 | // }).render()} 44 | export default App 45 | -------------------------------------------------------------------------------- /react-create-element/components/user-styled.js: -------------------------------------------------------------------------------- 1 | import styled from '../lib/styled-components.js' 2 | import { Component, createElement } from '../lib/react/index.js' 3 | 4 | const UserStyled = styled.div` 5 | background-image: linear-gradient(to bottom, ${({ primaryColor }) => primaryColor} 0%, ${({ primaryColor }) => primaryColor} 130px,${({ tertiaryColor }) => tertiaryColor} 130px, ${({ tertiaryColor }) => tertiaryColor} 131px, ${({ secondaryColor }) => secondaryColor} 131px, ${({ secondaryColor}) => secondaryColor} 100%); 6 | /* background: ${(props) => props.primaryColor}; */ 7 | color: ${({ fontColor }) => fontColor} ; 8 | text-align: center; 9 | overflow: hidden; 10 | padding: 20px; 11 | font-family: system-ui; 12 | border-radius: .3rem; 13 | box-shadow: 0 0 1px rgba(0,0,0,.5); 14 | cursor: pointer; 15 | user-select: none; 16 | ` 17 | 18 | const AvatarStyled = styled.img` 19 | max-width: 150px; 20 | border-radius: 50%; 21 | border: 5px solid white; 22 | box-shadow: 0 0 2px black; 23 | ` 24 | const theme = { 25 | light: { 26 | primaryColor: '#f9f9f9', 27 | secondaryColor: 'white', 28 | tertiaryColor: 'rgba(0,0,0,.15)', 29 | fontColor: 'black', 30 | }, 31 | dark: { 32 | primaryColor: '#212429', 33 | secondaryColor: '#212429', 34 | tertiaryColor: 'white', 35 | fontColor: 'white', 36 | } 37 | } 38 | 39 | class User extends Component { 40 | state = { 41 | mode: 'light' 42 | } 43 | setMode = (event) => { 44 | // console.log(event.matches) 45 | if(event.matches) { 46 | return this.setState({ 47 | mode: 'dark' 48 | }) 49 | } 50 | this.setState({ 51 | mode: 'light' 52 | }) 53 | } 54 | componentDidMount() { 55 | const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') 56 | this.setMode(mediaQuery) 57 | mediaQuery.addEventListener('change', this.setMode) 58 | } 59 | render() { 60 | const { mode } = this.state 61 | const colors = (mode === 'light') ? theme.light : theme.dark 62 | const { name, avatar } = this.props 63 | console.log(colors) 64 | return UserStyled({ 65 | ...colors, 66 | children: [ 67 | AvatarStyled({ 68 | src: avatar 69 | }), 70 | createElement('h2', null, `${name} ${mode} mode`) 71 | ] 72 | }, '') 73 | } 74 | } 75 | 76 | export default User -------------------------------------------------------------------------------- /react-create-element/components/user.js: -------------------------------------------------------------------------------- 1 | import { Component, createElement } from '../lib/react/index.js' 2 | 3 | class User extends Component { 4 | displayName = 'User' 5 | state = { 6 | age: this.props.age 7 | } 8 | componentDidMount() { 9 | console.log(`el componente ${this.displayName} se renderizó`) 10 | } 11 | componentWillMount() { 12 | console.log(`el componente ${this.displayName} se va a renderizar por primera vez`) 13 | } 14 | componentDidUpdate() { 15 | console.log(`el componente ${this.displayName} se actualizó`) 16 | } 17 | // constructor(props) { 18 | // super(props) 19 | // this.handleClick = this.handleClick.bind(this) 20 | // } 21 | // handleClick(event) { 22 | // console.log(this.props.name, event) 23 | // } 24 | handleClick = (event) => { 25 | console.log(this.state.age) 26 | this.setState({ 27 | age: this.state.age + 1, 28 | }) 29 | } 30 | render() { 31 | 32 | const { avatar, name } = this.props 33 | const { age } = this.state 34 | return createElement('div', { 35 | class: 'user', 36 | onClick: this.handleClick, 37 | children: [ 38 | createElement('div', { 39 | class: 'avatar', 40 | children: createElement('img', { 41 | src: avatar 42 | }) 43 | }), 44 | createElement('h2', null, `Hola soy ${name} y tengo ${age} años`) 45 | ] 46 | }) 47 | } 48 | } 49 | 50 | export default User -------------------------------------------------------------------------------- /react-create-element/components/wrapper.js: -------------------------------------------------------------------------------- 1 | import { Component, createElement } from '../lib/react/index.js' 2 | 3 | class Wrapper extends Component { 4 | render() { 5 | const { children } = this.props 6 | return createElement('div', { 7 | class: 'wrapper', 8 | children 9 | }) 10 | } 11 | } 12 | 13 | export default Wrapper -------------------------------------------------------------------------------- /react-create-element/images/ash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonidasEsteban/curso-javascript-react/7a52f7d0bf55754815d4dfacdf049b4f7141d4d2/react-create-element/images/ash.jpg -------------------------------------------------------------------------------- /react-create-element/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /react-create-element/index.js: -------------------------------------------------------------------------------- 1 | import { render } from './lib/react-dom.js' 2 | import App from './components/app.js' 3 | 4 | const container = document.querySelector('#root') 5 | 6 | render(new App(), container) -------------------------------------------------------------------------------- /react-create-element/lib/react-dom.js: -------------------------------------------------------------------------------- 1 | function render(element, container) { 2 | if (typeof element === 'string' || element instanceof Element) { 3 | return container.append(element) 4 | } 5 | function reRender(newChild) { 6 | container.replaceChild(newChild, childElement) 7 | childElement = newChild 8 | } 9 | element.update = reRender 10 | let childElement = element.build() 11 | container.append(childElement) 12 | element.componentDidMount() 13 | } 14 | 15 | 16 | export { 17 | render 18 | } -------------------------------------------------------------------------------- /react-create-element/lib/react/index.js: -------------------------------------------------------------------------------- 1 | export { 2 | Component 3 | } from './src/React.js' 4 | 5 | export { 6 | createElement, 7 | createElement as jsx, 8 | } from './src/ReactElement.js' -------------------------------------------------------------------------------- /react-create-element/lib/react/src/React.js: -------------------------------------------------------------------------------- 1 | class Component { 2 | constructor(props = {}, state = {}) { 3 | this.props = props 4 | this.state = state 5 | } 6 | update() {} 7 | #updater(){ 8 | this.update(this.render()) 9 | this.componentDidUpdate() 10 | } 11 | // se llama andes que se renderice el componente 12 | componentWillMount() { 13 | 14 | } 15 | componentDidMount() { 16 | 17 | } 18 | componentDidUpdate() { 19 | 20 | } 21 | setState(newState) { 22 | this.state = { 23 | ...this.state, 24 | ...newState 25 | } 26 | this.#updater() 27 | } 28 | build() { 29 | this.componentWillMount() 30 | return this.render() 31 | } 32 | } 33 | 34 | export { 35 | Component 36 | } -------------------------------------------------------------------------------- /react-create-element/lib/react/src/ReactElement.js: -------------------------------------------------------------------------------- 1 | import { render } from '../../react-dom.js' 2 | 3 | 4 | function renderChildren(children, container) { 5 | if (Array.isArray(children)) { 6 | return children.forEach(child => render(child, container)) 7 | } 8 | return render(children, container) 9 | } 10 | 11 | function setEvents(element, event, callback) { 12 | return element.addEventListener(event, callback) 13 | } 14 | 15 | function setProperties(prop, value, element) { 16 | // events support 17 | if(prop.startsWith('on')) { 18 | const event = prop.replace('on','').toLowerCase() 19 | return setEvents(element, event, value) 20 | } 21 | 22 | // support for children 23 | if (prop === 'children') { 24 | return renderChildren(value, element) 25 | } 26 | 27 | // support for attributes 28 | const attribute = value 29 | return element.setAttribute(prop, attribute) 30 | } 31 | 32 | export function createElement(type, props, content) { 33 | // Creando tipo de elemento 34 | const element = document.createElement(type) 35 | 36 | // Contenido 37 | if (content) { 38 | element.textContent = content 39 | } 40 | // Propiedades 41 | if (props) { 42 | Object.keys(props).forEach(prop => setProperties(prop, props[prop], element )) 43 | } 44 | 45 | // console.log(Object.keys(props)) 46 | 47 | return element 48 | } 49 | 50 | -------------------------------------------------------------------------------- /react-create-element/lib/styled-components.js: -------------------------------------------------------------------------------- 1 | import { createElement } from './react/index.js' 2 | 3 | const styled = {} 4 | 5 | const elements = [ 6 | 'h1', 7 | 'p', 8 | 'div', 9 | 'img', 10 | 'article', 11 | 'footer', 12 | 'header' 13 | ] 14 | 15 | function buildStyles(strings, dynamicValues, props) { 16 | let style = strings.slice() 17 | dynamicValues.forEach((value, index) => { 18 | style[index] += value(props) 19 | }) 20 | 21 | return style.join('') 22 | } 23 | 24 | 25 | elements.forEach((tag) => { 26 | styled[tag] = function (strings, ...dynamicValues) { 27 | return function (props, content) { 28 | const style = buildStyles(strings, dynamicValues, props) 29 | return createElement(tag, { 30 | ...props, 31 | style, 32 | }, content) 33 | 34 | } 35 | } 36 | }) 37 | 38 | 39 | 40 | 41 | export default styled 42 | -------------------------------------------------------------------------------- /react-create-element/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: system-ui; 4 | color: #2c2e31; 5 | } 6 | 7 | .wrapper { 8 | max-width: 1024px; 9 | width: 100%; 10 | margin: 0 auto; 11 | } 12 | 13 | .user { 14 | background-image: linear-gradient(to bottom, #f9f9f9 0%, #f9f9f9 130px,rgba(0,0,0,.15) 130px, rgba(0,0,0,.15) 131px, white 131px, white 100%); 15 | text-align: center; 16 | overflow: hidden; 17 | padding: 20px; 18 | font-family: system-ui; 19 | border-radius: .3rem; 20 | box-shadow: 0 0 1px rgba(0,0,0,.5); 21 | cursor: pointer; 22 | user-select: none; 23 | } 24 | 25 | .user img { 26 | max-width: 150px; 27 | border-radius: 50%; 28 | border: 5px solid white; 29 | box-shadow: 0 0 2px black; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /react-hooks/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: system-ui, Helvetica, sans-serif; 4 | background-color: white; 5 | text-align: center; 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | min-height: 100vh; 10 | background: #f8f9fd; 11 | } 12 | .wrapper { 13 | max-width: 1020px; 14 | margin: auto; 15 | } 16 | h1 { 17 | font-size: 110px; 18 | line-height: 100px; 19 | } 20 | 21 | .button { 22 | color: white; 23 | background: #3f69ff; 24 | border: none; 25 | font-size: 1em; 26 | padding: .7em 2em; 27 | border-radius: .5em; 28 | font-weight: bold; 29 | cursor: pointer; 30 | } 31 | 32 | .profile { 33 | flex: 1; 34 | } 35 | 36 | .profile-content { 37 | height: 700px; 38 | background: url('../images/user.png') center top no-repeat; 39 | background-size: contain; 40 | max-width: 350px; 41 | margin: 1em auto; 42 | border-radius: 1em; 43 | box-shadow: 1px 1px 5px rgba(0,0,0,.1); 44 | } 45 | .user { 46 | padding-top: 30px; 47 | } 48 | img { 49 | border-radius: 50%; 50 | } 51 | .details { 52 | background: white; 53 | padding: 1px; 54 | font-size: 1.2em; 55 | } 56 | .social { 57 | /* margin-top: 50px; */ 58 | padding: 1em 0; 59 | font-weight: bold; 60 | background: #f8f9fd; 61 | display: flex; 62 | justify-content: space-evenly; 63 | } 64 | 65 | .bio { 66 | display: flex; 67 | background: white; 68 | } 69 | 70 | .city { 71 | background: white; 72 | margin: 0; 73 | padding: 1em; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /react-hooks/images/ash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonidasEsteban/curso-javascript-react/7a52f7d0bf55754815d4dfacdf049b4f7141d4d2/react-hooks/images/ash.jpg -------------------------------------------------------------------------------- /react-hooks/images/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonidasEsteban/curso-javascript-react/7a52f7d0bf55754815d4dfacdf049b4f7141d4d2/react-hooks/images/user.png -------------------------------------------------------------------------------- /react-hooks/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React Hooks - useState 9 | 10 | 11 | 12 |
13 |
14 | 15 |
16 | 17 |
18 |
19 |
20 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /react/components/app.js: -------------------------------------------------------------------------------- 1 | import { Component } from '../lib/react.js' 2 | import User from './user.js' 3 | import Wrapper from './wrapper.js' 4 | import UserStyled from './user-styled.js' 5 | 6 | class App extends Component { 7 | render() { 8 | return ` 9 |
10 | ${new Wrapper({ 11 | children: ` 12 |

React.js ⭐️⭐️⭐️⭐️⭐️

13 | ${new User({ 14 | name: 'Ash', 15 | avatar: './images/ash.jpg' 16 | }).render()} 17 | ${new UserStyled({ 18 | name: 'Ash', 19 | avatar: './images/ash.jpg' 20 | }).render()} 21 | ` 22 | }).render()} 23 |
24 | ` 25 | } 26 | } 27 | 28 | export default App 29 | -------------------------------------------------------------------------------- /react/components/user-styled.js: -------------------------------------------------------------------------------- 1 | import styled from '../lib/styled-components.js' 2 | import { Component } from '../lib/react.js' 3 | 4 | 5 | const UserStyled = styled.div` 6 | background-image: linear-gradient(to bottom, #f9f9f9 0%, #f9f9f9 130px,rgba(0,0,0,.15) 130px, rgba(0,0,0,.15) 131px, white 131px, white 100%); 7 | text-align: center; 8 | overflow: hidden; 9 | padding: 20px; 10 | font-family: system-ui; 11 | border-radius: .3rem; 12 | box-shadow: 0 0 1px rgba(0,0,0,.5); 13 | cursor: pointer; 14 | user-select: none; 15 | ` 16 | 17 | const AvatarStyled = styled.img` 18 | max-width: 150px; 19 | border-radius: 50%; 20 | border: 5px solid white; 21 | box-shadow: 0 0 2px black; 22 | ` 23 | 24 | class User extends Component { 25 | render() { 26 | const { name, avatar } = this.props 27 | return ` 28 | ${UserStyled(` 29 | ${AvatarStyled(` 30 | src=${avatar} 31 | `)} 32 |

${name}

33 | `)} 34 | ` 35 | } 36 | } 37 | 38 | export default User -------------------------------------------------------------------------------- /react/components/user.js: -------------------------------------------------------------------------------- 1 | import { Component } from '../lib/react.js' 2 | 3 | class User extends Component { 4 | render() { 5 | const { avatar, name } = this.props 6 | return ` 7 |
8 |
9 | 10 |
11 |

${name}

12 |
13 | ` 14 | } 15 | } 16 | 17 | export default User -------------------------------------------------------------------------------- /react/components/wrapper.js: -------------------------------------------------------------------------------- 1 | import { Component } from '../lib/react.js' 2 | 3 | class Wrapper extends Component { 4 | render() { 5 | const { children } = this.props 6 | return ` 7 |
8 | ${children} 9 |
10 | ` 11 | } 12 | } 13 | 14 | export default Wrapper -------------------------------------------------------------------------------- /react/images/ash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonidasEsteban/curso-javascript-react/7a52f7d0bf55754815d4dfacdf049b4f7141d4d2/react/images/ash.jpg -------------------------------------------------------------------------------- /react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /react/index.js: -------------------------------------------------------------------------------- 1 | import { render } from './lib/react-dom.js' 2 | import App from './components/app.js' 3 | 4 | const container = document.querySelector('#root') 5 | 6 | render(new App(), container) -------------------------------------------------------------------------------- /react/lib/react-dom.js: -------------------------------------------------------------------------------- 1 | function render(element, container) { 2 | container.innerHTML = element.render() 3 | } 4 | 5 | 6 | export { 7 | render 8 | } -------------------------------------------------------------------------------- /react/lib/react.js: -------------------------------------------------------------------------------- 1 | class Component { 2 | constructor(props = {}) { 3 | this.props = props 4 | } 5 | } 6 | 7 | export { 8 | Component 9 | } -------------------------------------------------------------------------------- /react/lib/styled-components.js: -------------------------------------------------------------------------------- 1 | const styled = { 2 | h1: function (styles) { 3 | return function (content) { 4 | return ` 5 |

6 | ${content} 7 |

8 | ` 9 | } 10 | }, 11 | div: function (styles) { 12 | return function (content) { 13 | return ` 14 |
15 | ${content} 16 |
17 | ` 18 | } 19 | }, 20 | img: function (styles) { 21 | return function (content) { 22 | return ` 23 | 24 | ` 25 | } 26 | }, 27 | } 28 | 29 | 30 | export default styled 31 | -------------------------------------------------------------------------------- /react/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: system-ui; 4 | color: #2c2e31; 5 | } 6 | 7 | .wrapper { 8 | max-width: 1024px; 9 | width: 100%; 10 | margin: 0 auto; 11 | } 12 | 13 | .user { 14 | background-image: linear-gradient(to bottom, #f9f9f9 0%, #f9f9f9 130px,rgba(0,0,0,.15) 130px, rgba(0,0,0,.15) 131px, white 131px, white 100%); 15 | text-align: center; 16 | overflow: hidden; 17 | padding: 20px; 18 | font-family: system-ui; 19 | border-radius: .3rem; 20 | box-shadow: 0 0 1px rgba(0,0,0,.5); 21 | cursor: pointer; 22 | user-select: none; 23 | } 24 | 25 | .user img { 26 | max-width: 150px; 27 | border-radius: 50%; 28 | border: 5px solid white; 29 | box-shadow: 0 0 2px black; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /redux/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Redux 8 | 9 | 10 | 11 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /rest-spread/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: system-ui, Helvetica, sans-serif; 4 | background-color: white; 5 | text-align: center; 6 | min-height: 100vh; 7 | background: repeating-linear-gradient(-225deg, #a1f1c8, #a1f1c8 20px, white 20px, white 40px,#fdd1d0 40px, #fdd1d0 60px, white 60px, white 80px); 8 | } 9 | .wrapper { 10 | max-width: 1020px; 11 | margin: auto; 12 | } 13 | /* #d0d6f6 14 | white 15 | #d1d9fd */ 16 | h1 { 17 | font-size: 110px; 18 | line-height: 100px; 19 | } 20 | 21 | .hero-content { 22 | display: flex; 23 | /* border: 1px solid red; */ 24 | } 25 | .app { 26 | flex: 1; 27 | flex-direction: column; 28 | justify-content: center; 29 | /* align-items: center; */ 30 | font-size: 2em; 31 | text-align: left; 32 | } 33 | p { 34 | display: flex; 35 | justify-content: space-between; 36 | font-size: 2em; 37 | } 38 | .mac { 39 | height: 700px; 40 | flex: 1; 41 | background: url('../images/mac.png') center top no-repeat; 42 | background-size: contain; 43 | } 44 | 45 | .button { 46 | color: white; 47 | background: #3f69ff; 48 | border: none; 49 | font-size: 2em; 50 | padding: .7em 2em; 51 | border-radius: .5em; 52 | font-weight: bold; 53 | cursor: pointer; 54 | } 55 | 56 | 57 | .icrecream-grid { 58 | display: grid; 59 | grid-template-columns: auto-fit; 60 | grid-auto-flow: column; 61 | margin-bottom: 1em; 62 | } 63 | 64 | .icrecream-grid .flavor { 65 | height: 50px; 66 | } 67 | .flavor { 68 | transition: .3s; 69 | cursor: pointer; 70 | display: flex; 71 | align-items: center; 72 | justify-content: center; 73 | text-transform: capitalize; 74 | box-sizing: border-box; 75 | } 76 | .flavor.is-active { 77 | border: 5px dashed greenyellow; 78 | } 79 | .flavor:hover { 80 | z-index: 1; 81 | transform: scale(1.2); 82 | } 83 | .flavor:before { 84 | content: attr(data-flavor); 85 | color: white; 86 | font-weight: bold; 87 | text-shadow: 1px 1px 0 black; 88 | } 89 | 90 | [data-flavor="strawberry"] { 91 | background: red; 92 | } 93 | [data-flavor="chocolate"] { 94 | background: brown; 95 | } 96 | [data-flavor="vanilla"] { 97 | background: #f3e5ab; 98 | } 99 | [data-flavor="orange"] { 100 | background: orange; 101 | } 102 | [data-flavor="lemon"] { 103 | background: lime; 104 | } 105 | 106 | -------------------------------------------------------------------------------- /rest-spread/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Rest / Spread 8 | 9 | 10 | 11 | 12 |
13 |

Icecream Machine
V2

14 |

Elige los 2 sabores que más te gusten y haz tu orden

15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /shorthand-property-names/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: system-ui, Helvetica, sans-serif; 4 | background-color: white; 5 | text-align: center; 6 | } 7 | .wrapper { 8 | max-width: 1020px; 9 | margin: auto; 10 | padding: 0 2em; 11 | } 12 | h1 { 13 | font-size: 110px; 14 | line-height: 100px; 15 | } 16 | 17 | .hero-content { 18 | display: flex; 19 | /* border: 1px solid red; */ 20 | } 21 | .app { 22 | flex: 1; 23 | flex-direction: column; 24 | justify-content: center; 25 | /* align-items: center; */ 26 | font-size: 2em; 27 | text-align: left; 28 | } 29 | p { 30 | display: flex; 31 | justify-content: space-between; 32 | } 33 | .mac { 34 | height: 700px; 35 | flex: 1; 36 | background: url('../images/mac.png') center top no-repeat; 37 | background-size: contain; 38 | } 39 | 40 | .button { 41 | color: white; 42 | background: #3f69ff; 43 | border: none; 44 | font-size: 1em; 45 | padding: .7em 2em; 46 | border-radius: .5em; 47 | font-weight: bold; 48 | cursor: pointer; 49 | } -------------------------------------------------------------------------------- /shorthand-property-names/images/mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeonidasEsteban/curso-javascript-react/7a52f7d0bf55754815d4dfacdf049b4f7141d4d2/shorthand-property-names/images/mac.png -------------------------------------------------------------------------------- /shorthand-property-names/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ShortHand Property Names 8 | 9 | 10 | 11 | 12 |
13 |

14 | La mejor.
15 | Para los mejores 16 |

17 |
18 |
19 |
20 |
21 |
22 |

23 | 24 | Precio: 25 | 26 | - 27 |

28 |

29 | 30 | Precio oferta: 31 | 32 | - 33 |

34 |

35 | 36 | Precio Final: 37 | 38 | - 39 |

40 | 41 |
42 |
43 |
44 |
45 |
46 | 47 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /styled-components/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Styled Components 8 | 9 | 10 | 11 |
12 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /tagged-templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tagged Templates 8 | 30 | 31 | 32 | 33 | 34 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /template-literals-expresiones/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Template Literals - expresiones 8 | 9 | 10 | 11 | 24 | 25 | 26 | --------------------------------------------------------------------------------