100 lines
No EOL
2.7 KiB
Vue
100 lines
No EOL
2.7 KiB
Vue
<template>
|
|
<div
|
|
class="flex flex-col items-center gap-2 bg-dark-600 rounded-lg p-3 border border-solid"
|
|
:class="[$style.root, borderClass]"
|
|
:style="{
|
|
transform: dragPosition === null ? undefined : `translate(${dragPosition.x}px, ${dragPosition.y}px) scale(${isOverDropzone ? 0.5 : 1})`,
|
|
transformOrigin: pointerCoordinates === null ? undefined : `${pointerCoordinates.x}px ${pointerCoordinates.y}px`,
|
|
}"
|
|
:data-object-id="object.id"
|
|
>
|
|
<ObjectPicture :object-id="object.id"/>
|
|
<div class="text-sm text-gray-200">
|
|
{{ object.label }}
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style module lang="scss">
|
|
.root {
|
|
touch-action: none;
|
|
transition: transform 50ms ease, background-color 200ms ease;
|
|
}
|
|
|
|
.combineFirst {
|
|
animation: 500ms ease-in-out alternate infinite combineFirst;
|
|
}
|
|
|
|
@keyframes combineFirst {
|
|
from {
|
|
@apply border-blue-400;
|
|
}
|
|
|
|
to {
|
|
@apply border-blue-700;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<script setup lang="ts">
|
|
import type { GameObject } from "../../shared/script/types"
|
|
import interact from "@interactjs/interact"
|
|
import { useCurrentElement } from "@vueuse/core"
|
|
import { computed, onMounted, onUnmounted, ref, useCssModule } from "vue"
|
|
import ObjectPicture from "./ObjectPicture.vue"
|
|
|
|
const props = defineProps<{
|
|
object: GameObject
|
|
isOverDropzone: boolean
|
|
markedFor: null | "use" | "combine" | "combine-first"
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
"drag-start": []
|
|
"drag-end": []
|
|
}>()
|
|
|
|
const element = useCurrentElement()
|
|
const dragPosition = ref<null | { x: number; y: number }>(null)
|
|
const pointerCoordinates = ref<null | { x: number; y: number }>(null)
|
|
|
|
onMounted(() => {
|
|
const interactable = interact(element.value as HTMLElement).draggable({
|
|
listeners: {
|
|
start(event) {
|
|
const elementBounds = (element.value as HTMLElement).getBoundingClientRect()
|
|
pointerCoordinates.value = {
|
|
x: event.clientX0 - elementBounds.x,
|
|
y: event.clientY0 - elementBounds.y,
|
|
}
|
|
|
|
dragPosition.value = { x: 0, y: 0 }
|
|
emit("drag-start")
|
|
},
|
|
move(event) {
|
|
dragPosition.value = {
|
|
x: (dragPosition.value?.x ?? 0) + event.dx,
|
|
y: (dragPosition.value?.y ?? 0) + event.dy,
|
|
}
|
|
},
|
|
end(event) {
|
|
dragPosition.value = null
|
|
emit("drag-end")
|
|
}
|
|
}
|
|
})
|
|
|
|
onUnmounted(() => interactable.unset())
|
|
})
|
|
|
|
const style = useCssModule()
|
|
|
|
const borderClass = computed(() => {
|
|
switch (props.markedFor) {
|
|
case null: return "border-dark-600"
|
|
case "use": return "border-green-600"
|
|
case "combine": return "border-blue-500"
|
|
case "combine-first": return style.combineFirst
|
|
}
|
|
})
|
|
</script> |