From 2568a8b3a003ba22bced961078e3f16648fdcaee Mon Sep 17 00:00:00 2001
From: thepaperpilot <thepaperpilot@gmail.com>
Date: Sun, 22 May 2022 18:09:34 -0500
Subject: [PATCH] Fixed links being in the wrong spot after above elements
 added/removed

---
 src/components/Context.vue           | 46 ++++++++++++++++++++++++++--
 src/features/links/Links.vue         | 39 ++++++-----------------
 src/features/particles/Particles.vue | 21 +------------
 src/game/layers.tsx                  |  1 +
 4 files changed, 55 insertions(+), 52 deletions(-)

diff --git a/src/components/Context.vue b/src/components/Context.vue
index 867ffe1..c55bc10 100644
--- a/src/components/Context.vue
+++ b/src/components/Context.vue
@@ -1,5 +1,6 @@
 <template>
     <slot />
+    <div ref="resizeListener" class="resize-listener" />
 </template>
 
 <script setup lang="ts">
@@ -7,13 +8,39 @@ import {
     RegisterNodeInjectionKey,
     UnregisterNodeInjectionKey,
     NodesInjectionKey,
-    FeatureNode
+    FeatureNode,
+    BoundsInjectionKey
 } from "game/layers";
-import { nextTick, provide, ref } from "vue";
+import { nextTick, onMounted, provide, ref } from "vue";
 
 const nodes = ref<Record<string, FeatureNode | undefined>>({});
 
-defineExpose({ nodes });
+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()));
+            isDirty = true;
+        });
+    }
+}
+document.fonts.ready.then(updateBounds);
+
+defineExpose({ nodes, boundingRect });
 
 const observerOptions = {
     attributes: true,
@@ -32,6 +59,7 @@ provide(UnregisterNodeInjectionKey, id => {
     nodes.value[id] = undefined;
 });
 provide(NodesInjectionKey, nodes);
+provide(BoundsInjectionKey, boundingRect);
 
 function updateNode(id: string) {
     const node = nodes.value[id];
@@ -41,3 +69,15 @@ function updateNode(id: string) {
     node.rect = node.element.getBoundingClientRect();
 }
 </script>
+
+<style scoped>
+.resize-listener {
+    position: absolute;
+    top: 0px;
+    left: 0;
+    right: -4px;
+    bottom: 5px;
+    z-index: -10;
+    pointer-events: none;
+}
+</style>
diff --git a/src/features/links/Links.vue b/src/features/links/Links.vue
index 5071705..e87ff22 100644
--- a/src/features/links/Links.vue
+++ b/src/features/links/Links.vue
@@ -14,43 +14,24 @@
 
 <script setup lang="ts">
 import { Link } from "features/links/links";
-import { FeatureNode, NodesInjectionKey } from "game/layers";
-import { computed, inject, nextTick, onMounted, ref, toRef } from "vue";
+import { BoundsInjectionKey, NodesInjectionKey } from "game/layers";
+import { computed, inject, ref, toRef, watch } from "vue";
 import LinkVue from "./Link.vue";
 
 const _props = defineProps<{ links?: Link[] }>();
 const links = toRef(_props, "links");
 
-const resizeObserver = new ResizeObserver(updateNodes);
+const resizeListener = ref<Element | null>(null);
 
 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
 const nodes = inject(NodesInjectionKey)!;
-
-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 updateNodes() {
-    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()));
-            isDirty = true;
-        });
-    }
-}
-document.fonts.ready.then(updateNodes);
+// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+const outerBoundingRect = inject(BoundsInjectionKey)!;
+const boundingRect = ref<DOMRect | undefined>(undefined);
+watch(
+    [outerBoundingRect],
+    () => (boundingRect.value = resizeListener.value?.getBoundingClientRect())
+);
 
 const validLinks = computed(() => {
     const n = nodes.value;
diff --git a/src/features/particles/Particles.vue b/src/features/particles/Particles.vue
index 2c84973..9d1a7f9 100644
--- a/src/features/particles/Particles.vue
+++ b/src/features/particles/Particles.vue
@@ -1,4 +1,3 @@
-onMounted,
 <template>
     <div
         ref="resizeListener"
@@ -10,19 +9,9 @@ onMounted,
 
 <script lang="tsx">
 import { StyleValue } from "features/feature";
-import { FeatureNode, NodesInjectionKey } from "game/layers";
 import { Application } from "pixi.js";
 import { processedPropType } from "util/vue";
-import {
-    defineComponent,
-    inject,
-    nextTick,
-    onBeforeUnmount,
-    onMounted,
-    PropType,
-    ref,
-    unref
-} from "vue";
+import { defineComponent, nextTick, onBeforeUnmount, onMounted, PropType, ref, unref } from "vue";
 
 // TODO get typing support on the Particles component
 export default defineComponent({
@@ -44,10 +33,6 @@ export default defineComponent({
         const app = ref<null | Application>(null);
 
         const resizeObserver = new ResizeObserver(updateBounds);
-
-        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        const nodes = inject(NodesInjectionKey)!;
-
         const resizeListener = ref<HTMLElement | null>(null);
 
         onMounted(() => {
@@ -77,10 +62,6 @@ export default defineComponent({
                 isDirty = false;
                 nextTick(() => {
                     if (resizeListener.value != null && props.onContainerResized) {
-                        // TODO don't overlap with Links.vue
-                        (Object.values(nodes.value).filter(n => n) as FeatureNode[]).forEach(
-                            node => (node.rect = node.element.getBoundingClientRect())
-                        );
                         props.onContainerResized(resizeListener.value.getBoundingClientRect());
                         app.value?.resize();
                     }
diff --git a/src/game/layers.tsx b/src/game/layers.tsx
index cb7462a..605eb69 100644
--- a/src/game/layers.tsx
+++ b/src/game/layers.tsx
@@ -34,6 +34,7 @@ export const UnregisterNodeInjectionKey: InjectionKey<(id: string) => void> =
     Symbol("UnregisterNode");
 export const NodesInjectionKey: InjectionKey<Ref<Record<string, FeatureNode | undefined>>> =
     Symbol("Nodes");
+export const BoundsInjectionKey: InjectionKey<Ref<DOMRect | undefined>> = Symbol("Bounds");
 
 export interface LayerEvents {
     // Generation