mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-04 04:23:01 +00:00
rename setting to "use alt now playing ..."
+ fix right now playing component gap + show emoji if there's no search result + abstract now playing component settings into one setting + break context menu's context item into a component
This commit is contained in:
@@ -61,7 +61,6 @@ onStartTyping(() => {
|
||||
const elem = document.getElementById("globalsearch") as HTMLInputElement;
|
||||
elem.focus();
|
||||
elem.value = "";
|
||||
document.getElementById("ginner")?.classList.add("search-focused");
|
||||
});
|
||||
|
||||
function handleWelcomeModal() {
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
.bg-primary {
|
||||
background-color: $gray4;
|
||||
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.425)
|
||||
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.425);
|
||||
}
|
||||
|
||||
.heading {
|
||||
@@ -79,6 +79,14 @@ button {
|
||||
background-image: linear-gradient(70deg, #234ece, $darkblue);
|
||||
}
|
||||
|
||||
.btn-more {
|
||||
width: 2.5rem;
|
||||
|
||||
svg {
|
||||
transform: scale(1.25);
|
||||
}
|
||||
}
|
||||
|
||||
.separator {
|
||||
border-top: 0.1px $separator solid;
|
||||
color: transparent;
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="context-item">
|
||||
<div class="icon image" :class="option.icon"></div>
|
||||
<div class="label ellip">{{ option.label }}</div>
|
||||
<div class="more image" v-if="option.children"></div>
|
||||
<div class="children rounded shadow-sm" v-if="option.children">
|
||||
<div
|
||||
class="context-item"
|
||||
v-for="child in option.children"
|
||||
:key="child.label"
|
||||
:class="[{ critical: child.critical }, child.type]"
|
||||
@click="child.action()"
|
||||
>
|
||||
<div class="label ellip">
|
||||
{{ child.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Option } from "@/interfaces";
|
||||
|
||||
defineProps<{
|
||||
option: Option;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.context-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: default;
|
||||
padding: 0.4rem 1rem;
|
||||
position: relative;
|
||||
|
||||
.more {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
position: absolute;
|
||||
right: $small;
|
||||
background-image: url("../assets/icons/expand.svg");
|
||||
}
|
||||
|
||||
.children {
|
||||
position: absolute;
|
||||
right: -13rem;
|
||||
width: 13rem;
|
||||
top: -0.5rem;
|
||||
max-height: 23.5rem;
|
||||
|
||||
background-color: $context;
|
||||
transform: scale(0);
|
||||
transform-origin: top left;
|
||||
padding: $small 0;
|
||||
|
||||
.context-item {
|
||||
padding: $small 1rem;
|
||||
}
|
||||
|
||||
.separator {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #234ece;
|
||||
|
||||
.children {
|
||||
transform: scale(1);
|
||||
transition: transform 0.1s ease-in-out;
|
||||
transition-delay: 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
margin-right: $small;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 9rem;
|
||||
}
|
||||
|
||||
.folder {
|
||||
background-image: url("../../assets/icons/folder.svg");
|
||||
}
|
||||
|
||||
.artist {
|
||||
background-image: url("../../assets/icons/artist.svg");
|
||||
}
|
||||
|
||||
.album {
|
||||
background-image: url("../../assets/icons/album.svg");
|
||||
}
|
||||
|
||||
.delete {
|
||||
background-image: url("../../assets/icons/delete.svg");
|
||||
}
|
||||
|
||||
.plus {
|
||||
background-image: url("../../assets/icons/plus.svg");
|
||||
}
|
||||
|
||||
.add_to_queue {
|
||||
background-image: url("../../assets/icons/add_to_queue.svg");
|
||||
}
|
||||
|
||||
.heart {
|
||||
background-image: url("../../assets/icons/heart.svg");
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -5,15 +5,15 @@
|
||||
<Navigation />
|
||||
</div>
|
||||
|
||||
<nowPlaying v-if="settings.use_side_np" />
|
||||
<nowPlaying v-if="!settings.use_alt_np" />
|
||||
<!-- <Playlists /> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Logo from "@/components/Logo.vue";
|
||||
import Navigation from "@/components/LeftSidebar/Navigation.vue";
|
||||
import nowPlaying from "@/components/LeftSidebar/nowPlaying.vue";
|
||||
import Logo from "@/components/Logo.vue";
|
||||
// import Playlists from "./components/LeftSidebar/Playlists.vue";
|
||||
|
||||
import useSettingsStore from "@/stores/settings";
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<div class="r-sidebar">
|
||||
<div
|
||||
class="r-sidebar"
|
||||
:style="{
|
||||
marginBottom: !settings.use_alt_np ? '-1rem' : '',
|
||||
}"
|
||||
>
|
||||
<div class="grid">
|
||||
<div class="r-content">
|
||||
<div class="r-dash" v-show="tabs.current === tabs.tabs.home">
|
||||
@@ -21,8 +26,10 @@ import Search from "./Search/Main.vue";
|
||||
import Queue from "./Queue.vue";
|
||||
import DashBoard from "./Home/Main.vue";
|
||||
import useTabStore from "../../stores/tabs";
|
||||
import useSettingsStore from "@/stores/settings";
|
||||
|
||||
const tabs = useTabStore();
|
||||
const settings = useSettingsStore();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="b-bar bg-primary pad-medium rounded" v-if="settings.use_right_np">
|
||||
<div class="b-bar bg-primary pad-medium rounded" v-if="settings.use_alt_np">
|
||||
<div class="info">
|
||||
<img
|
||||
:src="paths.images.thumb + queue.currenttrack?.image"
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button class="more-action action">
|
||||
<MoreSvg />
|
||||
</button>
|
||||
<Option />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -23,7 +21,7 @@ import useQueueStore from "../../../stores/queue";
|
||||
|
||||
import ClearSvg from "@/assets/icons/delete.svg";
|
||||
import ShuffleSvg from "@/assets/icons/shuffle.svg";
|
||||
import MoreSvg from "@/assets/icons/more.svg";
|
||||
import Option from "@/components/shared/Option.vue";
|
||||
|
||||
const queue = useQueueStore();
|
||||
</script>
|
||||
@@ -50,8 +48,9 @@ const queue = useQueueStore();
|
||||
}
|
||||
}
|
||||
|
||||
.more-action {
|
||||
.btn-more {
|
||||
padding-right: $smaller;
|
||||
|
||||
svg {
|
||||
transform: scale(1.25);
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<div class="artists-results">
|
||||
<div class="search-results-grid">
|
||||
<AlbumCard
|
||||
v-for="album in search.albums.value"
|
||||
:key="`${album.artist}-${album.title}`"
|
||||
:album="album"
|
||||
/>
|
||||
</div>
|
||||
<LoadMore v-if="search.albums.more" @loadMore="loadMore()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import AlbumCard from "../../shared/AlbumCard.vue";
|
||||
import LoadMore from "./LoadMore.vue";
|
||||
import useSearchStore from "../../../stores/search";
|
||||
|
||||
const search = useSearchStore();
|
||||
|
||||
function loadMore() {
|
||||
search.updateLoadCounter("albums");
|
||||
search.loadAlbums(search.loadCounter.albums);
|
||||
}
|
||||
</script>
|
||||
@@ -1,9 +1,15 @@
|
||||
<template>
|
||||
<div class="artists-results">
|
||||
<div class="search-results-grid" v-if="album_grid == true">
|
||||
<div
|
||||
class="search-results-grid"
|
||||
v-if="album_grid == true && search.albums.value.length"
|
||||
>
|
||||
<AlbumCard v-for="a in search.albums.value" :key="a.albumid" :album="a" />
|
||||
</div>
|
||||
<div class="search-results-grid" v-else>
|
||||
<div
|
||||
class="search-results-grid"
|
||||
v-else-if="!album_grid && search.artists.value.length"
|
||||
>
|
||||
<ArtistCard
|
||||
v-for="artist in search.artists.value"
|
||||
:key="artist.image"
|
||||
@@ -11,6 +17,7 @@
|
||||
:alt="true"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="t-center"><h5>🤷</h5></div>
|
||||
<LoadMore
|
||||
v-if="album_grid && search.albums.more"
|
||||
:loader="search.loadAlbums"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div id="tracks-results" v-if="search.tracks.value">
|
||||
<TransitionGroup name="list">
|
||||
<div id="tracks-results">
|
||||
<TransitionGroup name="list" v-if="search.tracks.value.length">
|
||||
<TrackItem
|
||||
v-for="(track, index) in search.tracks.value"
|
||||
:key="track.trackid"
|
||||
:key="track?.trackid"
|
||||
:track="track"
|
||||
:isPlaying="queue.playing"
|
||||
:isCurrent="queue.currentid == track.trackid"
|
||||
@@ -11,6 +11,7 @@
|
||||
@PlayThis="updateQueue(index)"
|
||||
/>
|
||||
</TransitionGroup>
|
||||
<div v-else class="t-center"><h5>🤷</h5></div>
|
||||
<LoadMore v-if="search.tracks.more" :loader="search.loadTracks" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<template>
|
||||
<div id="gsearch-input" class="rounded">
|
||||
<div
|
||||
id="ginner"
|
||||
tabindex="0"
|
||||
class="bg-primary rounded"
|
||||
>
|
||||
<div id="ginner" tabindex="0" class="bg-primary rounded">
|
||||
<input
|
||||
id="globalsearch"
|
||||
v-model="search.query"
|
||||
placeholder="Search your library"
|
||||
type="search"
|
||||
@blur.prevent="removeFocusedClass"
|
||||
@focus.prevent="addFocusedClass"
|
||||
/>
|
||||
<SearchSvg />
|
||||
</div>
|
||||
@@ -21,6 +19,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import QueueSvg from "../../assets/icons/queue.svg";
|
||||
import SearchSvg from "../../assets/icons/search.svg";
|
||||
import useSearchStore from "../../stores/search";
|
||||
@@ -28,6 +27,20 @@ import useTabStore from "../../stores/tabs";
|
||||
|
||||
const search = useSearchStore();
|
||||
const tabs = useTabStore();
|
||||
const focused = ref(false);
|
||||
let classList: DOMTokenList | undefined;
|
||||
|
||||
onMounted(() => {
|
||||
classList = document.getElementById("ginner")?.classList;
|
||||
});
|
||||
|
||||
function addFocusedClass() {
|
||||
classList?.add("search-focused");
|
||||
}
|
||||
|
||||
function removeFocusedClass() {
|
||||
classList?.remove("search-focused");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -17,35 +17,20 @@
|
||||
top: context.y + 'px',
|
||||
}"
|
||||
>
|
||||
<div
|
||||
<ContextItem
|
||||
class="context-item"
|
||||
v-for="option in context.options"
|
||||
:key="option.label"
|
||||
:class="[{ critical: option.critical }, option.type]"
|
||||
@click="() => option.action && option.action()"
|
||||
>
|
||||
<div class="icon image" :class="option.icon"></div>
|
||||
<div class="label ellip">{{ option.label }}</div>
|
||||
<div class="more image" v-if="option.children"></div>
|
||||
<div class="children rounded shadow-sm" v-if="option.children">
|
||||
<div
|
||||
class="context-item"
|
||||
v-for="child in option.children"
|
||||
:key="child.label"
|
||||
:class="[{ critical: child.critical }, child.type]"
|
||||
@click="child.action && child.action()"
|
||||
>
|
||||
<div class="label ellip">
|
||||
{{ child.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
:option="option"
|
||||
@click="option.action()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import useContextStore from "../stores/context";
|
||||
import ContextItem from "./Contextmenu/ContextItem.vue";
|
||||
|
||||
const context = useContextStore();
|
||||
</script>
|
||||
@@ -64,91 +49,6 @@ const context = useContextStore();
|
||||
transform-origin: top left;
|
||||
font-size: 0.875rem;
|
||||
|
||||
.context-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: default;
|
||||
padding: $small 1rem;
|
||||
position: relative;
|
||||
|
||||
.more {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
position: absolute;
|
||||
right: $small;
|
||||
background-image: url("../assets/icons/expand.svg");
|
||||
}
|
||||
|
||||
.children {
|
||||
position: absolute;
|
||||
right: -13rem;
|
||||
width: 13rem;
|
||||
top: -0.5rem;
|
||||
max-height: 23.5rem;
|
||||
|
||||
padding: $small 0 !important;
|
||||
background-color: $context;
|
||||
transform: scale(0);
|
||||
transform-origin: top left;
|
||||
|
||||
.context-item {
|
||||
padding: $small 1rem;
|
||||
}
|
||||
|
||||
.separator {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
margin-right: $small;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 9rem;
|
||||
}
|
||||
|
||||
.folder {
|
||||
background-image: url("../assets/icons/folder.svg");
|
||||
}
|
||||
|
||||
.artist {
|
||||
background-image: url("../assets/icons/artist.svg");
|
||||
}
|
||||
|
||||
.album {
|
||||
background-image: url("../assets/icons/album.svg");
|
||||
}
|
||||
|
||||
.delete {
|
||||
background-image: url("../assets/icons/delete.svg");
|
||||
}
|
||||
|
||||
.plus {
|
||||
background-image: url("../assets/icons/plus.svg");
|
||||
}
|
||||
|
||||
.add_to_queue {
|
||||
background-image: url("../assets/icons/add_to_queue.svg");
|
||||
}
|
||||
|
||||
.heart {
|
||||
background-image: url("../assets/icons/heart.svg");
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #234ece;
|
||||
|
||||
.children {
|
||||
transform: scale(1);
|
||||
transition: transform 0.1s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.separator {
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<button id="option-drop" @click.stop.prevent="showDropdown">
|
||||
<button id="option-drop" @click.stop.prevent="showDropdown" class="btn-more">
|
||||
<MoreSvg />
|
||||
</button>
|
||||
</template>
|
||||
@@ -8,7 +8,7 @@ import { onMounted } from "vue";
|
||||
import MoreSvg from "../../assets/icons/more.svg";
|
||||
let elem: DOMRect;
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
src?: string;
|
||||
}>();
|
||||
|
||||
@@ -22,19 +22,11 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
function showDropdown(e: Event) {
|
||||
if (!props.src) return;
|
||||
|
||||
emit("showDropdown", {
|
||||
clientX: elem.left + 35,
|
||||
clientY: elem.top,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
#option-drop {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
transform: scale(1.25);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -140,15 +140,16 @@ export default async (
|
||||
};
|
||||
|
||||
const options: Option[] = [
|
||||
add_to_playlist,
|
||||
play_next,
|
||||
add_to_q,
|
||||
// add_to_fav,
|
||||
separator,
|
||||
add_to_playlist,
|
||||
separator,
|
||||
go_to_folder,
|
||||
go_to_album,
|
||||
// add_to_fav,
|
||||
// go_to_artist,
|
||||
// go_to_alb_artist,
|
||||
go_to_album,
|
||||
// separator,
|
||||
// del_track,
|
||||
];
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ export interface Artist {
|
||||
export interface Option {
|
||||
type?: string;
|
||||
label?: string;
|
||||
action?: () => void;
|
||||
action: () => void;
|
||||
children?: Option[] | false;
|
||||
icon?: string;
|
||||
critical?: Boolean;
|
||||
|
||||
@@ -5,15 +5,9 @@ const settings = useSettingsStore;
|
||||
|
||||
export default [
|
||||
{
|
||||
title: "Use right bottom now playing card",
|
||||
title: "Use alternate now playing card",
|
||||
type: SettingType.switch,
|
||||
source: () => settings().use_right_np,
|
||||
source: () => settings().use_alt_np,
|
||||
action: () => settings().toggleUseRightNP(),
|
||||
},
|
||||
{
|
||||
title: "Use left now playing card",
|
||||
type: SettingType.switch,
|
||||
source: () => settings().use_side_np,
|
||||
action: () => settings().toggleUseSideNP(),
|
||||
},
|
||||
];
|
||||
|
||||
+9
-17
@@ -25,7 +25,6 @@ function shuffle(tracks: Track[]) {
|
||||
type From = fromFolder | fromAlbum | fromPlaylist | fromSearch;
|
||||
|
||||
let audio = new Audio();
|
||||
let elem: HTMLElement;
|
||||
|
||||
export default defineStore("Queue", {
|
||||
state: () => ({
|
||||
@@ -43,9 +42,6 @@ export default defineStore("Queue", {
|
||||
tracklist: [] as Track[],
|
||||
}),
|
||||
actions: {
|
||||
bindProgressElem() {
|
||||
elem = document.getElementById("progress");
|
||||
},
|
||||
play(index: number = 0) {
|
||||
if (this.tracklist.length === 0) return;
|
||||
this.current = index;
|
||||
@@ -53,7 +49,6 @@ export default defineStore("Queue", {
|
||||
this.currentid = track.trackid;
|
||||
const uri = state.settings.uri + "/file/" + track.hash;
|
||||
this.updateCurrent(index);
|
||||
this.bindProgressElem();
|
||||
|
||||
new Promise((resolve, reject) => {
|
||||
audio.autoplay = true;
|
||||
@@ -260,32 +255,29 @@ export default defineStore("Queue", {
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
getNextTrack() {
|
||||
getNextTrack(): Track {
|
||||
if (this.current == this.tracklist.length - 1) {
|
||||
return this.tracklist[0];
|
||||
} else {
|
||||
return this.tracklist[this.current + 1];
|
||||
}
|
||||
},
|
||||
getPrevTrack() {
|
||||
getPrevTrack(): Track {
|
||||
if (this.current === 0) {
|
||||
return this.tracklist[this.tracklist.length - 1];
|
||||
} else {
|
||||
return this.tracklist[this.current - 1];
|
||||
}
|
||||
},
|
||||
fullTime() {
|
||||
return audio.duration;
|
||||
},
|
||||
currentTime() {
|
||||
return audio.currentTime;
|
||||
},
|
||||
getCurrentTrack() {
|
||||
getCurrentTrack(): Track {
|
||||
return this.tracklist[this.current];
|
||||
},
|
||||
getIsplaying() {
|
||||
return audio.paused ? false : true;
|
||||
},
|
||||
persist: {
|
||||
afterRestore: (context) => {
|
||||
let store = context.store;
|
||||
store.duration.current = 0;
|
||||
store.playing = false;
|
||||
},
|
||||
},
|
||||
persist: true,
|
||||
});
|
||||
|
||||
@@ -3,20 +3,12 @@ import useQueueStore from "../queue";
|
||||
|
||||
export default defineStore("settings", {
|
||||
state: () => ({
|
||||
use_side_np: false,
|
||||
use_right_np: true,
|
||||
use_alt_np: false,
|
||||
}),
|
||||
actions: {
|
||||
toggleNPs() {
|
||||
this.use_side_np = !this.use_side_np;
|
||||
this.use_right_np = !this.use_right_np;
|
||||
useQueueStore().bindProgressElem();
|
||||
},
|
||||
toggleUseSideNP() {
|
||||
this.toggleNPs();
|
||||
},
|
||||
toggleUseRightNP() {
|
||||
this.toggleNPs();
|
||||
this.use_alt_np = !this.use_alt_np;
|
||||
useQueueStore();
|
||||
},
|
||||
},
|
||||
getters: {},
|
||||
|
||||
@@ -19,5 +19,5 @@ export default function focusElem(
|
||||
inline: "center",
|
||||
});
|
||||
}
|
||||
}, delay | 300);
|
||||
}, delay || 300);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user