import Modal from "components/Modal.vue"; import { CoercableComponent, jsx, JSXFunction, Replace, setDefault, StyleValue } from "features/feature"; import { Link, LinkNode } from "features/links"; import { Computable, GetComputableType, GetComputableTypeWithDefault, processComputable, ProcessedComputable } from "util/computed"; import { createLazyProxy } from "util/proxies"; import { createNanoEvents, Emitter } from "nanoevents"; import { Ref, ref, unref } from "vue"; import { globalBus } from "./events"; import { persistent, PersistentRef } from "./persistence"; import player from "./player"; export interface LayerEvents { // Generation preUpdate: (diff: number) => void; // Actions (e.g. automation) update: (diff: number) => void; // Effects (e.g. milestones) postUpdate: (diff: number) => void; } export const layers: Record | undefined> = {}; window.layers = layers; declare module "@vue/runtime-dom" { interface CSSProperties { "--layer-color"?: string; } } export interface Position { x: number; y: number; } export interface LayerOptions { id: string; color?: Computable; display: Computable; classes?: Computable>; style?: Computable; name?: Computable; minimizable?: Computable; forceHideGoBack?: Computable; minWidth?: Computable; links?: Computable; } export interface BaseLayer { minimized: PersistentRef; emitter: Emitter; on: OmitThisParameter["on"]>; emit: (event: K, ...args: Parameters) => void; nodes: Ref>; } export type Layer = Replace< T & BaseLayer, { color: GetComputableType; display: GetComputableType; classes: GetComputableType; style: GetComputableType; name: GetComputableTypeWithDefault; minWidth: GetComputableTypeWithDefault; minimizable: GetComputableTypeWithDefault; forceHideGoBack: GetComputableType; links: GetComputableType; } >; export type GenericLayer = Replace< Layer, { name: ProcessedComputable; minWidth: ProcessedComputable; minimizable: ProcessedComputable; } >; export function createLayer( optionsFunc: (() => T) & ThisType ): Layer { return createLazyProxy(() => { const layer = {} as T & Partial; const emitter = (layer.emitter = createNanoEvents()); layer.on = emitter.on.bind(emitter); layer.emit = emitter.emit.bind(emitter); layer.nodes = ref({}); layer.minimized = persistent(false); Object.assign(layer, optionsFunc.call(layer)); processComputable(layer as T, "color"); processComputable(layer as T, "display"); processComputable(layer as T, "name"); setDefault(layer, "name", layer.id); processComputable(layer as T, "minWidth"); setDefault(layer, "minWidth", 600); processComputable(layer as T, "minimizable"); setDefault(layer, "minimizable", true); processComputable(layer as T, "links"); return layer as unknown as Layer; }); } export function addLayer( layer: GenericLayer, player: { layers?: Record> } ): void { console.info("Adding layer", layer.id); if (layers[layer.id]) { console.error( "Attempted to add layer with same ID as existing layer", layer.id, layers[layer.id] ); return; } setDefault(player, "layers", {}); if (player.layers[layer.id] == null) { player.layers[layer.id] = {}; } layers[layer.id] = layer; globalBus.emit("addLayer", layer, player.layers[layer.id]); } export function getLayer(layerID: string): T { return layers[layerID] as T; } export function removeLayer(layer: GenericLayer): void { console.info("Removing layer", layer.id); globalBus.emit("removeLayer", layer); layers[layer.id] = undefined; } export function reloadLayer(layer: GenericLayer): void { removeLayer(layer); // Re-create layer addLayer(layer, player); } export function setupLayerModal(layer: GenericLayer): { openModal: VoidFunction; modal: JSXFunction; } { const showModal = ref(false); return { openModal: () => (showModal.value = true), modal: jsx(() => ( (showModal.value = value)} v-slots={{ header: () =>

{unref(layer.name)}

, body: unref(layer.display) }} /> )) }; } globalBus.on("update", function updateLayers(diff) { Object.values(layers).forEach(layer => { layer?.emit("preUpdate", diff); }); Object.values(layers).forEach(layer => { layer?.emit("update", diff); }); Object.values(layers).forEach(layer => { layer?.emit("postUpdate", diff); }); });