-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[2단계 - 상세 정보 & UI/UX 개선하기] 센트(김영우) 미션 제출합니다. #69
Changes from 26 commits
5aadd5e
1cf533a
36ef1e9
a0c5819
e6333f6
2045f52
8d03a92
0ab90a5
dc4a7c6
8c453b7
8208eed
c2c6fe4
5f8fc8e
c10c137
d695e73
6299cf9
1b04be9
8ad3bb1
0133ba0
f5459d4
a167d75
2bcf9ed
c8e1962
65638a3
c194cc6
bb01706
f1ce9f8
44cbce4
54bb3b6
fdab5de
6d76b86
a1b203c
c86d953
118bff8
ef66def
c737c26
34e0c0a
c8517f2
b5f09e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
# javascript-movie-review | ||
|
||
FE 5기 레벨1 영화관 미션 | ||
## [배포 링크](https://kyw0716.github.io/javascript-movie-review/) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,17 @@ | ||
import { Header } from "./components/Header"; | ||
import { Modal } from "./components/Modal"; | ||
import { MovieList } from "./components/MovieList"; | ||
import { $ } from "./utils/selector"; | ||
|
||
export class App { | ||
#header; | ||
#movieList; | ||
#modal; | ||
|
||
constructor() { | ||
const $header = $("header"); | ||
const $movieList = $(".item-list"); | ||
const $modal = $(".modal-content"); | ||
|
||
this.#header = new Header( | ||
$header, | ||
devhyun637 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
@@ -17,6 +20,21 @@ export class App { | |
); | ||
|
||
this.#movieList = new MovieList($movieList); | ||
|
||
this.#modal = new Modal($modal); | ||
|
||
this.bindEvent($movieList); | ||
} | ||
|
||
bindEvent($movieList: Element) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다시 보니 뭔가 메서드명만 보고 해당 메서드가 어떤 역할을 할 지 바로 알아차리기 힘들어 보이네요😭😭 어떻게 바꿔볼까 고민해보다 이벤트 리스너를 추가해주어 이벤트 리스닝(?)을 시작한다는 의미를 전달하기 위해 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 음.. 가장 간단하게라면 저라면 여러가지 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다소 귀찮을 수 있는 질문이었는데 답변 감사합니다!! 👍👍👍 (이 부분은 혹시 여유가 되신다면 답변 부탁드립니다!! 바쁘시다면 굳이 시간 내셔서 답변하지 않으셔도 괜찮습니다!!!) 혹시 티케가 우테코 미션을 진행할 때 이벤트 리스너를 어떻게 추가하셨었는지 궁급합니다. 저는 1레벨 동안 고민해 본 결과 해당 이벤트의 책임을 가지고 있는 컴포넌트 내에서 모든 이벤트를 한번에 추가해주는 것이 좋을 것이라 판단해 |
||
$movieList.addEventListener("click", (event: Event) => { | ||
if (!(event.target instanceof HTMLElement)) return; | ||
|
||
const movieCard = event.target.closest("li"); | ||
const movieId = movieCard?.dataset.movieId; | ||
|
||
if (movieId) this.onClickMovieCard(Number(movieId)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵!! 클릭한 대상이 영화 카드가 아니라면 아무 동작이 되지 않도록 하기 위해 코드를 위와 같이 작성했었습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 사용자는 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 렌더링 된 영화 카드라면 당연히 만약 수정한 커밋: c772c19 |
||
}); | ||
} | ||
|
||
onSubmitSearchKeyword(serachKeyword: string) { | ||
|
@@ -25,14 +43,19 @@ export class App { | |
subTitle.innerHTML = `"${serachKeyword}" 검색 결과`; | ||
|
||
if (this.#movieList instanceof MovieList) | ||
this.#movieList.reset("search", serachKeyword); | ||
this.#movieList.changeShowTarget("search", serachKeyword); | ||
} | ||
|
||
onClickLogoImage() { | ||
const subTitle = $(".sub-title"); | ||
|
||
subTitle.innerHTML = `지금 인기 있는 영화`; | ||
|
||
if (this.#movieList instanceof MovieList) this.#movieList.reset("popular"); | ||
if (this.#movieList instanceof MovieList) | ||
this.#movieList.changeShowTarget("popular"); | ||
} | ||
|
||
onClickMovieCard(movieId: number) { | ||
this.#modal.open("movieDetail", movieId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
header h1 { | ||
cursor: pointer; | ||
user-select: none; | ||
font-size: 2rem; | ||
font-weight: bold; | ||
letter-spacing: -0.1rem; | ||
color: #f33f3f; | ||
} | ||
|
||
header > .search-box { | ||
background: #fff; | ||
padding: 8px; | ||
border-radius: 4px; | ||
} | ||
|
||
header .search-box > input { | ||
border: 0; | ||
} | ||
|
||
header .search-box > .search-button { | ||
width: 14px; | ||
border: 0; | ||
text-indent: -1000rem; | ||
background: url("../../../templates/search_button.png") transparent no-repeat | ||
0 1px; | ||
background-size: contain; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
import "./index.css"; | ||
|
||
import logoImage from "../../../templates/logo.png"; | ||
|
||
import { $ } from "../../utils/selector"; | ||
|
||
export class Header { | ||
|
@@ -12,7 +15,13 @@ export class Header { | |
this.#$target = $target; | ||
|
||
this.render(); | ||
this.bindEvent(onSubmitSearchKeyword, onClickLogoImage); | ||
} | ||
|
||
bindEvent( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사용하다보니 느낀 UX인데, 이건 개인 취향이라서 한번 생각해보시라고 제안합니다. 구글에 우리가 어떤 검색어를 입력하면, 검색창에 검색어를 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Header 컴포넌트를 수정하면서 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
그 후에 다시 생각해본 적이 없었는데 티케가 이야기 해주셔서 다시 보니 검색 결과를 보며 스크롤을 내리면 어떤 검색어를 입력했었는지 다시 확인하기 어렵지 않을까 하는 생각이 들게 되어 수정한 커밋: c8517f2 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
여기서 this.select()는 어떤 역할을 하나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
onSubmitSearchKeyword: (searchKeyword: string) => void, | ||
onClickLogoImage: () => void | ||
) { | ||
$(".search-box").addEventListener("submit", (event: Event) => { | ||
event.preventDefault(); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import type { MovieDetail } from "../../../types"; | ||
|
||
import filledStarImg from "../../../../templates/star_filled.png"; | ||
|
||
import { getStarSelectContainerTemplate } from "./StarSelect"; | ||
|
||
export function getDescriptionTemplate(movie: MovieDetail, starRate: number) { | ||
return /*html*/ ` | ||
<div class="modal-detail-container"> | ||
<div class="modal-movie-detail"> | ||
<p class="modal-movie-genre modal-detail--text"> | ||
${ | ||
movie.genre.length === 0 | ||
? `장르 정보 없음` | ||
: movie.genre.join(" ") | ||
} | ||
<span> | ||
<img | ||
src="${filledStarImg}" | ||
alt="별점 ${movie.vote_average}" | ||
/> | ||
${movie.vote_average.toFixed(1)} | ||
</span> | ||
</p> | ||
<p class="modal-movie-description modal-detail--text"> | ||
${movie.overview ? movie.overview : "상세 정보 없음"} | ||
</p> | ||
</div> | ||
<div class="modal-star-rate modal-detail--text"> | ||
${getStarSelectContainerTemplate(movie.id, starRate)} | ||
</div> | ||
</div> | ||
`; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
export function getImageContainerTemplate(imagePath: string, title: string) { | ||
return /*html*/ ` | ||
<div class="modal-image-container"> | ||
${ | ||
imagePath | ||
? /*html */ | ||
`<img | ||
class="modal-image skeleton" | ||
src="https://image.tmdb.org/t/p/w220_and_h330_face/${imagePath}" | ||
alt="${title} 포스터" | ||
/>` | ||
: /*html */ | ||
`<div | ||
class="modal-image center" | ||
style=" | ||
background-color:white; | ||
color:black; | ||
display:flex; | ||
justify-content:center; | ||
align-items:center; | ||
font-weight:600; | ||
font-size:24px; | ||
border-radius: 16px; | ||
" | ||
> | ||
<span>No Image</span> | ||
</div>` | ||
} | ||
</div> | ||
`; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import filledStarImg from "../../../../templates/star_filled.png"; | ||
import emptyStarImg from "../../../../templates/star_empty.png"; | ||
|
||
import { $ } from "../../../utils/selector"; | ||
|
||
const STAR_RATE_STRING = [ | ||
"나의 점수는?", | ||
"최악이예요", | ||
"별로예요", | ||
"보통이예요", | ||
"재미있어요", | ||
"명작이예요", | ||
]; | ||
|
||
export function renderStars(movieId: number, starRate: number) { | ||
const starRateContainer = $(".modal-star-rate"); | ||
|
||
if (starRateContainer instanceof HTMLElement) | ||
starRateContainer.innerHTML = getStarSelectContainerTemplate( | ||
movieId, | ||
starRate | ||
); | ||
} | ||
|
||
export function getStarSelectContainerTemplate( | ||
movieId: number, | ||
starRate: number | ||
) { | ||
const imgArray = getStarTemplate(movieId, starRate); | ||
|
||
return /*html*/ ` | ||
<span>내 별점</span> | ||
<span class="star-select-container"> | ||
${imgArray.join("")} | ||
</span> | ||
<span>${starRate * 2}점</span> | ||
<span class="star-rate-desc">${STAR_RATE_STRING[starRate]}</span> | ||
`; | ||
} | ||
|
||
export function getStarTemplate(movieId: number, starRate: number) { | ||
return Array.from( | ||
{ length: 5 }, | ||
(_, i) => | ||
`<img | ||
src="${starRate > i ? filledStarImg : emptyStarImg}" | ||
alt="별점" | ||
class="star-rate-select-img" | ||
data-movie-id="${movieId}" | ||
data-star-rate="${i}" | ||
/>` | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'#header' is declared but its value is never read.
라는 오류를 보여주고 있어요!어떤 문제가 있는지 한 번 살펴볼까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
혹시 나중에
#header
를 사용할지도 모른다고 생각해서 그대로 두었는데 결국 사용되지 않아서 생긴 오류인 것 같습니다! 오류를 해결하자고 그냥#header
를 지우기보다#header
가 무언가 하는 역할이 있도록 수정해보는건 어떨까 하고#header
가 입력값을 반환해주는 메서드를 가지도록 수정해보았습니다!수정한 커밋: f1ce9f8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오,, 아래와 바꿔도 상관없을 거라고 생각했는데, 다른 방법을 사용하셨군요~