├── .htmlnanorc ├── .sassrc ├── src ├── partials │ ├── pagination.html │ ├── modal-movie.html │ ├── main.html │ ├── modal-team.html │ ├── footer.html │ ├── loader.html │ ├── header-library.html │ ├── library-content.html │ └── header-main.html ├── sass │ ├── base │ │ ├── _module.scss │ │ ├── _container.scss │ │ └── _common.scss │ ├── utils │ │ ├── _module.scss │ │ ├── _mixins.scss │ │ ├── _reset.scss │ │ ├── _visually-hidden.scss │ │ └── _variables.scss │ ├── index.scss │ ├── layout │ │ ├── _module.scss │ │ ├── _main.scss │ │ ├── _footer.scss │ │ ├── _header-library.scss │ │ ├── _library.scss │ │ └── _header-main.scss │ ├── components │ │ ├── _module.scss │ │ ├── _go-top.scss │ │ ├── _backdrop.scss │ │ ├── _logo.scss │ │ ├── _arrows_pagination.scss │ │ ├── _modal-team.scss │ │ ├── _button.scss │ │ ├── _modal-movie.scss │ │ └── _loader.scss │ ├── index.css.map │ └── index.css ├── images │ ├── team │ │ ├── igoor.jpg │ │ ├── igor.jpg │ │ ├── inna2.jpg │ │ ├── katya.jpg │ │ ├── Dmytro.jpg │ │ ├── andrew.jpg │ │ ├── denys2.jpg │ │ ├── julia2.jpg │ │ ├── ruslan2.jpg │ │ ├── vasyl2.jpg │ │ ├── Volodymyr.jpg │ │ ├── vladyslav.jpg │ │ ├── yaroslav.jpg │ │ └── anastasiya_2.jpg │ ├── header-main │ │ ├── test2.jpg │ │ ├── hero-home.jpg │ │ ├── hero-home-2x.jpg │ │ ├── hero-home-mob.jpg │ │ ├── hero-home-tab.jpg │ │ ├── hamster-arrow-up.png │ │ ├── hero-home-mob-2x.jpg │ │ └── hero-home-tab-2x.jpg │ ├── no-photo │ │ └── no-photo.png │ ├── header-library │ │ ├── test1.png │ │ ├── hero-library.jpg │ │ ├── hero-library-2x.jpg │ │ ├── hero-library-mob.jpg │ │ ├── hero-library-tab.jpg │ │ ├── hero-library-mob-2x.jpg │ │ └── hero-library-tab-2x.jpg │ ├── no-watched │ │ ├── no-watched.png │ │ └── no-watched-transformed1.png │ └── svg │ │ ├── arrow-left-1.svg │ │ ├── arrow-right.svg │ │ ├── favicon.ico.svg │ │ ├── arrow-left.svg │ │ └── symbol-defs.svg ├── fonts │ ├── Roboto-Medium.ttf │ ├── Roboto-Medium.woff │ ├── Roboto-Medium.woff2 │ ├── Roboto-Regular.ttf │ ├── Roboto-Regular.woff │ └── Roboto-Regular.woff2 ├── js │ ├── local-storage.js │ ├── refs.js │ ├── loader-spinner.js │ ├── go-top.js │ ├── geners.js │ ├── change-theme.js │ ├── notification.js │ ├── trailer.js │ ├── pagination.js │ ├── api-fetch.js │ ├── search-input.js │ ├── markup-cards.js │ ├── queue-local-storage.js │ ├── watched-local-storage.js │ ├── buttons.js │ ├── modal-movie.js │ └── modal-team.js ├── index.js ├── library-page.html └── index.html ├── assets ├── status.png ├── how-it-works.png ├── repo-settings.png ├── actions-config-step-1.png └── actions-config-step-2.png ├── .posthtmlrc ├── .parcelrc ├── .editorconfig ├── .prettierrc ├── .github └── workflows │ └── deploy.yml ├── package.json ├── .gitignore ├── README.en.md ├── README.md ├── README.uk.md ├── README.pl.md └── README.es.md /.htmlnanorc: -------------------------------------------------------------------------------- 1 | { 2 | "minifySvg": false 3 | } -------------------------------------------------------------------------------- /.sassrc: -------------------------------------------------------------------------------- 1 | { 2 | "includePaths": [ 3 | "node_modules" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/partials/pagination.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/sass/base/_module.scss: -------------------------------------------------------------------------------- 1 | @import './common'; 2 | @import './container.scss'; 3 | -------------------------------------------------------------------------------- /assets/status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/assets/status.png -------------------------------------------------------------------------------- /.posthtmlrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "posthtml-include": { 4 | "root": "./src" 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /assets/how-it-works.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/assets/how-it-works.png -------------------------------------------------------------------------------- /assets/repo-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/assets/repo-settings.png -------------------------------------------------------------------------------- /src/images/team/igoor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/igoor.jpg -------------------------------------------------------------------------------- /src/images/team/igor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/igor.jpg -------------------------------------------------------------------------------- /src/images/team/inna2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/inna2.jpg -------------------------------------------------------------------------------- /src/images/team/katya.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/katya.jpg -------------------------------------------------------------------------------- /src/fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /src/images/team/Dmytro.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/Dmytro.jpg -------------------------------------------------------------------------------- /src/images/team/andrew.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/andrew.jpg -------------------------------------------------------------------------------- /src/images/team/denys2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/denys2.jpg -------------------------------------------------------------------------------- /src/images/team/julia2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/julia2.jpg -------------------------------------------------------------------------------- /src/images/team/ruslan2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/ruslan2.jpg -------------------------------------------------------------------------------- /src/images/team/vasyl2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/vasyl2.jpg -------------------------------------------------------------------------------- /src/fonts/Roboto-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/fonts/Roboto-Medium.woff -------------------------------------------------------------------------------- /src/fonts/Roboto-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/fonts/Roboto-Medium.woff2 -------------------------------------------------------------------------------- /src/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /src/fonts/Roboto-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/fonts/Roboto-Regular.woff -------------------------------------------------------------------------------- /src/fonts/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/fonts/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /src/images/team/Volodymyr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/Volodymyr.jpg -------------------------------------------------------------------------------- /src/images/team/vladyslav.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/vladyslav.jpg -------------------------------------------------------------------------------- /src/images/team/yaroslav.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/yaroslav.jpg -------------------------------------------------------------------------------- /assets/actions-config-step-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/assets/actions-config-step-1.png -------------------------------------------------------------------------------- /assets/actions-config-step-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/assets/actions-config-step-2.png -------------------------------------------------------------------------------- /src/images/header-main/test2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-main/test2.jpg -------------------------------------------------------------------------------- /src/images/no-photo/no-photo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/no-photo/no-photo.png -------------------------------------------------------------------------------- /src/images/team/anastasiya_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/team/anastasiya_2.jpg -------------------------------------------------------------------------------- /src/partials/modal-movie.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /src/images/header-library/test1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-library/test1.png -------------------------------------------------------------------------------- /src/sass/utils/_module.scss: -------------------------------------------------------------------------------- 1 | @import './variables'; 2 | @import './reset'; 3 | @import './visually-hidden'; 4 | @import './mixins'; 5 | -------------------------------------------------------------------------------- /src/images/header-main/hero-home.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-main/hero-home.jpg -------------------------------------------------------------------------------- /src/images/no-watched/no-watched.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/no-watched/no-watched.png -------------------------------------------------------------------------------- /src/images/header-main/hero-home-2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-main/hero-home-2x.jpg -------------------------------------------------------------------------------- /src/images/header-main/hero-home-mob.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-main/hero-home-mob.jpg -------------------------------------------------------------------------------- /src/images/header-main/hero-home-tab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-main/hero-home-tab.jpg -------------------------------------------------------------------------------- /src/sass/index.scss: -------------------------------------------------------------------------------- 1 | @import './utils/module'; 2 | @import './base/module'; 3 | @import './components/module'; 4 | @import './layout/module'; 5 | -------------------------------------------------------------------------------- /src/images/header-library/hero-library.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-library/hero-library.jpg -------------------------------------------------------------------------------- /src/images/header-library/hero-library-2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-library/hero-library-2x.jpg -------------------------------------------------------------------------------- /src/images/header-main/hamster-arrow-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-main/hamster-arrow-up.png -------------------------------------------------------------------------------- /src/images/header-main/hero-home-mob-2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-main/hero-home-mob-2x.jpg -------------------------------------------------------------------------------- /src/images/header-main/hero-home-tab-2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-main/hero-home-tab-2x.jpg -------------------------------------------------------------------------------- /src/images/header-library/hero-library-mob.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-library/hero-library-mob.jpg -------------------------------------------------------------------------------- /src/images/header-library/hero-library-tab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-library/hero-library-tab.jpg -------------------------------------------------------------------------------- /src/sass/layout/_module.scss: -------------------------------------------------------------------------------- 1 | @import './header-main'; 2 | @import './main'; 3 | @import './footer'; 4 | @import './header-library'; 5 | @import './library'; 6 | -------------------------------------------------------------------------------- /src/images/header-library/hero-library-mob-2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-library/hero-library-mob-2x.jpg -------------------------------------------------------------------------------- /src/images/header-library/hero-library-tab-2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/header-library/hero-library-tab-2x.jpg -------------------------------------------------------------------------------- /src/images/no-watched/no-watched-transformed1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/julieshapo/5th-element-filmoteka/HEAD/src/images/no-watched/no-watched-transformed1.png -------------------------------------------------------------------------------- /.parcelrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@parcel/config-default" 4 | ], 5 | "reporters": [ 6 | "...", 7 | "parcel-reporter-clean-dist" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = false 9 | 10 | 11 | [*.{json,md,yaml}] 12 | indent_size = 2 -------------------------------------------------------------------------------- /src/js/local-storage.js: -------------------------------------------------------------------------------- 1 | export function setWatchedLS(array) { 2 | localStorage.setItem('watched', JSON.stringify(array)); 3 | }; 4 | 5 | export function setQueueLS(array) { 6 | localStorage.setItem('queue', JSON.stringify(array)); 7 | }; -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "useTabs": false, 4 | "semi": true, 5 | "singleQuote": true, 6 | "trailingComma": "es5", 7 | "bracketSpacing": true, 8 | "arrowParens": "avoid", 9 | "proseWrap": "always" 10 | } 11 | -------------------------------------------------------------------------------- /src/images/svg/arrow-left-1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/sass/components/_module.scss: -------------------------------------------------------------------------------- 1 | @import './backdrop'; 2 | @import './modal-movie'; 3 | @import './modal-team'; 4 | @import './button'; 5 | @import './logo'; 6 | @import './arrows_pagination'; 7 | @import './loader'; 8 | @import './go-top'; 9 | -------------------------------------------------------------------------------- /src/images/svg/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/sass/utils/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin mobile { 2 | @media screen and (min-width: 480px) { 3 | @content; 4 | } 5 | } 6 | 7 | @mixin tablet { 8 | @media screen and (min-width: 768px) { 9 | @content; 10 | } 11 | } 12 | 13 | @mixin desktop { 14 | @media screen and (min-width: 1280px) { 15 | @content; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/images/svg/favicon.ico.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | film 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/js/refs.js: -------------------------------------------------------------------------------- 1 | export const refs = { 2 | pagination: document.getElementById('pagination'), 3 | spinner: document.querySelector('.js-spinner'), 4 | modal: document.querySelector('.js-modal'), 5 | modalMovie: document.querySelector('.modal-movie'), 6 | headerForm: document.querySelector('.header-form'), 7 | filmGallery: document.querySelector('.js-film-gallery'), 8 | }; 9 | -------------------------------------------------------------------------------- /src/sass/utils/_reset.scss: -------------------------------------------------------------------------------- 1 | p, 2 | h1, 3 | h2, 4 | h3, 5 | h4, 6 | h5, 7 | h6 { 8 | margin: 0; 9 | } 10 | 11 | ul { 12 | margin: 0; 13 | padding-left: 0; 14 | list-style: none; 15 | } 16 | 17 | a { 18 | text-decoration: none; 19 | } 20 | 21 | img { 22 | display: block; 23 | max-width: 100%; 24 | height: auto; 25 | } 26 | 27 | button { 28 | cursor: pointer; 29 | } -------------------------------------------------------------------------------- /src/sass/utils/_visually-hidden.scss: -------------------------------------------------------------------------------- 1 | .visually-hidden { 2 | position: absolute; 3 | white-space: nowrap; 4 | width: 1px; 5 | height: 1px; 6 | margin: -1px; 7 | border: 0; 8 | padding: 0; 9 | clip: rect(0 0 0 0); 10 | clip-path: inset(100%); 11 | overflow: hidden; 12 | } 13 | 14 | .is-hidden { 15 | opacity: 0; 16 | visibility: hidden; 17 | pointer-events: none; 18 | } -------------------------------------------------------------------------------- /src/images/svg/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/sass/base/_container.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | margin: 0 auto; 3 | padding-left: 20px; 4 | padding-right: 20px; 5 | max-width: 100%; 6 | 7 | @include mobile { 8 | max-width: 480px; 9 | } 10 | 11 | @include tablet { 12 | max-width: 768px; 13 | padding-left: 32px; 14 | padding-right: 32px; 15 | } 16 | 17 | @include desktop { 18 | max-width: 1280px; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/partials/main.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Film Gallery

4 | 5 |
6 | hamster with arrow up 11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /src/partials/modal-team.html: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /src/js/loader-spinner.js: -------------------------------------------------------------------------------- 1 | import { refs } from './refs'; 2 | 3 | // Функция показывает спинер 4 | 5 | export function showSpinner() { 6 | refs.spinner.classList.remove('visually-hidden'); 7 | } 8 | 9 | // Функция прячет спинер 10 | 11 | export function hideSpinner() { 12 | refs.spinner.classList.add('visually-hidden'); 13 | } 14 | 15 | window.onload = function () { 16 | window.setTimeout(function () { 17 | document.body.classList.add('loaded'); 18 | hideSpinner(); 19 | }, 500); 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './sass/index.scss'; 2 | // import 'animate.css'; 3 | import './js/refs'; 4 | import './js/search-input'; 5 | import './js/api-fetch'; 6 | import './js/loader-spinner'; 7 | import './js/markup-cards'; 8 | import './js/modal-movie'; 9 | import './js/pagination'; 10 | import './js/queue-local-storage'; 11 | import './js/watched-local-storage'; 12 | import './js/modal-team'; 13 | import './js/buttons'; 14 | import './js/trailer'; 15 | import './js/geners'; 16 | import './js/go-top'; 17 | import './js/change-theme'; 18 | -------------------------------------------------------------------------------- /src/sass/components/_go-top.scss: -------------------------------------------------------------------------------- 1 | .go-top { 2 | position: fixed; 3 | right: 25px; 4 | bottom: 25px; 5 | z-index: 1; 6 | 7 | display: none; 8 | padding: 0; 9 | font-size: 40px; 10 | 11 | cursor: pointer; 12 | background-color: transparent; 13 | transform: translateY(-20%); 14 | transition: transform 300ms linear; 15 | } 16 | 17 | .go-top--show { 18 | display: block; 19 | } 20 | 21 | .go-top:focus, 22 | .go-top:hover { 23 | transform: scale(1.01); 24 | } 25 | 26 | .go-top-img { 27 | width: 70px; 28 | height: 70px; 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and deploy to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | build-and-deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 🛎️ 12 | uses: actions/checkout@v2.3.1 13 | 14 | - name: Install and Build 🔧 15 | run: | 16 | npm ci 17 | npm run build 18 | 19 | - name: Deploy 🚀 20 | uses: JamesIves/github-pages-deploy-action@4.1.0 21 | with: 22 | branch: gh-pages 23 | folder: dist 24 | -------------------------------------------------------------------------------- /src/sass/utils/_variables.scss: -------------------------------------------------------------------------------- 1 | // BACKGROUND COLORS 2 | 3 | $colorBgLightMain: #e5e5e5; 4 | $colorBgAccentPagination: #ff6b08; 5 | $colorBgAccentDetails: #ff6b01; 6 | $colorModalBg: #ffffff; 7 | $colorModalBgSecondary: #f7f7f7; 8 | 9 | //TEXT COLORS 10 | 11 | $colorTextLight: #ffffff; 12 | $colorTextDark: #000000; 13 | $colorTexSecondary: #545454; 14 | $colorTextAccent: #ff6b08; 15 | $colorTextAccentCurrent: #ff001b; 16 | $colorModalTextSecondary: #8c8c8c; 17 | 18 | //BUTTONS COLORS 19 | 20 | $colorBtnAccent: #ff6b02; 21 | $colorBtnAccentSecondary: #ff6b08; 22 | $colorBtnLight: #ffffff; 23 | -------------------------------------------------------------------------------- /src/sass/components/_backdrop.scss: -------------------------------------------------------------------------------- 1 | .backdrop { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | z-index: 100; 6 | 7 | width: 100%; 8 | height: 100%; 9 | 10 | background: rgba(94, 99, 99, 0.7); 11 | 12 | overflow-y: auto; 13 | 14 | visibility: visible; 15 | opacity: 1; 16 | transition: opacity 250ms linear, visibility 250ms linear; 17 | 18 | &.visually-hidden { 19 | visibility: hidden; 20 | pointer-events: none; 21 | 22 | opacity: 0; 23 | } 24 | 25 | &.visually-hidden .modal-movie { 26 | transform: translate(-50%, -50%) scale(0.9); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/js/go-top.js: -------------------------------------------------------------------------------- 1 | const goTopBtn = document.querySelector('.go-top'); 2 | 3 | goTopBtn.addEventListener('click', goTop); 4 | window.addEventListener('scroll', trackScroll); 5 | 6 | function trackScroll() { 7 | const offset = window.pageYOffset; 8 | const coords = document.documentElement.clientHeight; 9 | if (offset > coords) { 10 | goTopBtn.classList.add('go-top--show'); 11 | } else { 12 | goTopBtn.classList.remove('go-top--show'); 13 | } 14 | } 15 | 16 | function goTop() { 17 | if (window.pageYOffset > 0) { 18 | window.scrollBy(0, -50); 19 | setTimeout(goTop, 0); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/partials/footer.html: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /src/partials/loader.html: -------------------------------------------------------------------------------- 1 |
2 | 24 |
25 | -------------------------------------------------------------------------------- /src/js/geners.js: -------------------------------------------------------------------------------- 1 | export const genres = [ 2 | { id: 28, name: 'Action' }, 3 | { id: 12, name: 'Adventure' }, 4 | { id: 16, name: 'Animation' }, 5 | { id: 35, name: 'Comedy' }, 6 | { id: 80, name: 'Crime' }, 7 | { id: 99, name: 'Documentary' }, 8 | { id: 18, name: 'Drama' }, 9 | { id: 10751, name: 'Family' }, 10 | { id: 14, name: 'Fantasy' }, 11 | { id: 36, name: 'History' }, 12 | { id: 27, name: 'Horror' }, 13 | { id: 10402, name: 'Music' }, 14 | { id: 9648, name: 'Mystery' }, 15 | { id: 10749, name: 'Romance' }, 16 | { id: 878, name: 'Science Fiction' }, 17 | { id: 10770, name: 'TV Movie' }, 18 | { id: 53, name: 'Thriller' }, 19 | { id: 10752, name: 'War' }, 20 | { id: 37, name: 'Western' }, 21 | ]; 22 | 23 | // Функция форматирует жанры с массива 24 | 25 | export function genresFormat(array) { 26 | return genres.reduce((acc, item) => { 27 | if (array?.includes(item.id)) { 28 | acc.push(item.name); 29 | } 30 | return acc; 31 | }, []); 32 | }; 33 | 34 | // Функция форматирует жанры с обьекта 35 | 36 | export function genresFormatModal(object) { 37 | return object?.map(item => item.name) 38 | }; -------------------------------------------------------------------------------- /src/sass/index.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["base/_common.scss","utils/_reset.scss","index.css","utils/_visually-hidden.scss","base/_container.scss","utils/_mixins.scss","components/_backdrop.scss","components/_modal-movie.scss","layout/_library.scss"],"names":[],"mappings":"AAAQ,6DAAA;ACAR;;;;;;;EAOE,SAAA;ACEF;;ADCA;;EAEE,SAAA;EACA,eAAA;ACEF;;ADCA;EACE,eAAA;ACEF;;ADCA;EACE,kBAAA;ACEF;;ADCA;EACE,cAAA;EACA,eAAA;EACA,YAAA;EAEA,WAAA;ACCF;;AC9BA;EACI,kBAAA;EACA,mBAAA;EACA,UAAA;EACA,WAAA;EACA,YAAA;EACA,SAAA;EACA,UAAA;EACA,mBAAA;EACA,8BAAA;UAAA,sBAAA;EACA,gBAAA;ADiCJ;;AE3CA;EACE,cAAA;EACA,kBAAA;EACA,mBAAA;EACA,eAAA;AF8CF;AGjDE;EDDF;IAOI,gBAAA;EF+CF;AACF;AGhDE;EDPF;IAWI,gBAAA;IACA,kBAAA;IACA,mBAAA;EFgDF;AACF;AGjDE;EDbF;IAiBI,iBAAA;EFiDF;AACF;;AInEA;EACE,eAAA;EACA,MAAA;EACA,OAAA;EACA,YAAA;EAEA,WAAA;EACA,YAAA;EAEA,iCAAA;EAEA,gBAAA;EAEA,qGAAA;AJkEF;;AK/EA;EACE,YAAA;EACA,aAAA;EACA,kBAAA;EACA,kBAAA;EACA,YAAA;EACA,UAAA;ALkFF;;AK/EA;EACE,kBAAA;EACA,mBAAA;ALkFF;;AK/EA;EACE,kBAAA;EACA,QAAA;EACA,SAAA;EACA,gCAAA;EAEA,4BAAA;EACA,cAAA;EACA,gBAAA;EACA,wBAAA;EACA,sBAAA;ALiFF;;AK9EA;EACE,gBAAA;EACA,kBAAA;ALiFF;;AK9EA;EACE,cAAA;EACA,YAAA;EACA,aAAA;ALiFF;;AMpHA;EACE,qBAAA;ANuHF","file":"index.css"} -------------------------------------------------------------------------------- /src/sass/base/_common.scss: -------------------------------------------------------------------------------- 1 | @import '~node_modules/modern-normalize/modern-normalize.css'; 2 | @font-face { 3 | font-weight: normal; 4 | font-family: 'Roboto', sans-serif; 5 | src: local('Roboto'), url('../fonts/Roboto-Regular.woff') format('woff'), 6 | url('../fonts/Roboto-Regular.woff2') format('woff2'); 7 | } 8 | @font-face { 9 | font-weight: 500; 10 | font-family: 'Roboto', sans-serif; 11 | src: local('Roboto'), url('../fonts/Roboto-Medium.woff') format('woff'), 12 | url('../fonts/Roboto-Medium.woff2') format('woff2'); 13 | } 14 | 15 | body { 16 | font-family: 'Roboto', sans-serif; 17 | font-size: 0.75rem; 18 | color: $colorModalBg; 19 | line-height: 1.3; 20 | position: relative; 21 | min-height: 100vh; 22 | } 23 | 24 | // полоса прокрутки (скроллбар) 25 | 26 | ::-webkit-scrollbar { 27 | width: 0.45rem; 28 | border: 0.0625rem solid rgba(0, 0, 0, 0.237); 29 | background-color: transparent; 30 | } 31 | 32 | // ползунок скроллбара 33 | 34 | ::-webkit-scrollbar-thumb { 35 | background-color: #545454; 36 | border-radius: 0.2rem; 37 | box-shadow: inset 0.0625rem 0.0625rem 0.3125rem $colorTextDark; 38 | } 39 | 40 | ::-webkit-scrollbar-thumb:hover { 41 | background-color: #ff6b08; 42 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parcel-project-template", 3 | "version": "2.0.0", 4 | "description": "", 5 | "homepage": "https://julieshapo.github.io/5th-element-filmoteka", 6 | "scripts": { 7 | "start": "parcel src/*.html", 8 | "build": "parcel build src/*.html --public-url /5th-element-filmoteka/" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/goitacademy/parcel-project-template.git" 13 | }, 14 | "keywords": [], 15 | "author": "Alexander Repeta ", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/goitacademy/parcel-project-template/issues" 19 | }, 20 | "dependencies": { 21 | "animate.css": "^4.1.1", 22 | "axios": "^1.3.4", 23 | "basiclightbox": "^5.0.4", 24 | "lodash.debounce": "^4.0.8", 25 | "modern-normalize": "^1.1.0", 26 | "notiflix": "^3.2.6", 27 | "tui-pagination": "^3.4.1" 28 | }, 29 | "devDependencies": { 30 | "@parcel/transformer-sass": "^2.6.0", 31 | "buffer": "^6.0.3", 32 | "parcel": "^2.6.0", 33 | "parcel-reporter-clean-dist": "^1.0.4", 34 | "posthtml-include": "^1.7.4" 35 | }, 36 | "browserslist": "> 0.5%, last 2 versions, not dead" 37 | } 38 | -------------------------------------------------------------------------------- /src/partials/header-library.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 27 |
28 | 31 | 32 |
33 |
34 |
35 | -------------------------------------------------------------------------------- /src/library-page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Filmoteka 7 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Filmoteka 7 | 12 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/js/change-theme.js: -------------------------------------------------------------------------------- 1 | const darkThemeBtn = document.querySelector('.toggle-darktheme-btn'); 2 | const header = document.querySelector('.header'); 3 | const noWatchedText = document.querySelector('.no-watched-text'); 4 | const noQueueText = document.querySelector('.no-queue-text'); 5 | if (!darkThemeBtn) { 6 | return; 7 | } 8 | darkThemeBtn.addEventListener('click', () => { 9 | console.log('click'); 10 | if (document.body.classList.contains('darkTheme')) { 11 | onLigthTheme(); 12 | } else { 13 | onDarkTheme(); 14 | } 15 | }); 16 | 17 | function onLigthTheme() { 18 | header.classList.remove('headerDark'); 19 | document.body.classList.remove('darkTheme'); 20 | darkThemeBtn.textContent = '🌙'; 21 | localStorage.theme = 'ligth'; 22 | 23 | if (!noWatchedText || !noQueueText) { 24 | return; 25 | } 26 | noWatchedText.classList.remove('darkText'); 27 | noQueueText.classList.remove('darkText'); 28 | } 29 | 30 | function onDarkTheme() { 31 | header.classList.add('headerDark'); 32 | document.body.classList.add('darkTheme'); 33 | darkThemeBtn.textContent = '🔆'; 34 | localStorage.theme = 'darkTheme'; 35 | 36 | if (!noWatchedText || !noQueueText) { 37 | return; 38 | } 39 | noWatchedText.classList.add('darkText'); 40 | noQueueText.classList.add('darkText'); 41 | } 42 | 43 | if (localStorage.theme === 'darkTheme') { 44 | onDarkTheme(); 45 | } 46 | -------------------------------------------------------------------------------- /src/partials/library-content.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

My Library

4 |
5 |
6 |

Don't have movies? Hamster is shocked!

7 | Choose movie 8 | no-watched 14 |
15 | 16 |
17 |

Choose your movie buddy, I got the popcorn!

18 | Choose movie 19 | no-queue 25 |
26 | 27 | 37 |
38 |
39 |
40 | hamster with arrow up 45 |
46 |
47 | -------------------------------------------------------------------------------- /src/js/notification.js: -------------------------------------------------------------------------------- 1 | // import Notiflix from 'notiflix'; 2 | // Notiflix.Notify.init({ 3 | // width: '500px', 4 | // margin: 0, 5 | // padding: 0, 6 | // position: 'center-top', 7 | // distance: '140px', 8 | // opacity: 1, 9 | // borderRadius: '2px', 10 | // rtl: false, 11 | // timeout: 3000000, 12 | // messageMaxLength: 110, 13 | // backOverlay: false, 14 | // backOverlayColor: 'rgba(0,0,0,0.5)', 15 | // plainText: true, 16 | // showOnlyTheLastOne: false, 17 | // clickToClose: false, 18 | // pauseOnHover: true, 19 | // ID: 'NotiflixNotify', 20 | // className: 'notiflix-notify', 21 | // zindex: 4001, 22 | // fontFamily: 'Georgia', 23 | // fontSize: '14px', 24 | // cssAnimation: true, 25 | // cssAnimationDuration: 400, 26 | // cssAnimationStyle: 'from-right', 27 | // closeButton: false, 28 | // useIcon: true, 29 | // useFontAwesome: false, 30 | // fontAwesomeIconStyle: 'basic', 31 | // fontAwesomeIconSize: '34px', 32 | // failure: { 33 | // background: 'transparent', 34 | // textColor: '#FF001B', 35 | // childClassName: 'notiflix-notify-failure', 36 | // notiflixIconColor: 'none', 37 | // fontAwesomeClassName: 'fas fa-times-circle', 38 | // fontAwesomeIconColor: 'rgba(0,0,0,0.2)', 39 | // backOverlayColor: 'rgba(255,85,73,0.2)', 40 | // }, 41 | // warning: { 42 | // background: 'transparent', 43 | // textColor: '#FF001B', 44 | // childClassName: 'notiflix-notify-warning', 45 | // notiflixIconColor: 'transparent', 46 | // fontAwesomeClassName: 'fas fa-exclamation-circle', 47 | // fontAwesomeIconColor: 'rgba(0,0,0,1)', 48 | // backOverlayColor: 'rgba(238,191,49,0.2)', 49 | // }, 50 | // }); -------------------------------------------------------------------------------- /src/js/trailer.js: -------------------------------------------------------------------------------- 1 | import { refs } from './refs'; 2 | import { getMovieTrailer } from './api-fetch'; 3 | import * as basicLightbox from 'basiclightbox'; 4 | import { showSpinner, hideSpinner } from './loader-spinner'; 5 | 6 | refs.modalMovie.addEventListener('click', onTrailerBtnClick); 7 | 8 | function onTrailerBtnClick(e) { 9 | const parent = e.target.closest('div'); 10 | const button = e.target.closest('button'); 11 | const { trailer } = button?.dataset || {}; 12 | const { id } = parent?.dataset || {}; 13 | 14 | if (!trailer) { 15 | return; 16 | } 17 | 18 | showTrailer(id); 19 | } 20 | 21 | let key = ''; 22 | 23 | async function showTrailer(id) { 24 | try { 25 | showSpinner(); 26 | const { results } = await getMovieTrailer(id); 27 | const trailer = results.find(item => item.type === 'Trailer'); 28 | 29 | if (trailer && trailer.type !== 'Trailer') { 30 | return; 31 | } 32 | key = trailer ? trailer.key : ''; 33 | 34 | showHideTrailer(); 35 | } catch (error) { 36 | console.log(error.message); 37 | } 38 | } 39 | 40 | function showHideTrailer(e) { 41 | const instance = basicLightbox.create( 42 | ` 43 | `, 44 | { 45 | onShow: () => { 46 | document.addEventListener('keydown', closeModal); 47 | }, 48 | onClose: () => { 49 | document.removeEventListener('keydown', closeModal); 50 | }, 51 | } 52 | ); 53 | 54 | instance.show(); 55 | hideSpinner(); 56 | 57 | function closeModal(e) { 58 | if (e.code === 'Escape') { 59 | instance.close(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/partials/header-main.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 25 |
26 | 33 | 38 |
39 |

40 | Please, enter your search request. 41 |

42 |

43 | Search result not successful. Enter the correct movie name and try again. 44 |

45 |
46 |
47 | -------------------------------------------------------------------------------- /src/js/pagination.js: -------------------------------------------------------------------------------- 1 | import Pagination from 'tui-pagination'; 2 | import { refs } from './refs'; 3 | import { renderSearchFilms, name } from './search-input'; 4 | import { TrendingMovie } from './markup-cards'; 5 | import { markupWatched } from './watched-local-storage'; 6 | import { markupQueue } from './queue-local-storage'; 7 | 8 | const TUI_PAGES_VISIBLE = 5; 9 | 10 | let currentPage = 1; 11 | let currentWatchedPage = 1; 12 | let currentQueuePage = 1; 13 | 14 | export function createPagination(totalItems, option, firstPage, itemsPerPage) { 15 | const options = { 16 | page: currentPage, 17 | itemsPerPage, 18 | centerAlign: true, 19 | totalItems, 20 | visiblePages: TUI_PAGES_VISIBLE, 21 | }; 22 | 23 | const pagination = new Pagination(refs.pagination, options); 24 | 25 | if (firstPage === 1) { 26 | pagination.reset(); 27 | } 28 | if (firstPage === 2) { 29 | pagination.reset(); 30 | pagination.movePageTo(currentWatchedPage); 31 | } 32 | if (firstPage === 3) { 33 | pagination.reset(); 34 | pagination.movePageTo(currentQueuePage); 35 | } 36 | if (option === 1) { 37 | pagination.on('beforeMove', event => { 38 | currentPage = event.page; 39 | renderSearchFilms(name, currentPage, 0); 40 | window.scrollTo({ top: 0, behavior: 'smooth' }); 41 | }); 42 | } 43 | if (option === 2) { 44 | pagination.on('beforeMove', event => { 45 | currentPage = event.page; 46 | TrendingMovie(currentPage); 47 | window.scrollTo({ top: 0, behavior: 'smooth' }); 48 | }); 49 | } 50 | if (option === 3) { 51 | pagination.on('beforeMove', event => { 52 | currentWatchedPage = event.page; 53 | markupWatched(currentWatchedPage); 54 | window.scrollTo({ top: 0, behavior: 'smooth' }); 55 | }); 56 | } 57 | if (option === 4) { 58 | pagination.on('beforeMove', event => { 59 | currentQueuePage = event.page; 60 | markupQueue(currentQueuePage); 61 | window.scrollTo({ top: 0, behavior: 'smooth' }); 62 | }); 63 | } 64 | } -------------------------------------------------------------------------------- /src/js/api-fetch.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const API_KEY = '169863a84bc27c731fc45c45dd4a4a7e'; 4 | 5 | //Запит фільму за ключовим словом 6 | 7 | export async function getMoviesByName(name, page) { 8 | try { 9 | const params = { 10 | api_key: API_KEY, 11 | query: name, 12 | page, 13 | language: 'en-US', 14 | }; 15 | const response = await axios.get( 16 | 'https://api.themoviedb.org/3/search/movie', 17 | { params } 18 | ); 19 | return response.data; 20 | } catch (error) { 21 | console.log(error); 22 | } 23 | }; 24 | 25 | //Запит на список найпопулярніших фільмів 26 | 27 | export async function getMoviesTrending(page = 1) { 28 | try { 29 | const params = { 30 | api_key: API_KEY, 31 | language: 'en-US', 32 | }; 33 | const response = await axios.get( 34 | `https://api.themoviedb.org/3/trending/movie/week?page=${page}`, 35 | // `https://api.themoviedb.org/3/trending/all/week?page=${page}`, 36 | { params } 37 | ); 38 | return response.data; 39 | } catch (error) { 40 | console.log(error); 41 | } 42 | }; 43 | 44 | //Запит повної інформації про кінофільм 45 | 46 | export async function getMovieFullInfo(movie_id) { 47 | try { 48 | const params = { 49 | api_key: API_KEY, 50 | language: 'en-US', 51 | }; 52 | const response = await axios.get( 53 | `https://api.themoviedb.org/3/movie/${movie_id}?`, 54 | { params } 55 | ); 56 | return response.data; 57 | } catch (error) { 58 | console.log(error); 59 | } 60 | }; 61 | 62 | //Запит трейлеру фільма 63 | 64 | export async function getMovieTrailer(movie_id) { 65 | try { 66 | const params = { 67 | api_key: API_KEY, 68 | language: 'en-US', 69 | page: 1, 70 | }; 71 | const response = await axios.get( 72 | `https://api.themoviedb.org/3/movie/${movie_id}/videos?`, 73 | { params } 74 | ); 75 | return response.data; 76 | } catch (error) { 77 | console.log(error); 78 | } 79 | }; -------------------------------------------------------------------------------- /src/js/search-input.js: -------------------------------------------------------------------------------- 1 | import debounce from 'lodash.debounce'; 2 | import axios from 'axios'; 3 | import { getMoviesByName } from './api-fetch'; 4 | import { createMarkupOneCard } from './markup-cards'; 5 | import { createPagination } from './pagination'; 6 | 7 | const ITEMS_PER_PAGES = 20; 8 | 9 | const form = document.querySelector('.header-form'); 10 | const input = document.querySelector('.header-form-input'); 11 | const filmGallery = document.querySelector('.js-film-gallery'); 12 | const searchError = document.querySelector('.search-error'); 13 | 14 | const searchError2 = document.querySelector('.search-error2'); 15 | 16 | export let name = ''; 17 | 18 | if (!form) { 19 | return; 20 | } 21 | input.addEventListener('focus', function () { 22 | // Очищаємо поле введення 23 | input.value = ''; 24 | }); 25 | 26 | form.addEventListener('submit', onFormSubmit); 27 | 28 | function onFormSubmit(event) { 29 | event.preventDefault(); 30 | 31 | const queryVal = event.currentTarget.elements.searchQuery.value.trim(); 32 | searchError2.style.display = 'none'; 33 | 34 | if (queryVal === '') { 35 | searchError2.style.display = 'flex'; 36 | setTimeout(() => { 37 | searchError2.style.display = 'none'; 38 | }, 5000); 39 | } 40 | if (name === input.value) { 41 | return; 42 | } 43 | name = input.value.trim(); 44 | input.value = name; 45 | renderSearchFilms(name, 1, 1); 46 | input.value = ''; 47 | } 48 | 49 | export async function renderSearchFilms(name, currentPage, firstPage) { 50 | try { 51 | if (name) { 52 | searchError.style.display = 'none'; 53 | const response = await getMoviesByName(name, currentPage); 54 | if (response.results.length < 1) { 55 | searchError.style.display = 'flex'; 56 | setTimeout(() => { 57 | searchError.style.display = 'none'; 58 | }, 5000); 59 | } 60 | filmGallery.innerHTML = createMarkupOneCard(response.results); 61 | 62 | createPagination(response.total_results, 1, firstPage, ITEMS_PER_PAGES); 63 | } 64 | } catch (error) { 65 | console.log(error.message); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/js/markup-cards.js: -------------------------------------------------------------------------------- 1 | import { getMoviesTrending } from './api-fetch'; 2 | import { genresFormat } from './geners'; 3 | import { refs } from './refs'; 4 | import { createPagination } from './pagination'; 5 | import { genresArray } from './watched-local-storage'; 6 | 7 | const ITEMS_PER_PAGES = 20; 8 | 9 | // let firstFunctionRun = 0; 10 | 11 | TrendingMovie(); 12 | 13 | // Функция которая ожидает и перебирает массив и возвращает разметку карточек фильмов 14 | 15 | export function createMarkupOneCard(array) { 16 | return array 17 | .map(item => { 18 | const genresFormatGetId = item.genres?.map(genre => genre.id); 19 | const geners = genresFormat(item.genre_ids || genresFormatGetId).join( 20 | ', ' 21 | ); 22 | const date = item.release_date ?? item.first_air_date ?? null; 23 | const year = date ? date.slice(0, 4) : 'Unknown year'; 24 | 25 | const poster = item.poster_path 26 | ? `https://image.tmdb.org/t/p/w500/${item.poster_path}` 27 | : 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/no-photo/no-photo.png?raw=true'; 28 | 29 | return ` 30 |
  • 31 |
    32 | ${item.title}
35 |               class= 37 |
    38 |
    39 |

    ${item.title}

    40 |

    ${geners} | ${year}

    41 |
    42 |
  • 43 | `; 44 | }) 45 | .join(''); 46 | } 47 | 48 | // Функция которая ожидает ответа от апи и вставляет разметку в галерею фильмов 49 | 50 | export async function TrendingMovie(currentPage) { 51 | try { 52 | const { results, total_results } = await getMoviesTrending(currentPage); 53 | if (!refs.filmGallery) { 54 | return; 55 | } 56 | refs.filmGallery.innerHTML = createMarkupOneCard(results); 57 | createPagination(total_results, 2, 0, ITEMS_PER_PAGES); 58 | } catch (error) { 59 | console.log(error.message); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/sass/layout/_main.scss: -------------------------------------------------------------------------------- 1 | .section { 2 | padding: 20px 0 40px 0; 3 | 4 | @include tablet { 5 | padding: 60px 0; 6 | } 7 | } 8 | 9 | .film-gallery { 10 | display: flex; 11 | flex-wrap: wrap; 12 | gap: 20px; 13 | 14 | @include tablet { 15 | gap: 32px; 16 | } 17 | 18 | @include desktop { 19 | gap: 32px 16px; 20 | justify-content: flex-start; 21 | } 22 | } 23 | 24 | .film-item { 25 | border-radius: 5px; 26 | background-color: $colorModalBg; 27 | transition: transform 300ms linear, box-shadow 300ms linear; 28 | box-shadow: 0px 3px 0px 0px rgba(0, 0, 0, 0); 29 | cursor: pointer; 30 | 31 | @include tablet { 32 | width: calc((100% - 32px) / 2); 33 | } 34 | 35 | @include desktop() { 36 | width: calc((100% - 32px) / 3); 37 | } 38 | 39 | &:hover, 40 | &:focus { 41 | transform: scale(1.01) rotate(0.5deg); 42 | box-shadow: 0 10px 20px 5px rgba(0, 0, 0, 0.4); 43 | } 44 | } 45 | 46 | .thumb { 47 | border-radius: 5px; 48 | overflow: hidden; 49 | margin-bottom: 10px; 50 | 51 | @include tablet { 52 | margin-bottom: 8px; 53 | width: 336px; 54 | height: 466px; 55 | } 56 | 57 | @include desktop { 58 | margin-bottom: 12px; 59 | width: 395px; 60 | height: 574px; 61 | } 62 | } 63 | 64 | .film-label { 65 | width: 100%; 66 | height: 100%; 67 | object-fit: cover; 68 | } 69 | 70 | .film-title, 71 | .film-genre { 72 | color: $colorTextDark; 73 | font-weight: 500; 74 | line-height: 1.33; 75 | 76 | @include desktop { 77 | font-size: 20px; 78 | line-height: 1.2; 79 | } 80 | } 81 | 82 | .film-wrap { 83 | padding: 5px; 84 | border-radius: 0 0 5px 5px; 85 | } 86 | 87 | .film-genre { 88 | color: $colorTextAccent; 89 | } 90 | 91 | .film-title { 92 | text-transform: uppercase; 93 | } 94 | 95 | body.darkTheme { 96 | background-color: rgba(22, 20, 20, 0.94); 97 | 98 | .film-title { 99 | color: $colorTextLight; 100 | } 101 | 102 | .film-item { 103 | background-color: rgba(22, 20, 20, 0.94); 104 | } 105 | 106 | .film-wrap { 107 | padding: 5px; 108 | background-color: rgba(22, 20, 20, 0.94); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Junk 2 | .DS_Store 3 | .vscode/ 4 | .idea/ 5 | 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # TypeScript v1 declaration files 50 | typings/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Microbundle cache 62 | .rpt2_cache/ 63 | .rts2_cache_cjs/ 64 | .rts2_cache_es/ 65 | .rts2_cache_umd/ 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variables file 77 | .env 78 | .env.test 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .parcel-cache 82 | 83 | # Next.js build output 84 | .next 85 | 86 | # Nuxt.js build / generate output 87 | .nuxt 88 | dist 89 | 90 | # Gatsby files 91 | .cache/ 92 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 93 | # https://nextjs.org/blog/next-9-1#public-directory-support 94 | # public 95 | 96 | # vuepress build output 97 | .vuepress/dist 98 | 99 | # Serverless directories 100 | .serverless/ 101 | 102 | # FuseBox cache 103 | .fusebox/ 104 | 105 | # DynamoDB Local files 106 | .dynamodb/ 107 | 108 | # TernJS port file 109 | .tern-port 110 | -------------------------------------------------------------------------------- /src/sass/components/_logo.scss: -------------------------------------------------------------------------------- 1 | .logo { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | stroke: $colorTextLight; 6 | transition: transform 300ms linear; 7 | 8 | @include tablet { 9 | height: 35px; 10 | gap: 10px; 11 | } 12 | 13 | &:hover { 14 | transform: scale(1.1); 15 | text-shadow: 0 10px 30px rgba(222, 156, 151, 0.3); 16 | } 17 | } 18 | 19 | // Анімація-хвиля для надпису Filmoteka 20 | 21 | .content { 22 | transform: translateY(2px); 23 | display: none; 24 | @include tablet { 25 | display: block; 26 | position: relative; 27 | } 28 | } 29 | 30 | .content span { 31 | color: $colorTextLight; 32 | font-size: 3em; 33 | position: absolute; 34 | left: 80px; 35 | transform: translate(-50%, -50%); 36 | } 37 | 38 | .content span:nth-child(1) { 39 | color: transparent; 40 | -webkit-text-stroke: 2px $colorTextLight; 41 | } 42 | 43 | .content span:nth-child(2) { 44 | color: $colorTextDark; 45 | animation: animate 4s ease-in-out infinite; 46 | } 47 | 48 | @keyframes animate { 49 | 0%, 50 | 100% { 51 | clip-path: polygon( 52 | 0% 45%, 53 | 16% 44%, 54 | 33% 50%, 55 | 54% 60%, 56 | 70% 61%, 57 | 84% 59%, 58 | 100% 52%, 59 | 100% 100%, 60 | 0% 100% 61 | ); 62 | } 63 | 64 | 50% { 65 | clip-path: polygon( 66 | 0% 60%, 67 | 15% 65%, 68 | 34% 66%, 69 | 51% 62%, 70 | 67% 50%, 71 | 84% 45%, 72 | 100% 46%, 73 | 100% 100%, 74 | 0% 100% 75 | ); 76 | } 77 | } 78 | 79 | // Анімація-обертання для логотипу 80 | 81 | .logo-icon { 82 | stroke: inherit; 83 | } 84 | 85 | .logo-icon { 86 | animation: 1s linear 0s normal none infinite running rot; 87 | -webkit-animation: 2s linear 0s normal none infinite running rot; 88 | animation-iteration-count: 1; 89 | } 90 | 91 | @keyframes rot { 92 | 0% { 93 | transform: rotateY(0deg); 94 | } 95 | 96 | 100% { 97 | transform: rotateY(360deg); 98 | } 99 | } 100 | 101 | @-webkit-keyframes rot { 102 | 0% { 103 | transform: rotateY(0deg); 104 | } 105 | 106 | 100% { 107 | transform: rotateY(360deg); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/sass/index.css: -------------------------------------------------------------------------------- 1 | @import '~node_modules/modern-normalize/modern-normalize.css'; 2 | h1, 3 | h2, 4 | h3, 5 | h4, 6 | h5, 7 | h6, 8 | p { 9 | margin: 0; 10 | } 11 | 12 | ul, 13 | ol { 14 | margin: 0; 15 | padding-left: 0; 16 | } 17 | 18 | button { 19 | cursor: pointer; 20 | } 21 | 22 | address { 23 | font-style: normal; 24 | } 25 | 26 | img { 27 | display: block; 28 | max-width: 100%; 29 | height: auto; 30 | width: 100%; 31 | } 32 | 33 | .visually-hidden { 34 | position: absolute; 35 | white-space: nowrap; 36 | width: 1px; 37 | height: 1px; 38 | margin: -1px; 39 | border: 0; 40 | padding: 0; 41 | clip: rect(0 0 0 0); 42 | -webkit-clip-path: inset(100%); 43 | clip-path: inset(100%); 44 | overflow: hidden; 45 | } 46 | 47 | .container { 48 | margin: 0 auto; 49 | padding-left: 20px; 50 | padding-right: 20px; 51 | max-width: 100%; 52 | } 53 | @media screen and (min-width: 480px) { 54 | .container { 55 | max-width: 480px; 56 | } 57 | } 58 | @media screen and (min-width: 768px) { 59 | .container { 60 | max-width: 768px; 61 | padding-left: 32px; 62 | padding-right: 32px; 63 | } 64 | } 65 | @media screen and (min-width: 1280px) { 66 | .container { 67 | max-width: 1280px; 68 | } 69 | } 70 | 71 | .backdrop { 72 | position: fixed; 73 | top: 0; 74 | left: 0; 75 | z-index: 100; 76 | width: 100%; 77 | height: 100%; 78 | background: rgba(94, 99, 99, 0.7); 79 | overflow-y: auto; 80 | transition: opacity 250ms cubic-bezier(0.4, 0, 0.2, 1), visibility 250ms cubic-bezier(0.4, 0, 0.2, 1); 81 | } 82 | 83 | .btn { 84 | width: 100px; 85 | height: 100px; 86 | border-radius: 50%; 87 | position: absolute; 88 | bottom: 10px; 89 | left: 10px; 90 | } 91 | 92 | .modal-container { 93 | padding-left: 20px; 94 | padding-right: 20px; 95 | } 96 | 97 | .modal-movie { 98 | position: absolute; 99 | top: 50%; 100 | left: 50%; 101 | transform: translate(-50%, -50%); 102 | padding: 48px 20px 40px 20px; 103 | margin: 0 auto; 104 | max-width: 280px; 105 | width: calc(100% - 40px); 106 | background-color: #fff; 107 | } 108 | 109 | .movie-thumb { 110 | overflow: hidden; 111 | border-radius: 5px; 112 | } 113 | 114 | .movie-img { 115 | display: block; 116 | width: 240px; 117 | height: 357px; 118 | } 119 | 120 | .library_films-item { 121 | background-color: red; 122 | }/*# sourceMappingURL=index.css.map */ -------------------------------------------------------------------------------- /src/js/queue-local-storage.js: -------------------------------------------------------------------------------- 1 | // в локал сторіджі дані по фільмам бібліотеки треба зберігати як масив обєктів з усіма необхідними властивостями, 2 | // а не масив просто айдішників. 3 | import { queue } from './buttons'; 4 | import { createMarkupOneCard } from './markup-cards'; 5 | import { onClickMovie, modalClose } from './modal-movie'; 6 | import { refs } from './refs'; 7 | import { btnWatched } from './watched-local-storage'; 8 | import { createPagination } from './pagination'; 9 | 10 | export const btnQueue = document.querySelector('.js-queue'); 11 | const libraryList = document.querySelector('.js-library_gallery'); 12 | const imgWatchedPlug = document.querySelector('.no-watched'); 13 | const imgQueuePlug = document.querySelector('.no-queue'); 14 | 15 | const ITEMS_PER_PAGES = 9; 16 | 17 | let firstFunctionRun = 0; 18 | let startElements = 0; 19 | let endElements = ITEMS_PER_PAGES; 20 | 21 | if (!libraryList) { 22 | return; 23 | } 24 | libraryList.addEventListener('click', onClickMovie); 25 | btnQueue.addEventListener('click', () => { 26 | btnWatched.classList.remove('is-active'); 27 | btnQueue.classList.add('is-active'); 28 | }); 29 | 30 | refs.modal.addEventListener('click', onClodeModalClick); 31 | 32 | function onClodeModalClick(e) { 33 | if (e.target === e.currentTarget) { 34 | modalClose(); 35 | } 36 | } 37 | 38 | if (!btnQueue) { 39 | return; 40 | } 41 | 42 | btnQueue.addEventListener('click', markupQueue); 43 | 44 | export function markupQueue(currentPage = 1) { 45 | if (!btnQueue || !btnWatched) { 46 | return; 47 | } 48 | 49 | if (!btnQueue.classList.contains('is-active')) { 50 | return; 51 | } 52 | 53 | btnWatched.style.backgroundColor = ''; 54 | btnWatched.style.border = ''; 55 | btnQueue.style.backgroundColor = '#ff6b02'; 56 | btnQueue.style.border = 'none'; 57 | btnWatched.disabled = false; 58 | btnQueue.disabled = true; 59 | 60 | if (!libraryList) { 61 | return; 62 | } else if (queue.length === 0) { 63 | imgQueuePlug.style.display = 'block'; 64 | } else if (queue.length > 0) { 65 | imgQueuePlug.style.display = 'none'; 66 | } 67 | 68 | imgWatchedPlug.style.display = 'none'; 69 | 70 | // Пагінатор 71 | if (!isNaN(currentPage)) { 72 | startElements = (currentPage - 1) * ITEMS_PER_PAGES; 73 | endElements = startElements + ITEMS_PER_PAGES; 74 | } 75 | const totalElements = queue.length; 76 | const filtredQueue = queue.slice(startElements, endElements); 77 | 78 | libraryList.innerHTML = createMarkupOneCard(filtredQueue); 79 | createPagination(totalElements, 4, 3, ITEMS_PER_PAGES); 80 | 81 | firstFunctionRun = 1; 82 | } 83 | -------------------------------------------------------------------------------- /src/sass/layout/_footer.scss: -------------------------------------------------------------------------------- 1 | .footer-section { 2 | padding-top: 29px; 3 | padding-bottom: 30px; 4 | background-color: #f7f7f7; 5 | 6 | @include tablet { 7 | padding-top: 30px; 8 | padding-bottom: 30px; 9 | } 10 | } 11 | 12 | .footer-container { 13 | margin: 0 auto; 14 | padding-left: 20px; 15 | padding-right: 20px; 16 | display: flex; 17 | align-items: center; 18 | justify-content: center; 19 | 20 | 21 | @media screen and (max-width: 320px) { 22 | width: 100%; 23 | } 24 | 25 | @include mobile { 26 | width: 480px; 27 | } 28 | 29 | @include tablet { 30 | width: 768px; 31 | padding: 0 32px 0 32px; 32 | } 33 | 34 | @include desktop { 35 | width: 1280px; 36 | } 37 | } 38 | 39 | .footer-text { 40 | text-align: center; 41 | color: #545454; 42 | font-weight: 400; 43 | font-size: 14px; 44 | line-height: 1.86; 45 | margin: 0; 46 | 47 | @include tablet { 48 | font-size: 16px; 49 | line-height: 1.19; 50 | } 51 | } 52 | 53 | .footer__gap { 54 | @include tablet { 55 | display: none; 56 | } 57 | } 58 | 59 | .footer__icon { 60 | margin-left: 10px; 61 | margin-right: 10px; 62 | } 63 | // .footer__icon-heart { 64 | // animation: ; 65 | // } 66 | 67 | .footer__link { 68 | border: none; 69 | background-color: transparent; 70 | color: inherit; 71 | text-decoration: underline; 72 | cursor: pointer; 73 | transition: color 250ms cubic-bezier(0.4, 0, 0.2, 1); 74 | } 75 | 76 | .footer__link:hover, 77 | .footer__link:focus { 78 | color: #ff6b08; 79 | } 80 | 81 | // Анимация сердца в футере 82 | 83 | .footer__icon-heart { 84 | -webkit-animation: pulsing 2.5s infinite; 85 | animation: pulsing 2.5s infinite; 86 | 87 | @-webkit-keyframes pulsing { 88 | 0% { 89 | -webkit-transform: scale(2, 2); 90 | transform: scale(2, 2); 91 | } 92 | 93 | 50% { 94 | -webkit-transform: scale(1, 1); 95 | transform: scale(1, 1); 96 | } 97 | 98 | 100% { 99 | -webkit-transform: scale(2, 2); 100 | transform: scale(2, 2); 101 | } 102 | } 103 | 104 | @keyframes pulsing { 105 | 0% { 106 | -webkit-transform: scale(2, 2); 107 | transform: scale(2, 2); 108 | } 109 | 110 | 50% { 111 | -webkit-transform: scale(1, 1); 112 | transform: scale(1, 1); 113 | } 114 | 115 | 100% { 116 | -webkit-transform: scale(2, 2); 117 | transform: scale(2, 2); 118 | } 119 | } 120 | } 121 | 122 | body.darkTheme { 123 | background-color: rgba(22, 20, 20, 0.94); 124 | 125 | .footer-title { 126 | color: $colorTextLight; 127 | } 128 | 129 | .footer-section { 130 | background-color: rgba(22, 20, 20, 0.94); 131 | } 132 | } -------------------------------------------------------------------------------- /src/sass/layout/_header-library.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | position: relative; 3 | padding-top: 40px; 4 | padding-bottom: 62px; 5 | 6 | @include tablet { 7 | padding-bottom: 60px; 8 | } 9 | } 10 | 11 | .header-nav { 12 | display: flex; 13 | justify-content: space-between; 14 | align-items: start; 15 | margin-bottom: 60px; 16 | 17 | @include tablet { 18 | margin-bottom: 40px; 19 | } 20 | } 21 | 22 | .nav-list { 23 | display: flex; 24 | gap: 39px; 25 | } 26 | 27 | .nav-list-item { 28 | font-family: 'Roboto', sans-serif; 29 | font-weight: 500; 30 | font-size: 12px; 31 | line-height: 1.67; 32 | text-transform: uppercase; 33 | color: #ffffff; 34 | position: relative; 35 | &::after { 36 | content: ''; 37 | display: block; 38 | width: 100%; 39 | height: 3px; 40 | background-color: #ff001b; 41 | position: absolute; 42 | bottom: -4px; 43 | transform: scaleX(0); 44 | transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1); 45 | } 46 | &:hover::after, 47 | &:focus::after { 48 | transform: scaleX(1); 49 | } 50 | } 51 | 52 | .header-buttons { 53 | display: flex; 54 | align-items: center; 55 | justify-content: center; 56 | gap: 20px; 57 | 58 | @include tablet { 59 | gap: 32px; 60 | } 61 | 62 | @include desktop { 63 | gap: 16px; 64 | } 65 | } 66 | 67 | .overlay { 68 | // height: 216px; 69 | margin-left: auto; 70 | margin-right: auto; 71 | background-image: linear-gradient(rgba(0, 0, 0, 0.56), rgba(0, 0, 0, 0.56)), 72 | url('../images/header-library/hero-library-mob.jpg'); 73 | 74 | @media (min-device-pixel-ratio: 2), 75 | (-webkit-min-device-pixel-ratio: 2), 76 | (min-resolution: 192dpi), 77 | (min-resolution: 2dppx) { 78 | background-image: url('../images/header-library/hero-library-mob-2x.jpg'); 79 | } 80 | 81 | @include tablet { 82 | background-image: linear-gradient(rgba(0, 0, 0, 0.56), rgba(0, 0, 0, 0.56)), 83 | url('../images/header-library/hero-library-tab.jpg'); 84 | 85 | @media (min-device-pixel-ratio: 2), 86 | (-webkit-min-device-pixel-ratio: 2), 87 | (min-resolution: 192dpi), 88 | (min-resolution: 2dppx) { 89 | background-image: url('../images/header-library/hero-library-tab-2x.jpg'); 90 | } 91 | } 92 | 93 | @include desktop { 94 | background-image: linear-gradient(rgba(0, 0, 0, 0.56), rgba(0, 0, 0, 0.56)), 95 | url('../images/header-library/hero-library.jpg'); 96 | 97 | @media (min-device-pixel-ratio: 2), 98 | (-webkit-min-device-pixel-ratio: 2), 99 | (min-resolution: 192dpi), 100 | (min-resolution: 2dppx) { 101 | background-image: url('../images/header-library/hero-library-2x.jpg'); 102 | } 103 | } 104 | 105 | background-repeat: no-repeat; 106 | background-size: cover; 107 | background-position: center; 108 | } 109 | -------------------------------------------------------------------------------- /src/js/watched-local-storage.js: -------------------------------------------------------------------------------- 1 | // в локал сторіджі дані по фільмам бібліотеки треба зберігати як масив обєктів з усіма необхідними властивостями, 2 | // а не масив просто айдішників. 3 | import { watched } from './buttons'; 4 | import { createMarkupOneCard } from './markup-cards'; 5 | import { 6 | onClickMovie, 7 | onClodeModalClick, 8 | onBtnClickClose, 9 | } from './modal-movie'; 10 | import { refs } from './refs'; 11 | import { btnQueue } from './queue-local-storage'; 12 | import { createPagination } from './pagination'; 13 | 14 | export const btnWatched = document.querySelector('.js-watched'); 15 | const libraryList = document.querySelector('.js-library_gallery'); 16 | const imgWatchedPlug = document.querySelector('.no-watched'); 17 | const imgQueuePlug = document.querySelector('.no-queue'); 18 | 19 | let ITEMS_PER_PAGES = 9; 20 | if (document.documentElement.clientWidth < 768) { 21 | ITEMS_PER_PAGES = 4; 22 | } 23 | if ( 24 | document.documentElement.clientWidth > 767 && 25 | document.documentElement.clientWidth < 1280 26 | ) { 27 | ITEMS_PER_PAGES = 8; 28 | } 29 | 30 | let startElements = 0; 31 | let endElements = ITEMS_PER_PAGES; 32 | 33 | if (!libraryList) { 34 | return; 35 | } 36 | libraryList.addEventListener('click', onClickMovie); 37 | btnWatched.addEventListener('click', () => { 38 | btnWatched.classList.add('is-active'); 39 | btnQueue.classList.remove('is-active'); 40 | }); 41 | refs.modalMovie.addEventListener('click', onBtnClickClose); 42 | refs.modal.addEventListener('click', onClodeModalClick); 43 | 44 | if (!btnWatched) { 45 | return; 46 | } 47 | btnWatched.addEventListener('click', markupWatched); 48 | 49 | export function markupWatched(currentPage = 1) { 50 | if (!btnQueue || !btnWatched) { 51 | return; 52 | } 53 | 54 | if (!btnWatched.classList.contains('is-active')) { 55 | return; 56 | } 57 | 58 | btnQueue.style.backgroundColor = ''; 59 | btnQueue.style.border = ''; 60 | btnWatched.style.backgroundColor = '#ff6b02'; 61 | btnWatched.style.border = 'none'; 62 | btnWatched.disabled = true; 63 | btnQueue.disabled = false; 64 | 65 | if (!libraryList) { 66 | return; 67 | } else if (watched.length === 0) { 68 | imgWatchedPlug.style.display = 'block'; 69 | } else if (watched.length > 0) { 70 | imgWatchedPlug.style.display = 'none'; 71 | } 72 | imgQueuePlug.style.display = 'none'; 73 | 74 | // Пагінатор 75 | if (!isNaN(currentPage)) { 76 | startElements = (currentPage - 1) * ITEMS_PER_PAGES; 77 | endElements = startElements + ITEMS_PER_PAGES; 78 | } 79 | const totalElements = watched.length; 80 | const filtredWatched = watched.slice(startElements, endElements); 81 | 82 | libraryList.innerHTML = createMarkupOneCard(filtredWatched); 83 | 84 | createPagination(totalElements, 3, 2, ITEMS_PER_PAGES); 85 | } 86 | 87 | document.addEventListener('DOMContentLoaded', markupWatched); 88 | document.addEventListener('DOMContentLoaded', () => { 89 | btnWatched.style.backgroundColor = '#ff6b02'; 90 | btnWatched.style.border = 'none'; 91 | }); 92 | -------------------------------------------------------------------------------- /src/sass/layout/_library.scss: -------------------------------------------------------------------------------- 1 | .no-watched { 2 | display: none; 3 | position: relative; 4 | } 5 | 6 | .no-watched-img { 7 | width: 700px; 8 | margin: 0 auto; 9 | flex-direction: column; 10 | } 11 | 12 | .no-watched-text { 13 | text-align: center; 14 | color: #000; 15 | text-shadow: 1px 1px 1px rgb(214, 116, 4); 16 | font-size: 30px; 17 | font-weight: 700; 18 | line-height: 1.16; 19 | 20 | @include tablet { 21 | position: absolute; 22 | top: 30px; 23 | right: 40px; 24 | } 25 | 26 | @include desktop { 27 | right: 270px; 28 | } 29 | } 30 | 31 | .no-watched-btn { 32 | display: block; 33 | margin: 12px auto 0; 34 | box-sizing: border-box; 35 | padding: 14px 36px 12px; 36 | max-width: 205px; 37 | 38 | font-weight: 500; 39 | font-size: 12px; 40 | line-height: 1.33; 41 | 42 | text-align: center; 43 | text-transform: uppercase; 44 | background-color: $colorBtnAccent; 45 | color: $colorBtnLight; 46 | 47 | border: 1px solid $colorBtnAccent; 48 | border-radius: 5px; 49 | cursor: pointer; 50 | transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1); 51 | 52 | &:hover, 53 | &:focus { 54 | color: $colorTextDark; 55 | background-color: $colorBtnLight; 56 | border: 1px solid inherit; 57 | transform: scale(1.02); 58 | } 59 | 60 | @include tablet { 61 | position: absolute; 62 | top: 100px; 63 | right: 40px; 64 | } 65 | 66 | @include desktop { 67 | right: 270px; 68 | } 69 | } 70 | 71 | .no-queue { 72 | display: none; 73 | position: relative; 74 | } 75 | 76 | .no-queue-img { 77 | width: 700px; 78 | margin: 50 auto; 79 | flex-direction: column; 80 | } 81 | 82 | .no-queue-text { 83 | text-align: center; 84 | color: #000; 85 | text-shadow: 1px 1px 1px rgb(214, 116, 4); 86 | font-size: 30px; 87 | font-weight: 700; 88 | line-height: 1.16; 89 | 90 | @include tablet { 91 | position: absolute; 92 | top: -35px; 93 | right: 40px; 94 | } 95 | 96 | @include desktop { 97 | right: 290px; 98 | } 99 | } 100 | 101 | .no-queue-btn { 102 | display: block; 103 | margin: 12px auto 0; 104 | box-sizing: border-box; 105 | padding: 14px 36px 12px; 106 | max-width: 205px; 107 | 108 | font-weight: 500; 109 | font-size: 12px; 110 | line-height: 1.33; 111 | 112 | text-align: center; 113 | text-transform: uppercase; 114 | background-color: $colorBtnAccent; 115 | color: $colorBtnLight; 116 | 117 | border: 1px solid $colorBtnAccent; 118 | border-radius: 5px; 119 | cursor: pointer; 120 | transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1); 121 | 122 | &:hover, 123 | &:focus { 124 | color: $colorTextDark; 125 | background-color: $colorBtnLight; 126 | border: 1px solid inherit; 127 | transform: scale(1.02); 128 | } 129 | 130 | @include tablet { 131 | position: absolute; 132 | top: 40px; 133 | right: 75px; 134 | } 135 | 136 | @include desktop { 137 | right: 335px; 138 | } 139 | } 140 | 141 | .darkText { 142 | color: #fff; 143 | } 144 | -------------------------------------------------------------------------------- /src/sass/components/_arrows_pagination.scss: -------------------------------------------------------------------------------- 1 | .tui-pagination * { 2 | -moz-user-select: none; 3 | -webkit-user-select: none; 4 | -ms-user-select: none; 5 | user-select: none; 6 | } 7 | 8 | .tui-pagination { 9 | display: flex; 10 | justify-content: center; 11 | height: 40px; 12 | margin: 14px 0 60px; 13 | line-height: normal; 14 | text-align: center; 15 | font-size: 0; 16 | } 17 | 18 | .tui-pagination .tui-page-btn { 19 | // cursor: pointer; 20 | display: flex; 21 | position: relative; 22 | padding: 16px; 23 | border-radius: 5px; 24 | justify-content: center; 25 | align-items: center; 26 | background: #ffffffcb; 27 | color: #333; 28 | font-size: 12px; 29 | font-weight: 500; 30 | line-height: 1; 31 | text-decoration: none; 32 | vertical-align: middle; 33 | border: none; 34 | } 35 | 36 | .tui-pagination .tui-prev-is-ellip, 37 | .tui-pagination .tui-next-is-ellip { 38 | @media screen and (max-width: 425px) { 39 | display: none; 40 | } 41 | display: flex; 42 | justify-content: center; 43 | align-items: center; 44 | margin: 0; 45 | padding: 16px; 46 | } 47 | 48 | .tui-pagination .tui-page-btn:hover { 49 | background-color: #f7f7f7; 50 | } 51 | 52 | .tui-pagination .tui-is-selected, 53 | .tui-pagination strong { 54 | height: 40px; 55 | width: 40px; 56 | color: #fff; 57 | background: #ff6b08; 58 | border-color: #ff6b08; 59 | cursor: default; 60 | } 61 | 62 | .tui-pagination .tui-prev, 63 | .tui-pagination .tui-next { 64 | height: 40px; 65 | width: 40px; 66 | background-color: #f7f7f7; 67 | } 68 | 69 | .tui-pagination .tui-ico-ellip::before { 70 | content: ''; 71 | visibility: visible; 72 | } 73 | .tui-pagination .tui-ico-prev, 74 | .tui-pagination .tui-ico-next { 75 | font-size: 0; 76 | } 77 | .tui-pagination .tui-first, 78 | .tui-pagination .tui-last, 79 | .tui-pagination .tui-is-disabled { 80 | display: none; 81 | } 82 | 83 | .tui-pagination .tui-ico-prev::before { 84 | content: ''; 85 | width: 16px; 86 | height: 16px; 87 | background-image: url('../images/svg/arrow-left.svg'); 88 | background-repeat: no-repeat; 89 | background-size: contain; 90 | display: flex; 91 | justify-content: center; 92 | background-size: contain; 93 | visibility: visible; 94 | } 95 | 96 | .tui-pagination .tui-ico-next::before { 97 | content: ''; 98 | display: inline-flex; 99 | width: 16px; 100 | height: 16px; 101 | background-image: url('../images/svg/arrow-right.svg'); 102 | background-repeat: no-repeat; 103 | background-size: contain; 104 | display: flex; 105 | justify-content: center; 106 | background-size: contain; 107 | visibility: visible; 108 | } 109 | .tui-pagination .tui-page-btn .tui-prev { 110 | visibility: hidden; 111 | } 112 | 113 | body.darkTheme { 114 | background-color: rgba(22, 20, 20, 0.94); 115 | 116 | .tui-pagination .tui-page-btn { 117 | background-color: #333; 118 | color: #ffff; 119 | } 120 | 121 | .tui-pagination .tui-page-btn:hover { 122 | background-color: #0000; 123 | } 124 | 125 | .tui-pagination .tui-is-selected, 126 | .tui-pagination strong { 127 | background: #ff6b08; 128 | border-color: #ff6b08; 129 | } 130 | } -------------------------------------------------------------------------------- /src/sass/components/_modal-team.scss: -------------------------------------------------------------------------------- 1 | .backdrop-team { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | 6 | .modal-team { 7 | position: absolute; 8 | top: 50%; 9 | left: 50%; 10 | transform: translate(-50%, -50%); 11 | 12 | overflow: auto; 13 | max-height: 80%; 14 | padding: 20px 30px; 15 | 16 | box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.12), 0px 1px 1px rgba(0, 0, 0), 17 | 0px 2px 1px rgba(0, 0, 0, 0.2); 18 | border-radius: 4px; 19 | 20 | transform: translate(-50%, -50%) scale(1); 21 | transition: transform 250ms linear; 22 | } 23 | 24 | .modal-team-close { 25 | background-color: transparent; 26 | border: none; 27 | border-radius: 50%; 28 | position: absolute; 29 | right: 0px; 30 | top: 0px; 31 | display: flex; 32 | align-items: center; 33 | justify-content: center; 34 | transition: color 250ms linear, transform 250ms linear; 35 | @include tablet { 36 | right: 8px; 37 | top: 8px; 38 | } 39 | 40 | @include desktop { 41 | right: 16px; 42 | top: 16px; 43 | } 44 | } 45 | 46 | .modal-team-close:hover, 47 | .modal-team-close:focus { 48 | transform: rotate(90deg); 49 | fill: $colorTextAccent; 50 | } 51 | 52 | .team-close { 53 | display: inline-block; 54 | } 55 | 56 | .is-hidden { 57 | opacity: 0; 58 | visibility: hidden; 59 | pointer-events: none; 60 | .modal-team { 61 | transform: translate(-50%, -50%) scale(0); 62 | } 63 | } 64 | 65 | .modal-team-title { 66 | margin-bottom: 30px; 67 | text-align: center; 68 | font-size: 18px; 69 | color: $colorTextAccent; 70 | text-transform: uppercase; 71 | @include tablet { 72 | font-size: 25px; 73 | } 74 | @include desktop { 75 | font-size: 30px; 76 | } 77 | } 78 | 79 | .team-list { 80 | display: flex; 81 | flex-wrap: wrap; 82 | gap: 30px; 83 | margin: 0 auto; 84 | justify-content: center; 85 | @include desktop { 86 | } 87 | } 88 | 89 | .team-photo { 90 | display: block; 91 | height: 180px; 92 | width: 120%; 93 | object-fit: cover; 94 | } 95 | 96 | .team-name { 97 | text-align: center; 98 | font-size: 15px; 99 | flex-direction: column; 100 | margin-top: 10px; 101 | } 102 | 103 | .team-position { 104 | text-align: center; 105 | color: $colorTextAccent; 106 | font-weight: 500; 107 | margin-bottom: 10px; 108 | } 109 | 110 | .team-list-item { 111 | width: 120px; 112 | height: auto; 113 | transition: transform 300ms linear, box-shadow 300ms linear; 114 | box-shadow: 0px 3px 0px 0px rgba(0, 0, 0, 0); 115 | border-radius: 5px; 116 | @include desktop { 117 | width: 150px; 118 | } 119 | &:hover, 120 | &:focus { 121 | transform: scale(1.01) rotate(0.5deg); 122 | box-shadow: 0 10px 20px 5px rgba(0, 0, 0, 0.4); 123 | } 124 | } 125 | 126 | .soc-icon { 127 | fill: $colorTexSecondary; 128 | display: inline-flex; 129 | object-fit: contain; 130 | 131 | &:hover, 132 | &:focus { 133 | fill: $colorTextAccent; 134 | } 135 | } 136 | 137 | .soc-links { 138 | display: flex; 139 | gap: 10px; 140 | justify-content: center; 141 | align-items: center; 142 | } 143 | 144 | // .github-info { 145 | // display: flex; 146 | // justify-content: center; 147 | // align-items: center; 148 | // } 149 | 150 | .no-scroll { 151 | overflow: hidden; 152 | } 153 | -------------------------------------------------------------------------------- /src/js/buttons.js: -------------------------------------------------------------------------------- 1 | import { refs } from './refs'; 2 | import { getMovieFullInfo } from './api-fetch'; 3 | import { setWatchedLS, setQueueLS } from './local-storage'; 4 | import { markupWatched } from './watched-local-storage'; 5 | import { markupQueue } from './queue-local-storage'; 6 | 7 | // Массивы в которые пушаться обьекты фильма при нажатии на кнопки "watched" и "queue" 8 | 9 | export let watched = JSON.parse(localStorage.getItem('watched')) || []; 10 | export let queue = JSON.parse(localStorage.getItem('queue')) || []; 11 | 12 | refs.modalMovie.addEventListener('click', onClickBtn); 13 | 14 | // Функция отрабатывает клики по кнопкам на модалке и вызывает соответствующую функцию 15 | 16 | export function onClickBtn(e) { 17 | const { add } = e.target.dataset; 18 | const parent = e.target.closest('div'); 19 | const { id } = parent?.dataset || {}; 20 | 21 | switch (add) { 22 | case 'watched': 23 | addToWatched(id); 24 | break; 25 | 26 | case 'queue': 27 | addToQueue(id); 28 | break; 29 | } 30 | } 31 | 32 | // Функция добаляет обьект фильма в массив "watched" если его там нет, и проверяет если он там есть - удаляет 33 | // P.S. Вызывается по клику кнопки "watched" 34 | 35 | export async function addToWatched(id) { 36 | try { 37 | 38 | const response = await getMovieFullInfo(id); 39 | const results = { 40 | genres: response.genres, 41 | genre_ids: response.genre_ids, 42 | release_date: response.release_date, 43 | first_air_date: response.first_air_date, 44 | poster_path: response.poster_path, 45 | title: response.title, 46 | id: response.id, 47 | }; 48 | 49 | const btnWatchedEl = document.querySelector('[data-add="watched"]'); 50 | const findFilm = watched.find(item => item.id === Number(id)); 51 | const findIndex = watched.findIndex(item => item.id === Number(id)); 52 | 53 | if (findFilm) { 54 | watched.splice(findIndex, 1); 55 | setWatchedLS(watched); 56 | markupWatched(); 57 | btnWatchedEl.textContent = 'Add to watched'; 58 | btnWatchedEl.classList.remove('btn-remove'); 59 | } else { 60 | watched.push(results); 61 | setWatchedLS(watched); 62 | markupWatched(); 63 | btnWatchedEl.textContent = 'Remove watched'; 64 | btnWatchedEl.classList.add('btn-remove'); 65 | } 66 | } catch (error) { 67 | console.log(error.message); 68 | } 69 | } 70 | 71 | // Функция добаляет обьект фильма в массив "queue" если его там нет, и проверяет если он там есть - удаляет 72 | // P.S. Вызывается по клику кнопки "queue" 73 | 74 | export async function addToQueue(id) { 75 | try { 76 | 77 | const response = await getMovieFullInfo(id); 78 | const results = { 79 | genres: response.genres, 80 | genre_ids: response.genre_ids, 81 | release_date: response.release_date, 82 | first_air_date: response.first_air_date, 83 | poster_path: response.poster_path, 84 | title: response.title, 85 | id: response.id, 86 | }; 87 | 88 | const btnQueueEl = document.querySelector('[data-add="queue"]'); 89 | const findFilm = queue.find(item => item.id === Number(id)); 90 | const findIndex = queue.findIndex(item => item.id === Number(id)); 91 | 92 | if (findFilm) { 93 | queue.splice(findIndex, 1); 94 | setQueueLS(queue); 95 | markupQueue(); 96 | btnQueueEl.textContent = 'Add to queue'; 97 | btnQueueEl.classList.remove('btn-remove'); 98 | } else { 99 | queue.push(results); 100 | setQueueLS(queue); 101 | markupQueue(); 102 | btnQueueEl.textContent = 'Remove queue'; 103 | btnQueueEl.classList.add('btn-remove'); 104 | } 105 | } catch (error) { 106 | console.log(error.message); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/sass/layout/_header-main.scss: -------------------------------------------------------------------------------- 1 | .overlay-main { 2 | margin-left: auto; 3 | margin-right: auto; 4 | background-image: url('../images/header-main/hero-home-mob.jpg'), 5 | linear-gradient(rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.9)); 6 | 7 | @media (min-device-pixel-ratio: 2), 8 | (-webkit-min-device-pixel-ratio: 2), 9 | (min-resolution: 192dpi), 10 | (min-resolution: 2dppx) { 11 | background-image: url('../images/header-main/hero-home-mob-2x.jpg'); 12 | } 13 | 14 | @include tablet { 15 | background-image: url('../images/header-main/hero-home-tab.jpg'), 16 | linear-gradient(rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.9)); 17 | 18 | @media (min-device-pixel-ratio: 2), 19 | (-webkit-min-device-pixel-ratio: 2), 20 | (min-resolution: 192dpi), 21 | (min-resolution: 2dppx) { 22 | background-image: url('../images/header-main/hero-home-tab-2x.jpg'); 23 | } 24 | } 25 | 26 | @include desktop { 27 | background-image: url('../images/header-main/hero-home.jpg'), 28 | linear-gradient(rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.9)); 29 | 30 | @media (min-device-pixel-ratio: 2), 31 | (-webkit-min-device-pixel-ratio: 2), 32 | (min-resolution: 192dpi), 33 | (min-resolution: 2dppx) { 34 | background-image: url('../images/header-main/hero-home-2x.jpg'); 35 | } 36 | } 37 | 38 | background-repeat: no-repeat; 39 | background-size: cover; 40 | background-position: center; 41 | } 42 | 43 | .header-form { 44 | display: flex; 45 | justify-content: center; 46 | align-items: center; 47 | } 48 | 49 | .header-form-input { 50 | position: relative; 51 | min-width: 280px; 52 | height: 20px; 53 | border-bottom: 0.5px solid #ffffff; 54 | border-top: 0; 55 | border-right: 0; 56 | border-left: 0; 57 | background-color: transparent; 58 | 59 | font-weight: 400; 60 | font-size: 14px; 61 | line-height: 1.14; 62 | color: #ffffff; 63 | margin-bottom: 4px; 64 | cursor: pointer; 65 | transition: transform 300ms linear, box-shadow 300ms linear; 66 | box-shadow: 0px 3px 0px 0px rgba(0, 0, 0, 0); 67 | 68 | @include tablet { 69 | width: 336px; 70 | } 71 | @include desktop { 72 | width: 394px; 73 | } 74 | 75 | &:hover, 76 | &:focus { 77 | outline: none; 78 | box-shadow: 0 0 0 3px #ff001b; 79 | border-radius: 5px; 80 | 81 | &:focus { 82 | outline: none; 83 | transform: scale(1.1); 84 | box-shadow: 0 10px 20px 5px rgba(222, 156, 151, 0.2); 85 | } 86 | } 87 | } 88 | 89 | .header-form-input::placeholder { 90 | font-family: inherit; 91 | font-weight: 400; 92 | font-size: 14px; 93 | line-height: 1.14; 94 | display: flex; 95 | align-items: center; 96 | color: #ffffff; 97 | margin-bottom: 4px; 98 | } 99 | 100 | .button-search { 101 | position: relative; 102 | width: 24px; 103 | height: 24px; 104 | background: transparent; 105 | border: none; 106 | right: 18px; 107 | display: flex; 108 | align-items: center; 109 | justify-content: center; 110 | transition: transform 250ms linear; 111 | &:hover, 112 | &:focus { 113 | outline: none; 114 | transform: scale(1.5); 115 | } 116 | } 117 | 118 | .search-icon { 119 | position: absolute; 120 | margin-bottom: 6px; 121 | } 122 | 123 | .search-error, 124 | .search-error2 { 125 | font-size: 14px; 126 | line-height: 1.16; 127 | align-items: center; 128 | text-align: center; 129 | color: #ff001b; 130 | justify-content: center; 131 | margin-top: 20px; 132 | display: none; 133 | 134 | justify-content: center; 135 | align-items: center; 136 | } 137 | 138 | .darktheme__button { 139 | position: relative; 140 | border: none; 141 | font-size: 20px; 142 | padding: 0; 143 | background-color: transparent; 144 | width: 30px; 145 | height: 30px; 146 | transition: transform var(--main-effect); 147 | 148 | &:hover { 149 | transform: rotate(-20deg) scale(1.2); 150 | } 151 | 152 | @include tablet { 153 | font-size: 25px; 154 | } 155 | } 156 | 157 | .darkTheme { 158 | transition: background-color 500ms cubic-bezier(0.175, 0.82, 0.165, 1); 159 | color: #ffffff; 160 | background-color: rgba(22, 20, 20, 0.94); 161 | height: 100%; 162 | scroll-behavior: smooth; 163 | } 164 | 165 | .current { 166 | &::before { 167 | content: ''; 168 | display: block; 169 | width: 100%; 170 | height: 3px; 171 | background-color: #ff001b; 172 | position: absolute; 173 | bottom: -4px; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # Parcel template 2 | 3 | This project was created with Parcel. For familiarization and setting additional features [refer to documentation](https://parceljs.org/). 4 | 5 | ## Preparing a new project 6 | 7 | 1. Make sure you have an LTS version of Node.js installed on your computer. 8 | [Download and install](https://nodejs.org/en/) if needed. 9 | 2. Clone this repository. 10 | 3. Change the folder name from `parcel-project-template` to the name of your project. 11 | 4. Create a new empty GitHub repository. 12 | 5. Open the project in VSCode, launch the terminal and link the project to the GitHub repository 13 | [by instructions](https://docs.github.com/en/get-started/getting-started-with-git/managing-remote-repositories#changing-a-remote-repositorys-url). 14 | 6. Install the project's dependencies in the terminal with the `npm install` command. 15 | 7. Start development mode by running the `npm start` command. 16 | 8. Go to [http://localhost:1234](http://localhost:1234) in your browser. 17 | This page will automatically reload after saving changes to the project files. 18 | 19 | ## Files and folders 20 | 21 | - All stylesheet parshas should be in the `src/sass` folder and imported into the page stylesheets. For example, for `index.html` the style file is named `index.scss`. 22 | - Add images to the `src/images` folder. The assembler optimizes them, but only when deploying the production version of the project. All this happens in the cloud so as not to burden your computer, as it can take a long time on weak machines. 23 | 24 | ## Deploy 25 | 26 | To set up a project deployment, you need to perform a few additional steps to set up your repository. Go to the `Settings` tab and in the `Actions` subsection select the `General` item. 27 | 28 | ![GitHub actions settings](./assets/actions-config-step-1.png) 29 | 30 | Scroll the page to the last section, in which make sure the options are selected as in the following image and click `Save`. Without these settings, the build will not have enough rights to automate the deployment process. 31 | 32 | ![GitHub actions settings](./assets/actions-config-step-2.png) 33 | 34 | The production version of the project will be automatically built and deployed to GitHub Pages, in the `gh-pages` branch, every time the `main` branch is updated. For example, after a direct push or an accepted pull request. To do this, you need to edit the `homepage` field and the `build` script in the `package.json` file, replacing `your_username` and `your_repo_name` with your own, and submit the changes to GitHub. 35 | 36 | 37 | ```json 38 | "homepage": "https://your_username.github.io/your_repo_name/", 39 | "scripts": { 40 | "build": "parcel build src/*.html --public-url /your_repo_name/" 41 | }, 42 | ``` 43 | 44 | Next, you need to go to the settings of the GitHub repository (`Settings` > `Pages`) and set the distribution of the production version of files from the `/root` folder of the `gh-pages` branch, if this was not done automatically. 45 | 46 | ![GitHub Pages settings](./assets/repo-settings.png) 47 | 48 | ### Deployment status 49 | 50 | The deployment status of the latest commit is displayed with an icon next to its ID. 51 | 52 | - **Yellow color** - the project is being built and deployed. 53 | - **Green color** - deployment completed successfully. 54 | - **Red color** - an error occurred during linting, build or deployment. 55 | 56 | More detailed information about the status can be viewed by clicking on the icon, and in the drop-down window, follow the link `Details`. 57 | 58 | ![Deployment status](./assets/status.png) 59 | 60 | ### Live page 61 | 62 | After some time, usually a couple of minutes, the live page can be viewed at the address specified in the edited `homepage` property. For example, here is a link to a live version for this repository 63 | [https://goitacademy.github.io/parcel-project-template](https://goitacademy.github.io/parcel-project-template). 64 | 65 | If a blank page opens, make sure there are no errors in the `Console` tab related to incorrect paths to the CSS and JS files of the project (**404**). Most likely you have the wrong value for the `homepage` property or the `build` script in the `package.json` file. 66 | 67 | ## How it works 68 | 69 | ![How it works](./assets/how-it-works.png) 70 | 71 | 1. After each push to the `main` branch of the GitHub repository, a special script (GitHub Action) is launched from the `.github/workflows/deploy.yml` file. 72 | 2. All repository files are copied to the server, where the project is initialized and built before deployment. 73 | 3. If all steps are successful, the built production version of the project files is sent to the `gh-pages` branch. Otherwise, the script execution log will indicate what the problem is. 74 | -------------------------------------------------------------------------------- /src/sass/components/_button.scss: -------------------------------------------------------------------------------- 1 | //HEADER-LIBRARY 2 | .header-button { 3 | box-sizing: border-box; 4 | 5 | font-family: inherit; 6 | font-weight: 500; 7 | font-size: 12px; 8 | line-height: 1.33; 9 | 10 | text-align: center; 11 | text-transform: uppercase; 12 | color: $colorBtnLight; 13 | 14 | border: 1px solid $colorBtnLight; 15 | border-radius: 5px; 16 | cursor: pointer; 17 | background-color: transparent; 18 | 19 | color: #ffffff; 20 | 21 | width: 130px; 22 | height: 44px; 23 | &:hover, 24 | &:focus { 25 | background-color: $colorBtnAccent; 26 | border: none; 27 | } 28 | @include tablet { 29 | width: 152px; 30 | height: 44px; 31 | } 32 | } 33 | 34 | //MODAL BOX 35 | 36 | .lightbox-modal__close-btn { 37 | position: absolute; 38 | top: 8px; 39 | right: 8px; 40 | 41 | width: 30px; 42 | height: 30px; 43 | padding: 8px; 44 | display: flex; 45 | justify-content: center; 46 | align-items: center; 47 | margin: 0; 48 | z-index: 10; 49 | background-color: transparent; 50 | border: none; 51 | color: $colorTextDark; 52 | transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1); 53 | 54 | &:hover, 55 | :focus { 56 | transform: scale(1.2); 57 | } 58 | } 59 | 60 | .lightbox-modal__trailer { 61 | position: absolute; 62 | bottom: 12px; 63 | left: 12px; 64 | 65 | width: 100px; 66 | height: 44px; 67 | padding: 6px; 68 | display: flex; 69 | align-items: center; 70 | justify-content: center; 71 | column-gap: 6px; 72 | border: none; 73 | background-color: $colorBtnAccent; 74 | border-radius: 5px; 75 | text-transform: uppercase; 76 | font-weight: 500; 77 | font-size: 12px; 78 | line-height: 1; 79 | color: $colorModalBg; 80 | transform: scale(1); 81 | transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1); 82 | 83 | &:hover, 84 | &:focus { 85 | transform: scale(1.1); 86 | } 87 | } 88 | 89 | .lightbox-modal__button { 90 | display: inline-flex; 91 | justify-content: center; 92 | align-items: center; 93 | width: 110px; 94 | height: 44px; 95 | margin-right: 20px; 96 | padding: 6px; 97 | border-radius: 5px; 98 | text-transform: uppercase; 99 | background-color: transparent; 100 | font-weight: 500; 101 | font-size: 12px; 102 | line-height: 1.33; 103 | transform: scale(1); 104 | transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1); 105 | 106 | &:hover, 107 | &:focus { 108 | border: none; 109 | background-color: $colorBtnAccent; 110 | color: $colorModalBg; 111 | transform: scale(1.1); 112 | } 113 | } 114 | 115 | .btn-remove { 116 | color: $colorBtnLight; 117 | background-color: $colorBtnAccent; 118 | 119 | &:hover, 120 | &:focus { 121 | background-color: $colorBtnAccent; 122 | border: none; 123 | } 124 | } 125 | 126 | //MAIN PAGE 127 | 128 | .logo-icon { 129 | &:hover, 130 | &:focus { 131 | color: $colorBtnAccent; 132 | } 133 | } 134 | 135 | .nav-title { 136 | &:hover, 137 | &:focus { 138 | color: $colorBtnAccent; 139 | } 140 | } 141 | 142 | .nav-list { 143 | display: flex; 144 | } 145 | 146 | .nav-list-item { 147 | position: relative; 148 | 149 | &:hover, 150 | &:focus { 151 | background-color: $colorBtnAccent; 152 | border: none; 153 | } 154 | } 155 | 156 | .current { 157 | position: relative; 158 | display: block; 159 | 160 | &::after { 161 | content: ''; 162 | position: absolute; 163 | bottom: -10px; 164 | left: 0; 165 | transform: translateY(1px); 166 | display: block; 167 | width: 100%; 168 | height: 3px; 169 | border: none; 170 | background-color: $colorTextAccentCurrent; 171 | transition: width 0.2s ease-in-out; 172 | } 173 | 174 | &:hover::after, 175 | &:focus::after { 176 | width: 0; 177 | } 178 | } 179 | 180 | //PAGINATION 181 | 182 | .page-button { 183 | width: 148px; 184 | height: 42px; 185 | border: 1px solid transparent; 186 | border-radius: 5px; 187 | justify-content: center; 188 | align-items: center; 189 | font-family: inherit; 190 | font-size: 12px; 191 | font-weight: 500; 192 | line-height: 1.33; 193 | transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); 194 | display: inline-flex; 195 | cursor: pointer; 196 | color: $colorBtnLight; 197 | border-color: $colorBtnLight; 198 | background-color: transparent; 199 | 200 | &:first-child { 201 | margin-right: 16px; 202 | } 203 | 204 | &:hover, 205 | &:focus { 206 | background-color: $colorBtnAccent; 207 | border: none; 208 | } 209 | } 210 | 211 | .is-active-library { 212 | background-color: $colorBtnAccent; 213 | border: none; 214 | } 215 | -------------------------------------------------------------------------------- /src/sass/components/_modal-movie.scss: -------------------------------------------------------------------------------- 1 | .modal-movie { 2 | padding: 48px 20px 40px 20px; 3 | margin: 20px auto; 4 | width: 280px; 5 | background-color: $colorModalBg; 6 | position: absolute; 7 | top: 70%; 8 | left: 50%; 9 | transform: translate(-50%, -50%) scale(1); 10 | transition: transform 250ms linear; 11 | border-radius: 5px; 12 | 13 | @include tablet { 14 | top: 50%; 15 | left: 50%; 16 | transform: translate(-50%, -50%); 17 | display: flex; 18 | gap: 68px; 19 | padding: 40px 72px 40px 36px; 20 | margin: 0 auto; 21 | width: 704px; 22 | } 23 | 24 | @include desktop { 25 | width: 806px; 26 | gap: 16px; 27 | padding: 40px 12px; 28 | } 29 | } 30 | 31 | .movie-thumb { 32 | overflow: hidden; 33 | border-radius: 5px; 34 | margin-bottom: 20px; 35 | 36 | @include tablet { 37 | margin-bottom: 0; 38 | } 39 | } 40 | 41 | .movie-wrap { 42 | @include tablet { 43 | max-width: 264px; 44 | } 45 | 46 | @include desktop { 47 | max-width: 391px; 48 | } 49 | } 50 | 51 | .trailer-btn { 52 | padding: 0; 53 | // width: 50px; 54 | // height: 44px; 55 | border: none; 56 | background-color: transparent; 57 | // top: 50px; 58 | } 59 | 60 | .youtube { 61 | display: block; 62 | fill: red; 63 | // margin-left: -15px; 64 | // margin-top: 2px; 65 | @include desktop { 66 | // margin-left: -7px; 67 | } 68 | } 69 | 70 | .movie-img { 71 | border-radius: 5px; 72 | object-fit: cover; 73 | width: 240px; 74 | height: 357px; 75 | 76 | @include tablet { 77 | width: 264px; 78 | height: 374px; 79 | } 80 | 81 | @include desktop { 82 | width: 375px; 83 | height: 478px; 84 | } 85 | } 86 | 87 | .movie-title { 88 | font-weight: 500; 89 | font-size: 20px; 90 | line-height: 1.15; 91 | margin-bottom: 20px; 92 | text-transform: uppercase; 93 | 94 | @include desktop { 95 | font-size: 30px; 96 | } 97 | } 98 | 99 | .movie-list-wrap { 100 | display: flex; 101 | gap: 40px; 102 | margin-bottom: 20px; 103 | 104 | @include desktop { 105 | gap: 80px; 106 | } 107 | } 108 | 109 | .movie-info-list { 110 | display: flex; 111 | flex-direction: column; 112 | gap: 8px; 113 | } 114 | 115 | .movie-info-text { 116 | font-weight: 500; 117 | line-height: 1.33; 118 | color: $colorModalTextSecondary; 119 | } 120 | 121 | .movie-info-value { 122 | font-weight: 500; 123 | line-height: 1.33; 124 | } 125 | 126 | .movie-info-rating { 127 | color: $colorTextLight; 128 | background-color: $colorTextAccent; 129 | padding: 1px 10px; 130 | border-radius: 5px; 131 | } 132 | 133 | .movie-info-slash { 134 | color: $colorModalTextSecondary; 135 | } 136 | 137 | .mobie-about { 138 | margin-bottom: 8px; 139 | font-weight: 500; 140 | font-size: 12px; 141 | line-height: 1.33; 142 | text-transform: uppercase; 143 | } 144 | 145 | .movie-desc { 146 | margin-bottom: 20px; 147 | font-weight: 500; 148 | line-height: 1.67; 149 | 150 | @include tablet { 151 | width: 264px; 152 | } 153 | 154 | @include desktop { 155 | width: 391px; 156 | } 157 | } 158 | 159 | .movie-btn-wrap { 160 | display: flex; 161 | flex-wrap: wrap; 162 | gap: 20px; 163 | 164 | @include tablet { 165 | gap: 15px; 166 | } 167 | } 168 | 169 | .movie-btn { 170 | font-weight: 500; 171 | line-height: 1.33; 172 | text-transform: uppercase; 173 | background-color: $colorModalBg; 174 | border: 1px solid $colorTextDark; 175 | color: $colorTextDark; 176 | border-radius: 5px; 177 | height: 44px; 178 | width: 110px; 179 | } 180 | 181 | .modal-movie-close { 182 | position: absolute; 183 | top: 5px; 184 | right: 3px; 185 | 186 | background-color: transparent; 187 | border: 1px solid transparent; 188 | 189 | transition: color 250ms linear, transform 250ms linear; 190 | } 191 | 192 | .modal-movie-close:hover, 193 | .modal-movie-close:focus { 194 | fill: $colorTextAccent; 195 | transform: rotate(90deg); 196 | } 197 | 198 | // Стили для трейлера 199 | 200 | .basicLightbox { 201 | position: fixed; 202 | top: 0; 203 | left: 0; 204 | z-index: 200; 205 | 206 | width: 100%; 207 | height: 100%; 208 | 209 | background: rgba(0, 0, 0, 0.8); 210 | } 211 | 212 | .modal-trailer { 213 | z-index: 500; 214 | position: absolute; 215 | top: 50%; 216 | left: 50%; 217 | transform: translate(-50%, -50%); 218 | 219 | @media screen and (max-width: 600px) { 220 | width: 90%; 221 | } 222 | } 223 | 224 | .ligthModal { 225 | background-color: $colorModalBg; 226 | color: $colorTextDark; 227 | fill: $colorTextDark; 228 | } 229 | 230 | .darkModal { 231 | background-color: #2a2a2a; 232 | color: #fff; 233 | fill: #fff; 234 | } 235 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Parcel template 2 | 3 | Этот проект был создан при помощи Parcel. Для знакомства и настройки 4 | дополнительных возможностей [обратись к документации](https://parceljs.org/). 5 | 6 | ## Подготовка нового проекта 7 | 8 | 1. Убедись что на компьютере установлена LTS-версия Node.js. 9 | [Скачай и установи](https://nodejs.org/en/) её если необходимо. 10 | 2. Склонируй этот репозиторий. 11 | 3. Измени имя папки с `parcel-project-template` на имя своего проекта. 12 | 4. Создай новый пустой репозиторий на GitHub. 13 | 5. Открой проект в VSCode, запусти терминал и свяжи проект с GitHub-репозиторием 14 | [по инструкции](https://docs.github.com/en/get-started/getting-started-with-git/managing-remote-repositories#changing-a-remote-repositorys-url). 15 | 6. Установи зависимости проекта в терминале командой `npm install` . 16 | 7. Запусти режим разработки, выполнив команду `npm start`. 17 | 8. Перейди в браузере по адресу [http://localhost:1234](http://localhost:1234). 18 | Эта страница будет автоматически перезагружаться после сохранения изменений в 19 | файлах проекта. 20 | 21 | ## Файлы и папки 22 | 23 | - Все паршалы файлов стилей должны лежать в папке `src/sass` и импортироваться в 24 | файлы стилей страниц. Например, для `index.html` файл стилей называется 25 | `index.scss`. 26 | - Изображения добавляй в папку `src/images`. Сборщик оптимизирует их, но только 27 | при деплое продакшн версии проекта. Все это происходит в облаке, чтобы не 28 | нагружать твой компьютер, так как на слабых машинах это может занять много 29 | времени. 30 | 31 | ## Деплой 32 | 33 | Для настройки деплоя проекта необходимо выполнить несколько дополнительных шагов 34 | по настройке твоего репозитория. Зайди во вкладку `Settings` и в подсекции 35 | `Actions` выбери выбери пункт `General`. 36 | 37 | ![GitHub actions settings](./assets/actions-config-step-1.png) 38 | 39 | Пролистай страницу до последней секции, в которой убедись что выбраны опции как 40 | на следующем изображении и нажми `Save`. Без этих настроек у сборки будет 41 | недостаточно прав для автоматизации процесса деплоя. 42 | 43 | ![GitHub actions settings](./assets/actions-config-step-2.png) 44 | 45 | Продакшн версия проекта будет автоматически собираться и деплоиться на GitHub 46 | Pages, в ветку `gh-pages`, каждый раз когда обновляется ветка `main`. Например, 47 | после прямого пуша или принятого пул-реквеста. Для этого необходимо в файле 48 | `package.json` отредактировать поле `homepage` и скрипт `build`, заменив 49 | `your_username` и `your_repo_name` на свои, и отправить изменения на GitHub. 50 | 51 | ```json 52 | "homepage": "https://your_username.github.io/your_repo_name/", 53 | "scripts": { 54 | "build": "parcel build src/*.html --public-url /your_repo_name/" 55 | }, 56 | ``` 57 | 58 | Далее необходимо зайти в настройки GitHub-репозитория (`Settings` > `Pages`) и 59 | выставить раздачу продакшн версии файлов из папки `/root` ветки `gh-pages`, если 60 | это небыло сделано автоматически. 61 | 62 | ![GitHub Pages settings](./assets/repo-settings.png) 63 | 64 | ### Статус деплоя 65 | 66 | Статус деплоя крайнего коммита отображается иконкой возле его идентификатора. 67 | 68 | - **Желтый цвет** - выполняется сборка и деплой проекта. 69 | - **Зеленый цвет** - деплой завершился успешно. 70 | - **Красный цвет** - во время линтинга, сборки или деплоя произошла ошибка. 71 | 72 | Более детальную информацию о статусе можно посмотреть кликнув по иконке, и в 73 | выпадающем окне перейти по ссылке `Details`. 74 | 75 | ![Deployment status](./assets/status.png) 76 | 77 | ### Живая страница 78 | 79 | Через какое-то время, обычно пару минут, живую страницу можно будет посмотреть 80 | по адресу указанному в отредактированном свойстве `homepage`. Например, вот 81 | ссылка на живую версию для этого репозитория 82 | [https://goitacademy.github.io/parcel-project-template](https://goitacademy.github.io/parcel-project-template). 83 | 84 | Если открывается пустая страница, убедись что во вкладке `Console` нет ошибок 85 | связанных с неправильными путями к CSS и JS файлам проекта (**404**). Скорее 86 | всего у тебя неправильное значение свойства `homepage` или скрипта `build` в 87 | файле `package.json`. 88 | 89 | ## Как это работает 90 | 91 | ![How it works](./assets/how-it-works.png) 92 | 93 | 1. После каждого пуша в ветку `main` GitHub-репозитория, запускается специальный 94 | скрипт (GitHub Action) из файла `.github/workflows/deploy.yml`. 95 | 2. Все файлы репозитория копируются на сервер, где проект инициализируется и 96 | проходит сборку перед деплоем. 97 | 3. Если все шаги прошли успешно, собранная продакшн версия файлов проекта 98 | отправляется в ветку `gh-pages`. В противном случае, в логе выполнения 99 | скрипта будет указано в чем проблема. 100 | -------------------------------------------------------------------------------- /README.uk.md: -------------------------------------------------------------------------------- 1 | # Parcel template 2 | 3 | Этот проект был создан при помощи Parcel. Для знакомства и настройки 4 | дополнительных возможностей [обратись к документации](https://parceljs.org/). 5 | 6 | ## Подготовка нового проекта 7 | 8 | 1. Убедись что на компьютере установлена LTS-версия Node.js. 9 | [Скачай и установи](https://nodejs.org/en/) её если необходимо. 10 | 2. Склонируй этот репозиторий. 11 | 3. Измени имя папки с `parcel-project-template` на имя своего проекта. 12 | 4. Создай новый пустой репозиторий на GitHub. 13 | 5. Открой проект в VSCode, запусти терминал и свяжи проект с GitHub-репозиторием 14 | [по инструкции](https://docs.github.com/en/get-started/getting-started-with-git/managing-remote-repositories#changing-a-remote-repositorys-url). 15 | 6. Установи зависимости проекта в терминале командой `npm install` . 16 | 7. Запусти режим разработки, выполнив команду `npm start`. 17 | 8. Перейди в браузере по адресу [http://localhost:1234](http://localhost:1234). 18 | Эта страница будет автоматически перезагружаться после сохранения изменений в 19 | файлах проекта. 20 | 21 | ## Файлы и папки 22 | 23 | - Все паршалы файлов стилей должны лежать в папке `src/sass` и импортироваться в 24 | файлы стилей страниц. Например, для `index.html` файл стилей называется 25 | `index.scss`. 26 | - Изображения добавляй в папку `src/images`. Сборщик оптимизирует их, но только 27 | при деплое продакшн версии проекта. Все это происходит в облаке, чтобы не 28 | нагружать твой компьютер, так как на слабых машинах это может занять много 29 | времени. 30 | 31 | ## Деплой 32 | 33 | Для настройки деплоя проекта необходимо выполнить несколько дополнительных шагов 34 | по настройке твоего репозитория. Зайди во вкладку `Settings` и в подсекции 35 | `Actions` выбери выбери пункт `General`. 36 | 37 | ![GitHub actions settings](./assets/actions-config-step-1.png) 38 | 39 | Пролистай страницу до последней секции, в которой убедись что выбраны опции как 40 | на следующем изображении и нажми `Save`. Без этих настроек у сборки будет 41 | недостаточно прав для автоматизации процесса деплоя. 42 | 43 | ![GitHub actions settings](./assets/actions-config-step-2.png) 44 | 45 | Продакшн версия проекта будет автоматически собираться и деплоиться на GitHub 46 | Pages, в ветку `gh-pages`, каждый раз когда обновляется ветка `main`. Например, 47 | после прямого пуша или принятого пул-реквеста. Для этого необходимо в файле 48 | `package.json` отредактировать поле `homepage` и скрипт `build`, заменив 49 | `your_username` и `your_repo_name` на свои, и отправить изменения на GitHub. 50 | 51 | ```json 52 | "homepage": "https://your_username.github.io/your_repo_name/", 53 | "scripts": { 54 | "build": "parcel build src/*.html --public-url /your_repo_name/" 55 | }, 56 | ``` 57 | 58 | Далее необходимо зайти в настройки GitHub-репозитория (`Settings` > `Pages`) и 59 | выставить раздачу продакшн версии файлов из папки `/root` ветки `gh-pages`, если 60 | это небыло сделано автоматически. 61 | 62 | ![GitHub Pages settings](./assets/repo-settings.png) 63 | 64 | ### Статус деплоя 65 | 66 | Статус деплоя крайнего коммита отображается иконкой возле его идентификатора. 67 | 68 | - **Желтый цвет** - выполняется сборка и деплой проекта. 69 | - **Зеленый цвет** - деплой завершился успешно. 70 | - **Красный цвет** - во время линтинга, сборки или деплоя произошла ошибка. 71 | 72 | Более детальную информацию о статусе можно посмотреть кликнув по иконке, и в 73 | выпадающем окне перейти по ссылке `Details`. 74 | 75 | ![Deployment status](./assets/status.png) 76 | 77 | ### Живая страница 78 | 79 | Через какое-то время, обычно пару минут, живую страницу можно будет посмотреть 80 | по адресу указанному в отредактированном свойстве `homepage`. Например, вот 81 | ссылка на живую версию для этого репозитория 82 | [https://goitacademy.github.io/parcel-project-template](https://goitacademy.github.io/parcel-project-template). 83 | 84 | Если открывается пустая страница, убедись что во вкладке `Console` нет ошибок 85 | связанных с неправильными путями к CSS и JS файлам проекта (**404**). Скорее 86 | всего у тебя неправильное значение свойства `homepage` или скрипта `build` в 87 | файле `package.json`. 88 | 89 | ## Как это работает 90 | 91 | ![How it works](./assets/how-it-works.png) 92 | 93 | 1. После каждого пуша в ветку `main` GitHub-репозитория, запускается специальный 94 | скрипт (GitHub Action) из файла `.github/workflows/deploy.yml`. 95 | 2. Все файлы репозитория копируются на сервер, где проект инициализируется и 96 | проходит сборку перед деплоем. 97 | 3. Если все шаги прошли успешно, собранная продакшн версия файлов проекта 98 | отправляется в ветку `gh-pages`. В противном случае, в логе выполнения 99 | скрипта будет указано в чем проблема. 100 | -------------------------------------------------------------------------------- /README.pl.md: -------------------------------------------------------------------------------- 1 | # Parcel template 2 | 3 | Ten projekt został stworzony przy pomocy Parcel. W celu zapoznania się i 4 | skonfigurowania dodatkowych opcji [zobacz dokumentację](https://parceljs.org/) 5 | 6 | ## Przygotowanie nowego projektu 7 | 8 | 1. Upewnij się, że na komputerze zainstalowana jest wersja LTS Node.js. 9 | [Ściągnij i zainstaluj](https://nodejs.org/en/), jeśli jest taka potrzeba. 10 | 2. Sklonuj to repozytorium. 11 | 3. Zmień nazwę folderu z `parcel-project-template` na nazwę swojego projektu. 12 | 4. Utwórz nowe, puste repozytorium na GitHub. 13 | 5. Otwórz projekt w VSCode, uruchom terminal i zwiąż projekt z repozytorium 14 | GitHub 15 | [zgodnie z instrukcją](https://docs.github.com/en/get-started/getting-started-with-git/managing-remote-repositories#changing-a-remote-repositorys-url). 16 | 6. Utwórz zależność projektu w terminalu przez polecenie `npm install` . 17 | 7. Włącz tryb edycji, wykonując polecenie `npm start`. 18 | 8. Przejdź w przeglądarce pod adres 19 | [http://localhost:1234](http://localhost:1234). Ta strona będzie się 20 | automatycznie odświeżać po dokonaniu zmian w plikach projektu. 21 | 22 | ## Pliki i foldery 23 | 24 | - Wszystkie partiale plików stylów powinny znajdować się w folderze `src/sass` i 25 | importować się w pliki stylów stron. Na przykład dla `index.html` plik stylów 26 | nazywa się `index.scss`. 27 | - Obrazy dodawaj do pliku `src/images`. Moduł zbierający optymalizuje je, ale 28 | tylko przy deploymencie wersji produkcyjnej projektu. Wszystko to zachodzi w 29 | chmurze, aby nie obciążać twojego komputera, ponieważ na słabszym sprzęcie 30 | może to zająć sporo czasu. 31 | 32 | ## Deployment 33 | 34 | Aby skonfigurować deployment projektu należy wykonać kilka dodatkowych kroków 35 | konfigurowania twojego repozytorium. Wejdź w zakładkę `Settings` i w podsekcji 36 | `Actions` wybierz punkt `General`. 37 | 38 | ![GitHub actions settings](./assets/actions-config-step-1.png) 39 | 40 | Przejdź do ostatniej sekcji, w której upewnij się, że wybrane opcje są takie 41 | same jak na następnym obrazku i kliknij `Save`. Bez tych ustawień w module 42 | zbierającym będzie zbyt mało pozwoleń dla automatyzacji procesu deploymentu. 43 | 44 | ![GitHub actions settings](./assets/actions-config-step-2.png) 45 | 46 | Wersja produkcyjna projektu będzie automatycznie gromadzić się i deployować na 47 | GitHub Pages w gałęzi `gh-pages` za każdym razem, gdy aktualizuje się gałąź 48 | `main`. Na przykład po bezpośrednim pushu lub przyjętym pull requeście. W tym 49 | celu niezbędne jest, aby w pliku `package.json` wyedytować pole `homepage` i 50 | skrypt `build`, zamieniając `your_username` i `your_repo_name` na swoje nazwy i 51 | wysłać zmiany na GitHub. 52 | 53 | ```json 54 | "homepage": "https://your_username.github.io/your_repo_name/", 55 | "scripts": { 56 | "build": "parcel build src/*.html --public-url /your_repo_name/" 57 | }, 58 | ``` 59 | 60 | Dalej należy wejść w ustawienia repozytorium GitHub (`Settings` > `Pages`) i 61 | wystawić dystrybucję wersji produkcyjnej z folderu `/root` gałęzi `gh-pages`, 62 | jeśli nie zrobiło się to automatycznie. 63 | 64 | ![GitHub Pages settings](./assets/repo-settings.png) 65 | 66 | ### Status deploymentu 67 | 68 | Status deploymentu ostatniego commitu wyświetla się na ikonie obok jego 69 | identyfikatora. 70 | 71 | - ** Żółty kolor** - wykonuje się zbudowanie i deployment projektu. 72 | - ** Zielony kolor** - deployment zakończył się sukcesem. 73 | - ** Czerwony kolor** - w czasie lintingu, budowania lub deplymentu pojawił się 74 | błąd. 75 | 76 | Więcej informacji o statusie można zobaczyć klikając na ikonkę i w wyskakującym 77 | oknie przejść do odnośnika `Details`. 78 | 79 | ![Deployment status](./assets/status.png) 80 | 81 | ### Działająca strona 82 | 83 | Po jakimś czasie, zazwyczaj kilku minut, działającą stronę będzie można zobaczyć 84 | pod adresem wskazanym w wyedytowanej właściwości `homepage`. Na przykład tu 85 | znajduje się odnośnik do działającej strony dla tego repozytorium 86 | [https://goitacademy.github.io/parcel-project-template](https://goitacademy.github.io/parcel-project-template). 87 | 88 | Jeżeli otwiera się pusta strona, upewnij się, że w zakładce `Console` nie ma 89 | błędów związanych z nieprawidłowymi ścieżkami do plików projektu CSS i JS 90 | (**404**). Najprawdopodobniej wprowadzona została nieprawidłowa wartość 91 | właściwości `homepage` lub skryptu `build` w pliku `package.json`. 92 | 93 | ## Jak to działa 94 | 95 | ![How it works](./assets/how-it-works.png) 96 | 97 | 1. Po każdym pushu w gałęzi `main` repozytorium GitHub, włącza się specjalny 98 | skrypt (GitHub Action) z pliku `.github/workflows/deploy.yml`. 99 | 2. Wszystkie pliki repozytorium kopiują się na serwer, gdzie projekt 100 | inicjalizuje się i buduje przed deploymentem. 101 | 3. Jeżeli wszystkie kroki zakończyły się sukcesem, zbudowana wersja produkcyjna 102 | plików projektu wysyła się w gałąź `gh-pages`. W przeciwnym razie, w logu 103 | wykonania skryptu wskazane zostanie, w czym jest problem. 104 | -------------------------------------------------------------------------------- /README.es.md: -------------------------------------------------------------------------------- 1 | # Parcel template 2 | 3 | Este proyecto fue creado con Parcel. [Consulte la documentación](https://parceljs.org/). 4 | para conocer y personalizar las funciones adicionales. 5 | 6 | ## Preparación de un nuevo proyecto 7 | 8 | 1. Asegúrate de que la versión LTS de Node.js está instalada en tu equipo. 9 | [Descárgala e instálala](https://nodejs.org/en/) si es necesario. 10 | 2. Clona este repositorio. 11 | 3. Cambie el nombre de la carpeta con `parcel-project-template` por el nombre de tu proyecto. 12 | 4. Crea un nuevo repositorio vacío en GitHub. 13 | 5. Abre el proyecto en VSCode, ejecuta el terminal y enlaza el proyecto con el repositorio de GitHub 14 | [según las instrucciones](https://docs.github.com/en/get-started/getting-started-with-git/managing-remote-repositories#changing-a-remote-repositorys-url). 15 | 6. Instala las dependencias del proyecto en el terminal con el comando `npm install`. 16 | 7. Inicia el modo de desarrollo, ejecutando el comando `npm start`. 17 | 8. Ve a la dirección [http://localhost:1234](http://localhost:1234) en tu navegador. 18 | Esta página se recargará automáticamente después de guardar los cambios en los 19 | archivos del proyecto. 20 | 21 | ## Archivos y carpetas 22 | 23 | - Todos los partials de los archivos de estilo deben estar en la carpeta `src/sass` 24 | y ser importados en los archivos de estilos de la página. Por ejemplo, para 25 | `index.html` el archivo de estilos se llama `index.scss`. 26 | - Añade las imágenes a la carpeta `src/images`. El ensamblador las optimizará, 27 | pero sólo cuando se cargue la versión de producción del proyecto. Todo esto 28 | se hace en la nube, para no sobrecargar tu ordenador, ya que puede tardar 29 | mucho en máquinas poco potentes. 30 | 31 | ## Deploy 32 | 33 | Para configurar un proyecto para ser implementado, hay algunos pasos adicionales 34 | para configurar tu repositorio. Ve a la pestaña `Settings` y en la subsección 35 | `Actions`, selecciona la opción `General`. 36 | 37 | ![GitHub actions settings](./assets/actions-config-step-1.png) 38 | 39 | Baja hasta la última sección, asegurándote de que las opciones esten seleccionadas 40 | como en la siguiente imagen, y haz clic en `Save`. Sin estas opciones, la compilación 41 | no tendrá suficientes permisos para automatizar el proceso de implementación. 42 | 43 | ![GitHub actions settings](./assets/actions-config-step-2.png) 44 | 45 | La versión de producción del proyecto se compilará e implementará automáticamente 46 | en GitHub Pages, en la rama `gh-pages`, cada vez que se actualice la rama `main`. 47 | Por ejemplo, después de un push directo o de un pool request aceptado. Para 48 | ello, edita el campo `homepage` y el script `build` en el archivo `package.json`, 49 | sustituyendo `your_username` y `your_repo_name` por los tuyos propios, y envía 50 | los cambios a GitHub. 51 | 52 | ```json 53 | "homepage": "https://your_username.github.io/your_repo_name/", 54 | "scripts": { 55 | "build": "parcel build src/*.html --public-url /your_repo_name/" 56 | }, 57 | ``` 58 | 59 | A continuación, hay que ir a la configuración del repositorio de GitHub 60 | (`Settings` > `Pages`) y seleccionar que la versión de producción de los archivos 61 | se distribuya desde la carpeta `/root` de la rama `gh-pages`, si no se hizo automáticamente. 62 | 63 | ![GitHub Pages settings](./assets/repo-settings.png) 64 | 65 | ### Estado del deploy 66 | 67 | El estado del deploy del último commit se indica con un icono junto a su identificador. 68 | 69 | - **Color amarillo** - el proyecto se está compilando y desplegando. 70 | - **Color verde** - el deploy se completó con éxito. 71 | - **Color rojo** - Se ha producido un error durante el linting, la compilación o el deploy. 72 | 73 | Se puede ver información de estado más detallada haciendo clic en el icono y 74 | en el enlace `Details` de la ventana desplegable. 75 | 76 | ![Deployment status](./assets/status.png) 77 | 78 | ### Página activa 79 | 80 | Después de un tiempo, normalmente un par de minutos, la página activa se puede 81 | ver en la dirección especificada en la propiedad `homepage`. Por ejemplo, aquí 82 | está el enlace a la versión activa de este repositorio. 83 | [https://goitacademy.github.io/parcel-project-template](https://goitacademy.github.io/parcel-project-template). 84 | 85 | Si se abre una página en blanco, asegúrese de que no haya errores en la pestaña 86 | `Console` relacionados con rutas incorrectas a los archivos CSS y JS del proyecto (**404**). 87 | Lo más probable es que tenga un valor incorrecto para la propiedad `homepage` o el 88 | script `build` en el archivo `package.json`. 89 | 90 | ## ¿Cómo funciona? 91 | 92 | ![How it works](./assets/how-it-works.png) 93 | 94 | 1. Después de cada push a la rama `main` del repositorio GitHub, se ejecuta un 95 | script especial (GitHub Action) del archivo `.github/workflows/deploy.yml`. 96 | 2. Todos los archivos del repositorio se copian en el servidor, donde el 97 | proyecto se inicializa y se compila antes de ser desplegado. 98 | 3. Si todos los pasos tienen éxito, la versión de producción compilada de los 99 | archivos del proyecto se envía a la rama `gh-pages`. De lo contrario, el 100 | registro de ejecución del script indicará cuál es el problema. 101 | -------------------------------------------------------------------------------- /src/images/svg/symbol-defs.svg: -------------------------------------------------------------------------------- 1 | 78 | -------------------------------------------------------------------------------- /src/js/modal-movie.js: -------------------------------------------------------------------------------- 1 | import { refs } from './refs'; 2 | import { genresFormatModal } from './geners'; 3 | import { getMovieFullInfo } from './api-fetch'; 4 | import { watched, queue } from './buttons'; 5 | // Функция которую которая покажет модалку при клике по карточке в списке фильмов 6 | 7 | const modalTheme = document.querySelector('.modal-movie'); 8 | 9 | if (!refs.filmGallery) { 10 | return; 11 | } 12 | refs.filmGallery.addEventListener('click', onClickMovie); 13 | 14 | export function onClickMovie(e) { 15 | const body = document.querySelector('body'); 16 | const parent = e.target.closest('li'); 17 | const { id } = parent?.dataset || {}; 18 | if (!id) { 19 | return; 20 | } 21 | showMovieInfo(id); 22 | refs.modal.classList.remove('visually-hidden'); 23 | window.addEventListener('keydown', onCloseModalKey); 24 | body.style.overflow = 'hidden'; 25 | } 26 | 27 | // Функция делает запрос за полной инфой по фильму и отображает её в модалке 28 | 29 | async function showMovieInfo(id) { 30 | try { 31 | const results = await getMovieFullInfo(id); 32 | const geners = genresFormatModal(results.genres).join(', '); 33 | const poster = results.poster_path 34 | ? `https://image.tmdb.org/t/p/w500/${results.poster_path}` 35 | : 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/no-photo/no-photo.png?raw=true'; 36 | refs.modalMovie.innerHTML = renderMarkupModalMovie(results, poster, geners); 37 | const bodyDark = document.body.classList.contains('darkTheme'); 38 | if (bodyDark) { 39 | modalTheme.classList.add('darkModal'); 40 | modalTheme.classList.remove('ligthModal'); 41 | } else { 42 | modalTheme.classList.remove('darkModal'); 43 | modalTheme.classList.add('ligthModal'); 44 | } 45 | } catch (error) { 46 | console.log(error.message); 47 | } 48 | } 49 | 50 | // Функция которую нужно вызвать что бы закрыть модалку 51 | 52 | export function modalClose() { 53 | const body = document.querySelector('body'); 54 | refs.modal.classList.add('visually-hidden'); 55 | window.removeEventListener('keydown', onCloseModalKey); 56 | body.style.overflow = 'auto'; 57 | refs.modalMovie.innerHTML = ''; 58 | } 59 | 60 | // Функция закрытия модалки при нажатии по бекдропу 61 | 62 | refs.modal.addEventListener('click', onClodeModalClick); 63 | 64 | export function onClodeModalClick(e) { 65 | if (e.target === e.currentTarget) { 66 | modalClose(); 67 | } 68 | } 69 | 70 | // Функция которая закрывает модалку при нажатии на крестик 71 | 72 | refs.modalMovie.addEventListener('click', onBtnClickClose); 73 | 74 | export function onBtnClickClose(e) { 75 | const { btn } = e.target.dataset; 76 | if (!btn) { 77 | return; 78 | } 79 | modalClose(); 80 | } 81 | 82 | // Функция закрытия модалки при нажатии на клавишу ESCAPE 83 | 84 | function onCloseModalKey(e) { 85 | if (e.code !== 'Escape') { 86 | return; 87 | } 88 | modalClose(); 89 | } 90 | 91 | // Функция которорая ожидает обьект и рендерит разметку для модалки 92 | 93 | function renderMarkupModalMovie(object, poster, geners) { 94 | let watchedBtnTxt = ''; 95 | let watchedBtnClass = ''; 96 | let queueBtnTxt = ''; 97 | let queueBtnClass = ''; 98 | const findFilmInWatched = watched.find(item => item.id === Number(object.id)); 99 | const findFilmInQueue = queue.find(item => item.id === Number(object.id)); 100 | 101 | if (findFilmInWatched) { 102 | watchedBtnTxt = 'Remove watched'; 103 | watchedBtnClass = 'btn-remove'; 104 | } else { 105 | watchedBtnTxt = 'Add to watched'; 106 | } 107 | if (findFilmInQueue) { 108 | queueBtnTxt = 'Remove queue'; 109 | queueBtnClass = 'btn-remove'; 110 | } else { 111 | queueBtnTxt = 'Add to queue'; 112 | } 113 | 114 | return `
    115 | ${object.title} 120 |
    121 |
    122 |

    ${object.title}

    123 |
    124 |
      125 |
    • 126 |

      Vote / Votes

      127 |
    • 128 |
    • 129 |

      Popularity

      130 |
    • 131 |
    • 132 |

      Original Title

      133 |
    • 134 |
    • 135 |

      Genre

      136 |
    • 137 |
    138 |
      139 |
    • 140 |

      141 | ${object.vote_average.toFixed( 142 | 1 143 | )} 144 | / ${object.vote_count} 145 |

      146 |
    • 147 |
    • 148 |

      ${ 149 | object.popularity.toFixed(1) ?? '-' 150 | }

      151 |
    • 152 |
    • 153 |

      ${object.original_title}

      154 |
    • 155 |
    • 156 |

      ${geners}

      157 |
    • 158 |
    159 |
    160 |

    About

    161 |

    162 | ${object.overview ?? '- - -'} 163 |

    164 |
    165 | 168 | 171 | 178 | 185 |
    186 |
    `; 187 | } 188 | -------------------------------------------------------------------------------- /src/sass/components/_loader.scss: -------------------------------------------------------------------------------- 1 | .wheel-and-hamster { 2 | --dur: 1s; 3 | position: relative; 4 | width: 12em; 5 | height: 12em; 6 | font-size: 8px; 7 | top: 50%; 8 | left: 50%; 9 | transform: translate(-50%, -50%); 10 | } 11 | 12 | .wheel, 13 | .hamster, 14 | .hamster div, 15 | .spoke { 16 | position: absolute; 17 | } 18 | 19 | .wheel, 20 | .spoke { 21 | border-radius: 50%; 22 | top: 0; 23 | left: 0; 24 | width: 100%; 25 | height: 100%; 26 | } 27 | 28 | .wheel { 29 | background: radial-gradient(100% 100% at center, hsla(0, 0%, 60%, 0) 47.8%, hsl(0, 0%, 0%) 48%); 30 | z-index: 2; 31 | } 32 | 33 | .hamster { 34 | animation: hamster var(--dur) ease-in-out infinite; 35 | top: 50%; 36 | left: calc(50% - 3.5em); 37 | width: 7em; 38 | height: 3.75em; 39 | transform: rotate(4deg) translate(-0.8em, 1.85em); 40 | transform-origin: 50% 0; 41 | z-index: 1; 42 | } 43 | 44 | .hamster__head { 45 | animation: hamsterHead var(--dur) ease-in-out infinite; 46 | background: hsl(30, 90%, 55%); 47 | border-radius: 70% 30% 0 100% / 40% 25% 25% 60%; 48 | box-shadow: 0 -0.25em 0 hsl(30, 90%, 80%) inset, 49 | 0.75em -1.55em 0 hsl(30, 90%, 90%) inset; 50 | top: 0; 51 | left: -2em; 52 | width: 2.75em; 53 | height: 2.5em; 54 | transform-origin: 100% 50%; 55 | } 56 | 57 | .hamster__ear { 58 | animation: hamsterEar var(--dur) ease-in-out infinite; 59 | background: hsl(0, 90%, 85%); 60 | border-radius: 50%; 61 | box-shadow: -0.25em 0 hsl(30, 90%, 55%) inset; 62 | top: -0.25em; 63 | right: -0.25em; 64 | width: 0.75em; 65 | height: 0.75em; 66 | transform-origin: 50% 75%; 67 | } 68 | 69 | .hamster__eye { 70 | animation: hamsterEye var(--dur) linear infinite; 71 | background-color: hsl(0, 0%, 0%); 72 | border-radius: 50%; 73 | top: 0.375em; 74 | left: 1.25em; 75 | width: 0.5em; 76 | height: 0.5em; 77 | } 78 | 79 | .hamster__nose { 80 | background: hsl(0, 90%, 75%); 81 | border-radius: 35% 65% 85% 15% / 70% 50% 50% 30%; 82 | top: 0.75em; 83 | left: 0; 84 | width: 0.2em; 85 | height: 0.25em; 86 | } 87 | 88 | .hamster__body { 89 | animation: hamsterBody var(--dur) ease-in-out infinite; 90 | background: hsl(30, 90%, 90%); 91 | border-radius: 50% 30% 50% 30% / 15% 60% 40% 40%; 92 | box-shadow: 0.1em 0.75em 0 hsl(30, 90%, 55%) inset, 93 | 0.15em -0.5em 0 hsl(30, 90%, 80%) inset; 94 | top: 0.25em; 95 | left: 2em; 96 | width: 4.5em; 97 | height: 3em; 98 | transform-origin: 17% 50%; 99 | transform-style: preserve-3d; 100 | } 101 | 102 | .hamster__limb--fr, 103 | .hamster__limb--fl { 104 | clip-path: polygon(0 0, 100% 0, 70% 80%, 60% 100%, 0% 100%, 40% 80%); 105 | top: 2em; 106 | left: 0.5em; 107 | width: 1em; 108 | height: 1.5em; 109 | transform-origin: 50% 0; 110 | } 111 | 112 | .hamster__limb--fr { 113 | animation: hamsterFRLimb var(--dur) linear infinite; 114 | background: linear-gradient(hsl(30, 90%, 80%) 80%, hsl(0, 90%, 75%) 80%); 115 | transform: rotate(15deg) translateZ(-1px); 116 | } 117 | 118 | .hamster__limb--fl { 119 | animation: hamsterFLLimb var(--dur) linear infinite; 120 | background: linear-gradient(hsl(30, 90%, 90%) 80%, hsl(0, 90%, 85%) 80%); 121 | transform: rotate(15deg); 122 | } 123 | 124 | .hamster__limb--br, 125 | .hamster__limb--bl { 126 | border-radius: 0.75em 0.75em 0 0; 127 | clip-path: polygon(0 0, 100% 0, 100% 30%, 70% 90%, 70% 100%, 30% 100%, 40% 90%, 0% 30%); 128 | top: 1em; 129 | left: 2.8em; 130 | width: 1.5em; 131 | height: 2.5em; 132 | transform-origin: 50% 30%; 133 | } 134 | 135 | .hamster__limb--br { 136 | animation: hamsterBRLimb var(--dur) linear infinite; 137 | background: linear-gradient(hsl(30, 90%, 80%) 90%, hsl(0, 90%, 75%) 90%); 138 | transform: rotate(-25deg) translateZ(-1px); 139 | } 140 | 141 | .hamster__limb--bl { 142 | animation: hamsterBLLimb var(--dur) linear infinite; 143 | background: linear-gradient(hsl(30, 90%, 90%) 90%, hsl(0, 90%, 85%) 90%); 144 | transform: rotate(-25deg); 145 | } 146 | 147 | .hamster__tail { 148 | animation: hamsterTail var(--dur) linear infinite; 149 | background: hsl(0, 90%, 85%); 150 | border-radius: 0.25em 50% 50% 0.25em; 151 | box-shadow: 0 -0.2em 0 hsl(0, 90%, 75%) inset; 152 | top: 1.5em; 153 | right: -0.5em; 154 | width: 1em; 155 | height: 0.5em; 156 | transform: rotate(30deg) translateZ(-1px); 157 | transform-origin: 0.25em 0.25em; 158 | } 159 | 160 | .spoke { 161 | animation: spoke var(--dur) linear infinite; 162 | background: radial-gradient(100% 100% at center, hsl(0, 0%, 0%) 4.8%, hsla(0, 0%, 60%, 0) 5%), 163 | linear-gradient(hsla(0, 0%, 55%, 0) 46.9%, hsl(0, 0%, 0%) 47% 52.9%, hsla(0, 0%, 65%, 0) 53%) 50% 50% / 99% 99% no-repeat; 164 | } 165 | 166 | /* Animations */ 167 | @keyframes hamster { 168 | 169 | from, 170 | to { 171 | transform: rotate(4deg) translate(-0.8em, 1.85em); 172 | } 173 | 174 | 50% { 175 | transform: rotate(0) translate(-0.8em, 1.85em); 176 | } 177 | } 178 | 179 | @keyframes hamsterHead { 180 | 181 | from, 182 | 25%, 183 | 50%, 184 | 75%, 185 | to { 186 | transform: rotate(0); 187 | } 188 | 189 | 12.5%, 190 | 37.5%, 191 | 62.5%, 192 | 87.5% { 193 | transform: rotate(8deg); 194 | } 195 | } 196 | 197 | @keyframes hamsterEye { 198 | 199 | from, 200 | 90%, 201 | to { 202 | transform: scaleY(1); 203 | } 204 | 205 | 95% { 206 | transform: scaleY(0); 207 | } 208 | } 209 | 210 | @keyframes hamsterEar { 211 | 212 | from, 213 | 25%, 214 | 50%, 215 | 75%, 216 | to { 217 | transform: rotate(0); 218 | } 219 | 220 | 12.5%, 221 | 37.5%, 222 | 62.5%, 223 | 87.5% { 224 | transform: rotate(12deg); 225 | } 226 | } 227 | 228 | @keyframes hamsterBody { 229 | 230 | from, 231 | 25%, 232 | 50%, 233 | 75%, 234 | to { 235 | transform: rotate(0); 236 | } 237 | 238 | 12.5%, 239 | 37.5%, 240 | 62.5%, 241 | 87.5% { 242 | transform: rotate(-2deg); 243 | } 244 | } 245 | 246 | @keyframes hamsterFRLimb { 247 | 248 | from, 249 | 25%, 250 | 50%, 251 | 75%, 252 | to { 253 | transform: rotate(50deg) translateZ(-1px); 254 | } 255 | 256 | 12.5%, 257 | 37.5%, 258 | 62.5%, 259 | 87.5% { 260 | transform: rotate(-30deg) translateZ(-1px); 261 | } 262 | } 263 | 264 | @keyframes hamsterFLLimb { 265 | 266 | from, 267 | 25%, 268 | 50%, 269 | 75%, 270 | to { 271 | transform: rotate(-30deg); 272 | } 273 | 274 | 12.5%, 275 | 37.5%, 276 | 62.5%, 277 | 87.5% { 278 | transform: rotate(50deg); 279 | } 280 | } 281 | 282 | @keyframes hamsterBRLimb { 283 | 284 | from, 285 | 25%, 286 | 50%, 287 | 75%, 288 | to { 289 | transform: rotate(-60deg) translateZ(-1px); 290 | } 291 | 292 | 12.5%, 293 | 37.5%, 294 | 62.5%, 295 | 87.5% { 296 | transform: rotate(20deg) translateZ(-1px); 297 | } 298 | } 299 | 300 | @keyframes hamsterBLLimb { 301 | 302 | from, 303 | 25%, 304 | 50%, 305 | 75%, 306 | to { 307 | transform: rotate(20deg); 308 | } 309 | 310 | 12.5%, 311 | 37.5%, 312 | 62.5%, 313 | 87.5% { 314 | transform: rotate(-60deg); 315 | } 316 | } 317 | 318 | @keyframes hamsterTail { 319 | 320 | from, 321 | 25%, 322 | 50%, 323 | 75%, 324 | to { 325 | transform: rotate(30deg) translateZ(-1px); 326 | } 327 | 328 | 12.5%, 329 | 37.5%, 330 | 62.5%, 331 | 87.5% { 332 | transform: rotate(10deg) translateZ(-1px); 333 | } 334 | } 335 | 336 | @keyframes spoke { 337 | from { 338 | transform: rotate(0); 339 | } 340 | 341 | to { 342 | transform: rotate(-1turn); 343 | } 344 | } 345 | 346 | .js-spinner { 347 | background: none repeat scroll 0 0 rgb(72, 72, 78); 348 | bottom: 0; 349 | height: 100%; 350 | left: 0; 351 | position: fixed; 352 | right: 0; 353 | top: 0; 354 | width: 100%; 355 | z-index: 9999; 356 | } 357 | -------------------------------------------------------------------------------- /src/js/modal-team.js: -------------------------------------------------------------------------------- 1 | import { BasicLightBox } from 'basiclightbox'; 2 | 3 | const modalTheme = document.querySelector('.modal-team'); 4 | const refs = { 5 | openTeamModalBtn: document.querySelector('[data-modal-open]'), 6 | closeTeamModalBtn: document.querySelector('[data-modal-close]'), 7 | teamModal: document.querySelector('[data-modal]'), 8 | teamList: document.querySelector('.team-list'), 9 | body: document.querySelector('body'), 10 | backdrop: document.querySelector('.backdrop-team'), 11 | }; 12 | 13 | const teamArray = [ 14 | { 15 | name: 'Juliya
    Shapovalenko', 16 | position: 'Team Leader', 17 | photo: 18 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/julia2.jpg?raw=true', 19 | github: 'https://github.com/julieshapo', 20 | linkedin: 'https://www.linkedin.com/in/juliya-shapovalenko-031958127/', 21 | }, 22 | { 23 | name: 'Ruslan
    Podosinovikov', 24 | position: 'Scrum Master', 25 | photo: 26 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/ruslan2.jpg?raw=true', 27 | github: 'https://github.com/nikopolzp', 28 | linkedin: 'https://www.linkedin.com/in/ruslan-podosinovikov-6a259926a/', 29 | }, 30 | { 31 | name: 'Andriy
    Savon', 32 | position: 'Developer', 33 | photo: 34 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/andrew.jpg?raw=true', 35 | github: 'https://github.com/stark1269', 36 | linkedin: 'https://www.linkedin.com/', 37 | }, 38 | { 39 | name: 'Dmytro
    Melnyk', 40 | position: 'Developer', 41 | photo: 42 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/Dmytro.jpg?raw=true', 43 | github: 'https://github.com/Melnyk675', 44 | linkedin: 'https://www.linkedin.com/in/dmytro-melnyk-a2b15121a/', 45 | }, 46 | { 47 | name: 'Vladyslav
    Matsala', 48 | position: 'Developer', 49 | photo: 50 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/vladyslav.jpg?raw=true', 51 | github: 'https://github.com/mentallaboratories', 52 | linkedin: 'https://www.linkedin.com/in/vlad-matsala-797a1a26b', 53 | }, 54 | { 55 | name: 'Igor
    Gromadskiy', 56 | position: 'Developer', 57 | photo: 58 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/igoor.jpg?raw=true', 59 | github: 'https://github.com/IG00RA', 60 | linkedin: 'https://www.linkedin.com/in/igoora', 61 | }, 62 | { 63 | name: 'Denys
    Kondratenko', 64 | position: 'Developer', 65 | photo: 66 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/denys2.jpg?raw=true', 67 | github: 'https://github.com/Denys-Kondratenko', 68 | linkedin: 'https://www.linkedin.com/in/denys-kondratenko-6718a2270/', 69 | }, 70 | { 71 | name: 'Kateryna
    Verveda', 72 | position: 'Developer', 73 | photo: 74 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/katya.jpg?raw=true', 75 | github: 'https://github.com/katushka211', 76 | linkedin: 'https://www.linkedin.com/in/kateryna-verveda-318ab3263/', 77 | }, 78 | { 79 | name: 'Volodymyr
    Zabiyaka', 80 | position: 'Developer', 81 | photo: 82 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/Volodymyr.jpg?raw=true', 83 | github: 'https://github.com/VolZa', 84 | linkedin: 85 | 'https://www.linkedin.com/in/%D0%B2%D0%BE%D0%BB%D0%BE%D0%B4%D0%B8%D0%BC%D0%B8%D1%80-%D0%B7%D0%B0%D0%B1%D1%96%D1%8F%D0%BA%D0%B0-0b27a5202/', 86 | }, 87 | { 88 | name: 'Yaroslav
    Marinich', 89 | position: 'Developer', 90 | photo: 91 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/yaroslav.jpg?raw=true', 92 | github: 'https://github.com/Yaroslav-Marinich', 93 | linkedin: 'https://www.linkedin.com/in/yaroslav-marynych-94b884270/', 94 | }, 95 | { 96 | name: 'Anastasia
    Korotenko', 97 | position: 'Developer', 98 | photo: 99 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/anastasiya_2.jpg?raw=true', 100 | github: 'https://github.com/Anastasiako6', 101 | linkedin: 'https://www.linkedin.com/in/anastasia-korotenko-001671249/', 102 | }, 103 | { 104 | name: 'Vasyl
    Kozhukh', 105 | position: 'Developer', 106 | photo: 107 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/vasyl2.jpg?raw=true', 108 | github: 'https://github.com/Kozhukh-Vasyl', 109 | linkedin: 'https://www.linkedin.com/in/vasyl-kozhukh-85b43b137/', 110 | }, 111 | { 112 | name: 'Inna
    Artysiuk', 113 | position: 'Developer', 114 | photo: 115 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/inna2.jpg?raw=true', 116 | github: 'https://github.com/Inna-Artysiuk', 117 | linkedin: 'https://www.linkedin.com/', 118 | }, 119 | { 120 | name: 'Igor
    Kalchyn', 121 | position: 'Developer', 122 | photo: 123 | 'https://github.com/julieshapo/5th-element-filmoteka/blob/main/src/images/team/igor.jpg?raw=true', 124 | github: 'https://github.com/KalchIgor', 125 | linkedin: 'https://www.linkedin.com/in/igor-kalchin-b4861523b/', 126 | }, 127 | ]; 128 | function createTeamGallery(array) { 129 | const urlIconGithub = './images/svg/favicon.ico.svg#github'; 130 | const markupTeam = array 131 | .map( 132 | item => ` 133 |
  • 134 | ${item.position} 135 |

    ${item.name}

    136 |

    ${item.position}

    137 | 155 |
  • ` 156 | ) 157 | .join(''); 158 | refs.teamList.insertAdjacentHTML('beforeend', markupTeam); 159 | const bodyDark = document.body.classList.contains('darkTheme'); 160 | if (bodyDark) { 161 | modalTheme.classList.add('darkModal'); 162 | modalTheme.classList.remove('ligthModal'); 163 | } else { 164 | modalTheme.classList.remove('darkModal'); 165 | modalTheme.classList.add('ligthModal'); 166 | } 167 | } 168 | 169 | refs.openTeamModalBtn.addEventListener('click', openTeamModal); 170 | refs.closeTeamModalBtn.addEventListener('click', closeTeamModal); 171 | 172 | function openTeamModal() { 173 | refs.body.classList.add('modal-open'); 174 | refs.teamModal.classList.remove('is-hidden'); 175 | refs.body.classList.add('no-scroll'); 176 | } 177 | 178 | function closeTeamModal() { 179 | refs.body.classList.remove('modal-open'); 180 | refs.teamModal.classList.add('is-hidden'); 181 | refs.body.classList.remove('no-scroll'); 182 | } 183 | 184 | // function toggleModal() { 185 | // document.body.classList.toggle('modal-open'); 186 | // refs.teamModal.classList.toggle('is-hidden'); 187 | // refs.body.classList.toggle('no-scroll'); 188 | // } 189 | 190 | refs.openTeamModalBtn.addEventListener('click', onTeamBtnClick); 191 | refs.body.addEventListener('keydown', onEscapeClick); 192 | refs.backdrop.addEventListener('click', onBackdropClick); 193 | 194 | function onTeamBtnClick(event) { 195 | refs.teamModal.classList.remove('visually-hidden'); 196 | refs.teamList.innerHTML = ''; 197 | createTeamGallery(teamArray); 198 | } 199 | 200 | function onEscapeClick(event) { 201 | if (event.code === 'Escape') { 202 | closeTeamModal(); 203 | } 204 | } 205 | 206 | function onBackdropClick(event) { 207 | if (event.target === event.currentTarget) { 208 | closeTeamModal(); 209 | } 210 | } 211 | --------------------------------------------------------------------------------