react-beautiful-dnd
Version:
Beautiful, accessible drag and drop for lists with React.js
136 lines (118 loc) • 3.79 kB
JavaScript
// @flow
import moveToEdge from '../../move-to-edge';
import getDisplacement from '../../get-displacement';
import withDroppableDisplacement from '../../with-droppable-displacement';
import type { Edge } from '../../move-to-edge';
import type { Result } from '../move-cross-axis-types';
import type {
Axis,
Viewport,
Displacement,
Position,
DragImpact,
DraggableDimension,
DroppableDimension,
} from '../../../types';
type Args = {|
amount: Position,
originalIndex: number,
target: ?DraggableDimension,
insideDroppable: DraggableDimension[],
draggable: DraggableDimension,
droppable: DroppableDimension,
previousImpact: DragImpact,
viewport: Viewport,
|}
export default ({
amount,
originalIndex,
target,
insideDroppable,
draggable,
droppable,
previousImpact,
viewport,
}: Args): ?Result => {
if (!target) {
console.error('there will always be a target in the original list');
return null;
}
const axis: Axis = droppable.axis;
const targetIndex: number = insideDroppable.indexOf(target);
if (targetIndex === -1) {
console.error('unable to find target in destination droppable');
return null;
}
// Moving back to original index
// Super simple - just move it back to the original center with no impact
if (targetIndex === originalIndex) {
const newCenter: Position = draggable.page.borderBox.center;
const newImpact: DragImpact = {
movement: {
displaced: [],
amount,
isBeyondStartPosition: false,
},
direction: droppable.axis.direction,
destination: {
droppableId: droppable.descriptor.id,
index: originalIndex,
},
};
return {
pageCenter: withDroppableDisplacement(droppable, newCenter),
impact: newImpact,
};
}
// When moving *before* where the item started:
// We align the dragging item top of the target
// and move everything from the target to the original position forwards
// When moving *after* where the item started:
// We align the dragging item to the end of the target
// and move everything from the target to the original position backwards
const isMovingPastOriginalIndex = targetIndex > originalIndex;
const edge: Edge = isMovingPastOriginalIndex ? 'end' : 'start';
const newCenter: Position = moveToEdge({
source: draggable.page.borderBox,
sourceEdge: edge,
destination: isMovingPastOriginalIndex ? target.page.borderBox : target.page.marginBox,
destinationEdge: edge,
destinationAxis: axis,
});
const modified: DraggableDimension[] = (() => {
if (!isMovingPastOriginalIndex) {
return insideDroppable.slice(targetIndex, originalIndex);
}
// We are aligning to the bottom of the target and moving everything
// back to the original index backwards
// We want everything after the original index to move
const from: number = originalIndex + 1;
// We need the target to move backwards
const to: number = targetIndex + 1;
// Need to ensure that the list is sorted with the closest item being first
return insideDroppable.slice(from, to).reverse();
})();
const displaced: Displacement[] = modified
.map((dimension: DraggableDimension): Displacement => getDisplacement({
draggable: dimension,
destination: droppable,
previousImpact,
viewport: viewport.subject,
}));
const newImpact: DragImpact = {
movement: {
displaced,
amount,
isBeyondStartPosition: isMovingPastOriginalIndex,
},
direction: axis.direction,
destination: {
droppableId: droppable.descriptor.id,
index: targetIndex,
},
};
return {
pageCenter: withDroppableDisplacement(droppable, newCenter),
impact: newImpact,
};
};