@ue-too/board
Version:
<h1 align="center"> uē-tôo </h1> <p align="center"> pan, zoom, rotate, and more with your html canvas. </p>
316 lines (315 loc) • 13.1 kB
TypeScript
import { EventReactions, EventGuards, Guard, TemplateState, TemplateStateMachine, EventArgs, EventResult, CreateStateType } from "@ue-too/being";
import type { Point } from "@ue-too/math";
import { Canvas, CursorStyle, KmtInputContext } from "./kmt-input-context";
declare const KMT_INPUT_STATES: readonly ["IDLE", "READY_TO_PAN_VIA_SPACEBAR", "READY_TO_PAN_VIA_SCROLL_WHEEL", "PAN", "INITIAL_PAN", "PAN_VIA_SCROLL_WHEEL", "DISABLED"];
/**
* Possible states of the Keyboard/Mouse/Trackpad input state machine.
*
* @remarks
* State transitions:
* - **IDLE**: Default state, waiting for user input
* - **READY_TO_PAN_VIA_SPACEBAR**: Spacebar pressed, ready to pan with left-click drag
* - **INITIAL_PAN**: First frame of pan via spacebar (detects accidental clicks)
* - **PAN**: Active panning via spacebar + left-click drag
* - **READY_TO_PAN_VIA_SCROLL_WHEEL**: Middle mouse button pressed, ready to pan
* - **PAN_VIA_SCROLL_WHEEL**: Active panning via middle-click drag
* - **DISABLED**: Input temporarily disabled (e.g., during UI interactions)
*
* @category Input State Machine - KMT
*/
export type KmtInputStates = CreateStateType<typeof KMT_INPUT_STATES>;
/**
* Payload for pointer events (mouse button press/release/move).
*
* @property x - X coordinate in window space
* @property y - Y coordinate in window space
*
* @category Input State Machine - KMT
*/
export type PointerEventPayload = {
x: number;
y: number;
};
/**
* @internal
*/
type EmptyPayload = {};
/**
* Payload for scroll wheel events.
*
* @property deltaX - Horizontal scroll delta
* @property deltaY - Vertical scroll delta
*
* @category Input State Machine - KMT
*/
export type ScrollEventPayload = {
deltaX: number;
deltaY: number;
};
/**
* Payload for scroll events combined with ctrl key (zoom gesture).
*
* @property deltaX - Horizontal scroll delta
* @property deltaY - Vertical scroll delta
* @property x - Cursor X coordinate in window space (zoom anchor point)
* @property y - Cursor Y coordinate in window space (zoom anchor point)
*
* @category Input State Machine - KMT
*/
export type ScrollWithCtrlEventPayload = {
deltaX: number;
deltaY: number;
x: number;
y: number;
};
/**
* Event mapping for the KMT input state machine.
*
* @remarks
* Maps event names to their payload types. Used by the state machine framework
* to provide type-safe event handling.
*
* Key events:
* - **leftPointerDown/Up/Move**: Left mouse button interactions
* - **middlePointerDown/Up/Move**: Middle mouse button interactions (pan)
* - **spacebarDown/Up**: Spacebar for pan mode
* - **scroll**: Regular scroll (pan or zoom depending on device)
* - **scrollWithCtrl**: Ctrl + scroll (always zoom)
* - **disable/enable**: Temporarily disable/enable input processing
*
* @category Input State Machine - KMT
*/
export type KmtInputEventMapping = {
leftPointerDown: PointerEventPayload;
leftPointerUp: PointerEventPayload;
leftPointerMove: PointerEventPayload;
spacebarDown: EmptyPayload;
spacebarUp: EmptyPayload;
stayIdle: EmptyPayload;
cursorOnElement: EmptyPayload;
scroll: ScrollWithCtrlEventPayload;
scrollWithCtrl: ScrollWithCtrlEventPayload;
middlePointerDown: PointerEventPayload;
middlePointerUp: PointerEventPayload;
middlePointerMove: PointerEventPayload;
disable: EmptyPayload;
enable: EmptyPayload;
pointerMove: PointerEventPayload;
};
/**
* Output events produced by the KMT state machine for the orchestrator.
*
* @remarks
* These high-level gesture events are the result of recognizing patterns in raw DOM events.
* The orchestrator receives these events and coordinates camera control and observer notification.
*
* **Event Types**:
* - **pan**: Camera translation with delta in viewport coordinates
* - **zoom**: Camera scale change with anchor point in viewport coordinates
* - **rotate**: Camera rotation change (currently unused in KMT)
* - **cursor**: Cursor style change request (handled by state uponEnter/beforeExit)
* - **none**: No action required
*
* **Coordinate Spaces**:
* - Pan delta is in viewport pixels
* - Zoom anchor point is in viewport coordinates (origin at viewport center)
*
* @category Input State Machine - KMT
*/
export type KmtOutputEvent = {
type: "pan";
delta: Point;
} | {
type: "zoom";
delta: number;
anchorPointInViewPort: Point;
} | {
type: "rotate";
deltaRotation: number;
} | {
type: "cursor";
style: CursorStyle;
} | {
type: "none";
};
/**
* Mapping of events to their output types.
*
* @remarks
* Defines which events produce outputs. Not all events produce outputs - some only
* cause state transitions. This mapping is used by the state machine framework for
* type-safe output handling.
*
* @category Input State Machine - KMT
*/
export type KmtInputEventOutputMapping = {
spacebarDown: number;
middlePointerMove: KmtOutputEvent;
scroll: KmtOutputEvent;
scrollWithCtrl: KmtOutputEvent;
leftPointerMove: KmtOutputEvent;
};
/**
* @internal
*/
export type KmtIdleStatePossibleTargetStates = "IDLE" | "READY_TO_PAN_VIA_SPACEBAR" | "READY_TO_PAN_VIA_SCROLL_WHEEL" | "DISABLED";
/**
* IDLE state - default state waiting for user input.
*
* @remarks
* This is the default state of the KMT input state machine. It handles scroll events
* for panning and zooming, and transitions to pan-ready states when the user presses
* spacebar or middle-click.
*
* **Responsibilities**:
* - Process scroll events (pan or zoom depending on device and modifiers)
* - Detect spacebar press to enter pan mode
* - Detect middle-click to enter pan mode
* - Distinguish between mouse and trackpad input modalities
*
* **Scroll Behavior**:
* - Ctrl + Scroll: Always zoom (both mouse and trackpad)
* - Scroll (no Ctrl): Pan (trackpad) or Zoom (mouse, determined by modality detection)
*
* **Input Modality Detection**:
* The state tracks horizontal scroll deltas to distinguish trackpads (which produce deltaX)
* from mice (which typically only produce deltaY). This affects zoom behavior.
*
* @category Input State Machine - KMT
*/
export declare class KmtIdleState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {
constructor();
protected _guards: Guard<KmtInputContext, "isIdle">;
protected _eventGuards: Partial<EventGuards<KmtInputEventMapping, KmtInputStates, KmtInputContext, Guard<KmtInputContext>>>;
scrollPan: (context: KmtInputContext, payload: ScrollEventPayload) => KmtOutputEvent;
scrollZoom: (context: KmtInputContext, payload: ScrollWithCtrlEventPayload) => KmtOutputEvent;
scrollHandler: (context: KmtInputContext, payload: ScrollWithCtrlEventPayload) => KmtOutputEvent;
scrollWithCtrlHandler: (context: KmtInputContext, payload: ScrollWithCtrlEventPayload) => KmtOutputEvent;
protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping>;
uponEnter(context: KmtInputContext): void;
spacebarDownHandler(context: KmtInputContext, payload: EmptyPayload): number;
middlePointerDownHandler(context: KmtInputContext, payload: PointerEventPayload): void;
}
export declare class DisabledState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {
constructor();
uponEnter(context: KmtInputContext): void;
beforeExit(context: KmtInputContext): void;
protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping>;
}
/**
* @description The ready to pan via space bar state of the keyboard mouse and trackpad input state machine.
*
* @category Input State Machine
*/
export declare class ReadyToPanViaSpaceBarState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {
constructor();
protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping>;
uponEnter(context: KmtInputContext): void;
leftPointerDownHandler(context: KmtInputContext, payload: PointerEventPayload): void;
}
/**
* @description The initial pan state of the keyboard mouse and trackpad input state machine.
*
* @category Input State Machine
*/
export declare class InitialPanState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {
constructor();
protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping>;
uponEnter(context: KmtInputContext): void;
leftPointerMoveHandler(context: KmtInputContext, payload: PointerEventPayload): KmtOutputEvent;
}
/**
* @description The ready to pan via scroll wheel state of the keyboard mouse and trackpad input state machine.
*
* @category Input State Machine
*/
export declare class ReadyToPanViaScrollWheelState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {
constructor();
protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping>;
uponEnter(context: KmtInputContext): void;
}
/**
* @description The pan state of the keyboard mouse and trackpad input state machine.
*
* @category Input State Machine
*/
export declare class PanState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {
constructor();
protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping>;
uponEnter(context: KmtInputContext): void;
beforeExit(context: KmtInputContext): void;
leftPointerMoveHandler(context: KmtInputContext, payload: PointerEventPayload): KmtOutputEvent;
}
/**
* @description The pan via scroll wheel state of the keyboard mouse and trackpad input state machine.
*
* @category Input State Machine
*/
export declare class PanViaScrollWheelState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {
protected _eventReactions: EventReactions<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping>;
middlePointerMoveHandler(context: KmtInputContext, payload: PointerEventPayload): KmtOutputEvent;
uponEnter(context: KmtInputContext): void;
}
export declare class KmtEmptyState extends TemplateState<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {
constructor();
}
/**
* Type alias for the KMT input state machine.
*
* @category Input State Machine - KMT
*/
export type KmtInputStateMachine = TemplateStateMachine<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping>;
/**
* Creates a new KMT (Keyboard/Mouse/Trackpad) input state machine.
*
* @param context - The context providing state and canvas access for the state machine
* @returns A configured state machine ready to process KMT input events
*
* @remarks
* This factory function creates a fully configured state machine with all KMT gesture
* recognition states. The state machine processes raw input events and produces
* high-level gesture outputs (pan, zoom, rotate).
*
* **State Flow**:
* ```
* IDLE → (spacebar) → READY_TO_PAN_VIA_SPACEBAR → (click) → INITIAL_PAN → PAN
* IDLE → (middle-click) → READY_TO_PAN_VIA_SCROLL_WHEEL → PAN_VIA_SCROLL_WHEEL
* IDLE → (scroll) → [produces pan or zoom output, stays in IDLE]
* ```
*
* **Gesture Recognition**:
* - **Pan via spacebar**: Spacebar + left-click drag
* - **Pan via middle-click**: Middle-click drag
* - **Zoom**: Ctrl + scroll (mouse) or scroll (trackpad, auto-detected)
* - **Pan via scroll**: Scroll (trackpad) or scroll without Ctrl (varies by device)
*
* @category Input State Machine - KMT
*
* @example
* ```typescript
* const canvasProxy = new CanvasProxy(canvasElement);
* const context = new ObservableInputTracker(canvasProxy);
* const stateMachine = createKmtInputStateMachine(context);
*
* // Process an event
* const result = stateMachine.happens("scroll", {
* deltaX: 0,
* deltaY: 10,
* x: 500,
* y: 300
* });
*
* // Check for output
* if (result.output) {
* console.log("Gesture recognized:", result.output.type);
* }
* ```
*/
export declare function createKmtInputStateMachine(context: KmtInputContext): KmtInputStateMachine;
export declare function createKmtInputStateMachineWithCanvas(canvas: Canvas): KmtInputStateMachine;
export declare class KmtInputStateMachineWebWorkerProxy extends TemplateStateMachine<KmtInputEventMapping, KmtInputContext, KmtInputStates, KmtInputEventOutputMapping> {
private _webworker;
constructor(webworker: Worker);
happens(...args: EventArgs<KmtInputEventMapping, keyof KmtInputEventMapping | string>): EventResult<KmtInputStates>;
}
export {};