react-beautiful-dnd
Version:
Beautiful, accessible drag and drop for lists with React.js
113 lines (92 loc) • 3.09 kB
JavaScript
// @flow
import type {
Axis,
DraggableDimension,
DraggableDimensionMap,
DragMovement,
DroppableDimension,
Position,
Area,
} from '../types';
import moveToEdge from './move-to-edge';
import getDraggablesInsideDroppable from './get-draggables-inside-droppable';
type NewHomeArgs = {|
movement: DragMovement,
draggable: DraggableDimension,
// all draggables in the system
draggables: DraggableDimensionMap,
destination: ?DroppableDimension,
|};
// Returns the client offset required to move an item from its
// original client position to its final resting position
export default ({
movement,
draggable,
draggables,
destination,
}: NewHomeArgs): Position => {
const homeCenter: Position = draggable.client.marginBox.center;
// not dropping anywhere
if (destination == null) {
return homeCenter;
}
const { displaced, isBeyondStartPosition } = movement;
const axis: Axis = destination.axis;
const isWithinHomeDroppable: boolean =
destination.descriptor.id === draggable.descriptor.droppableId;
// dropping back into home index
if (isWithinHomeDroppable && !displaced.length) {
return homeCenter;
}
// All the draggables in the destination (even the ones that haven't moved)
const draggablesInDestination: DraggableDimension[] = getDraggablesInsideDroppable(
destination, draggables
);
// Find the dimension we need to compare the dragged item with
const destinationFragment: Area = (() => {
if (isWithinHomeDroppable) {
return draggables[displaced[0].draggableId].client.marginBox;
}
// Not in home list
if (displaced.length) {
return draggables[displaced[0].draggableId].client.marginBox;
}
// If we're dragging to the last place in a new droppable
// which has items in it (but which haven't moved)
if (draggablesInDestination.length) {
return draggablesInDestination[
draggablesInDestination.length - 1
].client.marginBox;
}
// Otherwise, return the dimension of the empty foreign droppable
return destination.client.contentBox;
})();
const { sourceEdge, destinationEdge } = (() => {
if (isWithinHomeDroppable) {
if (isBeyondStartPosition) {
// move below the target
return { sourceEdge: 'end', destinationEdge: 'end' };
}
// move above the target
return { sourceEdge: 'start', destinationEdge: 'start' };
}
// not within our home droppable
// If we're moving in after the last draggable
// we want to move the draggable below the last item
if (!displaced.length && draggablesInDestination.length) {
return { sourceEdge: 'start', destinationEdge: 'end' };
}
// move above the target
return { sourceEdge: 'start', destinationEdge: 'start' };
})();
const source: Area = draggable.client.marginBox;
// This is the draggable's new home
const targetCenter: Position = moveToEdge({
source,
sourceEdge,
destination: destinationFragment,
destinationEdge,
destinationAxis: axis,
});
return targetCenter;
};