Skip to content
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
5 changes: 5 additions & 0 deletions .changeset/quick-cooks-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@dnd-kit/abstract": patch
---

Keep drag operation transform snapshots updated when no feedback plugin reads the live transform.
15 changes: 9 additions & 6 deletions packages/abstract/src/core/manager/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,17 @@ export class DragActions<
return;
}

const coordinates = args.to ?? {
x: dragOperation.position.current.x + (args.by?.x ?? 0),
y: dragOperation.position.current.y + (args.by?.y ?? 0),
};

const event = defaultPreventable(
{
nativeEvent: args.event,
operation: dragOperation.snapshot(),
operation: dragOperation.snapshot({
transform: dragOperation.getTransform(coordinates),
}),
by: args.by,
to: args.to,
},
Expand All @@ -208,12 +215,8 @@ export class DragActions<
return;
}

const coordinates = args.to ?? {
x: dragOperation.position.current.x + (args.by?.x ?? 0),
y: dragOperation.position.current.y + (args.by?.y ?? 0),
};

dragOperation.position.current = coordinates;
void dragOperation.transform;
});
});
}
Expand Down
37 changes: 25 additions & 12 deletions packages/abstract/src/core/manager/operation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Position, type Shape} from '@dnd-kit/geometry';
import {Point, Position, type Shape} from '@dnd-kit/geometry';
import type {Coordinates} from '@dnd-kit/geometry';
import {
batch,
Expand Down Expand Up @@ -155,39 +155,52 @@ export class DragOperation<T extends Draggable, U extends Droppable>

#transform = {x: 0, y: 0};

#computeTransform(transform: Coordinates) {
let nextTransform = transform;

for (const modifier of this.modifiers) {
nextTransform = modifier.apply({
...this.snapshot(),
transform: nextTransform,
});
}

return nextTransform;
}

/**
* Gets the current transform after applying all modifiers.
*
* @returns The transformed coordinates
*/
@derived
public get transform() {
const {x, y} = this.position.delta;
let transform = {x, y};

for (const modifier of this.modifiers) {
transform = modifier.apply({
...this.snapshot(),
transform,
});
}
const transform = this.#computeTransform(this.position.delta);

this.#transform = transform;

return transform;
}

public getTransform(coordinates = this.position.current) {
return this.#computeTransform(
Point.delta(Point.from(coordinates), this.position.initial)
);
}

/**
* Creates a snapshot of the current drag operation state.
*
* @returns An immutable snapshot of the current operation state
*/
public snapshot(): DragOperationSnapshot<T, U> {
public snapshot(
overrides: Partial<Pick<DragOperationSnapshot<T, U>, 'transform'>> = {}
): DragOperationSnapshot<T, U> {
return untracked(() => ({
source: this.source,
target: this.target,
activatorEvent: this.activatorEvent,
transform: this.#transform,
transform: overrides.transform ?? this.#transform,
shape: this.shape ? snapshot(this.shape) : null,
position: snapshot(this.position),
status: snapshot(this.status),
Expand Down
24 changes: 24 additions & 0 deletions packages/abstract/tests/manager-modifiers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,30 @@ describe('Manager modifier lifecycle', () => {
manager.destroy();
});

it('should expose the next modified transform in dragmove events without feedback', async () => {
const manager = new DragDropManager({modifiers: [ClampXModifier]});
const draggable = new Draggable({id: 'd1', register: false}, manager);
const transforms: Array<{x: number; y: number}> = [];

draggable.register();
manager.monitor.addEventListener('dragmove', (event) => {
transforms.push(event.operation.transform);
});

manager.actions.start({source: draggable, coordinates: {x: 0, y: 0}});
await flush();

manager.actions.move({by: {x: 100, y: 50}});

expect(transforms).toEqual([{x: 0, y: 50}]);

await flush();
expect(manager.dragOperation.snapshot().transform).toEqual({x: 0, y: 50});

draggable.destroy();
manager.destroy();
});

it('should destroy per-operation modifiers when draggable modifiers change mid-drag', () => {
let destroyCount = 0;

Expand Down