drag and drop init
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Play, X } from "@lucide/svelte";
|
||||
import { GripVertical, Play, X } from "@lucide/svelte";
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import { player } from "$lib/player/store.svelte";
|
||||
import type { Track } from "$lib/player/types";
|
||||
@@ -14,6 +14,30 @@
|
||||
// If it's in the queue, we can just set currentId.
|
||||
player.playId(track.id);
|
||||
}
|
||||
|
||||
let dragOverIndex = $state<number | null>(null);
|
||||
|
||||
function onDragStart(e: DragEvent, index: number) {
|
||||
if (!e.dataTransfer) return;
|
||||
e.dataTransfer.effectAllowed = "move";
|
||||
e.dataTransfer.setData("text/plain", index.toString());
|
||||
}
|
||||
|
||||
function onDragOver(e: DragEvent, index: number) {
|
||||
e.preventDefault(); // allow drop
|
||||
if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
|
||||
dragOverIndex = index;
|
||||
}
|
||||
|
||||
function onDrop(e: DragEvent, toIndex: number) {
|
||||
e.preventDefault();
|
||||
dragOverIndex = null;
|
||||
const fromIndexStr = e.dataTransfer?.getData("text/plain");
|
||||
if (fromIndexStr) {
|
||||
const fromIndex = parseInt(fromIndexStr, 10);
|
||||
player.move(fromIndex, toIndex);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -40,27 +64,36 @@
|
||||
Queue is empty
|
||||
</div>
|
||||
{:else}
|
||||
{#each player.displayQueue as track (track.id)}
|
||||
{#each player.displayQueue as track, i (track.id)}
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
draggable="true"
|
||||
ondragstart={(e) => onDragStart(e, i)}
|
||||
ondragover={(e) => onDragOver(e, i)}
|
||||
ondrop={(e) => onDrop(e, i)}
|
||||
onclick={() => onJump(track)}
|
||||
onkeydown={(e) => e.key === "Enter" && onJump(track)}
|
||||
class="group flex items-center gap-2 px-3 py-2 rounded-md hover:bg-muted/50 transition-colors cursor-pointer text-sm"
|
||||
class:active={player.currentId === track.id}
|
||||
class:border-t-2={dragOverIndex === i}
|
||||
class:border-primary={dragOverIndex === i}
|
||||
>
|
||||
<div
|
||||
class="w-8 shrink-0 text-center text-xs text-muted-foreground/60 font-mono"
|
||||
class="w-8 shrink-0 flex items-center justify-center cursor-grab active:cursor-grabbing text-muted-foreground/50 hover:text-foreground"
|
||||
aria-label="Drag to reorder"
|
||||
>
|
||||
<GripVertical class="h-4 w-4" />
|
||||
</div>
|
||||
<div
|
||||
class="w-6 shrink-0 text-center text-xs text-muted-foreground/60 font-mono"
|
||||
>
|
||||
{#if player.currentId === track.id}
|
||||
<div
|
||||
class="w-2 h-2 bg-primary rounded-full mx-auto animate-pulse"
|
||||
></div>
|
||||
{:else}
|
||||
<span class="group-hover:hidden">#</span>
|
||||
<Play
|
||||
class="hidden group-hover:block mx-auto h-3 w-3 text-muted-foreground"
|
||||
/>
|
||||
<span class="text-xs">{i + 1}</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -216,6 +216,28 @@ class PlayerStore {
|
||||
}
|
||||
}
|
||||
|
||||
move(fromIdx: number, toIdx: number) {
|
||||
if (fromIdx === toIdx) return;
|
||||
|
||||
if (this.isShuffled) {
|
||||
const indices = [...this.shuffledIndices];
|
||||
if (fromIdx < 0 || fromIdx >= indices.length) return;
|
||||
if (toIdx < 0 || toIdx >= indices.length) return;
|
||||
|
||||
const [item] = indices.splice(fromIdx, 1);
|
||||
indices.splice(toIdx, 0, item);
|
||||
this.shuffledIndices = indices;
|
||||
} else {
|
||||
const q = [...this.queue];
|
||||
if (fromIdx < 0 || fromIdx >= q.length) return;
|
||||
if (toIdx < 0 || toIdx >= q.length) return;
|
||||
|
||||
const [item] = q.splice(fromIdx, 1);
|
||||
q.splice(toIdx, 0, item);
|
||||
this.queue = q;
|
||||
}
|
||||
}
|
||||
|
||||
// Playback Controls
|
||||
|
||||
next() {
|
||||
|
||||
Reference in New Issue
Block a user