feat(frontend): custom drag preview with selection count badge when dragging multiple songs
This commit is contained in:
parent
febfb638b9
commit
6d2eae9c7b
@ -189,6 +189,7 @@ export const PaginatedSongList: React.FC<PaginatedSongListProps> = memo(({
|
||||
const [endDropHover, setEndDropHover] = useState<boolean>(false);
|
||||
const [isReorderDragging, setIsReorderDragging] = useState<boolean>(false);
|
||||
const lastSelectedIndexRef = useRef<number | null>(null);
|
||||
const dragPreviewRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
// Store current values in refs to avoid stale closures
|
||||
const hasMoreRef = useRef(hasMore);
|
||||
@ -313,6 +314,52 @@ export const PaginatedSongList: React.FC<PaginatedSongListProps> = memo(({
|
||||
e.dataTransfer.setData('application/json', JSON.stringify(payload));
|
||||
e.dataTransfer.setData('text/plain', JSON.stringify(payload));
|
||||
e.dataTransfer.effectAllowed = 'copyMove';
|
||||
|
||||
// Create a custom drag image with count badge when dragging multiple
|
||||
try {
|
||||
const count = ids.length;
|
||||
if (count > 1) {
|
||||
// Build a lightweight preview element
|
||||
const preview = document.createElement('div');
|
||||
preview.style.position = 'fixed';
|
||||
preview.style.top = '-1000px';
|
||||
preview.style.left = '-1000px';
|
||||
preview.style.pointerEvents = 'none';
|
||||
preview.style.padding = '6px 10px';
|
||||
preview.style.borderRadius = '8px';
|
||||
preview.style.background = 'rgba(26, 32, 44, 0.95)'; // gray.900
|
||||
preview.style.color = '#E2E8F0'; // gray.200
|
||||
preview.style.fontSize = '12px';
|
||||
preview.style.fontWeight = '600';
|
||||
preview.style.display = 'inline-flex';
|
||||
preview.style.alignItems = 'center';
|
||||
preview.style.gap = '8px';
|
||||
preview.style.boxShadow = '0 4px 12px rgba(0,0,0,0.5)';
|
||||
|
||||
const dot = document.createElement('div');
|
||||
dot.style.background = '#3182CE'; // blue.500
|
||||
dot.style.color = 'white';
|
||||
dot.style.minWidth = '20px';
|
||||
dot.style.height = '20px';
|
||||
dot.style.borderRadius = '10px';
|
||||
dot.style.display = 'flex';
|
||||
dot.style.alignItems = 'center';
|
||||
dot.style.justifyContent = 'center';
|
||||
dot.style.fontSize = '12px';
|
||||
dot.style.fontWeight = '700';
|
||||
dot.textContent = String(count);
|
||||
|
||||
const label = document.createElement('div');
|
||||
label.textContent = count === 1 ? 'song' : 'songs';
|
||||
|
||||
preview.appendChild(dot);
|
||||
preview.appendChild(label);
|
||||
document.body.appendChild(preview);
|
||||
dragPreviewRef.current = preview;
|
||||
// Offset so the cursor isn't on top of the preview
|
||||
e.dataTransfer.setDragImage(preview, 10, 10);
|
||||
}
|
||||
} catch {}
|
||||
}, [selectedSongs]);
|
||||
|
||||
const handleDragEnd = useCallback(() => {
|
||||
@ -321,6 +368,11 @@ export const PaginatedSongList: React.FC<PaginatedSongListProps> = memo(({
|
||||
setEndDropHover(false);
|
||||
setDragHoverIndex(null);
|
||||
dragSelectionRef.current = null;
|
||||
// Cleanup drag preview element
|
||||
if (dragPreviewRef.current && dragPreviewRef.current.parentNode) {
|
||||
try { dragPreviewRef.current.parentNode.removeChild(dragPreviewRef.current); } catch {}
|
||||
}
|
||||
dragPreviewRef.current = null;
|
||||
}, []);
|
||||
|
||||
// Virtualizer for large lists
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user