Made node positions more general purpose

This commit is contained in:
thepaperpilot 2022-03-20 12:30:08 -05:00
parent 0acfebb412
commit b60d170657
7 changed files with 51 additions and 25 deletions

View file

@ -29,8 +29,9 @@ const layerKeys = computed(() => Object.keys(layers));
const useHeader = projInfo.useHeader; const useHeader = projInfo.useHeader;
function gatherLayerProps(layer: GenericLayer) { function gatherLayerProps(layer: GenericLayer) {
const { display, minimized, minWidth, name, color, style, classes, links, minimizable } = layer; const { display, minimized, minWidth, name, color, style, classes, links, minimizable, nodes } =
return { display, minimized, minWidth, name, color, style, classes, links, minimizable }; layer;
return { display, minimized, minWidth, name, color, style, classes, links, minimizable, nodes };
} }
</script> </script>

View file

@ -10,7 +10,7 @@
:class="[{ showGoBack }, unref(classes)]" :class="[{ showGoBack }, unref(classes)]"
v-else v-else
> >
<Links :links="unref(links)"> <Links :links="unref(links)" ref="linksRef">
<component :is="component" /> <component :is="component" />
</Links> </Links>
</div> </div>
@ -24,11 +24,11 @@
import Links from "components/links/Links.vue"; import Links from "components/links/Links.vue";
import projInfo from "data/projInfo.json"; import projInfo from "data/projInfo.json";
import { CoercableComponent, StyleValue } from "features/feature"; import { CoercableComponent, StyleValue } from "features/feature";
import { Link } from "features/links"; import { Link, LinkNode } from "features/links";
import { PersistentRef } from "game/persistence"; import { PersistentRef } from "game/persistence";
import player from "game/player"; import player from "game/player";
import { computeComponent, processedPropType, wrapRef } from "util/vue"; 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({ export default defineComponent({
components: { Links }, components: { Links },
@ -61,7 +61,11 @@ export default defineComponent({
style: processedPropType<StyleValue>(String, Object, Array), style: processedPropType<StyleValue>(String, Object, Array),
classes: processedPropType<Record<string, boolean>>(Object), classes: processedPropType<Record<string, boolean>>(Object),
links: processedPropType<Link[]>(Array), 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) { setup(props) {
const { display, index, minimized, minWidth, tab } = toRefs(props); const { display, index, minimized, minWidth, tab } = toRefs(props);
@ -80,6 +84,16 @@ export default defineComponent({
updateTab(minimized, minWidth) 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) { function updateTab(minimized: boolean, minWidth: number) {
const tabValue = tab.value(); const tabValue = tab.value();
if (tabValue != undefined) { if (tabValue != undefined) {
@ -102,6 +116,7 @@ export default defineComponent({
return { return {
component, component,
showGoBack, showGoBack,
linksRef,
unref, unref,
goBack goBack
}; };

View file

@ -17,10 +17,9 @@
<slot name="header" :shown="isOpen"> default header </slot> <slot name="header" :shown="isOpen"> default header </slot>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<Links v-if="links" :links="links"> <Links :links="links" ref="linksRef">
<slot name="body" :shown="isOpen"> default body </slot> <slot name="body" :shown="isOpen"> default body </slot>
</Links> </Links>
<slot name="body" v-else :shown="isOpen"> default body </slot>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<slot name="footer" :shown="isOpen"> <slot name="footer" :shown="isOpen">
@ -40,7 +39,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Link } from "features/links"; import { Link, LinkNode } from "features/links";
import { computed, ref, toRefs } from "vue"; import { computed, ref, toRefs } from "vue";
import Links from "./links/Links.vue"; import Links from "./links/Links.vue";
@ -60,7 +59,12 @@ function close() {
const isAnimating = ref(false); 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> </script>
<style> <style>

View file

@ -16,6 +16,7 @@
import { import {
Link, Link,
LinkNode, LinkNode,
NodesInjectionKey,
RegisterLinkNodeInjectionKey, RegisterLinkNodeInjectionKey,
UnregisterLinkNodeInjectionKey UnregisterLinkNodeInjectionKey
} from "features/links"; } from "features/links";
@ -30,6 +31,8 @@ const resizeObserver = new ResizeObserver(updateNodes);
const nodes = ref<Record<string, LinkNode | undefined>>({}); const nodes = ref<Record<string, LinkNode | undefined>>({});
defineExpose({ nodes });
const resizeListener = ref<Element | null>(null); const resizeListener = ref<Element | null>(null);
onMounted(() => { onMounted(() => {
@ -71,6 +74,7 @@ provide(RegisterLinkNodeInjectionKey, (id, element) => {
provide(UnregisterLinkNodeInjectionKey, id => { provide(UnregisterLinkNodeInjectionKey, id => {
nodes.value[id] = undefined; nodes.value[id] = undefined;
}); });
provide(NodesInjectionKey, nodes);
let isDirty = true; let isDirty = true;
let boundingRect = resizeListener.value?.getBoundingClientRect(); let boundingRect = resizeListener.value?.getBoundingClientRect();
@ -90,10 +94,11 @@ function updateNode(id: string) {
if (!node || boundingRect == null) { if (!node || boundingRect == null) {
return; return;
} }
const linkStartRect = node.element.getBoundingClientRect(); const nodeRect = node.element.getBoundingClientRect();
node.x = linkStartRect.x + linkStartRect.width / 2 - boundingRect.x; node.y = nodeRect.x + nodeRect.width / 2 - boundingRect.x;
node.y = linkStartRect.y + linkStartRect.height / 2 - boundingRect.y; node.x = nodeRect.y + nodeRect.height / 2 - boundingRect.y;
node.rect = nodeRect;
} }
</script> </script>

View file

@ -1,9 +1,10 @@
import { Position } from "game/layers"; import { Position } from "game/layers";
import { InjectionKey, SVGAttributes } from "vue"; import { InjectionKey, Ref, SVGAttributes } from "vue";
export interface LinkNode { export interface LinkNode {
x?: number; x?: number;
y?: number; y?: number;
rect?: DOMRect;
element: HTMLElement; element: HTMLElement;
} }
@ -19,3 +20,5 @@ export const RegisterLinkNodeInjectionKey: InjectionKey<
> = Symbol("RegisterLinkNode"); > = Symbol("RegisterLinkNode");
export const UnregisterLinkNodeInjectionKey: InjectionKey<(id: string) => void> = export const UnregisterLinkNodeInjectionKey: InjectionKey<(id: string) => void> =
Symbol("UnregisterLinkNode"); Symbol("UnregisterLinkNode");
export const NodesInjectionKey: InjectionKey<Ref<Record<string, LinkNode | undefined>>> =
Symbol("Nodes");

View file

@ -2,7 +2,6 @@ import ParticlesComponent from "./Particles.vue";
import { IEmitter } from "tsparticles-plugin-emitters/Options/Interfaces/IEmitter"; import { IEmitter } from "tsparticles-plugin-emitters/Options/Interfaces/IEmitter";
import { EmitterInstance } from "tsparticles-plugin-emitters/EmitterInstance"; import { EmitterInstance } from "tsparticles-plugin-emitters/EmitterInstance";
import { EmitterContainer } from "tsparticles-plugin-emitters/EmitterContainer"; import { EmitterContainer } from "tsparticles-plugin-emitters/EmitterContainer";
import { ICoordinates } from "tsparticles-engine";
import { Ref, shallowRef } from "vue"; import { Ref, shallowRef } from "vue";
import { registerGameComponent } from "game/settings"; import { registerGameComponent } from "game/settings";
import { jsx } from "features/feature"; import { jsx } from "features/feature";
@ -14,18 +13,17 @@ const containerRef: Ref<null | EmitterContainer> = shallowRef(null);
let emittersToAdd: { let emittersToAdd: {
resolve: (value: EmitterInstance | PromiseLike<EmitterInstance>) => void; resolve: (value: EmitterInstance | PromiseLike<EmitterInstance>) => void;
options: IEmitter & { particles: Required<IEmitter>["particles"] }; options: IEmitter & { particles: Required<IEmitter>["particles"] };
position: ICoordinates;
}[] = []; }[] = [];
export function addEmitter( export function addEmitter(
options: IEmitter & { particles: Required<IEmitter>["particles"] }, options: IEmitter & { particles: Required<IEmitter>["particles"] }
position: ICoordinates
): Promise<EmitterInstance> { ): Promise<EmitterInstance> {
if (containerRef.value) { 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 => { 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) { function onInit(container: EmitterContainer) {
containerRef.value = container; containerRef.value = container;
emittersToAdd.forEach(({ resolve, options, position }) => emittersToAdd.forEach(({ resolve, options }) => resolve(container.addEmitter(options)));
resolve(container.addEmitter(options, position))
);
emittersToAdd = []; emittersToAdd = [];
} }

View file

@ -7,7 +7,7 @@ import {
setDefault, setDefault,
StyleValue StyleValue
} from "features/feature"; } from "features/feature";
import { Link } from "features/links"; import { Link, LinkNode } from "features/links";
import { import {
Computable, Computable,
GetComputableType, GetComputableType,
@ -17,7 +17,7 @@ import {
} from "util/computed"; } from "util/computed";
import { createLazyProxy } from "util/proxies"; import { createLazyProxy } from "util/proxies";
import { createNanoEvents, Emitter } from "nanoevents"; import { createNanoEvents, Emitter } from "nanoevents";
import { ref, unref } from "vue"; import { Ref, ref, unref } from "vue";
import { globalBus } from "./events"; import { globalBus } from "./events";
import { persistent, PersistentRef } from "./persistence"; import { persistent, PersistentRef } from "./persistence";
import player from "./player"; import player from "./player";
@ -63,6 +63,7 @@ export interface BaseLayer {
emitter: Emitter<LayerEvents>; emitter: Emitter<LayerEvents>;
on: OmitThisParameter<Emitter<LayerEvents>["on"]>; on: OmitThisParameter<Emitter<LayerEvents>["on"]>;
emit: <K extends keyof LayerEvents>(event: K, ...args: Parameters<LayerEvents[K]>) => void; 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< export type Layer<T extends LayerOptions> = Replace<
@ -97,6 +98,7 @@ export function createLayer<T extends LayerOptions>(
const emitter = (layer.emitter = createNanoEvents<LayerEvents>()); const emitter = (layer.emitter = createNanoEvents<LayerEvents>());
layer.on = emitter.on.bind(emitter); layer.on = emitter.on.bind(emitter);
layer.emit = emitter.emit.bind(emitter); layer.emit = emitter.emit.bind(emitter);
layer.nodes = ref({});
layer.minimized = persistent(false); layer.minimized = persistent(false);