@hello-pangea/dnd
Version:
Beautiful and accessible drag and drop for lists with React
111 lines (100 loc) • 2.9 kB
text/typescript
import type { Position } from 'css-box-model';
import type {
DraggableId,
DroppableDimension,
DraggableDimension,
DraggableDimensionMap,
DragImpact,
Displacement,
DisplacementGroups,
DraggableIdMap,
DisplacementMap,
Viewport,
} from '../../types';
import scrollViewport from '../scroll-viewport';
import scrollDroppable from '../droppable/scroll-droppable';
import { add } from '../position';
import getDisplacementGroups from '../get-displacement-groups';
interface SpeculativeArgs {
impact: DragImpact;
destination: DroppableDimension;
viewport: Viewport;
draggables: DraggableDimensionMap;
maxScrollChange: Position;
}
function getDraggables(
ids: DraggableId[],
draggables: DraggableDimensionMap,
): DraggableDimension[] {
return ids.map((id: DraggableId): DraggableDimension => draggables[id]);
}
function tryGetVisible(
id: DraggableId,
groups: DisplacementGroups[],
): Displacement | null {
for (let i = 0; i < groups.length; i++) {
const displacement: Displacement | null = groups[i].visible[id];
if (displacement) {
return displacement;
}
}
return null;
}
export default ({
impact,
viewport,
destination,
draggables,
maxScrollChange,
}: SpeculativeArgs): DragImpact => {
const scrolledViewport: Viewport = scrollViewport(
viewport,
add(viewport.scroll.current, maxScrollChange),
);
const scrolledDroppable: DroppableDimension = destination.frame
? scrollDroppable(
destination,
add(destination.frame.scroll.current, maxScrollChange),
)
: destination;
const last: DisplacementGroups = impact.displaced;
const withViewportScroll: DisplacementGroups = getDisplacementGroups({
afterDragging: getDraggables(last.all, draggables),
destination,
displacedBy: impact.displacedBy,
viewport: scrolledViewport.frame,
last,
// we want the addition to be animated
forceShouldAnimate: false,
});
const withDroppableScroll: DisplacementGroups = getDisplacementGroups({
afterDragging: getDraggables(last.all, draggables),
destination: scrolledDroppable,
displacedBy: impact.displacedBy,
viewport: viewport.frame,
last,
// we want the addition to be animated
forceShouldAnimate: false,
});
const invisible: DraggableIdMap = {};
const visible: DisplacementMap = {};
const groups: DisplacementGroups[] = [
// this will populate the previous entries with the correct animation values
last,
withViewportScroll,
withDroppableScroll,
];
last.all.forEach((id: DraggableId) => {
const displacement: Displacement | null = tryGetVisible(id, groups);
if (displacement) {
visible[id] = displacement;
return;
}
invisible[id] = true;
});
const newImpact: DragImpact = {
...impact,
displaced: { all: last.all, invisible, visible },
};
return newImpact;
};