first commit
@@ -0,0 +1,875 @@
|
||||
|
||||
:root {
|
||||
--background: hsla(220, 17%, 7%, 1);
|
||||
--banner-background: hsla(250, 6%, 20%, 1);
|
||||
--white-alpha-20: hsla(0, 0%, 100%, 0.2);
|
||||
--on-background: hsla(220, 100%, 95%, 1);
|
||||
--on-surface: hsla(250, 100%, 95%, 1);
|
||||
--on-surface-variant: hsla(250, 1%, 44%, 1);
|
||||
--primary: rgb(4, 146, 211);
|
||||
--primary-variant: hsla(349, 69%, 51%, 1);
|
||||
--rating-color: hsla(44, 100%, 49%, 1);
|
||||
--surface: hsla(250, 13%, 11%, 1);
|
||||
--text-color: hsla(250, 2%, 59%, 1);
|
||||
--white: hsla(0, 0%, 100%, 1);
|
||||
--banner-overlay: 90deg, hsl(220, 17%, 7%) 0%, hsla(220, 17%, 7%, 0.5) 100%;
|
||||
--bottom-overlay: 180deg, hsla(250, 13%, 11%, 0), hsla(250, 13%, 11%, 1);
|
||||
--ff-dm-sans: 'DM Sans', sans-serif;
|
||||
--fs-heading: 4rem;
|
||||
--fs-title-lg: 2.6rem;
|
||||
--fs-title: 2rem;
|
||||
--fs-body: 1.8rem;
|
||||
--fs-button: 1.5rem;
|
||||
--fs-label: 1.4rem;
|
||||
--weight-bold: 700;
|
||||
--shadow-1: 0 1px 4px hsla(0, 0%, 0%, 0.75);
|
||||
--shadow-2: 0 2px 4px hsla(202, 100%, 50%, 0.3);
|
||||
--radius-4: 4px;
|
||||
--radius-8: 8px;
|
||||
--radius-16: 16px;
|
||||
--radius-24: 24px;
|
||||
--radius-36: 36px;
|
||||
|
||||
|
||||
|
||||
--transition-short: 250ms ease;
|
||||
--transition-long: 500ms ease;
|
||||
|
||||
}
|
||||
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
li { list-style: none; }
|
||||
|
||||
a,
|
||||
img,
|
||||
span,
|
||||
iframe,
|
||||
button { display: block; }
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
img { height: auto; }
|
||||
|
||||
input,
|
||||
button {
|
||||
background: none;
|
||||
border: none;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input { width: 100%; }
|
||||
|
||||
button {
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: var(--ff-dm-sans);
|
||||
font-size: 10px;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--background);
|
||||
color: var(--on-background);
|
||||
font-size: var(--fs-body);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
:focus-visible { outline-color: var(--primary-variant); }
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--banner-background);
|
||||
border-radius: var(--radius-8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.search-wrapper::before,
|
||||
.load-more::before {
|
||||
content: "";
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid var(--white);
|
||||
border-radius: var(--radius-24);
|
||||
border-inline-end-color: transparent;
|
||||
animation: loading 500ms linear infinite;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-wrapper::before {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
.search-wrapper.searching::before { display: block; }
|
||||
|
||||
.load-more {
|
||||
background-color: var(--primary-variant);
|
||||
margin-inline: auto;
|
||||
margin-block: 36px 60px;
|
||||
}
|
||||
|
||||
.load-more:is(:hover, :focus-visible) {
|
||||
--primary-variant: hsla(350, 67%, 39%, 1);
|
||||
}
|
||||
|
||||
.load-more.loading::before { display: block; }
|
||||
|
||||
@keyframes loading {
|
||||
0% { transform: rotate(0); }
|
||||
100% { transform: rotate(1turn); }
|
||||
}
|
||||
|
||||
.heading,
|
||||
.title-large,
|
||||
.title {
|
||||
font-weight: var(--weight-bold);
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.title { font-size: var(--fs-title); }
|
||||
|
||||
.heading {
|
||||
color: var(--white);
|
||||
font-size: var(--fs-heading);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.title-large { font-size: var(--fs-title-lg); }
|
||||
|
||||
.img-cover {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.meta-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
color: var(--white);
|
||||
font-size: var(--fs-button);
|
||||
font-weight: var(--weight-bold);
|
||||
max-width: max-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding-block: 12px;
|
||||
padding-inline: 20px 24px;
|
||||
border-radius: var(--radius-8);
|
||||
transition: var(--transition-short);
|
||||
}
|
||||
|
||||
.card-badge {
|
||||
background-color: var(--banner-background);
|
||||
color: var(--white);
|
||||
font-size: var(--fs-label);
|
||||
font-weight: var(--weight-bold);
|
||||
padding-inline: 6px;
|
||||
border-radius: var(--radius-4);
|
||||
}
|
||||
|
||||
.poster-box {
|
||||
background-image: url('../images/poster-bg-icon.png');
|
||||
aspect-ratio: 2 / 3;
|
||||
}
|
||||
|
||||
.poster-box,
|
||||
.video-card {
|
||||
background-repeat: no-repeat;
|
||||
background-size: 50px;
|
||||
background-position: center;
|
||||
background-color: var(--banner-background);
|
||||
border-radius: var(--radius-16);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.title-wrapper { margin-block-end: 24px; }
|
||||
|
||||
.slider-list {
|
||||
margin-inline: -20px;
|
||||
overflow-x: overlay;
|
||||
padding-block-end: 16px;
|
||||
margin-block-end: -16px;
|
||||
}
|
||||
|
||||
.slider-list::-webkit-scrollbar-thumb { background-color: transparent; }
|
||||
|
||||
.slider-list:is(:hover, :focus-within)::-webkit-scrollbar-thumb {
|
||||
background-color: var(--banner-background);
|
||||
}
|
||||
|
||||
.slider-list::-webkit-scrollbar-button { width: 20px; }
|
||||
|
||||
.slider-list .slider-inner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.slider-list .slider-inner::before,
|
||||
.slider-list .slider-inner::after {
|
||||
content: "";
|
||||
min-width: 4px;
|
||||
}
|
||||
|
||||
.separator {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background-color: var(--white-alpha-20);
|
||||
border-radius: var(--radius-8);
|
||||
}
|
||||
|
||||
.video-card {
|
||||
background-image: url('../images/video-bg-icon.png');
|
||||
aspect-ratio: 16 / 9;
|
||||
flex-shrink: 0;
|
||||
max-width: 500px;
|
||||
width: calc(100% - 40px);
|
||||
}
|
||||
|
||||
/* .container::after, */
|
||||
.search-modal::after {
|
||||
content: "";
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
background-image: linear-gradient(var(--bottom-overlay));
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.header {
|
||||
position: relative;
|
||||
padding-block: 24px;
|
||||
padding-inline: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.header .logo { margin-inline-end: auto; }
|
||||
|
||||
.search-btn,
|
||||
.menu-btn { padding: 12px; }
|
||||
|
||||
.search-btn {
|
||||
background-color: var(--banner-background);
|
||||
border-radius: var(--radius-8);
|
||||
}
|
||||
|
||||
.search-btn img {
|
||||
opacity: 0.5;
|
||||
transition: var(--transition-short);
|
||||
}
|
||||
|
||||
.search-btn:is(:hover, :focus-visible) img { opacity: 1; }
|
||||
|
||||
.menu-btn.active .menu,
|
||||
.menu-btn .close { display: none; }
|
||||
|
||||
.menu-btn .menu,
|
||||
.menu-btn.active .close { display: block; }
|
||||
|
||||
.search-box {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--background);
|
||||
padding: 24px 16px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
z-index: 1;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-box.active { display: flex; }
|
||||
|
||||
.search-wrapper {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.search-field {
|
||||
background-color: var(--banner-background);
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
padding-inline: 44px 16px;
|
||||
outline: none;
|
||||
border-radius: var(--radius-8);
|
||||
transition: var(--transition-short);
|
||||
}
|
||||
|
||||
.search-field::placeholder { color: var(--on-surface-variant); }
|
||||
|
||||
.search-field:hover { box-shadow: 0 0 0 2px var(--on-surface-variant); }
|
||||
|
||||
.search-field:focus {
|
||||
box-shadow: 0 0 0 2px var(--on-surface);
|
||||
padding-inline-start: 16px;
|
||||
}
|
||||
|
||||
.search-wrapper .leading-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
left: 12px;
|
||||
opacity: 0.5;
|
||||
transition: var(--transition-short);
|
||||
}
|
||||
|
||||
.search-wrapper:focus-within .leading-icon { opacity: 0; }
|
||||
|
||||
|
||||
.sidebar {
|
||||
position: absolute;
|
||||
background-color: var(--background);
|
||||
top: 96px;
|
||||
bottom: 0;
|
||||
left: -340px;
|
||||
max-width: 340px;
|
||||
width: 100%;
|
||||
border-block-start: 1px solid var(--banner-background);
|
||||
overflow-y: overlay;
|
||||
z-index: 4;
|
||||
visibility: hidden;
|
||||
transition: var(--transition-long);
|
||||
}
|
||||
|
||||
.sidebar.active {
|
||||
transform: translateX(340px);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.sidebar-inner {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
padding-block: 36px;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar-thumb { background-color: transparent; }
|
||||
|
||||
.sidebar:is(:hover, :focus-within)::-webkit-scrollbar-thumb {
|
||||
background-color: var(--banner-background);
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar-button { height: 16px; }
|
||||
|
||||
.sidebar-list,
|
||||
.sidebar-footer { padding-inline: 36px; }
|
||||
|
||||
.sidebar-link {
|
||||
color: var(--on-surface-variant);
|
||||
transition: var(--transition-short);
|
||||
}
|
||||
|
||||
.sidebar-link:is(:hover, :focus-visible) { color: var(--on-background); }
|
||||
|
||||
.sidebar-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.sidebar-list .title { margin-block-end: 8px; }
|
||||
|
||||
.sidebar-footer {
|
||||
border-block-start: 1px solid var(--banner-background);
|
||||
padding-block-start: 28px;
|
||||
margin-block-start: 16px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
color: white;
|
||||
margin-block-end: 20px;
|
||||
}
|
||||
|
||||
.copyright a { display: inline-block; }
|
||||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
top: 96px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background: var(--background);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: var(--transition-short);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.overlay.active {
|
||||
opacity: 0.5;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------*\
|
||||
#HOME PAGE
|
||||
\*-----------------------------------*/
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
background-color: var(--surface);
|
||||
color: var(--on-surface);
|
||||
padding: 24px 20px 48px;
|
||||
height: calc(100vh - 96px);
|
||||
overflow-y: overlay;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* BANNER
|
||||
*/
|
||||
|
||||
.banner {
|
||||
position: relative;
|
||||
height: 700px;
|
||||
border-radius: var(--radius-24);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.banner-slider .slider-item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 120%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--banner-background);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity var(--transition-long);
|
||||
}
|
||||
|
||||
.banner-slider .slider-item::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: linear-gradient(var(--banner-overlay));
|
||||
}
|
||||
|
||||
.banner-slider .active {
|
||||
left: 0;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.banner-content {
|
||||
position: absolute;
|
||||
left: 24px;
|
||||
right: 24px;
|
||||
bottom: 206px;
|
||||
z-index: 1;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.banner :is(.heading, .banner-text) {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.banner .heading {
|
||||
-webkit-line-clamp: 3;
|
||||
margin-block-end: 16px;
|
||||
}
|
||||
|
||||
.banner .genre { margin-block: 12px; }
|
||||
|
||||
.banner-text {
|
||||
-webkit-line-clamp: 2;
|
||||
margin-block-end: 24px;
|
||||
}
|
||||
|
||||
.banner .btn { background-color: var(--primary); }
|
||||
|
||||
.banner .btn:is(:hover, :focus-visible) { box-shadow: var(--shadow-2); }
|
||||
|
||||
.slider-control {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
right: 0;
|
||||
border-radius: var(--radius-16) 0 0 var(--radius-16);
|
||||
user-select: none;
|
||||
padding: 4px 0 4px 4px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.slider-control::-webkit-scrollbar { display: none; }
|
||||
|
||||
.control-inner {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.control-inner::after {
|
||||
content: "";
|
||||
min-width: 12px;
|
||||
}
|
||||
|
||||
.slider-control .slider-item {
|
||||
width: 100px;
|
||||
border-radius: var(--radius-8);
|
||||
flex-shrink: 0;
|
||||
filter: brightness(0.4);
|
||||
}
|
||||
|
||||
.slider-control .active {
|
||||
filter: brightness(1);
|
||||
box-shadow: var(--shadow-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* MOVIE LIST
|
||||
*/
|
||||
|
||||
.movie-list { padding-block-start: 32px; }
|
||||
|
||||
.movie-card {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.movie-card .card-banner { width: 200px; }
|
||||
|
||||
.movie-card .title {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
margin-block: 8px 4px;
|
||||
}
|
||||
|
||||
.movie-card .meta-list { justify-content: space-between; }
|
||||
|
||||
.movie-card .card-btn {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------*\
|
||||
#DETAIL PAGE
|
||||
\*-----------------------------------*/
|
||||
|
||||
.backdrop-image {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.backdrop-image::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: linear-gradient(0deg, hsla(250, 13%, 11%, 1), hsla(250, 13%, 11%, 0.9));
|
||||
}
|
||||
|
||||
.movie-detail .movie-poster {
|
||||
max-width: 300px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.movie-detail .heading { margin-block: 24px 12px; }
|
||||
|
||||
.movie-detail :is(.meta-list, .genre) { color: var(--text-color); }
|
||||
|
||||
.movie-detail .genre { margin-block: 12px 16px; }
|
||||
|
||||
.detail-list { margin-block: 24px 32px; }
|
||||
|
||||
.movie-detail .list-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
margin-block-end: 12px;
|
||||
}
|
||||
|
||||
.movie-detail .list-name {
|
||||
color: var(--text-color);
|
||||
min-width: 112px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------*\
|
||||
#MOVIE LIST PAGE
|
||||
\*-----------------------------------*/
|
||||
|
||||
.genre-list .title-wrapper { margin-block-end: 56px; }
|
||||
|
||||
.grid-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
column-gap: 16px;
|
||||
row-gap: 20px;
|
||||
}
|
||||
|
||||
:is(.genre-list, .search-modal) :is(.movie-card, .card-banner) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------*\
|
||||
#SEARCH MODAL
|
||||
\*-----------------------------------*/
|
||||
|
||||
.search-modal {
|
||||
position: fixed;
|
||||
top: 96px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: var(--surface);
|
||||
padding: 50px 24px;
|
||||
overflow-y: overlay;
|
||||
z-index: 4;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-modal.active { display: block; }
|
||||
|
||||
.search-modal .label {
|
||||
color: var(--primary-variant);
|
||||
font-weight: var(--weight-bold);
|
||||
margin-block-end: 8px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------*\
|
||||
#MEDIA QUERIES
|
||||
\*-----------------------------------*/
|
||||
|
||||
|
||||
/**
|
||||
* responsive for larger than 575px screen
|
||||
*/
|
||||
|
||||
@media (min-width: 575px) {
|
||||
|
||||
/**
|
||||
* HOME PAGE
|
||||
*/
|
||||
|
||||
.banner-content {
|
||||
right: auto;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.slider-control { left: calc(100% - 400px); }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* DETAIL PAGE
|
||||
*/
|
||||
|
||||
.detail-content { max-width: 750px; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* responsive for larger than 768px screen
|
||||
*/
|
||||
|
||||
@media (min-width: 768px) {
|
||||
|
||||
/**
|
||||
* CUSTOM PROPERTY
|
||||
*/
|
||||
|
||||
:root {
|
||||
|
||||
/* gradient color */
|
||||
--banner-overlay: 90deg, hsl(220, 17%, 7%) 0%, hsla(220, 17%, 7%, 0) 100%;
|
||||
|
||||
/* font size */
|
||||
--fs-heading: 5.4rem;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* HOME PAGE
|
||||
*/
|
||||
|
||||
.container { padding-inline: 24px; }
|
||||
|
||||
.slider-list { margin-inline: -24px; }
|
||||
|
||||
.search-btn { display: none; }
|
||||
|
||||
.search-box {
|
||||
all: unset;
|
||||
display: block;
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.banner { height: 500px; }
|
||||
|
||||
.banner-content {
|
||||
bottom: 50%;
|
||||
transform: translateY(50%);
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* MOVIE DETAIL PAGE
|
||||
*/
|
||||
|
||||
.movie-detail {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.movie-detail .detail-box { flex-grow: 1; }
|
||||
|
||||
.movie-detail .movie-poster {
|
||||
flex-shrink: 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.movie-detail .slider-list {
|
||||
margin-inline-start: 0;
|
||||
border-radius: var(--radius-16) 0 0 var(--radius-16);
|
||||
}
|
||||
|
||||
.movie-detail .slider-inner::before { display: none; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* responsive for larger than 1200px screen
|
||||
*/
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
|
||||
/**
|
||||
* HOME PAGE
|
||||
*/
|
||||
|
||||
.header { padding: 28px 56px; }
|
||||
|
||||
.logo img { width: 160px; }
|
||||
|
||||
.menu-btn,
|
||||
.overlay { display: none; }
|
||||
|
||||
main {
|
||||
display: grid;
|
||||
grid-template-columns: 250px 1fr;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: static;
|
||||
visibility: visible;
|
||||
border-block-start: 0;
|
||||
height: calc(100vh - 104px);
|
||||
}
|
||||
|
||||
.sidebar.active { transform: none; }
|
||||
|
||||
.sidebar-list,
|
||||
.sidebar-footer { padding-inline: 56px; }
|
||||
|
||||
.container {
|
||||
height: calc(100vh - 104px);
|
||||
border-top-left-radius: var(--radius-36);
|
||||
}
|
||||
|
||||
.banner-content { left: 100px; }
|
||||
|
||||
.search-modal {
|
||||
top: 104px;
|
||||
padding: 60px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* MOVIE DETAIL PAGE
|
||||
*/
|
||||
|
||||
.backdrop-image { border-top-left-radius: var(--radius-36); }
|
||||
|
||||
}
|
||||
|
After Width: | Height: | Size: 366 B |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 397 B |
|
After Width: | Height: | Size: 355 B |
|
After Width: | Height: | Size: 823 B |
|
After Width: | Height: | Size: 825 B |
|
After Width: | Height: | Size: 569 B |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 925 KiB |
|
After Width: | Height: | Size: 605 B |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg width="130" height="17" viewBox="0 0 130 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_88_1285)">
|
||||
<path d="M91.2168 16.817H121.599C122.702 16.817 123.794 16.5997 124.814 16.1775C125.833 15.7553 126.759 15.1364 127.539 14.3563C128.319 13.5761 128.938 12.65 129.36 11.6307C129.783 10.6114 130 9.51891 130 8.41562C130.002 7.31114 129.786 6.21713 129.365 5.19618C128.943 4.17524 128.325 3.24739 127.544 2.46574C126.764 1.68409 125.837 1.06398 124.817 0.640877C123.797 0.217778 122.703 -1.59183e-06 121.599 0L91.2168 0C90.1123 -1.59183e-06 89.0187 0.217778 87.9985 0.640877C86.9782 1.06398 86.0514 1.68409 85.2711 2.46574C84.4908 3.24739 83.8722 4.17524 83.4509 5.19618C83.0295 6.21713 82.8136 7.31114 82.8154 8.41562C82.8154 10.6438 83.7006 12.7807 85.2761 14.3563C86.8517 15.9318 88.9886 16.817 91.2168 16.817ZM4.80214 16.8408H8.51072V3.29018H13.3129V0H0V3.28067H4.80214V16.8408ZM18.1625 16.8408H21.8711V3.92254H21.9187L26.174 16.8312H29.0268L33.4248 3.92254H33.4723V16.8312H37.1809V0H31.5942L27.6955 10.9831H27.6479L23.7729 0H18.1625V16.8408ZM42.3824 0.0570551H47.9453C49.2404 0.0590456 50.5305 0.218707 51.787 0.532514C52.9313 0.803976 54.0096 1.30191 54.9583 1.99693C55.8759 2.68686 56.6145 3.5869 57.1121 4.62146C57.6747 5.85059 57.9464 7.19284 57.9062 8.544C57.9319 9.78876 57.6682 11.0225 57.1359 12.148C56.6402 13.1608 55.9295 14.0532 55.0534 14.763C54.1566 15.482 53.1323 16.0256 52.0342 16.3653C50.8735 16.7338 49.6623 16.9183 48.4445 16.9121H42.3824V0.0570551ZM46.091 13.465H47.9928C48.7927 13.4705 49.5905 13.3827 50.3701 13.2035C51.0657 13.0623 51.7236 12.7755 52.3005 12.3619C52.8511 11.9453 53.2891 11.3978 53.5747 10.7691C53.9086 10.0087 54.0709 9.18406 54.0502 8.35381C54.0643 7.60989 53.9013 6.87328 53.5747 6.20474C53.2782 5.61229 52.8505 5.09521 52.3243 4.69278C51.7773 4.28658 51.1599 3.98513 50.5033 3.80367C49.779 3.60116 49.0301 3.50035 48.2781 3.50413H46.091V13.465ZM63.2788 0.0570551H69.5549C70.2915 0.0574142 71.0271 0.109845 71.7563 0.213957C72.448 0.303562 73.1188 0.512707 73.7389 0.832053C74.3182 1.13261 74.8096 1.57818 75.1653 2.1253C75.5598 2.79294 75.7499 3.56161 75.7121 4.33619C75.7447 5.19872 75.4625 6.04366 74.9181 6.71348C74.3624 7.35704 73.63 7.82326 72.8118 8.05428V8.0828C73.3383 8.15797 73.849 8.31857 74.3238 8.55826C74.7622 8.77842 75.1569 9.07647 75.4886 9.43786C75.8172 9.79977 76.0709 10.2231 76.2351 10.6836C76.4122 11.1711 76.5007 11.6863 76.4966 12.205C76.5251 12.9863 76.327 13.7591 75.926 14.4302C75.553 15.0144 75.0482 15.5029 74.4521 15.8566C73.8255 16.2327 73.1387 16.4981 72.4219 16.6411C71.6828 16.7995 70.9289 16.8792 70.173 16.8788H63.2788V0.0570551ZM66.9874 6.7848H69.6738C69.9586 6.78635 70.2427 6.75444 70.5201 6.68971C70.7854 6.6321 71.0382 6.52743 71.2665 6.38066C71.4893 6.23697 71.6738 6.04114 71.8038 5.81011C71.9431 5.54684 72.0119 5.25201 72.0035 4.95428C72.0188 4.65169 71.9461 4.35114 71.7943 4.08895C71.6468 3.86428 71.4458 3.67968 71.2095 3.55168C70.9446 3.41865 70.6607 3.32724 70.3679 3.28067C70.0781 3.22462 69.7836 3.19596 69.4883 3.19508H66.9684L66.9874 6.7848ZM66.9874 13.7503H70.3156C70.6083 13.7511 70.9001 13.7192 71.1857 13.6552C71.4696 13.5971 71.7393 13.484 71.9797 13.3224C72.2196 13.164 72.4197 12.9525 72.5645 12.7043C72.7207 12.4208 72.7979 12.1006 72.788 11.7771C72.8007 11.4359 72.6967 11.1005 72.4932 10.8262C72.2936 10.5794 72.0353 10.3865 71.742 10.2652C71.4373 10.1382 71.1178 10.0504 70.7911 10.0037C70.4682 9.9578 70.1425 9.93397 69.8164 9.93234H67.0112L66.9874 13.7503Z" fill="url(#paint0_linear_88_1285)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_88_1285" x1="0" y1="8.44415" x2="130" y2="8.44415" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#90CEA1"/>
|
||||
<stop offset="0.56" stop-color="#3CBEC9"/>
|
||||
<stop offset="1" stop-color="#00B3E5"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_88_1285">
|
||||
<rect width="130" height="16.8883" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 718 B |
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const api_key = '054582e9ee66adcbe911e0008aa482a8';
|
||||
const imageBaseURL = 'https://image.tmdb.org/t/p/';
|
||||
|
||||
const fetchDataFromServer = function (url, callback, optionalParam) {
|
||||
fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(data => callback(data, optionalParam));
|
||||
}
|
||||
|
||||
export { imageBaseURL, api_key, fetchDataFromServer };
|
||||
@@ -0,0 +1,179 @@
|
||||
'use strict';
|
||||
|
||||
import { api_key, imageBaseURL, fetchDataFromServer } from "./api.js";
|
||||
import { sidebar } from "./sidebar.js";
|
||||
import { createMovieCard } from "./movie-card.js";
|
||||
import { search } from "./search.js";
|
||||
|
||||
|
||||
const movieId = window.localStorage.getItem("movieId");
|
||||
const pageContent = document.querySelector("[page-content]");
|
||||
|
||||
|
||||
|
||||
sidebar();
|
||||
|
||||
|
||||
|
||||
const getGenres = function (genreList) {
|
||||
const newGenreList = [];
|
||||
|
||||
for (const { name } of genreList) newGenreList.push(name);
|
||||
|
||||
return newGenreList.join(", ");
|
||||
}
|
||||
|
||||
const getCasts = function (castList) {
|
||||
const newCastList = [];
|
||||
|
||||
for (let i = 0, len = castList.length; i < len && i < 10; i++) {
|
||||
const { name } = castList[i];
|
||||
newCastList.push(name);
|
||||
}
|
||||
|
||||
return newCastList.join(", ");
|
||||
}
|
||||
|
||||
const getDirectors = function (crewList) {
|
||||
const directors = crewList.filter(({ job }) => job === "Director");
|
||||
|
||||
const directorList = [];
|
||||
for (const { name } of directors) directorList.push(name);
|
||||
|
||||
return directorList.join(", ");
|
||||
}
|
||||
|
||||
const filterVideos = function (videoList) {
|
||||
return videoList.filter(({ type, site }) => (type === "Trailer" || type === "Teaser") && site === "YouTube");
|
||||
}
|
||||
|
||||
fetchDataFromServer(`https://api.themoviedb.org/3/movie/${movieId}?api_key=${api_key}&append_to_response=casts,videos,images,releases`, function (movie) {
|
||||
|
||||
const {
|
||||
backdrop_path,
|
||||
poster_path,
|
||||
title,
|
||||
release_date,
|
||||
runtime,
|
||||
vote_average,
|
||||
releases: { countries: [{ certification } = { certification: "N/A" }] },
|
||||
genres,
|
||||
overview,
|
||||
casts: { cast, crew },
|
||||
videos: { results: videos }
|
||||
} = movie;
|
||||
|
||||
document.title = `${title} - TD`;
|
||||
|
||||
const movieDetail = document.createElement("div");
|
||||
movieDetail.classList.add("movie-detail");
|
||||
|
||||
movieDetail.innerHTML = `
|
||||
<div class="backdrop-image" style="background-image: url('${imageBaseURL}${"w1280" || "original"}${backdrop_path || poster_path}')"></div>
|
||||
|
||||
<figure class="poster-box movie-poster">
|
||||
<img src="${imageBaseURL}w342${poster_path}" alt="${title} poster" class="img-cover">
|
||||
</figure>
|
||||
|
||||
<div class="detail-box">
|
||||
|
||||
<div class="detail-content">
|
||||
<h1 class="heading">${title}</h1>
|
||||
|
||||
<div class="meta-list">
|
||||
|
||||
<div class="meta-item">
|
||||
<img src="./assets/images/star.png" width="20" height="20" alt="rating">
|
||||
|
||||
<span class="span">${vote_average.toFixed(1)}</span>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="meta-item">${runtime}m</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<div class="meta-item">${release_date?.split("-")[0] ?? "Not Released"}</div>
|
||||
|
||||
<div class="meta-item card-badge">${certification}</div>
|
||||
|
||||
</div>
|
||||
|
||||
<p class="genre">${getGenres(genres)}</p>
|
||||
|
||||
<p class="overview">${overview}</p>
|
||||
|
||||
<ul class="detail-list">
|
||||
|
||||
<div class="list-item">
|
||||
<p class="list-name">Starring</p>
|
||||
|
||||
<p>${getCasts(cast)}</p>
|
||||
</div>
|
||||
|
||||
<div class="list-item">
|
||||
<p class="list-name">Directed By</p>
|
||||
|
||||
<p>${getDirectors(crew)}</p>
|
||||
</div>
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="title-wrapper">
|
||||
<h3 class="title-large">Trailers and Clips</h3>
|
||||
</div>
|
||||
|
||||
<div class="slider-list">
|
||||
<div class="slider-inner"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`;
|
||||
|
||||
for (const { key, name } of filterVideos(videos)) {
|
||||
const videoCard = document.createElement("div");
|
||||
videoCard.classList.add("video-card");
|
||||
|
||||
videoCard.innerHTML = `
|
||||
<iframe width="500" height="294" src="https://www.youtube.com/embed/${key}?&theme=dark&color=white&rel=0"
|
||||
frameborder="0" allowfullscreen="1" title="${name}" class="img-cover" loading="lazy"></iframe>
|
||||
`;
|
||||
|
||||
movieDetail.querySelector(".slider-inner").appendChild(videoCard);
|
||||
}
|
||||
|
||||
pageContent.appendChild(movieDetail);
|
||||
|
||||
fetchDataFromServer(`https://api.themoviedb.org/3/movie/${movieId}/recommendations?api_key=${api_key}&page=1`, addSuggestedMovies);
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
const addSuggestedMovies = function ({ results: movieList }, title) {
|
||||
|
||||
const movieListElem = document.createElement("section");
|
||||
movieListElem.classList.add("movie-list");
|
||||
movieListElem.ariaLabel = "You May Also Like";
|
||||
|
||||
movieListElem.innerHTML = `
|
||||
|
||||
|
||||
`;
|
||||
|
||||
for (const movie of movieList) {
|
||||
const movieCard = createMovieCard(movie);
|
||||
|
||||
movieListElem.querySelector(".slider-inner").appendChild(movieCard);
|
||||
}
|
||||
|
||||
pageContent.appendChild(movieListElem);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
search();
|
||||
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
|
||||
const addEventOnElements = function (elements, eventType, callback) {
|
||||
for (const elem of elements) elem.addEventListener(eventType, callback);
|
||||
}
|
||||
|
||||
|
||||
const searchBox = document.querySelector("[search-box]");
|
||||
const searchTogglers = document.querySelectorAll("[search-toggler]");
|
||||
|
||||
addEventOnElements(searchTogglers, "click", function () {
|
||||
searchBox.classList.toggle("active");
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
const getMovieDetail = function (movieId) {
|
||||
window.localStorage.setItem("movieId", String(movieId));
|
||||
}
|
||||
|
||||
|
||||
|
||||
const getMovieList = function (urlParam, genreName) {
|
||||
window.localStorage.setItem("urlParam", urlParam);
|
||||
window.localStorage.setItem("genreName", genreName);
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
import { sidebar } from "./sidebar.js";
|
||||
import { api_key, imageBaseURL, fetchDataFromServer } from "./api.js";
|
||||
import { createMovieCard } from "./movie-card.js";
|
||||
import { search } from "./search.js";
|
||||
|
||||
const pageContent = document.querySelector("[page-content]");
|
||||
|
||||
|
||||
|
||||
sidebar();
|
||||
|
||||
|
||||
|
||||
|
||||
const homePageSections = [
|
||||
{
|
||||
title: "Upcoming Movies",
|
||||
path: "/movie/upcoming"
|
||||
},
|
||||
{
|
||||
title: "Weekly Trending Movies",
|
||||
path: "/trending/movie/week"
|
||||
},
|
||||
{
|
||||
title: "Top Rated Movies",
|
||||
path: "/movie/top_rated"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
const genreList = {
|
||||
|
||||
|
||||
asString(genreIdList) {
|
||||
let newGenreList = [];
|
||||
|
||||
for (const genreId of genreIdList) {
|
||||
this[genreId] && newGenreList.push(this[genreId]);
|
||||
}
|
||||
|
||||
return newGenreList.join(", ");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
fetchDataFromServer(`https://api.themoviedb.org/3/genre/movie/list?api_key=${api_key}`, function ({ genres }) {
|
||||
for (const { id, name } of genres) {
|
||||
genreList[id] = name;
|
||||
}
|
||||
|
||||
fetchDataFromServer(`https://api.themoviedb.org/3/movie/popular?api_key=${api_key}&page=1`, heroBanner);
|
||||
});
|
||||
|
||||
|
||||
|
||||
const heroBanner = function ({ results: movieList }) {
|
||||
|
||||
const banner = document.createElement("section");
|
||||
banner.classList.add("banner");
|
||||
banner.ariaLabel = "Popular Movies";
|
||||
|
||||
banner.innerHTML = `
|
||||
<div class="banner-slider"></div>
|
||||
|
||||
<div class="slider-control">
|
||||
<div class="control-inner"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
let controlItemIndex = 0;
|
||||
|
||||
for (const [index, movie] of movieList.entries()) {
|
||||
|
||||
const {
|
||||
backdrop_path,
|
||||
title,
|
||||
release_date,
|
||||
genre_ids,
|
||||
overview,
|
||||
poster_path,
|
||||
vote_average,
|
||||
id
|
||||
} = movie;
|
||||
|
||||
const sliderItem = document.createElement("div");
|
||||
sliderItem.classList.add("slider-item");
|
||||
sliderItem.setAttribute("slider-item", "");
|
||||
|
||||
sliderItem.innerHTML = `
|
||||
<img src="${imageBaseURL}w1280${backdrop_path}" alt="${title}" class="img-cover" loading=${index === 0 ? "eager" : "lazy"
|
||||
}>
|
||||
|
||||
<div class="banner-content">
|
||||
|
||||
<h2 class="heading">${title}</h2>
|
||||
|
||||
<div class="meta-list">
|
||||
<div class="meta-item">${release_date?.split("-")[0] ?? "Not Released"}</div>
|
||||
|
||||
<div class="meta-item card-badge">${vote_average.toFixed(1)}</div>
|
||||
</div>
|
||||
|
||||
<p class="genre">${genreList.asString(genre_ids)}</p>
|
||||
|
||||
<p class="banner-text">${overview}</p>
|
||||
|
||||
<a href="./detail.html" class="btn" onclick="getMovieDetail(${id})">
|
||||
<img src="./assets/images/play_circle.png" width="24" height="24" aria-hidden="true" alt="play circle">
|
||||
|
||||
<span class="span">Watch Now</span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
`;
|
||||
banner.querySelector(".banner-slider").appendChild(sliderItem);
|
||||
|
||||
|
||||
const controlItem = document.createElement("button");
|
||||
controlItem.classList.add("poster-box", "slider-item");
|
||||
controlItem.setAttribute("slider-control", `${controlItemIndex}`);
|
||||
|
||||
controlItemIndex++;
|
||||
|
||||
controlItem.innerHTML = `
|
||||
<img src="${imageBaseURL}w154${poster_path}" alt="Slide to ${title}" loading="lazy" draggable="false" class="img-cover">
|
||||
`;
|
||||
banner.querySelector(".control-inner").appendChild(controlItem);
|
||||
|
||||
}
|
||||
|
||||
pageContent.appendChild(banner);
|
||||
|
||||
addHeroSlide();
|
||||
|
||||
|
||||
for (const { title, path } of homePageSections) {
|
||||
fetchDataFromServer(`https://api.themoviedb.org/3${path}?api_key=${api_key}&page=1`, createMovieList, title);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const addHeroSlide = function () {
|
||||
|
||||
const sliderItems = document.querySelectorAll("[slider-item]");
|
||||
const sliderControls = document.querySelectorAll("[slider-control]");
|
||||
|
||||
let lastSliderItem = sliderItems[0];
|
||||
let lastSliderControl = sliderControls[0];
|
||||
|
||||
lastSliderItem.classList.add("active");
|
||||
lastSliderControl.classList.add("active");
|
||||
|
||||
const sliderStart = function () {
|
||||
lastSliderItem.classList.remove("active");
|
||||
lastSliderControl.classList.remove("active");
|
||||
|
||||
// `this` == slider-control
|
||||
sliderItems[Number(this.getAttribute("slider-control"))].classList.add("active");
|
||||
this.classList.add("active");
|
||||
|
||||
lastSliderItem = sliderItems[Number(this.getAttribute("slider-control"))];
|
||||
lastSliderControl = this;
|
||||
}
|
||||
|
||||
addEventOnElements(sliderControls, "click", sliderStart);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const createMovieList = function ({ results: movieList }, title) {
|
||||
|
||||
const movieListElem = document.createElement("section");
|
||||
movieListElem.classList.add("movie-list");
|
||||
movieListElem.ariaLabel = `${title}`;
|
||||
|
||||
movieListElem.innerHTML = `
|
||||
<div class="title-wrapper">
|
||||
<h3 class="title-large">${title}</h3>
|
||||
</div>
|
||||
|
||||
<div class="slider-list">
|
||||
<div class="slider-inner"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
for (const movie of movieList) {
|
||||
const movieCard = createMovieCard(movie); // called from movie_card.js
|
||||
|
||||
movieListElem.querySelector(".slider-inner").appendChild(movieCard);
|
||||
}
|
||||
|
||||
pageContent.appendChild(movieListElem);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
search();
|
||||
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
|
||||
import { imageBaseURL } from "./api.js";
|
||||
|
||||
|
||||
/**
|
||||
* movie card
|
||||
*/
|
||||
|
||||
export function createMovieCard(movie) {
|
||||
|
||||
const {
|
||||
poster_path,
|
||||
title,
|
||||
vote_average,
|
||||
release_date,
|
||||
id
|
||||
} = movie;
|
||||
|
||||
const card = document.createElement("div");
|
||||
card.classList.add("movie-card");
|
||||
|
||||
card.innerHTML = `
|
||||
<figure class="poster-box card-banner">
|
||||
<img src="${imageBaseURL}w342${poster_path}" alt="${title}" class="img-cover" loading="lazy">
|
||||
</figure>
|
||||
|
||||
<h4 class="title">${title}</h4>
|
||||
|
||||
<div class="meta-list">
|
||||
<div class="meta-item">
|
||||
<img src="./assets/images/star.png" width="20" height="20" loading="lazy" alt="rating">
|
||||
|
||||
<span class="span">${vote_average.toFixed(1)}</span>
|
||||
</div>
|
||||
|
||||
<div class="card-badge">${release_date.split("-")[0]}</div>
|
||||
</div>
|
||||
|
||||
<a href="./detail.html" class="card-btn" title="${title}" onclick="getMovieDetail(${id})"></a>
|
||||
`;
|
||||
|
||||
return card;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
'use strict';
|
||||
|
||||
import { api_key, fetchDataFromServer } from "./api.js";
|
||||
import { sidebar } from "./sidebar.js";
|
||||
import { createMovieCard } from "./movie-card.js";
|
||||
import { search } from "./search.js";
|
||||
|
||||
// collectc genre name & url parameter from local storage
|
||||
const genreName = window.localStorage.getItem("genreName");
|
||||
const urlParam = window.localStorage.getItem("urlParam");
|
||||
|
||||
const pageContent = document.querySelector("[page-content]");
|
||||
|
||||
|
||||
|
||||
sidebar();
|
||||
|
||||
|
||||
|
||||
let currentPage = 1;
|
||||
let totalPages = 0;
|
||||
|
||||
fetchDataFromServer(`https://api.themoviedb.org/3/discover/movie?api_key=${api_key}&sort_by=popularity.desc&include_adult=false&page=${currentPage}&${urlParam}`, function ({ results: movieList, total_pages }) {
|
||||
|
||||
totalPages = total_pages;
|
||||
|
||||
document.title = `${genreName} Movies - Tvflix`;
|
||||
|
||||
const movieListElem = document.createElement("section");
|
||||
movieListElem.classList.add("movie-list", "genre-list");
|
||||
movieListElem.ariaLabel = `${genreName} Movies`;
|
||||
|
||||
movieListElem.innerHTML = `
|
||||
<div class="title-wrapper">
|
||||
<h1 class="heading">All ${genreName} Movies</h1>
|
||||
</div>
|
||||
|
||||
<div class="grid-list"></div>
|
||||
|
||||
<button class="btn load-more" load-more>Load More</button>
|
||||
`;
|
||||
|
||||
/**
|
||||
* add movie card based on fetched item
|
||||
*/
|
||||
for (const movie of movieList) {
|
||||
const movieCard = createMovieCard(movie);
|
||||
|
||||
movieListElem.querySelector(".grid-list").appendChild(movieCard);
|
||||
}
|
||||
|
||||
pageContent.appendChild(movieListElem);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* load more button functionality
|
||||
*/
|
||||
document.querySelector("[load-more]").addEventListener("click", function () {
|
||||
|
||||
if (currentPage >= totalPages) {
|
||||
this.style.display = "none"; // this == loading-btn
|
||||
return;
|
||||
}
|
||||
|
||||
currentPage++;
|
||||
this.classList.add("loading"); // this == loading-btn
|
||||
|
||||
fetchDataFromServer(`https://api.themoviedb.org/3/discover/movie?api_key=${api_key}&sort_by=popularity.desc&include_adult=false&page=${currentPage}&${urlParam}`, ({ results: movieList }) => {
|
||||
this.classList.remove("loading"); // this == loading-btn
|
||||
|
||||
for (const movie of movieList) {
|
||||
const movieCard = createMovieCard(movie);
|
||||
|
||||
movieListElem.querySelector(".grid-list").appendChild(movieCard);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
search();
|
||||
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
|
||||
import { api_key, fetchDataFromServer } from "./api.js";
|
||||
import { createMovieCard } from "./movie-card.js";
|
||||
|
||||
|
||||
export function search() {
|
||||
|
||||
const searchWrapper = document.querySelector("[search-wrapper]");
|
||||
const searchField = document.querySelector("[search-field]");
|
||||
|
||||
const searchResultModal = document.createElement("div");
|
||||
searchResultModal.classList.add("search-modal");
|
||||
document.querySelector("main").appendChild(searchResultModal);
|
||||
|
||||
let searchTimeout;
|
||||
|
||||
searchField.addEventListener("input", function () {
|
||||
|
||||
if (!searchField.value.trim()) {
|
||||
searchResultModal.classList.remove("active");
|
||||
searchWrapper.classList.remove("searching");
|
||||
clearTimeout(searchTimeout);
|
||||
return;
|
||||
}
|
||||
|
||||
searchWrapper.classList.add("searching");
|
||||
clearTimeout(searchTimeout);
|
||||
|
||||
searchTimeout = setTimeout(function () {
|
||||
|
||||
fetchDataFromServer(`https://api.themoviedb.org/3/search/movie?api_key=${api_key}&query=${searchField.value}&page=1&include_adult=false`, function ({ results: movieList }) {
|
||||
|
||||
searchWrapper.classList.remove("searching");
|
||||
searchResultModal.classList.add("active");
|
||||
searchResultModal.innerHTML = ""; // remove old results
|
||||
|
||||
searchResultModal.innerHTML = `
|
||||
<p class="label">Results for</p>
|
||||
|
||||
<h1 class="heading">${searchField.value}</h1>
|
||||
|
||||
<div class="movie-list">
|
||||
<div class="grid-list"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
for (const movie of movieList) {
|
||||
const movieCard = createMovieCard(movie);
|
||||
|
||||
searchResultModal.querySelector(".grid-list").appendChild(movieCard);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}, 500);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
import { api_key, fetchDataFromServer } from "./api.js";
|
||||
|
||||
|
||||
export function sidebar() {
|
||||
|
||||
const excludedGenres = ["Drama", "Romance"];
|
||||
const genreList = {};
|
||||
|
||||
fetchDataFromServer(`https://api.themoviedb.org/3/genre/movie/list?api_key=${api_key}`, function ({ genres }) {
|
||||
const filteredGenres = genres.filter(genre => !excludedGenres.includes(genre.name));
|
||||
|
||||
for (const { id, name } of filteredGenres) {
|
||||
genreList[id] = name;
|
||||
}
|
||||
|
||||
genreLink();
|
||||
});
|
||||
|
||||
const sidebarInner = document.createElement("div");
|
||||
sidebarInner.classList.add("sidebar-inner");
|
||||
|
||||
sidebarInner.innerHTML = `
|
||||
<div class="sidebar-list">
|
||||
<p class="title">Genre</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<p class="copyright">
|
||||
<h6>All Rights Reserved.</h6>
|
||||
Copyright 2024 <a href="https://tdvorak.site" target="_blank">Tomas Dvorak </a>
|
||||
</p>
|
||||
|
||||
<p>Powered by
|
||||
<img src="./assets/images/tmdb-logo.svg" width="130" height="17" alt="the movie database logo">
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
const genreLink = function () {
|
||||
|
||||
for (const [genreId, genreName] of Object.entries(genreList)) {
|
||||
|
||||
const link = document.createElement("a");
|
||||
link.classList.add("sidebar-link");
|
||||
link.setAttribute("href", "./movie-list.html");
|
||||
link.setAttribute("menu-close", "");
|
||||
link.setAttribute("onclick", `getMovieList("with_genres=${genreId}", "${genreName}")`);
|
||||
link.textContent = genreName;
|
||||
|
||||
sidebarInner.querySelectorAll(".sidebar-list")[0].appendChild(link);
|
||||
|
||||
}
|
||||
|
||||
const sidebar = document.querySelector("[sidebar]");
|
||||
sidebar.appendChild(sidebarInner);
|
||||
toggleSidebar(sidebar);
|
||||
|
||||
}
|
||||
|
||||
|
||||
const toggleSidebar = function (sidebar) {
|
||||
|
||||
|
||||
const sidebarBtn = document.querySelector("[menu-btn]");
|
||||
const sidebarTogglers = document.querySelectorAll("[menu-toggler]");
|
||||
const sidebarClose = document.querySelectorAll("[menu-close]");
|
||||
const overlay = document.querySelector("[overlay]");
|
||||
|
||||
addEventOnElements(sidebarTogglers, "click", function () {
|
||||
sidebar.classList.toggle("active");
|
||||
sidebarBtn.classList.toggle("active");
|
||||
overlay.classList.toggle("active");
|
||||
});
|
||||
|
||||
addEventOnElements(sidebarClose, "click", function () {
|
||||
sidebar.classList.remove("active");
|
||||
sidebarBtn.classList.remove("active");
|
||||
overlay.classList.remove("active");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||