fix(rundown): apply the active data fallback to drag over

expandOverGroup read the dragged element type straight from dnd-kit, so
once the virtualiser unmounted a dragged group the guard stopped matching
and dragging over a collapsed group expanded it. canDrop then saw an
expanded target and rejected an otherwise valid drop after that group.

Both handlers now resolve the active data through the same helper, which
falls back to the drag start snapshot.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEnUVARPnSqXijiz8KiLiE
This commit is contained in:
Claude
2026-09-16 19:01:30 +00:00
parent f4e70e6e99
commit 03dc07976e
@@ -1,4 +1,5 @@
import { import {
type Active,
type Data, type Data,
DragEndEvent, DragEndEvent,
DragOverEvent, DragOverEvent,
@@ -44,6 +45,15 @@ export function useRundownDnd({
const activeDataRef = useRef<Data | null>(null); const activeDataRef = useRef<Data | null>(null);
const [activeId, setActiveId] = useState<EntryId | null>(null); const [activeId, setActiveId] = useState<EntryId | null>(null);
/**
* Resolves the data of the dragged element
* If the element was unmounted by the virtualiser, dnd-kit gives us empty data
* in which case we fallback to the snapshot taken on drag start
*/
const getActiveData = useCallback((active: Active): Data | null => {
return active.data.current?.sortable ? active.data.current : activeDataRef.current;
}, []);
/** /**
* Discards any reference to the dragged element, also used as the drag cancel handler * Discards any reference to the dragged element, also used as the drag cancel handler
*/ */
@@ -59,9 +69,7 @@ export function useRundownDnd({
const handleOnDragEnd = useCallback( const handleOnDragEnd = useCallback(
(event: DragEndEvent) => { (event: DragEndEvent) => {
const { active, over } = event; const { active, over } = event;
// if the dragged element was unmounted by the virtualiser, dnd-kit gives us empty data const activeData = getActiveData(active);
// in which case we fallback to the snapshot taken on drag start
const activeData = active.data.current?.sortable ? active.data.current : activeDataRef.current;
clearActive(); clearActive();
if (!over?.id || active.id === over.id) { if (!over?.id || active.id === over.id) {
@@ -131,7 +139,7 @@ export function useRundownDnd({
setSortableData(currentEntries); setSortableData(currentEntries);
}); });
}, },
[entries, sortableData, setSortableData, getIsCollapsed, reorderEntry], [entries, sortableData, setSortableData, getIsCollapsed, reorderEntry, getActiveData, clearActive],
); );
/** /**
@@ -159,7 +167,8 @@ export function useRundownDnd({
const expandOverGroup = useCallback( const expandOverGroup = useCallback(
(event: DragOverEvent) => { (event: DragOverEvent) => {
// if we are dragging a group, the drop operation is invalid so we dont expand // if we are dragging a group, the drop operation is invalid so we dont expand
if (event.active.data.current?.type === SupportedEntry.Group) { // expanding the group here would also make an otherwise valid drop after it invalid
if (getActiveData(event.active)?.type === SupportedEntry.Group) {
return; return;
} }
if (event.over?.data.current?.type !== SupportedEntry.Group) { if (event.over?.data.current?.type !== SupportedEntry.Group) {
@@ -169,7 +178,7 @@ export function useRundownDnd({
const groupId = event.over?.id as EntryId | undefined; const groupId = event.over?.id as EntryId | undefined;
handleCollapseGroup(false, groupId); handleCollapseGroup(false, groupId);
}, },
[handleCollapseGroup], [handleCollapseGroup, getActiveData],
); );
return useMemo( return useMemo(