Skip to content

Commit e2a2cab

Browse files
committed
Section 32: App Setup
1 parent cde12d9 commit e2a2cab

7 files changed

Lines changed: 313 additions & 0 deletions

File tree

6 KB
Binary file not shown.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/node_modules
2+
node_modules/
3+
apikey.js
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const createAutoComplete = ({
2+
root,
3+
renderOption,
4+
onOptionSelect,
5+
inputValue,
6+
fetchData
7+
}) => {
8+
root.innerHTML = `
9+
<label><b>Search</b></label>
10+
<input class="input" />
11+
<div class="dropdown">
12+
<div class="dropdown-menu">
13+
<div class="dropdown-content results"></div>
14+
</div>
15+
</div>
16+
`;
17+
18+
const input = root.querySelector('input');
19+
const dropdown = root.querySelector('.dropdown');
20+
const resultsWrapper = root.querySelector('.results');
21+
22+
const onInput = async event => {
23+
const items = await fetchData(event.target.value);
24+
25+
if (!items.length) {
26+
dropdown.classList.remove('is-active');
27+
return;
28+
}
29+
30+
resultsWrapper.innerHTML = '';
31+
dropdown.classList.add('is-active');
32+
for (let item of items) {
33+
const option = document.createElement('a');
34+
35+
option.classList.add('dropdown-item');
36+
option.innerHTML = renderOption(item);
37+
option.addEventListener('click', () => {
38+
dropdown.classList.remove('is-active');
39+
input.value = inputValue(item);
40+
onOptionSelect(item);
41+
});
42+
43+
resultsWrapper.appendChild(option);
44+
}
45+
};
46+
input.addEventListener('input', debounce(onInput, 500));
47+
48+
document.addEventListener('click', event => {
49+
if (!root.contains(event.target)) {
50+
dropdown.classList.remove('is-active');
51+
}
52+
});
53+
};
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>
5+
<link
6+
rel="stylesheet"
7+
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.css"
8+
/>
9+
<link
10+
rel="stylesheet"
11+
href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.css"
12+
/>
13+
<link rel="stylesheet" href="style.css" />
14+
</head>
15+
16+
<body>
17+
<section class="hero is-primary is-bold">
18+
<div class="hero-body">
19+
<div class="container">
20+
<h1 class="title">
21+
Movie Fight
22+
<span class="icon">
23+
<i class="fas fa-film"></i>
24+
</span>
25+
</h1>
26+
</div>
27+
</div>
28+
</section>
29+
<div class="container">
30+
<div class="columns">
31+
<div class="column">
32+
<div id="left-autocomplete"></div>
33+
<div id="left-summary"></div>
34+
</div>
35+
36+
<div class="column">
37+
<div id="right-autocomplete"></div>
38+
<div id="right-summary"></div>
39+
</div>
40+
</div>
41+
42+
<div class="column is-half notification is-primary tutorial">
43+
<h1 class="title">Search For a Movie on Both Sides</h1>
44+
<p class="subtitle">We will tell you which is best!</p>
45+
</div>
46+
</div>
47+
<script src="./apikey.js"></script>
48+
<script src="utils.js"></script>
49+
<script src="autocomplete.js"></script>
50+
<script src="index.js"></script>
51+
</body>
52+
</html>
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
const autoCompleteConfig = {
2+
renderOption(movie) {
3+
const imgSrc = movie.Poster === 'N/A' ? '' : movie.Poster;
4+
return `
5+
<img src="${imgSrc}" />
6+
${movie.Title} (${movie.Year})
7+
`;
8+
},
9+
inputValue(movie) {
10+
return movie.Title;
11+
},
12+
async fetchData(searchTerm) {
13+
const response = await axios.get('http://www.omdbapi.com/', {
14+
params: {
15+
apikey: apikey,
16+
s: searchTerm,
17+
},
18+
})
19+
20+
if (response.data.Error) {
21+
return [];
22+
}
23+
24+
return response.data.Search;
25+
}
26+
};
27+
28+
createAutoComplete({
29+
...autoCompleteConfig,
30+
root: document.querySelector('#left-autocomplete'),
31+
onOptionSelect(movie) {
32+
document.querySelector('.tutorial').classList.add('is-hidden');
33+
onMovieSelect(movie, document.querySelector('#left-summary'), 'left');
34+
}
35+
});
36+
createAutoComplete({
37+
...autoCompleteConfig,
38+
root: document.querySelector('#right-autocomplete'),
39+
onOptionSelect(movie) {
40+
document.querySelector('.tutorial').classList.add('is-hidden');
41+
onMovieSelect(movie, document.querySelector('#right-summary'), 'right');
42+
}
43+
});
44+
45+
let leftMovie;
46+
let rightMovie;
47+
const onMovieSelect = async (movie, summaryElement, side) => {
48+
const response = await axios.get('http://www.omdbapi.com/', {
49+
params: {
50+
apikey: apikey,
51+
i: movie.imdbID,
52+
},
53+
})
54+
55+
summaryElement.innerHTML = movieTemplate(response.data);
56+
57+
if (side === 'left') {
58+
leftMovie = response.data;
59+
} else {
60+
rightMovie = response.data;
61+
}
62+
63+
if (leftMovie && rightMovie) {
64+
runComparison();
65+
}
66+
};
67+
68+
const runComparison = () => {
69+
const leftSideStats = document.querySelectorAll(
70+
'#left-summary .notification'
71+
);
72+
const rightSideStats = document.querySelectorAll(
73+
'#right-summary .notification'
74+
);
75+
76+
leftSideStats.forEach((leftStat, index) => {
77+
const rightStat = rightSideStats[index];
78+
79+
const leftSideValue = leftStat.dataset.value;
80+
const rightSideValue = rightStat.dataset.value;
81+
82+
if (rightSideValue > leftSideValue) {
83+
leftStat.classList.remove('is-primary');
84+
leftStat.classList.add('is-warning');
85+
} else {
86+
rightStat.classList.remove('is-primary');
87+
rightStat.classList.add('is-warning');
88+
}
89+
});
90+
};
91+
92+
const movieTemplate = movieDetail => {
93+
const dollars = parseInt(
94+
movieDetail.BoxOffice.replace(/\$/g, '').replace(/,/g, '')
95+
);
96+
const metascore = parseInt(movieDetail.Metascore);
97+
const imdbRating = parseFloat(movieDetail.imdbRating);
98+
const imdbVotes = parseInt(movieDetail.imdbVotes.replace(/,/g, ''));
99+
const awards = movieDetail.Awards.split(' ').reduce((prev, word) => {
100+
const value = parseInt(word);
101+
102+
if (isNaN(value)) {
103+
return prev;
104+
} else {
105+
return prev + value;
106+
}
107+
}, 0);
108+
109+
return `
110+
<article class="media">
111+
<figure class="media-left">
112+
<p class="image">
113+
<img src="${movieDetail.Poster}" />
114+
</p>
115+
</figure>
116+
<div class="media-content">
117+
<div class="content">
118+
<h1>${movieDetail.Title}</h1>
119+
<h4>${movieDetail.Genre}</h4>
120+
<p>${movieDetail.Plot}</p>
121+
</div>
122+
</div>
123+
</article>
124+
125+
<article data-value=${awards} class="notification is-primary">
126+
<p class="title">${movieDetail.Awards}</p>
127+
<p class="subtitle">Awards</p>
128+
</article>
129+
<article data-value=${dollars} class="notification is-primary">
130+
<p class="title">${movieDetail.BoxOffice}</p>
131+
<p class="subtitle">Box Office</p>
132+
</article>
133+
<article data-value=${metascore} class="notification is-primary">
134+
<p class="title">${movieDetail.Metascore}</p>
135+
<p class="subtitle">Metascore</p>
136+
</article>
137+
<article data-value=${imdbRating} class="notification is-primary">
138+
<p class="title">${movieDetail.imdbRating}</p>
139+
<p class="subtitle">IMDB Rating</p>
140+
</article>
141+
<article data-value=${imdbVotes} class="notification is-primary">
142+
<p class="title">${movieDetail.imdbVotes}</p>
143+
<p class="subtitle">IMDB Votes</p>
144+
</article>
145+
`;
146+
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
.tutorial {
2+
width: 600px;
3+
text-align: center;
4+
margin: auto;
5+
}
6+
7+
figure.image {
8+
display: flex;
9+
justify-content: center;
10+
background-color: black;
11+
}
12+
13+
figure .image img {
14+
width: 128px;
15+
}
16+
17+
.notification {
18+
margin-top: 20px !important;
19+
}
20+
21+
.title .icon {
22+
margin-left: 15px;
23+
}
24+
25+
.hero {
26+
margin-bottom: 20px;
27+
}
28+
29+
.forms {
30+
display: flex;
31+
justify-content: space-around;
32+
}
33+
34+
.results {
35+
max-height: 500px;
36+
overflow-y: scroll;
37+
}
38+
39+
.dropdown-item {
40+
display: flex;
41+
align-items: center;
42+
height: 60px;
43+
}
44+
45+
.dropdown-item img {
46+
height: 50px;
47+
margin-right: 10px;
48+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const debounce = (func, delay = 1000) => {
2+
let timeoutId;
3+
return (...args) => {
4+
if (timeoutId) {
5+
clearTimeout(timeoutId);
6+
}
7+
timeoutId = setTimeout(() => {
8+
func.apply(null, args);
9+
}, delay);
10+
};
11+
};

0 commit comments

Comments
 (0)