Skip to content

Commit

Permalink
fix: prevent droppable from colliding with own child
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisvxd committed Nov 11, 2024
1 parent 10a407d commit bd0c918
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/dont-collide-with-child.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@dnd-kit/dom': patch
---

Track the path of the item to prevent a droppable from colliding with its own child.
17 changes: 14 additions & 3 deletions packages/abstract/src/core/collision/observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class CollisionObserver<
return DEFAULT_VALUE;
}

const collisions: Collision[] = [];
const collisionMap: Map<Droppable, Collision> = new Map();

for (const entry of entries ?? registry.droppables) {
if (entry.disabled) {
Expand All @@ -90,7 +90,6 @@ export class CollisionObserver<
continue;
}

entry.shape;
const collision = untracked(() =>
detectCollision({
droppable: entry,
Expand All @@ -103,10 +102,22 @@ export class CollisionObserver<
collision.priority = entry.collisionPriority;
}

collisions.push(collision);
collisionMap.set(entry, collision);
}
}

// Filter out collisions of items that contain other items
const collisions = Array.from(collisionMap.entries())
.filter(([droppable]) => {
if (source && droppable.path.indexOf(source.id) !== -1) {
// Dragged item is parent of collision target. Filter out collision
return false;
}

return true;
})
.map(([_, collision]) => collision);

collisions.sort(sortCollisions);

return collisions;
Expand Down
9 changes: 8 additions & 1 deletion packages/abstract/src/core/entities/droppable/droppable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import {derived, effects, reactive, type Effect} from '@dnd-kit/state';
import type {Shape} from '@dnd-kit/geometry';

import {Entity} from '../entity/index.ts';
import type {EntityInput, Data, Type} from '../entity/index.ts';
import type {
EntityInput,
Data,
Type,
UniqueIdentifier,
} from '../entity/index.ts';
import {
CollisionPriority,
type CollisionDetector,
Expand Down Expand Up @@ -90,4 +95,6 @@ export class Droppable<
public get isDropTarget() {
return this.manager?.dragOperation.target?.id === this.id;
}

public path: UniqueIdentifier[] = [];
}
31 changes: 31 additions & 0 deletions packages/dom/src/core/entities/droppable/droppable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Droppable as AbstractDroppable} from '@dnd-kit/abstract';
import type {
Data,
DroppableInput as AbstractDroppableInput,
UniqueIdentifier,
} from '@dnd-kit/abstract';
import {defaultCollisionDetection} from '@dnd-kit/collision';
import type {CollisionDetector} from '@dnd-kit/collision';
Expand All @@ -19,6 +20,32 @@ export interface Input<T extends Data = Data>
element?: Element;
}

function getPathArray(
droppables: DragDropManager['registry']['droppables'],
target: Element
): UniqueIdentifier[] {
// Create a map from element to id for easy lookup
const elementMap = new Map<Element, UniqueIdentifier>();
Array.from(droppables.value).forEach((item) => {
if (item?.element) {
elementMap.set(item.element, item.id);
}
});

const path: UniqueIdentifier[] = [];
let currentElement = target.parentElement;

while (currentElement) {
const parentId = elementMap.get(currentElement);
if (parentId) {
path.unshift(parentId);
}
currentElement = currentElement.parentElement;
}

return path;
}

export class Droppable<T extends Data = Data> extends AbstractDroppable<
T,
DragDropManager
Expand Down Expand Up @@ -69,6 +96,10 @@ export class Droppable<T extends Data = Data> extends AbstractDroppable<
!this.disabled &&
this.accepts(source);

this.path = element
? getPathArray(manager.registry.droppables, element)
: [];

if (observePosition) {
const positionObserver = new PositionObserver(
element,
Expand Down

0 comments on commit bd0c918

Please sign in to comment.