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