<script setup lang="ts" generic="T extends {
id: string
}">
const container = useTemplateRef<HTMLDivElement>('containerRef')
const props = defineProps<{
fragments: T[]
}>()
const refs = computed(() => {
return props.fragments.map((fragment) => ({
fragment: fragment,
ref: ref<HTMLElement>(),
}));
});
type Draggable = {
position: globalThis.Ref<{
x: number;
y: number;
}>;
isDragging: globalThis.ComputedRef<boolean>;
style: globalThis.ComputedRef<string>;
x: globalThis.Ref<number>;
y: globalThis.Ref<number>;
}
const draggables = ref<Record<string, Draggable>>({})
watch(refs, async (newRefs, oldRefs) => {
await nextTick()
const old = oldRefs ?? []
// check all old refs and see if any of them are not in the new refs
const removedRefs = old.filter((oldRef) => !newRefs.includes(oldRef))
const addedRefs = newRefs.filter((newRef) => !old.includes(newRef))
removedRefs.forEach((ref) => {
delete draggables.value[ref.fragment.id]
})
addedRefs.forEach((ref) => {
draggables.value[ref.fragment.id] = useDraggable(ref.ref, {
containerElement: container,
})
})
}, {
immediate: true
})
</script>
<template>
<div ref="containerRef" class="w-full h-full bg-green-300 relative">
<div v-for="(fragment, index) in fragments" :key="fragment.id" class="absolute" :ref="(el) => refs[index]!.ref.value = el as HTMLElement" :style="(draggables[fragment.id]?.style as any as string)">
<slot :fragment="fragment" :drag="draggables[index]!" />
</div>
</div>
</template>