<template> <interact @dragstart="handleDragStart" @dragmove="handleDragMove" :draggable="!isDisabled" id="wrapper" ref="wrapper" @dragend="handleDragEnd" :data-transfer="dataTransferString" > <slot></slot> </interact> </template> <script> export default { name: "DragContainer", methods: { handleDragStart(event) { if (this.isDisabled) return; let rect = event.target.getBoundingClientRect(); this.dataTransfer = { key: this.dragID, x: this.x, y: this.y, w: this.w, h: this.h, data: this.data, context: this.context, originGridId: this.gridId, mouseX: event.clientX - rect.x - rect.width / (2 * this.w), // relative to center of the top left square mouseY: event.clientY - rect.y - rect.height / (2 * this.h), }; }, handleDragEnd() { this.offsetX = 0; this.offsetY = 0; }, handleDragMove(event) { this.offsetX += event.dx; this.offsetY += event.dy; }, }, props: { dragID: { type: String, required: true, }, x: { type: Number, required: true, }, y: { type: Number, required: true, }, w: { type: Number, required: true, }, h: { type: Number, required: true, }, data: { type: Object, required: false, default: () => ({}), }, context: { type: String, required: true, }, gridId: { type: String, required: true, }, disabled: Boolean, }, computed: { isInGrid() { return this.x >= 0 && this.y >= 0; }, isNotInGrid() { return this.x === -1 || this.y === -1; }, getX() { return this.x === 0 ? "auto" : this.x; }, getY() { return this.y === 0 ? "auto" : this.y; }, getDisplay() { return this.isInGrid ? "block" : "none"; }, isDisabled() { return this.disabled || this.isNotInGrid; }, cursor() { return this.disabled ? "auto" : "grab"; }, dataTransferString() { return JSON.stringify(this.dataTransfer); }, }, data() { return { dataTransfer: {}, offsetX: 0, offsetY: 0, }; }, }; </script> <style scoped> #wrapper { grid-column: v-bind(getX) / span v-bind(w); grid-row: v-bind(getY) / span v-bind(h); display: v-bind(getDisplay); cursor: v-bind(cursor); transform: translate( calc(1px * v-bind(offsetX)), calc(1px * v-bind(offsetY)) ); } </style>