Skip to content

Commit 58d79dc

Browse files
authored
Add distance option
1 parent 4b881a1 commit 58d79dc

File tree

11 files changed

+170
-72
lines changed

11 files changed

+170
-72
lines changed

src/Draggable/Draggable.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const defaultOptions = {
4848
draggable: '.draggable-source',
4949
handle: null,
5050
delay: 100,
51+
distance: 0,
5152
placedTimeout: 800,
5253
plugins: [],
5354
sensors: [],

src/Draggable/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ on the entire element. Default: `null`
8080
If you want to delay a drag start you can specify delay in milliseconds. This can be useful
8181
for draggable elements within scrollable containers. Default: `100`
8282

83+
**`distance {Number}`**
84+
The distance you want the pointer to have moved before drag starts. This can be useful
85+
for clickable draggable elements, such as links. Default: `0`
86+
8387
**`plugins {Plugin[]}`**
8488
Plugins add behaviour to Draggable by hooking into its life cycle, e.g. one of the default
8589
plugins controls the mirror movement. Default: `[]`

src/Draggable/Sensors/DragSensor/DragSensor.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ export default class DragSensor extends Sensor {
161161
this.trigger(container, dragStopEvent);
162162

163163
this.dragging = false;
164+
this.delayOver = false;
165+
this.startEvent = null;
164166

165167
this[reset]();
166168
}
@@ -205,7 +207,10 @@ export default class DragSensor extends Sensor {
205207
return;
206208
}
207209

210+
this.startEvent = event;
211+
208212
this.mouseDownTimeout = setTimeout(() => {
213+
this.delayOver = true;
209214
target.draggable = true;
210215
this.draggableElement = target;
211216
}, this.options.delay);

src/Draggable/Sensors/DragSensor/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ This value will delay touch start
2828
### Known issues
2929

3030
The drag sensor uses the native Drag and Drop API and therefor Draggable does not create
31-
a mirror. This means there is less control over the mirror
31+
a mirror. This means there is less control over the mirror.
3232

3333
### Example
3434

@@ -42,3 +42,7 @@ const draggable = new Draggable(containers, {
4242
// Remove default mouse sensor
4343
draggable.removeSensor(Sensors.MouseSensor);
4444
```
45+
46+
### Caveats
47+
48+
- The `distance` option will not work with this sensor.

src/Draggable/Sensors/MouseSensor/MouseSensor.js

Lines changed: 57 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import {closest} from 'shared/utils';
1+
import {closest, distance} from 'shared/utils';
22
import Sensor from '../Sensor';
33
import {DragStartSensorEvent, DragMoveSensorEvent, DragStopSensorEvent} from '../SensorEvent';
44

55
const onContextMenuWhileDragging = Symbol('onContextMenuWhileDragging');
66
const onMouseDown = Symbol('onMouseDown');
77
const onMouseMove = Symbol('onMouseMove');
88
const onMouseUp = Symbol('onMouseUp');
9+
const startDrag = Symbol('startDrag');
10+
const onDistanceChange = Symbol('onDistanceChange');
911

1012
/**
1113
* This sensor picks up native browser mouse events and dictates drag operations
@@ -23,31 +25,19 @@ export default class MouseSensor extends Sensor {
2325
constructor(containers = [], options = {}) {
2426
super(containers, options);
2527

26-
/**
27-
* Indicates if mouse button is still down
28-
* @property mouseDown
29-
* @type {Boolean}
30-
*/
31-
this.mouseDown = false;
32-
3328
/**
3429
* Mouse down timer which will end up triggering the drag start operation
3530
* @property mouseDownTimeout
3631
* @type {Number}
3732
*/
3833
this.mouseDownTimeout = null;
3934

40-
/**
41-
* Indicates if context menu has been opened during drag operation
42-
* @property openedContextMenu
43-
* @type {Boolean}
44-
*/
45-
this.openedContextMenu = false;
46-
4735
this[onContextMenuWhileDragging] = this[onContextMenuWhileDragging].bind(this);
4836
this[onMouseDown] = this[onMouseDown].bind(this);
4937
this[onMouseMove] = this[onMouseMove].bind(this);
5038
this[onMouseUp] = this[onMouseUp].bind(this);
39+
this[startDrag] = this[startDrag].bind(this);
40+
this[onDistanceChange] = this[onDistanceChange].bind(this);
5141
}
5242

5343
/**
@@ -74,43 +64,67 @@ export default class MouseSensor extends Sensor {
7464
return;
7565
}
7666

67+
this.startEvent = event;
68+
7769
document.addEventListener('mouseup', this[onMouseUp]);
70+
document.addEventListener('mousemove', this[onDistanceChange]);
7871

79-
const target = document.elementFromPoint(event.clientX, event.clientY);
80-
const container = closest(target, this.containers);
72+
const container = closest(event.target, this.containers);
8173

8274
if (!container) {
8375
return;
8476
}
8577

8678
document.addEventListener('dragstart', preventNativeDragStart);
8779

88-
this.mouseDown = true;
89-
90-
clearTimeout(this.mouseDownTimeout);
80+
this.currentContainer = container;
9181
this.mouseDownTimeout = setTimeout(() => {
92-
if (!this.mouseDown) {
82+
this.delayOver = true;
83+
if (this.distance < this.options.distance) {
9384
return;
9485
}
86+
this[startDrag]();
87+
}, this.options.delay);
88+
}
89+
90+
/**
91+
* Start the drag
92+
* @private
93+
*/
94+
[startDrag]() {
95+
const startEvent = this.startEvent;
96+
const container = this.currentContainer;
97+
98+
const dragStartEvent = new DragStartSensorEvent({
99+
clientX: startEvent.clientX,
100+
clientY: startEvent.clientY,
101+
target: startEvent.target,
102+
container,
103+
originalEvent: startEvent,
104+
});
95105

96-
const dragStartEvent = new DragStartSensorEvent({
97-
clientX: event.clientX,
98-
clientY: event.clientY,
99-
target,
100-
container,
101-
originalEvent: event,
102-
});
106+
this.trigger(this.currentContainer, dragStartEvent);
103107

104-
this.trigger(container, dragStartEvent);
108+
this.dragging = !dragStartEvent.canceled();
105109

106-
this.currentContainer = container;
107-
this.dragging = !dragStartEvent.canceled();
110+
if (this.dragging) {
111+
document.addEventListener('contextmenu', this[onContextMenuWhileDragging], true);
112+
document.addEventListener('mousemove', this[onMouseMove]);
113+
}
114+
}
108115

109-
if (this.dragging) {
110-
document.addEventListener('contextmenu', this[onContextMenuWhileDragging]);
111-
document.addEventListener('mousemove', this[onMouseMove]);
112-
}
113-
}, this.options.delay);
116+
/**
117+
* Detect change in distance
118+
* @private
119+
* @param {Event} event - Mouse move event
120+
*/
121+
[onDistanceChange](event) {
122+
if (this.dragging) return;
123+
this.distance = distance(this.startEvent.pageX, this.startEvent.pageY, event.pageX, event.pageY);
124+
125+
if (this.delayOver && this.distance >= this.options.distance) {
126+
this[startDrag]();
127+
}
114128
}
115129

116130
/**
@@ -142,15 +156,15 @@ export default class MouseSensor extends Sensor {
142156
* @param {Event} event - Mouse up event
143157
*/
144158
[onMouseUp](event) {
145-
this.mouseDown = Boolean(this.openedContextMenu);
159+
clearTimeout(this.mouseDownTimeout);
146160

147-
if (this.openedContextMenu) {
148-
this.openedContextMenu = false;
161+
if (event.button !== 0) {
149162
return;
150163
}
151164

152165
document.removeEventListener('mouseup', this[onMouseUp]);
153166
document.removeEventListener('dragstart', preventNativeDragStart);
167+
document.removeEventListener('mousemove', this[onDistanceChange]);
154168

155169
if (!this.dragging) {
156170
return;
@@ -168,11 +182,14 @@ export default class MouseSensor extends Sensor {
168182

169183
this.trigger(this.currentContainer, dragStopEvent);
170184

171-
document.removeEventListener('contextmenu', this[onContextMenuWhileDragging]);
185+
document.removeEventListener('contextmenu', this[onContextMenuWhileDragging], true);
172186
document.removeEventListener('mousemove', this[onMouseMove]);
173187

174188
this.currentContainer = null;
175189
this.dragging = false;
190+
this.distance = 0;
191+
this.delayOver = false;
192+
this.startEvent = null;
176193
}
177194

178195
/**
@@ -182,7 +199,6 @@ export default class MouseSensor extends Sensor {
182199
*/
183200
[onContextMenuWhileDragging](event) {
184201
event.preventDefault();
185-
this.openedContextMenu = true;
186202
}
187203
}
188204

src/Draggable/Sensors/Sensor/Sensor.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,27 @@ export default class Sensor {
3838
* @type {HTMLElement}
3939
*/
4040
this.currentContainer = null;
41+
42+
/**
43+
* The distance moved from the first pointer down location, no longer updated after the drag has started
44+
* @property distance
45+
* @type {Number}
46+
*/
47+
this.distance = 0;
48+
49+
/**
50+
* Indicates whether the delay has ended
51+
* @property delayOver
52+
* @type {Boolean}
53+
*/
54+
this.delayOver = false;
55+
56+
/**
57+
* The event of the initial sensor down
58+
* @property startEvent
59+
* @type {Event}
60+
*/
61+
this.startEvent = null;
4162
}
4263

4364
/**

0 commit comments

Comments
 (0)