level-up/frontend/components/ObjectCard.vue
2025-04-11 21:03:18 +02:00

100 lines
No EOL
2.8 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 { SceneObjectDefinition } 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: SceneObjectDefinition
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>