diff --git a/web-player/src/lib/components/player/Queue.svelte b/web-player/src/lib/components/player/Queue.svelte index bc89e63..d84f6a9 100644 --- a/web-player/src/lib/components/player/Queue.svelte +++ b/web-player/src/lib/components/player/Queue.svelte @@ -11,6 +11,28 @@ const idx = player.tracks.findIndex((t) => t.id === player.currentId); return idx === -1 ? player.tracks : player.tracks.slice(idx + 1); }); + + let dragFrom = $state(null); + let dropAt = $state(null); + + function onDragOver(event: DragEvent, index: number) { + if (dragFrom === null) return; + event.preventDefault(); + if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'; + const rect = (event.currentTarget as HTMLElement).getBoundingClientRect(); + dropAt = event.clientY < rect.top + rect.height / 2 ? index : index + 1; + } + + function onDrop(event: DragEvent) { + event.preventDefault(); + if (dragFrom !== null && dropAt !== null) player.moveQueueItem(dragFrom, dropAt); + endDrag(); + } + + function endDrag() { + dragFrom = null; + dropAt = null; + }
{#each upcoming as track, i (track.id)} {@const index = player.tracks.length - upcoming.length + i} - player.playQueueIndex(index)} - size="sm" - title={track.title} - subtitle={track.artist || '—'} - subtitleHref={track.ownerUserId} - trackId={track.id} - class="p-2" + +
{ + dragFrom = index; + if (event.dataTransfer) event.dataTransfer.effectAllowed = 'move'; + }} + ondragover={(event) => onDragOver(event, index)} + ondrop={onDrop} + ondragend={endDrag} + class="relative transition-opacity {dragFrom === index ? 'opacity-40' : ''}" > - {#snippet trailing()} - {#if track.duration} - {fmtTime(track.duration)} - {/if} - {/snippet} - + {#if dropAt === index && dragFrom !== null} +
+ {:else if dropAt === index + 1 && dropAt === player.tracks.length && dragFrom !== null} +
+ {/if} + player.playQueueIndex(index)} + size="sm" + title={track.title} + subtitle={track.artist || '—'} + subtitleHref={track.ownerUserId} + trackId={track.id} + class="p-2" + > + {#snippet trailing()} + {#if track.duration} + {fmtTime(track.duration)} + {/if} + {/snippet} + +
{:else}

No hay más canciones en la cola.

{/each} diff --git a/web-player/src/lib/player/player.svelte.ts b/web-player/src/lib/player/player.svelte.ts index 67f2fd8..c43c7e4 100644 --- a/web-player/src/lib/player/player.svelte.ts +++ b/web-player/src/lib/player/player.svelte.ts @@ -456,6 +456,16 @@ class PlayerState { this.#loadCurrent(); } + moveQueueItem(from: number, insertAt: number) { + if (from < 0 || from >= this.tracks.length) return; + const target = insertAt > from ? insertAt - 1 : insertAt; + if (target === from) return; + const tracks = [...this.tracks]; + const [item] = tracks.splice(from, 1); + tracks.splice(target, 0, item); + this.tracks = tracks; + } + clearUpcoming() { this.tracks = this.currentId === null ? [] : [this.current]; }