react-beautiful-dnd
Version:
Beautiful, accessible drag and drop for lists with React.js
144 lines (121 loc) • 3.8 kB
JavaScript
// @flow
import getDisplacement from '../get-displacement';
import type {
Viewport,
Axis,
DraggableId,
DragImpact,
DraggableDimensionMap,
DroppableDimension,
DraggableDimension,
Displacement,
} from '../../types';
type WithAdded = {|
add: DraggableId,
previousImpact: DragImpact,
droppable: DroppableDimension,
draggables: DraggableDimensionMap,
viewport: Viewport,
|}
export const withFirstAdded = ({
add,
previousImpact,
droppable,
draggables,
viewport,
}: WithAdded): Displacement[] => {
const newDisplacement: Displacement = {
draggableId: add,
isVisible: true,
shouldAnimate: true,
};
const added: Displacement[] = [
newDisplacement,
...previousImpact.movement.displaced,
];
const withUpdatedVisibility: Displacement[] =
added.map((current: Displacement): Displacement => {
// we have already calculated the displacement for this item
if (current === newDisplacement) {
return current;
}
const updated: Displacement = getDisplacement({
draggable: draggables[current.draggableId],
destination: droppable,
previousImpact,
viewport: viewport.subject,
});
return updated;
});
return withUpdatedVisibility;
};
type WithLastRemoved = {|
dragging: DraggableId,
isVisibleInNewLocation: boolean,
previousImpact: DragImpact,
droppable: DroppableDimension,
draggables: DraggableDimensionMap,
|}
const forceVisibleDisplacement = (current: Displacement): Displacement => {
// if already visible - can use the existing displacement
if (current.isVisible) {
return current;
}
// if not visible - immediately force visibility
return {
draggableId: current.draggableId,
isVisible: true,
shouldAnimate: false,
};
};
export const withFirstRemoved = ({
dragging,
isVisibleInNewLocation,
previousImpact,
droppable,
draggables,
}: WithLastRemoved): Displacement[] => {
const last: Displacement[] = previousImpact.movement.displaced;
if (!last.length) {
console.error('cannot remove displacement from empty list');
return [];
}
const withFirstRestored: Displacement[] = last.slice(1, last.length);
// list is now empty
if (!withFirstRestored.length) {
return withFirstRestored;
}
// Simple case: no forced movement required
// no displacement visibility will be updated by this move
// so we can simply return the previous values
if (isVisibleInNewLocation) {
return withFirstRestored;
}
const axis: Axis = droppable.axis;
// When we are forcing this displacement, we need to adjust the visibility of draggables
// within a particular range. This range is the size of the dragging item and the item
// that is being restored to its original
const sizeOfRestored: number = draggables[last[0].draggableId].page.marginBox[axis.size];
const sizeOfDragging: number = draggables[dragging].page.marginBox[axis.size];
let buffer: number = sizeOfRestored + sizeOfDragging;
const withUpdatedVisibility: Displacement[] =
withFirstRestored.map((displacement: Displacement, index: number): Displacement => {
// we are ripping this one away and forcing it to move
if (index === 0) {
return forceVisibleDisplacement(displacement);
}
if (buffer > 0) {
const current: DraggableDimension = draggables[displacement.draggableId];
const size: number = current.page.marginBox[axis.size];
buffer -= size;
return forceVisibleDisplacement(displacement);
}
// We know that these items cannot be visible after the move
return {
draggableId: displacement.draggableId,
isVisible: false,
shouldAnimate: false,
};
});
return withUpdatedVisibility;
};