@hello-pangea/dnd
Version:
Beautiful and accessible drag and drop for lists with React
77 lines (65 loc) • 1.95 kB
text/typescript
import memoizeOne from 'memoize-one';
import type { Position } from 'css-box-model';
import type {
DroppableDimension,
DroppableDimensionMap,
DroppableId,
} from '../../../types';
import { invariant } from '../../../invariant';
import isPositionInFrame from '../../visibility/is-position-in-frame';
import { toDroppableList } from '../../dimension-structures';
const getScrollableDroppables = memoizeOne(
(droppables: DroppableDimensionMap): DroppableDimension[] =>
toDroppableList(droppables).filter(
(droppable: DroppableDimension): boolean => {
// exclude disabled droppables
if (!droppable.isEnabled) {
return false;
}
// only want droppables that are scrollable
if (!droppable.frame) {
return false;
}
return true;
},
),
);
const getScrollableDroppableOver = (
target: Position,
droppables: DroppableDimensionMap,
): DroppableDimension | null => {
const maybe =
getScrollableDroppables(droppables).find(
(droppable: DroppableDimension): boolean => {
invariant(droppable.frame, 'Invalid result');
return isPositionInFrame(droppable.frame.pageMarginBox)(target);
},
) || null;
return maybe;
};
interface Api {
center: Position;
destination: DroppableId | null;
droppables: DroppableDimensionMap;
}
export default ({
center,
destination,
droppables,
}: Api): DroppableDimension | null => {
// We need to scroll the best droppable frame we can so that the
// placeholder buffer logic works correctly
if (destination) {
const dimension: DroppableDimension = droppables[destination];
if (!dimension.frame) {
return null;
}
return dimension;
}
// 2. If we are not over a droppable - are we over a droppable frame?
const dimension: DroppableDimension | null = getScrollableDroppableOver(
center,
droppables,
);
return dimension;
};