From a32a4c5f6228b9f03bf460b8403a38b8c3de493f Mon Sep 17 00:00:00 2001 From: SebH Date: Mon, 23 Aug 2021 17:19:01 +0200 Subject: [PATCH] Support for tolerance in DistanceConstraint (#399) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Claudéric Demers --- .changeset/distance-constraint-tolerance.md | 20 +++++++ cypress/integration/draggable_spec.ts | 60 +++++++++++++++++++ .../sensors/pointer/AbstractPointerSensor.ts | 7 +++ .../1 - Core/Draggable/1-Draggable.story.tsx | 24 ++++++++ 4 files changed, 111 insertions(+) create mode 100644 .changeset/distance-constraint-tolerance.md 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 = () => (