@ue-too/board 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -229,20 +229,9 @@ flowchart TB
229
229
  ```
230
230
 
231
231
  **Key concepts:**
232
- - **Event Parsers**: Register listeners on canvas (works with vanilla, pixi.js, fabric.js, konva)
232
+ - **Event Parsers**: Register listeners on canvas (should work with vanilla out of the box, pixi.js, fabric.js, konva with some modifications)
233
233
  - **Input State Machine**: Interprets raw events into camera intents (pan/zoom/rotate)
234
- - **Input Orchestrator**: Routes outputs in parallel — always publishes raw input, and asks CameraMux for permission
234
+ - **Input Orchestrator**: Routes outputs in parallel — always publishes raw input, and asks CameraMux for permission to pass through the input to the camera rig.
235
235
  - **Camera Mux**: Controls input priority (e.g., user input can cancel animations). Returns `{allowPassThrough: true/false}`
236
236
  - **Camera Rig**: Applies movement restrictions and clamping before updating camera
237
237
  - **Observable Camera**: Final camera state with change observers
238
-
239
- ## TODO
240
- - [x] Add a canvas position dimension publisher that can be used to get the position and dimension of the canvas.
241
- - [ ] Add a `boardify-pixi` package that contains utility functions for the board to work with pixi.js.
242
- - [ ] Add a `boardify-fabric` package that contains utility functions for the board to work with fabric.js.
243
- - [ ] Add a `boardify-konva` package that contains utility functions for the board to work with konva.js.
244
- - [ ] Add an example of the board being used with react.
245
- - [ ] Add an example of the board being used with svelte. (I'm learning svelte right now so I can make a example for that)
246
- - [ ] Add an example of the board being used with vue. (Currently I don't have any plans on learning vue so probably not going to make one very soon)
247
- - [ ] A documentation site. There is a lot of util stuff that I don't think will fit in here in the readme. So stay tuned! (I am experimenting with docusaurus right now so it might be a docusaurus site)
248
-
@@ -6,6 +6,8 @@ import { CameraEventMap, CameraState, UnSubscribe } from '../camera/update-publi
6
6
  import { UnsubscribeToUserRawInput, RawUserInputEventMap } from '../input-interpretation/raw-input-publisher';
7
7
  import { CameraMux } from '../camera/camera-mux';
8
8
  import { CameraRig } from '../camera/camera-rig';
9
+ import { CanvasDimensions } from '../input-interpretation/input-state-machine';
10
+ import { InputOrchestrator } from '../input-interpretation/input-orchestrator';
9
11
  /**
10
12
  * Main user-facing API class that provides an infinite canvas with pan, zoom, and rotate capabilities.
11
13
  *
@@ -171,7 +173,11 @@ export default class Board {
171
173
  private _observableInputTracker;
172
174
  private _touchInputTracker;
173
175
  private _inputOrchestrator;
176
+ private _cachedCanvasWidth;
177
+ private _cachedCanvasHeight;
178
+ private _cachedCanvasPixelRatio;
174
179
  private lastUpdateTime;
180
+ private _canvasElement;
175
181
  /**
176
182
  * Creates a new Board instance with an optional canvas element.
177
183
  *
@@ -322,6 +328,9 @@ export default class Board {
322
328
  * @see {@link context} for accessing the rendering context
323
329
  */
324
330
  attach(canvas: HTMLCanvasElement, debug?: boolean): void;
331
+ disableEventListeners(): void;
332
+ enableEventListeners(): void;
333
+ get inputOrchestrator(): InputOrchestrator;
325
334
  /**
326
335
  * @group LifeCycle
327
336
  * @description This function is used to clean up the board. It removes all the event listeners and disconnects the resize observer and the attribute observer.
@@ -442,4 +451,6 @@ export default class Board {
442
451
  set clampRotation(value: boolean);
443
452
  getCameraRig(): CameraRig;
444
453
  setInputMode(mode: 'kmt' | 'trackpad'): void;
454
+ onCanvasDimensionChange(callback: (dimensions: CanvasDimensions) => void): () => void;
455
+ get canvasDimensions(): CanvasDimensions;
445
456
  }
package/camera/base.d.ts CHANGED
@@ -47,6 +47,7 @@ export default class BaseCamera implements BoardCamera {
47
47
  private _rotation;
48
48
  private _zoomLevel;
49
49
  private currentCachedTransform;
50
+ private currentCachedTRS;
50
51
  private _viewPortWidth;
51
52
  private _viewPortHeight;
52
53
  private _boundaries?;
@@ -271,7 +272,7 @@ export default class BaseCamera implements BoardCamera {
271
272
  *
272
273
  * @see {@link getTRS} for decomposed transformation components
273
274
  */
274
- getTransform(devicePixelRatio: number, alignCoorindate: boolean): {
275
+ getTransform(devicePixelRatio?: number, alignCoorindate?: boolean): {
275
276
  cached: boolean;
276
277
  a: number;
277
278
  b: number;
@@ -291,16 +292,17 @@ export default class BaseCamera implements BoardCamera {
291
292
  * This is useful when you need individual transformation components rather than
292
293
  * the combined matrix. Internally calls {@link getTransform} and decomposes the result.
293
294
  */
294
- getTRS(devicePixelRatio: number, alignCoorindate: boolean): {
295
- translation: {
295
+ getTRS(devicePixelRatio?: number, alignCoorindate?: boolean): {
296
+ scale: {
296
297
  x: number;
297
298
  y: number;
298
299
  };
299
300
  rotation: number;
300
- scale: {
301
+ translation: {
301
302
  x: number;
302
303
  y: number;
303
304
  };
305
+ cached: boolean;
304
306
  };
305
307
  /**
306
308
  * Sets camera state by decomposing a transformation matrix.
@@ -321,7 +323,7 @@ export default class BaseCamera implements BoardCamera {
321
323
  * camera.setUsingTransformationMatrix(matrix);
322
324
  * ```
