@ue-too/board-react-adapter 0.14.1 → 0.15.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
@@ -22,16 +22,19 @@ React adapter for the @ue-too/board infinite canvas library.
22
22
  ## Installation
23
23
 
24
24
  Using Bun:
25
+
25
26
  ```bash
26
27
  bun add @ue-too/board-react-adapter react react-dom
27
28
  ```
28
29
 
29
30
  Using npm:
31
+
30
32
  ```bash
31
33
  npm install @ue-too/board-react-adapter react react-dom
32
34
  ```
33
35
 
34
36
  **Peer Dependencies:**
37
+
35
38
  - React >= 19.0.0
36
39
  - React-DOM >= 19.0.0
37
40
 
@@ -43,20 +46,20 @@ Here's a simple example creating an infinite canvas with React:
43
46
  import Board from '@ue-too/board-react-adapter';
44
47
 
45
48
  function App() {
46
- return (
47
- <Board
48
- width={800}
49
- height={600}
50
- animationCallback={(timestamp, ctx, camera) => {
51
- // Clear canvas
52
- ctx.clearRect(0, 0, 800, 600);
53
-
54
- // Draw a blue square at world origin
55
- ctx.fillStyle = 'blue';
56
- ctx.fillRect(0, 0, 100, 100);
57
- }}
58
- />
59
- );
49
+ return (
50
+ <Board
51
+ width={800}
52
+ height={600}
53
+ animationCallback={(timestamp, ctx, camera) => {
54
+ // Clear canvas
55
+ ctx.clearRect(0, 0, 800, 600);
56
+
57
+ // Draw a blue square at world origin
58
+ ctx.fillStyle = 'blue';
59
+ ctx.fillRect(0, 0, 100, 100);
60
+ }}
61
+ />
62
+ );
60
63
  }
61
64
  ```
62
65
 
@@ -78,6 +81,7 @@ Main component that renders the canvas and manages the board instance.
78
81
  ```
79
82
 
80
83
  **Props:**
84
+
81
85
  - `width`: Canvas width in pixels
82
86
  - `height`: Canvas height in pixels
83
87
  - `animationCallback`: Function called on each frame with timestamp, context, and camera
@@ -89,8 +93,8 @@ The component supports children, which will have access to the board via context
89
93
 
90
94
  ```tsx
91
95
  <Board width={800} height={600}>
92
- <Controls />
93
- <StatusDisplay />
96
+ <Controls />
97
+ <StatusDisplay />
94
98
  </Board>
95
99
  ```
96
100
 
@@ -102,20 +106,26 @@ Subscribe to a specific camera state property.
102
106
 
103
107
  ```tsx
104
108
  function useBoardCameraState<K extends keyof CameraState>(
105
- key: K
109
+ key: K
106
110
  ): CameraState[K];
107
111
  ```
108
112
 
109
113
  **Example:**
114
+
110
115
  ```tsx
111
116
  function CameraPosition() {
112
- const position = useBoardCameraState('position');
117
+ const position = useBoardCameraState('position');
113
118
 
114
- return <div>Position: ({position.x.toFixed(0)}, {position.y.toFixed(0)})</div>;
119
+ return (
120
+ <div>
121
+ Position: ({position.x.toFixed(0)}, {position.y.toFixed(0)})
122
+ </div>
123
+ );
115
124
  }
116
125
  ```
117
126
 
118
127
  **Available Keys:**
128
+
119
129
  - `position`: `{ x: number, y: number }` - Camera world position
120
130
  - `rotation`: `number` - Camera rotation in radians
121
131
  - `zoomLevel`: `number` - Current zoom level
@@ -129,17 +139,20 @@ function useAllBoardCameraState(): CameraState;
129
139
  ```
130
140
 
131
141
  **Example:**
142
+
132
143
  ```tsx
133
144
  function CameraInfo() {
134
- const camera = useAllBoardCameraState();
135
-
136
- return (
137
- <div>
138
- <p>Position: ({camera.position.x}, {camera.position.y})</p>
139
- <p>Rotation: {camera.rotation}rad</p>
140
- <p>Zoom: {camera.zoomLevel}x</p>
141
- </div>
142
- );
145
+ const camera = useAllBoardCameraState();
146
+
147
+ return (
148
+ <div>
149
+ <p>
150
+ Position: ({camera.position.x}, {camera.position.y})
151
+ </p>
152
+ <p>Rotation: {camera.rotation}rad</p>
153
+ <p>Zoom: {camera.zoomLevel}x</p>
154
+ </div>
155
+ );
143
156
  }
144
157
  ```
145
158
 
@@ -167,27 +180,28 @@ Get camera control functions.
167
180
 
168
181
  ```tsx
169
182
  function useCameraInput(): {
170
- panToWorld: (position: Point) => void;
171
- panByScreen: (offset: Point) => void;
172
- zoomTo: (level: number) => void;
173
- zoomIn: () => void;
174
- zoomOut: () => void;
175
- rotateTo: (angle: number) => void;
183
+ panToWorld: (position: Point) => void;
184
+ panByScreen: (offset: Point) => void;
185
+ zoomTo: (level: number) => void;
186
+ zoomIn: () => void;
187
+ zoomOut: () => void;
188
+ rotateTo: (angle: number) => void;
176
189
  };
177
190
  ```
178
191
 
179
192
  **Example:**
193
+
180
194
  ```tsx
181
195
  function Controls() {
182
- const { panToWorld, zoomTo, rotateTo } = useCameraInput();
183
-
184
- return (
185
- <div>
186
- <button onClick={() => panToWorld({ x: 0, y: 0 })}>Center</button>
187
- <button onClick={() => zoomTo(1.0)}>Reset Zoom</button>
188
- <button onClick={() => rotateTo(0)}>Reset Rotation</button>
189
- </div>
190
- );
196
+ const { panToWorld, zoomTo, rotateTo } = useCameraInput();
197
+
198
+ return (
199
+ <div>
200
+ <button onClick={() => panToWorld({ x: 0, y: 0 })}>Center</button>
201
+ <button onClick={() => zoomTo(1.0)}>Reset Zoom</button>
202
+ <button onClick={() => rotateTo(0)}>Reset Rotation</button>
203
+ </div>
204
+ );
191
205
  }
192
206
  ```
193
207
 
@@ -196,9 +210,7 @@ function Controls() {
196
210
  Set a custom camera multiplexer for advanced camera control.
197
211
 
198
212
  ```tsx
199
- function useCustomCameraMux(
200
- customMux: CameraMux | undefined
201
- ): void;
213
+ function useCustomCameraMux(customMux: CameraMux | undefined): void;
202
214
  ```
203
215
 
204
216
  #### `useBoardify(width, height, animationCallback)`
@@ -207,12 +219,12 @@ Create a standalone board instance without using the provider pattern.
207
219
 
208
220
  ```tsx
209
221
  function useBoardify(
210
- width: number,
211
- height: number,
212
- animationCallback: AnimationCallback
222
+ width: number,
223
+ height: number,
224
+ animationCallback: AnimationCallback
213
225
  ): {
214
- canvas: HTMLCanvasElement | null;
215
- board: BoardType | null;
226
+ canvas: HTMLCanvasElement | null;
227
+ board: BoardType | null;
216
228
  };
217
229
  ```
218
230
 
@@ -223,21 +235,20 @@ function useBoardify(
223
235
  Generic animation frame hook.
224
236
 
225
237
  ```tsx
226
- function useAnimationFrame(
227
- callback: (timestamp: number) => void
228
- ): void;
238
+ function useAnimationFrame(callback: (timestamp: number) => void): void;
229
239
  ```
230
240
 
231
241
  **Example:**
242
+
232
243
  ```tsx
233
244
  function AnimatedComponent() {
234
- const [rotation, setRotation] = useState(0);
245
+ const [rotation, setRotation] = useState(0);
235
246
 
236
- useAnimationFrame((timestamp) => {
237
- setRotation(timestamp * 0.001); // Rotate based on time
238
- });
247
+ useAnimationFrame(timestamp => {
248
+ setRotation(timestamp * 0.001); // Rotate based on time
249
+ });
239
250
 
240
- return <div style={{ transform: `rotate(${rotation}rad)` }}>Spinning</div>;
251
+ return <div style={{ transform: `rotate(${rotation}rad)` }}>Spinning</div>;
241
252
  }
242
253
  ```
243
254
 
@@ -247,7 +258,11 @@ Animation loop integrated with `board.step()`.
247
258
 
248
259
  ```tsx
249
260
  function useAnimationFrameWithBoard(
250
- callback: (timestamp: number, ctx: CanvasRenderingContext2D, camera: Camera) => void
261
+ callback: (
262
+ timestamp: number,
263
+ ctx: CanvasRenderingContext2D,
264
+ camera: Camera
265
+ ) => void
251
266
  ): void;
252
267
  ```
253
268
 
@@ -256,39 +271,44 @@ function useAnimationFrameWithBoard(
256
271
  ### Basic Canvas with Pan and Zoom
257
272
 
258
273
  ```tsx
259
- import Board, { useCameraInput, useBoardCameraState } from '@ue-too/board-react-adapter';
274
+ import Board, {
275
+ useBoardCameraState,
276
+ useCameraInput,
277
+ } from '@ue-too/board-react-adapter';
260
278
 
261
279
  function Controls() {
262
- const position = useBoardCameraState('position');
263
- const zoom = useBoardCameraState('zoomLevel');
264
- const { panToWorld, zoomTo } = useCameraInput();
265
-
266
- return (
267
- <div style={{ position: 'absolute', top: 10, left: 10 }}>
268
- <p>Position: ({position.x.toFixed(0)}, {position.y.toFixed(0)})</p>
269
- <p>Zoom: {zoom.toFixed(2)}x</p>
270
- <button onClick={() => panToWorld({ x: 0, y: 0 })}>Center</button>
271
- <button onClick={() => zoomTo(1.0)}>Reset Zoom</button>
272
- </div>
273
- );
280
+ const position = useBoardCameraState('position');
281
+ const zoom = useBoardCameraState('zoomLevel');
282
+ const { panToWorld, zoomTo } = useCameraInput();
283
+
284
+ return (
285
+ <div style={{ position: 'absolute', top: 10, left: 10 }}>
286
+ <p>
287
+ Position: ({position.x.toFixed(0)}, {position.y.toFixed(0)})
288
+ </p>
289
+ <p>Zoom: {zoom.toFixed(2)}x</p>
290
+ <button onClick={() => panToWorld({ x: 0, y: 0 })}>Center</button>
291
+ <button onClick={() => zoomTo(1.0)}>Reset Zoom</button>
292
+ </div>
293
+ );
274
294
  }
