Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add verticalSortableList collision detection strategy #805

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions packages/core/src/utilities/algorithms/horizontalSortableList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {CollisionDetection, DroppableContainer} from '@dnd-kit/core';
import {sortBy} from 'lodash';

export const horizontalSortableListCollisionDetection: CollisionDetection = (
args
) => {
if (
args.collisionRect.left < (args.active.rect.current?.initial?.left ?? 0)
) {
return leftmostDroppableContainerMajorityCovered(args);
} else {
return rightmostDroppableContainerMajorityCovered(args);
}
};

const leftmostDroppableContainerMajorityCovered: CollisionDetection = ({
droppableContainers,
collisionRect,
}) => {
const ascendingDroppableContainers = sortBy(
droppableContainers,
(c) => c?.rect.current?.left
);

for (const droppableContainer of ascendingDroppableContainers) {
const {
rect: {current: droppableRect},
} = droppableContainer;

if (droppableRect) {
const coveredPercentage =
(droppableRect.left + droppableRect.width - collisionRect.left) /
droppableRect.width;

if (coveredPercentage > 0.5) {
return [collision(droppableContainer)];
}
}
}

return [collision(ascendingDroppableContainers[0])];
};

const rightmostDroppableContainerMajorityCovered: CollisionDetection = ({
droppableContainers,
collisionRect,
}) => {
const descendingDroppableContainers = sortBy(
droppableContainers,
(c) => c?.rect.current?.left
).reverse();

for (const droppableContainer of descendingDroppableContainers) {
const {
rect: {current: droppableRect},
} = droppableContainer;

if (droppableRect) {
const coveredPercentage =
(collisionRect.right - droppableRect.left) / droppableRect.width;

if (coveredPercentage > 0.5) {
return [collision(droppableContainer)];
}
}
}

return [collision(descendingDroppableContainers[0])];
};

const collision = (droppableContainer?: DroppableContainer) => {
return {
id: droppableContainer?.id ?? '',
value: droppableContainer,
};
};
6 changes: 4 additions & 2 deletions packages/core/src/utilities/algorithms/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export {closestCenter} from './closestCenter';
export {closestCorners} from './closestCorners';
export {rectIntersection} from './rectIntersection';
export {getFirstCollision} from './helpers';
export {horizontalSortableListCollisionDetection} from './horizontalSortableList';
export {pointerWithin} from './pointerWithin';
export {rectIntersection} from './rectIntersection';
export type {Collision, CollisionDescriptor, CollisionDetection} from './types';
export {getFirstCollision} from './helpers';
export {verticalSortableListCollisionDetection} from './verticalSortableList';
80 changes: 80 additions & 0 deletions packages/core/src/utilities/algorithms/verticalSortableList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { CollisionDetection, DroppableContainer } from '@dnd-kit/core';
import { sortBy } from 'lodash';

export const verticalSortableListCollisionDetection: CollisionDetection = (
args
) => {
if (args.collisionRect.top < (args.active.rect.current?.initial?.top ?? 0)) {
return highestDroppableContainerMajorityCovered(args);
} else {
return lowestDroppableContainerMajorityCovered(args);
}
};

// Look for the first (/ furthest up / highest) droppable container that is at least
// 50% covered by the top edge of the dragging container.
const highestDroppableContainerMajorityCovered: CollisionDetection = ({
droppableContainers,
collisionRect,
}) => {
const ascendingDroppabaleContainers = sortBy(
droppableContainers,
(c) => c?.rect.current?.top
);

for (const droppableContainer of ascendingDroppabaleContainers) {
const {
rect: { current: droppableRect },
} = droppableContainer;

if (droppableRect) {
const coveredPercentage =
(droppableRect.top + droppableRect.height - collisionRect.top) /
droppableRect.height;

if (coveredPercentage > 0.5) {
return [collision(droppableContainer)];
}
}
}

// if we haven't found anything then we are off the top, so return the first item
return [collision(ascendingDroppabaleContainers[0])];
};

// Look for the last (/ furthest down / lowest) droppable container that is at least
// 50% covered by the bottom edge of the dragging container.
const lowestDroppableContainerMajorityCovered: CollisionDetection = ({
droppableContainers,
collisionRect,
}) => {
const descendingDroppabaleContainers = sortBy(
droppableContainers,
(c) => c?.rect.current?.top
).reverse();

for (const droppableContainer of descendingDroppabaleContainers) {
const {
rect: { current: droppableRect },
} = droppableContainer;

if (droppableRect) {
const coveredPercentage =
(collisionRect.bottom - droppableRect.top) / droppableRect.height;

if (coveredPercentage > 0.5) {
return [collision(droppableContainer)];
}
}
}

// if we haven't found anything then we are off the bottom, so return the last item
return [collision(descendingDroppabaleContainers[0])];
};

const collision = (dropppableContainer?: DroppableContainer) => {
return {
id: dropppableContainer?.id ?? '',
value: dropppableContainer,
};
};