fix: Warning toasts when user drops folder item outside of dnd context (#38304)

This commit is contained in:
Kamil Gabryjelski
2026-02-28 06:23:19 +01:00
committed by GitHub
parent 7f280f5de9
commit 1d141b2948

View File

@@ -69,9 +69,6 @@ export function useDragHandlers({
const [draggedFolderChildIds, setDraggedFolderChildIds] = useState<
Set<string>
>(new Set());
// Last non-null overId — fallback target when dropping in dead zones
const lastValidOverIdRef = useRef<UniqueIdentifier | null>(null);
// Store the flattened items at drag start to keep them stable during drag
// This prevents react-window from re-rendering due to flattenedItems reference changes
const dragStartFlattenedItemsRef = useRef<FlattenedTreeItem[] | null>(null);
@@ -159,11 +156,23 @@ export function useDragHandlers({
[childrenByParentId],
);
const warnItemsOutsideFolders = useCallback(
(hasDraggedColumn: boolean, hasDraggedMetric: boolean) => {
if (hasDraggedColumn && hasDraggedMetric) {
addWarningToast(t('Columns and metrics should be inside folders'));
} else if (hasDraggedColumn) {
addWarningToast(t('Columns should be inside folders'));
} else if (hasDraggedMetric) {
addWarningToast(t('Metrics should be inside folders'));
}
},
[addWarningToast],
);
const resetDragState = useCallback(() => {
setActiveId(null);
setOverId(null);
offsetLeftRef.current = 0;
lastValidOverIdRef.current = null;
setCurrentDropTargetId(null);
setDraggedItemIds(new Set());
setDraggedFolderChildIds(new Set());
@@ -238,9 +247,6 @@ export function useDragHandlers({
const handleDragOver = useCallback(
({ over }: DragOverEvent) => {
setOverId(over?.id ?? null);
if (over) {
lastValidOverIdRef.current = over.id;
}
if (activeId && over) {
if (typeof over.id === 'string' && over.id.endsWith('-empty')) {
@@ -270,22 +276,30 @@ export function useDragHandlers({
const itemsBeingDragged = Array.from(draggedItemIds);
const folderChildIds = draggedFolderChildIds;
const finalOffsetLeft = offsetLeftRef.current;
// Capture fallback overId before reset (for dead-zone drops)
const fallbackOverId = lastValidOverIdRef.current;
resetDragState();
// Folder drags only: hidden children create dead zones where over is null.
// Regular drags with null over just cancel.
const effectiveOver =
over ??
(folderChildIds.size > 0 && fallbackOverId
? { id: fallbackOverId }
: null);
if (!effectiveOver || itemsBeingDragged.length === 0) {
if (itemsBeingDragged.length === 0) {
return;
}
let targetOverId = effectiveOver.id;
if (!over) {
let hasDraggedColumn = false;
let hasDraggedMetric = false;
for (const id of itemsBeingDragged) {
const item = fullItemsByUuid.get(id);
if (item) {
if (item.type === FoldersEditorItemType.Column) {
hasDraggedColumn = true;
} else if (item.type === FoldersEditorItemType.Metric) {
hasDraggedMetric = true;
}
}
}
warnItemsOutsideFolders(hasDraggedColumn, hasDraggedMetric);
return;
}
let targetOverId = over.id;
let isEmptyDrop = false;
if (typeof targetOverId === 'string' && targetOverId.endsWith('-empty')) {
targetOverId = targetOverId.replace('-empty', '');
@@ -396,13 +410,7 @@ export function useDragHandlers({
if (hasNonFolderItems) {
if (!projectedPosition || !projectedPosition.parentId) {
if (hasDraggedColumn && hasDraggedMetric) {
addWarningToast(t('Columns and metrics should be inside folders'));
} else if (hasDraggedColumn) {
addWarningToast(t('Columns should be inside folders'));
} else {
addWarningToast(t('Metrics should be inside folders'));
}
warnItemsOutsideFolders(hasDraggedColumn, hasDraggedMetric);
return;
}
}