drag drop pt. 2

This commit is contained in:
2026-02-06 05:30:50 -08:00
parent 03be6760cc
commit c18f4e80b9
2 changed files with 83 additions and 14 deletions

View File

@@ -279,10 +279,12 @@
// We reorder by track.id (annSongId). The player module adjusts traversal state.
let dragId = $state<number | null>(null);
let dragOverId = $state<number | null>(null);
let dragOverEdge = $state<"top" | "bottom" | null>(null);
function onDragStart(trackId: number, e: DragEvent) {
dragId = trackId;
dragOverId = null;
dragOverEdge = null;
// Best-effort: set payload (useful for some browsers)
try {
@@ -293,10 +295,22 @@
}
}
function edgeFromPointer(e: DragEvent) {
const el = e.currentTarget as HTMLElement | null;
if (!el) return "bottom" as const;
const rect = el.getBoundingClientRect();
const y = e.clientY;
const mid = rect.top + rect.height / 2;
return y < mid ? ("top" as const) : ("bottom" as const);
}
function onDragOver(trackId: number, e: DragEvent) {
// Required to allow dropping
e.preventDefault();
dragOverId = trackId;
dragOverEdge = edgeFromPointer(e);
try {
e.dataTransfer!.dropEffect = "move";
@@ -319,22 +333,59 @@
if (sourceId == null) {
dragId = null;
dragOverId = null;
dragOverEdge = null;
return;
}
if (sourceId === targetTrackId) {
dragId = null;
dragOverId = null;
return;
}
// Reorder by queue indices
// Reorder by queue indices (insert between rows based on pointer edge)
const fromIndex = snap.queue.findIndex((t) => t.id === sourceId);
const toIndex = snap.queue.findIndex((t) => t.id === targetTrackId);
const targetIndex = snap.queue.findIndex((t) => t.id === targetTrackId);
if (fromIndex === -1 || toIndex === -1) {
if (fromIndex === -1 || targetIndex === -1) {
dragId = null;
dragOverId = null;
dragOverEdge = null;
return;
}
const edge = dragOverEdge ?? edgeFromPointer(e);
// Compute insertion index in the *post-removal* list.
//
// We interpret:
// - edge === "top" -> insert before target row
// - edge === "bottom" -> insert after target row
//
// Because `reorderTrackById` uses "toIndex in current queue", we must
// translate the desired insertion slot into that API carefully.
//
// Approach:
// - Convert "insert after" into a "before the next item" index.
// - Adjust for the fact that removing `fromIndex` shifts indices.
let insertIndex = edge === "top" ? targetIndex : targetIndex + 1;
// Clamp to [0..len]
insertIndex = Math.max(0, Math.min(snap.queue.length, insertIndex));
// If moving downward and inserting after/before beyond itself, removal shifts indices.
// `reorderTrackById` expects a final *index of the moved item*, so we convert the
// insertion slot (0..len) into a destination index (0..len-1).
let toIndex: number;
if (insertIndex <= fromIndex) {
// You're inserting somewhere before (or at) the source position:
// final index is exactly the insert slot.
toIndex = insertIndex;
} else {
// You're inserting after the source position; after removal, everything shifts left by 1.
// The moved item ends up at insertIndex - 1.
toIndex = insertIndex - 1;
}
// If dropping "between" where it already is, no-op
if (toIndex === fromIndex) {
dragId = null;
dragOverId = null;
dragOverEdge = null;
return;
}
@@ -342,17 +393,31 @@
dragId = null;
dragOverId = null;
dragOverEdge = null;
}
function onDragEnd() {
dragId = null;
dragOverId = null;
dragOverEdge = null;
}
function draggableHint(trackId: number) {
if (dragId == null) return "";
if (dragId === trackId) return "opacity-70";
if (dragOverId === trackId) return "ring-2 ring-primary/40";
if (dragOverId === trackId) {
if (dragOverEdge === "top") return "ring-2 ring-primary/40 ring-offset-0";
if (dragOverEdge === "bottom")
return "ring-2 ring-primary/40 ring-offset-0";
return "ring-2 ring-primary/40 ring-offset-0";
}
return "";
}
function dropIndicatorClass(trackId: number) {
if (dragOverId !== trackId) return "";
if (dragOverEdge === "top") return "border-t-2 border-t-primary/50";
if (dragOverEdge === "bottom") return "border-b-2 border-b-primary/50";
return "";
}
@@ -783,7 +848,9 @@
<li
class={[
"flex items-center gap-2 border-b px-2 py-2 last:border-b-0",
// visual affordances
draggableHint(item.track.id),
dropIndicatorClass(item.track.id),
]
.filter(Boolean)
.join(" ")}
@@ -1070,7 +1137,9 @@
<li
class={[
"flex items-center gap-2 border-b px-2 py-2 last:border-b-0",
// visual affordances
draggableHint(item.track.id),
dropIndicatorClass(item.track.id),
]
.filter(Boolean)
.join(" ")}

View File

@@ -67,20 +67,20 @@
}
</script>
<div class="rounded border px-3 py-2">
<div class="flex flex-wrap items-baseline gap-x-2 gap-y-1">
<div class="rounded border flex-row px-3 py-2">
<div class="flex flex-wrap w-fit items-baseline gap-x-2 gap-y-1">
<span class="rounded bg-muted px-2 py-0.5 text-sm text-muted-foreground"
>{displayTypeNumber}</span
>
{animeName}
</div>
<div class="mt-1 text-foreground/80">
<div class="mt-1 w-fit text-foreground/80">
{songName}
<span class="text-sm text-muted-foreground">{artistDisplay}</span>
</div>
<div class="mt-2 flex flex-wrap items-center gap-2">
<div class="mt-2 w-fitflex flex-wrap items-center gap-2">
<button
type="button"
class="btn-icon"