From b60d17065717c7bccd5f0135e30e9739021f7f94 Mon Sep 17 00:00:00 2001 From: thepaperpilot <thepaperpilot@gmail.com> Date: Sun, 20 Mar 2022 12:30:08 -0500 Subject: [PATCH] Made node positions more general purpose --- src/components/Game.vue | 5 +++-- src/components/Layer.vue | 23 +++++++++++++++++++---- src/components/Modal.vue | 12 ++++++++---- src/components/links/Links.vue | 11 ++++++++--- src/features/links.ts | 5 ++++- src/features/particles/particles.tsx | 14 +++++--------- src/game/layers.tsx | 6 ++++-- 7 files changed, 51 insertions(+), 25 deletions(-) diff --git a/src/components/Game.vue b/src/components/Game.vue index dda801f..33067af 100644 --- a/src/components/Game.vue +++ b/src/components/Game.vue @@ -29,8 +29,9 @@ const layerKeys = computed(() => Object.keys(layers)); const useHeader = projInfo.useHeader; function gatherLayerProps(layer: GenericLayer) { - const { display, minimized, minWidth, name, color, style, classes, links, minimizable } = layer; - return { display, minimized, minWidth, name, color, style, classes, links, minimizable }; + const { display, minimized, minWidth, name, color, style, classes, links, minimizable, nodes } = + layer; + return { display, minimized, minWidth, name, color, style, classes, links, minimizable, nodes }; } </script> diff --git a/src/components/Layer.vue b/src/components/Layer.vue index cb26662..7ca24ae 100644 --- a/src/components/Layer.vue +++ b/src/components/Layer.vue @@ -10,7 +10,7 @@ :class="[{ showGoBack }, unref(classes)]" v-else > - <Links :links="unref(links)"> + <Links :links="unref(links)" ref="linksRef"> <component :is="component" /> </Links> </div> @@ -24,11 +24,11 @@ import Links from "components/links/Links.vue"; import projInfo from "data/projInfo.json"; import { CoercableComponent, StyleValue } from "features/feature"; -import { Link } from "features/links"; +import { Link, LinkNode } from "features/links"; import { PersistentRef } from "game/persistence"; import player from "game/player"; import { computeComponent, processedPropType, wrapRef } from "util/vue"; -import { computed, defineComponent, nextTick, PropType, toRefs, unref, watch } from "vue"; +import { computed, defineComponent, nextTick, PropType, Ref, ref, toRefs, unref, watch } from "vue"; export default defineComponent({ components: { Links }, @@ -61,7 +61,11 @@ export default defineComponent({ style: processedPropType<StyleValue>(String, Object, Array), classes: processedPropType<Record<string, boolean>>(Object), links: processedPropType<Link[]>(Array), - minimizable: processedPropType<boolean>(Boolean) + minimizable: processedPropType<boolean>(Boolean), + nodes: { + type: Object as PropType<Ref<Record<string, LinkNode | undefined>>>, + required: true + } }, setup(props) { const { display, index, minimized, minWidth, tab } = toRefs(props); @@ -80,6 +84,16 @@ export default defineComponent({ updateTab(minimized, minWidth) ); + const linksRef = ref<typeof Links | null>(null); + watch( + () => linksRef.value?.nodes, + nodes => { + if (nodes) { + props.nodes.value = nodes; + } + } + ); + function updateTab(minimized: boolean, minWidth: number) { const tabValue = tab.value(); if (tabValue != undefined) { @@ -102,6 +116,7 @@ export default defineComponent({ return { component, showGoBack, + linksRef, unref, goBack }; diff --git a/src/components/Modal.vue b/src/components/Modal.vue index b53de06..967036c 100644 --- a/src/components/Modal.vue +++ b/src/components/Modal.vue @@ -17,10 +17,9 @@ <slot name="header" :shown="isOpen"> default header </slot> </div> <div class="modal-body"> - <Links v-if="links" :links="links"> + <Links :links="links" ref="linksRef"> <slot name="body" :shown="isOpen"> default body </slot> </Links> - <slot name="body" v-else :shown="isOpen"> default body </slot> </div> <div class="modal-footer"> <slot name="footer" :shown="isOpen"> @@ -40,7 +39,7 @@ </template> <script setup lang="ts"> -import { Link } from "features/links"; +import { Link, LinkNode } from "features/links"; import { computed, ref, toRefs } from "vue"; import Links from "./links/Links.vue"; @@ -60,7 +59,12 @@ function close() { const isAnimating = ref(false); -defineExpose({ isOpen }); +const linksRef = ref<typeof Links | null>(null); +const nodes = computed<Record<string, LinkNode | undefined> | null>( + () => linksRef.value?.nodes ?? null +); + +defineExpose({ isOpen, nodes }); </script> <style> diff --git a/src/components/links/Links.vue b/src/components/links/Links.vue index 1369fb9..701de9f 100644 --- a/src/components/links/Links.vue +++ b/src/components/links/Links.vue @@ -16,6 +16,7 @@ import { Link, LinkNode, + NodesInjectionKey, RegisterLinkNodeInjectionKey, UnregisterLinkNodeInjectionKey } from "features/links"; @@ -30,6 +31,8 @@ const resizeObserver = new ResizeObserver(updateNodes); const nodes = ref<Record<string, LinkNode | undefined>>({}); +defineExpose({ nodes }); + const resizeListener = ref<Element | null>(null); onMounted(() => { @@ -71,6 +74,7 @@ provide(RegisterLinkNodeInjectionKey, (id, element) => { provide(UnregisterLinkNodeInjectionKey, id => { nodes.value[id] = undefined; }); +provide(NodesInjectionKey, nodes); let isDirty = true; let boundingRect = resizeListener.value?.getBoundingClientRect(); @@ -90,10 +94,11 @@ function updateNode(id: string) { if (!node || boundingRect == null) { return; } - const linkStartRect = node.element.getBoundingClientRect(); + const nodeRect = node.element.getBoundingClientRect(); - node.x = linkStartRect.x + linkStartRect.width / 2 - boundingRect.x; - node.y = linkStartRect.y + linkStartRect.height / 2 - boundingRect.y; + node.y = nodeRect.x + nodeRect.width / 2 - boundingRect.x; + node.x = nodeRect.y + nodeRect.height / 2 - boundingRect.y; + node.rect = nodeRect; } </script> diff --git a/src/features/links.ts b/src/features/links.ts index eb37cf5..e0cb276 100644 --- a/src/features/links.ts +++ b/src/features/links.ts @@ -1,9 +1,10 @@ import { Position } from "game/layers"; -import { InjectionKey, SVGAttributes } from "vue"; +import { InjectionKey, Ref, SVGAttributes } from "vue"; export interface LinkNode { x?: number; y?: number; + rect?: DOMRect; element: HTMLElement; } @@ -19,3 +20,5 @@ export const RegisterLinkNodeInjectionKey: InjectionKey< > = Symbol("RegisterLinkNode"); export const UnregisterLinkNodeInjectionKey: InjectionKey<(id: string) => void> = Symbol("UnregisterLinkNode"); +export const NodesInjectionKey: InjectionKey<Ref<Record<string, LinkNode | undefined>>> = + Symbol("Nodes"); diff --git a/src/features/particles/particles.tsx b/src/features/particles/particles.tsx index 0b05ef5..010bb36 100644 --- a/src/features/particles/particles.tsx +++ b/src/features/particles/particles.tsx @@ -2,7 +2,6 @@ import ParticlesComponent from "./Particles.vue"; import { IEmitter } from "tsparticles-plugin-emitters/Options/Interfaces/IEmitter"; import { EmitterInstance } from "tsparticles-plugin-emitters/EmitterInstance"; import { EmitterContainer } from "tsparticles-plugin-emitters/EmitterContainer"; -import { ICoordinates } from "tsparticles-engine"; import { Ref, shallowRef } from "vue"; import { registerGameComponent } from "game/settings"; import { jsx } from "features/feature"; @@ -14,18 +13,17 @@ const containerRef: Ref<null | EmitterContainer> = shallowRef(null); let emittersToAdd: { resolve: (value: EmitterInstance | PromiseLike<EmitterInstance>) => void; options: IEmitter & { particles: Required<IEmitter>["particles"] }; - position: ICoordinates; }[] = []; export function addEmitter( - options: IEmitter & { particles: Required<IEmitter>["particles"] }, - position: ICoordinates + options: IEmitter & { particles: Required<IEmitter>["particles"] } ): Promise<EmitterInstance> { if (containerRef.value) { - return Promise.resolve(containerRef.value.addEmitter(options, position)); + // TODO why does addEmitter require a position parameter + return Promise.resolve(containerRef.value.addEmitter(options)); } return new Promise<EmitterInstance>(resolve => { - emittersToAdd.push({ resolve, options, position }); + emittersToAdd.push({ resolve, options }); }); } @@ -36,8 +34,6 @@ export function removeEmitter(emitter: EmitterInstance) { function onInit(container: EmitterContainer) { containerRef.value = container; - emittersToAdd.forEach(({ resolve, options, position }) => - resolve(container.addEmitter(options, position)) - ); + emittersToAdd.forEach(({ resolve, options }) => resolve(container.addEmitter(options))); emittersToAdd = []; } diff --git a/src/game/layers.tsx b/src/game/layers.tsx index 2a7bc45..8a93374 100644 --- a/src/game/layers.tsx +++ b/src/game/layers.tsx @@ -7,7 +7,7 @@ import { setDefault, StyleValue } from "features/feature"; -import { Link } from "features/links"; +import { Link, LinkNode } from "features/links"; import { Computable, GetComputableType, @@ -17,7 +17,7 @@ import { } from "util/computed"; import { createLazyProxy } from "util/proxies"; import { createNanoEvents, Emitter } from "nanoevents"; -import { ref, unref } from "vue"; +import { Ref, ref, unref } from "vue"; import { globalBus } from "./events"; import { persistent, PersistentRef } from "./persistence"; import player from "./player"; @@ -63,6 +63,7 @@ export interface BaseLayer { emitter: Emitter<LayerEvents>; on: OmitThisParameter<Emitter<LayerEvents>["on"]>; emit: <K extends keyof LayerEvents>(event: K, ...args: Parameters<LayerEvents[K]>) => void; + nodes: Ref<Record<string, LinkNode | undefined>>; } export type Layer<T extends LayerOptions> = Replace< @@ -97,6 +98,7 @@ export function createLayer<T extends LayerOptions>( const emitter = (layer.emitter = createNanoEvents<LayerEvents>()); layer.on = emitter.on.bind(emitter); layer.emit = emitter.emit.bind(emitter); + layer.nodes = ref({}); layer.minimized = persistent(false);