draggable-rails 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
}
|