react-beautiful-dnd
Version:
Beautiful, accessible drag and drop for lists with React.js
76 lines (68 loc) • 2.12 kB
JavaScript
// @flow
import { type Position } from 'css-box-model';
import { distance } from '../../position';
import { isTotallyVisible } from '../../visibility/is-visible';
import withDroppableDisplacement from '../../with-droppable-displacement';
import type {
Viewport,
Axis,
DraggableDimension,
DroppableDimension,
} from '../../../types';
type Args = {|
axis: Axis,
viewport: Viewport,
pageBorderBoxCenter: Position,
// the droppable that is being moved to
destination: DroppableDimension,
// the droppables inside the destination
insideDestination: DraggableDimension[],
|};
export default ({
axis,
viewport,
pageBorderBoxCenter,
destination,
insideDestination,
}: Args): ?DraggableDimension => {
// Empty list - bail out
if (!insideDestination.length) {
return null;
}
const result: DraggableDimension[] = insideDestination
// Remove any options that are hidden by overflow
// Draggable must be totally visible to move to it
.filter(
(draggable: DraggableDimension): boolean =>
isTotallyVisible({
target: draggable.page.borderBox,
destination,
viewport: viewport.frame,
}),
)
.sort(
(a: DraggableDimension, b: DraggableDimension): number => {
// Need to consider the change in scroll in the destination
const distanceToA = distance(
pageBorderBoxCenter,
withDroppableDisplacement(destination, a.page.borderBox.center),
);
const distanceToB = distance(
pageBorderBoxCenter,
withDroppableDisplacement(destination, b.page.borderBox.center),
);
// if a is closer - return a
if (distanceToA < distanceToB) {
return -1;
}
// if b is closer - return b
if (distanceToB < distanceToA) {
return 1;
}
// if the distance to a and b are the same:
// return the one that appears first on the main axis
return a.page.borderBox[axis.start] - b.page.borderBox[axis.start];
},
);
return result.length ? result[0] : null;
};