275
295
 
276
296
  function App() {
277
- return (
278
- <Board
279
- width={800}
280
- height={600}
281
- animationCallback={(timestamp, ctx) => {
282
- ctx.fillStyle = 'lightblue';
283
- ctx.fillRect(-200, -200, 400, 400);
284
-
285
- ctx.fillStyle = 'red';
286
- ctx.fillRect(-50, -50, 100, 100);
287
- }}
288
- >
289
- <Controls />
290
- </Board>
291
- );
297
+ return (
298
+ <Board
299
+ width={800}
300
+ height={600}
301
+ animationCallback={(timestamp, ctx) => {
302
+ ctx.fillStyle = 'lightblue';
303
+ ctx.fillRect(-200, -200, 400, 400);
304
+
305
+ ctx.fillStyle = 'red';
306
+ ctx.fillRect(-50, -50, 100, 100);
307
+ }}
308
+ >
309
+ <Controls />
310
+ </Board>
311
+ );
292
312
  }
293
313
  ```
294
314
 
@@ -299,36 +319,39 @@ import Board, { useBoard } from '@ue-too/board-react-adapter';
299
319
  import { useState } from 'react';
300
320
 
301
321
  function Drawing() {
302
- const board = useBoard();
303
- const [points, setPoints] = useState<Point[]>([]);
304
-
305
- const handleClick = (e: React.MouseEvent<HTMLCanvasElement>) => {
306
- if (!board) return;
307
-
308
- const rect = e.currentTarget.getBoundingClientRect();
309
- const screenX = e.clientX - rect.left;
310
- const screenY = e.clientY - rect.top;
311
-
312
- const worldPoint = board.camera.screenToWorld({ x: screenX, y: screenY });
313
- setPoints([...points, worldPoint]);
314
- };
315
-
316
- return (
317
- <Board
318
- width={800}
319
- height={600}
320
- onClick={handleClick}
321
- animationCallback={(timestamp, ctx) => {
322
- // Draw all points
323
- ctx.fillStyle = 'red';
324
- points.forEach(point => {
325
- ctx.beginPath();
326
- ctx.arc(point.x, point.y, 5, 0, Math.PI * 2);
327
- ctx.fill();
322
+ const board = useBoard();
323
+ const [points, setPoints] = useState<Point[]>([]);
324
+
325
+ const handleClick = (e: React.MouseEvent<HTMLCanvasElement>) => {
326
+ if (!board) return;
327
+
328
+ const rect = e.currentTarget.getBoundingClientRect();
329
+ const screenX = e.clientX - rect.left;
330
+ const screenY = e.clientY - rect.top;
331
+
332
+ const worldPoint = board.camera.screenToWorld({
333
+ x: screenX,
334
+ y: screenY,
328
335
  });
329
- }}
330
- />
331
- );
336
+ setPoints([...points, worldPoint]);
337
+ };
338
+
339
+ return (
340
+ <Board
341
+ width={800}
342
+ height={600}
343
+ onClick={handleClick}
344
+ animationCallback={(timestamp, ctx) => {
345
+ // Draw all points
346
+ ctx.fillStyle = 'red';
347
+ points.forEach(point => {
348
+ ctx.beginPath();
349
+ ctx.arc(point.x, point.y, 5, 0, Math.PI * 2);
350
+ ctx.fill();
351
+ });
352
+ }}
353
+ />
354
+ );
332
355
  }
333
356
  ```
334
357
 
@@ -339,67 +362,74 @@ import Board, { useAnimationFrameWithBoard } from '@ue-too/board-react-adapter';
339
362
  import { useState } from 'react';
340
363
 
341
364
  function AnimatedScene() {
342
- const [time, setTime] = useState(0);
365
+ const [time, setTime] = useState(0);
343
366
 
344
- useAnimationFrameWithBoard((timestamp, ctx, camera) => {
345
- setTime(timestamp * 0.001);
367
+ useAnimationFrameWithBoard((timestamp, ctx, camera) => {
368
+ setTime(timestamp * 0.001);
346
369
 
347
- // Draw animated circle
348
- const x = Math.cos(time) * 100;
349
- const y = Math.sin(time) * 100;
370
+ // Draw animated circle
371
+ const x = Math.cos(time) * 100;
372
+ const y = Math.sin(time) * 100;
350
373
 
351
- ctx.fillStyle = 'green';
352
- ctx.beginPath();
353
- ctx.arc(x, y, 20, 0, Math.PI * 2);
354
- ctx.fill();
355
- });
374
+ ctx.fillStyle = 'green';
375
+ ctx.beginPath();
376
+ ctx.arc(x, y, 20, 0, Math.PI * 2);
377
+ ctx.fill();
378
+ });
356
379
 
357
- return null; // No UI, just updates
380
+ return null; // No UI, just updates
358
381
  }
359
382
 
360
383
  function App() {
361
- return (
362
- <Board width={800} height={600}>
363
- <AnimatedScene />
364
- </Board>
365
- );
384
+ return (
385
+ <Board width={800} height={600}>
386
+ <AnimatedScene />
387
+ </Board>
388
+ );
366
389
  }
367
390
  ```
368
391
 
369
392
  ### Camera Controls with Buttons
370
393
 
371
394
  ```tsx
372
- import Board, { useCameraInput, useBoardCameraState } from '@ue-too/board-react-adapter';
395
+ import Board, {
396
+ useBoardCameraState,
397
+ useCameraInput,
398
+ } from '@ue-too/board-react-adapter';
373
399
 
374
400
  function CameraControls() {
375
- const { panByScreen, zoomIn, zoomOut, rotateTo } = useCameraInput();
376
- const rotation = useBoardCameraState('rotation');
377
-
378
- return (
379
- <div style={{ position: 'absolute', top: 10, right: 10 }}>
380
- <button onClick={() => panByScreen({ x: 0, y: -50 })}>↑</button>
381
- <br />
382
- <button onClick={() => panByScreen({ x: -50, y: 0 })}>←</button>
383
- <button onClick={() => panByScreen({ x: 50, y: 0 })}>→</button>
384
- <br />
385
- <button onClick={() => panByScreen({ x: 0, y: 50 })}>↓</button>
386
- <br />
387
- <button onClick={zoomIn}>Zoom In</button>
388
- <button onClick={zoomOut}>Zoom Out</button>
389
- <br />
390
- <button onClick={() => rotateTo((rotation + Math.PI / 4) % (Math.PI * 2))}>
391
- Rotate 45°
392
- </button>
393
- </div>
394
- );
401
+ const { panByScreen, zoomIn, zoomOut, rotateTo } = useCameraInput();
402
+ const rotation = useBoardCameraState('rotation');
403
+
404
+ return (
405
+ <div style={{ position: 'absolute', top: 10, right: 10 }}>
406
+ <button onClick={() => panByScreen({ x: 0, y: -50 })}>↑</button>
407
+ <br />
408
+ <button onClick={() => panByScreen({ x: -50, y: 0 })}>←</button>
409
+ <button onClick={() => panByScreen({ x: 50, y: 0 })}>→</button>
410
+ <br />
411
+ <button onClick={() => panByScreen({ x: 0, y: 50 })}>↓</button>
412
+ <br />
413
+ <button onClick={zoomIn}>Zoom In</button>
414
+ <button onClick={zoomOut}>Zoom Out</button>
415
+ <br />
416
+ <button
417
+ onClick={() =>
418
+ rotateTo((rotation + Math.PI / 4) % (Math.PI * 2))
419
+ }
420
+ >
421
+ Rotate 45°
422
+ </button>
423
+ </div>
424
+ );
395
425
  }
396
426
 
397
427
  function App() {
398
- return (
399
- <Board width={800} height={600}>
400
- <CameraControls />
401
- </Board>
402
- );
428
+ return (
429
+ <Board width={800} height={600}>
430
+ <CameraControls />
431
+ </Board>
432
+ );
403
433
  }
404
434
  ```
405
435
 
@@ -410,23 +440,23 @@ import { useBoardify } from '@ue-too/board-react-adapter';
410
440
  import { useEffect, useRef } from 'react';
411
441
 
412
442
  function CustomBoard() {
413
- const containerRef = useRef<HTMLDivElement>(null);
414
- const { canvas, board } = useBoardify(
415
- 800,
416
- 600,
417
- (timestamp, ctx, camera) => {
418
- ctx.fillStyle = 'purple';
419
- ctx.fillRect(-100, -100, 200, 200);
420
- }
421
- );
422
-
423
- useEffect(() => {
424
- if (canvas && containerRef.current) {
425
- containerRef.current.appendChild(canvas);
426
- }
427
- }, [canvas]);
428
-
429
- return <div ref={containerRef} />;
443
+ const containerRef = useRef<HTMLDivElement>(null);
444
+ const { canvas, board } = useBoardify(
445
+ 800,
446
+ 600,
447
+ (timestamp, ctx, camera) => {
448
+ ctx.fillStyle = 'purple';
449
+ ctx.fillRect(-100, -100, 200, 200);
450
+ }
451
+ );
452
+
453
+ useEffect(() => {
454
+ if (canvas && containerRef.current) {
455
+ containerRef.current.appendChild(canvas);
456
+ }
457
+ }, [canvas]);
458
+
459
+ return <div ref={containerRef} />;
430
460
  }
431
461
  ```
432
462
 
@@ -440,10 +470,10 @@ This package is written in TypeScript with complete type definitions:
440
470
 
441
471
  ```tsx
442
472
  import Board, {
443
- useBoardCameraState,
444
- useCameraInput,
445
- type CameraState,
446
- type AnimationCallback
473
+ type AnimationCallback,
474
+ type CameraState,
475
+ useBoardCameraState,
476
+ useCameraInput,
447
477
  } from '@ue-too/board-react-adapter';
448
478
 
449
479
  // State is fully typed
@@ -451,11 +481,12 @@ const position: Point = useBoardCameraState('position');
451
481
  const zoom: number = useBoardCameraState('zoomLevel');
452
482
 
453
483
  // Functions are type-safe
454
- const { panToWorld }: { panToWorld: (position: Point) => void } = useCameraInput();
484
+ const { panToWorld }: { panToWorld: (position: Point) => void } =
485
+ useCameraInput();
455
486
 
456
487
  // Callbacks are typed
457
488
  const callback: AnimationCallback = (timestamp, ctx, camera) => {
458
- // All parameters are properly typed
489
+ // All parameters are properly typed
459
490
  };
460
491
  ```
461
492
 
