2022-05-23 08:56:15 -07:00
|
|
|
<template>
|
2022-08-24 05:47:42 -07:00
|
|
|
<component
|
|
|
|
:is="tag"
|
2023-12-28 00:49:58 -08:00
|
|
|
ref="wrapper"
|
2022-05-23 08:56:15 -07:00
|
|
|
:class="{ [$style.dragging]: isDragging }"
|
|
|
|
@mousedown="onDragStart"
|
|
|
|
>
|
2023-12-28 00:49:58 -08:00
|
|
|
<slot :is-dragging="isDragging"></slot>
|
2022-07-20 04:32:51 -07:00
|
|
|
|
|
|
|
<Teleport to="body">
|
2023-12-28 00:49:58 -08:00
|
|
|
<div v-show="isDragging" ref="draggable" :class="$style.draggable" :style="draggableStyle">
|
|
|
|
<slot name="preview" :can-drop="canDrop" :el="draggingEl"></slot>
|
2022-07-20 04:32:51 -07:00
|
|
|
</div>
|
|
|
|
</Teleport>
|
2022-08-24 05:47:42 -07:00
|
|
|
</component>
|
2022-05-23 08:56:15 -07:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script lang="ts">
|
2023-04-24 03:18:24 -07:00
|
|
|
import type { XYPosition } from '@/Interface';
|
2023-05-05 01:41:54 -07:00
|
|
|
import { useNDVStore } from '@/stores/ndv.store';
|
2022-11-04 06:04:31 -07:00
|
|
|
import { mapStores } from 'pinia';
|
2023-04-21 08:51:08 -07:00
|
|
|
import { defineComponent } from 'vue';
|
2022-05-23 08:56:15 -07:00
|
|
|
|
2023-04-21 08:51:08 -07:00
|
|
|
export default defineComponent({
|
2023-12-28 00:49:58 -08:00
|
|
|
name: 'Draggable',
|
2022-07-20 04:32:51 -07:00
|
|
|
props: {
|
|
|
|
disabled: {
|
|
|
|
type: Boolean,
|
|
|
|
},
|
|
|
|
type: {
|
|
|
|
type: String,
|
|
|
|
},
|
|
|
|
data: {
|
|
|
|
type: String,
|
|
|
|
},
|
2022-08-24 05:47:42 -07:00
|
|
|
tag: {
|
|
|
|
type: String,
|
|
|
|
default: 'div',
|
|
|
|
},
|
|
|
|
targetDataKey: {
|
|
|
|
type: String,
|
|
|
|
},
|
2022-07-20 04:32:51 -07:00
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
data() {
|
2022-10-06 06:03:55 -07:00
|
|
|
const draggablePosition = {
|
|
|
|
x: -100,
|
|
|
|
y: -100,
|
|
|
|
};
|
|
|
|
|
2022-05-23 08:56:15 -07:00
|
|
|
return {
|
|
|
|
isDragging: false,
|
2022-10-06 06:03:55 -07:00
|
|
|
draggablePosition,
|
2022-08-24 05:47:42 -07:00
|
|
|
draggingEl: null as null | HTMLElement,
|
2022-10-06 06:03:55 -07:00
|
|
|
draggableStyle: {
|
|
|
|
transform: `translate(${draggablePosition.x}px, ${draggablePosition.y}px)`,
|
|
|
|
},
|
|
|
|
animationFrameId: 0,
|
2022-05-23 08:56:15 -07:00
|
|
|
};
|
|
|
|
},
|
2022-07-20 04:32:51 -07:00
|
|
|
computed: {
|
2022-11-04 06:04:31 -07:00
|
|
|
...mapStores(useNDVStore),
|
2022-07-20 04:32:51 -07:00
|
|
|
canDrop(): boolean {
|
2022-11-04 06:04:31 -07:00
|
|
|
return this.ndvStore.canDraggableDrop;
|
2022-07-20 04:32:51 -07:00
|
|
|
},
|
|
|
|
stickyPosition(): XYPosition | null {
|
2022-11-04 06:04:31 -07:00
|
|
|
return this.ndvStore.draggableStickyPos;
|
2022-07-20 04:32:51 -07:00
|
|
|
},
|
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
methods: {
|
2022-10-06 06:03:55 -07:00
|
|
|
setDraggableStyle() {
|
|
|
|
this.draggableStyle = {
|
|
|
|
transform: `translate(${this.draggablePosition.x}px, ${this.draggablePosition.y}px)`,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
onDragStart(e: MouseEvent) {
|
2022-07-20 04:32:51 -07:00
|
|
|
if (this.disabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-06 06:03:55 -07:00
|
|
|
this.draggingEl = e.target as HTMLElement;
|
2022-12-06 03:50:06 -08:00
|
|
|
if (this.targetDataKey && this.draggingEl.dataset?.target !== this.targetDataKey) {
|
|
|
|
this.draggingEl = this.draggingEl.closest(
|
|
|
|
`[data-target="${this.targetDataKey}"]`,
|
|
|
|
) as HTMLElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.targetDataKey && this.draggingEl?.dataset?.target !== this.targetDataKey) {
|
2022-08-24 05:47:42 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-17 04:55:43 -07:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
|
2022-10-06 06:03:55 -07:00
|
|
|
this.isDragging = false;
|
|
|
|
this.draggablePosition = { x: e.pageX, y: e.pageY };
|
|
|
|
this.setDraggableStyle();
|
2022-05-23 08:56:15 -07:00
|
|
|
|
|
|
|
window.addEventListener('mousemove', this.onDrag);
|
|
|
|
window.addEventListener('mouseup', this.onDragEnd);
|
2023-03-02 04:02:29 -08:00
|
|
|
|
|
|
|
// blur so that any focused inputs update value
|
|
|
|
const activeElement = document.activeElement as HTMLElement;
|
|
|
|
if (activeElement) {
|
|
|
|
activeElement.blur();
|
|
|
|
}
|
2022-05-23 08:56:15 -07:00
|
|
|
},
|
|
|
|
onDrag(e: MouseEvent) {
|
2022-10-06 06:03:55 -07:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
|
2022-07-20 04:32:51 -07:00
|
|
|
if (this.disabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-06 06:03:55 -07:00
|
|
|
if (!this.isDragging) {
|
|
|
|
this.isDragging = true;
|
2022-05-23 08:56:15 -07:00
|
|
|
|
2022-10-06 06:03:55 -07:00
|
|
|
const data =
|
|
|
|
this.targetDataKey && this.draggingEl ? this.draggingEl.dataset.value : this.data || '';
|
2022-11-04 06:04:31 -07:00
|
|
|
this.ndvStore.draggableStartDragging({ type: this.type, data: data || '' });
|
2022-10-06 06:03:55 -07:00
|
|
|
|
|
|
|
this.$emit('dragstart', this.draggingEl);
|
|
|
|
document.body.style.cursor = 'grabbing';
|
2022-07-20 04:32:51 -07:00
|
|
|
}
|
|
|
|
|
2022-10-06 06:03:55 -07:00
|
|
|
this.animationFrameId = window.requestAnimationFrame(() => {
|
|
|
|
if (this.canDrop && this.stickyPosition) {
|
|
|
|
this.draggablePosition = { x: this.stickyPosition[0], y: this.stickyPosition[1] };
|
|
|
|
} else {
|
|
|
|
this.draggablePosition = { x: e.pageX, y: e.pageY };
|
|
|
|
}
|
|
|
|
this.setDraggableStyle();
|
|
|
|
this.$emit('drag', this.draggablePosition);
|
|
|
|
});
|
2022-05-23 08:56:15 -07:00
|
|
|
},
|
2022-10-06 06:03:55 -07:00
|
|
|
onDragEnd() {
|
2022-07-20 04:32:51 -07:00
|
|
|
if (this.disabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-05-23 08:56:15 -07:00
|
|
|
document.body.style.cursor = 'unset';
|
|
|
|
window.removeEventListener('mousemove', this.onDrag);
|
|
|
|
window.removeEventListener('mouseup', this.onDragEnd);
|
2022-10-06 06:03:55 -07:00
|
|
|
window.cancelAnimationFrame(this.animationFrameId);
|
2022-05-23 08:56:15 -07:00
|
|
|
|
|
|
|
setTimeout(() => {
|
2022-08-24 05:47:42 -07:00
|
|
|
this.$emit('dragend', this.draggingEl);
|
2022-05-23 08:56:15 -07:00
|
|
|
this.isDragging = false;
|
2022-08-24 05:47:42 -07:00
|
|
|
this.draggingEl = null;
|
2022-11-04 06:04:31 -07:00
|
|
|
this.ndvStore.draggableStopDragging();
|
2022-05-23 08:56:15 -07:00
|
|
|
}, 0);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" module>
|
|
|
|
.dragging {
|
|
|
|
visibility: visible;
|
|
|
|
cursor: grabbing;
|
|
|
|
}
|
2022-07-20 04:32:51 -07:00
|
|
|
|
|
|
|
.draggable {
|
|
|
|
position: fixed;
|
|
|
|
z-index: 9999999;
|
2022-10-06 06:03:55 -07:00
|
|
|
top: 0;
|
|
|
|
left: 0;
|
2022-07-20 04:32:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
.draggable-data-transfer {
|
2022-10-06 06:03:55 -07:00
|
|
|
width: 0;
|
|
|
|
height: 0;
|
2022-07-20 04:32:51 -07:00
|
|
|
}
|
2022-05-23 08:56:15 -07:00
|
|
|
</style>
|