import { useSelector } from '../../useSelector'; import { NestedTableDragAndDropState } from './NestedTableDragAndDropState'; import { NestedTableKeyedItem } from '../../state'; /** * Factory function to create a custom hook `useIsDropDisabled` that determines * whether the drop is disabled for a given node based on drag-and-drop state. */ export const createUseIsDropDisabled = ( state: NestedTableDragAndDropState, ) => { return (targetNodeId: string | number): boolean => { // Get the ID of the currently active (dragged) item const activeItemId = useSelector(() => state.dnd.activeId); // If there's no active (dragged) item, disable dropping if (!activeItemId) { return true; } // Retrieve the state of the active (dragged) item and the target (hovered) item const activeItemState = state.nestedState.getKeyedItem(activeItemId)?.item.state; const targetItemState = state.nestedState.getKeyedItem( targetNodeId.toString(), )?.item.state; // If either the active or target node is missing, disable dropping if (!activeItemState || !targetItemState) { return true; } const activeKeyedItem = state.dnd.collection.getKeyedItem(activeItemId); const targetKeyedItem = state.dnd.collection.getKeyedItem( targetNodeId.toString(), ); if (!activeKeyedItem || !targetKeyedItem) { return true; } // If levels contain the same entity, dropping is always allowed if (state.isSameEntityAsOtherLevels) { return false; } // Get the index of the active (dragged) item and the target (hovered) item in the nested state const activeItemIndex = state.nestedState.getKeyedItem(activeItemId)?.index || 0; const targetItemIndex = state.nestedState.getKeyedItem(targetNodeId.toString())?.index || 0; // Get the previous and next items relative to the target item const previousItem = ( activeItemIndex >= targetItemIndex ? state.nestedState.keyedItems[targetItemIndex - 1] : state.nestedState.keyedItems[targetItemIndex] ) as NestedTableKeyedItem | null; const nextItem = ( activeItemIndex <= targetItemIndex ? state.nestedState.keyedItems[targetItemIndex + 1] : state.nestedState.keyedItems[targetItemIndex] ) as NestedTableKeyedItem | null; // Check if the dragged item is moving down to a sublevel const isMovingDownToSublevel = targetItemState.childrenDepth === activeItemState.childrenDepth - 1 && nextItem != null && nextItem.item.state.childrenDepth >= activeItemState.childrenDepth && targetItemIndex === previousItem?.index; if (isMovingDownToSublevel) { return false; } // Check if the dragged item is moving up to a sublevel const isMovingUpToSubLevel = targetItemState.childrenDepth === activeItemState.childrenDepth - 1 && previousItem?.item.state.childrenDepth === activeItemState.childrenDepth && (nextItem == null || nextItem.item.state.childrenDepth < activeItemState.childrenDepth) && targetItemIndex === nextItem?.index; if (isMovingUpToSubLevel) { return false; } // Determine if the target item has children const targetItemHasChildren = targetItemState.childrenDepth + 1 === nextItem?.item.state?.childrenDepth; // Check if the dragged item is on the same level without children const isSameLevelWithoutChildren = !targetItemHasChildren && targetItemState.childrenDepth === activeItemState.childrenDepth; if (isSameLevelWithoutChildren) { return false; } // Check if the dragged item is moving to the next level after a node without children const isNextLevelWithoutChildren = !targetItemHasChildren && previousItem?.item.state.childrenDepth === activeItemState.childrenDepth - 1; if (isNextLevelWithoutChildren) { return false; } // Check if the dragged item is stepping out to the same level (not entering a sublevel) const isStepOutToSameLevel = !targetItemHasChildren && previousItem != null && previousItem.item.state.childrenDepth >= activeItemState.childrenDepth + 1 && (nextItem == null || nextItem.item.state.childrenDepth <= activeItemState.childrenDepth); if (isStepOutToSameLevel) { return false; } return true; }; };