@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
74 lines (66 loc) • 2.7 kB
text/typescript
import type { Object3D } from "three";
import type { IComponent } from "./engine_types.js";
/** Typed event map for {@link Context.events}.
* Known events get full autocomplete; custom events can be typed at the call site via generic parameter.
*/
export interface ContextEventMap {
"scene-content-changed": {
/** The component that triggered the change (e.g. SceneSwitcher, DropListener) */
readonly source: IComponent;
/** The root object that was added/loaded */
readonly object: Object3D;
};
}
/** Options for {@link EventBus.on}. */
export interface EventBusListenerOptions {
/** If true the listener is automatically removed after the first invocation. */
once?: boolean;
}
/** Typed event bus. Known {@link ContextEventMap} events get full autocomplete.
* Custom events can be typed at the call site via generic parameter.
* @example Known events
* ```ts
* context.events.on("scene-content-changed", e => e.object);
* ```
* @example Custom events — type at call site
* ```ts
* context.events.emit<{ pts: number }>("scored", { pts: 10 });
* context.events.on<{ pts: number }>("scored", e => e.pts);
* ```
* @example Once
* ```ts
* context.events.on("scene-content-changed", e => { ... }, { once: true });
* ```
*/
export class EventBus {
private _listeners = new Map<string, Function[]>();
/** Emit a known {@link ContextEventMap} event */
emit<K extends keyof ContextEventMap & string>(type: K, detail?: ContextEventMap[K]): void;
/** Emit a custom event with user-provided type */
emit<T>(type: string, detail?: T): void;
emit(type: string, detail?: unknown): void {
const arr = this._listeners.get(type);
if (arr) for (const cb of [...arr]) cb(detail);
}
/** Subscribe to a known {@link ContextEventMap} event. Returns an unsubscribe function. */
on<K extends keyof ContextEventMap & string>(type: K, callback: (args: ContextEventMap[K]) => void, options?: EventBusListenerOptions): () => void;
/** Subscribe to a custom event with user-provided type. Returns an unsubscribe function. */
on<T>(type: string, callback: (args: T) => void, options?: EventBusListenerOptions): () => void;
on(type: string, callback: Function, options?: EventBusListenerOptions): () => void {
let arr = this._listeners.get(type);
if (!arr) { arr = []; this._listeners.set(type, arr); }
const unsub = () => {
const i = arr.indexOf(wrapped);
if (i >= 0) arr.splice(i, 1);
};
const wrapped = options?.once
? (...args: unknown[]) => { unsub(); callback(...args); }
: callback;
arr.push(wrapped);
return unsub;
}
/** Remove all listeners. Called when the context is cleared or destroyed. */
clear(): void {
this._listeners.clear();
}
}