@@ -476,6 +507,7 @@ This adapter follows these principles:
476
507
  - **Canvas Updates**: Board automatically handles canvas clearing and transformation
477
508
 
478
509
  **Performance Tips:**
510
+
479
511
  - Subscribe only to the state you need
480
512
  - Use `useMemo` for expensive calculations in render
481
513
  - Avoid creating new objects in animation callbacks
@@ -112,4 +112,4 @@ export type BoardProps = {
112
112
  * @see {@link useCameraInput} for camera control functions
113
113
  * @see {@link useBoard} for accessing the board instance
114
114
  */
115
- export default function BoardWrapperWithChildren({ width, height, fullScreen, animationCallback: animationCallbackProp, children }: BoardProps): import("react/jsx-runtime").JSX.Element;
115
+ export default function BoardWrapperWithChildren({ width, height, fullScreen, animationCallback: animationCallbackProp, children, }: BoardProps): import("react/jsx-runtime").JSX.Element;
@@ -6,5 +6,5 @@
6
6
  *
7
7
  * @module
8
8
  */
9
- export * from "./Board";
10
- export { default as Board } from "./Board";
9
+ export * from './Board';
10
+ export { default as Board } from './Board';
package/hooks/index.d.ts CHANGED
@@ -10,6 +10,6 @@
10
10
  *
11
11
  * @module
12
12
  */
13
- export * from "./useAnimationFrame";
14
- export * from "./useBoardify";
15
- export * from "./useCanvasProxy";
13
+ export * from './useAnimationFrame';
14
+ export * from './useBoardify';
15
+ export * from './useCanvasProxy';
@@ -1,6 +1,6 @@
1
- import { Board as Boardify, KMTEventParser, OutputEvent, TouchEventParser } from "@ue-too/board";
2
- import { CameraMux, CameraState } from "@ue-too/board/camera";
3
- import { Point } from "@ue-too/math";
1
+ import { Board as Boardify, KMTEventParser, OutputEvent, TouchEventParser } from '@ue-too/board';
2
+ import { CameraMux, CameraState } from '@ue-too/board/camera';
3
+ import { Point } from '@ue-too/math';
4
4
  /**
5
5
  * Hook to subscribe to a specific camera state property with automatic re-rendering.
6
6
  *
@@ -1,4 +1,4 @@
1
- import { CanvasProxy } from "@ue-too/board";
1
+ import { CanvasProxy } from '@ue-too/board';
2
2
  export declare function useCanvasProxy(): CanvasProxy;
3
3
  export declare function useCanvasProxyWithRef(): {
4
4
  canvasProxy: CanvasProxy;
package/index.d.ts CHANGED
@@ -89,5 +89,5 @@
89
89
  *
90
90
  * @see {@link Board} for the main component
91
91
  */
92
- export * from "./components/Board";
93
- export * from "./hooks";
92
+ export * from './components/Board';
93
+ export * from './hooks';
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import{useCallback as S,useEffect as I,useRef as L}from"react";import{Board as T}from"@ue-too/board";import{createContext as U,useCallback as O,useContext as V,useEffect as $,useMemo as Y,useRef as A,useSyncExternalStore as q}from"react";import{jsxDEV as j}from"react/jsx-dev-runtime";function P(G){let H=N(),J=G==="position"?"pan":G==="zoomLevel"?"zoom":"rotate",_=A(null);return q((K)=>H.camera.on(J,K),()=>{if(G==="position"){let K=H.camera.position,Q=_.current;if(Q&&Q.x===K.x&&Q.y===K.y)return Q;let X={...K};return _.current=X,X}return H.camera[G]})}function f(){let G=N();return Y(()=>{let J=G.getCameraRig();return{panByViewPort:(_)=>{J.panByViewPort(_)},panByWorld:(_)=>{J.panByWorld(_)},panToWorld:(_)=>{J.panToWorld(_)},panToViewPort:(_)=>{J.panToViewPort(_)},zoomToAtViewPort:(_,K)=>{J.zoomToAt(_,K)},zoomToAtWorld:(_,K)=>{J.zoomToAtWorld(_,K)},zoomTo:(_)=>{J.zoomTo(_)},zoomBy:(_)=>{J.zoomBy(_)},rotateTo:(_)=>{J.rotateTo(_)},rotateBy:(_)=>{J.rotateBy(_)}}},[G])}function w(){let G=N(),H=A(null);return q((J)=>{return G.camera.on("all",J)},()=>{let J=G.camera.position,_=G.camera.rotation,K=G.camera.zoomLevel,Q=H.current;if(Q&&Q.position.x===J.x&&Q.position.y===J.y&&Q.rotation===_&&Q.zoomLevel===K)return Q;let X={position:{...J},rotation:_,zoomLevel:K};return H.current=X,X})}function v(G){let H=N();$(()=>{H.cameraMux=G},[G,H])}function b(){let G=N(),H=O((J)=>{G.inputOrchestrator.processInputEvent(J)},[G]);return $(()=>{return G.disableEventListeners(),()=>{G.enableEventListeners()}},[G]),{processInputEvent:H}}var W=U(null);function Z({children:G}){let H=Y(()=>new T,[]);return j(W.Provider,{value:H,children:G},void 0,!1,void 0,this)}function N(){let G=V(W);if(G==null)throw Error("Board Provider not found");return G}function p(){return N().camera}function u(){let G=N();return q((H)=>G.onCanvasDimensionChange(H),()=>{return G.canvasDimensions})}function h(){let G=N();return O((H)=>{return G.convertWindowPoint2WorldCoord(H)},[G])}function m(G){let H=N();$(()=>{H.kmtParser=G},[G,H])}function d(G){let H=N();$(()=>{H.touchParser=G},[G,H])}function D(G){let H=L(null);I(()=>{let J=(_)=>{G(_),H.current=requestAnimationFrame(J)};return H.current=requestAnimationFrame(J),()=>{if(H.current)cancelAnimationFrame(H.current)}},[G])}function M(G){let H=N(),J=S((_)=>{H.step(_);let K=H.context;if(K==null){console.warn("Canvas context not available");return}G?.(_,K)},[G,H]);D(J)}import{PointCal as JG}from"@ue-too/math";import{jsxDEV as NG}from"react/jsx-dev-runtime";import{CanvasProxy as z}from"@ue-too/board";import{useState as C,useCallback as y}from"react";function F(){let[G,H]=C(()=>new z);return G}function YG(){let G=F(),H=y((J)=>{if(J==null){G.tearDown();return}G.attach(J)},[G]);return{canvasProxy:G,refCallback:H}}export{d as useCustomTouchEventParser,m as useCustomKMTEventParser,b as useCustomInputHandling,v as useCustomCameraMux,h as useCoordinateConversion,YG as useCanvasProxyWithRef,F as useCanvasProxy,u as useCanvasDimension,f as useCameraInput,P as useBoardCameraState,p as useBoardCamera,N as useBoard,M as useAnimationFrameWithBoard,D as useAnimationFrame,w as useAllBoardCameraState,Z as BoardProvider};
1
+ import{PointCal as t}from"@ue-too/math";import{useCallback as S,useEffect as I,useRef as L}from"react";import{Board as T}from"@ue-too/board";import{createContext as U,useCallback as O,useContext as V,useEffect as $,useMemo as Y,useRef as A,useSyncExternalStore as q}from"react";import{jsxDEV as j}from"react/jsx-dev-runtime";function P(G){let H=N(),J=G==="position"?"pan":G==="zoomLevel"?"zoom":"rotate",_=A(null);return q((K)=>H.camera.on(J,K),()=>{if(G==="position"){let K=H.camera.position,Q=_.current;if(Q&&Q.x===K.x&&Q.y===K.y)return Q;let X={...K};return _.current=X,X}return H.camera[G]})}function f(){let G=N();return Y(()=>{let J=G.getCameraRig();return{panByViewPort:(_)=>{J.panByViewPort(_)},panByWorld:(_)=>{J.panByWorld(_)},panToWorld:(_)=>{J.panToWorld(_)},panToViewPort:(_)=>{J.panToViewPort(_)},zoomToAtViewPort:(_,K)=>{J.zoomToAt(_,K)},zoomToAtWorld:(_,K)=>{J.zoomToAtWorld(_,K)},zoomTo:(_)=>{J.zoomTo(_)},zoomBy:(_)=>{J.zoomBy(_)},rotateTo:(_)=>{J.rotateTo(_)},rotateBy:(_)=>{J.rotateBy(_)}}},[G])}function w(){let G=N(),H=A(null);return q((J)=>{return G.camera.on("all",J)},()=>{let J=G.camera.position,_=G.camera.rotation,K=G.camera.zoomLevel,Q=H.current;if(Q&&Q.position.x===J.x&&Q.position.y===J.y&&Q.rotation===_&&Q.zoomLevel===K)return Q;let X={position:{...J},rotation:_,zoomLevel:K};return H.current=X,X})}function v(G){let H=N();$(()=>{H.cameraMux=G},[G,H])}function b(){let G=N(),H=O((J)=>{G.inputOrchestrator.processInputEvent(J)},[G]);return $(()=>{return G.disableEventListeners(),()=>{G.enableEventListeners()}},[G]),{processInputEvent:H}}var W=U(null);function Z({children:G}){let H=Y(()=>new T,[]);return j(W.Provider,{value:H,children:G},void 0,!1,void 0,this)}function N(){let G=V(W);if(G==null)throw Error("Board Provider not found");return G}function p(){return N().camera}function u(){let G=N();return q((H)=>G.onCanvasDimensionChange(H),()=>{return G.canvasDimensions})}function h(){let G=N();return O((H)=>{return G.convertWindowPoint2WorldCoord(H)},[G])}function m(G){let H=N();$(()=>{H.kmtParser=G},[G,H])}function d(G){let H=N();$(()=>{H.touchParser=G},[G,H])}function D(G){let H=L(null);I(()=>{let J=(_)=>{G(_),H.current=requestAnimationFrame(J)};return H.current=requestAnimationFrame(J),()=>{if(H.current)cancelAnimationFrame(H.current)}},[G])}function M(G){let H=N(),J=S((_)=>{H.step(_);let K=H.context;if(K==null){console.warn("Canvas context not available");return}G?.(_,K)},[G,H]);D(J)}import{jsxDEV as NG}from"react/jsx-dev-runtime";import{CanvasProxy as z}from"@ue-too/board";import{useCallback as C,useState as y}from"react";function F(){let[G,H]=y(()=>new z);return G}function YG(){let G=F(),H=C((J)=>{if(J==null){G.tearDown();return}G.attach(J)},[G]);return{canvasProxy:G,refCallback:H}}export{d as useCustomTouchEventParser,m as useCustomKMTEventParser,b as useCustomInputHandling,v as useCustomCameraMux,h as useCoordinateConversion,YG as useCanvasProxyWithRef,F as useCanvasProxy,u as useCanvasDimension,f as useCameraInput,P as useBoardCameraState,p as useBoardCamera,N as useBoard,M as useAnimationFrameWithBoard,D as useAnimationFrame,w as useAllBoardCameraState,Z as BoardProvider};
2
2
 
3
- //# debugId=6B2CEE4C23D45A0964756E2164756E21
3
+ //# debugId=E2568A6219EBF3E864756E2164756E21
4
4
 
5
5
  //# sourceMappingURL=index.js.map
package/index.js.map CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": [
4
+ "Board.tsx",
4
5
  "useAnimationFrame.ts",
5
6
  "useBoardify.tsx",
6
- "Board.tsx",
7
7
  "useCanvasProxy.ts"
8
8
  ],
