feat: separate discs in album page

+ remove tooltip markup
+ refactor css classnames
This commit is contained in:
geoffrey45
2022-10-01 21:35:02 +03:00
committed by Mungai Njoroge
parent 278439eee8
commit 977d9282cb
23 changed files with 187 additions and 193 deletions
-1
View File
@@ -2,7 +2,6 @@
<ContextMenu /> <ContextMenu />
<Modal /> <Modal />
<Notification /> <Notification />
<div id="tooltip"></div>
<section <section
id="app-grid" id="app-grid"
:class="{ :class="{
+44 -49
View File
@@ -1,14 +1,9 @@
// TEXT
.t-center { .t-center {
text-align: center; text-align: center;
} }
.image {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
transition: transform 0.3s ease-in-out;
}
.ellip { .ellip {
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 1; -webkit-line-clamp: 1;
@@ -20,6 +15,24 @@
max-width: 100%; max-width: 100%;
} }
.heading {
font-size: 2rem;
font-weight: bold;
}
a {
text-decoration: none;
color: #fff;
}
.image {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
transition: transform 0.3s ease-in-out;
}
// BORDERS
.rounded { .rounded {
border-radius: 1rem; border-radius: 1rem;
} }
@@ -36,8 +49,8 @@
border-radius: 10rem; border-radius: 10rem;
} }
.flex { .border {
display: flex; border: 1px solid $gray3;
} }
.bg-primary { .bg-primary {
@@ -45,20 +58,7 @@
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.425); box-shadow: 0 0 1rem rgba(0, 0, 0, 0.425);
} }
.border { // BUTTONS
border: 1px solid $gray3;
}
.heading {
font-size: 2rem;
font-weight: bold;
}
a {
text-decoration: none;
color: #fff;
}
button { button {
border: none; border: none;
font-size: 0.9rem !important; font-size: 0.9rem !important;
@@ -99,6 +99,23 @@ button {
width: 2.5rem; width: 2.5rem;
} }
// POSITION
.abs {
position: absolute;
}
// OTHERS
.grid {
display: grid;
}
.flex {
display: flex;
}
.separator { .separator {
border-top: 1px $separator solid; border-top: 1px $separator solid;
color: transparent; color: transparent;
@@ -106,37 +123,15 @@ button {
opacity: 0.5; opacity: 0.5;
} }
// NO THIS, NO THAT (OVERRIDES)
.no-border { .no-border {
border: none; border: none;
} }
.noscroll { .no-scroll {
overflow: hidden; overflow: hidden;
} }
.abs { .no-select {
position: absolute; user-select: none;
}
.grid {
display: grid;
}
.card-dark {
background-color: #fff;
}
#tooltip {
background-color: $darkestblue;
border-radius: $smaller;
padding: $smaller;
font-size: 0.85rem;
visibility: hidden;
position: absolute;
z-index: 300;
}
#tooltip[data-popper-reference-hidden] {
visibility: hidden !important;
pointer-events: none;
} }
+1 -3
View File
@@ -14,6 +14,7 @@ $large: 1.5rem;
$larger: 2rem; $larger: 2rem;
$banner-height: 18rem; $banner-height: 18rem;
$song-item-height: 4rem;
// apple human design guideline colors // apple human design guideline colors
$black: #181a1c; $black: #181a1c;
@@ -49,9 +50,6 @@ $playlist-card-bg: $gray4;
// SVG COLORS // SVG COLORS
$default: $accent; $default: $accent;
$track-btn-svg: $accent;
$side-nav-svg: $red; $side-nav-svg: $red;
$overshoot: cubic-bezier(0.68, -0.55, 0.265, 1.55); $overshoot: cubic-bezier(0.68, -0.55, 0.265, 1.55);
+32
View File
@@ -0,0 +1,32 @@
<template>
<div
class="album_disc_header no-select"
v-if="album_disc.is_album_disc_number"
>
<div class="disc_number">Disc {{ album_disc.album_page_disc_number }}</div>
<div></div>
</div>
</template>
<script setup lang="ts">
import { AlbumDisc } from "@/interfaces";
defineProps<{
album_disc: AlbumDisc;
}>();
</script>
<style lang="scss">
.album_disc_header {
display: grid;
grid-template-columns: 1fr max-content;
align-items: center;
padding-left: 1rem;
height: $song-item-height;
.disc_number {
font-size: $medium;
opacity: 0.75;
}
}
</style>
+7 -7
View File
@@ -10,7 +10,7 @@
: '', : '',
}" }"
> >
<div class="big-img noscroll" :class="{ imgSmall: widthIsSmall }"> <div class="big-img no-scroll" :class="{ imgSmall: widthIsSmall }">
<img :src="imguri.thumb.large + album.image" class="rounded" /> <img :src="imguri.thumb.large + album.image" class="rounded" />
</div> </div>
<div <div
@@ -59,13 +59,13 @@
import { ref } from "vue"; import { ref } from "vue";
import { paths } from "@/config"; import { paths } from "@/config";
import useNavStore from "@/stores/nav";
import { AlbumInfo } from "../../interfaces";
import useAlbumStore from "@/stores/pages/album";
import { playSources } from "../../composables/enums";
import { useVisibility, formatSeconds } from "@/utils";
import { getButtonColor, isLight } from "../../composables/colors/album";
import { isSmall as widthIsSmall } from "@/stores/content-width"; import { isSmall as widthIsSmall } from "@/stores/content-width";
import useNavStore from "@/stores/nav";
import useAlbumStore from "@/stores/pages/album";
import { formatSeconds, useVisibility } from "@/utils";
import { getButtonColor, isLight } from "../../composables/colors/album";
import { playSources } from "../../composables/enums";
import { AlbumInfo } from "../../interfaces";
import PlayBtnRect from "../shared/PlayBtnRect.vue"; import PlayBtnRect from "../shared/PlayBtnRect.vue";
+3 -3
View File
@@ -71,9 +71,9 @@ import { formatSeconds } from "@/utils";
import { Routes } from "@/composables/enums"; import { Routes } from "@/composables/enums";
import useSettingsStore from "@/stores/settings"; import useSettingsStore from "@/stores/settings";
import ArtistName from "@/components/shared/ArtistName.vue";
import HotKeys from "@/components/LeftSidebar/NP/HotKeys.vue"; import HotKeys from "@/components/LeftSidebar/NP/HotKeys.vue";
import Progress from "@/components/LeftSidebar/NP/Progress.vue"; import Progress from "@/components/LeftSidebar/NP/Progress.vue";
import ArtistName from "@/components/shared/ArtistName.vue";
import HeartSvg from "@/assets/icons/heart.svg"; import HeartSvg from "@/assets/icons/heart.svg";
// import PlusSvg from "@/assets/icons/plus.svg"; // import PlusSvg from "@/assets/icons/plus.svg";
@@ -159,7 +159,7 @@ const settings = useSettingsStore();
} }
.time { .time {
font-size: 12px; font-size: $medium;
height: fit-content; height: fit-content;
width: 3rem; width: 3rem;
@@ -187,7 +187,7 @@ const settings = useSettingsStore();
.artist { .artist {
opacity: 0.75; opacity: 0.75;
margin-bottom: -$smaller; margin-bottom: -$smaller;
font-size: 12px; font-size: $medium;
} }
} }
} }
+2 -3
View File
@@ -1,5 +1,5 @@
<template> <template>
<div class="hotkeys rounded-sm noscroll"> <div class="hotkeys rounded-sm no-scroll">
<button @click.prevent="q.playPrev"> <button @click.prevent="q.playPrev">
<PrevSvg /> <PrevSvg />
</button> </button>
@@ -16,8 +16,7 @@
<script setup lang="ts"> <script setup lang="ts">
import useQStore from "@/stores/queue"; import useQStore from "@/stores/queue";
import NextSvg from "../../../assets/icons/next.svg"; import { default as NextSvg, default as PrevSvg } from "../../../assets/icons/next.svg";
import PrevSvg from "../../../assets/icons/next.svg";
import PauseSvg from "../../../assets/icons/pause.svg"; import PauseSvg from "../../../assets/icons/pause.svg";
import PlaySvg from "../../../assets/icons/play.svg"; import PlaySvg from "../../../assets/icons/play.svg";
+2 -2
View File
@@ -1,5 +1,5 @@
<template> <template>
<div v-auto-animate class="l-sidebar noscroll"> <div v-auto-animate class="l-sidebar no-scroll">
<div class="withlogo"> <div class="withlogo">
<Logo /> <Logo />
<Navigation /> <Navigation />
@@ -11,8 +11,8 @@
<script setup lang="ts"> <script setup lang="ts">
import Navigation from "@/components/LeftSidebar/Navigation.vue"; import Navigation from "@/components/LeftSidebar/Navigation.vue";
import NowPlayingImage from "./NowPlayingImg.vue";
import Logo from "@/components/Logo.vue"; import Logo from "@/components/Logo.vue";
import NowPlayingImage from "./NowPlayingImg.vue";
import useSettingsStore from "@/stores/settings"; import useSettingsStore from "@/stores/settings";
+1 -1
View File
@@ -1,6 +1,6 @@
<template> <template>
<div <div
class="p-header image rounded noscroll" class="p-header image rounded no-scroll"
ref="playlistheader" ref="playlistheader"
:style="[ :style="[
{ {
@@ -1,7 +1,7 @@
<template> <template>
<router-link <router-link
:to="{ name: 'PlaylistView', params: { pid: playlist.playlistid } }" :to="{ name: 'PlaylistView', params: { pid: playlist.playlistid } }"
class="p-card rounded noscroll" class="p-card rounded no-scroll"
> >
<img <img
:src="imguri + playlist.thumb" :src="imguri + playlist.thumb"
+1 -1
View File
@@ -1,7 +1,7 @@
<template> <template>
<div class="r-sidebar"> <div class="r-sidebar">
<SearchInput /> <SearchInput />
<div v-auto-animate class="r-content noscroll" > <div v-auto-animate class="r-content no-scroll" >
<div class="r-dash" v-if="tabs.current === tabs.tabs.home"> <div class="r-dash" v-if="tabs.current === tabs.tabs.home">
<DashBoard /> <DashBoard />
</div> </div>
@@ -2,7 +2,7 @@
<div id="right-tabs" class="rounded"> <div id="right-tabs" class="rounded">
<div class="tab-buttons-wrapper"> <div class="tab-buttons-wrapper">
<Teleport :disabled="!isOnSearchPage" to="#nav-tab-headers"> <Teleport :disabled="!isOnSearchPage" to="#nav-tab-headers">
<div class="tabheaders rounded-sm noscroll"> <div class="tabheaders rounded-sm no-scroll">
<div <div
class="tab" class="tab"
v-for="tab in tabs" v-for="tab in tabs"
@@ -1,5 +1,5 @@
<template> <template>
<div id="tracks-results" class="noscroll"> <div id="tracks-results" class="no-scroll">
<div v-if="search.tracks.value.length"> <div v-if="search.tracks.value.length">
<TrackComponent <TrackComponent
v-for="(track, index) in search.tracks.value" v-for="(track, index) in search.tracks.value"
-17
View File
@@ -159,22 +159,5 @@ function update_playlist(e: Event) {
background-color: $gray4; background-color: $gray4;
} }
} }
textarea {
width: 100%;
max-width: 28rem;
max-height: 5rem;
color: $white;
background-color: $gray2;
border: none;
font-family: inherit;
padding: $small;
outline: none;
margin: $small 0;
&:focus {
outline: solid 2px $gray1;
}
}
} }
</style> </style>
-39
View File
@@ -1,39 +0,0 @@
<template>
<div id="nav-search">
<form>
<input
type="search"
name=""
id=""
placeholder="Search here"
class="rounded"
/>
</form>
</div>
</template>
<style lang="scss">
#nav-search {
// border: solid 1px $gray2;
form {
display: flex;
gap: $small;
padding: 0;
margin: 0;
input[type="search"] {
background-color: $gray5;
border: none;
padding: $small;
width: 100%;
color: $white;
font-size: 1rem;
&:focus {
outline: solid $accent;
}
}
}
}
</style>
+2 -2
View File
@@ -1,8 +1,8 @@
<template> <template>
<div class="nav-queue-title"> <div class="nav-queue-title">
<div class="first noscroll"> <div class="first no-scroll">
<router-link :to="(location as RouteLocationRaw)"> <router-link :to="(location as RouteLocationRaw)">
<button>Go to source</button> <button class="btn-active">Go to source</button>
</router-link> </router-link>
<div class="playing-from"> <div class="playing-from">
<div class="border rounded-sm pad-sm"> <div class="border rounded-sm pad-sm">
+11 -21
View File
@@ -12,11 +12,8 @@
<HeartSvg /> <HeartSvg />
</div> </div>
<div class="flex"> <div class="flex">
<div v-auto-animate @click.pre@dblclick.prevent="emitUpdate" class="thumbnail"> <div @click.prevent="emitUpdate" class="thumbnail">
<img <img :src="imguri + track.image" class="album-art image rounded-sm" />
:src="imguri + track.image"
class="album-art image rounded-sm"
/>
<div <div
class="now-playing-track-indicator image" class="now-playing-track-indicator image"
v-if="isCurrent" v-if="isCurrent"
@@ -24,11 +21,7 @@
></div> ></div>
</div> </div>
<div v-tooltip class="song-title"> <div v-tooltip class="song-title">
<div <div class="title ellip" @click.prevent="emitUpdate" ref="artisttitle">
class="title ellip"
@click.pre@dblclick.prevent="emitUpdate"
ref="artisttitle"
>
{{ track.title }} {{ track.title }}
</div> </div>
<div class="isSmallArtists" style="display: none"> <div class="isSmallArtists" style="display: none">
@@ -58,7 +51,7 @@
<div class="song-duration">{{ formatSeconds(track.duration) }}</div> <div class="song-duration">{{ formatSeconds(track.duration) }}</div>
<div <div
class="options-icon circular" class="options-icon circular"
:class="{ options_button_clicked }" :class="{ 'btn-active': options_button_clicked }"
@click.stop="showMenu" @click.stop="showMenu"
@dblclick.stop="() => {}" @dblclick.stop="() => {}"
> >
@@ -72,7 +65,7 @@ import { ref } from "vue";
import { showTrackContextMenu as showContext } from "@/composables/context"; import { showTrackContextMenu as showContext } from "@/composables/context";
import { paths } from "@/config"; import { paths } from "@/config";
import { Track } from "@/interfaces"; import { AlbumDisc, Track } from "@/interfaces";
import { formatSeconds } from "@/utils"; import { formatSeconds } from "@/utils";
import HeartSvg from "@/assets/icons/heart.svg"; import HeartSvg from "@/assets/icons/heart.svg";
@@ -87,7 +80,7 @@ const artisttitle = ref<HTMLElement | null>(null);
const props = defineProps<{ const props = defineProps<{
track: Track; track: Track;
index: number | string; index: Number | String;
isCurrent: Boolean; isCurrent: Boolean;
isCurrentPlaying: Boolean; isCurrentPlaying: Boolean;
no_album?: Boolean; no_album?: Boolean;
@@ -107,12 +100,14 @@ function showMenu(e: Event) {
</script> </script>
<style lang="scss"> <style lang="scss">
.songlist-item { .songlist-item {
display: grid; display: grid;
grid-template-columns: 1.5rem 2fr 1fr 1.5fr 2.5rem 2.5rem; grid-template-columns: 1.5rem 2fr 1fr 1.5fr 2.5rem 2.5rem;
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
height: 3.75rem; height: $song-item-height;
gap: 1rem; gap: 1rem;
user-select: none; user-select: none;
padding-left: $small; padding-left: $small;
@@ -190,16 +185,11 @@ function showMenu(e: Event) {
width: 2rem; width: 2rem;
svg { svg {
transition: all 0.2s ease-in; stroke: $white;
stroke: $track-btn-svg;
circle {
fill: $track-btn-svg;
}
} }
&:hover { &:hover {
background-color: $gray5; background-color: $darkestblue;
} }
} }
+7 -4
View File
@@ -1,6 +1,11 @@
import { FromOptions, NotifType } from "./composables/enums"; import { FromOptions, NotifType } from "./composables/enums";
export interface Track { export interface AlbumDisc {
is_album_disc_number?: boolean;
album_page_disc_number?: number;
}
export interface Track extends AlbumDisc {
trackid: string; trackid: string;
title: string; title: string;
album?: string; album?: string;
@@ -18,7 +23,7 @@ export interface Track {
index: number; index: number;
hash: string; hash: string;
copyright?: string; copyright?: string;
filetype: string filetype: string;
} }
export interface Folder { export interface Folder {
@@ -116,5 +121,3 @@ export interface FuseResult {
item: Track; item: Track;
refIndex: number; refIndex: number;
} }
+30 -23
View File
@@ -20,33 +20,40 @@
ref="header" ref="header"
class="header rounded" class="header rounded"
v-if="!no_header" v-if="!no_header"
:style="{ height: headerHeight + 24 + 'px' }" :style="{ height: headerHeight + (on_album_page ? 0 : 24) + 'px' }"
> >
<div ref="header_content" class="header-content"> <div ref="header_content" class="header-content">
<slot name="header"></slot> <slot name="header"></slot>
</div> </div>
</div> </div>
<SongItem <div v-for="t in tracks">
style="height: 60px" <AlbumDiscBar
v-for="t in tracks" v-if="on_album_page && t.data.is_album_disc_number"
:key="t.data.trackid" :album_disc="t.data"
:track="t.data" />
:no_album="on_album_page"
:index=" <SongItem
on_album_page v-else
? t.data.track style="height: 60px"
: t.data.index !== undefined :key="t.data.trackid"
? t.data.index + 1 :track="t.data"
: t.index + 1 :no_album="on_album_page"
" :index="
:isCurrent="queue.currentid === t.data.trackid" on_album_page
:isCurrentPlaying=" ? t.data.track
queue.currentid === t.data.trackid && queue.playing : t.data.index !== undefined
" ? t.data.index + 1
@playThis=" : t.index + 1
updateQueue(t.data.index !== undefined ? t.data.index : t.index) "
" :isCurrent="queue.currentid === t.data.trackid"
/> :isCurrentPlaying="
queue.currentid === t.data.trackid && queue.playing
"
@playThis="
updateQueue(t.data.index !== undefined ? t.data.index : t.index)
"
/>
</div>
<div class="page-bottom-padding" style="height: 64px"></div> <div class="page-bottom-padding" style="height: 64px"></div>
</div> </div>
</div> </div>
@@ -61,7 +68,7 @@ import { Track } from "@/interfaces";
import useQStore from "@/stores/queue"; import useQStore from "@/stores/queue";
import SongItem from "@/components/shared/SongItem.vue"; import SongItem from "@/components/shared/SongItem.vue";
import AlbumDiscBar from "@/components/AlbumView/AlbumDiscBar.vue";
// EMITS & PROPS // EMITS & PROPS
const emit = defineEmits<{ const emit = defineEmits<{
(e: "playFromPage", index: number): void; (e: "playFromPage", index: number): void;
+13 -4
View File
@@ -1,6 +1,7 @@
import { useFuse } from "@/utils"; import { useFuse } from "@/utils";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { ComputedRef } from "vue"; import { ComputedRef } from "vue";
import { AlbumDisc } from "./../../interfaces";
import { FuseTrackOptions } from "@/composables/enums"; import { FuseTrackOptions } from "@/composables/enums";
@@ -62,7 +63,18 @@ export default defineStore("album", {
}, },
getters: { getters: {
filteredTracks(): ComputedRef<FuseResult[]> { filteredTracks(): ComputedRef<FuseResult[]> {
return useFuse(this.query, this.allTracks, FuseTrackOptions); const discs = createDiscs(this.allTracks);
let tracks: (Track[] | AlbumDisc[]) = [];
Object.keys(discs).forEach((disc) => {
const discHeader = {
is_album_disc_number: true,
album_page_disc_number: parseInt(disc),
} as AlbumDisc;
tracks = [...tracks, discHeader, ...discs[disc]];
});
return useFuse(this.query, tracks, FuseTrackOptions);
}, },
tracks(): Track[] { tracks(): Track[] {
const tracks = this.filteredTracks.value.map((result: FuseResult) => { const tracks = this.filteredTracks.value.map((result: FuseResult) => {
@@ -76,8 +88,5 @@ export default defineStore("album", {
return tracks; return tracks;
}, },
discs(): Discs {
return createDiscs(this.tracks);
},
}, },
}); });
+18
View File
@@ -12,10 +12,28 @@
<script setup lang="ts"> <script setup lang="ts">
import useQStore from "@/stores/queue"; import useQStore from "@/stores/queue";
import Layout from "@/layouts/HeaderAndVList.vue"; import Layout from "@/layouts/HeaderAndVList.vue";
import { onBeforeMount, onMounted } from "vue";
const queue = useQStore(); const queue = useQStore();
function playFromQueue(index: number) { function playFromQueue(index: number) {
queue.play(index); queue.play(index);
} }
function scrollToCurrent() {
const scrollable = document.getElementById("v-page-scrollable");
const itemHeight = 64;
const top = queue.currentindex * itemHeight - 290;
scrollable?.scrollTo({
top,
behavior: "smooth",
});
}
onBeforeMount(() => {
setTimeout(() => {
scrollToCurrent();
}, 1000);
});
</script> </script>
+7 -7
View File
@@ -14,7 +14,7 @@
{{ page }} {{ page }}
</button> </button>
</div> </div>
<div ref="page" class="page noscroll" v-auto-animate> <div ref="page" class="page no-scroll" v-auto-animate>
<component :is="component" /> <component :is="component" />
</div> </div>
<button <button
@@ -28,14 +28,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRoute } from "vue-router";
import { computed, onMounted, ref } from "vue";
import TracksPage from "./tracks.vue";
import AlbumPage from "./albums.vue";
import ArtistPage from "./artists.vue";
import { Routes } from "@/composables/enums"; import { Routes } from "@/composables/enums";
import useSearchStore from "@/stores/search"; import useSearchStore from "@/stores/search";
import { focusElemByClass } from "@/utils"; import { focusElemByClass } from "@/utils";
import { computed, onMounted, ref } from "vue";
import { useRoute } from "vue-router";
import AlbumPage from "./albums.vue";
import ArtistPage from "./artists.vue";
import TracksPage from "./tracks.vue";
// width of album and artist cards // width of album and artist cards
const defaultItemCount = 6; const defaultItemCount = 6;
@@ -169,7 +169,7 @@ onMounted(() => {
} }
} }
.page.noscroll { .page.no-scroll {
overflow-x: visible; overflow-x: visible;
} }
+2 -2
View File
@@ -1,6 +1,6 @@
<template> <template>
<div class="search-tracks-view"> <div class="search-tracks-view">
<div class="noscroll"> <div class="no-scroll">
<Layout :no_header="true" :tracks="search.tracks.value" /> <Layout :no_header="true" :tracks="search.tracks.value" />
</div> </div>
</div> </div>
@@ -17,7 +17,7 @@ const search = useSearchStore();
.search-tracks-view { .search-tracks-view {
height: 100%; height: 100%;
.noscroll { .no-scroll {
height: 100%; height: 100%;
} }