diff --git a/src/game/layers.tsx b/src/game/layers.tsx index 351d1fc..e8d22c3 100644 --- a/src/game/layers.tsx +++ b/src/game/layers.tsx @@ -24,33 +24,51 @@ import { createLazyProxy } from "util/proxies"; import type { InjectionKey, Ref } from "vue"; import { ref, shallowReactive, unref } from "vue"; +/** A feature's node in the DOM that has its size tracked. */ export interface FeatureNode { rect: DOMRect; observer: MutationObserver; element: HTMLElement; } +/** + * An injection key that a {@link Context} will use to provide a function that registers a {@link FeatureNode} with the given id and HTML element. + */ export const RegisterNodeInjectionKey: InjectionKey<(id: string, element: HTMLElement) => void> = - Symbol("RegisterNode"); + Symbol("RegisterNode"); +/** + * An injection key that a {@link Context} will use to provide a function that unregisters a {@link FeatureNode} with the given id. + */ export const UnregisterNodeInjectionKey: InjectionKey<(id: string) => void> = Symbol("UnregisterNode"); +/** + * An injection key that a {@link Context} will use to provide a ref to a map of all currently registered {@link FeatureNode}s. + */ export const NodesInjectionKey: InjectionKey>> = Symbol("Nodes"); +/** + * An injection key that a {@link Context} will use to provide a ref to a bounding rect of the Context. + */ export const BoundsInjectionKey: InjectionKey> = Symbol("Bounds"); +/** All types of events able to be sent or emitted from a layer's emitter. */ export interface LayerEvents { - // Generation + /** Sent every game tick, before the update event. Intended for "generation" type actions. */ preUpdate: (diff: number) => void; - // Actions (e.g. automation) + /** Sent every game tick. Intended for "automation" type actions. */ update: (diff: number) => void; - // Effects (e.g. milestones) + /** Sent every game tick, after the update event. Intended for checking state. */ postUpdate: (diff: number) => void; } +/** + * A reference to all the current layers. + * It is shallow reactive so it will update when layers are added or removed, but not interfere with the existing refs within each layer. + */ export const layers: Record | undefined> = shallowReactive({}); declare global { - /** Augment the window object so the layers can be accessed from the console */ + /** Augment the window object so the layers can be accessed from the console. */ interface Window { layers: Record | undefined>; } @@ -58,36 +76,81 @@ declare global { window.layers = layers; declare module "@vue/runtime-dom" { + /** Augment CSS Properties to allow for setting the layer color CSS variable. */ interface CSSProperties { "--layer-color"?: string; } } +/** An object representing the position of some entity. */ export interface Position { + /** The X component of the entity's position. */ x: number; + /** The Y component of the entity's position. */ y: number; } +/** + * An object that configures a {@link Layer}. + * Even moreso than features, the developer is expected to include extra properties in this object. + * All {@link persistent} refs must be included somewhere within the layer object. + */ export interface LayerOptions { + /** The color of the layer, used to theme the entire layer's display. */ color?: Computable; + /** + * The layout of this layer's features. + * When the layer is open in {@link player.tabs}, this is the content that is display. + */ display: Computable; + /** An object of classes that should be applied to the display. */ classes?: Computable>; + /** Styles that should be applied to the display. */ style?: Computable; + /** + * The name of the layer, used on minimized tabs. + * Defaults to {@link id}. + */ name?: Computable; + /** + * Whether or not the layer can be minimized. + * Defaults to true. + */ minimizable?: Computable; + /** + * Whether or not to force the go back button to be hidden. + * If true, go back will be hidden regardless of {@link projInfo.allowGoBack}. + */ forceHideGoBack?: Computable; + /** + * A CSS min-width value that is applied to the layer. + * Can be a number, in which case the unit is assumed to be px. + * Defaults to 600px. + */ minWidth?: Computable; } +/** The properties that are added onto a processed {@link LayerOptions} to create a {@link Layer} */ export interface BaseLayer { + /** + * The ID of the layer. + * Populated from the {@link createLayer} parameters. + * Used for saving and tracking open tabs. + */ id: string; + /** A persistent ref tracking if the tab is minimized or not. */ minimized: Persistent; + /** An emitter for sending {@link LayerEvents} events for this layer. */ emitter: Emitter; + /** A function to register an event listener on {@link emitter}. */ on: OmitThisParameter["on"]>; - emit: (event: K, ...args: Parameters) => void; + /** A function to emit a {@link LayerEvents} event to this layer. */ + emit: (...args: [K, ...Parameters]) => void; + /** A map of {@link FeatureNode}s present in this layer's {@link Context} component. */ nodes: Ref>; } +/** An unit of game content. Displayed to the user as a tab or modal. */ export type Layer = Replace< T & BaseLayer, { @@ -102,6 +165,7 @@ export type Layer = Replace< } >; +/** A type that matches any valid {@link Layer} object. */ export type GenericLayer = Replace< Layer, { @@ -111,8 +175,19 @@ export type GenericLayer = Replace< } >; +/** + * When creating layers, this object a map of layer ID to a set of any created persistent refs in order to check they're all included in the final layer object. + */ export const persistentRefs: Record> = {}; +/** + * When creating layers, this array stores the layers currently being created, as a stack. + */ export const addingLayers: string[] = []; +/** + * Lazily creates a layer with the given options. + * @param id The ID this layer will have. See {@link BaseLayer.id}. + * @param optionsFunc Layer options. + */ export function createLayer( id: string, optionsFunc: OptionsFunc @@ -150,6 +225,14 @@ export function createLayer( }); } +/** + * Enables a layer object, so it will be updated every tick. + * Note that accessing a layer/its properties does NOT require it to be enabled. + * For dynamic layers you can call this function and {@link removeLayer} as necessary. Just make sure {@link projEntry.getInitialLayers} will provide an accurate list of layers based on the player data object. + * For static layers just make {@link projEntry.getInitialLayers} return all the layers. + * @param layer The layer to add. + * @param player The player data object, which will have a data object for this layer. + */ export function addLayer( layer: GenericLayer, player: { layers?: Record> } @@ -173,10 +256,19 @@ export function addLayer( globalBus.emit("addLayer", layer, player.layers[layer.id]); } +/** + * Convenience method for getting a layer by its ID with correct typing. + * @param layerID The ID of the layer to get. + */ export function getLayer(layerID: string): T { return layers[layerID] as T; } +/** + * Disables a layer, so it will no longer be updated every tick. + * Note that accessing a layer/its properties does NOT require it to be enabled. + * @param layer The layer to remove. + */ export function removeLayer(layer: GenericLayer): void { console.info("Removing layer", layer.id); globalBus.emit("removeLayer", layer); @@ -184,6 +276,11 @@ export function removeLayer(layer: GenericLayer): void { layers[layer.id] = undefined; } +/** + * Convenience method for removing and immediately re-adding a layer. + * This is useful for layers with dynamic content, to ensure persistent refs are correctly configured. + * @param layer Layer to remove and then re-add + */ export function reloadLayer(layer: GenericLayer): void { removeLayer(layer); @@ -191,6 +288,11 @@ export function reloadLayer(layer: GenericLayer): void { addLayer(layer, player); } +/** + * Utility function for creating a modal that display's a {@link layer.display}. + * Returns the modal itself, which can be rendered anywhere you need, as well as a function to open the modal. + * @param layer The layer to display in the modal. + */ export function setupLayerModal(layer: GenericLayer): { openModal: VoidFunction; modal: JSXFunction;