From 59118400106ced3eb898d9503d74e1d32d8d201c Mon Sep 17 00:00:00 2001 From: damedelion Date: Thu, 19 Dec 2024 15:48:39 +0300 Subject: [PATCH 1/7] new header with mobile layout v0 --- src/shared/config/api.js | 20 ++++----- src/widgets/header/ui/Header.hbs | 65 +++++++++++---------------- src/widgets/header/ui/Header.js | 12 ++++- src/widgets/header/ui/Header.scss | 49 +++++++++++++++++--- src/widgets/sideMenu/index.js | 1 + src/widgets/sideMenu/ui/sideMenu.hbs | 16 +++++++ src/widgets/sideMenu/ui/sideMenu.js | 52 +++++++++++++++++++++ src/widgets/sideMenu/ui/sideMenu.scss | 62 +++++++++++++++++++++++++ 8 files changed, 220 insertions(+), 57 deletions(-) create mode 100644 src/widgets/sideMenu/index.js create mode 100644 src/widgets/sideMenu/ui/sideMenu.hbs create mode 100644 src/widgets/sideMenu/ui/sideMenu.js create mode 100644 src/widgets/sideMenu/ui/sideMenu.scss diff --git a/src/shared/config/api.js b/src/shared/config/api.js index 1747b9b..c5a4ba2 100644 --- a/src/shared/config/api.js +++ b/src/shared/config/api.js @@ -1,21 +1,21 @@ // export const BASE_URL = "http://localhost:8080"; -// export const API_USER_URL = 'http://localhost:8080'; -// export const API_ALBUM_URL = 'http://localhost:8080'; -// export const API_ARTIST_URL = 'http://localhost:8080'; -// export const API_PLAYLIST_URL = 'http://localhost:8080'; -// export const API_TRACK_URL = 'http://localhost:8080'; -// export const API_GENRE_URL = 'http://localhost:8080'; -// export const API_CSAT_URL = 'http://localhost:8080'; -// export const S3_URL = 'http://localhost:8080/storage'; +export const API_USER_URL = 'http://localhost:8080'; +export const API_ALBUM_URL = 'http://localhost:8080'; +export const API_ARTIST_URL = 'http://localhost:8080'; +export const API_PLAYLIST_URL = 'http://localhost:8080'; +export const API_TRACK_URL = 'http://localhost:8080'; +export const API_GENRE_URL = 'http://localhost:8080'; +export const API_CSAT_URL = 'http://localhost:8080'; +export const S3_URL = 'http://localhost:8080/storage'; export const BASE_URL = 'https://nova-music.ru'; -export const API_USER_URL = 'https://nova-music.ru'; +/* export const API_USER_URL = 'https://nova-music.ru'; export const API_ALBUM_URL = 'https://nova-music.ru'; export const API_ARTIST_URL = 'https://nova-music.ru:'; export const API_PLAYLIST_URL = 'https://nova-music.ru'; export const API_TRACK_URL = 'https://nova-music.ru'; export const API_GENRE_URL = 'https://nova-music.ru'; export const API_CSAT_URL = 'https://nova-music.ru'; -export const S3_URL = 'https://nova-music.ru/storage'; +export const S3_URL = 'https://nova-music.ru/storage'; */ diff --git a/src/widgets/header/ui/Header.hbs b/src/widgets/header/ui/Header.hbs index 7fda021..d73f301 100644 --- a/src/widgets/header/ui/Header.hbs +++ b/src/widgets/header/ui/Header.hbs @@ -1,53 +1,40 @@
-
+
+
+ +

+
-
- -

