UNPKG

@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
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 {};