2022-01-14 04:25:47 +00:00
|
|
|
<template>
|
|
|
|
<slot />
|
2022-05-22 23:09:34 +00:00
|
|
|
<div ref="resizeListener" class="resize-listener" />
|
2022-01-14 04:25:47 +00:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
import {
|
2022-03-20 18:57:45 +00:00
|
|
|
RegisterNodeInjectionKey,
|
|
|
|
UnregisterNodeInjectionKey,
|
2022-03-20 17:30:08 +00:00
|
|
|
NodesInjectionKey,
|
2022-05-22 23:09:34 +00:00
|
|
|
BoundsInjectionKey
|
2022-03-20 18:57:45 +00:00
|
|
|
} from "game/layers";
|
2022-06-27 00:17:22 +00:00
|
|
|
import type { FeatureNode } from "game/layers";
|
2022-05-22 23:09:34 +00:00
|
|
|
import { nextTick, onMounted, provide, ref } from "vue";
|
2022-01-25 04:23:30 +00:00
|
|
|
|
2022-05-23 01:08:40 +00:00
|
|
|
const emit = defineEmits<{
|
|
|
|
(e: "updateNodes", nodes: Record<string, FeatureNode | undefined>): void;
|
|
|
|
}>();
|
|
|
|
|
2022-03-20 18:57:45 +00:00
|
|
|
const nodes = ref<Record<string, FeatureNode | undefined>>({});
|
2022-01-25 04:23:30 +00:00
|
|
|
|
2022-05-22 23:09:34 +00:00
|
|
|
const resizeObserver = new ResizeObserver(updateBounds);
|
|
|
|
const resizeListener = ref<Element | null>(null);
|
|
|
|
onMounted(() => {
|
|
|
|
// ResizeListener exists because ResizeObserver's don't work when told to observe an SVG element
|
|
|
|
const resListener = resizeListener.value;
|
|
|
|
if (resListener != null) {
|
|
|
|
resizeObserver.observe(resListener);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
let isDirty = true;
|
|
|
|
let boundingRect = ref(resizeListener.value?.getBoundingClientRect());
|
|
|
|
function updateBounds() {
|
|
|
|
if (resizeListener.value != null && isDirty) {
|
|
|
|
isDirty = false;
|
|
|
|
nextTick(() => {
|
|
|
|
boundingRect.value = resizeListener.value?.getBoundingClientRect();
|
|
|
|
(Object.values(nodes.value) as FeatureNode[])
|
|
|
|
.filter(n => n) // Sometimes the values become undefined
|
|
|
|
.forEach(node => (node.rect = node.element.getBoundingClientRect()));
|
2022-05-23 01:08:40 +00:00
|
|
|
emit("updateNodes", nodes.value);
|
2022-05-22 23:09:34 +00:00
|
|
|
isDirty = true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
document.fonts.ready.then(updateBounds);
|
|
|
|
|
2022-01-14 04:25:47 +00:00
|
|
|
const observerOptions = {
|
2022-05-24 03:31:42 +00:00
|
|
|
attributes: false,
|
2022-01-14 04:25:47 +00:00
|
|
|
childList: true,
|
2022-03-02 05:32:21 +00:00
|
|
|
subtree: false
|
2022-01-14 04:25:47 +00:00
|
|
|
};
|
|
|
|
|
2022-03-20 18:57:45 +00:00
|
|
|
provide(RegisterNodeInjectionKey, (id, element) => {
|
2022-03-23 03:55:48 +00:00
|
|
|
const observer = new MutationObserver(() => updateNode(id));
|
2022-01-14 04:25:47 +00:00
|
|
|
observer.observe(element, observerOptions);
|
2022-03-28 00:06:58 +00:00
|
|
|
nodes.value[id] = { element, observer, rect: element.getBoundingClientRect() };
|
2022-05-23 01:08:40 +00:00
|
|
|
emit("updateNodes", nodes.value);
|
2022-03-23 03:55:48 +00:00
|
|
|
nextTick(() => updateNode(id));
|
2022-01-14 04:25:47 +00:00
|
|
|
});
|
2022-03-20 18:57:45 +00:00
|
|
|
provide(UnregisterNodeInjectionKey, id => {
|
2022-03-28 00:06:58 +00:00
|
|
|
nodes.value[id]?.observer.disconnect();
|
2022-01-25 04:23:30 +00:00
|
|
|
nodes.value[id] = undefined;
|
2022-05-23 01:08:40 +00:00
|
|
|
emit("updateNodes", nodes.value);
|
2022-01-14 04:25:47 +00:00
|
|
|
});
|
2022-03-20 17:30:08 +00:00
|
|
|
provide(NodesInjectionKey, nodes);
|
2022-05-22 23:09:34 +00:00
|
|
|
provide(BoundsInjectionKey, boundingRect);
|
2022-01-14 04:25:47 +00:00
|
|
|
|
|
|
|
function updateNode(id: string) {
|
2022-01-25 04:23:30 +00:00
|
|
|
const node = nodes.value[id];
|
2022-03-23 03:55:48 +00:00
|
|
|
if (node == null) {
|
2022-01-14 04:25:47 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-03-23 03:55:48 +00:00
|
|
|
node.rect = node.element.getBoundingClientRect();
|
2022-05-23 01:08:40 +00:00
|
|
|
emit("updateNodes", nodes.value);
|
2022-01-14 04:25:47 +00:00
|
|
|
}
|
|
|
|
</script>
|
2022-05-22 23:09:34 +00:00
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
.resize-listener {
|
|
|
|
position: absolute;
|
|
|
|
top: 0px;
|
|
|
|
left: 0;
|
|
|
|
right: -4px;
|
|
|
|
bottom: 5px;
|
|
|
|
z-index: -10;
|
|
|
|
pointer-events: none;
|
|
|
|
}
|
|
|
|
</style>
|