Fixed links being in the wrong spot after above elements added/removed

This commit is contained in:
thepaperpilot 2022-05-22 18:09:34 -05:00
parent d650cda84f
commit 2568a8b3a0
4 changed files with 55 additions and 52 deletions

View file

@ -1,5 +1,6 @@
<template> <template>
<slot /> <slot />
<div ref="resizeListener" class="resize-listener" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -7,13 +8,39 @@ import {
RegisterNodeInjectionKey, RegisterNodeInjectionKey,
UnregisterNodeInjectionKey, UnregisterNodeInjectionKey,
NodesInjectionKey, NodesInjectionKey,
FeatureNode FeatureNode,
BoundsInjectionKey
} from "game/layers"; } from "game/layers";
import { nextTick, provide, ref } from "vue"; import { nextTick, onMounted, provide, ref } from "vue";
const nodes = ref<Record<string, FeatureNode | undefined>>({}); 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 = { const observerOptions = {
attributes: true, attributes: true,
@ -32,6 +59,7 @@ provide(UnregisterNodeInjectionKey, id => {
nodes.value[id] = undefined; nodes.value[id] = undefined;
}); });
provide(NodesInjectionKey, nodes); provide(NodesInjectionKey, nodes);
provide(BoundsInjectionKey, boundingRect);
function updateNode(id: string) { function updateNode(id: string) {
const node = nodes.value[id]; const node = nodes.value[id];
@ -41,3 +69,15 @@ function updateNode(id: string) {
node.rect = node.element.getBoundingClientRect(); node.rect = node.element.getBoundingClientRect();
} }
</script> </script>
<style scoped>
.resize-listener {
position: absolute;
top: 0px;
left: 0;
right: -4px;
bottom: 5px;
z-index: -10;
pointer-events: none;
}
</style>

View file

@ -14,43 +14,24 @@
<script setup lang="ts"> <script setup lang="ts">
import { Link } from "features/links/links"; import { Link } from "features/links/links";
import { FeatureNode, NodesInjectionKey } from "game/layers"; import { BoundsInjectionKey, NodesInjectionKey } from "game/layers";
import { computed, inject, nextTick, onMounted, ref, toRef } from "vue"; import { computed, inject, ref, toRef, watch } from "vue";
import LinkVue from "./Link.vue"; import LinkVue from "./Link.vue";
const _props = defineProps<{ links?: Link[] }>(); const _props = defineProps<{ links?: Link[] }>();
const links = toRef(_props, "links"); 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 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const nodes = inject(NodesInjectionKey)!; const nodes = inject(NodesInjectionKey)!;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const resizeListener = ref<Element | null>(null); const outerBoundingRect = inject(BoundsInjectionKey)!;
const boundingRect = ref<DOMRect | undefined>(undefined);
onMounted(() => { watch(
// ResizeListener exists because ResizeObserver's don't work when told to observe an SVG element [outerBoundingRect],
const resListener = resizeListener.value; () => (boundingRect.value = resizeListener.value?.getBoundingClientRect())
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);
const validLinks = computed(() => { const validLinks = computed(() => {
const n = nodes.value; const n = nodes.value;

View file

@ -1,4 +1,3 @@
onMounted,
<template> <template>
<div <div
ref="resizeListener" ref="resizeListener"
@ -10,19 +9,9 @@ onMounted,
<script lang="tsx"> <script lang="tsx">
import { StyleValue } from "features/feature"; import { StyleValue } from "features/feature";
import { FeatureNode, NodesInjectionKey } from "game/layers";
import { Application } from "pixi.js"; import { Application } from "pixi.js";
import { processedPropType } from "util/vue"; import { processedPropType } from "util/vue";
import { import { defineComponent, nextTick, onBeforeUnmount, onMounted, PropType, ref, unref } from "vue";
defineComponent,
inject,
nextTick,
onBeforeUnmount,
onMounted,
PropType,
ref,
unref
} from "vue";
// TODO get typing support on the Particles component // TODO get typing support on the Particles component
export default defineComponent({ export default defineComponent({
@ -44,10 +33,6 @@ export default defineComponent({
const app = ref<null | Application>(null); const app = ref<null | Application>(null);
const resizeObserver = new ResizeObserver(updateBounds); 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); const resizeListener = ref<HTMLElement | null>(null);
onMounted(() => { onMounted(() => {
@ -77,10 +62,6 @@ export default defineComponent({
isDirty = false; isDirty = false;
nextTick(() => { nextTick(() => {
if (resizeListener.value != null && props.onContainerResized) { 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()); props.onContainerResized(resizeListener.value.getBoundingClientRect());
app.value?.resize(); app.value?.resize();
} }

View file

@ -34,6 +34,7 @@ export const UnregisterNodeInjectionKey: InjectionKey<(id: string) => void> =
Symbol("UnregisterNode"); Symbol("UnregisterNode");
export const NodesInjectionKey: InjectionKey<Ref<Record<string, FeatureNode | undefined>>> = export const NodesInjectionKey: InjectionKey<Ref<Record<string, FeatureNode | undefined>>> =
Symbol("Nodes"); Symbol("Nodes");
export const BoundsInjectionKey: InjectionKey<Ref<DOMRect | undefined>> = Symbol("Bounds");
export interface LayerEvents { export interface LayerEvents {
// Generation // Generation