-
{{#if user.isAuthorized}} -

Главная

-

Поиск

+

Главная

+

Поиск

{{/if}}
+
{{#if user.isAuthorized}} -
- -

Выход

-
+
+ +

Выход

+
{{else}} -

-

+

+

{{/if}}
+
+ add_track +
+
+
\ No newline at end of file diff --git a/src/widgets/header/ui/Header.js b/src/widgets/header/ui/Header.js index a8467a7..d77155d 100644 --- a/src/widgets/header/ui/Header.js +++ b/src/widgets/header/ui/Header.js @@ -5,6 +5,8 @@ import { ModalConfirmView } from '../../../widgets/modalConfirm/index.js'; import template from './Header.hbs'; import * as styles from './Header.scss'; import logoLightIcon from '../../../../public/images/icons/logo_light.svg'; +import addIcon from '../../../../public/images/icons/add.svg'; +import { SideMenu } from '../../sideMenu/ui/sideMenu.js'; export class Header { parent; @@ -23,7 +25,10 @@ export class Header { user.image = `${S3_BUCKETS.AVATAR_IMAGES}/${user.image}`; } - this.parent.innerHTML = template({ styles, user, logoLightIcon }); + this.parent.innerHTML = template({ styles, user, logoLightIcon, addIcon }); + + this.sideMenu = new SideMenu() + this.sideMenu.render() this.getElements(); this.bindEvents(); @@ -50,6 +55,11 @@ export class Header { }); eventBus.on('navigate', this.handleNavigation.bind(this)); + + const icon = this.parent.querySelector(".icon"); + if (icon) { + icon.addEventListener("click", this.sideMenu.open); + } } handleSignOutBtn(event) { diff --git a/src/widgets/header/ui/Header.scss b/src/widgets/header/ui/Header.scss index 04acd14..89588de 100644 --- a/src/widgets/header/ui/Header.scss +++ b/src/widgets/header/ui/Header.scss @@ -13,14 +13,15 @@ $header-img-size: 45px; &__container { max-width: 1280px; margin: 0 auto; + padding: 10px; @media (max-width: 1380px) { width: 90%; } - @media (max-width: 768px) { + /* @media (max-width: 768px) { padding: 0 10px; - } + } */ } &__row { @@ -35,31 +36,66 @@ $header-img-size: 45px; } } + &__grid { + display: grid; + width: 100%; + grid-template-columns: min-content min-content 1fr min-content; + column-gap: 20px; + + @media (max-width: 768px) { + grid-template-columns: min-content 1fr min-content; + } + } + &__left_block { display: flex; + grid-column: 2/3; align-items: center; gap: $header-gap; @media (max-width: 768px) { - justify-content: center; - width: 100%; + /* justify-content: center; + width: 100%; */ + display: none; } } &__logo_block { display: flex; + grid-column: 1/2; align-items: center; gap: 5px; } + &__right_button { + display: none; + padding: 10px; + + @media (max-width: 768px) { + display: flex; + grid-column: 3/4; + } + } + + &__spacer { + display: flex; + grid-column: 3/4; + + @media (max-width: 768px) { + grid-column: 2/3; + } + } + &__right_block { display: flex; + grid-column: 4/5; align-items: center; gap: $header-gap; @media (max-width: 768px) { - justify-content: center; - width: 100%; + /* justify-content: center; + width: 100%; */ + display: none; } } @@ -120,7 +156,6 @@ $header-img-size: 45px; } } - &__link, &__link { display: block; color: colors.$white; diff --git a/src/widgets/sideMenu/index.js b/src/widgets/sideMenu/index.js new file mode 100644 index 0000000..d4b7c9c --- /dev/null +++ b/src/widgets/sideMenu/index.js @@ -0,0 +1 @@ +export { SideMenu } from "./ui/sideMenu"; \ No newline at end of file diff --git a/src/widgets/sideMenu/ui/sideMenu.hbs b/src/widgets/sideMenu/ui/sideMenu.hbs new file mode 100644 index 0000000..e1248aa --- /dev/null +++ b/src/widgets/sideMenu/ui/sideMenu.hbs @@ -0,0 +1,16 @@ +{{#if user.isAuthorized}} +

Главная

+

Поиск

+

Профиль

+
Выход
+ +{{else}} +

+

+{{/if}} \ No newline at end of file diff --git a/src/widgets/sideMenu/ui/sideMenu.js b/src/widgets/sideMenu/ui/sideMenu.js new file mode 100644 index 0000000..5ac3691 --- /dev/null +++ b/src/widgets/sideMenu/ui/sideMenu.js @@ -0,0 +1,52 @@ +import { userStore } from "../../../entities/user"; +import template from "./sideMenu.hbs"; +import * as styles from"./sideMenu.scss" + +export class SideMenu { + constructor() { + this.parent = document.querySelector("#root"); // Ensure this targets the correct container + } + + render = () => { + // Ensure this.parent exists before appending + console.log('side menu render') + if (!this.parent) { + console.error("Parent container not found for SideMenu."); + return; + } + + const user = userStore.storage.user; + + // Create and append the side menu + this.elem = document.createElement("div"); + this.elem.classList.add(`${styles["side_menu"]}`); + this.elem.innerHTML = template({ styles, user }); // Render the Handlebars template + document.body.insertBefore(this.elem, this.parent); + + // Bind events + this.bindEvents(); + } + + bindEvents() { + const closeButton = this.elem.querySelector(".closebtn"); + if (closeButton) { + closeButton.addEventListener("click", this.close); + } + } + + open = () => { + if (this.elem) { + this.elem.style.width = "250px"; + } else { + console.error("SideMenu element not found. Ensure render() is called."); + } + }; + + close = () => { + if (this.elem) { + this.elem.style.width = "0"; + } else { + console.error("SideMenu element not found. Ensure render() is called."); + } + }; +} diff --git a/src/widgets/sideMenu/ui/sideMenu.scss b/src/widgets/sideMenu/ui/sideMenu.scss new file mode 100644 index 0000000..b769198 --- /dev/null +++ b/src/widgets/sideMenu/ui/sideMenu.scss @@ -0,0 +1,62 @@ +@use "/src/colors.scss"; + +.side_menu { + height: 100%; // 100% Full-height + width: 0; // 0 width - change this with JavaScript + position: fixed; // Stay in place + z-index: 1; // Stay on top + top: 0; // Stay at the top + right: -20px; + background-color: #111; // Black + overflow-x: hidden; // Disable horizontal scroll + padding: 60px 10px 0px; // Place content 60px from the top + transition: margin-left 0.5s; // 0.5 second transition effect to slide in the sidenav + display: none; + box-sizing: border-box; + + @media (max-width: 768px) { + display: flex; + flex-direction: column; + column-gap: 10px; + justify-content: start; + } + + &>* { + margin-bottom: 10px; + } + + &__link { + display: block; + color: colors.$white; + text-decoration: none; + + &:hover { + opacity: 0.5; + } + + @media (max-width: 1023px) { + font-size: 20px; + } + + @media (max-width: 768px) { + font-size: 18px; + } + } + + .closebtn { + position: absolute; + top: 0; + right: 25px; + font-size: 36px; + margin-left: 50px; + } +} + +.active { + border-bottom: 3px solid colors.$orange; + + @media (max-width: 768px) { + border-bottom: 2px solid colors.$orange; + ; + } +} \ No newline at end of file From f0c251fc51fb33d2c20918add89e5a0165c8bc6a Mon Sep 17 00:00:00 2001 From: damedelion Date: Fri, 20 Dec 2024 18:17:00 +0300 Subject: [PATCH 2/7] fix header mobile layout, modal windows fixes, add playlists carousel, add create btn on track-to-playlist modal --- public/images/icons/heart-outline.svg | 3 + public/images/icons/sidebar-left.svg | 5 + src/index.scss | 6 +- src/pages/feed/ui/Feed.js | 9 +- src/pages/sign-in/ui/SignIn.scss | 3 +- .../artistCarousel/ui/artistCarousel.js | 12 +- .../createPlaylist/ui/createPlaylist.hbs | 44 ++--- .../createPlaylist/ui/createPlaylist.js | 16 +- .../createPlaylist/ui/createPlaylist.scss | 46 +++--- src/widgets/csatWindow/ui/csatWindow.scss | 13 +- src/widgets/footerPlayer/ui/footerPlayer.scss | 52 +++--- src/widgets/header/ui/Header.hbs | 2 +- src/widgets/header/ui/Header.js | 4 +- src/widgets/playlistCarousel/api/api.js | 44 +++++ src/widgets/playlistCarousel/index.js | 2 + .../playlistCarousel/ui/playlistCarousel.hbs | 19 +++ .../playlistCarousel/ui/playlistCarousel.js | 151 ++++++++++++++++++ .../playlistCarousel/ui/playlistCarousel.scss | 48 ++++++ src/widgets/sideMenu/ui/sideMenu.hbs | 15 +- src/widgets/sideMenu/ui/sideMenu.js | 122 ++++++++++---- src/widgets/sideMenu/ui/sideMenu.scss | 18 +-- .../trackInPlaylist/ui/trackInPlaylist.hbs | 34 ++-- .../trackInPlaylist/ui/trackInPlaylist.js | 15 +- .../trackInPlaylist/ui/trackInPlaylist.scss | 63 +++++--- 24 files changed, 569 insertions(+), 177 deletions(-) create mode 100644 public/images/icons/heart-outline.svg create mode 100644 public/images/icons/sidebar-left.svg create mode 100644 src/widgets/playlistCarousel/api/api.js create mode 100644 src/widgets/playlistCarousel/index.js create mode 100644 src/widgets/playlistCarousel/ui/playlistCarousel.hbs create mode 100644 src/widgets/playlistCarousel/ui/playlistCarousel.js create mode 100644 src/widgets/playlistCarousel/ui/playlistCarousel.scss diff --git a/public/images/icons/heart-outline.svg b/public/images/icons/heart-outline.svg new file mode 100644 index 0000000..c24f5b5 --- /dev/null +++ b/public/images/icons/heart-outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/icons/sidebar-left.svg b/public/images/icons/sidebar-left.svg new file mode 100644 index 0000000..89fc825 --- /dev/null +++ b/public/images/icons/sidebar-left.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/index.scss b/src/index.scss index 1ac8199..f759d78 100644 --- a/src/index.scss +++ b/src/index.scss @@ -9,17 +9,17 @@ body { background: linear-gradient(180deg, #172b7b 0%, #020718 100%); background-attachment: fixed; font-family: 'Comfortaa', sans-serif; - font-size: 18px; + font-size: 16px; font-weight: 300; margin: 0; padding: 0; @media (max-width: 1024px) { - font-size: 16px; + font-size: 14px; } @media (max-width: 768px) { - font-size: 14px; + font-size: 12px; } } diff --git a/src/pages/feed/ui/Feed.js b/src/pages/feed/ui/Feed.js index 76ba6fd..512aea8 100644 --- a/src/pages/feed/ui/Feed.js +++ b/src/pages/feed/ui/Feed.js @@ -13,6 +13,7 @@ import { CSATWindow } from "../../../widgets/csatWindow/ui/csatWindow.js"; import { userStore } from "../../../entities/user/index.js"; import { eventBus } from "../../../shared/lib/eventbus.js"; import { csatStore } from "../../../entities/csat/index.js"; +import { PlaylistCarouselAPI, PlaylistCarouselView } from "../../../widgets/playlistCarousel/index.js"; export class FeedPage { /** @@ -59,9 +60,9 @@ export class FeedPage { const artists = await artistCarouselAPI.get(); await artistCarouselView.render(artists); - const playlistListAPI = new PlaylistListAPI(); - const playlistListView = new PlaylistListView(this.pageContent); - const playlists = await playlistListAPI.get(); - await playlistListView.render(playlists.slice(0, 5)); + const playlistCarouselAPI = new PlaylistCarouselAPI(); + const playlistCarouselView = new PlaylistCarouselView(this.pageContent); + const playlists = await playlistCarouselAPI.get(); + await playlistCarouselView.render(playlists); } } diff --git a/src/pages/sign-in/ui/SignIn.scss b/src/pages/sign-in/ui/SignIn.scss index 86b0814..7f8b7fc 100644 --- a/src/pages/sign-in/ui/SignIn.scss +++ b/src/pages/sign-in/ui/SignIn.scss @@ -105,7 +105,8 @@ color: colors.$white; padding: 0px 10px; display: flex; - justify-content: space-between; + justify-content: center; + gap: 10px; align-items: center; @media (max-width: 600px) { diff --git a/src/widgets/artistCarousel/ui/artistCarousel.js b/src/widgets/artistCarousel/ui/artistCarousel.js index 984dace..8efeabd 100644 --- a/src/widgets/artistCarousel/ui/artistCarousel.js +++ b/src/widgets/artistCarousel/ui/artistCarousel.js @@ -59,13 +59,13 @@ export class ArtistCarouselView { } onEvents() { - eventBus.on("carousel:next", this.handleGoNext); - eventBus.on("carousel:prev", this.handleGoPrev); + eventBus.on("artists-carousel:next", this.handleGoNext); + eventBus.on("artists-carousel:prev", this.handleGoPrev); } offEvents() { - eventBus.off("carousel:next", this.handleGoNext); - eventBus.off("carousel:prev", this.handleGoPrev); + eventBus.off("artists-carousel:next", this.handleGoNext); + eventBus.off("artists-carousel:prev", this.handleGoPrev); } addEvents() { @@ -137,11 +137,11 @@ export class ArtistCarouselView { }; handleNextBtn = () => { - eventBus.emit("carousel:next"); + eventBus.emit("artists-carousel:next"); }; handlePrevBtn = () => { - eventBus.emit("carousel:prev"); + eventBus.emit("artists-carousel:prev"); }; destructor() { diff --git a/src/widgets/createPlaylist/ui/createPlaylist.hbs b/src/widgets/createPlaylist/ui/createPlaylist.hbs index a262ade..ef7be75 100644 --- a/src/widgets/createPlaylist/ui/createPlaylist.hbs +++ b/src/widgets/createPlaylist/ui/createPlaylist.hbs @@ -1,23 +1,25 @@ -
-
-

Создать плейлист

- -
-
-
- - -
- - -
-
+
+
+
+

Создать плейлист

+ +
+
+
+ + +
+ + +
+
+
\ No newline at end of file diff --git a/src/widgets/createPlaylist/ui/createPlaylist.js b/src/widgets/createPlaylist/ui/createPlaylist.js index 0a32156..2f77aff 100644 --- a/src/widgets/createPlaylist/ui/createPlaylist.js +++ b/src/widgets/createPlaylist/ui/createPlaylist.js @@ -14,8 +14,9 @@ export class CreatePlaylistModal { * Initializes the CreatePlaylistModal. * */ - constructor(parent) { + constructor(parent, onClose=null) { this.parent = parent ?? document.querySelector('#root'); + this.onClose = onClose; } async render() { @@ -32,14 +33,17 @@ export class CreatePlaylistModal { const cancelButton = this.createPlaylistModal.querySelector(`.${styles['create-playlist-modal__cancel']}`); form.addEventListener('submit', this.handleFormSubmit.bind(this)); - cancelButton.addEventListener('click', this.handleClose.bind(this)); + cancelButton.addEventListener('click', this.handleClose); const closeButton = this.createPlaylistModal.querySelector(`.${styles['create-playlist-modal__close-btn']}`); - closeButton.addEventListener('click', this.handleClose.bind(this)); + closeButton.addEventListener('click', this.handleClose); } - handleClose() { + handleClose = () => { this.createPlaylistModal.remove(); + if (this.onClose) { + this.onClose(); + } } async handleFormSubmit(event) { @@ -61,5 +65,9 @@ export class CreatePlaylistModal { } catch (error) { eventBus.emit('notification', { type: 'error', message: `Failed to create playlist: ${error.message}` }); } + + if (this.onClose) { + this.onClose(); + } } } \ No newline at end of file diff --git a/src/widgets/createPlaylist/ui/createPlaylist.scss b/src/widgets/createPlaylist/ui/createPlaylist.scss index 6ed2957..169aa05 100644 --- a/src/widgets/createPlaylist/ui/createPlaylist.scss +++ b/src/widgets/createPlaylist/ui/createPlaylist.scss @@ -14,31 +14,34 @@ } */ .create-playlist-modal { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - background: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - z-index: 1000; - + + + &__fade { + content: ""; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 9999; + } &__container { - background: colors.$black; - border-radius: 8px; - border: 1px solid colors.$white; + position: fixed; + top: 50%; + left: 50%; padding: 20px; - width: 400px; - text-align: center; + border: 1px solid colors.$white; + border-radius: 10px; + transform: translate(-50%, -50%); + background-color: colors.$black; } &__header { display: flex; justify-content: space-between; - align-items: center; + align-items: start; color: colors.$white; } @@ -46,8 +49,8 @@ border: none; background: none; color: colors.$white; - font-size: 20px; cursor: pointer; + display: inline-block; } &__body { @@ -55,12 +58,12 @@ } &__title { - margin-bottom: 20px; color: colors.$white; } &__label { - display: block; + display: flex; + justify-content: center; margin-bottom: 8px; color: colors.$white; } @@ -78,7 +81,8 @@ &__actions { display: flex; - justify-content: space-between; + justify-content: center; + gap: 15px; margin-top: 20px; } diff --git a/src/widgets/csatWindow/ui/csatWindow.scss b/src/widgets/csatWindow/ui/csatWindow.scss index dbe6b81..9c99956 100644 --- a/src/widgets/csatWindow/ui/csatWindow.scss +++ b/src/widgets/csatWindow/ui/csatWindow.scss @@ -1,11 +1,5 @@ @use "/src/colors.scss"; -html, -body { - font-family: "Comfortaa", sans-serif; - font-size: 16px; -} - button, input { font-family: inherit; @@ -21,9 +15,10 @@ input { } .csat_window { - //font-family: 'Sansation', sans-serif; &__container { + font-family: "Comfortaa", sans-serif; + font-size: 16px; width: 80%; height: 80%; max-width: 400px; @@ -37,7 +32,6 @@ input { flex-direction: column; align-items: center; justify-content: center; - //box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } &__btn_close { @@ -53,7 +47,6 @@ input { &:hover { color: colors.$orange; - transform: scale(1.1); } } @@ -114,4 +107,4 @@ input { border-radius: 9px; color: colors.$white; } -} +} \ No newline at end of file diff --git a/src/widgets/footerPlayer/ui/footerPlayer.scss b/src/widgets/footerPlayer/ui/footerPlayer.scss index e4605ae..c80d1e7 100644 --- a/src/widgets/footerPlayer/ui/footerPlayer.scss +++ b/src/widgets/footerPlayer/ui/footerPlayer.scss @@ -2,6 +2,7 @@ width: 100%; position: fixed; bottom: 0px; + z-index: 10001; &__container { height: 100%; @@ -13,7 +14,7 @@ &__grid { display: grid; - grid-template-columns: 135px fit-content(300px) 1fr 300px; + grid-template-columns: min-content fit-content(300px) 1fr 300px; grid-template-rows: min-content 1fr; width: 100%; } @@ -25,15 +26,23 @@ grid-column: 1 / 5; } - &__buttons_controls { - margin-left: 10px; + &__buttons_controls, + &__buttons_actions { + margin: 0px 12px; display: flex; align-items: center; gap: 12px; grid-row: 2; + } + + &__buttons_controls { grid-column: 1 / 2; } + &__buttons_actions { + grid-column: 3 / 4; + } + &__player_details { display: flex; align-items: center; @@ -42,15 +51,6 @@ grid-column: 2 / 3; } - &__buttons_actions { - margin-left: 10px; - display: flex; - align-items: center; - gap: 12px; - grid-row: 2; - grid-column: 3 / 4; - } - &__volume_slider { margin-right: 10px; display: flex; @@ -128,10 +128,6 @@ } } -.spacer { - height: 200px; -} - .liked_footer_player { opacity: 0.5; @@ -158,21 +154,19 @@ } } - .player { - - &__track_name, - &__track_artist { - font-size: 14px; - } - } - .footer_player { &__player_details { gap: 15px; } + &__buttons_controls, + &__buttons_actions { + margin: 0px 10px; + gap: 10px; + } + &__grid { - grid-template-columns: 160px 1fr 220px; + grid-template-columns: min-content fit-content(300px) 1fr 250px; } &__icon { @@ -211,7 +205,7 @@ } &__grid { - grid-template-columns: 130px 1fr 180px; + grid-template-columns: min-content fit-content(300px) 1fr 200px; } &__buttons { @@ -237,7 +231,11 @@ @media (max-width: 600px) { .footer_player { &__grid { - grid-template-columns: 120px 1fr; + grid-template-columns: min-content fit-content(300px) 1fr; + } + + &__buttons_actions { + justify-content: end; } &__volume_slider { diff --git a/src/widgets/header/ui/Header.hbs b/src/widgets/header/ui/Header.hbs index d73f301..2e179bc 100644 --- a/src/widgets/header/ui/Header.hbs +++ b/src/widgets/header/ui/Header.hbs @@ -31,7 +31,7 @@ {{/if}}
- add_track + add_track
diff --git a/src/widgets/header/ui/Header.js b/src/widgets/header/ui/Header.js index d77155d..d36109a 100644 --- a/src/widgets/header/ui/Header.js +++ b/src/widgets/header/ui/Header.js @@ -5,7 +5,7 @@ import { ModalConfirmView } from '../../../widgets/modalConfirm/index.js'; import template from './Header.hbs'; import * as styles from './Header.scss'; import logoLightIcon from '../../../../public/images/icons/logo_light.svg'; -import addIcon from '../../../../public/images/icons/add.svg'; +import sideBarLeftIcon from '../../../../public/images/icons/sidebar-left.svg'; import { SideMenu } from '../../sideMenu/ui/sideMenu.js'; export class Header { @@ -25,7 +25,7 @@ export class Header { user.image = `${S3_BUCKETS.AVATAR_IMAGES}/${user.image}`; } - this.parent.innerHTML = template({ styles, user, logoLightIcon, addIcon }); + this.parent.innerHTML = template({ styles, user, logoLightIcon, sideBarLeftIcon }); this.sideMenu = new SideMenu() this.sideMenu.render() diff --git a/src/widgets/playlistCarousel/api/api.js b/src/widgets/playlistCarousel/api/api.js new file mode 100644 index 0000000..e13a4d7 --- /dev/null +++ b/src/widgets/playlistCarousel/api/api.js @@ -0,0 +1,44 @@ +import { GET } from "../../../shared/api/index.js"; +import { API_ENDPOINTS } from "../../../shared/lib/index.js"; + +export class PlaylistCarouselAPI { + /** + * Url path to backend api which returns playlists + * + */ + url; + + /** + * Initializes the PlaylistCarouselAPI. + * + */ + constructor() { + this.url = API_ENDPOINTS.GET_POPULAR_PLAYLISTS; + } + + async get() { + try { + const response = await GET(this.url); + if (!response.error) { + return response.data; + } else { + console.log("Error during PlaylistCarousel loading:"); + } + } catch (error) { + console.error(error); + } + } + + async getFavorite() { + try { + const response = await GET(`${API_ENDPOINTS.GET_FAVORITE_PLAYLISTS}`); + if (!response.error) { + return response.data; + } else { + console.log("Error during PlaylistCarousel loading:"); + } + } catch (error) { + console.error(error); + } + } +} diff --git a/src/widgets/playlistCarousel/index.js b/src/widgets/playlistCarousel/index.js new file mode 100644 index 0000000..53dbff0 --- /dev/null +++ b/src/widgets/playlistCarousel/index.js @@ -0,0 +1,2 @@ +export { PlaylistCarouselAPI } from "./api/api"; +export { PlaylistCarouselView } from "./ui/playlistCarousel"; \ No newline at end of file diff --git a/src/widgets/playlistCarousel/ui/playlistCarousel.hbs b/src/widgets/playlistCarousel/ui/playlistCarousel.hbs new file mode 100644 index 0000000..5940209 --- /dev/null +++ b/src/widgets/playlistCarousel/ui/playlistCarousel.hbs @@ -0,0 +1,19 @@ +
+
+
+
+

Популярные артисты

+
+

Показать еще

+
+ +
+
diff --git a/src/widgets/playlistCarousel/ui/playlistCarousel.js b/src/widgets/playlistCarousel/ui/playlistCarousel.js new file mode 100644 index 0000000..ae5e5ec --- /dev/null +++ b/src/widgets/playlistCarousel/ui/playlistCarousel.js @@ -0,0 +1,151 @@ +import { PlaylistView } from "../../../entities/playlist/index.js"; +import { PlaylistCarouselAPI } from "../api/api.js"; +import { eventBus } from "../../../shared/lib/eventbus.js"; +import template from "./playlistCarousel.hbs"; +import * as styles from "./playlistCarousel.scss"; + +export class PlaylistCarouselView { + /** + * The parent HTML element. + * @type {HTMLElement} + */ + parent; + + /** + * Initializes the PlaylistView. + * + */ + constructor(parent, args) { + this.parent = parent ?? document.querySelector("#root"); + this.position = 0; + this.favorite = args?.favorite ?? false; + } + + /** + * Renders the playlist view. + */ + async render(playlists) { + this.playlistCarouselElement = document.createElement("div"); + this.playlistCarouselElement.classList.add("popular_playlists"); + + let titleText; + let showMoreHref; + if (this.favorite) { + showMoreHref = `/more_playlists/favorite`; + titleText = "Любимые плейлисты"; + } else { + showMoreHref = `/more_playlists/popular`; + titleText = "Популярные плейлисты"; + } + + this.playlistCarouselElement.innerHTML = template({ styles, showMoreHref }); + this.parent.appendChild(this.playlistCarouselElement); + + const playlistsBlock = document.getElementById("popular-playlists"); + Array.from(playlists).forEach((playlist) => { + const playlistCarouselItem = document.createElement("div"); + playlistCarouselItem.classList.add("carousel__item"); + const playlistView = new PlaylistView(playlistCarouselItem); + playlistView.render(playlist); + playlistsBlock.appendChild(playlistCarouselItem); + }); + + await this.getElements(); + + this.setTitle(titleText); + + this.onEvents(); + this.addEvents(); + } + + onEvents() { + eventBus.on("playlists-carousel:next", this.handleGoNext); + eventBus.on("playlists-carousel:prev", this.handleGoPrev); + } + + offEvents() { + eventBus.off("playlists-carousel:next", this.handleGoNext); + eventBus.off("playlists-carousel:prev", this.handleGoPrev); + } + + addEvents() { + this.nextBtn.addEventListener("click", this.handleNextBtn); + this.prevBtn.addEventListener("click", this.handlePrevBtn); + + const links = this.parent.querySelectorAll(".popular_playlists__show_more"); + links.forEach((link) => { + link.addEventListener("click", (event) => this.handleLink(event)); + }); + } + + deleteEvents() { + this.nextBtn.removeEventListener("click", this.handleNextBtn); + this.prevBtn.removeEventListener("click", this.handlePrevBtn); + + const links = this.parent.querySelectorAll(".popular_playlists__show_more"); + links.forEach((link) => { + link.removeEventListener("click", (event) => this.handleLink(event)); + }); + } + + async getElements() { + this.carouselInner = this.playlistCarouselElement.querySelector(".carousel__inner"); + this.carouselItems = this.playlistCarouselElement.querySelectorAll(".carousel__item"); + this.carouselArea = this.playlistCarouselElement.querySelector(".carousel__area"); + this.nextBtn = this.playlistCarouselElement.querySelector(".carousel__button_next"); + this.prevBtn = this.playlistCarouselElement.querySelector(".carousel__button_prev"); + this.title = this.playlistCarouselElement.querySelector( + `.${styles["popular_playlists__text"]}>h4`, + ); + + this.itemWidth = this.carouselItems[0]?.offsetWidth ?? 0; + this.carouselAreaWidth = this.carouselArea.offsetWidth; + this.carouselInnerWidth = this.carouselInner.offsetWidth; + + this.maxPosition = -(this.carouselInnerWidth - this.carouselAreaWidth); + } + + setTitle(title) { + this.title.textContent = title; + } + + handleLink(event) { + event.preventDefault(); + const href = event.target.getAttribute("href"); + eventBus.emit("navigate", href); + } + + handleGoNext = () => { + const remainingWidth = + this.carouselInnerWidth + this.position - this.carouselAreaWidth; + + if (remainingWidth > this.itemWidth) { + this.position -= this.itemWidth; + } else if (remainingWidth > 0) { + this.position -= remainingWidth; + } + + this.carouselInner.style.transform = `translateX(${this.position}px)`; + }; + + handleGoPrev = () => { + if (this.position < 0) { + this.position += + this.position + this.itemWidth > 0 ? -this.position : this.itemWidth; + this.carouselInner.style.transform = `translateX(${this.position}px)`; + } + }; + + handleNextBtn = () => { + eventBus.emit("playlists-carousel:next"); + }; + + handlePrevBtn = () => { + eventBus.emit("playlists-carousel:prev"); + }; + + destructor() { + this.deleteEvents(); + this.offEvents(); + } +} diff --git a/src/widgets/playlistCarousel/ui/playlistCarousel.scss b/src/widgets/playlistCarousel/ui/playlistCarousel.scss new file mode 100644 index 0000000..bce1831 --- /dev/null +++ b/src/widgets/playlistCarousel/ui/playlistCarousel.scss @@ -0,0 +1,48 @@ +@use "/src/colors.scss"; + +.popular_playlists { + &__upper_wrapper { + padding: 0 10px; + display: flex; + justify-content: space-between; + } + + &__text { + display: flex; + justify-content: space-between; + align-items: center; + color: colors.$white; + } + + &__list { + margin-top: 10px; + display: flex; + justify-content: space-around; + } +} + +// @media (max-width: 1380px) { +// .popular_artists { +// &__block { +// width: 90%; +// } +// } +// } + +// @media (max-width: 768px) { +// .popular_artists { +// &__show_more, +// &__text { +// font-size: 19px; +// } +// } +// } + +// @media (max-width: 480px) { +// .popular_artists { +// &__show_more, +// &__text { +// font-size: 16px; +// } +// } +// } \ No newline at end of file diff --git a/src/widgets/sideMenu/ui/sideMenu.hbs b/src/widgets/sideMenu/ui/sideMenu.hbs index e1248aa..c234532 100644 --- a/src/widgets/sideMenu/ui/sideMenu.hbs +++ b/src/widgets/sideMenu/ui/sideMenu.hbs @@ -1,16 +1,19 @@ +
+

×

+
{{#if user.isAuthorized}}

Главная

+ href="/">Главная

Поиск

+ href="/search">Поиск

Профиль

+ href="/profiles/{{user.username}}">Профиль
Выход
+ id="side_menu_logout_link" href="/signin">Выход {{else}}

+ id="side_menu_signin_link" href="/signin">Войти

+ id="side_menu_signup_link" href="/signup">Регистрация {{/if}} \ No newline at end of file diff --git a/src/widgets/sideMenu/ui/sideMenu.js b/src/widgets/sideMenu/ui/sideMenu.js index 5ac3691..01da163 100644 --- a/src/widgets/sideMenu/ui/sideMenu.js +++ b/src/widgets/sideMenu/ui/sideMenu.js @@ -1,52 +1,118 @@ import { userStore } from "../../../entities/user"; +import { eventBus, handleLink } from "../../../shared/lib"; +import { ModalConfirmView } from "../../modalConfirm"; import template from "./sideMenu.hbs"; -import * as styles from"./sideMenu.scss" +import * as styles from "./sideMenu.scss" export class SideMenu { constructor() { - this.parent = document.querySelector("#root"); // Ensure this targets the correct container + this.root = document.querySelector("#root"); // Ensure this targets the correct container } - render = () => { - // Ensure this.parent exists before appending - console.log('side menu render') - if (!this.parent) { - console.error("Parent container not found for SideMenu."); - return; - } + render() { + const existed = document.querySelectorAll(`.${styles["side_menu"]}`); + existed.forEach((elem) => elem.remove()); const user = userStore.storage.user; // Create and append the side menu - this.elem = document.createElement("div"); - this.elem.classList.add(`${styles["side_menu"]}`); - this.elem.innerHTML = template({ styles, user }); // Render the Handlebars template - document.body.insertBefore(this.elem, this.parent); + this.sideMenuElement = document.createElement("div"); + this.sideMenuElement.classList.add(`${styles["side_menu"]}`); + this.sideMenuElement.innerHTML = template({ styles, user }); + document.body.insertBefore(this.sideMenuElement, this.root); - // Bind events - this.bindEvents(); + this.addEvents(); + this.switchActiveNavlink(window.location.pathname); } - bindEvents() { - const closeButton = this.elem.querySelector(".closebtn"); - if (closeButton) { - closeButton.addEventListener("click", this.close); + addEvents() { + const closeButton = this.sideMenuElement.querySelector(`.${styles["side_menu__button_close"]}>h2`); + closeButton.addEventListener("click", this.close); + + const logoutLink = this.sideMenuElement.querySelector('#side_menu_logout_link'); + if (logoutLink) { + logoutLink.addEventListener('click', this.handleSignOutBtn); } + + const links = this.sideMenuElement.querySelectorAll('.navlink'); + links.forEach((link) => { + link.addEventListener('click', handleLink); + }); + + eventBus.on('navigate', this.handleNavigation); } open = () => { - if (this.elem) { - this.elem.style.width = "250px"; - } else { - console.error("SideMenu element not found. Ensure render() is called."); - } + this.sideMenuElement.style.width = "200px"; }; close = () => { - if (this.elem) { - this.elem.style.width = "0"; - } else { - console.error("SideMenu element not found. Ensure render() is called."); + this.sideMenuElement.style.width = "0"; + }; + + handleSignOutBtn = (event) => { + event.preventDefault(); + const modalConfirmView = new ModalConfirmView( + null, + 'Вы уверены, что хотите выйти?', + this.handleSignOut, + ); + modalConfirmView.render(); + } + + async handleSignOut() { + try { + await userStore.signOut(); + eventBus.emit('hidePlayer'); + eventBus.emit('navigate', '/signin'); + } catch (error) { + console.error('unable to sign out', error); } + } + + onEvents() { + eventBus.on('signInSuccess', this.onSignInSuccess); + eventBus.on('signUpSuccess', this.onSignUpSuccess); + eventBus.on('signOutSuccess', this.onSignOutSuccess); + eventBus.on('unauthorized', this.onSignOutSuccess); + } + + offEvents() { + eventBus.off('signInSuccess', this.onSignInSuccess); + eventBus.off('signUpSuccess', this.onSignUpSuccess); + eventBus.off('signOutSuccess', this.onSignOutSuccess); + eventBus.off('unauthorized', this.onSignOutSuccess); + } + + onSignInSuccess = () => { + this.render(); + }; + + onSignUpSuccess = () => { + this.render(); + }; + + onSignOutSuccess = () => { + this.render(); }; + + destructor() { + this.offEvents(); + } + + handleNavigation = (href) => { + this.switchActiveNavlink(href); + this.close(); + } + + switchActiveNavlink(href) { + let navlinks = document.querySelectorAll('.navlink_switch'); + navlinks.forEach((navlink) => { + if (navlink.getAttribute('href') == href) { + navlink.classList.add(styles.active); + } else { + navlink.classList.remove(styles.active); + } + }); + } } diff --git a/src/widgets/sideMenu/ui/sideMenu.scss b/src/widgets/sideMenu/ui/sideMenu.scss index b769198..8cfc383 100644 --- a/src/widgets/sideMenu/ui/sideMenu.scss +++ b/src/widgets/sideMenu/ui/sideMenu.scss @@ -4,12 +4,12 @@ height: 100%; // 100% Full-height width: 0; // 0 width - change this with JavaScript position: fixed; // Stay in place - z-index: 1; // Stay on top + z-index: 10000; // Stay on top top: 0; // Stay at the top right: -20px; - background-color: #111; // Black + background-color: colors.$black; // Black overflow-x: hidden; // Disable horizontal scroll - padding: 60px 10px 0px; // Place content 60px from the top + padding: 0px 10px; // Place content 60px from the top transition: margin-left 0.5s; // 0.5 second transition effect to slide in the sidenav display: none; box-sizing: border-box; @@ -43,12 +43,12 @@ } } - .closebtn { - position: absolute; - top: 0; - right: 25px; - font-size: 36px; - margin-left: 50px; + &__button_close { + display: flex; + height: 55px; + justify-content: start; + align-items: center; + color: colors.$white; } } diff --git a/src/widgets/trackInPlaylist/ui/trackInPlaylist.hbs b/src/widgets/trackInPlaylist/ui/trackInPlaylist.hbs index 318cef5..1da7933 100644 --- a/src/widgets/trackInPlaylist/ui/trackInPlaylist.hbs +++ b/src/widgets/trackInPlaylist/ui/trackInPlaylist.hbs @@ -1,13 +1,23 @@ -
-
-

Выберите плейлист

- +
+
+
+

Выберите плейлист

+ +
+
+ {{#each playlists}} +
+ {{this.name}} +
+ {{/each}} +
+
+ +
-
- {{#each playlists}} -
- {{this.name}} -
- {{/each}} -
-
+
\ No newline at end of file diff --git a/src/widgets/trackInPlaylist/ui/trackInPlaylist.js b/src/widgets/trackInPlaylist/ui/trackInPlaylist.js index 70cf664..fdfd96f 100644 --- a/src/widgets/trackInPlaylist/ui/trackInPlaylist.js +++ b/src/widgets/trackInPlaylist/ui/trackInPlaylist.js @@ -4,6 +4,7 @@ import { UserPlaylistsAPI } from '../../userPlaylists/index.js'; import { TrackInPlaylistAPI } from '../api/api.js'; import template from './trackInPlaylist.hbs'; import * as styles from './trackInPlaylist.scss'; +import { CreatePlaylistModal } from '../../createPlaylist/index.js'; export class TrackInPlaylistModal { /** @@ -19,7 +20,7 @@ export class TrackInPlaylistModal { async render() { this.modal = document.createElement('div'); - this.modal.classList.add(styles['track-in-playlist-modal-container']); + this.modal.classList.add(styles['track-in-playlist-modal']); this.parent.appendChild(this.modal); try { @@ -41,6 +42,9 @@ export class TrackInPlaylistModal { playlistLinks.forEach((link) => { link.addEventListener('click', (event) => this.handleLink(event)); }); + + const createButton = this.modal.querySelector(`.${styles["track-in-playlist-modal__create-btn"]}`); + createButton.addEventListener('click', this.handleCreate) } handleClose = () => { @@ -64,6 +68,15 @@ export class TrackInPlaylistModal { eventBus.emit('navigate', href); } + handleCreate = async () => { + this.modal.remove(); + const createPlaylistModal = new CreatePlaylistModal(null, async () => { + const trackInPlaylistModal = new TrackInPlaylistModal(this.parent, this.trackId); + await trackInPlaylistModal.render(); + }); + await createPlaylistModal.render(); + } + removeEventListeners() { const closeButton = this.modal.querySelector(`.${styles['track-in-playlist-modal__close-btn']}`); closeButton.removeEventListener('click', this.handleClose.bind(this)); diff --git a/src/widgets/trackInPlaylist/ui/trackInPlaylist.scss b/src/widgets/trackInPlaylist/ui/trackInPlaylist.scss index 7d68293..e4bde02 100644 --- a/src/widgets/trackInPlaylist/ui/trackInPlaylist.scss +++ b/src/widgets/trackInPlaylist/ui/trackInPlaylist.scss @@ -1,31 +1,35 @@ @use "/src/colors.scss"; -.track-in-playlist-modal-container { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - background: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - z-index: 1000; -} - .track-in-playlist-modal { - background: colors.$black; - border-radius: 8px; - border: 1px solid colors.$white; - padding: 20px; - width: 400px; - text-align: center; + + &__fade { + content: ""; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 9999; + } + + &__container { + position: fixed; + top: 50%; + left: 50%; + padding: 20px; + border: 1px solid colors.$white; + border-radius: 10px; + transform: translate(-50%, -50%); + background-color: colors.$black; + } &__header { color: colors.$white; display: flex; - justify-content: space-between; + justify-content: center; align-items: center; + gap: 30px; } &__close-btn { @@ -39,7 +43,8 @@ } &__playlist-link { - display: block; + display: flex; + justify-content: center; padding: 10px; margin: 5px 0; border: 1px solid colors.$white; @@ -47,4 +52,20 @@ color: colors.$white; border-radius: 4px; } + + &__actions { + display: flex; + justify-content: center; + gap: 15px; + margin-top: 20px; + } + + &__create-btn { + padding: 10px 20px; + border-radius: 10px; + color: colors.$white; + cursor: pointer; + border: none; + background: colors.$orange; + } } \ No newline at end of file From 9c73857ee6622cfe139838f63b8e641c2a2ed429 Mon Sep 17 00:00:00 2001 From: damedelion Date: Fri, 20 Dec 2024 19:43:39 +0300 Subject: [PATCH 3/7] add cursors to player buttons, add scale to card buttons --- src/components.scss | 8 ++++++++ src/widgets/albumCard/ui/albumCard.hbs | 12 ++++++------ src/widgets/albumCard/ui/albumCard.js | 4 ++-- src/widgets/artistCard/ui/artistCard.hbs | 12 ++++++------ src/widgets/artistCard/ui/artistCard.js | 4 ++-- src/widgets/createPlaylist/ui/createPlaylist.js | 1 + src/widgets/footerPlayer/ui/footerPlayer.scss | 16 ++++------------ src/widgets/header/ui/Header.scss | 1 + src/widgets/listenBlock/ui/listenBlock.hbs | 2 +- src/widgets/listenBlock/ui/listenBlock.scss | 6 ------ src/widgets/playlistCard/ui/playlistCard.hbs | 14 +++++++------- src/widgets/playlistCard/ui/playlistCard.js | 4 ++-- src/widgets/sideMenu/ui/sideMenu.scss | 1 + 13 files changed, 41 insertions(+), 44 deletions(-) diff --git a/src/components.scss b/src/components.scss index b5dd1be..c1e77ce 100644 --- a/src/components.scss +++ b/src/components.scss @@ -152,6 +152,14 @@ } } +.player_button { + cursor: pointer; + + &:hover { + opacity: 0.6; + } + } + .carousel { &__container { position: relative; diff --git a/src/widgets/albumCard/ui/albumCard.hbs b/src/widgets/albumCard/ui/albumCard.hbs index ea07faf..bec5bb4 100644 --- a/src/widgets/albumCard/ui/albumCard.hbs +++ b/src/widgets/albumCard/ui/albumCard.hbs @@ -12,15 +12,15 @@
- -
+

Слушать

-
- -
+

Слушать

-