koel/resources/assets/js/components/song/AddToMenu.vue

199 lines
4.8 KiB
Vue
Raw Normal View History

2022-04-15 14:24:30 +00:00
<template>
<div class="add-to" data-testid="add-to-menu" tabindex="0">
2022-04-15 14:24:30 +00:00
<section class="existing-playlists">
<p>Add {{ pluralize(songs, 'song') }} to</p>
2022-04-15 14:24:30 +00:00
<ul>
<template v-if="config.queue">
<template v-if="queue.length">
<li
v-if="currentSong"
class="queue-after-current"
data-testid="queue-after-current"
tabindex="0"
@click="queueSongsAfterCurrent"
>
After Current Song
</li>
<li class="bottom-queue" data-testid="queue-bottom" tabindex="0" @click="queueSongsToBottom">
Bottom of Queue
</li>
<li class="top-queue" data-testid="queue-top" tabindex="0" @click="queueSongsToTop">Top of Queue</li>
</template>
<li v-else data-testid="queue" tabindex="0" @click="queueSongsToBottom">Queue</li>
2022-04-15 14:24:30 +00:00
</template>
<li
v-if="config.favorites"
2022-04-15 14:24:30 +00:00
class="favorites"
data-testid="add-to-favorites"
2022-04-15 14:24:30 +00:00
tabindex="0"
@click="addSongsToFavorite"
2022-04-15 14:24:30 +00:00
>
Favorites
</li>
<template v-if="config.playlists">
<li
v-for="playlist in playlists"
2022-04-15 14:24:30 +00:00
:key="playlist.id"
class="playlist"
data-testid="add-to-playlist"
2022-04-15 14:24:30 +00:00
tabindex="0"
@click="addSongsToExistingPlaylist(playlist)"
2022-04-15 14:24:30 +00:00
>
{{ playlist.name }}
</li>
</template>
</ul>
</section>
<section v-if="config.newPlaylist" class="new-playlist" data-testid="new-playlist">
2022-04-15 14:24:30 +00:00
<p>or create a new playlist</p>
<form class="form-save form-simple form-new-playlist" @submit.prevent="createNewPlaylistFromSongs">
<input
v-model="newPlaylistName"
data-testid="new-playlist-name"
placeholder="Playlist name"
2022-04-15 14:24:30 +00:00
required
type="text"
@keyup.esc.prevent="close"
2022-04-15 14:24:30 +00:00
>
<Btn title="Save" type="submit"></Btn>
2022-04-15 14:24:30 +00:00
</form>
</section>
</div>
</template>
2022-04-15 17:00:08 +00:00
<script lang="ts" setup>
import { computed, nextTick, ref, toRef, toRefs, watch } from 'vue'
2022-11-18 18:44:20 +00:00
import { pluralize } from '@/utils'
import { playlistStore, queueStore } from '@/stores'
2022-11-18 18:44:20 +00:00
import { useMessageToaster, useRouter, useSongMenuMethods } from '@/composables'
2022-04-15 14:24:30 +00:00
import Btn from '@/components/ui/Btn.vue'
2022-04-15 17:00:08 +00:00
const { toastSuccess } = useMessageToaster()
2022-11-18 18:44:20 +00:00
const { go } = useRouter()
const props = defineProps<{ songs: Song[], config: AddToMenuConfig }>()
const { songs, config } = toRefs(props)
2022-04-15 17:00:08 +00:00
const newPlaylistName = ref('')
const queue = toRef(queueStore.state, 'songs')
const currentSong = queueStore.current
const allPlaylists = toRef(playlistStore.state, 'playlists')
const playlists = computed(() => allPlaylists.value.filter(playlist => !playlist.is_smart))
2022-04-15 17:00:08 +00:00
const emit = defineEmits<{ (e: 'closing'): void }>()
2022-04-15 17:00:08 +00:00
const close = () => emit('closing')
const {
queueSongsAfterCurrent,
queueSongsToBottom,
queueSongsToTop,
addSongsToFavorite,
addSongsToExistingPlaylist
2022-04-20 10:20:09 +00:00
} = useSongMenuMethods(songs, close)
2022-04-15 17:00:08 +00:00
watch(songs, () => songs.value.length || close())
/**
* Save the selected songs as a playlist.
* As of current we don't have selective save.
*/
const createNewPlaylistFromSongs = async () => {
newPlaylistName.value = newPlaylistName.value.trim()
if (!newPlaylistName.value) {
return
2022-04-15 14:24:30 +00:00
}
2022-04-15 17:00:08 +00:00
const playlist = await playlistStore.store(newPlaylistName.value, {}, songs.value)
2022-04-15 17:00:08 +00:00
newPlaylistName.value = ''
toastSuccess(`Playlist "${playlist.name}" created.`)
2022-04-15 17:00:08 +00:00
// Activate the new playlist right away
await nextTick()
2022-11-18 18:44:20 +00:00
go(`playlist/${playlist.id}`)
2022-04-15 17:00:08 +00:00
close()
}
2022-04-15 14:24:30 +00:00
</script>
<style lang="scss" scoped>
.add-to {
width: 100%;
max-width: 225px;
padding: .75rem;
> * + * {
margin-top: 1rem;
}
p {
margin-bottom: .5rem;
font-size: .9rem;
}
.new-playlist {
margin-top: .5rem;
}
ul {
max-height: 12rem;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
> li + li {
margin-top: .3rem;
}
}
li {
height: 2.25rem;
line-height: 2.25rem;
padding: 0 .75rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
border-radius: 3px;
background: rgba(255, 255, 255, .05);
cursor: pointer;
&:hover {
background: var(--color-highlight);
color: var(--color-text-primary);
}
}
form {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 3px;
overflow: hidden;
2022-04-15 14:24:30 +00:00
input[type="text"] {
width: 100%;
height: 28px;
border-radius: 0;
2022-04-15 14:24:30 +00:00
}
button[type="submit"] {
margin-top: 0;
border-radius: 0;
2022-04-15 14:24:30 +00:00
height: 28px;
line-height: 28px;
padding-top: 0;
padding-bottom: 0;
margin-left: -2px !important;
}
}
}
</style>