mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-04 12:33:03 +00:00
Implement fuzzy page search using fuse.js (#86)
This commit is contained in:
@@ -3,12 +3,17 @@
|
||||
class="a-header rounded"
|
||||
ref="albumheaderthing"
|
||||
:style="{
|
||||
backgroundImage: `linear-gradient(
|
||||
backgroundImage: album.colors
|
||||
? `linear-gradient(
|
||||
37deg, ${album.colors[0]}, ${album.colors[3]}
|
||||
)`,
|
||||
)`
|
||||
: '',
|
||||
}"
|
||||
>
|
||||
<div class="info" :class="{ nocontrast: isLight(album.colors[0]) }">
|
||||
<div
|
||||
class="info"
|
||||
:class="{ nocontrast: album.colors ? isLight(album.colors[0]) : false }"
|
||||
>
|
||||
<div class="art">
|
||||
<img
|
||||
:src="imguri.artist + album.artistimg"
|
||||
@@ -31,8 +36,11 @@
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="stats">
|
||||
{{ album.artist }} • {{ album.date }} • {{ album.count }} Tracks •
|
||||
{{ formatSeconds(album.duration, true) }}
|
||||
<div class="border rounded-sm pad-sm">
|
||||
{{ album.artist }} • {{ album.date }} • {{ album.count }}
|
||||
{{ album.count === 1 ? "Track" : "Tracks" }} •
|
||||
{{ formatSeconds(album.duration, true) }}
|
||||
</div>
|
||||
</div>
|
||||
<PlayBtnRect
|
||||
:source="playSources.album"
|
||||
@@ -62,9 +70,8 @@ import { getButtonColor, isLight } from "../../composables/colors/album";
|
||||
|
||||
import PlayBtnRect from "../shared/PlayBtnRect.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
defineProps<{
|
||||
album: AlbumInfo;
|
||||
bio: string | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -136,12 +143,14 @@ useVisibility(albumheaderthing, handleVisibilityState);
|
||||
|
||||
.top {
|
||||
.h {
|
||||
font-size: 14px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 600;
|
||||
width: fit-content;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.artist {
|
||||
@@ -157,15 +166,19 @@ useVisibility(albumheaderthing, handleVisibilityState);
|
||||
margin-top: $smaller;
|
||||
|
||||
.stats {
|
||||
border-radius: $small;
|
||||
font-weight: bold;
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: 0.75rem;
|
||||
cursor: text;
|
||||
|
||||
|
||||
div {
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// grid-template-columns: 1fr !important;
|
||||
@include for-desktop-down {
|
||||
.art > img {
|
||||
height: 6rem;
|
||||
|
||||
@@ -135,7 +135,7 @@ export default {
|
||||
padding: $small 0.95rem $small 0.95rem;
|
||||
margin: $smaller;
|
||||
transition: all 0.2s ease-in-out;
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
float: left;
|
||||
|
||||
.play {
|
||||
@@ -146,7 +146,7 @@ export default {
|
||||
width: 3rem;
|
||||
background: url(../../assets/icons/play.svg) no-repeat center;
|
||||
background-size: 60%;
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
opacity: 0;
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
@@ -208,7 +208,7 @@ export default {
|
||||
input::-webkit-search-cancel-button {
|
||||
position: relative;
|
||||
right: 20px;
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export default {
|
||||
align-items: center;
|
||||
grid-template-columns: 7.5rem 1fr;
|
||||
padding: $small;
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -68,7 +68,7 @@ export default {
|
||||
padding: $small 0.95rem $small 0.95rem;
|
||||
margin: $smaller;
|
||||
transition: all 0.2s ease-in-out;
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
float: left;
|
||||
|
||||
&:hover {
|
||||
@@ -116,7 +116,7 @@ export default {
|
||||
input::-webkit-search-cancel-button {
|
||||
position: relative;
|
||||
right: 20px;
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ export default {
|
||||
align-items: center;
|
||||
grid-template-columns: 7.5rem 1fr;
|
||||
padding: $small;
|
||||
cursor: pointer;
|
||||
cursor: default;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -32,7 +32,6 @@ defineProps<{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: default;
|
||||
padding: 0.4rem 1rem;
|
||||
position: relative;
|
||||
|
||||
|
||||
@@ -16,11 +16,17 @@
|
||||
</div>
|
||||
<div class="songlist">
|
||||
<SongItem
|
||||
v-for="(track, index) in getTrackList()"
|
||||
v-for="(track, index) in tracks"
|
||||
:key="track.trackid"
|
||||
:track="track"
|
||||
:index="track.index"
|
||||
@updateQueue="updateQueue(index)"
|
||||
:index="
|
||||
on_album_page
|
||||
? track.tracknumber
|
||||
: track.index !== undefined
|
||||
? track.index + 1
|
||||
: index + 1
|
||||
"
|
||||
@playThis="updateQueue(track.index !== undefined ? track.index : index)"
|
||||
:isPlaying="queue.playing"
|
||||
:isCurrent="queue.currentid == track.trackid"
|
||||
/>
|
||||
@@ -37,7 +43,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { onUpdated, ref } from "vue";
|
||||
import { useElementSize } from "@vueuse/core";
|
||||
import { computed } from "@vue/reactivity";
|
||||
|
||||
@@ -59,6 +65,10 @@ const props = defineProps<{
|
||||
copyright?: string | null;
|
||||
}>();
|
||||
|
||||
// onUpdated(() => {
|
||||
// console.log(props.tracks[1].index);
|
||||
// });
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "playFromPage", index: number): void;
|
||||
}>();
|
||||
@@ -79,11 +89,11 @@ function updateQueue(index: number) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to show track numbers as indexes in the album page.
|
||||
* Used to show handle track indexes.
|
||||
*/
|
||||
function getTrackList() {
|
||||
if (props.on_album_page) {
|
||||
let tracks = props.tracks.map((track) => {
|
||||
const tracks = props.tracks.map((track) => {
|
||||
track.index = track.tracknumber;
|
||||
return track;
|
||||
});
|
||||
@@ -91,12 +101,7 @@ function getTrackList() {
|
||||
return tracks;
|
||||
}
|
||||
|
||||
const tracks = props.tracks.map((track, index) => {
|
||||
track.index = index + 1;
|
||||
return track;
|
||||
});
|
||||
|
||||
return tracks;
|
||||
return props.tracks;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ const q = useQStore();
|
||||
height: 2.5rem;
|
||||
width: 100%;
|
||||
background-size: 1.5rem !important;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: $darkestblue;
|
||||
|
||||
@@ -25,34 +25,26 @@
|
||||
</router-link>
|
||||
|
||||
<div class="bottom">
|
||||
<div class="title ellip">{{ props.track?.title }}</div>
|
||||
<div
|
||||
class="artists ellip"
|
||||
v-if="track?.artists && track?.artists[0] !== ''"
|
||||
>
|
||||
<span v-for="artist in putCommas(track.artists)" :key="artist">{{
|
||||
artist
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="artists" v-else-if="track?.artists">
|
||||
<span>{{ track.albumartist }}</span>
|
||||
</div>
|
||||
<div class="artists" v-else>
|
||||
<span>Meh</span>
|
||||
</div>
|
||||
<div class="title ellip t-center">{{ props.track?.title }}</div>
|
||||
<ArtistName
|
||||
:artists="track?.artists || ['Artist']"
|
||||
:albumartist="track?.albumartist"
|
||||
class="artists"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { putCommas } from "@/utils";
|
||||
import { paths } from "../../../config";
|
||||
import { Track } from "../../../interfaces";
|
||||
const imguri = paths.images.thumb;
|
||||
import ArtistName from "@/components/shared/ArtistName.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
track: Track | null;
|
||||
}>();
|
||||
|
||||
const imguri = paths.images.thumb;
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -91,12 +83,14 @@ const props = defineProps<{
|
||||
|
||||
.title {
|
||||
font-weight: 900;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.artists {
|
||||
font-size: 0.85rem;
|
||||
opacity: 0.75;
|
||||
|
||||
margin: 0 auto;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline 1px !important;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="l-sidebar">
|
||||
<div class="l-sidebar noscroll">
|
||||
<div class="withlogo">
|
||||
<Logo />
|
||||
<Navigation />
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from "@vue/reactivity";
|
||||
import ArtistCard from "@/components/shared/ArtistCard.vue";
|
||||
import { Artist } from "@/interfaces";
|
||||
import { ref } from "@vue/reactivity";
|
||||
import ArrowSvg from "../../assets/icons/right-arrow.svg";
|
||||
|
||||
defineProps<{
|
||||
@@ -82,7 +82,6 @@ const scrollRight = () => {
|
||||
|
||||
.icon {
|
||||
border-radius: $small;
|
||||
cursor: pointer;
|
||||
transition: all 0.5s ease;
|
||||
background-color: rgb(51, 51, 51);
|
||||
padding: $smaller;
|
||||
|
||||
@@ -37,20 +37,17 @@
|
||||
import { ref } from "vue";
|
||||
|
||||
import useNavStore from "@/stores/nav";
|
||||
import useModalStore from "../../stores/modal";
|
||||
import pContext from "../../contexts/playlist";
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import useContextStore from "../../stores/context";
|
||||
import useModalStore from "../../stores/modal";
|
||||
|
||||
import { playSources } from "@/composables/enums";
|
||||
import { formatSeconds, useVisibility } from "@/utils";
|
||||
import { paths } from "../../config";
|
||||
import { Playlist } from "../../interfaces";
|
||||
import { useVisibility, formatSeconds } from "@/utils";
|
||||
import { ContextSrc, playSources } from "@/composables/enums";
|
||||
|
||||
import PlayBtnRect from "../shared/PlayBtnRect.vue";
|
||||
|
||||
const imguri = paths.images.playlist;
|
||||
const context = useContextStore();
|
||||
const modal = useModalStore();
|
||||
const nav = useNavStore();
|
||||
const playlistheader = ref<HTMLElement | null>(null);
|
||||
@@ -64,10 +61,6 @@ const props = defineProps<{
|
||||
function editPlaylist() {
|
||||
modal.showEditPlaylistModal(props.info);
|
||||
}
|
||||
|
||||
function showDropdown(e: any) {
|
||||
context.showContextMenu(e, pContext(), ContextSrc.PHeader);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -155,16 +148,7 @@ function showDropdown(e: any) {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 900;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.desc {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: initial;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
max-width: 50%;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.duration {
|
||||
@@ -173,6 +157,7 @@ function showDropdown(e: any) {
|
||||
padding: $smaller;
|
||||
padding-left: 0;
|
||||
font-weight: 900;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.btns {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="p-card new-playlist-card rounded"
|
||||
@click="Modal.showNewPlaylistModal()"
|
||||
>
|
||||
<PlusSvg />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import PlusSvg from "../../assets/icons/plus.svg";
|
||||
import useModalStore from "../../stores/modal";
|
||||
|
||||
const Modal = useModalStore();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.new-playlist-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
transform: scale(3);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -60,7 +60,6 @@ const songs = [
|
||||
|
||||
&:hover {
|
||||
background-color: #3a39393d;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,11 @@
|
||||
v-for="(t, index) in queue.tracklist"
|
||||
:key="index"
|
||||
:track="t"
|
||||
:index="index + 1"
|
||||
:index="index"
|
||||
:isPlaying="queue.playing"
|
||||
:isHighlighted="false"
|
||||
:isCurrent="index === queue.currentindex"
|
||||
:isQueueTrack="true"
|
||||
@PlayThis="playFromQueue(index)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
<template>
|
||||
<div id="playing-from" class="bg-primary rounded" @click="goTo">
|
||||
<div class="h">
|
||||
<div class="icon image" :class="from.icon"></div>
|
||||
Playing from
|
||||
</div>
|
||||
<div class="name">
|
||||
<div id="to">
|
||||
{{ from.text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FromOptions } from "@/composables/enums";
|
||||
import {
|
||||
fromAlbum, fromFolder, fromPlaylist,
|
||||
fromSearch
|
||||
} from "@/interfaces";
|
||||
import { computed } from "@vue/reactivity";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const props = defineProps<{
|
||||
from: fromFolder | fromAlbum | fromPlaylist | fromSearch;
|
||||
}>();
|
||||
|
||||
interface from {
|
||||
icon: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const from = computed((): from => {
|
||||
switch (props.from.type) {
|
||||
case undefined:
|
||||
return {
|
||||
icon: "album",
|
||||
text: "Welcome to Alice",
|
||||
};
|
||||
case FromOptions.folder:
|
||||
return {
|
||||
icon: "folder",
|
||||
text: props.from.name,
|
||||
};
|
||||
case FromOptions.album:
|
||||
return {
|
||||
icon: "album",
|
||||
text: `${props.from.name} - ${props.from.albumartist}`,
|
||||
};
|
||||
case FromOptions.playlist:
|
||||
return {
|
||||
icon: "playlist",
|
||||
text: props.from.name,
|
||||
};
|
||||
case FromOptions.search:
|
||||
return {
|
||||
icon: "search",
|
||||
text: `Search results for: "${props.from.query}"`,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function goToAlbum(from: fromAlbum) {
|
||||
router.push({
|
||||
name: "AlbumView",
|
||||
params: {
|
||||
hash: from.hash,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function goToFolder(from: fromFolder) {
|
||||
router.push({
|
||||
name: "FolderView",
|
||||
params: {
|
||||
path: from.path,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function goToPlaylist(from: fromPlaylist) {
|
||||
router.push({
|
||||
name: "PlaylistView",
|
||||
params: {
|
||||
pid: from.playlistid,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function goTo() {
|
||||
switch (props.from.type) {
|
||||
case FromOptions.folder:
|
||||
goToFolder(props.from);
|
||||
break;
|
||||
case FromOptions.album:
|
||||
goToAlbum(props.from);
|
||||
break;
|
||||
case FromOptions.playlist:
|
||||
goToPlaylist(props.from);
|
||||
break;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#playing-from {
|
||||
background-size: 120%;
|
||||
padding: 0.75rem;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s ease;
|
||||
background-color: $black;
|
||||
|
||||
&:hover {
|
||||
background-position: -4rem;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.h {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: $small;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $small;
|
||||
color: rgba(255, 255, 255, 0.849);
|
||||
|
||||
.icon {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
}
|
||||
|
||||
.folder {
|
||||
background-image: url("../../../assets/icons/folder.fill.svg") !important;
|
||||
}
|
||||
|
||||
.album {
|
||||
background-image: url("../../../assets/icons/album.svg") !important;
|
||||
}
|
||||
|
||||
.playlist {
|
||||
background-image: url("../../../assets/icons/playlist.svg") !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -50,7 +50,6 @@ function showMenu(e: Event) {
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: $gray4;
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
:isHighlighted="false"
|
||||
:isPlaying="queue.playing"
|
||||
:track="track"
|
||||
@PlayThis="updateQueue(index)"
|
||||
@playThis="updateQueue(index)"
|
||||
:index="index + 1"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="t-center"><h5>🤷</h5></div>
|
||||
@@ -17,12 +18,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
|
||||
import LoadMore from "./LoadMore.vue";
|
||||
import TrackItem from "@/components/shared/TrackItem.vue";
|
||||
import SongItem from "@/components/shared/SongItem.vue";
|
||||
import useQStore from "../../../stores/queue";
|
||||
import useSearchStore from "../../../stores/search";
|
||||
import { computed } from "vue";
|
||||
import useQStore from "@/stores/queue";
|
||||
import useSearchStore from "@/stores/search";
|
||||
|
||||
const queue = useQStore();
|
||||
const search = useSearchStore();
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
|
||||
defineProps<{
|
||||
state: boolean;
|
||||
}>();
|
||||
@@ -14,16 +12,16 @@ defineProps<{
|
||||
|
||||
<style lang="scss">
|
||||
.switch {
|
||||
height: 2rem;
|
||||
height: 1.5rem;
|
||||
background-color: $gray;
|
||||
width: 3.75rem;
|
||||
width: 2.5rem;
|
||||
padding: $smaller;
|
||||
position: relative;
|
||||
transition: all 0.25s ease;
|
||||
|
||||
.circle {
|
||||
transition: all 0.25s ease;
|
||||
height: 1.5rem;
|
||||
height: 1rem;
|
||||
aspect-ratio: 1;
|
||||
background-color: $gray1;
|
||||
position: absolute;
|
||||
@@ -37,7 +35,7 @@ defineProps<{
|
||||
|
||||
.circle {
|
||||
background-color: $white;
|
||||
left: calc((100% - ($smaller + 1.5rem)));
|
||||
left: calc((100% - ($smaller + 1rem)));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,7 +20,7 @@ defineProps<{
|
||||
<style lang="scss">
|
||||
.settingscontent {
|
||||
width: 100%;
|
||||
max-width: 40rem;
|
||||
// max-width: 40rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div class="settingsgroup">
|
||||
<div>
|
||||
<h4 v-if="group.title">{{ group.title }}</h4>
|
||||
<div v-if="group.name || group.desc">
|
||||
<h4 v-if="group.name">{{ group.name }}</h4>
|
||||
<div class="desc" v-if="group.desc">{{ group.desc }}</div>
|
||||
</div>
|
||||
<div class="setting rounded bg-primary pad-lg">
|
||||
<div class="setting rounded border pad-lg">
|
||||
<div
|
||||
v-for="(setting, index) in group.settings"
|
||||
:key="index"
|
||||
:class="{ inactive: setting.inactive && setting.inactive() }"
|
||||
>
|
||||
<div class="title">
|
||||
<div class="title ellip" @click="setting.action()">
|
||||
{{ setting.title }}
|
||||
</div>
|
||||
<div class="options">
|
||||
@@ -69,6 +69,7 @@ defineProps<{
|
||||
|
||||
.title {
|
||||
margin: auto 0;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="settingsnav">
|
||||
<div class="buttongroup rounded bg-primary">
|
||||
<div class="buttongroup rounded-sm bg-primary">
|
||||
<button v-for="(group, index) in settingGroups" :key="index">
|
||||
{{ group.title }}
|
||||
</button>
|
||||
|
||||
@@ -108,7 +108,6 @@
|
||||
|
||||
button {
|
||||
padding: 0 $medium;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,6 @@ function hideModal() {
|
||||
transform: rotate(45deg);
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import { onMounted } from "vue";
|
||||
import { updatePlaylist } from "@/composables/fetch/playlists";
|
||||
import { Playlist } from "@/interfaces";
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import { onMounted } from "vue";
|
||||
|
||||
const pStore = usePStore();
|
||||
|
||||
@@ -132,7 +132,6 @@ function update_playlist(e: Event) {
|
||||
place-items: center;
|
||||
color: $gray1;
|
||||
margin: $small 0;
|
||||
cursor: pointer;
|
||||
|
||||
#update-pl-img-preview {
|
||||
width: 4.5rem;
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
<div class="topnav">
|
||||
<div class="left">
|
||||
<NavButtons />
|
||||
|
||||
<div
|
||||
class="info"
|
||||
:style="{
|
||||
overflow: $route.name === Routes.search ? 'visible' : 'hidden',
|
||||
overflowY: hideOverflow() ? 'visible' : 'hidden',
|
||||
}"
|
||||
class="info"
|
||||
>
|
||||
<APTitle v-if="showAPTitle" />
|
||||
<SimpleTitle v-if="$route.name == Routes.settings" :text="'Settings'" />
|
||||
<Folder v-if="$route.name == Routes.folder" :subPaths="subPaths" />
|
||||
<APTitle
|
||||
v-if="$route.name == Routes.album || $route.name == Routes.playlist"
|
||||
:header_shown="nav.h_visible"
|
||||
/>
|
||||
<SettingsTitle v-if="$route.name == Routes.settings" :text="'Settings'" />
|
||||
<FolderTitle v-if="$route.name == Routes.folder" :subPaths="subPaths" />
|
||||
<SearchTitle v-if="$route.name == Routes.search" />
|
||||
<PlaylistsTitle v-if="$route.name == Routes.playlists" />
|
||||
<QueueTitle v-if="$route.name == Routes.queue" />
|
||||
@@ -26,7 +30,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { computed } from "@vue/reactivity";
|
||||
|
||||
import { subPath } from "@/interfaces";
|
||||
import useNavStore from "@/stores/nav";
|
||||
@@ -34,26 +37,26 @@ import { createSubPaths } from "@/utils";
|
||||
import { Routes } from "@/composables/enums";
|
||||
|
||||
import NavButtons from "./NavButtons.vue";
|
||||
// import Loader from "../shared/Loader.vue";
|
||||
|
||||
import Folder from "./Titles/Folder.vue";
|
||||
import FolderTitle from "./Titles/Folder.vue";
|
||||
import SimpleTitle from "./Titles/SimpleTitle.vue";
|
||||
import APTitle from "./Titles/APTitle.vue";
|
||||
import SearchTitle from "./Titles/SearchTitle.vue";
|
||||
import PlaylistsTitle from "./Titles/PlaylistsTitle.vue";
|
||||
import QueueTitle from "./Titles/QueueTitle.vue";
|
||||
import SettingsTitle from "./Titles/SettingsTitle.vue";
|
||||
|
||||
const route = useRoute();
|
||||
const nav = useNavStore();
|
||||
|
||||
const subPaths = ref<subPath[]>([]);
|
||||
|
||||
const showAPTitle = computed(() => {
|
||||
return (
|
||||
(route.name == Routes.album || route.name == Routes.playlist) &&
|
||||
!nav.h_visible
|
||||
);
|
||||
});
|
||||
function hideOverflow() {
|
||||
const { name } = route;
|
||||
const { album, playlist, search, folder } = Routes;
|
||||
|
||||
return (album + playlist + search + folder).includes(name as string);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.name,
|
||||
@@ -88,7 +91,6 @@ watch(
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min-content;
|
||||
width: 100%;
|
||||
// gap: $small;
|
||||
|
||||
.left {
|
||||
display: grid;
|
||||
|
||||
@@ -1,19 +1,33 @@
|
||||
<template>
|
||||
<div class="title albumnavtitle">
|
||||
<PlayBtn :source="things.source" :store="things.store" />
|
||||
<div class="ellip">
|
||||
{{ things.text }}
|
||||
<div
|
||||
class="title grid albumnavtitle"
|
||||
:class="{
|
||||
hide_play: header_shown,
|
||||
}"
|
||||
>
|
||||
<div class="first grid">
|
||||
<PlayBtn :source="things.source" :store="things.store" />
|
||||
<div class="ellip">
|
||||
{{ things.text }}
|
||||
</div>
|
||||
</div>
|
||||
<Input :page="($route.name as string)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from "vue-router";
|
||||
import { computed } from "@vue/reactivity";
|
||||
|
||||
import PlayBtn from "@/components/shared/PlayBtn.vue";
|
||||
import { playSources, Routes } from "@/composables/enums";
|
||||
import useAlbumStore from "@/stores/pages/album";
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import { computed } from "@vue/reactivity";
|
||||
import { useRoute } from "vue-router";
|
||||
import Input from "@/components/shared/Input.vue";
|
||||
|
||||
defineProps<{
|
||||
header_shown: boolean;
|
||||
}>();
|
||||
|
||||
const things = computed(() => {
|
||||
const route = useRoute();
|
||||
@@ -46,10 +60,22 @@ const things = computed(() => {
|
||||
|
||||
<style lang="scss">
|
||||
.albumnavtitle {
|
||||
display: flex;
|
||||
grid-template-columns: max-content 1fr;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: $small;
|
||||
outline: solid 1px $gray3;
|
||||
height: 100%;
|
||||
|
||||
.first {
|
||||
grid-template-columns: max-content 1fr;
|
||||
gap: $small;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.albumnavtitle.hide_play {
|
||||
.first {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,37 +1,43 @@
|
||||
<template>
|
||||
<div id="folder-nav-title">
|
||||
<div class="folder">
|
||||
<div class="fname">
|
||||
<div
|
||||
class="icon image"
|
||||
@click="
|
||||
$router.push({ name: Routes.folder, params: { path: '$home' } })
|
||||
"
|
||||
></div>
|
||||
<div class="paths">
|
||||
<div class="fname-wrapper">
|
||||
<div class="fname">
|
||||
<div
|
||||
class="path"
|
||||
v-for="path in subPaths.slice(1)"
|
||||
:key="path.path"
|
||||
:class="{ inthisfolder: path.active }"
|
||||
>
|
||||
<router-link
|
||||
class="text"
|
||||
:to="{ name: Routes.folder, params: { path: path.path } }"
|
||||
>{{ path.name }}</router-link
|
||||
class="icon image"
|
||||
@click="
|
||||
$router.push({ name: Routes.folder, params: { path: '$home' } })
|
||||
"
|
||||
></div>
|
||||
<div class="paths">
|
||||
<div
|
||||
class="path"
|
||||
v-for="path in subPaths.slice(1)"
|
||||
:key="path.path"
|
||||
:class="{ inthisfolder: path.active }"
|
||||
>
|
||||
<router-link
|
||||
class="text"
|
||||
:to="{ name: Routes.folder, params: { path: path.path } }"
|
||||
>{{ path.name }}</router-link
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Input :page="Routes.folder" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { focusElem } from "@/utils";
|
||||
import { subPath } from "@/interfaces";
|
||||
import { onUpdated } from "vue";
|
||||
import Input from "@/components/shared/Input.vue";
|
||||
import { Routes } from "@/composables/enums";
|
||||
import { subPath } from "@/interfaces";
|
||||
import { focusElem } from "@/utils";
|
||||
import { onUpdated } from "vue";
|
||||
|
||||
defineProps<{
|
||||
subPaths: subPath[];
|
||||
@@ -44,24 +50,16 @@ onUpdated(() => {
|
||||
|
||||
<style lang="scss">
|
||||
#folder-nav-title {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
.folder {
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr max-content;
|
||||
gap: $small;
|
||||
|
||||
.playbtnrect {
|
||||
height: 2.25rem;
|
||||
}
|
||||
|
||||
.drop-btn {
|
||||
width: 2.25rem;
|
||||
|
||||
.drop-icon {
|
||||
height: 2.25rem;
|
||||
width: 2.25rem;
|
||||
}
|
||||
.fname-wrapper {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.fname {
|
||||
@@ -70,8 +68,9 @@ onUpdated(() => {
|
||||
height: 2.25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: auto;
|
||||
padding-right: $smaller;
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
|
||||
.icon {
|
||||
height: 2rem;
|
||||
@@ -79,7 +78,6 @@ onUpdated(() => {
|
||||
background-image: url("../../../assets/icons/folder.fill.svg");
|
||||
background-size: 1.5rem;
|
||||
margin-left: $smaller;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.paths {
|
||||
@@ -97,10 +95,6 @@ onUpdated(() => {
|
||||
white-space: nowrap;
|
||||
margin: auto 0;
|
||||
|
||||
a {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.text {
|
||||
padding: $smaller;
|
||||
border-radius: $smaller;
|
||||
|
||||
@@ -20,10 +20,10 @@ import QueueActions from "@/components/RightSideBar/Queue/QueueActions.vue";
|
||||
import { FromOptions, Routes } from "@/composables/enums";
|
||||
import useQueueStore from "@/stores/queue";
|
||||
|
||||
import FolderSvg from "@/assets/icons/folder.svg";
|
||||
import SearchSvg from "@/assets/icons/search.svg";
|
||||
import AlbumSvg from "@/assets/icons/album.svg";
|
||||
import FolderSvg from "@/assets/icons/folder.svg";
|
||||
import PlaylistSvg from "@/assets/icons/playlist.svg";
|
||||
import SearchSvg from "@/assets/icons/search.svg";
|
||||
|
||||
import { RouteLocationRaw } from "vue-router";
|
||||
|
||||
@@ -120,9 +120,6 @@ const { name, icon: SourceIcon, location } = getSource();
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.queue-actions {
|
||||
|
||||
@@ -23,6 +23,7 @@ import SearchInput from "@/components/RightSideBar/SearchInput.vue";
|
||||
#ginner {
|
||||
max-width: 30rem;
|
||||
margin: 0 auto;
|
||||
border-radius: $small;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="settings-nav">
|
||||
<SimpleTitle :text="'Settings'" />
|
||||
<Nav />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SimpleTitle from "./SimpleTitle.vue";
|
||||
import Nav from "@/components/SettingsView/Nav.vue";
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.settings-nav {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr max-content;
|
||||
}
|
||||
</style>
|
||||
@@ -32,7 +32,6 @@ defineProps<{
|
||||
border-radius: 0.75rem;
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
.artist-image {
|
||||
width: 100%;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div v-tooltip="returnArtists()" style="width: auto;">
|
||||
<div class="ellip" v-if="artists[0] !== '' && artists.length > 1">
|
||||
<div v-tooltip="returnArtists()" style="width: auto">
|
||||
<div class="ellip" v-if="artists[0] === '' && artists.length === 1">
|
||||
<span>{{ albumartist }}</span>
|
||||
</div>
|
||||
<div class="ellip" v-else>
|
||||
<span v-for="artist in putCommas(artists)" :key="artist">{{
|
||||
artist
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="ellip" v-else>
|
||||
<span>{{ albumartist }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<input
|
||||
type="search"
|
||||
class="header-input rounded-sm pad-sm"
|
||||
placeholder="search here"
|
||||
v-model.trim="source"
|
||||
id="page-search"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import usePStore from "@/stores/pages/playlist";
|
||||
import useFolderStore from "@/stores/pages/folder";
|
||||
import useAlbumStore from "@/stores/pages/album";
|
||||
|
||||
import { storeToRefs } from "pinia";
|
||||
import { Routes } from "@/composables/enums";
|
||||
|
||||
const { query: playlistQuery } = storeToRefs(usePStore());
|
||||
const { query: folderQuery } = storeToRefs(useFolderStore());
|
||||
const { query: albumQuery } = storeToRefs(useAlbumStore());
|
||||
|
||||
const props = defineProps<{
|
||||
page: Routes | string;
|
||||
}>();
|
||||
|
||||
function getRef() {
|
||||
switch (props.page) {
|
||||
case Routes.playlist:
|
||||
return playlistQuery;
|
||||
|
||||
case Routes.folder:
|
||||
return folderQuery;
|
||||
|
||||
case Routes.album:
|
||||
return albumQuery;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const source = getRef();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.header-input {
|
||||
background-color: $gray3;
|
||||
outline: none;
|
||||
border: none;
|
||||
color: inherit;
|
||||
font-size: 1rem;
|
||||
z-index: 200;
|
||||
|
||||
&:focus {
|
||||
outline: solid;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -5,7 +5,9 @@
|
||||
@dblclick="emitUpdate(track)"
|
||||
@contextmenu.prevent="showMenu"
|
||||
>
|
||||
<div class="index t-center ellip">{{ index }}</div>
|
||||
<div class="index t-center ellip">
|
||||
{{ index }}
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div @click="emitUpdate(track)" class="thumbnail">
|
||||
<img
|
||||
@@ -54,6 +56,7 @@
|
||||
class="options-icon circular"
|
||||
:class="{ options_button_clicked }"
|
||||
@click.stop="showMenu"
|
||||
@dblclick.stop="() => {}"
|
||||
>
|
||||
<OptionSvg />
|
||||
</div>
|
||||
@@ -79,17 +82,17 @@ const artisttitle = ref<HTMLElement | null>(null);
|
||||
|
||||
const props = defineProps<{
|
||||
track: Track;
|
||||
index?: number;
|
||||
index: number;
|
||||
isPlaying: Boolean;
|
||||
isCurrent: Boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "updateQueue"): void;
|
||||
(e: "playThis"): void;
|
||||
}>();
|
||||
|
||||
function emitUpdate(track: Track) {
|
||||
emit("updateQueue");
|
||||
emit("playThis");
|
||||
}
|
||||
|
||||
function showMenu(e: Event) {
|
||||
@@ -118,7 +121,7 @@ function showMenu(e: Event) {
|
||||
|
||||
.song-album {
|
||||
max-width: max-content;
|
||||
cursor: pointer;
|
||||
cursor: pointer !important;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
@@ -128,10 +131,6 @@ function showMenu(e: Event) {
|
||||
.song-artists {
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
|
||||
.artist {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.index {
|
||||
@@ -158,7 +157,6 @@ function showMenu(e: Event) {
|
||||
|
||||
svg {
|
||||
transition: all 0.2s ease-in;
|
||||
// transform: rotate(90deg);
|
||||
stroke: $track-btn-svg;
|
||||
|
||||
circle {
|
||||
@@ -196,10 +194,6 @@ function showMenu(e: Event) {
|
||||
left: $small;
|
||||
top: $small;
|
||||
}
|
||||
|
||||
.title {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
|
||||
@@ -24,14 +24,7 @@
|
||||
</div>
|
||||
<hr />
|
||||
<div class="artist">
|
||||
<div class="ellip" v-if="track.artists[0] !== ''">
|
||||
<span v-for="artist in putCommas(track.artists)" :key="artist">{{
|
||||
artist
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="ellip" v-else>
|
||||
<span>{{ track.albumartist }}</span>
|
||||
</div>
|
||||
<ArtistName :artists="track.artists" :albumartist="track.albumartist" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -47,12 +40,12 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
|
||||
import { paths } from "@/config";
|
||||
import { putCommas } from "@/utils";
|
||||
import { Track } from "@/interfaces";
|
||||
import DelSvg from "@/assets/icons/delete.svg";
|
||||
import { showTrackContextMenu as showContext } from "@/composables/context";
|
||||
import DelSvg from "@/assets/icons/plus.svg";
|
||||
import { paths } from "@/config";
|
||||
import { Track } from "@/interfaces";
|
||||
import useQueueStore from "@/stores/queue";
|
||||
import ArtistName from "./ArtistName.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
track: Track;
|
||||
@@ -70,11 +63,11 @@ function showMenu(e: Event) {
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "PlayThis"): void;
|
||||
(e: "playThis"): void;
|
||||
}>();
|
||||
|
||||
const playThis = (track: Track) => {
|
||||
emit("PlayThis");
|
||||
emit("playThis");
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -103,7 +96,7 @@ const playThis = (track: Track) => {
|
||||
.remove-track {
|
||||
opacity: 0;
|
||||
transition: all 0.25s ease;
|
||||
transform: translateX(1rem) rotate(45deg);
|
||||
transform: scale(0.75) translateY(1rem);
|
||||
|
||||
&:hover {
|
||||
opacity: 1 !important;
|
||||
@@ -113,10 +106,9 @@ const playThis = (track: Track) => {
|
||||
&:hover {
|
||||
.remove-track {
|
||||
opacity: 0.5;
|
||||
transform: translateX(0) rotate(45deg);
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
background: linear-gradient(37deg, $gray4, $gray3, $gray3);
|
||||
}
|
||||
|
||||
@@ -146,6 +138,7 @@ const playThis = (track: Track) => {
|
||||
.artist {
|
||||
font-size: small;
|
||||
opacity: 0.67;
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user