9
9
  "sourcesContent": [
10
- "import { useCallback, useEffect, useRef } from 'react';\nimport { useBoard } from './useBoardify';\n\n/**\n * Hook to run a callback on every animation frame.\n *\n * @remarks\n * This hook uses `requestAnimationFrame` to execute a callback repeatedly for smooth animations.\n * The animation loop starts when the component mounts and stops when it unmounts, automatically\n * cleaning up the animation frame request.\n *\n * **Performance Note**: The callback is called on every frame, so ensure your callback is\n * optimized to avoid performance issues. The callback dependency should be stable to prevent\n * restarting the animation loop unnecessarily.\n *\n * @param callback - Function to call on each animation frame, receives the current timestamp\n *\n * @example\n * ```tsx\n * function AnimatedComponent() {\n * const [rotation, setRotation] = useState(0);\n *\n * useAnimationFrame((timestamp) => {\n * // Rotate 45 degrees per second\n * setRotation((prev) => prev + (Math.PI / 4) * (1 / 60));\n * });\n *\n * return <div style={{ transform: `rotate(${rotation}rad)` }}>Spinning!</div>;\n * }\n * ```\n *\n * @category Hooks\n * @see {@link useAnimationFrameWithBoard} for board-integrated animation loop\n */\nexport function useAnimationFrame(callback: (timestamp: number) => void) {\n const animationFrameRef = useRef<number | null>(null);\n\n useEffect(() => {\n const step = (timestamp: number) => {\n callback(timestamp);\n animationFrameRef.current = requestAnimationFrame(step);\n };\n\n // Start the animation loop\n animationFrameRef.current = requestAnimationFrame(step);\n\n // Cleanup function\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n };\n }, [callback]);\n}\n\n/**\n * Hook to run an animation loop integrated with the Board's step function.\n *\n * @remarks\n * This hook automatically calls `board.step(timestamp)` on every frame to update the board's\n * camera transform, then invokes your callback with the timestamp and canvas context.\n * This is the recommended way to implement drawing logic for board-based applications.\n *\n * The hook handles:\n * - Calling `board.step()` to update camera transforms\n * - Providing the canvas context for drawing\n * - Warning if context is not available\n * - Cleaning up the animation loop on unmount\n *\n * **Typical Usage Pattern**:\n * 1. Board calls `step()` to update transforms\n * 2. Your callback draws on the canvas\n * 3. Browser paints the frame\n * 4. Repeat next frame\n *\n * @param callback - Optional function to call after board.step(), receives timestamp and canvas context\n *\n * @example\n * ```tsx\n * function MyBoard() {\n * useAnimationFrameWithBoard((timestamp, ctx) => {\n * // Draw a rectangle at world position (0, 0)\n * ctx.fillStyle = 'red';\n * ctx.fillRect(0, 0, 100, 100);\n *\n * // Draw a circle that moves\n * const x = Math.sin(timestamp / 1000) * 200;\n * const y = Math.cos(timestamp / 1000) * 200;\n * ctx.fillStyle = 'blue';\n * ctx.beginPath();\n * ctx.arc(x, y, 20, 0, Math.PI * 2);\n * ctx.fill();\n * });\n *\n * return <Board width={800} height={600} />;\n * }\n * ```\n *\n * @category Hooks\n * @see {@link useAnimationFrame} for generic animation frame hook\n */\nexport function useAnimationFrameWithBoard(callback?: (timestamp: number, ctx: CanvasRenderingContext2D) => void) {\n\n const board = useBoard();\n\n const animationCallback = useCallback((timestamp: number) => {\n board.step(timestamp);\n const ctx = board.context;\n if (ctx == undefined) {\n console.warn('Canvas context not available');\n return;\n }\n callback?.(timestamp, ctx);\n }, [callback, board]);\n\n useAnimationFrame(animationCallback);\n}\n",
11
- "import {Board as Boardify, KMTEventParser, KmtInputStateMachine, OutputEvent, TouchEventParser} from \"@ue-too/board\";\nimport {createContext, useCallback, useContext, useEffect, useMemo, useRef, useSyncExternalStore} from \"react\";\nimport { CameraMux, CameraState } from \"@ue-too/board/camera\";\nimport { Point } from \"@ue-too/math\";\n\n/**\n * Maps camera state keys to their corresponding event names.\n * @internal\n */\ntype StateToEventKey<K extends keyof CameraState> =\n K extends \"position\" ? \"pan\" : K extends \"zoomLevel\" ? \"zoom\" : \"rotate\";\n\n/**\n * Hook to subscribe to a specific camera state property with automatic re-rendering.\n *\n * @remarks\n * This hook uses React's `useSyncExternalStore` to efficiently subscribe to camera state changes.\n * It only triggers re-renders when the specified property actually changes, and uses caching\n * to maintain referential equality for object values (like position).\n *\n * **Performance**: The hook is optimized to prevent unnecessary re-renders by:\n * - Caching object values (position) to maintain referential equality\n * - Using `useSyncExternalStore` for efficient subscription management\n * - Only subscribing to the specific state property needed\n *\n * @typeParam K - Key of the camera state to subscribe to\n * @param state - The camera state property to track (\"position\", \"rotation\", or \"zoomLevel\")\n * @returns The current value of the specified camera state property\n *\n * @example\n * ```tsx\n * function CameraInfo() {\n * const position = useBoardCameraState('position');\n * const rotation = useBoardCameraState('rotation');\n * const zoomLevel = useBoardCameraState('zoomLevel');\n *\n * return (\n * <div>\n * Position: {position.x}, {position.y}<br/>\n * Rotation: {rotation}<br/>\n * Zoom: {zoomLevel}\n * </div>\n * );\n * }\n * ```\n *\n * @category Hooks\n * @see {@link useAllBoardCameraState} for subscribing to all camera state at once\n */\nexport function useBoardCameraState<K extends keyof CameraState>(state: K): CameraState[K] {\n const board = useBoard();\n const stateKey = (state === \"position\" ? \"pan\" : state === \"zoomLevel\" ? \"zoom\" : \"rotate\") as StateToEventKey<K>;\n const cachedPositionRef = useRef<{ x: number; y: number } | null>(null);\n\n return useSyncExternalStore(\n (cb) => board.camera.on(stateKey, cb),\n () => {\n // For position (object), we need to cache to avoid creating new objects\n if (state === \"position\") {\n const currentPosition = board.camera.position;\n const cached = cachedPositionRef.current;\n \n if (cached && cached.x === currentPosition.x && cached.y === currentPosition.y) {\n // Return cached snapshot to maintain referential equality\n return cached as CameraState[K];\n }\n \n // Cache the new position object\n const newPosition = {...currentPosition};\n cachedPositionRef.current = newPosition;\n return newPosition as CameraState[K];\n }\n \n // For primitive values (rotation, zoomLevel), return directly\n // Object.is works correctly for primitives\n return board.camera[state] as CameraState[K];\n },\n );\n}\n\n/**\n * Hook to get camera control functions for programmatic camera manipulation.\n *\n * @remarks\n * This hook provides a stable set of functions to control the camera programmatically.\n * The functions are memoized and only recreate when the board instance changes.\n *\n * All camera operations go through the camera rig, which enforces boundaries,\n * restrictions, and other constraints configured on the board.\n *\n * @returns Object containing camera control functions:\n * - `panToWorld` - Pan camera to a world position\n * - `panToViewPort` - Pan camera to a viewport position\n * - `zoomTo` - Set camera zoom to specific level\n * - `zoomBy` - Adjust camera zoom by delta\n * - `rotateTo` - Set camera rotation to specific angle\n * - `rotateBy` - Adjust camera rotation by delta\n *\n * @example\n * ```tsx\n * function CameraControls() {\n * const { panToWorld, zoomTo, rotateTo } = useCameraInput();\n *\n * return (\n * <div>\n * <button onClick={() => panToWorld({ x: 0, y: 0 })}>\n * Center Camera\n * </button>\n * <button onClick={() => zoomTo(1.0)}>\n * Reset Zoom\n * </button>\n * <button onClick={() => rotateTo(0)}>\n * Reset Rotation\n * </button>\n * </div>\n * );\n * }\n * ```\n *\n * @category Hooks\n */\nexport function useCameraInput(){\n const board = useBoard();\n\n const test = useMemo(()=>{\n const cameraRig = board.getCameraRig();\n\n return {\n panByViewPort: (delta: Point) => {\n cameraRig.panByViewPort(delta);\n },\n panByWorld: (delta: Point) => {\n cameraRig.panByWorld(delta);\n },\n panToWorld: (worldPosition: Point) => {\n cameraRig.panToWorld(worldPosition);\n },\n panToViewPort: (viewPortPosition: Point) => {\n cameraRig.panToViewPort(viewPortPosition);\n },\n zoomToAtViewPort: (zoomLevel: number, at: Point) => {\n cameraRig.zoomToAt(zoomLevel, at);\n },\n zoomToAtWorld: (zoomLevel: number, at: Point) => {\n cameraRig.zoomToAtWorld(zoomLevel, at);\n },\n zoomTo: (zoomLevel: number) => {\n cameraRig.zoomTo(zoomLevel);\n },\n zoomBy: (zoomDelta: number) => {\n cameraRig.zoomBy(zoomDelta);\n },\n rotateTo: (rotation: number) => {\n cameraRig.rotateTo(rotation);\n },\n rotateBy: (rotationDelta: number) => {\n cameraRig.rotateBy(rotationDelta);\n }\n }\n\n }, [board]);\n\n return test;\n}\n\n/**\n * Hook to subscribe to all camera state properties with automatic re-rendering.\n *\n * @remarks\n * This hook provides a snapshot of all camera state (position, rotation, zoomLevel) and\n * re-renders only when any of these values change. It's more efficient than using multiple\n * {@link useBoardCameraState} calls when you need all state properties.\n *\n * **Performance**: The hook uses snapshot caching to maintain referential equality when\n * values haven't changed, preventing unnecessary re-renders in child components.\n *\n * @returns Object containing:\n * - `position` - Current camera position {x, y}\n * - `rotation` - Current camera rotation in radians\n * - `zoomLevel` - Current camera zoom level\n *\n * @example\n * ```tsx\n * function CameraStateDisplay() {\n * const { position, rotation, zoomLevel } = useAllBoardCameraState();\n *\n * return (\n * <div>\n * <h3>Camera State</h3>\n * <p>Position: ({position.x.toFixed(2)}, {position.y.toFixed(2)})</p>\n * <p>Rotation: {rotation.toFixed(2)} rad</p>\n * <p>Zoom: {zoomLevel.toFixed(2)}x</p>\n * </div>\n * );\n * }\n * ```\n *\n * @category Hooks\n * @see {@link useBoardCameraState} for subscribing to individual state properties\n */\nexport function useAllBoardCameraState() {\n const board = useBoard();\n const cachedSnapshotRef = useRef<{\n position: { x: number; y: number };\n rotation: number;\n zoomLevel: number;\n } | null>(null);\n\n return useSyncExternalStore(\n (cb) => { return board.camera.on(\"all\", cb) },\n () => {\n const currentPosition = board.camera.position;\n const currentRotation = board.camera.rotation;\n const currentZoomLevel = board.camera.zoomLevel;\n\n // Check if values actually changed\n const cached = cachedSnapshotRef.current;\n if (\n cached &&\n cached.position.x === currentPosition.x &&\n cached.position.y === currentPosition.y &&\n cached.rotation === currentRotation &&\n cached.zoomLevel === currentZoomLevel\n ) {\n // Return cached snapshot to maintain referential equality\n return cached;\n }\n\n // Create new snapshot only when values changed\n const newSnapshot = {\n position: {...currentPosition},\n rotation: currentRotation,\n zoomLevel: currentZoomLevel,\n };\n cachedSnapshotRef.current = newSnapshot;\n return newSnapshot;\n },\n )\n}\n\n/**\n * Hook to set a custom camera multiplexer on the board.\n *\n * @remarks\n * This hook allows you to replace the board's default camera mux with a custom implementation.\n * Useful when you need custom input coordination, animation control, or state-based input blocking.\n *\n * The camera mux is updated whenever the provided `cameraMux` instance changes.\n *\n * @param cameraMux - Custom camera mux implementation to use\n *\n * @example\n * ```tsx\n * function CustomMuxBoard() {\n * const myCustomMux = useMemo(() => {\n * return createCameraMuxWithAnimationAndLock(camera);\n * }, []);\n *\n * useCustomCameraMux(myCustomMux);\n *\n * return <Board />;\n * }\n * ```\n *\n * @category Hooks\n * @see {@link CameraMux} from @ue-too/board for camera mux interface\n */\nexport function useCustomCameraMux(cameraMux: CameraMux) {\n const board = useBoard();\n\n useEffect(()=>{\n board.cameraMux = cameraMux;\n }, [cameraMux, board]);\n}\n\n/**\n * The custom input handling logic is before everything else. To use this hook, you would need to handle the event from the canvas and pass down the result to the `processInputEvent` function. \n * @returns Object containing the `processInputEvent` function\n * @example\n * ```typescript\n * const { processInputEvent } = useCustomInputHandling();\n * \n * const handlePointerDown = (e: React.PointerEvent<HTMLCanvasElement>) => {\n * // custom logic to determine the user input\n * \n * // if the user input is valid, pass it to the `processInputEvent` function\n * // e.g. pass the pan event down the input handling system\n * processInputEvent({\n * type: \"pan\",\n * delta: {\n * x: 10,\n * y: 10,\n * },\n * });\n * }\n * ```\n */\nexport function useCustomInputHandling(){\n const board = useBoard();\n\n const processInputEvent = useCallback((input: OutputEvent) => {\n board.inputOrchestrator.processInputEvent(input);\n }, [board]);\n\n useEffect(()=>{\n board.disableEventListeners();\n\n return () => {\n board.enableEventListeners();\n };\n }, [board])\n\n return {\n processInputEvent\n };\n}\n\n\n\n/**\n * React context for sharing a Board instance across components.\n * @internal\n */\nconst BoardContext = createContext<Boardify | null>(null);\n\n/**\n * Provider component for sharing a Board instance across the component tree.\n *\n * @remarks\n * This component creates a single Board instance and makes it available to all child\n * components via the {@link useBoard} hook. This is the recommended way to use the\n * board in React applications when you need to access it from multiple components.\n *\n * The board instance is created once when the provider mounts and persists for the\n * lifetime of the provider.\n *\n * @param props - Component props\n * @param props.children - Child components that will have access to the board\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <BoardProvider>\n * <Board width={800} height={600} />\n * <CameraControls />\n * <CameraStateDisplay />\n * </BoardProvider>\n * );\n * }\n * ```\n *\n * @category Components\n * @see {@link useBoard} for accessing the board instance\n */\nexport function BoardProvider({children}: {children: React.ReactNode}) {\n const board = useMemo(() => new Boardify(), []);\n return <BoardContext.Provider value={board}>{children}</BoardContext.Provider>;\n}\n\n/**\n * Hook to access the Board instance from context.\n *\n * @remarks\n * This hook retrieves the Board instance provided by {@link BoardProvider}.\n * It must be used within a component that is a descendant of BoardProvider,\n * otherwise it will throw an error.\n *\n * @returns The Board instance from context\n * @throws Error if used outside of BoardProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const board = useBoard();\n *\n * useEffect(() => {\n * // Configure board\n * board.camera.boundaries = { min: { x: -1000, y: -1000 }, max: { x: 1000, y: 1000 } };\n * }, [board]);\n *\n * return <div>Board ready</div>;\n * }\n * ```\n *\n * @category Hooks\n * @see {@link BoardProvider} for providing the board instance\n */\nexport function useBoard() {\n const board = useContext(BoardContext);\n if (board == null) {\n throw new Error('Board Provider not found');\n }\n return board;\n}\n\n/**\n * Hook to access the camera instance from the Board context.\n *\n * @remarks\n * This is a convenience hook that returns the camera from the board instance.\n * Equivalent to calling `useBoard().camera` but more concise.\n *\n * @returns The camera instance from the board\n * @throws Error if used outside of BoardProvider\n *\n * @example\n * ```tsx\n * function CameraConfig() {\n * const camera = useBoardCamera();\n *\n * useEffect(() => {\n * camera.setMinZoomLevel(0.5);\n * camera.setMaxZoomLevel(4.0);\n * }, [camera]);\n *\n * return null;\n * }\n * ```\n *\n * @category Hooks\n * @see {@link useBoard} for accessing the full board instance\n */\nexport function useBoardCamera() {\n const board = useBoard();\n return board.camera;\n}\n\nexport function useCanvasDimension() {\n const board = useBoard();\n\n return useSyncExternalStore((cb) => board.onCanvasDimensionChange(cb), ()=> {\n return board.canvasDimensions\n });\n}\n\nexport function useCoordinateConversion() {\n const board = useBoard();\n return useCallback((pointInWindow: Point)=>{\n return board.convertWindowPoint2WorldCoord(pointInWindow);\n }, [board]);\n}\n\n\nexport function useCustomKMTEventParser(eventParser: KMTEventParser) {\n const board = useBoard();\n\n useEffect(()=>{\n board.kmtParser = eventParser;\n }, [eventParser, board]);\n}\n\nexport function useCustomTouchEventParser(eventParser: TouchEventParser) {\n const board = useBoard();\n\n useEffect(()=>{\n board.touchParser = eventParser;\n }, [eventParser, board]);\n}\n",
12
- "import {useRef, useCallback, useSyncExternalStore, useMemo, useState} from \"react\";\nimport {useAnimationFrame, useAnimationFrameWithBoard} from \"../hooks/useAnimationFrame\";\nimport {BoardProvider, useBoard, useBoardCameraState, useCustomCameraMux, useCustomInputHandling} from \"../hooks/useBoardify\";\nimport { Point, PointCal } from \"@ue-too/math\";\nimport type { CameraMux } from \"@ue-too/board/camera\";\nimport { useCanvasProxyWithRef } from \"../hooks/useCanvasProxy\";\nimport { useEffect } from \"react\";\nimport { OutputEvent } from \"@ue-too/board\";\n/**\n * Props for the Board component.\n *\n * @category Components\n */\nexport type BoardProps = {\n /** Enable fullscreen mode (canvas resizes with window) */\n fullScreen?: boolean;\n /** Canvas width in pixels */\n width?: number;\n /** Canvas height in pixels */\n height?: number;\n /** Callback function for drawing on each animation frame */\n animationCallback?: (timestamp: number, ctx: CanvasRenderingContext2D) => void;\n /** Child components that can access the board via hooks */\n children?: React.ReactNode;\n}\n\n/**\n * Internal Board component that renders the canvas element.\n *\n * @remarks\n * This component must be used within a {@link BoardProvider} to access the board instance.\n * It handles canvas attachment/detachment and integrates the animation loop.\n *\n * @internal\n */\nfunction Board({width, height, fullScreen, animationCallback: animationCallbackProp}: BoardProps) {\n\n const board = useBoard();\n\n useAnimationFrameWithBoard(animationCallbackProp);\n\n // const {processInputEvent} = useCustomInputHandling();\n\n // const keyboardMouseInput = useMemo(() => {console.log(\"new KeyboardMouseInput\"); return new KeyboardMouseInput(processInputEvent)}, [processInputEvent]);\n\n return (\n <canvas\n width={width}\n height={height}\n ref={(ref) => {\n if (ref == null) {\n board.tearDown();\n return;\n }\n\n board.attach(ref);\n }}\n // onPointerDown={(e: React.PointerEvent<HTMLCanvasElement>) => keyboardMouseInput.pointerdownHandler(e.nativeEvent)}\n // onPointerMove={(e: React.PointerEvent<HTMLCanvasElement>) => keyboardMouseInput.pointermoveHandler(e.nativeEvent)}\n // onPointerUp={(e: React.PointerEvent<HTMLCanvasElement>) => keyboardMouseInput.pointerupHandler(e.nativeEvent)}\n />\n );\n}\n\n/**\n * Main Board component with provider wrapper for React applications.\n *\n * @remarks\n * This component provides a complete infinite canvas solution for React. It combines:\n * - A {@link BoardProvider} to share the board instance across components\n * - A canvas element configured with the @ue-too/board package\n * - An integrated animation loop for drawing\n * - Support for child components that can access board state and controls\n *\n * ## Features\n *\n * - **Infinite Canvas**: Pan, zoom, and rotate with mouse/touch input\n * - **Animation Loop**: Automatic rendering loop with customizable draw callback\n * - **React Integration**: Use hooks to access camera state and controls\n * - **Fullscreen Support**: Optional auto-resize with window\n * - **Type-Safe**: Full TypeScript support\n *\n * ## Usage Pattern\n *\n * 1. Render the Board component\n * 2. Provide an animation callback for drawing\n * 3. Use child components with board hooks for UI controls\n * 4. Access camera state reactively via hooks\n *\n * @param props - Component props\n * @param props.width - Canvas width in pixels (default: auto)\n * @param props.height - Canvas height in pixels (default: auto)\n * @param props.fullScreen - Enable fullscreen mode (canvas resizes with window)\n * @param props.animationCallback - Function called on each frame with timestamp and context\n * @param props.children - Child components that can use board hooks\n *\n * @example\n * Basic usage with drawing\n * ```tsx\n * function App() {\n * return (\n * <Board\n * width={800}\n * height={600}\n * animationCallback={(timestamp, ctx) => {\n * // Draw a rectangle at world position (0, 0)\n * ctx.fillStyle = 'blue';\n * ctx.fillRect(0, 0, 100, 100);\n * }}\n * />\n * );\n * }\n * ```\n *\n * @example\n * With child components and camera controls\n * ```tsx\n * function CameraControls() {\n * const { panToWorld, zoomTo } = useCameraInput();\n * const position = useBoardCameraState('position');\n *\n * return (\n * <div style={{ position: 'absolute', top: 10, left: 10 }}>\n * <p>Position: ({position.x.toFixed(0)}, {position.y.toFixed(0)})</p>\n * <button onClick={() => panToWorld({ x: 0, y: 0 })}>Center</button>\n * <button onClick={() => zoomTo(1.0)}>Reset Zoom</button>\n * </div>\n * );\n * }\n *\n * function App() {\n * return (\n * <Board width={800} height={600} animationCallback={drawScene}>\n * <CameraControls />\n * </Board>\n * );\n * }\n * ```\n *\n * @example\n * Fullscreen mode\n * ```tsx\n * function App() {\n * return (\n * <Board\n * fullScreen\n * animationCallback={(timestamp, ctx) => {\n * // Canvas automatically resizes to window size\n * ctx.fillStyle = 'green';\n * ctx.fillRect(-50, -50, 100, 100);\n * }}\n * />\n * );\n * }\n * ```\n *\n * @category Components\n * @see {@link useBoardCameraState} for accessing camera state\n * @see {@link useCameraInput} for camera control functions\n * @see {@link useBoard} for accessing the board instance\n */\nexport default function BoardWrapperWithChildren({width, height, fullScreen, animationCallback: animationCallbackProp, children}: BoardProps) {\n return (\n <BoardProvider>\n <Board width={width} height={height} fullScreen={fullScreen} animationCallback={animationCallbackProp} />\n {children}\n </BoardProvider>\n );\n}\n\nclass KeyboardMouseInput{\n\n private isPanning: boolean;\n private panStartPoint: Point;\n private processInputEvent: (input: OutputEvent) => void;\n\n constructor(processInputEvent: (input: OutputEvent) => void){\n this.isPanning = false;\n this.panStartPoint = {x: 0, y: 0};\n this.processInputEvent = processInputEvent;\n this.bindFunctions();\n }\n\n bindFunctions(): void {\n this.pointerdownHandler = this.pointerdownHandler.bind(this);\n this.pointermoveHandler = this.pointermoveHandler.bind(this);\n this.pointerupHandler = this.pointerupHandler.bind(this);\n }\n\n pointerdownHandler(event: PointerEvent): void {\n if(event.pointerType !== \"mouse\"){\n return;\n }\n this.isPanning = true;\n this.panStartPoint = {x: event.clientX, y: event.clientY};\n }\n\n pointermoveHandler(event: PointerEvent): void {\n if(!this.isPanning){\n return;\n }\n const curPosition = {x: event.clientX, y: event.clientY};\n const diff = PointCal.subVector(this.panStartPoint, curPosition);\n this.processInputEvent({\n type: 'pan',\n delta: diff,\n });\n this.panStartPoint = curPosition;\n }\n\n pointerupHandler(event: PointerEvent): void {\n if(event.pointerType !== \"mouse\"){\n return;\n }\n this.isPanning = false;\n }\n\n}\n",
13
- "import { CanvasProxy } from \"@ue-too/board\";\nimport { useEffect, useState, useRef, useCallback } from \"react\";\n\n\nexport function useCanvasProxy() {\n const [canvasProxy, _] = useState(() => new CanvasProxy());\n\n return canvasProxy;\n}\n\nexport function useCanvasProxyWithRef() {\n const canvasProxy = useCanvasProxy();\n const refCallback = useCallback((canvas: HTMLCanvasElement | null)=>{\n if(canvas == null){\n canvasProxy.tearDown();\n return;\n }\n canvasProxy.attach(canvas);\n }, [canvasProxy]);\n\n return {\n canvasProxy,\n refCallback,\n };\n}\n"
10
+ "import { OutputEvent } from '@ue-too/board';\nimport type { CameraMux } from '@ue-too/board/camera';\nimport { Point, PointCal } from '@ue-too/math';\nimport {\n useCallback,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { useEffect } from 'react';\n\nimport {\n useAnimationFrame,\n useAnimationFrameWithBoard,\n} from '../hooks/useAnimationFrame';\nimport {\n BoardProvider,\n useBoard,\n useBoardCameraState,\n useCustomCameraMux,\n useCustomInputHandling,\n} from '../hooks/useBoardify';\nimport { useCanvasProxyWithRef } from '../hooks/useCanvasProxy';\n\n/**\n * Props for the Board component.\n *\n * @category Components\n */\nexport type BoardProps = {\n /** Enable fullscreen mode (canvas resizes with window) */\n fullScreen?: boolean;\n /** Canvas width in pixels */\n width?: number;\n /** Canvas height in pixels */\n height?: number;\n /** Callback function for drawing on each animation frame */\n animationCallback?: (\n timestamp: number,\n ctx: CanvasRenderingContext2D\n ) => void;\n /** Child components that can access the board via hooks */\n children?: React.ReactNode;\n};\n\n/**\n * Internal Board component that renders the canvas element.\n *\n * @remarks\n * This component must be used within a {@link BoardProvider} to access the board instance.\n * It handles canvas attachment/detachment and integrates the animation loop.\n *\n * @internal\n */\nfunction Board({\n width,\n height,\n fullScreen,\n animationCallback: animationCallbackProp,\n}: BoardProps) {\n const board = useBoard();\n\n useAnimationFrameWithBoard(animationCallbackProp);\n\n // const {processInputEvent} = useCustomInputHandling();\n\n // const keyboardMouseInput = useMemo(() => {console.log(\"new KeyboardMouseInput\"); return new KeyboardMouseInput(processInputEvent)}, [processInputEvent]);\n\n return (\n <canvas\n width={width}\n height={height}\n ref={ref => {\n if (ref == null) {\n board.tearDown();\n return;\n }\n\n board.attach(ref);\n }}\n // onPointerDown={(e: React.PointerEvent<HTMLCanvasElement>) => keyboardMouseInput.pointerdownHandler(e.nativeEvent)}\n // onPointerMove={(e: React.PointerEvent<HTMLCanvasElement>) => keyboardMouseInput.pointermoveHandler(e.nativeEvent)}\n // onPointerUp={(e: React.PointerEvent<HTMLCanvasElement>) => keyboardMouseInput.pointerupHandler(e.nativeEvent)}\n />\n );\n}\n\n/**\n * Main Board component with provider wrapper for React applications.\n *\n * @remarks\n * This component provides a complete infinite canvas solution for React. It combines:\n * - A {@link BoardProvider} to share the board instance across components\n * - A canvas element configured with the @ue-too/board package\n * - An integrated animation loop for drawing\n * - Support for child components that can access board state and controls\n *\n * ## Features\n *\n * - **Infinite Canvas**: Pan, zoom, and rotate with mouse/touch input\n * - **Animation Loop**: Automatic rendering loop with customizable draw callback\n * - **React Integration**: Use hooks to access camera state and controls\n * - **Fullscreen Support**: Optional auto-resize with window\n * - **Type-Safe**: Full TypeScript support\n *\n * ## Usage Pattern\n *\n * 1. Render the Board component\n * 2. Provide an animation callback for drawing\n * 3. Use child components with board hooks for UI controls\n * 4. Access camera state reactively via hooks\n *\n * @param props - Component props\n * @param props.width - Canvas width in pixels (default: auto)\n * @param props.height - Canvas height in pixels (default: auto)\n * @param props.fullScreen - Enable fullscreen mode (canvas resizes with window)\n * @param props.animationCallback - Function called on each frame with timestamp and context\n * @param props.children - Child components that can use board hooks\n *\n * @example\n * Basic usage with drawing\n * ```tsx\n * function App() {\n * return (\n * <Board\n * width={800}\n * height={600}\n * animationCallback={(timestamp, ctx) => {\n * // Draw a rectangle at world position (0, 0)\n * ctx.fillStyle = 'blue';\n * ctx.fillRect(0, 0, 100, 100);\n * }}\n * />\n * );\n * }\n * ```\n *\n * @example\n * With child components and camera controls\n * ```tsx\n * function CameraControls() {\n * const { panToWorld, zoomTo } = useCameraInput();\n * const position = useBoardCameraState('position');\n *\n * return (\n * <div style={{ position: 'absolute', top: 10, left: 10 }}>\n * <p>Position: ({position.x.toFixed(0)}, {position.y.toFixed(0)})</p>\n * <button onClick={() => panToWorld({ x: 0, y: 0 })}>Center</button>\n * <button onClick={() => zoomTo(1.0)}>Reset Zoom</button>\n * </div>\n * );\n * }\n *\n * function App() {\n * return (\n * <Board width={800} height={600} animationCallback={drawScene}>\n * <CameraControls />\n * </Board>\n * );\n * }\n * ```\n *\n * @example\n * Fullscreen mode\n * ```tsx\n * function App() {\n * return (\n * <Board\n * fullScreen\n * animationCallback={(timestamp, ctx) => {\n * // Canvas automatically resizes to window size\n * ctx.fillStyle = 'green';\n * ctx.fillRect(-50, -50, 100, 100);\n * }}\n * />\n * );\n * }\n * ```\n *\n * @category Components\n * @see {@link useBoardCameraState} for accessing camera state\n * @see {@link useCameraInput} for camera control functions\n * @see {@link useBoard} for accessing the board instance\n */\nexport default function BoardWrapperWithChildren({\n width,\n height,\n fullScreen,\n animationCallback: animationCallbackProp,\n children,\n}: BoardProps) {\n return (\n <BoardProvider>\n <Board\n width={width}\n height={height}\n fullScreen={fullScreen}\n animationCallback={animationCallbackProp}\n />\n {children}\n </BoardProvider>\n );\n}\n\nclass KeyboardMouseInput {\n private isPanning: boolean;\n private panStartPoint: Point;\n private processInputEvent: (input: OutputEvent) => void;\n\n constructor(processInputEvent: (input: OutputEvent) => void) {\n this.isPanning = false;\n this.panStartPoint = { x: 0, y: 0 };\n this.processInputEvent = processInputEvent;\n this.bindFunctions();\n }\n\n bindFunctions(): void {\n this.pointerdownHandler = this.pointerdownHandler.bind(this);\n this.pointermoveHandler = this.pointermoveHandler.bind(this);\n this.pointerupHandler = this.pointerupHandler.bind(this);\n }\n\n pointerdownHandler(event: PointerEvent): void {\n if (event.pointerType !== 'mouse') {\n return;\n }\n this.isPanning = true;\n this.panStartPoint = { x: event.clientX, y: event.clientY };\n }\n\n pointermoveHandler(event: PointerEvent): void {\n if (!this.isPanning) {\n return;\n }\n const curPosition = { x: event.clientX, y: event.clientY };\n const diff = PointCal.subVector(this.panStartPoint, curPosition);\n this.processInputEvent({\n type: 'pan',\n delta: diff,\n });\n this.panStartPoint = curPosition;\n }\n\n pointerupHandler(event: PointerEvent): void {\n if (event.pointerType !== 'mouse') {\n return;\n }\n this.isPanning = false;\n }\n}\n",
11
+ "import { useCallback, useEffect, useRef } from 'react';\n\nimport { useBoard } from './useBoardify';\n\n/**\n * Hook to run a callback on every animation frame.\n *\n * @remarks\n * This hook uses `requestAnimationFrame` to execute a callback repeatedly for smooth animations.\n * The animation loop starts when the component mounts and stops when it unmounts, automatically\n * cleaning up the animation frame request.\n *\n * **Performance Note**: The callback is called on every frame, so ensure your callback is\n * optimized to avoid performance issues. The callback dependency should be stable to prevent\n * restarting the animation loop unnecessarily.\n *\n * @param callback - Function to call on each animation frame, receives the current timestamp\n *\n * @example\n * ```tsx\n * function AnimatedComponent() {\n * const [rotation, setRotation] = useState(0);\n *\n * useAnimationFrame((timestamp) => {\n * // Rotate 45 degrees per second\n * setRotation((prev) => prev + (Math.PI / 4) * (1 / 60));\n * });\n *\n * return <div style={{ transform: `rotate(${rotation}rad)` }}>Spinning!</div>;\n * }\n * ```\n *\n * @category Hooks\n * @see {@link useAnimationFrameWithBoard} for board-integrated animation loop\n */\nexport function useAnimationFrame(callback: (timestamp: number) => void) {\n const animationFrameRef = useRef<number | null>(null);\n\n useEffect(() => {\n const step = (timestamp: number) => {\n callback(timestamp);\n animationFrameRef.current = requestAnimationFrame(step);\n };\n\n // Start the animation loop\n animationFrameRef.current = requestAnimationFrame(step);\n\n // Cleanup function\n return () => {\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n };\n }, [callback]);\n}\n\n/**\n * Hook to run an animation loop integrated with the Board's step function.\n *\n * @remarks\n * This hook automatically calls `board.step(timestamp)` on every frame to update the board's\n * camera transform, then invokes your callback with the timestamp and canvas context.\n * This is the recommended way to implement drawing logic for board-based applications.\n *\n * The hook handles:\n * - Calling `board.step()` to update camera transforms\n * - Providing the canvas context for drawing\n * - Warning if context is not available\n * - Cleaning up the animation loop on unmount\n *\n * **Typical Usage Pattern**:\n * 1. Board calls `step()` to update transforms\n * 2. Your callback draws on the canvas\n * 3. Browser paints the frame\n * 4. Repeat next frame\n *\n * @param callback - Optional function to call after board.step(), receives timestamp and canvas context\n *\n * @example\n * ```tsx\n * function MyBoard() {\n * useAnimationFrameWithBoard((timestamp, ctx) => {\n * // Draw a rectangle at world position (0, 0)\n * ctx.fillStyle = 'red';\n * ctx.fillRect(0, 0, 100, 100);\n *\n * // Draw a circle that moves\n * const x = Math.sin(timestamp / 1000) * 200;\n * const y = Math.cos(timestamp / 1000) * 200;\n * ctx.fillStyle = 'blue';\n * ctx.beginPath();\n * ctx.arc(x, y, 20, 0, Math.PI * 2);\n * ctx.fill();\n * });\n *\n * return <Board width={800} height={600} />;\n * }\n * ```\n *\n * @category Hooks\n * @see {@link useAnimationFrame} for generic animation frame hook\n */\nexport function useAnimationFrameWithBoard(\n callback?: (timestamp: number, ctx: CanvasRenderingContext2D) => void\n) {\n const board = useBoard();\n\n const animationCallback = useCallback(\n (timestamp: number) => {\n board.step(timestamp);\n const ctx = board.context;\n if (ctx == undefined) {\n console.warn('Canvas context not available');\n return;\n }\n callback?.(timestamp, ctx);\n },\n [callback, board]\n );\n\n useAnimationFrame(animationCallback);\n}\n",
12
+ "import {\n Board as Boardify,\n KMTEventParser,\n KmtInputStateMachine,\n OutputEvent,\n TouchEventParser,\n} from '@ue-too/board';\nimport { CameraMux, CameraState } from '@ue-too/board/camera';\nimport { Point } from '@ue-too/math';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react';\n\n/**\n * Maps camera state keys to their corresponding event names.\n * @internal\n */\ntype StateToEventKey<K extends keyof CameraState> = K extends 'position'\n ? 'pan'\n : K extends 'zoomLevel'\n ? 'zoom'\n : 'rotate';\n\n/**\n * Hook to subscribe to a specific camera state property with automatic re-rendering.\n *\n * @remarks\n * This hook uses React's `useSyncExternalStore` to efficiently subscribe to camera state changes.\n * It only triggers re-renders when the specified property actually changes, and uses caching\n * to maintain referential equality for object values (like position).\n *\n * **Performance**: The hook is optimized to prevent unnecessary re-renders by:\n * - Caching object values (position) to maintain referential equality\n * - Using `useSyncExternalStore` for efficient subscription management\n * - Only subscribing to the specific state property needed\n *\n * @typeParam K - Key of the camera state to subscribe to\n * @param state - The camera state property to track (\"position\", \"rotation\", or \"zoomLevel\")\n * @returns The current value of the specified camera state property\n *\n * @example\n * ```tsx\n * function CameraInfo() {\n * const position = useBoardCameraState('position');\n * const rotation = useBoardCameraState('rotation');\n * const zoomLevel = useBoardCameraState('zoomLevel');\n *\n * return (\n * <div>\n * Position: {position.x}, {position.y}<br/>\n * Rotation: {rotation}<br/>\n * Zoom: {zoomLevel}\n * </div>\n * );\n * }\n * ```\n *\n * @category Hooks\n * @see {@link useAllBoardCameraState} for subscribing to all camera state at once\n */\nexport function useBoardCameraState<K extends keyof CameraState>(\n state: K\n): CameraState[K] {\n const board = useBoard();\n const stateKey = (\n state === 'position' ? 'pan' : state === 'zoomLevel' ? 'zoom' : 'rotate'\n ) as StateToEventKey<K>;\n const cachedPositionRef = useRef<{ x: number; y: number } | null>(null);\n\n return useSyncExternalStore(\n cb => board.camera.on(stateKey, cb),\n () => {\n // For position (object), we need to cache to avoid creating new objects\n if (state === 'position') {\n const currentPosition = board.camera.position;\n const cached = cachedPositionRef.current;\n\n if (\n cached &&\n cached.x === currentPosition.x &&\n cached.y === currentPosition.y\n ) {\n // Return cached snapshot to maintain referential equality\n return cached as CameraState[K];\n }\n\n // Cache the new position object\n const newPosition = { ...currentPosition };\n cachedPositionRef.current = newPosition;\n return newPosition as CameraState[K];\n }\n\n // For primitive values (rotation, zoomLevel), return directly\n // Object.is works correctly for primitives\n return board.camera[state] as CameraState[K];\n }\n );\n}\n\n/**\n * Hook to get camera control functions for programmatic camera manipulation.\n *\n * @remarks\n * This hook provides a stable set of functions to control the camera programmatically.\n * The functions are memoized and only recreate when the board instance changes.\n *\n * All camera operations go through the camera rig, which enforces boundaries,\n * restrictions, and other constraints configured on the board.\n *\n * @returns Object containing camera control functions:\n * - `panToWorld` - Pan camera to a world position\n * - `panToViewPort` - Pan camera to a viewport position\n * - `zoomTo` - Set camera zoom to specific level\n * - `zoomBy` - Adjust camera zoom by delta\n * - `rotateTo` - Set camera rotation to specific angle\n * - `rotateBy` - Adjust camera rotation by delta\n *\n * @example\n * ```tsx\n * function CameraControls() {\n * const { panToWorld, zoomTo, rotateTo } = useCameraInput();\n *\n * return (\n * <div>\n * <button onClick={() => panToWorld({ x: 0, y: 0 })}>\n * Center Camera\n * </button>\n * <button onClick={() => zoomTo(1.0)}>\n * Reset Zoom\n * </button>\n * <button onClick={() => rotateTo(0)}>\n * Reset Rotation\n * </button>\n * </div>\n * );\n * }\n * ```\n *\n * @category Hooks\n */\nexport function useCameraInput() {\n const board = useBoard();\n\n const test = useMemo(() => {\n const cameraRig = board.getCameraRig();\n\n return {\n panByViewPort: (delta: Point) => {\n cameraRig.panByViewPort(delta);\n },\n panByWorld: (delta: Point) => {\n cameraRig.panByWorld(delta);\n },\n panToWorld: (worldPosition: Point) => {\n cameraRig.panToWorld(worldPosition);\n },\n panToViewPort: (viewPortPosition: Point) => {\n cameraRig.panToViewPort(viewPortPosition);\n },\n zoomToAtViewPort: (zoomLevel: number, at: Point) => {\n cameraRig.zoomToAt(zoomLevel, at);\n },\n zoomToAtWorld: (zoomLevel: number, at: Point) => {\n cameraRig.zoomToAtWorld(zoomLevel, at);\n },\n zoomTo: (zoomLevel: number) => {\n cameraRig.zoomTo(zoomLevel);\n },\n zoomBy: (zoomDelta: number) => {\n cameraRig.zoomBy(zoomDelta);\n },\n rotateTo: (rotation: number) => {\n cameraRig.rotateTo(rotation);\n },\n rotateBy: (rotationDelta: number) => {\n cameraRig.rotateBy(rotationDelta);\n },\n };\n }, [board]);\n\n return test;\n}\n\n/**\n * Hook to subscribe to all camera state properties with automatic re-rendering.\n *\n * @remarks\n * This hook provides a snapshot of all camera state (position, rotation, zoomLevel) and\n * re-renders only when any of these values change. It's more efficient than using multiple\n * {@link useBoardCameraState} calls when you need all state properties.\n *\n * **Performance**: The hook uses snapshot caching to maintain referential equality when\n * values haven't changed, preventing unnecessary re-renders in child components.\n *\n * @returns Object containing:\n * - `position` - Current camera position {x, y}\n * - `rotation` - Current camera rotation in radians\n * - `zoomLevel` - Current camera zoom level\n *\n * @example\n * ```tsx\n * function CameraStateDisplay() {\n * const { position, rotation, zoomLevel } = useAllBoardCameraState();\n *\n * return (\n * <div>\n * <h3>Camera State</h3>\n * <p>Position: ({position.x.toFixed(2)}, {position.y.toFixed(2)})</p>\n * <p>Rotation: {rotation.toFixed(2)} rad</p>\n * <p>Zoom: {zoomLevel.toFixed(2)}x</p>\n * </div>\n * );\n * }\n * ```\n *\n * @category Hooks\n * @see {@link useBoardCameraState} for subscribing to individual state properties\n */\nexport function useAllBoardCameraState() {\n const board = useBoard();\n const cachedSnapshotRef = useRef<{\n position: { x: number; y: number };\n rotation: number;\n zoomLevel: number;\n } | null>(null);\n\n return useSyncExternalStore(\n cb => {\n return board.camera.on('all', cb);\n },\n () => {\n const currentPosition = board.camera.position;\n const currentRotation = board.camera.rotation;\n const currentZoomLevel = board.camera.zoomLevel;\n\n // Check if values actually changed\n const cached = cachedSnapshotRef.current;\n if (\n cached &&\n cached.position.x === currentPosition.x &&\n cached.position.y === currentPosition.y &&\n cached.rotation === currentRotation &&\n cached.zoomLevel === currentZoomLevel\n ) {\n // Return cached snapshot to maintain referential equality\n return cached;\n }\n\n // Create new snapshot only when values changed\n const newSnapshot = {\n position: { ...currentPosition },\n rotation: currentRotation,\n zoomLevel: currentZoomLevel,\n };\n cachedSnapshotRef.current = newSnapshot;\n return newSnapshot;\n }\n );\n}\n\n/**\n * Hook to set a custom camera multiplexer on the board.\n *\n * @remarks\n * This hook allows you to replace the board's default camera mux with a custom implementation.\n * Useful when you need custom input coordination, animation control, or state-based input blocking.\n *\n * The camera mux is updated whenever the provided `cameraMux` instance changes.\n *\n * @param cameraMux - Custom camera mux implementation to use\n *\n * @example\n * ```tsx\n * function CustomMuxBoard() {\n * const myCustomMux = useMemo(() => {\n * return createCameraMuxWithAnimationAndLock(camera);\n * }, []);\n *\n * useCustomCameraMux(myCustomMux);\n *\n * return <Board />;\n * }\n * ```\n *\n * @category Hooks\n * @see {@link CameraMux} from @ue-too/board for camera mux interface\n */\nexport function useCustomCameraMux(cameraMux: CameraMux) {\n const board = useBoard();\n\n useEffect(() => {\n board.cameraMux = cameraMux;\n }, [cameraMux, board]);\n}\n\n/**\n * The custom input handling logic is before everything else. To use this hook, you would need to handle the event from the canvas and pass down the result to the `processInputEvent` function.\n * @returns Object containing the `processInputEvent` function\n * @example\n * ```typescript\n * const { processInputEvent } = useCustomInputHandling();\n *\n * const handlePointerDown = (e: React.PointerEvent<HTMLCanvasElement>) => {\n * // custom logic to determine the user input\n *\n * // if the user input is valid, pass it to the `processInputEvent` function\n * // e.g. pass the pan event down the input handling system\n * processInputEvent({\n * type: \"pan\",\n * delta: {\n * x: 10,\n * y: 10,\n * },\n * });\n * }\n * ```\n */\nexport function useCustomInputHandling() {\n const board = useBoard();\n\n const processInputEvent = useCallback(\n (input: OutputEvent) => {\n board.inputOrchestrator.processInputEvent(input);\n },\n [board]\n );\n\n useEffect(() => {\n board.disableEventListeners();\n\n return () => {\n board.enableEventListeners();\n };\n }, [board]);\n\n return {\n processInputEvent,\n };\n}\n\n/**\n * React context for sharing a Board instance across components.\n * @internal\n */\nconst BoardContext = createContext<Boardify | null>(null);\n\n/**\n * Provider component for sharing a Board instance across the component tree.\n *\n * @remarks\n * This component creates a single Board instance and makes it available to all child\n * components via the {@link useBoard} hook. This is the recommended way to use the\n * board in React applications when you need to access it from multiple components.\n *\n * The board instance is created once when the provider mounts and persists for the\n * lifetime of the provider.\n *\n * @param props - Component props\n * @param props.children - Child components that will have access to the board\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <BoardProvider>\n * <Board width={800} height={600} />\n * <CameraControls />\n * <CameraStateDisplay />\n * </BoardProvider>\n * );\n * }\n * ```\n *\n * @category Components\n * @see {@link useBoard} for accessing the board instance\n */\nexport function BoardProvider({ children }: { children: React.ReactNode }) {\n const board = useMemo(() => new Boardify(), []);\n return (\n <BoardContext.Provider value={board}>{children}</BoardContext.Provider>\n );\n}\n\n/**\n * Hook to access the Board instance from context.\n *\n * @remarks\n * This hook retrieves the Board instance provided by {@link BoardProvider}.\n * It must be used within a component that is a descendant of BoardProvider,\n * otherwise it will throw an error.\n *\n * @returns The Board instance from context\n * @throws Error if used outside of BoardProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const board = useBoard();\n *\n * useEffect(() => {\n * // Configure board\n * board.camera.boundaries = { min: { x: -1000, y: -1000 }, max: { x: 1000, y: 1000 } };\n * }, [board]);\n *\n * return <div>Board ready</div>;\n * }\n * ```\n *\n * @category Hooks\n * @see {@link BoardProvider} for providing the board instance\n */\nexport function useBoard() {\n const board = useContext(BoardContext);\n if (board == null) {\n throw new Error('Board Provider not found');\n }\n return board;\n}\n\n/**\n * Hook to access the camera instance from the Board context.\n *\n * @remarks\n * This is a convenience hook that returns the camera from the board instance.\n * Equivalent to calling `useBoard().camera` but more concise.\n *\n * @returns The camera instance from the board\n * @throws Error if used outside of BoardProvider\n *\n * @example\n * ```tsx\n * function CameraConfig() {\n * const camera = useBoardCamera();\n *\n * useEffect(() => {\n * camera.setMinZoomLevel(0.5);\n * camera.setMaxZoomLevel(4.0);\n * }, [camera]);\n *\n * return null;\n * }\n * ```\n *\n * @category Hooks\n * @see {@link useBoard} for accessing the full board instance\n */\nexport function useBoardCamera() {\n const board = useBoard();\n return board.camera;\n}\n\nexport function useCanvasDimension() {\n const board = useBoard();\n\n return useSyncExternalStore(\n cb => board.onCanvasDimensionChange(cb),\n () => {\n return board.canvasDimensions;\n }\n );\n}\n\nexport function useCoordinateConversion() {\n const board = useBoard();\n return useCallback(\n (pointInWindow: Point) => {\n return board.convertWindowPoint2WorldCoord(pointInWindow);\n },\n [board]\n );\n}\n\nexport function useCustomKMTEventParser(eventParser: KMTEventParser) {\n const board = useBoard();\n\n useEffect(() => {\n board.kmtParser = eventParser;\n }, [eventParser, board]);\n}\n\nexport function useCustomTouchEventParser(eventParser: TouchEventParser) {\n const board = useBoard();\n\n useEffect(() => {\n board.touchParser = eventParser;\n }, [eventParser, board]);\n}\n",
13
+ "import { CanvasProxy } from '@ue-too/board';\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nexport function useCanvasProxy() {\n const [canvasProxy, _] = useState(() => new CanvasProxy());\n\n return canvasProxy;\n}\n\nexport function useCanvasProxyWithRef() {\n const canvasProxy = useCanvasProxy();\n const refCallback = useCallback(\n (canvas: HTMLCanvasElement | null) => {\n if (canvas == null) {\n canvasProxy.tearDown();\n return;\n }\n canvasProxy.attach(canvas);\n },\n [canvasProxy]\n );\n\n return {\n canvasProxy,\n refCallback,\n };\n}\n"
14
14
  ],
15
- "mappings": "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",
16
- "debugId": "6B2CEE4C23D45A0964756E2164756E21",
15
+ "mappings": "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",
16
+ "debugId": "E2568A6219EBF3E864756E2164756E21",
17
17
  "names": []
18
18
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ue-too/board-react-adapter",
3
3
  "type": "module",
4
- "version": "0.14.1",
4
+ "version": "0.15.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/ue-too/ue-too.git"
@@ -23,8 +23,8 @@
23
23
  "types": "./index.d.ts",
24
24
  "module": "./index.js",
25
25
  "dependencies": {
26
- "@ue-too/board": "^0.14.1",
27
- "@ue-too/math": "^0.14.1"
26
+ "@ue-too/board": "^0.15.0",
27
+ "@ue-too/math": "^0.15.0"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "react": "^19.0.1",