diff --git a/.changeset/distance-constraint-tolerance.md b/.changeset/distance-constraint-tolerance.md
new file mode 100644
index 00000000..6977da48
--- /dev/null
+++ b/.changeset/distance-constraint-tolerance.md
@@ -0,0 +1,20 @@
+---
+"@dnd-kit/core": minor
+---
+
+Added support for `tolerance` in DistanceConstrain. As soon as the `tolerance` is exceeded, the drag operation will be aborted, unless it has already started (because distance criteria was met).
+
+Example usage:
+
+```
+// Require the pointer be moved by 10 pixels vertically to initiate drag operation
+// Abort if the pointer is moved by more than 5 pixels horizontally.
+{
+ distance: {y: 10},
+ tolerance: {x: 5},
+}
+```
+
+Be careful not to pick conflicting settings for distance and tolerance if used together. For example, picking a tolerance that is lower than the distance in the same axis would result in the activation constraint never being met.
+
+
diff --git a/cypress/integration/draggable_spec.ts b/cypress/integration/draggable_spec.ts
index 818c4551..1cdd79b0 100644
--- a/cypress/integration/draggable_spec.ts
+++ b/cypress/integration/draggable_spec.ts
@@ -347,5 +347,65 @@ describe('Draggable', () => {
return subject;
});
});
+
+ it('Activates if the mouse is moved more than the minimum distance x and less than tolerance y', () => {
+ const deltaX = 100;
+ const deltaY = 5;
+
+ cy.visitStory('core-draggable-hooks-usedraggable--minimum-distance-x-tolerance-y')
+ .findFirstDraggableItem()
+ .mouseMoveBy(deltaX, deltaY)
+ .then(([subject, {delta}]) => {
+ expect(delta.x).eq(deltaX);
+ expect(delta.y).eq(deltaY);
+
+ return subject;
+ });
+ });
+
+ it('Does not activate if the mouse is moved more than the minimum distance x and more than tolerance y', () => {
+ const deltaX = 100;
+ const deltaY = 150;
+
+ cy.visitStory('core-draggable-hooks-usedraggable--minimum-distance-x-tolerance-y')
+ .findFirstDraggableItem()
+ .mouseMoveBy(deltaX, deltaY)
+ .then(([subject, {delta}]) => {
+ expect(delta.x).eq(0);
+ expect(delta.y).eq(0);
+
+ return subject;
+ });
+ });
+
+ it('Activates if the mouse is moved more than the minimum distance y and less than tolerance x', () => {
+ const deltaX = 5;
+ const deltaY = 100;
+
+ cy.visitStory('core-draggable-hooks-usedraggable--minimum-distance-y-tolerance-x')
+ .findFirstDraggableItem()
+ .mouseMoveBy(deltaX, deltaY)
+ .then(([subject, {delta}]) => {
+ expect(delta.x).eq(deltaX);
+ expect(delta.y).eq(deltaY);
+
+ return subject;
+ });
+ });
+
+ it('Does not activate if the mouse is moved more than the minimum distance y and more than tolerance x', () => {
+ const deltaX = 150;
+ const deltaY = 100;
+
+ cy.visitStory('core-draggable-hooks-usedraggable--minimum-distance-y-tolerance-x')
+ .findFirstDraggableItem()
+ .mouseMoveBy(deltaX, deltaY)
+ .then(([subject, {delta}]) => {
+ expect(delta.x).eq(0);
+ expect(delta.y).eq(0);
+
+ return subject;
+ });
+ });
});
});
diff --git a/packages/core/src/sensors/pointer/AbstractPointerSensor.ts b/packages/core/src/sensors/pointer/AbstractPointerSensor.ts
index ac844de7..e7a92b4d 100644
--- a/packages/core/src/sensors/pointer/AbstractPointerSensor.ts
+++ b/packages/core/src/sensors/pointer/AbstractPointerSensor.ts
@@ -17,6 +17,7 @@ import type {Coordinates, DistanceMeasurement} from '../../types';
interface DistanceConstraint {
distance: DistanceMeasurement;
+ tolerance?: DistanceMeasurement;
}
interface DelayConstraint {
@@ -172,6 +173,12 @@ export class AbstractPointerSensor implements SensorInstance {
}
if (isDistanceConstraint(activationConstraint)) {
+ if (
+ activationConstraint.tolerance != null &&
+ hasExceededDistance(delta, activationConstraint.tolerance)
+ ) {
+ return this.handleCancel();
+ }
if (hasExceededDistance(delta, activationConstraint.distance)) {
return this.handleStart();
}
diff --git a/stories/1 - Core/Draggable/1-Draggable.story.tsx b/stories/1 - Core/Draggable/1-Draggable.story.tsx
index 1b28e734..47f83b92 100644
--- a/stories/1 - Core/Draggable/1-Draggable.story.tsx
+++ b/stories/1 - Core/Draggable/1-Draggable.story.tsx
@@ -210,6 +210,30 @@ export const MinimumDistanceXY = () => (
MinimumDistanceXY.storyName = 'Minimum Distance – X&Y Axis';
+export const MinimumDistanceXToleranceY = () => (
+
+);
+
+MinimumDistanceXToleranceY.storyName = 'Minimum Distance X Axis and Tolerance Y Axis';
+
+export const MinimumDistanceYToleranceX = () => (
+
+);
+
+MinimumDistanceYToleranceX.storyName = 'Minimum Distance Y Axis and Tolerance X Axis';
+
export const HorizontalAxis = () => (