forked from profectus/Profectus
Documented game/layers.tsx
This commit is contained in:
parent
0b8210c18d
commit
21e739070d
1 changed files with 108 additions and 6 deletions
|
@ -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<Ref<Record<string, FeatureNode | undefined>>> =
|
||||
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<Ref<DOMRect | undefined>> = 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<string, Readonly<GenericLayer> | 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<string, Readonly<GenericLayer> | 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<string>;
|
||||
/**
|
||||
* 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<CoercableComponent>;
|
||||
/** An object of classes that should be applied to the display. */
|
||||
classes?: Computable<Record<string, boolean>>;
|
||||
/** Styles that should be applied to the display. */
|
||||
style?: Computable<StyleValue>;
|
||||
/**
|
||||
* The name of the layer, used on minimized tabs.
|
||||
* Defaults to {@link id}.
|
||||
*/
|
||||
name?: Computable<string>;
|
||||
/**
|
||||
* Whether or not the layer can be minimized.
|
||||
* Defaults to true.
|
||||
*/
|
||||
minimizable?: Computable<boolean>;
|
||||
/**
|
||||
* 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<boolean>;
|
||||
/**
|
||||
* 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<number | string>;
|
||||
}
|
||||
|
||||
/** 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<boolean>;
|
||||
/** An emitter for sending {@link LayerEvents} events for this layer. */
|
||||
emitter: Emitter<LayerEvents>;
|
||||
/** A function to register an event listener on {@link emitter}. */
|
||||
on: OmitThisParameter<Emitter<LayerEvents>["on"]>;
|
||||
emit: <K extends keyof LayerEvents>(event: K, ...args: Parameters<LayerEvents[K]>) => void;
|
||||
/** A function to emit a {@link LayerEvents} event to this layer. */
|
||||
emit: <K extends keyof LayerEvents>(...args: [K, ...Parameters<LayerEvents[K]>]) => void;
|
||||
/** A map of {@link FeatureNode}s present in this layer's {@link Context} component. */
|
||||
nodes: Ref<Record<string, FeatureNode | undefined>>;
|
||||
}
|
||||
|
||||
/** An unit of game content. Displayed to the user as a tab or modal. */
|
||||
export type Layer<T extends LayerOptions> = Replace<
|
||||
T & BaseLayer,
|
||||
{
|
||||
|
@ -102,6 +165,7 @@ export type Layer<T extends LayerOptions> = Replace<
|
|||
}
|
||||
>;
|
||||
|
||||
/** A type that matches any valid {@link Layer} object. */
|
||||
export type GenericLayer = Replace<
|
||||
Layer<LayerOptions>,
|
||||
{
|
||||
|
@ -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<string, Set<Persistent>> = {};
|
||||
/**
|
||||
* 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<T extends LayerOptions>(
|
||||
id: string,
|
||||
optionsFunc: OptionsFunc<T, BaseLayer>
|
||||
|
@ -150,6 +225,14 @@ export function createLayer<T extends LayerOptions>(
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<string, Record<string, unknown>> }
|
||||
|
@ -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<T extends GenericLayer>(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;
|
||||
|
|
Loading…
Reference in a new issue