react-beautiful-dnd-next
Version:
Beautiful and accessible drag and drop for lists with React
112 lines (96 loc) • 3.13 kB
JavaScript
// @flow
import { offset, type Position, type BoxModel } from 'css-box-model';
import type {
Axis,
DraggableDimension,
DraggableDimensionMap,
DragMovement,
DroppableDimension,
OnLift,
} from '../../../types';
import { goBefore, goAfter, goIntoStart } from '../move-relative-to';
import getDraggablesInsideDroppable from '../../get-draggables-inside-droppable';
import { negate } from '../../position';
import didStartDisplaced from '../../starting-displaced/did-start-displaced';
type NewHomeArgs = {|
movement: DragMovement,
draggable: DraggableDimension,
draggables: DraggableDimensionMap,
droppable: DroppableDimension,
onLift: OnLift,
|};
// Returns the client offset required to move an item from its
// original client position to its final resting position
export default ({
movement,
draggable,
draggables,
droppable,
onLift,
}: NewHomeArgs): Position => {
const insideDestination: DraggableDimension[] = getDraggablesInsideDroppable(
droppable.descriptor.id,
draggables,
);
const draggablePage: BoxModel = draggable.page;
const axis: Axis = droppable.axis;
// this will only happen in a foreign list
if (!insideDestination.length) {
return goIntoStart({
axis,
moveInto: droppable.page,
isMoving: draggablePage,
});
}
const { displaced, displacedBy } = movement;
// go before the first displaced item
// items can only be displaced forwards
if (displaced.length) {
const closestAfter: DraggableDimension =
draggables[displaced[0].draggableId];
// want to go before where it would be with the displacement
// target is displaced and is already in it's starting position
if (didStartDisplaced(closestAfter.descriptor.id, onLift)) {
return goBefore({
axis,
moveRelativeTo: closestAfter.page,
isMoving: draggablePage,
});
}
// target has been displaced during the drag and it is not in its starting position
// we need to account for the displacement
const withDisplacement: BoxModel = offset(
closestAfter.page,
displacedBy.point,
);
return goBefore({
axis,
moveRelativeTo: withDisplacement,
isMoving: draggablePage,
});
}
// Nothing in list is displaced, we should go after the last item
const last: DraggableDimension =
insideDestination[insideDestination.length - 1];
// we can just go into our original position if the last item
// is the dragging item
if (last.descriptor.id === draggable.descriptor.id) {
return draggablePage.borderBox.center;
}
if (didStartDisplaced(last.descriptor.id, onLift)) {
// if the item started displaced and it is no longer displaced then
// we need to go after it it's non-displaced position
const page: BoxModel = offset(last.page, negate(onLift.displacedBy.point));
return goAfter({
axis,
moveRelativeTo: page,
isMoving: draggablePage,
});
}
// item is in its resting spot. we can go straight after it
return goAfter({
axis,
moveRelativeTo: last.page,
isMoving: draggablePage,
});
};