Merge branch 'feat/drag-preview-count' into main

This commit is contained in:
Geert Rademakes 2025-08-13 16:29:52 +02:00
commit 862c482565

View File

@ -189,6 +189,7 @@ export const PaginatedSongList: React.FC<PaginatedSongListProps> = memo(({
const [endDropHover, setEndDropHover] = useState<boolean>(false); const [endDropHover, setEndDropHover] = useState<boolean>(false);
const [isReorderDragging, setIsReorderDragging] = useState<boolean>(false); const [isReorderDragging, setIsReorderDragging] = useState<boolean>(false);
const lastSelectedIndexRef = useRef<number | null>(null); const lastSelectedIndexRef = useRef<number | null>(null);
const dragPreviewRef = useRef<HTMLDivElement | null>(null);
// Store current values in refs to avoid stale closures // Store current values in refs to avoid stale closures
const hasMoreRef = useRef(hasMore); 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('application/json', JSON.stringify(payload));
e.dataTransfer.setData('text/plain', JSON.stringify(payload)); e.dataTransfer.setData('text/plain', JSON.stringify(payload));
e.dataTransfer.effectAllowed = 'copyMove'; 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]); }, [selectedSongs]);
const handleDragEnd = useCallback(() => { const handleDragEnd = useCallback(() => {
@ -321,6 +368,11 @@ export const PaginatedSongList: React.FC<PaginatedSongListProps> = memo(({
setEndDropHover(false); setEndDropHover(false);
setDragHoverIndex(null); setDragHoverIndex(null);
dragSelectionRef.current = 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 // Virtualizer for large lists