Fixed links being in the wrong spot after above elements added/removed
This commit is contained in:
parent
d650cda84f
commit
2568a8b3a0
4 changed files with 55 additions and 52 deletions
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue