mirror of
https://github.com/koel/koel
synced 2024-11-24 13:13:05 +00:00
feat: allow dragging and dropping playlist folders to queue (#1624)
This commit is contained in:
parent
c6c805c007
commit
9bb4e8c1f0
3 changed files with 38 additions and 10 deletions
|
@ -3,8 +3,10 @@
|
|||
class="playlist-folder"
|
||||
:class="{ droppable }"
|
||||
tabindex="0"
|
||||
draggable="true"
|
||||
@dragleave="onDragLeave"
|
||||
@dragover="onDragOver"
|
||||
@dragstart="onDragStart"
|
||||
@drop="onDrop"
|
||||
>
|
||||
<a @click.prevent="toggle" @contextmenu.prevent="onContextMenu">
|
||||
|
@ -32,7 +34,7 @@ import { faFolder, faFolderOpen } from '@fortawesome/free-solid-svg-icons'
|
|||
import { computed, defineAsyncComponent, ref, toRefs } from 'vue'
|
||||
import { playlistFolderStore, playlistStore } from '@/stores'
|
||||
import { eventBus } from '@/utils'
|
||||
import { useDroppable } from '@/composables'
|
||||
import { useDraggable, useDroppable } from '@/composables'
|
||||
|
||||
const PlaylistSidebarItem = defineAsyncComponent(() => import('./PlaylistSidebarItem.vue'))
|
||||
|
||||
|
@ -46,9 +48,12 @@ const droppableOnHatch = ref(false)
|
|||
const playlistsInFolder = computed(() => playlistStore.byFolder(folder.value))
|
||||
|
||||
const { acceptsDrop, resolveDroppedValue } = useDroppable(['playlist'])
|
||||
const { startDragging } = useDraggable('playlist-folder')
|
||||
|
||||
const toggle = () => (opened.value = !opened.value)
|
||||
|
||||
const onDragStart = (event: DragEvent) => startDragging(event, folder.value)
|
||||
|
||||
const onDragOver = (event: DragEvent) => {
|
||||
if (!acceptsDrop(event)) return false
|
||||
|
||||
|
@ -97,7 +102,11 @@ const onDropOnHatch = async (event: DragEvent) => {
|
|||
await playlistFolderStore.removePlaylistFromFolder(folder.value, playlist)
|
||||
}
|
||||
|
||||
const onContextMenu = event => eventBus.emit('PLAYLIST_FOLDER_CONTEXT_MENU_REQUESTED', event, folder.value)
|
||||
const onContextMenu = (event: MouseEvent) => eventBus.emit(
|
||||
'PLAYLIST_FOLDER_CONTEXT_MENU_REQUESTED',
|
||||
event,
|
||||
folder.value
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
import { faListOl } from '@fortawesome/free-solid-svg-icons'
|
||||
import { ref } from 'vue'
|
||||
import { queueStore } from '@/stores'
|
||||
import { useDroppable } from '@/composables'
|
||||
import { useDroppable, useMessageToaster } from '@/composables'
|
||||
|
||||
import SidebarItem from './SidebarItem.vue'
|
||||
import { pluralize } from '@/utils'
|
||||
|
||||
const { acceptsDrop, resolveDroppedSongs } = useDroppable(['songs', 'album', 'artist', 'playlist'])
|
||||
const { toastWarning, toastSuccess } = useMessageToaster()
|
||||
const { acceptsDrop, resolveDroppedSongs } = useDroppable(['songs', 'album', 'artist', 'playlist', 'playlist-folder'])
|
||||
|
||||
const droppable = ref(false)
|
||||
|
||||
|
@ -32,7 +34,13 @@ const onQueueDrop = async (event: DragEvent) => {
|
|||
|
||||
event.preventDefault()
|
||||
const songs = await resolveDroppedSongs(event) || []
|
||||
songs.length && queueStore.queue(songs)
|
||||
|
||||
if (songs.length) {
|
||||
queueStore.queue(songs)
|
||||
toastSuccess(`Added ${ pluralize(songs, 'song') } to queue.`)
|
||||
} else {
|
||||
toastWarning('No applicable songs to queue.')
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { arrayify, logger, pluralize } from '@/utils'
|
||||
import { albumStore, artistStore, playlistStore, songStore } from '@/stores'
|
||||
import { albumStore, artistStore, playlistFolderStore, playlistStore, songStore } from '@/stores'
|
||||
|
||||
type Draggable = Song | Song[] | Album | Artist | Playlist
|
||||
const draggableTypes = <const>['songs', 'album', 'artist', 'playlist']
|
||||
type Draggable = Song | Song[] | Album | Artist | Playlist | PlaylistFolder
|
||||
const draggableTypes = <const>['songs', 'album', 'artist', 'playlist', 'playlist-folder']
|
||||
type DraggableType = typeof draggableTypes[number]
|
||||
|
||||
const createGhostDragImage = (event: DragEvent, text: string): void => {
|
||||
|
@ -33,7 +33,7 @@ export const useDraggable = (type: DraggableType) => {
|
|||
return
|
||||
}
|
||||
|
||||
let text
|
||||
let text: string
|
||||
let data: any
|
||||
|
||||
switch (type) {
|
||||
|
@ -62,6 +62,12 @@ export const useDraggable = (type: DraggableType) => {
|
|||
data = dragged.id
|
||||
break
|
||||
|
||||
case 'playlist-folder':
|
||||
dragged = <PlaylistFolder>dragged
|
||||
text = dragged.name
|
||||
data = dragged.id
|
||||
break
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
@ -125,8 +131,13 @@ export const useDroppable = (acceptedTypes: DraggableType[]) => {
|
|||
const artist = await artistStore.resolve(<number>data)
|
||||
return artist ? await songStore.fetchForArtist(artist) : <Song[]>[]
|
||||
case 'playlist':
|
||||
const playlist = await playlistStore.byId(<number>data)
|
||||
const playlist = playlistStore.byId(<number>data)
|
||||
return playlist ? await songStore.fetchForPlaylist(playlist) : <Song[]>[]
|
||||
case 'playlist-folder':
|
||||
const folder = playlistFolderStore.byId(<string>data)
|
||||
return folder ? await songStore.fetchForPlaylistFolder(folder) : <Song[]>[]
|
||||
default:
|
||||
throw new Error(`Unknown drag type: ${type}`)
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(error, event)
|
||||
|
|
Loading…
Reference in a new issue