diff --git a/package.json b/package.json index c0ad805589a010c35b031b74a3172677596c1c02..2df9369261372a06693e06c24089e20778ec0e4d 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "vuepress": "^1.9.8" }, "dependencies": { - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "vue-interactjs": "^0.1.10" } } diff --git a/src/DragContainer.vue b/src/DragContainer.vue index df4a8657f356a40f05a31e68f9a3ccb836e9d43a..6c2b6441dc6bd9fbdf268e9a8e0cd19aafadce7f 100644 --- a/src/DragContainer.vue +++ b/src/DragContainer.vue @@ -1,13 +1,15 @@ <template> - <div + <interact @dragstart="handleDragStart" + @dragmove="handleDragMove" :draggable="!isDisabled" id="wrapper" ref="wrapper" @dragend="handleDragEnd" + :data-transfer="dataTransferString" > <slot></slot> - </div> + </interact> </template> <script> @@ -16,26 +18,27 @@ export default { methods: { handleDragStart(event) { if (this.isDisabled) return; - this.$refs.wrapper.style.cursor = "grabbing"; let rect = event.target.getBoundingClientRect(); - event.dataTransfer.setData( - "vueDrag/gridItem", - JSON.stringify({ - 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), - }) - ); + 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.$refs.wrapper.style.cursor = "grab"; + this.offsetX = 0; + this.offsetY = 0; + }, + handleDragMove(event) { + this.offsetX += event.dx; + this.offsetY += event.dy; }, }, props: { @@ -96,6 +99,16 @@ export default { cursor() { return this.disabled ? "auto" : "grab"; }, + dataTransferString() { + return JSON.stringify(this.dataTransfer); + }, + }, + data() { + return { + dataTransfer: {}, + offsetX: 0, + offsetY: 0, + }; }, }; </script> @@ -106,5 +119,9 @@ export default { 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> diff --git a/src/DragGrid.vue b/src/DragGrid.vue index 78a6cb76daafc994e5a724dee99252bdc3e96875..2b926ef1d041df8c54be187f05d3bf84ff5ba3ff 100644 --- a/src/DragGrid.vue +++ b/src/DragGrid.vue @@ -1,6 +1,7 @@ <template> - <div - @dragover.prevent="disabled ? undefined : handleDragOver($event)" + <interact + droppable + @dropmove="disabled ? undefined : handleDragOver($event)" @drop.prevent="disabled ? undefined : handleDrop($event)" @dragleave="$refs.highlightContainer.style.display = 'none'" class="grid" @@ -47,7 +48,7 @@ </GridItem> </template> <slot></slot> - </div> + </interact> </template> <script> @@ -143,12 +144,12 @@ export default { return true; }, handleDragOver(event) { - let data = event.dataTransfer.getData("vueDrag/gridItem"); + let data = event.relatedTarget.dataset.transfer; if (!data) return; let element = JSON.parse(data); let coords = this.getCoords( - event.clientX - element.mouseX, - event.clientY - element.mouseY + event.dragEvent.client.x - element.mouseX, + event.dragEvent.client.y - element.mouseY ); if (element.context !== this.context || this.noHighlight) { @@ -179,7 +180,7 @@ export default { }, handleDrop(event) { this.$refs.highlightContainer.style.display = "none"; - let data = event.dataTransfer.getData("vueDrag/gridItem"); + let data = event.relatedTarget.dataset.transfer; if (!data) return; let element = JSON.parse(data); @@ -190,8 +191,8 @@ export default { } let coords = this.getCoords( - event.clientX - element.mouseX, - event.clientY - element.mouseY + event.dragEvent.client.x - element.mouseX, + event.dragEvent.client.y - element.mouseY ); let newPositionValid = true; @@ -221,6 +222,8 @@ export default { elementCopy.context = undefined; elementCopy.originGridId = undefined; + elementCopy.mouseX = undefined; + elementCopy.mouseY = undefined; valueCopy.push(elementCopy); this.$emit("input", valueCopy); @@ -239,11 +242,20 @@ export default { this.$emit("itemChanged", element); }, + clamp: (min, num, max) => Math.min(Math.max(num, min), max), getCoords(x, y) { let rect = this.$el.getBoundingClientRect(); return { - x: Math.ceil((x - rect.x) / (rect.width / this.cols)), - y: Math.ceil((y - rect.y) / (rect.height / this.rows)), + x: this.clamp( + 1, + Math.ceil((x - rect.x) / (rect.width / this.cols)), + this.cols + ), + y: this.clamp( + 1, + Math.ceil((y - rect.y) / (rect.height / this.rows)), + this.rows + ), }; }, getInt(property, item) { @@ -301,5 +313,6 @@ export default { min-width: 100px; min-height: 100px; gap: 1em; + touch-action: none; } </style> diff --git a/src/index.js b/src/index.js index 7d9aa6ed2162895c67d6cf60c1d77bf2b21dd5ba..e29a2d81b8d4782dd12752065b996dd7d813eebe 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,8 @@ import components from "./components"; +import Vue from "vue"; +import VueInteractJs from "vue-interactjs"; + +Vue.use(VueInteractJs); const plugin = { install(Vue) {