vue-easy-dnd
Version:
Easy-DnD is a drag and drop implementation for Vue 3 that uses only standard mouse events instead of the HTML5 drag and drop API, which is [impossible to work with](https://www.quirksmode.org/blog/archives/2009/09/the_html5_drag.html). Think of it as a wa
112 lines (99 loc) • 3.29 kB
JavaScript
import { reactive } from 'vue';
import mitt from 'mitt';
/**
* This is the class of the global object that holds the state of the drag and drop during its progress. It emits events
* reporting its state evolution during the progress of the drag and drop. Its data is reactive and listeners can be
* attached to it using the method on.
*/
export class DnD {
inProgress = false;
type = null;
data = null;
source = null;
top = null;
position = null;
eventBus = mitt();
success = null;
startDrag (source, event, x, y, type, data) {
this.type = type;
this.data = data;
this.source = source;
this.position = { x, y };
this.top = null;
this.inProgress = true;
this.emit(event, 'dragstart');
this.emit(event, 'dragtopchanged', { previousTop: null });
}
resetVariables () {
this.inProgress = false;
this.data = null;
this.source = null;
this.position = null;
this.success = null;
}
stopDrag (event) {
this.success = this.top !== null && this.top['compatibleMode'] && this.top['dropAllowed'];
if (this.top !== null) {
this.emit(event, 'drop');
}
this.emit(event, 'dragend');
this.resetVariables();
}
cancelDrag (event) {
this.success = false;
this.emit(event, 'dragend');
this.resetVariables();
}
mouseMove (event, comp) {
if (this.inProgress) {
let prevent = false;
const previousTop = this.top;
if (comp === null) {
// The mouse move event reached the top of the document without hitting a drop component.
this.top = null;
prevent = true;
}
else if (comp['isDropMask']) {
// The mouse move event bubbled until it reached a drop mask.
this.top = null;
prevent = true;
}
else if (comp['candidate'](this.type, this.data, this.source)) {
// The mouse move event bubbled until it reached a drop component that participates in the current drag operation.
this.top = comp;
prevent = true;
}
if (prevent) {
// We prevent the mouse move event from bubbling further up the tree because it reached the foremost drop component and that component is all that matters.
event.stopPropagation();
}
if (this.top !== previousTop) {
this.emit(event.detail.native, 'dragtopchanged', { previousTop: previousTop });
}
this.position = {
x: event.detail.x,
y: event.detail.y
};
this.emit(event.detail.native, 'dragpositionchanged');
}
}
emit (native, event, data = {}) {
this.eventBus.emit(event, {
type: this.type,
data: this.data,
top: this.top,
source: this.source,
position: this.position,
success: this.success,
native,
...data
});
}
on (event, callback) {
this.eventBus.on(event, callback);
}
off (event, callback) {
this.eventBus.off(event, callback);
}
}
export const dnd = reactive(new DnD());