323
325
  */
324
- setUsingTransformationMatrix(transformationMatrix: TransformationMatrix): void;
326
+ setUsingTransformationMatrix(transformationMatrix: TransformationMatrix, devicePixelRatio?: number): void;
325
327
  /**
326
328
  * Sets the camera rotation with boundary validation and normalization.
327
329
  *
@@ -1,10 +1,8 @@
1
1
  import { CameraMux, CameraMuxPanOutput, CameraMuxZoomOutput, CameraMuxRotationOutput } from "../interface";
2
2
  import { Point } from "@ue-too/math";
3
- import { ObservableBoardCamera } from "../../interface";
4
3
  import { PanControlStateMachine } from "./pan-control-state-machine";
5
4
  import { ZoomControlStateMachine } from "./zoom-control-state-machine";
6
5
  import { RotateControlStateMachine, RotateControlOutputEvent } from "./rotation-control-state-machine";
7
- import { CameraRig } from "../../camera-rig";
8
6
  /**
9
7
  * Advanced camera input multiplexer with animation support and input locking via state machines.
10
8
  *
@@ -95,7 +93,7 @@ export declare class CameraMuxWithAnimationAndLock implements CameraMux {
95
93
  */
96
94
  notifyPanToAnimationInput(target: Point): CameraMuxPanOutput;
97
95
  /**
98
- * Processes user pan input (implements {@link CameraMux.notifyPanInput}).
96
+ * Processes user pan input (implements {@link CameraMux}).
99
97
  *
100
98
  * @param delta - Pan delta in viewport coordinates
101
99
  * @returns Output indicating whether pan is allowed
@@ -118,7 +116,7 @@ export declare class CameraMuxWithAnimationAndLock implements CameraMux {
118
116
  */
119
117
  notifyPanInput(delta: Point): CameraMuxPanOutput;
120
118
  /**
121
- * Processes user zoom input (implements {@link CameraMux.notifyZoomInput}).
119
+ * Processes user zoom input (implements {@link CameraMux}).
122
120
  *
123
121
  * @param delta - Zoom delta (change in zoom level)
124
122
  * @param at - Anchor point in viewport coordinates
@@ -187,7 +185,7 @@ export declare class CameraMuxWithAnimationAndLock implements CameraMux {
187
185
  */
