draggable-rails 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/draggable/rails/version.rb +1 -1
- data/vendor/assets/javascripts/behaviour/collidable.js +99 -0
- data/vendor/assets/javascripts/behaviour/snappable.js +107 -0
- data/vendor/assets/javascripts/core/accessibility.js +58 -0
- data/vendor/assets/javascripts/core/mirror.js +129 -0
- data/vendor/assets/javascripts/draggable.js +487 -0
- data/vendor/assets/javascripts/droppable.js +168 -0
- data/vendor/assets/javascripts/events/abstract-event.js +45 -0
- data/vendor/assets/javascripts/events/collidable-event.js +25 -0
- data/vendor/assets/javascripts/events/drag-event.js +91 -0
- data/vendor/assets/javascripts/events/draggable-event.js +17 -0
- data/vendor/assets/javascripts/events/droppable-event.js +21 -0
- data/vendor/assets/javascripts/events/mirror-event.js +43 -0
- data/vendor/assets/javascripts/events/sensor-event.js +47 -0
- data/vendor/assets/javascripts/events/snappable-event.js +15 -0
- data/vendor/assets/javascripts/events/sortable-event.js +35 -0
- data/vendor/assets/javascripts/events/swappable-event.js +23 -0
- data/vendor/assets/javascripts/index.js +18 -0
- data/vendor/assets/javascripts/sensors/drag-sensor.js +158 -0
- data/vendor/assets/javascripts/sensors/force-touch-sensor.js +166 -0
- data/vendor/assets/javascripts/sensors/mouse-sensor.js +110 -0
- data/vendor/assets/javascripts/sensors/sensor.js +23 -0
- data/vendor/assets/javascripts/sensors/touch-sensor.js +145 -0
- data/vendor/assets/javascripts/sortable.js +161 -0
- data/vendor/assets/javascripts/swappable.js +104 -0
- data/vendor/assets/javascripts/utils.js +52 -0
- metadata +26 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ca6434b4515d382cf869565d05835704455c6d7
|
4
|
+
data.tar.gz: 76116c2095b2282b6f29b0ad987466c72582da6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d7800084b1fa71f466f93a8cece89c9c52c84dea9b5d8f4f81cf8bc1d7ba270f9cc72915c2b82a6a437d49f9130ded5191bf9199883d840610ba8a827e5ecc1
|
7
|
+
data.tar.gz: 14482cd176bd243dd63cff18650ae4e88ad62f377bc8ad63b9577cf44ac893c70a06b26e7bb26efbc0981c2325227d7bb7e6bb23ad01023a6c7fa12fd7fa219e
|
@@ -0,0 +1,99 @@
|
|
1
|
+
import {closest} from './../utils';
|
2
|
+
|
3
|
+
import {
|
4
|
+
CollidableInEvent,
|
5
|
+
CollidableOutEvent,
|
6
|
+
} from './../events/collidable-event';
|
7
|
+
|
8
|
+
export default class Collidable {
|
9
|
+
constructor(draggable) {
|
10
|
+
this.draggable = draggable;
|
11
|
+
|
12
|
+
this._onDragMove = this._onDragMove.bind(this);
|
13
|
+
this._onDragStop = this._onDragStop.bind(this);
|
14
|
+
this._onRequestAnimationFrame = this._onRequestAnimationFrame.bind(this);
|
15
|
+
}
|
16
|
+
|
17
|
+
attach() {
|
18
|
+
this.draggable.on('drag:move', this._onDragMove);
|
19
|
+
this.draggable.on('drag:stop', this._onDragStop);
|
20
|
+
}
|
21
|
+
|
22
|
+
detach() {
|
23
|
+
this.draggable.off('drag:move', this._onDragMove);
|
24
|
+
this.draggable.off('drag:stop', this._onDragStop);
|
25
|
+
}
|
26
|
+
|
27
|
+
_onDragMove(event) {
|
28
|
+
const target = event.sensorEvent.target;
|
29
|
+
|
30
|
+
this.currentAnimationFrame = requestAnimationFrame(this._onRequestAnimationFrame(target));
|
31
|
+
|
32
|
+
if (this.currentlyCollidingElement) {
|
33
|
+
event.cancel();
|
34
|
+
}
|
35
|
+
|
36
|
+
const collidableInEvent = new CollidableInEvent({
|
37
|
+
dragEvent: event,
|
38
|
+
collidingElement: this.currentlyCollidingElement,
|
39
|
+
});
|
40
|
+
|
41
|
+
const collidableOutEvent = new CollidableOutEvent({
|
42
|
+
dragEvent: event,
|
43
|
+
collidingElement: this.lastCollidingElement,
|
44
|
+
});
|
45
|
+
|
46
|
+
const enteringCollidable = Boolean(this.currentlyCollidingElement && this.lastCollidingElement !== this.currentlyCollidingElement);
|
47
|
+
const leavingCollidable = Boolean(!this.currentlyCollidingElement && this.lastCollidingElement);
|
48
|
+
|
49
|
+
if (enteringCollidable) {
|
50
|
+
if (this.lastCollidingElement) {
|
51
|
+
this.draggable.triggerEvent(collidableOutEvent);
|
52
|
+
}
|
53
|
+
|
54
|
+
this.draggable.triggerEvent(collidableInEvent);
|
55
|
+
} else if (leavingCollidable) {
|
56
|
+
this.draggable.triggerEvent(collidableOutEvent);
|
57
|
+
}
|
58
|
+
|
59
|
+
this.lastCollidingElement = this.currentlyCollidingElement;
|
60
|
+
}
|
61
|
+
|
62
|
+
_onDragStop(event) {
|
63
|
+
const lastCollidingElement = this.currentlyCollidingElement || this.lastCollidingElement;
|
64
|
+
const collidableOutEvent = new CollidableOutEvent({
|
65
|
+
dragEvent: event,
|
66
|
+
collidingElement: lastCollidingElement,
|
67
|
+
});
|
68
|
+
|
69
|
+
if (lastCollidingElement) {
|
70
|
+
this.draggable.triggerEvent(collidableOutEvent);
|
71
|
+
}
|
72
|
+
|
73
|
+
this.lastCollidingElement = null;
|
74
|
+
this.currentlyCollidingElement = null;
|
75
|
+
}
|
76
|
+
|
77
|
+
_onRequestAnimationFrame(target) {
|
78
|
+
return () => {
|
79
|
+
const collidables = this._getCollidables();
|
80
|
+
this.currentlyCollidingElement = closest(target, (element) => collidables.includes(element));
|
81
|
+
};
|
82
|
+
}
|
83
|
+
|
84
|
+
_getCollidables() {
|
85
|
+
const collidables = this.draggable.options.collidables;
|
86
|
+
|
87
|
+
if (typeof collidables === 'string') {
|
88
|
+
return Array.prototype.slice.call(document.querySelectorAll(collidables));
|
89
|
+
} else if (collidables instanceof NodeList || collidables instanceof Array) {
|
90
|
+
return Array.prototype.slice.call(collidables);
|
91
|
+
} else if (collidables instanceof HTMLElement) {
|
92
|
+
return [collidables];
|
93
|
+
} else if (typeof collidables === 'function') {
|
94
|
+
return collidables();
|
95
|
+
} else {
|
96
|
+
return [];
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
@@ -0,0 +1,107 @@
|
|
1
|
+
import {
|
2
|
+
SnapInEvent,
|
3
|
+
SnapOutEvent,
|
4
|
+
} from './../events/snappable-event';
|
5
|
+
|
6
|
+
export default class Snappable {
|
7
|
+
constructor(draggable) {
|
8
|
+
this.draggable = draggable;
|
9
|
+
|
10
|
+
this._onDragStart = this._onDragStart.bind(this);
|
11
|
+
this._onDragStop = this._onDragStop.bind(this);
|
12
|
+
this._onDragOver = this._onDragOver.bind(this);
|
13
|
+
this._onDragOut = this._onDragOut.bind(this);
|
14
|
+
}
|
15
|
+
|
16
|
+
attach() {
|
17
|
+
this.draggable
|
18
|
+
.on('drag:start', this._onDragStart)
|
19
|
+
.on('drag:stop', this._onDragStop)
|
20
|
+
.on('drag:over', this._onDragOver)
|
21
|
+
.on('drag:out', this._onDragOut)
|
22
|
+
.on('droppable:over', this._onDragOver)
|
23
|
+
.on('droppable:out', this._onDragOut);
|
24
|
+
}
|
25
|
+
|
26
|
+
detach() {
|
27
|
+
this.draggable
|
28
|
+
.off('drag:start', this._onDragStart)
|
29
|
+
.off('drag:stop', this._onDragStop)
|
30
|
+
.off('drag:over', this._onDragOver)
|
31
|
+
.off('drag:out', this._onDragOut)
|
32
|
+
.off('droppable:over', this._onDragOver)
|
33
|
+
.off('droppable:out', this._onDragOut);
|
34
|
+
}
|
35
|
+
|
36
|
+
_onDragStart(event) {
|
37
|
+
if (event.canceled()) {
|
38
|
+
return;
|
39
|
+
}
|
40
|
+
|
41
|
+
this.firstSource = event.source;
|
42
|
+
}
|
43
|
+
|
44
|
+
_onDragStop() {
|
45
|
+
this.firstSource = null;
|
46
|
+
}
|
47
|
+
|
48
|
+
_onDragOver(event) {
|
49
|
+
if (event.canceled()) {
|
50
|
+
return;
|
51
|
+
}
|
52
|
+
|
53
|
+
const source = event.source || event.dragEvent.source;
|
54
|
+
const mirror = event.mirror || event.dragEvent.mirror;
|
55
|
+
|
56
|
+
if (source === this.firstSource) {
|
57
|
+
this.firstSource = null;
|
58
|
+
return;
|
59
|
+
}
|
60
|
+
|
61
|
+
const snapInEvent = new SnapInEvent({
|
62
|
+
dragEvent: event,
|
63
|
+
});
|
64
|
+
|
65
|
+
this.draggable.triggerEvent(snapInEvent);
|
66
|
+
|
67
|
+
if (snapInEvent.canceled()) {
|
68
|
+
return;
|
69
|
+
}
|
70
|
+
|
71
|
+
if (mirror) {
|
72
|
+
mirror.style.display = 'none';
|
73
|
+
}
|
74
|
+
|
75
|
+
source.classList.remove(this.draggable.getClassNameFor('source:dragging'));
|
76
|
+
source.classList.add(this.draggable.getClassNameFor('source:placed'));
|
77
|
+
|
78
|
+
setTimeout(() => {
|
79
|
+
source.classList.remove(this.draggable.getClassNameFor('source:placed'));
|
80
|
+
}, this.draggable.options.placedTimeout);
|
81
|
+
}
|
82
|
+
|
83
|
+
_onDragOut(event) {
|
84
|
+
if (event.canceled()) {
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
|
88
|
+
const mirror = event.mirror || event.dragEvent.mirror;
|
89
|
+
const source = event.source || event.dragEvent.source;
|
90
|
+
|
91
|
+
const snapOutEvent = new SnapOutEvent({
|
92
|
+
dragEvent: event,
|
93
|
+
});
|
94
|
+
|
95
|
+
this.draggable.triggerEvent(snapOutEvent);
|
96
|
+
|
97
|
+
if (snapOutEvent.canceled()) {
|
98
|
+
return;
|
99
|
+
}
|
100
|
+
|
101
|
+
if (mirror) {
|
102
|
+
mirror.style.display = '';
|
103
|
+
}
|
104
|
+
|
105
|
+
source.classList.add(this.draggable.getClassNameFor('source:dragging'));
|
106
|
+
}
|
107
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
const ARIA_GRABBED = 'aria-grabbed';
|
2
|
+
const ARIA_DROPEFFECT = 'aria-dropeffect';
|
3
|
+
const TABINDEX = 'tabindex';
|
4
|
+
|
5
|
+
export default class Accessibility {
|
6
|
+
constructor(draggable) {
|
7
|
+
this.draggable = draggable;
|
8
|
+
|
9
|
+
this._onInit = this._onInit.bind(this);
|
10
|
+
this._onDestroy = this._onDestroy.bind(this);
|
11
|
+
}
|
12
|
+
|
13
|
+
attach() {
|
14
|
+
this.draggable
|
15
|
+
.on('init', this._onInit)
|
16
|
+
.on('destroy', this._onDestroy)
|
17
|
+
.on('drag:start', _onDragStart)
|
18
|
+
.on('drag:stop', _onDragStop);
|
19
|
+
}
|
20
|
+
|
21
|
+
detach() {
|
22
|
+
this.draggable
|
23
|
+
.off('init', this._onInit)
|
24
|
+
.off('destroy', this._onDestroy)
|
25
|
+
.off('drag:start', _onDragStart)
|
26
|
+
.off('drag:stop', _onDragStop);
|
27
|
+
}
|
28
|
+
|
29
|
+
_onInit({containers}) {
|
30
|
+
for (const container of containers) {
|
31
|
+
container.setAttribute(ARIA_DROPEFFECT, this.draggable.options.type);
|
32
|
+
|
33
|
+
for (const element of container.querySelectorAll(this.draggable.options.draggable)) {
|
34
|
+
element.setAttribute(TABINDEX, 0);
|
35
|
+
element.setAttribute(ARIA_GRABBED, false);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
_onDestroy({containers}) {
|
41
|
+
for (const container of containers) {
|
42
|
+
container.removeAttribute(ARIA_DROPEFFECT);
|
43
|
+
|
44
|
+
for (const element of container.querySelectorAll(this.draggable.options.draggable)) {
|
45
|
+
element.removeAttribute(TABINDEX, 0);
|
46
|
+
element.removeAttribute(ARIA_GRABBED, false);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
function _onDragStart({source}) {
|
53
|
+
source.setAttribute(ARIA_GRABBED, true);
|
54
|
+
}
|
55
|
+
|
56
|
+
function _onDragStop({source}) {
|
57
|
+
source.setAttribute(ARIA_GRABBED, false);
|
58
|
+
}
|
@@ -0,0 +1,129 @@
|
|
1
|
+
export default class Mirror {
|
2
|
+
constructor(draggable) {
|
3
|
+
this.draggable = draggable;
|
4
|
+
|
5
|
+
this._onMirrorCreated = this._onMirrorCreated.bind(this);
|
6
|
+
this._onMirrorMove = this._onMirrorMove.bind(this);
|
7
|
+
}
|
8
|
+
|
9
|
+
attach() {
|
10
|
+
this.draggable
|
11
|
+
.on('mirror:created', this._onMirrorCreated)
|
12
|
+
.on('mirror:created', onMirrorCreated)
|
13
|
+
.on('mirror:move', this._onMirrorMove);
|
14
|
+
}
|
15
|
+
|
16
|
+
detach() {
|
17
|
+
this.draggable
|
18
|
+
.off('mirror:created', this._onMirrorCreated)
|
19
|
+
.off('mirror:created', onMirrorCreated)
|
20
|
+
.off('mirror:move', this._onMirrorMove);
|
21
|
+
}
|
22
|
+
|
23
|
+
_onMirrorCreated({mirror, source, sensorEvent}) {
|
24
|
+
const mirrorClass = this.draggable.getClassNameFor('mirror');
|
25
|
+
|
26
|
+
const setState = (data) => {
|
27
|
+
this.mirrorOffset = data.mirrorOffset;
|
28
|
+
return data;
|
29
|
+
};
|
30
|
+
|
31
|
+
Promise.resolve({mirror, source, sensorEvent, mirrorClass})
|
32
|
+
.then(computeMirrorDimensions)
|
33
|
+
.then(calculateMirrorOffset)
|
34
|
+
.then(addMirrorClasses)
|
35
|
+
.then(positionMirror())
|
36
|
+
.then(removeMirrorID)
|
37
|
+
.then(setState)
|
38
|
+
.catch();
|
39
|
+
}
|
40
|
+
|
41
|
+
_onMirrorMove({mirror, sensorEvent}) {
|
42
|
+
Promise.resolve({mirror, sensorEvent, mirrorOffset: this.mirrorOffset})
|
43
|
+
.then(positionMirror({raf: true}))
|
44
|
+
.catch();
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
function onMirrorCreated({mirror, source}) {
|
49
|
+
Promise.resolve({mirror, source})
|
50
|
+
.then(resetMirror)
|
51
|
+
.catch();
|
52
|
+
}
|
53
|
+
|
54
|
+
function resetMirror(data) {
|
55
|
+
return withPromise((resolve) => {
|
56
|
+
const {mirror, source} = data;
|
57
|
+
|
58
|
+
mirror.style.position = 'fixed';
|
59
|
+
mirror.style.pointerEvents = 'none';
|
60
|
+
mirror.style.top = 0;
|
61
|
+
mirror.style.left = 0;
|
62
|
+
mirror.style.width = `${source.offsetWidth}px`;
|
63
|
+
mirror.style.height = `${source.offsetHeight}px`;
|
64
|
+
|
65
|
+
resolve(data);
|
66
|
+
});
|
67
|
+
}
|
68
|
+
|
69
|
+
function computeMirrorDimensions(data) {
|
70
|
+
return withPromise((resolve) => {
|
71
|
+
const {source} = data;
|
72
|
+
const sourceRect = source.getBoundingClientRect();
|
73
|
+
resolve({...data, sourceRect});
|
74
|
+
});
|
75
|
+
}
|
76
|
+
|
77
|
+
function calculateMirrorOffset(data) {
|
78
|
+
return withPromise((resolve) => {
|
79
|
+
const {sensorEvent, sourceRect} = data;
|
80
|
+
const mirrorOffset = {top: sensorEvent.clientY - sourceRect.top, left: sensorEvent.clientX - sourceRect.left};
|
81
|
+
resolve({...data, mirrorOffset});
|
82
|
+
});
|
83
|
+
}
|
84
|
+
|
85
|
+
function addMirrorClasses(data) {
|
86
|
+
return withPromise((resolve) => {
|
87
|
+
const {mirror, mirrorClass} = data;
|
88
|
+
mirror.classList.add(mirrorClass);
|
89
|
+
resolve(data);
|
90
|
+
});
|
91
|
+
}
|
92
|
+
|
93
|
+
function removeMirrorID(data) {
|
94
|
+
return withPromise((resolve) => {
|
95
|
+
const {mirror} = data;
|
96
|
+
mirror.removeAttribute('id');
|
97
|
+
delete mirror.id;
|
98
|
+
resolve(data);
|
99
|
+
});
|
100
|
+
}
|
101
|
+
|
102
|
+
function positionMirror({withFrame = false} = {}) {
|
103
|
+
return (data) => {
|
104
|
+
return withPromise((resolve) => {
|
105
|
+
const {mirror, sensorEvent, mirrorOffset} = data;
|
106
|
+
|
107
|
+
if (mirrorOffset) {
|
108
|
+
const x = sensorEvent.clientX - mirrorOffset.left;
|
109
|
+
const y = sensorEvent.clientY - mirrorOffset.top;
|
110
|
+
|
111
|
+
mirror.style.transform = `translate3d(${x}px, ${y}px, 0)`;
|
112
|
+
}
|
113
|
+
|
114
|
+
resolve(data);
|
115
|
+
}, {frame: withFrame});
|
116
|
+
};
|
117
|
+
}
|
118
|
+
|
119
|
+
function withPromise(callback, {raf = false} = {}) {
|
120
|
+
return new Promise((resolve, reject) => {
|
121
|
+
if (raf) {
|
122
|
+
requestAnimationFrame(() => {
|
123
|
+
callback(resolve, reject);
|
124
|
+
});
|
125
|
+
} else {
|
126
|
+
callback(resolve, reject);
|
127
|
+
}
|
128
|
+
});
|
129
|
+
}
|
@@ -0,0 +1,487 @@
|
|
1
|
+
import {closest} from './utils';
|
2
|
+
|
3
|
+
import Accessibility from './core/accessibility';
|
4
|
+
import Mirror from './core/mirror';
|
5
|
+
|
6
|
+
import Collidable from './behaviour/collidable';
|
7
|
+
import Snappable from './behaviour/snappable';
|
8
|
+
|
9
|
+
import DragSensor from './sensors/drag-sensor';
|
10
|
+
import MouseSensor from './sensors/mouse-sensor';
|
11
|
+
import TouchSensor from './sensors/touch-sensor';
|
12
|
+
import ForceTouchSensor from './sensors/force-touch-sensor';
|
13
|
+
|
14
|
+
import {
|
15
|
+
DraggableInitializedEvent,
|
16
|
+
DraggableDestroyEvent,
|
17
|
+
} from './events/draggable-event';
|
18
|
+
|
19
|
+
import {
|
20
|
+
DragStartEvent,
|
21
|
+
DragMoveEvent,
|
22
|
+
DragOutContainerEvent,
|
23
|
+
DragOutEvent,
|
24
|
+
DragOverContainerEvent,
|
25
|
+
DragOverEvent,
|
26
|
+
DragStopEvent,
|
27
|
+
DragPressureEvent,
|
28
|
+
} from './events/drag-event';
|
29
|
+
|
30
|
+
import {
|
31
|
+
MirrorCreatedEvent,
|
32
|
+
MirrorAttachedEvent,
|
33
|
+
MirrorMoveEvent,
|
34
|
+
MirrorDestroyEvent,
|
35
|
+
} from './events/mirror-event';
|
36
|
+
|
37
|
+
const defaults = {
|
38
|
+
draggable: '.draggable-source',
|
39
|
+
handle: null,
|
40
|
+
delay: 0,
|
41
|
+
placedTimeout: 800,
|
42
|
+
native: false,
|
43
|
+
plugins: [Mirror, Accessibility],
|
44
|
+
classes: {
|
45
|
+
'container:dragging': 'draggable-container--is-dragging',
|
46
|
+
'source:dragging': 'draggable-source--is-dragging',
|
47
|
+
'source:placed': 'draggable-source--placed',
|
48
|
+
'container:placed': 'draggable-container--placed',
|
49
|
+
'body:dragging': 'draggable--is-dragging',
|
50
|
+
'draggable:over': 'draggable--over',
|
51
|
+
'container:over': 'draggable-container--over',
|
52
|
+
mirror: 'draggable-mirror',
|
53
|
+
},
|
54
|
+
};
|
55
|
+
|
56
|
+
/**
|
57
|
+
* This is the core draggable library that does the heavy lifting
|
58
|
+
* @module Draggable
|
59
|
+
*/
|
60
|
+
export default class Draggable {
|
61
|
+
static get Plugins() {
|
62
|
+
return {Accessibility, Mirror};
|
63
|
+
}
|
64
|
+
|
65
|
+
static get Behaviour() {
|
66
|
+
return {Collidable, Snappable};
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* Draggable constructor.
|
71
|
+
* @constructs Draggable
|
72
|
+
* @param {Array|NodeList} containers - Draggable containers
|
73
|
+
* @param {Object} options - Options for draggable
|
74
|
+
*/
|
75
|
+
constructor(containers = [], options = {}) {
|
76
|
+
this.containers = containers;
|
77
|
+
this.options = Object.assign({}, defaults, options);
|
78
|
+
this.activeSensors = [];
|
79
|
+
this.activePlugins = [];
|
80
|
+
this.callbacks = {};
|
81
|
+
this.dragging = false;
|
82
|
+
|
83
|
+
this.dragStart = this.dragStart.bind(this);
|
84
|
+
this.dragMove = this.dragMove.bind(this);
|
85
|
+
this.dragStop = this.dragStop.bind(this);
|
86
|
+
this.dragPressure = this.dragPressure.bind(this);
|
87
|
+
|
88
|
+
for (const container of this.containers) {
|
89
|
+
container.addEventListener('drag:start', this.dragStart, true);
|
90
|
+
container.addEventListener('drag:move', this.dragMove, true);
|
91
|
+
container.addEventListener('drag:stop', this.dragStop, true);
|
92
|
+
container.addEventListener('drag:pressure', this.dragPressure, true);
|
93
|
+
}
|
94
|
+
|
95
|
+
for (const Plugin of this.options.plugins) {
|
96
|
+
const plugin = new Plugin(this);
|
97
|
+
plugin.attach();
|
98
|
+
this.activePlugins.push(plugin);
|
99
|
+
}
|
100
|
+
|
101
|
+
for (const Sensor of this.sensors()) {
|
102
|
+
const sensor = new Sensor(this.containers, options);
|
103
|
+
sensor.attach();
|
104
|
+
this.activeSensors.push(sensor);
|
105
|
+
}
|
106
|
+
|
107
|
+
const draggableInitializedEvent = new DraggableInitializedEvent({
|
108
|
+
draggable: this,
|
109
|
+
});
|
110
|
+
|
111
|
+
this.triggerEvent(draggableInitializedEvent);
|
112
|
+
}
|
113
|
+
|
114
|
+
/**
|
115
|
+
* Destroys Draggable instance. This removes all internal event listeners and
|
116
|
+
* deactivates sensors and plugins
|
117
|
+
*/
|
118
|
+
destroy() {
|
119
|
+
for (const container of this.containers) {
|
120
|
+
container.removeEventListener('drag:start', this.dragStart, true);
|
121
|
+
container.removeEventListener('drag:move', this.dragMove, true);
|
122
|
+
container.removeEventListener('drag:stop', this.dragStop, true);
|
123
|
+
container.removeEventListener('drag:pressure', this.dragPressure, true);
|
124
|
+
}
|
125
|
+
|
126
|
+
const draggableDestroyEvent = new DraggableDestroyEvent({
|
127
|
+
draggable: this,
|
128
|
+
});
|
129
|
+
|
130
|
+
this.triggerEvent(draggableDestroyEvent);
|
131
|
+
|
132
|
+
for (const activePlugin of this.activePlugins) {
|
133
|
+
activePlugin.detach();
|
134
|
+
}
|
135
|
+
|
136
|
+
for (const activeSensor of this.activeSensors) {
|
137
|
+
activeSensor.detach();
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
/**
|
142
|
+
* Adds listener for draggable events
|
143
|
+
* @example draggable.on('drag:start', (dragEvent) => dragEvent.cancel());
|
144
|
+
*/
|
145
|
+
on(type, callback) {
|
146
|
+
if (!this.callbacks[type]) {
|
147
|
+
this.callbacks[type] = [];
|
148
|
+
}
|
149
|
+
|
150
|
+
this.callbacks[type].push(callback);
|
151
|
+
return this;
|
152
|
+
}
|
153
|
+
|
154
|
+
/**
|
155
|
+
* Removes listener from draggable
|
156
|
+
* @example draggable.off('drag:start', handlerFunction);
|
157
|
+
*/
|
158
|
+
off(type, callback) {
|
159
|
+
if (!this.callbacks[type]) { return null; }
|
160
|
+
const copy = this.callbacks[type].slice(0);
|
161
|
+
for (let i = 0; i < copy.length; i++) {
|
162
|
+
if (callback === copy[i]) {
|
163
|
+
this.callbacks[type].splice(i, 1);
|
164
|
+
}
|
165
|
+
}
|
166
|
+
return this;
|
167
|
+
}
|
168
|
+
|
169
|
+
trigger(type, ...args) {
|
170
|
+
if (!this.callbacks[type]) { return; }
|
171
|
+
for (let i = this.callbacks[type].length - 1; i >= 0; i--) {
|
172
|
+
const callback = this.callbacks[type][i];
|
173
|
+
callback(...args);
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
/**
|
178
|
+
* Active sensors
|
179
|
+
* @return {Array} sensors
|
180
|
+
*/
|
181
|
+
sensors() {
|
182
|
+
return [
|
183
|
+
TouchSensor,
|
184
|
+
ForceTouchSensor,
|
185
|
+
(this.options.native ? DragSensor : MouseSensor),
|
186
|
+
];
|
187
|
+
}
|
188
|
+
|
189
|
+
dragStart(event) {
|
190
|
+
const sensorEvent = getSensorEvent(event);
|
191
|
+
const {target, container, originalEvent} = sensorEvent;
|
192
|
+
|
193
|
+
if (this.options.handle && target && !closest(target, this.options.handle)) {
|
194
|
+
sensorEvent.cancel();
|
195
|
+
return;
|
196
|
+
}
|
197
|
+
|
198
|
+
// Find draggable source element
|
199
|
+
this.source = closest(target, this.options.draggable);
|
200
|
+
this.sourceContainer = container;
|
201
|
+
|
202
|
+
if (!this.source) {
|
203
|
+
sensorEvent.cancel();
|
204
|
+
return;
|
205
|
+
}
|
206
|
+
|
207
|
+
this.dragging = true;
|
208
|
+
|
209
|
+
if (!isDragEvent(originalEvent)) {
|
210
|
+
const appendableContainer = this.getAppendableContainer({source: this.source});
|
211
|
+
this.mirror = this.source.cloneNode(true);
|
212
|
+
|
213
|
+
const mirrorCreatedEvent = new MirrorCreatedEvent({
|
214
|
+
source: this.source,
|
215
|
+
mirror: this.mirror,
|
216
|
+
sourceContainer: container,
|
217
|
+
sensorEvent,
|
218
|
+
});
|
219
|
+
|
220
|
+
const mirrorAttachedEvent = new MirrorAttachedEvent({
|
221
|
+
source: this.source,
|
222
|
+
mirror: this.mirror,
|
223
|
+
sourceContainer: container,
|
224
|
+
sensorEvent,
|
225
|
+
});
|
226
|
+
|
227
|
+
this.triggerEvent(mirrorCreatedEvent);
|
228
|
+
appendableContainer.appendChild(this.mirror);
|
229
|
+
this.triggerEvent(mirrorAttachedEvent);
|
230
|
+
}
|
231
|
+
|
232
|
+
this.source.classList.add(this.getClassNameFor('source:dragging'));
|
233
|
+
this.sourceContainer.classList.add(this.getClassNameFor('container:dragging'));
|
234
|
+
document.body.classList.add(this.getClassNameFor('body:dragging'));
|
235
|
+
|
236
|
+
if (this.mirror) {
|
237
|
+
const mirrorMoveEvent = new MirrorMoveEvent({
|
238
|
+
source: this.source,
|
239
|
+
mirror: this.mirror,
|
240
|
+
sourceContainer: container,
|
241
|
+
sensorEvent,
|
242
|
+
});
|
243
|
+
|
244
|
+
this.triggerEvent(mirrorMoveEvent);
|
245
|
+
}
|
246
|
+
|
247
|
+
// Find the closest scrollable parent
|
248
|
+
this.scrollableParent = closest(container, (element) => element.offsetHeight < element.scrollHeight);
|
249
|
+
|
250
|
+
const dragEvent = new DragStartEvent({
|
251
|
+
source: this.source,
|
252
|
+
mirror: this.mirror,
|
253
|
+
sourceContainer: container,
|
254
|
+
sensorEvent,
|
255
|
+
});
|
256
|
+
|
257
|
+
this.triggerEvent(dragEvent);
|
258
|
+
|
259
|
+
if (!dragEvent.canceled()) {
|
260
|
+
return;
|
261
|
+
}
|
262
|
+
|
263
|
+
if (this.mirror) {
|
264
|
+
this.mirror.parentNode.removeChild(this.mirror);
|
265
|
+
}
|
266
|
+
|
267
|
+
this.source.classList.remove(this.getClassNameFor('source:dragging'));
|
268
|
+
this.sourceContainer.classList.remove(this.getClassNameFor('container:dragging'));
|
269
|
+
document.body.classList.remove(this.getClassNameFor('body:dragging'));
|
270
|
+
}
|
271
|
+
|
272
|
+
triggerEvent(event) {
|
273
|
+
return this.trigger(event.type, event);
|
274
|
+
}
|
275
|
+
|
276
|
+
dragMove(event) {
|
277
|
+
const sensorEvent = getSensorEvent(event);
|
278
|
+
const {container} = sensorEvent;
|
279
|
+
let target = sensorEvent.target;
|
280
|
+
|
281
|
+
const dragMoveEvent = new DragMoveEvent({
|
282
|
+
source: this.source,
|
283
|
+
mirror: this.mirror,
|
284
|
+
sourceContainer: container,
|
285
|
+
sensorEvent,
|
286
|
+
});
|
287
|
+
|
288
|
+
this.triggerEvent(dragMoveEvent);
|
289
|
+
|
290
|
+
if (dragMoveEvent.canceled()) {
|
291
|
+
sensorEvent.cancel();
|
292
|
+
}
|
293
|
+
|
294
|
+
if (this.mirror && !dragMoveEvent.canceled()) {
|
295
|
+
const mirrorMoveEvent = new MirrorMoveEvent({
|
296
|
+
source: this.source,
|
297
|
+
mirror: this.mirror,
|
298
|
+
sourceContainer: container,
|
299
|
+
sensorEvent,
|
300
|
+
});
|
301
|
+
|
302
|
+
this.triggerEvent(mirrorMoveEvent);
|
303
|
+
}
|
304
|
+
|
305
|
+
target = closest(target, this.options.draggable);
|
306
|
+
const overContainer = sensorEvent.overContainer || this.closestContainer(sensorEvent.target);
|
307
|
+
const isLeavingContainer = this.currentOverContainer && (overContainer !== this.currentOverContainer);
|
308
|
+
const isLeavingDraggable = this.currentOver && (target !== this.currentOver);
|
309
|
+
const isOverContainer = overContainer && (this.currentOverContainer !== overContainer);
|
310
|
+
const isOverDraggable = target && (this.currentOver !== target);
|
311
|
+
|
312
|
+
if (isLeavingDraggable) {
|
313
|
+
const dragOutEvent = new DragOutEvent({
|
314
|
+
source: this.source,
|
315
|
+
mirror: this.mirror,
|
316
|
+
sourceContainer: container,
|
317
|
+
sensorEvent,
|
318
|
+
over: this.currentOver,
|
319
|
+
});
|
320
|
+
|
321
|
+
this.triggerEvent(dragOutEvent);
|
322
|
+
|
323
|
+
this.currentOver.classList.remove(this.getClassNameFor('draggable:over'));
|
324
|
+
this.currentOver = null;
|
325
|
+
}
|
326
|
+
|
327
|
+
if (isLeavingContainer) {
|
328
|
+
const dragOutContainerEvent = new DragOutContainerEvent({
|
329
|
+
source: this.source,
|
330
|
+
mirror: this.mirror,
|
331
|
+
sourceContainer: container,
|
332
|
+
sensorEvent,
|
333
|
+
overContainer: this.overContainer,
|
334
|
+
});
|
335
|
+
|
336
|
+
this.triggerEvent(dragOutContainerEvent);
|
337
|
+
|
338
|
+
this.currentOverContainer.classList.remove(this.getClassNameFor('container:over'));
|
339
|
+
this.currentOverContainer = null;
|
340
|
+
}
|
341
|
+
|
342
|
+
if (isOverContainer) {
|
343
|
+
overContainer.classList.add(this.getClassNameFor('container:over'));
|
344
|
+
|
345
|
+
const dragOverContainerEvent = new DragOverContainerEvent({
|
346
|
+
source: this.source,
|
347
|
+
mirror: this.mirror,
|
348
|
+
sourceContainer: container,
|
349
|
+
sensorEvent,
|
350
|
+
overContainer,
|
351
|
+
});
|
352
|
+
|
353
|
+
this.triggerEvent(dragOverContainerEvent);
|
354
|
+
|
355
|
+
this.currentOverContainer = overContainer;
|
356
|
+
}
|
357
|
+
|
358
|
+
if (isOverDraggable) {
|
359
|
+
target.classList.add(this.getClassNameFor('draggable:over'));
|
360
|
+
|
361
|
+
const dragOverEvent = new DragOverEvent({
|
362
|
+
source: this.source,
|
363
|
+
mirror: this.mirror,
|
364
|
+
sourceContainer: container,
|
365
|
+
sensorEvent,
|
366
|
+
overContainer,
|
367
|
+
over: target,
|
368
|
+
});
|
369
|
+
|
370
|
+
this.triggerEvent(dragOverEvent);
|
371
|
+
|
372
|
+
this.currentOver = target;
|
373
|
+
}
|
374
|
+
}
|
375
|
+
|
376
|
+
dragStop(event) {
|
377
|
+
this.dragging = false;
|
378
|
+
|
379
|
+
const sensorEvent = getSensorEvent(event);
|
380
|
+
const dragStopEvent = new DragStopEvent({
|
381
|
+
source: this.source,
|
382
|
+
mirror: this.mirror,
|
383
|
+
sensorEvent: event.sensorEvent,
|
384
|
+
sourceContainer: this.sourceContainer,
|
385
|
+
});
|
386
|
+
|
387
|
+
this.triggerEvent(dragStopEvent);
|
388
|
+
|
389
|
+
this.source.classList.remove(this.getClassNameFor('source:dragging'));
|
390
|
+
this.source.classList.add(this.getClassNameFor('source:placed'));
|
391
|
+
this.sourceContainer.classList.add(this.getClassNameFor('container:placed'));
|
392
|
+
this.sourceContainer.classList.remove(this.getClassNameFor('container:dragging'));
|
393
|
+
document.body.classList.remove(this.getClassNameFor('body:dragging'));
|
394
|
+
|
395
|
+
if (this.currentOver) {
|
396
|
+
this.currentOver.classList.remove(this.getClassNameFor('draggable:over'));
|
397
|
+
}
|
398
|
+
|
399
|
+
if (this.currentOverContainer) {
|
400
|
+
this.currentOverContainer.classList.remove(this.getClassNameFor('container:over'));
|
401
|
+
}
|
402
|
+
|
403
|
+
if (this.mirror) {
|
404
|
+
const mirrorDestroyEvent = new MirrorDestroyEvent({
|
405
|
+
source: this.source,
|
406
|
+
mirror: this.mirror,
|
407
|
+
sourceContainer: sensorEvent.container,
|
408
|
+
sensorEvent,
|
409
|
+
});
|
410
|
+
|
411
|
+
this.triggerEvent(mirrorDestroyEvent);
|
412
|
+
|
413
|
+
if (!mirrorDestroyEvent.canceled()) {
|
414
|
+
this.mirror.parentNode.removeChild(this.mirror);
|
415
|
+
}
|
416
|
+
}
|
417
|
+
|
418
|
+
const lastSource = this.source;
|
419
|
+
const lastSourceContainer = this.sourceContainer;
|
420
|
+
|
421
|
+
setTimeout(() => {
|
422
|
+
if (lastSource) {
|
423
|
+
lastSource.classList.remove(this.getClassNameFor('source:placed'));
|
424
|
+
}
|
425
|
+
|
426
|
+
if (lastSourceContainer) {
|
427
|
+
lastSourceContainer.classList.remove(this.getClassNameFor('container:placed'));
|
428
|
+
}
|
429
|
+
}, this.options.placedTimeout);
|
430
|
+
|
431
|
+
this.source = null;
|
432
|
+
this.mirror = null;
|
433
|
+
this.currentOverContainer = null;
|
434
|
+
this.currentOver = null;
|
435
|
+
this.sourceContainer = null;
|
436
|
+
}
|
437
|
+
|
438
|
+
dragPressure(event) {
|
439
|
+
const sensorEvent = getSensorEvent(event);
|
440
|
+
const source = this.source || closest(sensorEvent.originalEvent.target, this.options.draggable);
|
441
|
+
|
442
|
+
const dragPressureEvent = new DragPressureEvent({
|
443
|
+
sensorEvent,
|
444
|
+
source,
|
445
|
+
pressure: sensorEvent.pressure,
|
446
|
+
});
|
447
|
+
|
448
|
+
this.triggerEvent(dragPressureEvent);
|
449
|
+
}
|
450
|
+
|
451
|
+
getAppendableContainer({source}) {
|
452
|
+
const appendTo = this.options.appendTo;
|
453
|
+
|
454
|
+
if (typeof appendTo === 'string') {
|
455
|
+
return document.querySelector(appendTo);
|
456
|
+
} else if (appendTo instanceof HTMLElement) {
|
457
|
+
return appendTo;
|
458
|
+
} else if (typeof appendTo === 'function') {
|
459
|
+
return appendTo(source);
|
460
|
+
} else {
|
461
|
+
return document.body;
|
462
|
+
}
|
463
|
+
}
|
464
|
+
|
465
|
+
getClassNameFor(name) {
|
466
|
+
return this.options.classes[name] || defaults.classes[name];
|
467
|
+
}
|
468
|
+
|
469
|
+
closestContainer(target) {
|
470
|
+
return closest(target, (element) => {
|
471
|
+
for (const containerEl of this.containers) {
|
472
|
+
if (element === containerEl) {
|
473
|
+
return true;
|
474
|
+
}
|
475
|
+
}
|
476
|
+
return false;
|
477
|
+
});
|
478
|
+
}
|
479
|
+
}
|
480
|
+
|
481
|
+
function getSensorEvent(event) {
|
482
|
+
return event.detail;
|
483
|
+
}
|
484
|
+
|
485
|
+
function isDragEvent(event) {
|
486
|
+
return /^drag/.test(event.type);
|
487
|
+
}
|