vue3-dragula
Version:
Vue3 wrapper library for dragula
293 lines • 10.1 kB
JavaScript
//--------------------------------------------------//
// vue3-dragula //
// Created by basicx-StrgV //
// https://github.com/basicx-StrgV/ //
//--------------------------------------------------//
// Based on 'vue-dragula' by Astray-git //
// https://github.com/Astray-git //
//--------------------------------------------------//
import dragula from 'dragula';
import mitt from 'mitt';
import { nextTick } from 'vue';
if (!dragula) {
throw new Error('[vue3-dragula] Cannot locate package: dragula');
}
if (!mitt) {
throw new Error('[vue3-dragula] Cannot locate package: mitt');
}
export const VueDragula = {
install(app, options) {
vueDragula(app);
}
};
export class Bag {
constructor(name, drake) {
this.drakeRegistert = false;
this.initEvents = false;
this.models = [];
this.name = name;
this.drake = drake;
}
destroy() {
this.drakeRegistert = false;
this.initEvents = false;
this.models.splice(0, this.models.length);
if (this.drake != null) {
this.drake.destroy();
}
this.drake = null;
}
}
export class VueDragulaGlobal {
}
function vueDragula(vueApp) {
const service = new DragulaService();
VueDragulaGlobal.eventBus = service.eventBus;
VueDragulaGlobal.find = service.find.bind(service);
VueDragulaGlobal.options = service.setOptions.bind(service);
VueDragulaGlobal.injectOptions = service.injectOptions.bind(service);
vueApp.directive('dragula', {
beforeMount(container, binding, vnode) {
let name = 'globalBag';
let drake;
const bagName = vnode.props ? vnode.props['bag'] : null;
if (bagName != null && bagName.trim().length !== 0) {
name = bagName;
}
let bag = service.find(name);
if (bag != null) {
drake = bag.drake;
if (drake == null) {
return;
}
drake.containers.push(container);
updateModelBinding(container, binding, vnode);
return;
}
drake = dragula({ containers: [container] });
bag = service.add(name, drake);
updateModelBinding(container, binding, vnode);
service.handleModels(name, bag);
},
updated(container, binding, vnode, oldVnode) {
updateModelBinding(container, binding, vnode);
},
unmounted(container, binding, vnode) {
let unbindBagName = 'globalBag';
const bagName = vnode.props ? vnode.props['bag'] : null;
if (bagName != null && bagName.trim().length !== 0) {
unbindBagName = bagName;
}
let bag = service.find(unbindBagName);
if (bag == null || bag.drake == null) {
return;
}
let containerIndex = bag.drake.containers.indexOf(container);
if (containerIndex > -1) {
bag.drake.containers.splice(containerIndex, 1);
}
if (bag.drake.containers.length === 0) {
service.destroy(unbindBagName);
}
}
});
function updateModelBinding(container, binding, vnode) {
let name = 'globalBag';
const newValue = vnode ? binding.value : null;
if (newValue == null) {
return;
}
const bagName = vnode.props ? vnode.props['bag'] : null;
if (bagName != null && bagName.trim().length !== 0) {
name = bagName;
}
const bag = service.find(name);
if (bag == null) {
return;
}
let modelContainer = service.findModelContainerByContainer(container, bag);
if (modelContainer) {
modelContainer.model = newValue;
}
else {
bag.models.push({
model: newValue,
container: container
});
}
}
}
class DragulaService {
constructor() {
this.bags = [];
this.eventBus = mitt();
this.events = [
'cancel',
'cloned',
'drag',
'dragend',
'drop',
'out',
'over',
'remove',
'shadow',
'dropModel',
'removeModel'
];
}
add(name, drake) {
let bag = this.find(name);
if (bag != null) {
throw new Error('[vue3-dragula] Bag named: "' + name + '" already exists.');
}
bag = new Bag(name, drake);
this.bags.push(bag);
this.handleModels(name, bag);
if (!bag.initEvents) {
this.setupEvents(bag);
}
return bag;
}
find(name) {
let bags = this.bags;
for (const bag of bags) {
if (bag.name === name) {
return bag;
}
}
return null;
}
handleModels(name, bag) {
// Cancel if drake object does not exist or if it is already registert
if (bag.drake == null || bag.drakeRegistert) {
return;
}
let dragElm;
let dragIndex;
let isManualCancel = false;
// On Remove event handler
bag.drake.on('remove', (el, container, source) => {
if (bag.drake == null) {
return;
}
let sourceModel = this.findModelForContainer(source, bag);
sourceModel.splice(dragIndex, 1);
isManualCancel = true;
bag.drake.cancel(true);
// Emit removeModel event
this.eventBus.emit('removeModel', [name, el, source, dragIndex]);
});
// On Drag event handler
bag.drake.on('drag', (el, source) => {
dragElm = el;
dragIndex = this.domIndexOf(el, source);
});
// On Drop event handler
bag.drake.on('drop', (dropElm, target, source) => {
if (bag.drake == null || target == null) {
return;
}
let dropIndex = this.domIndexOf(dropElm, target);
let sourceModel = this.findModelForContainer(source, bag);
let dropElmModel = null;
if (target === source) {
nextTick(() => {
sourceModel.splice(dropIndex, 0, sourceModel.splice(dragIndex, 1)[0]);
});
dropElmModel = sourceModel[dropIndex];
}
else {
let notCopy = dragElm === dropElm;
let targetModel = this.findModelForContainer(target, bag);
dropElmModel = notCopy ? sourceModel[dragIndex] : this.cloneObject(sourceModel[dragIndex]);
if (notCopy) {
nextTick(() => {
sourceModel.splice(dragIndex, 1);
});
}
nextTick(() => {
targetModel.splice(dropIndex, 0, dropElmModel);
});
}
isManualCancel = true;
bag.drake.cancel(true);
// Emit dropModel event
this.eventBus.emit('dropModel', [name, dropElm, dropElmModel, target, source, dropIndex]);
});
// On Cancel event handler
bag.drake.on('cancel', (dropElm, container, source) => {
// Only handle the cancel if it was triggerd by dragula and not from this library
if (bag.drake == null || isManualCancel) {
isManualCancel = false;
return;
}
let sourceModel = this.findModelForContainer(source, bag);
let dropElmModel = sourceModel[dragIndex];
// Emit cancelModel event
this.eventBus.emit('cancelModel', [name, dropElm, dropElmModel, source]);
});
// Set registration flag
bag.drakeRegistert = true;
}
destroy(name) {
let bag = this.find(name);
if (bag == null) {
return;
}
let bagIndex = this.bags.indexOf(bag);
this.bags.splice(bagIndex, 1);
bag.destroy();
}
setOptions(name, options) {
let bag = this.add(name, dragula(options));
if (bag.drake != null) {
this.handleModels(name, bag);
}
}
injectOptions(name, options) {
let bag = this.find(name);
if (bag == null) {
throw new Error('[vue3-dragula] Bag named: "' + name + '" does not exists.');
}
let currentContainers = [];
if (bag.drake != null) {
currentContainers = bag.drake.containers;
bag.drake.destroy();
bag.drake = null;
bag.drakeRegistert = false;
bag.initEvents = false;
}
options.containers = currentContainers;
bag.drake = dragula(options);
this.handleModels(name, bag);
this.setupEvents(bag);
}
setupEvents(bag) {
bag.initEvents = true;
let _this = this;
let emitter = (type) => {
function replicate() {
let args = Array.prototype.slice.call(arguments);
_this.eventBus.emit(type, [bag.name].concat(args));
}
if (bag.drake != null) {
bag.drake.on(type, replicate);
}
};
this.events.forEach(emitter);
}
domIndexOf(child, parent) {
return Array.prototype.indexOf.call(parent.children, child);
}
findModelForContainer(container, bag) {
var _a;
return (_a = this.findModelContainerByContainer(container, bag)) === null || _a === void 0 ? void 0 : _a.model;
}
findModelContainerByContainer(container, bag) {
return bag.models.find((model) => model.container === container);
}
cloneObject(object) {
return JSON.parse(JSON.stringify(object));
}
}
//# sourceMappingURL=vue3-dragula.js.map