mirror of
https://github.com/koel/koel
synced 2024-11-28 06:50:27 +00:00
fix: broken song reordering (#1524)
This commit is contained in:
parent
d7a361e532
commit
2edbda54d5
2 changed files with 29 additions and 45 deletions
|
@ -87,11 +87,11 @@
|
|||
:item="item"
|
||||
draggable="true"
|
||||
@click="rowClicked(item, $event)"
|
||||
@dragleave="removeDroppableState"
|
||||
@dragstart="rowDragStart(item, $event)"
|
||||
@dragenter.prevent="allowDrop"
|
||||
@dragleave="onDragLeave"
|
||||
@dragstart="onDragStart(item, $event)"
|
||||
@dragenter.prevent="onDragEnter"
|
||||
@dragover.prevent
|
||||
@drop.prevent="handleDrop(item, $event)"
|
||||
@drop.prevent="onDrop(item, $event)"
|
||||
@contextmenu.prevent="openContextMenu(item, $event)"
|
||||
/>
|
||||
</VirtualScroller>
|
||||
|
@ -103,9 +103,9 @@ import { findIndex } from 'lodash'
|
|||
import isMobile from 'ismobilejs'
|
||||
import { faAngleDown, faAngleUp } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faClock } from '@fortawesome/free-regular-svg-icons'
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
||||
import { computed, onMounted, Ref, ref, watch } from 'vue'
|
||||
import { eventBus, requireInjection } from '@/utils'
|
||||
import { useDraggable } from '@/composables'
|
||||
import { useDraggable, useDroppable } from '@/composables'
|
||||
import {
|
||||
ScreenNameKey,
|
||||
SelectedSongsKey,
|
||||
|
@ -119,15 +119,16 @@ import VirtualScroller from '@/components/ui/VirtualScroller.vue'
|
|||
import SongListItem from '@/components/song/SongListItem.vue'
|
||||
|
||||
const { startDragging } = useDraggable('songs')
|
||||
const { getDroppedData, acceptsDrop } = useDroppable(['songs'])
|
||||
|
||||
const emit = defineEmits(['press:enter', 'press:delete', 'reorder', 'sort', 'scroll-breakpoint', 'scrolled-to-end'])
|
||||
|
||||
const [items] = requireInjection(SongsKey)
|
||||
const [items] = requireInjection<[Ref<Song[]>]>(SongsKey)
|
||||
const [screen] = requireInjection<[ScreenName]>(ScreenNameKey)
|
||||
const [selectedSongs, setSelectedSongs] = requireInjection(SelectedSongsKey)
|
||||
const [sortField, setSortField] = requireInjection(SongListSortFieldKey)
|
||||
const [sortOrder, setSortOrder] = requireInjection(SongListSortOrderKey)
|
||||
const [injectedConfig] = requireInjection(SongListConfigKey, {})
|
||||
const [selectedSongs, setSelectedSongs] = requireInjection<[Ref<Song[]>, Closure]>(SelectedSongsKey)
|
||||
const [sortField, setSortField] = requireInjection<[Ref<SongListSortField>, Closure]>(SongListSortFieldKey)
|
||||
const [sortOrder, setSortOrder] = requireInjection<[Ref<SortOrder>, Closure]>(SongListSortOrderKey)
|
||||
const [injectedConfig] = requireInjection<[Partial<SongListConfig>]>(SongListConfigKey, [{}])
|
||||
|
||||
const lastSelectedRow = ref<SongRow>()
|
||||
const sortFields = ref<SongListSortField[]>([])
|
||||
|
@ -250,12 +251,7 @@ const selectRowsBetween = (first: SongRow, second: SongRow) => {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable dragging songs by capturing the dragstart event on a table row.
|
||||
* Even though the event is triggered on one row only, we'll collect other
|
||||
* selected rows, if any, as well.
|
||||
*/
|
||||
const rowDragStart = (row: SongRow, event: DragEvent) => {
|
||||
const onDragStart = (row: SongRow, event: DragEvent) => {
|
||||
// If the user is dragging an unselected row, clear the current selection.
|
||||
if (!row.selected) {
|
||||
clearSelection()
|
||||
|
@ -265,52 +261,42 @@ const rowDragStart = (row: SongRow, event: DragEvent) => {
|
|||
startDragging(event, selectedSongs.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a "droppable" class and set the drop effect when other songs are dragged over a row.
|
||||
*/
|
||||
const allowDrop = (event: DragEvent) => {
|
||||
if (!allowReordering) return;
|
||||
const onDragEnter = (event: DragEvent) => {
|
||||
if (!allowReordering) return
|
||||
|
||||
(event.target as Element).parentElement?.classList.add('droppable')
|
||||
event.dataTransfer!.dropEffect = 'move'
|
||||
if (acceptsDrop(event)) {
|
||||
(event.target as Element).parentElement?.classList.add('droppable')
|
||||
event.dataTransfer!.dropEffect = 'move'
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const handleDrop = (item: SongRow, event: DragEvent) => {
|
||||
if (
|
||||
!allowReordering ||
|
||||
!event.dataTransfer!.getData('application/x-koel.text+plain') ||
|
||||
!selectedSongs.value.length
|
||||
) {
|
||||
return removeDroppableState(event)
|
||||
const onDrop = (item: SongRow, event: DragEvent) => {
|
||||
if (!allowReordering || !getDroppedData(event) || !selectedSongs.value.length) {
|
||||
return onDragLeave(event)
|
||||
}
|
||||
|
||||
emit('reorder', item.song)
|
||||
return removeDroppableState(event)
|
||||
return onDragLeave(event)
|
||||
}
|
||||
|
||||
const removeDroppableState = (event: DragEvent) => {
|
||||
const onDragLeave = (event: DragEvent) => {
|
||||
(event.target as Element).parentElement?.classList.remove('droppable')
|
||||
return false
|
||||
}
|
||||
|
||||
const openContextMenu = async (row: SongRow, event: MouseEvent) => {
|
||||
// If the user is right-clicking an unselected row,
|
||||
// clear the current selection and select it instead.
|
||||
if (!row.selected) {
|
||||
clearSelection()
|
||||
toggleRow(row)
|
||||
}
|
||||
|
||||
await nextTick()
|
||||
|
||||
eventBus.emit('SONG_CONTEXT_MENU_REQUESTED', event, selectedSongs.value)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getAllSongsWithSort,
|
||||
sort
|
||||
getAllSongsWithSort
|
||||
})
|
||||
|
||||
onMounted(() => render())
|
||||
|
|
|
@ -2,7 +2,8 @@ import { arrayify, logger, pluralize } from '@/utils'
|
|||
import { albumStore, artistStore, playlistStore, songStore } from '@/stores'
|
||||
|
||||
type Draggable = Song | Song[] | Album | Artist | Playlist
|
||||
type DraggableType = 'songs' | 'album' | 'artist' | 'playlist'
|
||||
const draggableTypes = <const>['songs', 'album', 'artist', 'playlist']
|
||||
type DraggableType = typeof draggableTypes[number]
|
||||
|
||||
const createGhostDragImage = (event: DragEvent, text: string): void => {
|
||||
if (!event.dataTransfer) {
|
||||
|
@ -23,11 +24,7 @@ const createGhostDragImage = (event: DragEvent, text: string): void => {
|
|||
}
|
||||
|
||||
const getDragType = (event: DragEvent) => {
|
||||
const types: DraggableType[] = ['songs', 'album', 'artist', 'playlist']
|
||||
|
||||
for (let i = 0, count = types.length; i < count; ++i) {
|
||||
if (event.dataTransfer?.types.includes(`application/x-koel.${types[i]}`)) return types[i]
|
||||
}
|
||||
return draggableTypes.find(type => event.dataTransfer?.types.includes(`application/x-koel.${type}`))
|
||||
}
|
||||
|
||||
export const useDraggable = (type: DraggableType) => {
|
||||
|
@ -139,6 +136,7 @@ export const useDroppable = (acceptedTypes: DraggableType[]) => {
|
|||
|
||||
return {
|
||||
acceptsDrop,
|
||||
getDroppedData,
|
||||
resolveDroppedValue,
|
||||
resolveDroppedSongs
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue