@hello-pangea/dnd
Version:
Beautiful and accessible drag and drop for lists with React
95 lines (79 loc) • 2.33 kB
text/typescript
import { invariant } from '../../../../invariant';
import type {
DraggableDimension,
DroppableDimension,
DragImpact,
CombineImpact,
DraggableLocation,
DraggableId,
} from '../../../../types';
import { tryGetDestination } from '../../../get-impact-location';
import removeDraggableFromList from '../../../remove-draggable-from-list';
export interface Args {
isMovingForward: boolean;
draggable: DraggableDimension;
destination: DroppableDimension;
insideDestination: DraggableDimension[];
previousImpact: DragImpact;
}
export default ({
isMovingForward,
draggable,
destination,
insideDestination,
previousImpact,
}: Args): DragImpact | null => {
if (!destination.isCombineEnabled) {
return null;
}
const location: DraggableLocation | null = tryGetDestination(previousImpact);
if (!location) {
return null;
}
function getImpact(target: DraggableId) {
const at: CombineImpact = {
type: 'COMBINE',
combine: {
draggableId: target,
droppableId: destination.descriptor.id,
},
};
return {
...previousImpact,
at,
};
}
const all: DraggableId[] = previousImpact.displaced.all;
const closestId: DraggableId | null = all.length ? all[0] : null;
if (isMovingForward) {
return closestId ? getImpact(closestId) : null;
}
const withoutDraggable = removeDraggableFromList(
draggable,
insideDestination,
);
// Moving backwards
// if nothing is displaced - move backwards onto the last item
if (!closestId) {
if (!withoutDraggable.length) {
return null;
}
const last: DraggableDimension =
withoutDraggable[withoutDraggable.length - 1];
return getImpact(last.descriptor.id);
}
// We are moving from being between two displaced items
// backwards onto the first one
// need to find the first item before the closest
const indexOfClosest: number = withoutDraggable.findIndex(
(d) => d.descriptor.id === closestId,
);
invariant(indexOfClosest !== -1, 'Could not find displaced item in set');
const proposedIndex: number = indexOfClosest - 1;
// There is no displaced item before
if (proposedIndex < 0) {
return null;
}
const before: DraggableDimension = withoutDraggable[proposedIndex];
return getImpact(before.descriptor.id);
};