feat: DragSelect 추가

- Vue2 버전을 Vue3로 이식
This commit is contained in:
2021-12-26 22:32:04 +09:00
parent dd5e686d03
commit a998500839
+186
View File
@@ -0,0 +1,186 @@
<template>
<div
ref="container"
style="
position: relative;
user-select: none;
overflow: hidden;
touch-action: none;
"
>
<slot :selected="intersected" />
</div>
</template>
<script lang="ts">
/// https://github.com/andi23rosca/drag-select-vue/blob/master/src/DragSelect.vue
import { defineComponent, ref, watch, onMounted, onBeforeUnmount, PropType } from "vue";
import VueTypes from "vue-types";
function getDimensions(p1: coord, p2: coord): rect {
return {
width: Math.abs(p1.x - p2.x),
height: Math.abs(p1.y - p2.y),
};
}
function collisionCheck(node1: DOMRect, node2: DOMRect): boolean {
return (
node1.left < node2.left + node2.width &&
node1.left + node1.width > node2.left &&
node1.top < node2.top + node2.height &&
node1.top + node1.height > node2.top
);
}
type coord = { x: number; y: number };
type rect = { width: number; height: number };
export default defineComponent({
props: {
attribute: VueTypes.string.isRequired,
color: {
...VueTypes.string.def("#4299E1"),
required: false,
},
opacity: {
...VueTypes.number.def(0.7),
required: false,
},
modelValue: {
type: Object as PropType<Set<string>>,
required: false,
default: ()=>new Set(),
}
},
emits: ["update:modelValue", "dragDone"],
setup(props, { emit }) {
const intersected = ref<Set<string>>(props.modelValue);
const container = ref<HTMLElement>();
watch(intersected, (val) => {
emit("update:modelValue", val);
});
watch(props.modelValue, (val)=>{
if(intersected.value === val){
return;
}
intersected.value = val;
});
onMounted(() => {
if (!container.value) {
console.error(`Container is not referenced.`);
return;
}
const uContainer = container.value;
let containerRect = uContainer.getBoundingClientRect();
function getCoords(e: MouseEvent | Touch): coord {
return {
x: e.clientX - containerRect.left,
y: e.clientY - containerRect.top,
};
}
let children: HTMLCollection;
let box = document.createElement("div");
box.setAttribute("data-drag-box-component", "");
box.style.position = "absolute";
box.style.backgroundColor = props.color;
box.style.opacity = `${props.opacity}`;
let start = { x: 0, y: 0 };
let end = { x: 0, y: 0 };
function intersection() {
const rect = box.getBoundingClientRect();
const localIntersected = new Set<string>();
for (let i = 0; i < children.length; i++) {
if (collisionCheck(rect, children[i].getBoundingClientRect())) {
const attr = children[i].getAttribute(props.attribute);
if (children[i].hasAttribute(props.attribute)) {
localIntersected.add(attr as string);
}
}
}
let dismatch = false;
for(const oldVal of intersected.value){
if(!localIntersected.has(oldVal)){
dismatch = true;
break;
}
}
if(!dismatch){
for(const newVal of localIntersected){
if(!intersected.value.has(newVal)){
dismatch = true;
break;
}
}
}
if(dismatch){
intersected.value = localIntersected;
}
}
function touchStart(e: TouchEvent) {
e.preventDefault();
startDrag(e.touches[0]);
}
function touchMove(e: TouchEvent) {
e.preventDefault();
drag(e.touches[0]);
}
function startDrag(e: MouseEvent | Touch) {
containerRect =uContainer.getBoundingClientRect();
children = uContainer.children;
start = getCoords(e);
end = start;
document.addEventListener("mousemove", drag);
document.addEventListener("touchmove", touchMove);
box.style.top = start.y + "px";
box.style.left = start.x + "px";
uContainer.prepend(box);
intersection();
}
function drag(e: MouseEvent | Touch) {
end = getCoords(e);
const dimensions = getDimensions(start, end);
if (end.x < start.x) {
box.style.left = end.x + "px";
}
if (end.y < start.y) {
box.style.top = end.y + "px";
}
box.style.width = dimensions.width + "px";
box.style.height = dimensions.height + "px";
intersection();
}
function endDrag() {
start = { x: 0, y: 0 };
end = { x: 0, y: 0 };
box.style.width = "0";
box.style.height = "0";
document.removeEventListener("mousemove", drag);
document.removeEventListener("touchmove", touchMove);
box.remove();
emit('dragDone', intersected.value);
}
uContainer.addEventListener("mousedown", startDrag);
uContainer.addEventListener("touchstart", touchStart);
document.addEventListener("mouseup", endDrag);
document.addEventListener("touchend", endDrag);
onBeforeUnmount(() => {
uContainer.removeEventListener("mousedown", startDrag);
uContainer.removeEventListener("touchstart", touchStart);
document.removeEventListener("mouseup", endDrag);
document.removeEventListener("touchend", endDrag);
});
});
return {
intersected,
container,
};
},
});
</script>