a
This commit is contained in:
87
DEV4.1/tp4/ex1/css/style.css
Normal file
87
DEV4.1/tp4/ex1/css/style.css
Normal file
@@ -0,0 +1,87 @@
|
||||
main {
|
||||
position: absolute;
|
||||
top: 2rem;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
.loader {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.loader {
|
||||
margin: 6rem 0 0;
|
||||
}
|
||||
.error {
|
||||
color: #FFFAAA;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
label {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
label span {
|
||||
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
|
||||
}
|
||||
input {
|
||||
margin: 1rem 0 0;
|
||||
font-size: 1.6rem;
|
||||
font-weight: 300;
|
||||
padding: 0.8rem 1rem;
|
||||
color: white;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
transition: all 0.3s;
|
||||
box-shadow: 1px 1px 2px rgba(0,0,0, 0.3);
|
||||
-moz-appearance:none;
|
||||
-webkit-appearance:none;
|
||||
outline: none;
|
||||
}
|
||||
input:focus {
|
||||
border: 1px solid transparent;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 1rem 0 2rem;
|
||||
}
|
||||
ul li {
|
||||
padding: 0.6rem 1rem;
|
||||
margin: 1px 0;
|
||||
line-height: 1.4rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
align-content: space-between;
|
||||
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 0 2px rgba(0,0,0, 0.3);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
ul li:hover,
|
||||
ul li:active,
|
||||
ul li:focus {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
ul li a {
|
||||
margin: 0 0.6rem 0 0;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
ul li span {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial;
|
||||
font-weight: 300;
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
background-image: -webkit-radial-gradient(ellipse farthest-corner at top, #661141, #000000);
|
||||
background-image: radial-gradient(ellipse farthest-corner at top, #661141, #000000);
|
||||
color: white;
|
||||
}
|
13
DEV4.1/tp4/ex1/img/puff.svg
Normal file
13
DEV4.1/tp4/ex1/img/puff.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL -->
|
||||
<svg width="44" height="44" viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg" stroke="#fff">
|
||||
<g fill="none" fill-rule="evenodd" stroke-width="2">
|
||||
<circle cx="22" cy="22" r="1">
|
||||
<animate attributeName="r" begin="0s" dur="1.8s" values="1; 20" calcMode="spline" keyTimes="0; 1" keySplines="0.165, 0.84, 0.44, 1" repeatCount="indefinite"/>
|
||||
<animate attributeName="stroke-opacity" begin="0s" dur="1.8s" values="1; 0" calcMode="spline" keyTimes="0; 1" keySplines="0.3, 0.61, 0.355, 1" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="22" cy="22" r="1">
|
||||
<animate attributeName="r" begin="-0.9s" dur="1.8s" values="1; 20" calcMode="spline" keyTimes="0; 1" keySplines="0.165, 0.84, 0.44, 1" repeatCount="indefinite"/>
|
||||
<animate attributeName="stroke-opacity" begin="-0.9s" dur="1.8s" values="1; 0" calcMode="spline" keyTimes="0; 1" keySplines="0.3, 0.61, 0.355, 1" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
28
DEV4.1/tp4/ex1/index.html
Normal file
28
DEV4.1/tp4/ex1/index.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="initial-scale=1,witdh=device-width">
|
||||
<link rel="stylesheet" type="text/css" href="./css/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- vue html -->
|
||||
<main>
|
||||
<label>
|
||||
<span>Search a movie</span>
|
||||
<input type="input" placeholder="28 Days Later..">
|
||||
</label>
|
||||
|
||||
<div class="loader">
|
||||
<img src="./img/puff.svg">
|
||||
</div>
|
||||
|
||||
<p class="error"></p>
|
||||
<div id="list-movies">
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
<script type="module" src="./js/app.js"></script>
|
||||
</html>
|
7
DEV4.1/tp4/ex1/js/app.js
Normal file
7
DEV4.1/tp4/ex1/js/app.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import model from './model'
|
||||
import Controller from './controller'
|
||||
import View from './view'
|
||||
|
||||
const view = new View()
|
||||
const app = new Controller(view,model)
|
||||
|
45
DEV4.1/tp4/ex1/js/controller.js
Normal file
45
DEV4.1/tp4/ex1/js/controller.js
Normal file
@@ -0,0 +1,45 @@
|
||||
class Controller {
|
||||
constructor(view,model){
|
||||
this.view = view
|
||||
this.model = model
|
||||
|
||||
this.loading = false
|
||||
this.lastSearch = null
|
||||
this.error = null
|
||||
this.results = []
|
||||
|
||||
this.view.setLoading(false)
|
||||
this.view.bindSearch(this.search.bind(this))
|
||||
}
|
||||
reset() {
|
||||
this.loading = false
|
||||
this.error = null
|
||||
this.results = []
|
||||
}
|
||||
|
||||
async search(searchMovie) {
|
||||
try {
|
||||
this.reset();
|
||||
this.loading = true;
|
||||
this.view.setLoading(true);
|
||||
|
||||
const movies = await this.model.getMovies(searchMovie);
|
||||
|
||||
if (movies && movies.length > 0) {
|
||||
this.results = movies;
|
||||
} else {
|
||||
this.error = "No movies found.";
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error occurred during search:', error);
|
||||
this.error = "An error occurred during search.";
|
||||
} finally {
|
||||
this.loading = false;
|
||||
this.view.setLoading(false);
|
||||
this.view.renderResults(this.results, this.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Controller
|
||||
|
12
DEV4.1/tp4/ex1/js/helpers.js
Normal file
12
DEV4.1/tp4/ex1/js/helpers.js
Normal file
@@ -0,0 +1,12 @@
|
||||
// Returns a function, that, as long as it continues to be invoked, will not
|
||||
// be triggered. The function will be called after it stops being called for
|
||||
// N milliseconds.
|
||||
function debounce(fn, wait) {
|
||||
let timeout
|
||||
|
||||
return (...args) => {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => fn(...args), wait)
|
||||
}
|
||||
}
|
||||
export default debounce
|
23
DEV4.1/tp4/ex1/js/model.js
Normal file
23
DEV4.1/tp4/ex1/js/model.js
Normal file
@@ -0,0 +1,23 @@
|
||||
let apiKey = 'a776e2df'
|
||||
|
||||
let model = {
|
||||
getMovies(search){
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(`http://www.omdbapi.com/?apikey=${apiKey}&s=${search}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
resolve(data.Search);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('There was a problem with the fetch operation:', error);}
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default model
|
54
DEV4.1/tp4/ex1/js/view.js
Normal file
54
DEV4.1/tp4/ex1/js/view.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import debounce from "./helpers.js"
|
||||
|
||||
class View {
|
||||
constructor(){
|
||||
this.listMovies = document.querySelector("#list-movies")
|
||||
this.inputSearch = document.querySelector("input")
|
||||
this.loader = document.querySelector(".loader")
|
||||
this.message = document.querySelector("p.error")
|
||||
}
|
||||
|
||||
_getInput(){
|
||||
return this.inputSearch.value
|
||||
}
|
||||
|
||||
setLoading(loading){
|
||||
if (loading)
|
||||
this.loader.style.display = "block"
|
||||
else
|
||||
this.loader.style.display = "none"
|
||||
}
|
||||
|
||||
renderMessage(error){
|
||||
this.message.style.display = "block"
|
||||
this.message.textContent = error
|
||||
}
|
||||
|
||||
renderList(movies){
|
||||
let ul = document.createElement("ul")
|
||||
movies.forEach((movie)=>{
|
||||
let li = document.createElement("li")
|
||||
let a = document.createElement("a")
|
||||
let span = document.createElement("span")
|
||||
a.href = `http://www.imdb.com/title/${movie.imdbID}`
|
||||
a.target="_blank"
|
||||
a.textContent = movie.Title
|
||||
span.textContent = movie.Year
|
||||
|
||||
|
||||
li.appendChild(a)
|
||||
li.appendChild(span)
|
||||
ul.appendChild(li)
|
||||
})
|
||||
|
||||
this.listMovies.replaceChildren(ul)
|
||||
}
|
||||
|
||||
bindSearch(handler){
|
||||
this.inputSearch.addEventListener("input",debounce((e)=>{
|
||||
handler(this._getInput())
|
||||
},500))
|
||||
}
|
||||
}
|
||||
|
||||
export default View
|
Reference in New Issue
Block a user