@ue-too/board
Version:
<h1 align="center"> uē-tôo </h1> <p align="center"> pan, zoom, rotate, and more with your html canvas. </p>
227 lines (226 loc) • 7.13 kB
TypeScript
import type { Point } from "@ue-too/math";
/**
* Function to unsubscribe from raw user input events.
*
* @remarks
* Calling this function removes the subscriber from the event stream.
*
* @category Raw Input Publisher
*/
export type UnsubscribeToUserRawInput = () => void;
/**
* Payload for pan input events.
*
* @property diff - The pan delta in viewport pixels
*
* @category Raw Input Publisher
*/
export type RawUserPanInputEventPayload = {
diff: Point;
};
/**
* Pan input event with discriminated type.
*
* @remarks
* The `type` property allows TypeScript discriminated unions to distinguish
* between different event types when subscribing to the "all" event stream.
*
* @category Raw Input Publisher
*/
export type RawUserPanInputEvent = {
type: "pan";
} & RawUserPanInputEventPayload;
/**
* Payload for zoom input events.
*
* @property deltaZoomAmount - The zoom delta (scale change)
* @property anchorPoint - The zoom anchor point in viewport coordinates
*
* @category Raw Input Publisher
*/
export type RawUserZoomInputEventPayload = {
deltaZoomAmount: number;
anchorPoint: Point;
};
/**
* Zoom input event with discriminated type.
*
* @remarks
* The `type` property allows TypeScript discriminated unions to distinguish
* between different event types when subscribing to the "all" event stream.
*
* @category Raw Input Publisher
*/
export type RawUserZoomInputEvent = {
type: "zoom";
} & RawUserZoomInputEventPayload;
/**
* Payload for rotate input events.
*
* @property deltaRotation - The rotation delta in radians
*
* @category Raw Input Publisher
*/
export type RawUserRotateInputEventPayload = {
deltaRotation: number;
};
/**
* Rotate input event with discriminated type.
*
* @remarks
* The `type` property allows TypeScript discriminated unions to distinguish
* between different event types when subscribing to the "all" event stream.
*
* @category Raw Input Publisher
*/
export type RawUserRotateInputEvent = {
type: "rotate";
} & RawUserRotateInputEventPayload;
/**
* Mapping of event names to their payload types.
*
* @remarks
* This type enables type-safe event subscription:
* - Subscribe to specific events ("pan", "zoom", "rotate") to receive only those payloads
* - Subscribe to "all" to receive all events with discriminated type property
*
* @category Raw Input Publisher
*/
export type RawUserInputEventMap = {
"pan": RawUserPanInputEventPayload;
"zoom": RawUserZoomInputEventPayload;
"rotate": RawUserRotateInputEventPayload;
"all": RawUserInputEvent;
};
/**
* Union type of all raw user input events.
*
* @remarks
* Use the `type` discriminator property to determine which event variant you have.
*
* @category Raw Input Publisher
*/
export type RawUserInputEvent = RawUserPanInputEvent | RawUserZoomInputEvent | RawUserRotateInputEvent;
/**
* Callback function type for raw user input events.
*
* @typeParam K - The event name key from RawUserInputEventMap
*
* @category Raw Input Publisher
*/
export type RawUserInputCallback<K extends keyof RawUserInputEventMap> = (event: RawUserInputEventMap[K]) => void;
/**
* Interface for publishing raw user input events to observers.
*
* @remarks
* This interface defines the contract for broadcasting user input events
* to external subscribers. Implementations provide the observable pattern
* for input event distribution.
*
* @category Raw Input Publisher
*/
export interface UserInputPublisher {
/** Notifies subscribers of a pan gesture */
notifyPan(diff: Point): void;
/** Notifies subscribers of a zoom gesture */
notifyZoom(deltaZoomAmount: number, anchorPoint: Point): void;
/** Notifies subscribers of a rotate gesture */
notifyRotate(deltaRotation: number): void;
/** Subscribes to input events */
on<K extends keyof RawUserInputEventMap>(eventName: K, callback: (event: RawUserInputEventMap[K]) => void): UnsubscribeToUserRawInput;
}
/**
* Publisher for broadcasting raw user input events to observers.
*
* @remarks
* This class implements the observable pattern to distribute user input events
* to external subscribers. It operates in parallel to camera control - the
* orchestrator both sends events to this publisher AND controls the camera.
*
* **Architecture**:
* ```
* Orchestrator → Publisher → Observers (UI, analytics, etc.)
* ↓
* CameraMux → CameraRig
* ```
*
* **Event Streams**:
* - **Specific streams**: Subscribe to "pan", "zoom", or "rotate" for typed events
* - **Unified stream**: Subscribe to "all" for all events with type discriminator
*
* **Use Cases**:
* - Update UI elements based on user interactions
* - Log analytics about user gestures
* - Synchronize secondary views or previews
* - Implement custom gesture reactions independent of camera
*
* **Observable Implementation**:
* Uses AsyncObservable for asynchronous event delivery, preventing observers
* from blocking the input processing pipeline.
*
* @category Raw Input Publisher
*
* @example
* ```typescript
* const publisher = new RawUserInputPublisher();
*
* // Subscribe to pan events
* const unsubscribe = publisher.on("pan", (event) => {
* console.log("User panned by:", event.diff);
* updateMinimap(event.diff);
* });
*
* // Subscribe to all events
* publisher.on("all", (event) => {
* switch (event.type) {
* case "pan":
* analytics.log("pan", event.diff);
* break;
* case "zoom":
* analytics.log("zoom", event.deltaZoomAmount, event.anchorPoint);
* break;
* case "rotate":
* analytics.log("rotate", event.deltaRotation);
* break;
* }
* });
*
* // Later, unsubscribe
* unsubscribe();
* ```
*/
export declare class RawUserInputPublisher implements UserInputPublisher {
private pan;
private zoom;
private rotate;
private all;
constructor();
notifyPan(diff: Point): void;
notifyZoom(deltaZoomAmount: number, anchorPoint: Point): void;
notifyRotate(deltaRotation: number): void;
on<K extends keyof RawUserInputEventMap>(eventName: K, callback: (event: RawUserInputEventMap[K]) => void): UnsubscribeToUserRawInput;
}
/**
* Creates a default raw user input publisher.
*
* @returns A new RawUserInputPublisher instance
*
* @remarks
* Factory function for creating a standard publisher. Useful for dependency injection
* and testing scenarios where you want to swap implementations.
*
* @category Raw Input Publisher
*/
export declare function createDefaultRawUserInputPublisher(): RawUserInputPublisher;
export declare class RawUserInputPublisherWithWebWorkerRelay implements UserInputPublisher {
private pan;
private zoom;
private rotate;
private all;
private webWorker;
constructor(webWorker: Worker);
notifyPan(diff: Point): void;
notifyZoom(deltaZoomAmount: number, anchorPoint: Point): void;
notifyRotate(deltaRotation: number): void;
on<K extends keyof RawUserInputEventMap>(eventName: K, callback: (event: RawUserInputEventMap[K]) => void): UnsubscribeToUserRawInput;
}