ue-too
Version:
pan, zoom, and rotate your html canvas
199 lines (198 loc) • 10.8 kB
TypeScript
export interface BaseContext {
setup(): void;
cleanup(): void;
}
type NOOP = () => void;
export declare const NO_OP: NOOP;
/**
* @description This is the interface for the state machine. The interface takes in a few generic parameters.
*
* Generic parameters:
* - EventPayloadMapping: A mapping of events to their payloads.
* - Context: The context of the state machine. (which can be used by each state to do calculations that would persist across states)
* - States: All of the possible states that the state machine can be in. e.g. a string literal union like "IDLE" | "SELECTING" | "PAN" | "ZOOM"
*
* You can probably get by using the TemplateStateMachine class.
* The naming is that an event would "happen" and the state of the state machine would "handle" it.
*
* @see {@link TemplateStateMachine}
* @see {@link KmtInputStateMachine}
*
* @category being
*/
export interface StateMachine<EventPayloadMapping, Context extends BaseContext, States extends string = 'IDLE'> {
switchTo(state: States): void;
happens<K extends keyof EventPayloadMapping>(event: K, payload: EventPayloadMapping[K]): States | undefined;
setContext(context: Context): void;
states: Record<States, State<EventPayloadMapping, Context, string extends States ? string : States>>;
onStateChange(callback: StateChangeCallback<States>): void;
possibleStates: States[];
onHappens(callback: (event: keyof EventPayloadMapping, payload: EventPayloadMapping[keyof EventPayloadMapping], context: Context) => void): void;
}
/**
* @description This is the type for the callback that is called when the state changes.
*
* @category being
*/
export type StateChangeCallback<States extends string = 'IDLE'> = (currentState: States, nextState: States) => void;
/**
* @description This is the interface for the state. The interface takes in a few generic parameters:
* You can probably get by extending the TemplateState class.
*
* Generic parameters:
* - EventPayloadMapping: A mapping of events to their payloads.
* - Context: The context of the state machine. (which can be used by each state to do calculations that would persist across states)
* - States: All of the possible states that the state machine can be in. e.g. a string literal union like "IDLE" | "SELECTING" | "PAN" | "ZOOM"
*
* A state's all possible states can be only a subset of the possible states of the state machine. (a state only needs to know what states it can transition to)
* This allows for a state to be reusable across different state machines.
*
* @see {@link TemplateState}
*
* @category being
*/
export interface State<EventPayloadMapping, Context extends BaseContext, States extends string = 'IDLE'> {
uponEnter(context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, States>, from: States): void;
beforeExit(context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, States>, to: States): void;
handles<K extends keyof Partial<EventPayloadMapping>>(event: K, payload: EventPayloadMapping[K], context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, States>): States | undefined;
eventReactions: EventReactions<EventPayloadMapping, Context, States>;
guards: Guard<Context>;
eventGuards: Partial<EventGuards<EventPayloadMapping, States, Context, Guard<Context>>>;
delay: Delay<Context, EventPayloadMapping, States> | undefined;
}
/**
* @description This is the type for the event reactions of a state.
*
* Generic parameters:
* - EventPayloadMapping: A mapping of events to their payloads.
* - Context: The context of the state machine. (which can be used by each state to do calculations that would persist across states)
* - States: All of the possible states that the state machine can be in. e.g. a string literal union like "IDLE" | "SELECTING" | "PAN" | "ZOOM"
*
* @category being
*/
export type EventReactions<EventPayloadMapping, Context extends BaseContext, States extends string> = {
[K in keyof Partial<EventPayloadMapping>]: {
action: (context: Context, event: EventPayloadMapping[K], stateMachine: StateMachine<EventPayloadMapping, Context, States>) => void;
defaultTargetState: States;
};
};
/**
* @description This is the type for the guard evaluation when a state transition is happening.
*
* Guard evaluations are evaluated after the state has handled the event with the action.
* Guard evaluations can be defined in an array and the first guard that evaluates to true will be used to determine the next state.
*
* Generic parameters:
* - Context: The context of the state machine. (which can be used by each state to do calculations that would persist across states)
*
* @category being
*/
export type GuardEvaluation<Context extends BaseContext> = (context: Context) => boolean;
/**
* @description This is the type for the guard of a state.
*
* guard is an object that maps a key to a guard evaluation.
* K is all the possible keys that can be used to evaluate the guard.
* K is optional but if it is not provided, typescript won't be able to type guard in the EventGuards type.
*
* @category being
*/
export type Guard<Context extends BaseContext, K extends string = string> = {
[P in K]: GuardEvaluation<Context>;
};
export type Action<Context extends BaseContext, EventPayloadMapping, States extends string> = {
action: (context: Context, event: EventPayloadMapping[keyof EventPayloadMapping], stateMachine: StateMachine<EventPayloadMapping, Context, States>) => void;
defaultTargetState: States;
};
export type Delay<Context extends BaseContext, EventPayloadMapping, States extends string> = {
time: number;
action: Action<Context, EventPayloadMapping, States>;
};
/**
* @description This is a mapping of a guard to a target state.
*
* Generic parameters:
* - Context: The context of the state machine. (which can be used by each state to do calculations that would persist across states)
* - G: The guard type.
* - States: All of the possible states that the state machine can be in. e.g. a string literal union like "IDLE" | "SELECTING" | "PAN" | "ZOOM"
*
* You probably don't need to use this type directly.
*
* @see {@link TemplateState['eventGuards']}
*
* @category being
*/
export type GuardMapping<Context extends BaseContext, G, States extends string> = {
guard: G extends Guard<Context, infer K> ? K : never;
target: States;
};
/**
* @description This is a mapping of an event to a guard evaluation.
*
* Generic parameters:
* - EventPayloadMapping: A mapping of events to their payloads.
* - States: All of the possible states that the state machine can be in. e.g. a string literal union like "IDLE" | "SELECTING" | "PAN" | "ZOOM"
* - Context: The context of the state machine. (which can be used by each state to do calculations that would persist across states)
* - T: The guard type.
*
* You probably don't need to use this type directly.
* This is a mapping of an event to a guard evaluation.
*
* @see {@link TemplateState['eventGuards']}
*
* @category being
*/
export type EventGuards<EventPayloadMapping, States extends string, Context extends BaseContext, T extends Guard<Context>> = {
[K in keyof EventPayloadMapping]: GuardMapping<Context, T, States>[];
};
/**
* @description This is the template for the state machine.
*
* You can use this class to create a state machine. Usually this is all you need for the state machine. Unless you need extra functionality.
* To create a state machine, just instantiate this class and pass in the states, initial state and context.
*
* @see {@link createKmtInputStateMachine} for an example of how to create a state machine.
*
* @category being
*/
export declare class TemplateStateMachine<EventPayloadMapping, Context extends BaseContext, States extends string = 'IDLE'> implements StateMachine<EventPayloadMapping, Context, States> {
protected _currentState: States;
protected _states: Record<States, State<EventPayloadMapping, Context, States>>;
protected _context: Context;
protected _statesArray: States[];
protected _stateChangeCallbacks: StateChangeCallback<States>[];
protected _happensCallbacks: ((event: keyof EventPayloadMapping, payload: EventPayloadMapping[keyof EventPayloadMapping], context: Context) => void)[];
protected _timeouts: ReturnType<typeof setTimeout> | undefined;
constructor(states: Record<States, State<EventPayloadMapping, Context, States>>, initialState: States, context: Context);
switchTo(state: States): void;
happens<K extends keyof EventPayloadMapping>(event: K, payload: EventPayloadMapping[K]): States | undefined;
onStateChange(callback: StateChangeCallback<States>): void;
onHappens(callback: (event: keyof EventPayloadMapping, payload: EventPayloadMapping[keyof EventPayloadMapping], context: Context) => void): void;
get currentState(): States;
setContext(context: Context): void;
get possibleStates(): States[];
get states(): Record<States, State<EventPayloadMapping, Context, States>>;
}
/**
* @description This is the template for the state.
*
* This is a base template that you can extend to create a state.
* Unlike the TemplateStateMachine, this class is abstract. You need to implement the specific methods that you need.
* The core part off the state is the event reactions in which you would define how to handle each event in a state.
* You can define an eventReactions object that maps only the events that you need. If this state does not need to handle a specific event, you can just not define it in the eventReactions object.
*
* @category being
*/
export declare abstract class TemplateState<EventPayloadMapping, Context extends BaseContext, States extends string = 'IDLE'> implements State<EventPayloadMapping, Context, States> {
abstract eventReactions: EventReactions<EventPayloadMapping, Context, States>;
protected _guards: Guard<Context>;
protected _eventGuards: Partial<EventGuards<EventPayloadMapping, States, Context, Guard<Context>>>;
protected _delay: Delay<Context, EventPayloadMapping, States> | undefined;
get guards(): Guard<Context>;
get eventGuards(): Partial<EventGuards<EventPayloadMapping, States, Context, Guard<Context>>>;
get delay(): Delay<Context, EventPayloadMapping, States> | undefined;
uponEnter(context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, States>, from: States): void;
beforeExit(context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, States>, to: States): void;
handles<K extends keyof EventPayloadMapping>(event: K, payload: EventPayloadMapping[K], context: Context, stateMachine: StateMachine<EventPayloadMapping, Context, States>): States | undefined;
}
export {};