single track drag-and-drop ghost

This commit is contained in:
2026-02-23 09:42:11 -05:00
parent bd9652b378
commit d89dee44e3
5 changed files with 122 additions and 12 deletions
@@ -39,6 +39,7 @@ import type { DragPayload } from '@utils/drag-controller';
import {
createAlbumArtDragImage,
createDragImage,
createTrackCardDragImage,
removeDragImage,
} from '@utils/drag-image';
@@ -2850,9 +2851,16 @@ export class CoverGrid extends LitElement {
JSON.stringify(payload),
);
this.dragImageEl = createDragImage(
filePaths.length,
);
this.dragImageEl =
filePaths.length === 1
? createTrackCardDragImage(
track.TrackName,
track.ArtistName,
track.FilePath,
)
: createDragImage(
filePaths.length,
);
dataTransfer.setDragImage(
this.dragImageEl,
0,
@@ -37,6 +37,7 @@ import {
} from '@utils/drag-controller';
import {
createDragImage,
createTrackCardDragImage,
removeDragImage,
} from '@utils/drag-image';
@@ -1143,9 +1144,14 @@ export class PlaylistView
sourcePlaylistId: entry.summary.ID,
});
this.dragImageEl = createDragImage(
filePaths.length,
);
this.dragImageEl =
filePaths.length === 1
? createTrackCardDragImage(
track.Title,
track.Artist,
track.FilePath,
)
: createDragImage(filePaths.length);
e.dataTransfer?.setDragImage(
this.dragImageEl,
0,
@@ -26,6 +26,7 @@ import {
} from '@utils/drag-controller';
import {
createDragImage,
createTrackCardDragImage,
removeDragImage,
} from '@utils/drag-image';
@@ -1096,9 +1097,21 @@ export class QueuePanel
source: 'queue',
});
this.dragImageEl = createDragImage(
filePaths.length,
);
if (filePaths.length === 1) {
const t = tracks[index]!;
this.dragImageEl =
createTrackCardDragImage(
t.title,
t.artist,
t.filePath,
);
} else {
this.dragImageEl = createDragImage(
filePaths.length,
);
}
e.dataTransfer?.setDragImage(
this.dragImageEl,
0,
@@ -21,6 +21,7 @@ import {
} from '@utils/drag-controller';
import {
createDragImage,
createTrackCardDragImage,
removeDragImage,
} from '@utils/drag-image';
import '@lit-labs/virtualizer';
@@ -1135,9 +1136,14 @@ export class TrackList extends LitElement implements SelectionHost {
});
// Custom drag image.
this.dragImageEl = createDragImage(
filePaths.length,
);
this.dragImageEl =
filePaths.length === 1
? createTrackCardDragImage(
track.TrackName,
track.ArtistName,
track.FilePath,
)
: createDragImage(filePaths.length);
e.dataTransfer?.setDragImage(
this.dragImageEl,
0,
+77
View File
@@ -66,6 +66,83 @@ export function createAlbumArtDragImage(
return wrapper;
}
/**
* Creates a drag image styled like a queue track card showing the
* track title and artist. Used when dragging a single track.
*
* If `title` is empty and `filePath` is provided the filename
* (without extension) is used as a fallback. An empty `artist`
* falls back to "Unknown Artist".
*/
export function createTrackCardDragImage(
title: string,
artist: string,
filePath?: string,
): HTMLElement {
let displayTitle = title;
if (!displayTitle && filePath) {
const parts = filePath.split(/[\\/]/);
const filename =
parts[parts.length - 1] ?? filePath;
displayTitle = filename.replace(/\.[^.]+$/, '');
}
if (!displayTitle) {
displayTitle = 'Unknown Title';
}
const displayArtist = artist || 'Unknown Artist';
const card = document.createElement('div');
card.style.cssText = [
'position: fixed',
'top: -1000px',
'left: -1000px',
'max-width: 220px',
'padding: 8px 14px',
'border-radius: 6px',
'background: #2a2a2a',
'box-shadow: 0 2px 8px rgba(0,0,0,0.4)',
'pointer-events: none',
'z-index: 9999',
'display: flex',
'flex-direction: column',
'gap: 2px',
'font-family: inherit',
].join(';');
const titleEl = document.createElement('span');
titleEl.textContent = displayTitle;
titleEl.style.cssText = [
'font-size: 13px',
'color: #fff',
'white-space: nowrap',
'overflow: hidden',
'text-overflow: ellipsis',
].join(';');
const artistEl = document.createElement('span');
artistEl.textContent = displayArtist;
artistEl.style.cssText = [
'font-size: 11px',
'color: #b3b3b3',
'white-space: nowrap',
'overflow: hidden',
'text-overflow: ellipsis',
].join(';');
card.appendChild(titleEl);
card.appendChild(artistEl);
document.body.appendChild(card);
return card;
}
/** Remove a drag image element created by createDragImage. */
export function removeDragImage(el: HTMLElement): void {
el.remove();