188
186
  notifyZoomInputAnimationWorld(targetZoom: number, at?: Point): void;
189
187
  /**
190
- * Processes user rotation input (implements {@link CameraMux.notifyRotationInput}).
188
+ * Processes user rotation input (implements {@link CameraMux}).
191
189
  *
192
190
  * @param delta - Rotation delta in radians
193
191
  * @returns Output indicating whether rotation is allowed
@@ -303,50 +301,4 @@ export declare class CameraMuxWithAnimationAndLock implements CameraMux {
303
301
  * @see {@link CameraMuxWithAnimationAndLock} for the implementation
304
302
  * @see {@link createCameraMuxWithAnimationAndLockWithCameraRig} for custom rig version
305
303
  */
306
- export declare function createCameraMuxWithAnimationAndLock(camera: ObservableBoardCamera): CameraMux;
307
- /**
308
- * Creates a camera mux with animation and locking capabilities from a camera rig.
309
- *
310
- * @param cameraRig - Pre-configured camera rig to use
311
- * @returns Configured camera mux with animation support
312
- *
313
- * @remarks
314
- * Similar to {@link createCameraMuxWithAnimationAndLock} but accepts an existing
315
- * camera rig instead of creating a default one. Use this when you need:
316
- * - Custom camera rig configuration
317
- * - Specific pan/zoom/rotation constraints
318
- * - Non-default handler pipelines
319
- * - To share a camera rig between multiple systems
320
- *
321
- * **Advantages over camera-only variant:**
322
- * - Full control over camera rig settings
323
- * - Ability to configure boundaries, restrictions, clamping
324
- * - Use custom handler functions
325
- * - Reuse existing rig instance
326
- *
327
- * @example
328
- * ```typescript
329
- * // Create custom camera rig with specific config
330
- * const camera = new DefaultBoardCamera(1920, 1080);
331
- * const rig = new DefaultCameraRig({
332
- * limitEntireViewPort: true,
333
- * clampTranslation: true,
334
- * boundaries: {
335
- * min: { x: 0, y: 0 },
336
- * max: { x: 2000, y: 1000 }
337
- * }
338
- * }, camera);
339
- *
340
- * // Create mux with custom rig
341
- * const mux = createCameraMuxWithAnimationAndLockWithCameraRig(rig);
342
- *
343
- * // Animations respect rig's boundaries and constraints
344
- * mux.notifyPanToAnimationInput({ x: 3000, y: 1500 });
345
- * // Camera will be clamped to boundaries during animation
346
- * ```
347
- *
348
- * @category Input Flow Control
349
- * @see {@link CameraMuxWithAnimationAndLock} for the implementation
350
- * @see {@link createCameraMuxWithAnimationAndLock} for simpler camera-only version
351
- */
352
- export declare function createCameraMuxWithAnimationAndLockWithCameraRig(cameraRig: CameraRig): CameraMux;
304
+ export declare function createCameraMuxWithAnimationAndLock(): CameraMux;
@@ -151,7 +151,7 @@ export declare class PanControlStateMachine extends TemplateStateMachine<PanEven
151
151
  * Forces state change to begin animation or transition sequence.
152
152
  * Called when starting programmatic camera movements.
153
153
  */
154
- initateTransition(): void;
154
+ initateTransition(): import("@ue-too/being").EventResult<PanControlStates, void>;
155
155
  }
156
156
  /**
157
157
  * State implementation for accepting user pan input (idle/normal state).
@@ -160,7 +160,7 @@ export declare class PanControlStateMachine extends TemplateStateMachine<PanEven
160
160
  */
161
161
  export declare class AcceptingUserInputState extends TemplateState<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> {
162
162
  constructor();
163
- eventReactions: EventReactions<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping>;
163
+ protected _eventReactions: EventReactions<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping>;
164
164
  userPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent;
165
165
  userPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent;
166
166
  lockedOnObjectPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent;
@@ -173,7 +173,7 @@ export declare class AcceptingUserInputState extends TemplateState<PanEventPaylo
173
173
  */
174
174
  export declare class TransitionState extends TemplateState<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> {
175
175
  constructor();
176
- eventReactions: EventReactions<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping>;
176
+ protected _eventReactions: EventReactions<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping>;
177
177
  userPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent;
178
178
  userPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent;
179
179
  transitionPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent;
@@ -188,7 +188,7 @@ export declare class TransitionState extends TemplateState<PanEventPayloadMappin
188
188
  */
189
189
  export declare class LockedOnObjectState extends TemplateState<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping> {
190
190
  constructor();
191
- eventReactions: EventReactions<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping>;
191
+ protected _eventReactions: EventReactions<PanEventPayloadMapping, BaseContext, PanControlStates, PanControlOutputMapping>;
192
192
  lockedOnObjectPanByInputHandler(context: BaseContext, payload: PanByInputEventPayload): PanControlOutputEvent;
193
193
  lockedOnObjectPanToInputHandler(context: BaseContext, payload: PanToInputEventPayload): PanControlOutputEvent;
194
194
  }
@@ -216,5 +216,5 @@ export declare function createDefaultPanControlStates(): Record<PanControlStates
216
216
  *
217
217
  * @category Input Flow Control
218
218
  */
219
- export declare function createDefaultPanControlStateMachine(context: BaseContext): PanControlStateMachine;
219
+ export declare function createDefaultPanControlStateMachine(context?: BaseContext): PanControlStateMachine;
220
220
  export {};
@@ -159,7 +159,7 @@ export declare class RotateControlStateMachine extends TemplateStateMachine<Rota
159
159
  */
160
160
  export declare class RotationAcceptingUserInputState extends TemplateState<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> {
161
161
  constructor();
162
- eventReactions: EventReactions<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping>;
162
+ protected _eventReactions: EventReactions<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping>;
163
163
  userRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent;
164
164
  userRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent;
165
165
  lockedOnObjectRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent;
@@ -172,7 +172,7 @@ export declare class RotationAcceptingUserInputState extends TemplateState<Rotat
172
172
  */
173
173
  export declare class RotationTransitionState extends TemplateState<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> {
174
174
  constructor();
175
- eventReactions: EventReactions<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping>;
175
+ protected _eventReactions: EventReactions<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping>;
176
176
  userRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent;
177
177
  userRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent;
178
178
  transitionRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent;
@@ -187,7 +187,7 @@ export declare class RotationTransitionState extends TemplateState<RotateEventPa
187
187
  */
188
188
  export declare class RotationLockedOnObjectState extends TemplateState<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping> {
189
189
  constructor();
190
- eventReactions: EventReactions<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping>;
190
+ protected _eventReactions: EventReactions<RotateEventPayloadMapping, BaseContext, RotateControlStates, RotateControlOutputMapping>;
191
191
  lockedOnObjectRotateByInputHandler(context: BaseContext, payload: RotateByInputEventPayload): RotateControlOutputEvent;
192
192
  lockedOnObjectRotateToInputHandler(context: BaseContext, payload: RotateToInputEventPayload): RotateControlOutputEvent;
193
193
  }
@@ -215,5 +215,5 @@ export declare function createDefaultRotateControlStates(): Record<RotateControl
215
215
  *
216
216
  * @category Input Flow Control
217
217
  */
218
- export declare function createDefaultRotateControlStateMachine(context: BaseContext): RotateControlStateMachine;
218
+ export declare function createDefaultRotateControlStateMachine(context?: BaseContext): RotateControlStateMachine;
219
219
  export {};
@@ -137,8 +137,7 @@ export type ZoomControlOutputMapping = {
137
137
  * @category Input Flow Control
138
138
  */
139
139
  export declare class ZoomAcceptingUserInputState extends TemplateState<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {
140
- private _eventReactions;
141
- get eventReactions(): EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping>;
140
+ protected _eventReactions: EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping>;
142
141
  userZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping["userZoomByAtInput"]): ZoomControlOutputEvent;
143
142
  userZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping["userZoomToAtInput"]): ZoomControlOutputEvent;
144
143
  }
@@ -149,8 +148,7 @@ export declare class ZoomAcceptingUserInputState extends TemplateState<ZoomEvent
149
148
  */
150
149
  export declare class ZoomTransitionState extends TemplateState<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {
151
150
  constructor();
152
- private _eventReactions;
153
- get eventReactions(): EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping>;
151
+ protected _eventReactions: EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping>;
154
152
  lockedOnObjectZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping["lockedOnObjectZoomByAtInput"]): ZoomControlOutputEvent;
155
153
  lockedOnObjectZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping["lockedOnObjectZoomToAtInput"]): ZoomControlOutputEvent;
156
154
  userZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping["userZoomByAtInput"]): ZoomControlOutputEvent;
@@ -168,8 +166,7 @@ export declare class ZoomTransitionState extends TemplateState<ZoomEventPayloadM
168
166
  */
169
167
  export declare class ZoomLockedOnObjectState extends TemplateState<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping> {
170
168
  constructor();
171
- private _eventReactions;
172
- get eventReactions(): EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping>;
169
+ protected _eventReactions: EventReactions<ZoomEventPayloadMapping, BaseContext, ZoomControlStates, ZoomControlOutputMapping>;
173
170
  lockedOnObjectZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping["lockedOnObjectZoomByAtInput"]): ZoomControlOutputEvent;
174
171
  lockedOnObjectZoomToAtInput(context: BaseContext, payload: ZoomEventPayloadMapping["lockedOnObjectZoomToAtInput"]): ZoomControlOutputEvent;
175
172
  userZoomByAtInput(context: BaseContext, payload: ZoomEventPayloadMapping["userZoomByAtInput"]): ZoomControlOutputEvent;
@@ -287,4 +284,4 @@ export declare function createDefaultZoomControlStates(): Record<ZoomControlStat
287
284
  *
288
285
  * @category Input Flow Control
289
286
  */
290
- export declare function createDefaultZoomControlStateMachine(context: BaseContext): ZoomControlStateMachine;
287
+ export declare function createDefaultZoomControlStateMachine(context?: BaseContext): ZoomControlStateMachine;
@@ -507,7 +507,7 @@ export declare class DefaultCameraRig implements CameraRig {
507
507
  * When true, pan boundaries ensure the entire viewport stays within configured limits.
508
508
  * When false, only the camera center point is constrained.
509
509
  *
510
- * This is a convenience setter for {@link CameraRigConfig.limitEntireViewPort}.
510
+ * This is a convenience setter for {@link CameraRigConfig}.limitEntireViewPort.
511
511
  */
512
512
  set limitEntireViewPort(limit: boolean);
513
513
  /**
@@ -199,7 +199,7 @@ export default class DefaultBoardCamera implements ObservableBoardCamera {
199
199
  * @param alignCoorindate Whether to align the coordinate system to the camera's position
200
200
  * @returns The transformation matrix
201
201
  */
202
- getTransform(devicePixelRatio: number, alignCoorindate?: boolean): TransformationMatrix;
202
+ getTransform(devicePixelRatio?: number, alignCoorindate?: boolean): TransformationMatrix;
203
203
  /**
204
204
  * Sets the camera rotation and notifies observers if successful.
205
205
  *
@@ -303,7 +303,7 @@ export default class DefaultBoardCamera implements ObservableBoardCamera {
303
303
  * ```
304
304
  */
305
305
  on<K extends keyof CameraEventMap>(eventName: K, callback: (event: CameraEventMap[K], cameraState: CameraState) => void, options?: SubscriptionOptions): UnSubscribe;
306
- getTRS(devicePixelRatio: number, alignCoordinateSystem: boolean): {
306
+ getTRS(devicePixelRatio?: number, alignCoordinateSystem?: boolean): {
307
307
  scale: {
308
308
  x: number;
309
309
  y: number;
@@ -313,7 +313,9 @@ export default class DefaultBoardCamera implements ObservableBoardCamera {
313
313
  x: number;
314
314
  y: number;
315
315
  };
316
+ cached: boolean;
316
317
  };
318
+ setUsingTransformationMatrix(transformationMatrix: TransformationMatrix, devicePixelRatio?: number): void;
317
319
  viewPortInWorldSpace(alignCoordinate?: boolean): {
318
320
  top: {
319
321
  left: Point;