@markup-canvas/core 1.0.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.
Files changed (194) hide show
  1. package/README.md +245 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/lib/MarkupCanvas.d.ts +78 -0
  4. package/dist/lib/canvas/calcVisibleArea.d.ts +10 -0
  5. package/dist/lib/canvas/checkContainerDimensions.d.ts +1 -0
  6. package/dist/lib/canvas/config.d.ts +2 -0
  7. package/dist/lib/canvas/createCanvas.d.ts +2 -0
  8. package/dist/lib/canvas/createCanvasLayers.d.ts +6 -0
  9. package/dist/lib/canvas/getCanvasBounds.d.ts +2 -0
  10. package/dist/lib/canvas/getCanvasMethods.d.ts +12 -0
  11. package/dist/lib/canvas/getEmptyBounds.d.ts +2 -0
  12. package/dist/lib/canvas/index.d.ts +3 -0
  13. package/dist/lib/canvas/moveExistingContent.d.ts +1 -0
  14. package/dist/lib/canvas/setupCanvasContainer.d.ts +1 -0
  15. package/dist/lib/canvas/setupContentLayer.d.ts +1 -0
  16. package/dist/lib/canvas/setupTransformLayer.d.ts +2 -0
  17. package/dist/lib/config/constants.d.ts +2 -0
  18. package/dist/lib/config/createMarkupCanvasConfig.d.ts +2 -0
  19. package/dist/lib/constants.d.ts +7 -0
  20. package/dist/lib/events/EventEmitter.d.ts +7 -0
  21. package/dist/lib/events/constants.d.ts +7 -0
  22. package/dist/lib/events/index.d.ts +6 -0
  23. package/dist/lib/events/keyboard/handleKeyDown.d.ts +4 -0
  24. package/dist/lib/events/keyboard/handleKeyUp.d.ts +6 -0
  25. package/dist/lib/events/keyboard/setupKeyboardEvents.d.ts +2 -0
  26. package/dist/lib/events/keyboard/setupKeyboardNavigation.d.ts +2 -0
  27. package/dist/lib/events/mouse/handleClickToZoom.d.ts +2 -0
  28. package/dist/lib/events/mouse/handleMouseDown.d.ts +11 -0
  29. package/dist/lib/events/mouse/handleMouseLeave.d.ts +5 -0
  30. package/dist/lib/events/mouse/handleMouseMove.d.ts +7 -0
  31. package/dist/lib/events/mouse/handleMouseUp.d.ts +7 -0
  32. package/dist/lib/events/mouse/setupMouseDrag.d.ts +4 -0
  33. package/dist/lib/events/mouse/setupMouseEvents.d.ts +4 -0
  34. package/dist/lib/events/touch/getTouchCenter.d.ts +4 -0
  35. package/dist/lib/events/touch/getTouchDistance.d.ts +1 -0
  36. package/dist/lib/events/touch/handleTouchEnd.d.ts +2 -0
  37. package/dist/lib/events/touch/handleTouchMove.d.ts +2 -0
  38. package/dist/lib/events/touch/handleTouchStart.d.ts +2 -0
  39. package/dist/lib/events/touch/setupTouchEvents.d.ts +2 -0
  40. package/dist/lib/events/trackpad/createTrackpadPanHandler.d.ts +4 -0
  41. package/dist/lib/events/trackpad/detectTrackpadGesture.d.ts +2 -0
  42. package/dist/lib/events/utils/getAdaptiveZoomSpeed.d.ts +2 -0
  43. package/dist/lib/events/utils/resetClickState.d.ts +4 -0
  44. package/dist/lib/events/utils/resetDragState.d.ts +5 -0
  45. package/dist/lib/events/utils/updateCursor.d.ts +2 -0
  46. package/dist/lib/events/wheel/handleWheel.d.ts +2 -0
  47. package/dist/lib/events/wheel/setupWheelEvents.d.ts +2 -0
  48. package/dist/lib/events/wheel/setupWheelHandler.d.ts +2 -0
  49. package/dist/lib/helpers/index.d.ts +6 -0
  50. package/dist/lib/helpers/withClampedZoom.d.ts +2 -0
  51. package/dist/lib/helpers/withDebounce.d.ts +1 -0
  52. package/dist/lib/helpers/withFeatureEnabled.d.ts +2 -0
  53. package/dist/lib/helpers/withRAF.d.ts +4 -0
  54. package/dist/lib/helpers/withRulerCheck.d.ts +18 -0
  55. package/dist/lib/helpers/withRulerOffset.d.ts +3 -0
  56. package/dist/lib/matrix/canvasToContent.d.ts +2 -0
  57. package/dist/lib/matrix/clampZoom.d.ts +2 -0
  58. package/dist/lib/matrix/contentToCanvas.d.ts +2 -0
  59. package/dist/lib/matrix/createMatrix.d.ts +1 -0
  60. package/dist/lib/matrix/createMatrixString.d.ts +1 -0
  61. package/dist/lib/matrix/getZoomToMouseTransform.d.ts +2 -0
  62. package/dist/lib/matrix/index.d.ts +5 -0
  63. package/dist/lib/rulers/RulerElements.d.ts +6 -0
  64. package/dist/lib/rulers/constants.d.ts +19 -0
  65. package/dist/lib/rulers/createCornerBox.d.ts +2 -0
  66. package/dist/lib/rulers/createGridOverlay.d.ts +2 -0
  67. package/dist/lib/rulers/createHorizontalRuler.d.ts +2 -0
  68. package/dist/lib/rulers/createRulerElements.d.ts +3 -0
  69. package/dist/lib/rulers/createRulers.d.ts +2 -0
  70. package/dist/lib/rulers/createVerticalRuler.d.ts +2 -0
  71. package/dist/lib/rulers/index.d.ts +2 -0
  72. package/dist/lib/rulers/setupRulerEvents.d.ts +2 -0
  73. package/dist/lib/rulers/ticks/calculateTickSpacing.d.ts +1 -0
  74. package/dist/lib/rulers/ticks/createHorizontalTick.d.ts +2 -0
  75. package/dist/lib/rulers/ticks/createVerticalTick.d.ts +2 -0
  76. package/dist/lib/rulers/ticks/index.d.ts +3 -0
  77. package/dist/lib/rulers/updateGrid.d.ts +1 -0
  78. package/dist/lib/rulers/updateHorizontalRuler.d.ts +2 -0
  79. package/dist/lib/rulers/updateRulers.d.ts +2 -0
  80. package/dist/lib/rulers/updateVerticalRuler.d.ts +2 -0
  81. package/dist/lib/transform/applyTransform.d.ts +1 -0
  82. package/dist/lib/transform/applyZoomToCanvas.d.ts +2 -0
  83. package/dist/lib/transform/hardware-acceleration.d.ts +1 -0
  84. package/dist/lib/transform/index.d.ts +2 -0
  85. package/dist/lib/transition/disableTransition.d.ts +7 -0
  86. package/dist/lib/transition/enableTransition.d.ts +7 -0
  87. package/dist/lib/transition/index.d.ts +3 -0
  88. package/dist/lib/transition/withTransition.d.ts +2 -0
  89. package/dist/markup-canvas.cjs.js +2000 -0
  90. package/dist/markup-canvas.esm.js +1995 -0
  91. package/dist/markup-canvas.umd.js +2003 -0
  92. package/dist/markup-canvas.umd.min.js +1 -0
  93. package/dist/types/canvas.d.ts +86 -0
  94. package/dist/types/config.d.ts +38 -0
  95. package/dist/types/events.d.ts +33 -0
  96. package/dist/types/index.d.ts +5 -0
  97. package/dist/types/matrix.d.ts +17 -0
  98. package/dist/types/rulers.d.ts +31 -0
  99. package/dist/umd.d.ts +1 -0
  100. package/package.json +56 -0
  101. package/src/index.ts +19 -0
  102. package/src/lib/MarkupCanvas.ts +434 -0
  103. package/src/lib/canvas/calcVisibleArea.ts +20 -0
  104. package/src/lib/canvas/checkContainerDimensions.ts +20 -0
  105. package/src/lib/canvas/config.ts +29 -0
  106. package/src/lib/canvas/createCanvas.ts +61 -0
  107. package/src/lib/canvas/createCanvasLayers.ts +39 -0
  108. package/src/lib/canvas/getCanvasBounds.ts +68 -0
  109. package/src/lib/canvas/getCanvasMethods.ts +104 -0
  110. package/src/lib/canvas/getEmptyBounds.ts +22 -0
  111. package/src/lib/canvas/index.ts +3 -0
  112. package/src/lib/canvas/moveExistingContent.ts +9 -0
  113. package/src/lib/canvas/setupCanvasContainer.ts +22 -0
  114. package/src/lib/canvas/setupContentLayer.ts +6 -0
  115. package/src/lib/canvas/setupTransformLayer.ts +15 -0
  116. package/src/lib/config/constants.ts +56 -0
  117. package/src/lib/config/createMarkupCanvasConfig.ts +56 -0
  118. package/src/lib/constants.ts +16 -0
  119. package/src/lib/events/EventEmitter.ts +34 -0
  120. package/src/lib/events/constants.ts +9 -0
  121. package/src/lib/events/index.ts +6 -0
  122. package/src/lib/events/keyboard/handleKeyDown.ts +18 -0
  123. package/src/lib/events/keyboard/handleKeyUp.ts +28 -0
  124. package/src/lib/events/keyboard/setupKeyboardEvents.ts +114 -0
  125. package/src/lib/events/keyboard/setupKeyboardNavigation.ts +115 -0
  126. package/src/lib/events/mouse/handleClickToZoom.ts +54 -0
  127. package/src/lib/events/mouse/handleMouseDown.ts +45 -0
  128. package/src/lib/events/mouse/handleMouseLeave.ts +18 -0
  129. package/src/lib/events/mouse/handleMouseMove.ts +57 -0
  130. package/src/lib/events/mouse/handleMouseUp.ts +40 -0
  131. package/src/lib/events/mouse/setupMouseDrag.ts +159 -0
  132. package/src/lib/events/mouse/setupMouseEvents.ts +158 -0
  133. package/src/lib/events/touch/getTouchCenter.ts +6 -0
  134. package/src/lib/events/touch/getTouchDistance.ts +5 -0
  135. package/src/lib/events/touch/handleTouchEnd.ts +9 -0
  136. package/src/lib/events/touch/handleTouchMove.ts +58 -0
  137. package/src/lib/events/touch/handleTouchStart.ts +14 -0
  138. package/src/lib/events/touch/setupTouchEvents.ts +40 -0
  139. package/src/lib/events/trackpad/createTrackpadPanHandler.ts +35 -0
  140. package/src/lib/events/trackpad/detectTrackpadGesture.ts +22 -0
  141. package/src/lib/events/utils/getAdaptiveZoomSpeed.ts +21 -0
  142. package/src/lib/events/utils/resetClickState.ts +4 -0
  143. package/src/lib/events/utils/resetDragState.ts +17 -0
  144. package/src/lib/events/utils/updateCursor.ts +20 -0
  145. package/src/lib/events/wheel/handleWheel.ts +67 -0
  146. package/src/lib/events/wheel/setupWheelEvents.ts +24 -0
  147. package/src/lib/events/wheel/setupWheelHandler.ts +24 -0
  148. package/src/lib/helpers/index.ts +12 -0
  149. package/src/lib/helpers/withClampedZoom.ts +7 -0
  150. package/src/lib/helpers/withDebounce.ts +15 -0
  151. package/src/lib/helpers/withFeatureEnabled.ts +8 -0
  152. package/src/lib/helpers/withRAF.ts +38 -0
  153. package/src/lib/helpers/withRulerCheck.ts +52 -0
  154. package/src/lib/helpers/withRulerOffset.ts +14 -0
  155. package/src/lib/matrix/canvasToContent.ts +20 -0
  156. package/src/lib/matrix/clampZoom.ts +5 -0
  157. package/src/lib/matrix/contentToCanvas.ts +20 -0
  158. package/src/lib/matrix/createMatrix.ts +3 -0
  159. package/src/lib/matrix/createMatrixString.ts +3 -0
  160. package/src/lib/matrix/getZoomToMouseTransform.ts +46 -0
  161. package/src/lib/matrix/index.ts +5 -0
  162. package/src/lib/rulers/RulerElements.ts +6 -0
  163. package/src/lib/rulers/constants.ts +23 -0
  164. package/src/lib/rulers/createCornerBox.ts +27 -0
  165. package/src/lib/rulers/createGridOverlay.ts +22 -0
  166. package/src/lib/rulers/createHorizontalRuler.ts +24 -0
  167. package/src/lib/rulers/createRulerElements.ts +27 -0
  168. package/src/lib/rulers/createRulers.ts +94 -0
  169. package/src/lib/rulers/createVerticalRuler.ts +24 -0
  170. package/src/lib/rulers/index.ts +2 -0
  171. package/src/lib/rulers/setupRulerEvents.ts +23 -0
  172. package/src/lib/rulers/ticks/calculateTickSpacing.ts +15 -0
  173. package/src/lib/rulers/ticks/createHorizontalTick.ts +41 -0
  174. package/src/lib/rulers/ticks/createVerticalTick.ts +43 -0
  175. package/src/lib/rulers/ticks/index.ts +3 -0
  176. package/src/lib/rulers/updateGrid.ts +11 -0
  177. package/src/lib/rulers/updateHorizontalRuler.ts +32 -0
  178. package/src/lib/rulers/updateRulers.ts +33 -0
  179. package/src/lib/rulers/updateVerticalRuler.ts +31 -0
  180. package/src/lib/transform/applyTransform.ts +15 -0
  181. package/src/lib/transform/applyZoomToCanvas.ts +7 -0
  182. package/src/lib/transform/hardware-acceleration.ts +11 -0
  183. package/src/lib/transform/index.ts +2 -0
  184. package/src/lib/transition/disableTransition.ts +33 -0
  185. package/src/lib/transition/enableTransition.ts +26 -0
  186. package/src/lib/transition/index.ts +3 -0
  187. package/src/lib/transition/withTransition.ts +13 -0
  188. package/src/types/canvas.ts +89 -0
  189. package/src/types/config.ts +54 -0
  190. package/src/types/events.ts +31 -0
  191. package/src/types/index.ts +28 -0
  192. package/src/types/matrix.ts +19 -0
  193. package/src/types/rulers.ts +35 -0
  194. package/src/umd.ts +1 -0
@@ -0,0 +1,54 @@
1
+ import { CLICK_THRESHOLDS } from "@/lib/events/constants.js";
2
+ import { withRulerOffset } from "@/lib/helpers/index.js";
3
+ import { withTransition } from "@/lib/transition/withTransition.js";
4
+ import type { Canvas, MarkupCanvasConfig, Transform } from "@/types/index.js";
5
+
6
+ export function handleClickToZoom(
7
+ event: MouseEvent,
8
+ canvas: Canvas,
9
+ config: Required<MarkupCanvasConfig>,
10
+ mouseDownTime: number,
11
+ hasDragged: boolean,
12
+ isDragging: boolean
13
+ ): void {
14
+ const clickDuration = Date.now() - mouseDownTime;
15
+
16
+ // Check if Option/Alt key is required and pressed
17
+ const optionKeyPressed = event.altKey;
18
+ const shouldZoom = config.requireOptionForClickZoom ? optionKeyPressed : true;
19
+
20
+ if (clickDuration < CLICK_THRESHOLDS.MAX_DURATION && !hasDragged && !isDragging && shouldZoom) {
21
+ event.preventDefault();
22
+
23
+ const rect = canvas.container.getBoundingClientRect();
24
+ const rawClickX = event.clientX - rect.left;
25
+ const rawClickY = event.clientY - rect.top;
26
+
27
+ const { clickX, clickY } = withRulerOffset(canvas, rawClickX, rawClickY, (adjustedX, adjustedY) => ({
28
+ clickX: adjustedX,
29
+ clickY: adjustedY,
30
+ }));
31
+
32
+ // Convert canvas coordinates to content coordinates at current scale
33
+ const contentCoords = canvas.canvasToContent(clickX, clickY);
34
+
35
+ // Calculate the center of the canvas
36
+ const canvasCenterX = rect.width / 2;
37
+ const canvasCenterY = rect.height / 2;
38
+
39
+ const newScale = config.clickZoomLevel;
40
+
41
+ const newTranslateX = canvasCenterX - contentCoords.x * newScale;
42
+ const newTranslateY = canvasCenterY - contentCoords.y * newScale;
43
+
44
+ const newTransform: Partial<Transform> = {
45
+ scale: newScale,
46
+ translateX: newTranslateX,
47
+ translateY: newTranslateY,
48
+ };
49
+
50
+ withTransition(canvas.transformLayer, canvas.config, () => {
51
+ canvas.updateTransform(newTransform);
52
+ });
53
+ }
54
+ }
@@ -0,0 +1,45 @@
1
+ import { updateCursor } from "@/lib/events/utils/updateCursor.js";
2
+ import type { Canvas, MarkupCanvasConfig } from "@/types/index.js";
3
+
4
+ export function handleMouseDown(
5
+ event: MouseEvent,
6
+ canvas: Canvas,
7
+ config: Required<MarkupCanvasConfig>,
8
+ isDragEnabled: boolean,
9
+ isSpacePressed: boolean,
10
+ setters: {
11
+ setMouseDownTime: (value: number) => void;
12
+ setMouseDownX: (value: number) => void;
13
+ setMouseDownY: (value: number) => void;
14
+ setHasDragged: (value: boolean) => void;
15
+ setIsDragging: (value: boolean) => void;
16
+ setDragButton: (value: number) => void;
17
+ setLastMouseX: (value: number) => void;
18
+ setLastMouseY: (value: number) => void;
19
+ }
20
+ ): void {
21
+ const isLeftButton = event.button === 0;
22
+ const isMiddleButton = event.button === 1;
23
+
24
+ if (isLeftButton) {
25
+ setters.setMouseDownTime(Date.now());
26
+ setters.setMouseDownX(event.clientX);
27
+ setters.setMouseDownY(event.clientY);
28
+ setters.setHasDragged(false);
29
+ }
30
+
31
+ if (!isDragEnabled) return;
32
+
33
+ // Check if drag is allowed based on configuration
34
+ const canDrag = config.requireSpaceForMouseDrag ? isSpacePressed : true;
35
+
36
+ if (canDrag && ((isLeftButton && config.enableLeftDrag) || (isMiddleButton && config.enableMiddleDrag))) {
37
+ event.preventDefault();
38
+ // Don't set isDragging to true yet - wait for mouse move
39
+ setters.setDragButton(event.button);
40
+ setters.setLastMouseX(event.clientX);
41
+ setters.setLastMouseY(event.clientY);
42
+
43
+ updateCursor(canvas, config, isDragEnabled, isSpacePressed, false); // ← Changed to false
44
+ }
45
+ }
@@ -0,0 +1,18 @@
1
+ import { resetDragState } from "@/lib/events/utils/resetDragState.js";
2
+ import type { Canvas, MarkupCanvasConfig } from "@/types/index.js";
3
+
4
+ export function handleMouseLeave(
5
+ canvas: Canvas,
6
+ config: Required<MarkupCanvasConfig>,
7
+ isDragEnabled: boolean,
8
+ isSpacePressed: boolean,
9
+ isDragging: boolean,
10
+ setters: {
11
+ setIsDragging: (value: boolean) => void;
12
+ setDragButton: (value: number) => void;
13
+ }
14
+ ): void {
15
+ if (isDragging) {
16
+ resetDragState(canvas, config, isDragEnabled, isSpacePressed, setters);
17
+ }
18
+ }
@@ -0,0 +1,57 @@
1
+ import { CLICK_THRESHOLDS } from "@/lib/events/constants.js";
2
+ import { withRAFThrottle } from "@/lib/helpers/index.js";
3
+ import type { Canvas, Transform } from "@/types/index.js";
4
+
5
+ export function handleMouseMove(
6
+ event: MouseEvent,
7
+ canvas: Canvas,
8
+ isDragEnabled: boolean,
9
+ isDragging: boolean,
10
+ mouseDownTime: number,
11
+ mouseDownX: number,
12
+ mouseDownY: number,
13
+ lastMouseX: number,
14
+ lastMouseY: number,
15
+ setters: {
16
+ setHasDragged: (value: boolean) => void;
17
+ setIsDragging: (value: boolean) => void;
18
+ setLastMouseX: (value: number) => void;
19
+ setLastMouseY: (value: number) => void;
20
+ }
21
+ ): void {
22
+ if (mouseDownTime > 0) {
23
+ const deltaX = Math.abs(event.clientX - mouseDownX);
24
+ const deltaY = Math.abs(event.clientY - mouseDownY);
25
+ if (deltaX > CLICK_THRESHOLDS.MAX_MOVEMENT || deltaY > CLICK_THRESHOLDS.MAX_MOVEMENT) {
26
+ setters.setHasDragged(true);
27
+
28
+ if (!isDragging && isDragEnabled) {
29
+ setters.setIsDragging(true);
30
+ }
31
+ }
32
+ }
33
+
34
+ if (!isDragging || !isDragEnabled) return;
35
+
36
+ event.preventDefault();
37
+
38
+ const handleMouseMoveThrottled = withRAFThrottle((...args: unknown[]) => {
39
+ const moveEvent = args[0] as MouseEvent;
40
+ if (!isDragging || !isDragEnabled) return;
41
+
42
+ const deltaX = moveEvent.clientX - lastMouseX;
43
+ const deltaY = moveEvent.clientY - lastMouseY;
44
+
45
+ const newTransform: Partial<Transform> = {
46
+ translateX: canvas.transform.translateX + deltaX,
47
+ translateY: canvas.transform.translateY + deltaY,
48
+ };
49
+
50
+ canvas.updateTransform(newTransform);
51
+
52
+ setters.setLastMouseX(moveEvent.clientX);
53
+ setters.setLastMouseY(moveEvent.clientY);
54
+ });
55
+
56
+ handleMouseMoveThrottled(event);
57
+ }
@@ -0,0 +1,40 @@
1
+ import { handleClickToZoom } from "@/lib/events/mouse/handleClickToZoom.js";
2
+ import { resetClickState } from "@/lib/events/utils/resetClickState.js";
3
+ import { resetDragState } from "@/lib/events/utils/resetDragState.js";
4
+ import type { Canvas, MarkupCanvasConfig } from "@/types/index.js";
5
+
6
+ export function handleMouseUp(
7
+ event: MouseEvent,
8
+ canvas: Canvas,
9
+ config: Required<MarkupCanvasConfig>,
10
+ isDragEnabled: boolean,
11
+ isSpacePressed: boolean,
12
+ isDragging: boolean,
13
+ dragButton: number,
14
+ mouseDownTime: number,
15
+ hasDragged: boolean,
16
+ setters: {
17
+ setIsDragging: (value: boolean) => void;
18
+ setDragButton: (value: number) => void;
19
+ setMouseDownTime: (value: number) => void;
20
+ setHasDragged: (value: boolean) => void;
21
+ }
22
+ ): void {
23
+ if (isDragging && event.button === dragButton) {
24
+ resetDragState(canvas, config, isDragEnabled, isSpacePressed, {
25
+ setIsDragging: setters.setIsDragging,
26
+ setDragButton: setters.setDragButton,
27
+ });
28
+ }
29
+
30
+ if (isDragEnabled && event.button === 0 && config.enableClickToZoom && mouseDownTime > 0) {
31
+ handleClickToZoom(event, canvas, config, mouseDownTime, hasDragged, isDragging);
32
+ }
33
+
34
+ if (event.button === 0) {
35
+ resetClickState({
36
+ setMouseDownTime: setters.setMouseDownTime,
37
+ setHasDragged: setters.setHasDragged,
38
+ });
39
+ }
40
+ }
@@ -0,0 +1,159 @@
1
+ import { handleKeyDown } from "@/lib/events/keyboard/handleKeyDown.js";
2
+ import { handleKeyUp } from "@/lib/events/keyboard/handleKeyUp.js";
3
+ import { handleMouseDown } from "@/lib/events/mouse/handleMouseDown.js";
4
+ import { handleMouseLeave } from "@/lib/events/mouse/handleMouseLeave.js";
5
+ import { handleMouseMove } from "@/lib/events/mouse/handleMouseMove.js";
6
+ import { handleMouseUp } from "@/lib/events/mouse/handleMouseUp.js";
7
+ import { resetDragState } from "@/lib/events/utils/resetDragState.js";
8
+ import { updateCursor } from "@/lib/events/utils/updateCursor.js";
9
+ import type { MouseDragControls } from "@/types/events.js";
10
+ import type { Canvas, MarkupCanvasConfig } from "@/types/index.js";
11
+
12
+ export function setupMouseEvents(canvas: Canvas, config: Required<MarkupCanvasConfig>, withControls: true): MouseDragControls;
13
+ export function setupMouseEvents(canvas: Canvas, config: Required<MarkupCanvasConfig>, withControls: false): () => void;
14
+ export function setupMouseEvents(
15
+ canvas: Canvas,
16
+ config: Required<MarkupCanvasConfig>,
17
+ withControls: boolean = true
18
+ ): MouseDragControls | (() => void) {
19
+ // State management
20
+ let isDragEnabled = true;
21
+ let isDragging = false;
22
+ let lastMouseX = 0;
23
+ let lastMouseY = 0;
24
+ let dragButton = -1;
25
+ let isSpacePressed = false;
26
+
27
+ // Click-to-zoom tracking
28
+ let mouseDownTime = 0;
29
+ let mouseDownX = 0;
30
+ let mouseDownY = 0;
31
+ let hasDragged = false;
32
+
33
+ // State setters for passing to functions
34
+ const setters = {
35
+ setIsDragging: (value: boolean) => {
36
+ isDragging = value;
37
+ },
38
+ setDragButton: (value: number) => {
39
+ dragButton = value;
40
+ },
41
+ setIsSpacePressed: (value: boolean) => {
42
+ isSpacePressed = value;
43
+ },
44
+ setMouseDownTime: (value: number) => {
45
+ mouseDownTime = value;
46
+ },
47
+ setMouseDownX: (value: number) => {
48
+ mouseDownX = value;
49
+ },
50
+ setMouseDownY: (value: number) => {
51
+ mouseDownY = value;
52
+ },
53
+ setHasDragged: (value: boolean) => {
54
+ hasDragged = value;
55
+ },
56
+ setLastMouseX: (value: number) => {
57
+ lastMouseX = value;
58
+ },
59
+ setLastMouseY: (value: number) => {
60
+ lastMouseY = value;
61
+ },
62
+ };
63
+
64
+ // Event handler wrappers
65
+ const keyDownHandler = (event: KeyboardEvent) => {
66
+ handleKeyDown(event, canvas, config, isDragEnabled, isDragging, {
67
+ setIsSpacePressed: setters.setIsSpacePressed,
68
+ });
69
+ };
70
+
71
+ const keyUpHandler = (event: KeyboardEvent) => {
72
+ handleKeyUp(event, canvas, config, isDragEnabled, isDragging, {
73
+ setIsSpacePressed: setters.setIsSpacePressed,
74
+ setIsDragging: setters.setIsDragging,
75
+ setDragButton: setters.setDragButton,
76
+ });
77
+ };
78
+
79
+ const mouseDownHandler = (event: MouseEvent) => {
80
+ handleMouseDown(event, canvas, config, isDragEnabled, isSpacePressed, setters);
81
+ };
82
+
83
+ const mouseMoveHandler = (event: MouseEvent) => {
84
+ handleMouseMove(event, canvas, isDragEnabled, isDragging, mouseDownTime, mouseDownX, mouseDownY, lastMouseX, lastMouseY, {
85
+ setHasDragged: setters.setHasDragged,
86
+ setIsDragging: setters.setIsDragging,
87
+ setLastMouseX: setters.setLastMouseX,
88
+ setLastMouseY: setters.setLastMouseY,
89
+ });
90
+ };
91
+
92
+ const mouseUpHandler = (event: MouseEvent) => {
93
+ handleMouseUp(event, canvas, config, isDragEnabled, isSpacePressed, isDragging, dragButton, mouseDownTime, hasDragged, {
94
+ setIsDragging: setters.setIsDragging,
95
+ setDragButton: setters.setDragButton,
96
+ setMouseDownTime: setters.setMouseDownTime,
97
+ setHasDragged: setters.setHasDragged,
98
+ });
99
+ };
100
+
101
+ const mouseLeaveHandler = () => {
102
+ handleMouseLeave(canvas, config, isDragEnabled, isSpacePressed, isDragging, {
103
+ setIsDragging: setters.setIsDragging,
104
+ setDragButton: setters.setDragButton,
105
+ });
106
+ };
107
+
108
+ // Set up event listeners
109
+ canvas.container.addEventListener("mousedown", mouseDownHandler);
110
+ document.addEventListener("mousemove", mouseMoveHandler);
111
+ document.addEventListener("mouseup", mouseUpHandler);
112
+ canvas.container.addEventListener("mouseleave", mouseLeaveHandler);
113
+
114
+ // Add keyboard listeners if space requirement is enabled
115
+ if (config.requireSpaceForMouseDrag) {
116
+ document.addEventListener("keydown", keyDownHandler);
117
+ document.addEventListener("keyup", keyUpHandler);
118
+ }
119
+
120
+ updateCursor(canvas, config, isDragEnabled, isSpacePressed, isDragging);
121
+
122
+ const cleanup = () => {
123
+ canvas.container.removeEventListener("mousedown", mouseDownHandler);
124
+ document.removeEventListener("mousemove", mouseMoveHandler);
125
+ document.removeEventListener("mouseup", mouseUpHandler);
126
+ canvas.container.removeEventListener("mouseleave", mouseLeaveHandler);
127
+
128
+ if (config.requireSpaceForMouseDrag) {
129
+ document.removeEventListener("keydown", keyDownHandler);
130
+ document.removeEventListener("keyup", keyUpHandler);
131
+ }
132
+ };
133
+
134
+ if (withControls) {
135
+ return {
136
+ cleanup,
137
+ enable: () => {
138
+ isDragEnabled = true;
139
+ updateCursor(canvas, config, isDragEnabled, isSpacePressed, isDragging);
140
+ return true;
141
+ },
142
+ disable: () => {
143
+ isDragEnabled = false;
144
+ // Stop any current dragging
145
+ if (isDragging) {
146
+ resetDragState(canvas, config, isDragEnabled, isSpacePressed, {
147
+ setIsDragging: setters.setIsDragging,
148
+ setDragButton: setters.setDragButton,
149
+ });
150
+ }
151
+ updateCursor(canvas, config, isDragEnabled, isSpacePressed, isDragging);
152
+ return true;
153
+ },
154
+ isEnabled: () => isDragEnabled,
155
+ };
156
+ }
157
+
158
+ return cleanup;
159
+ }
@@ -0,0 +1,158 @@
1
+ import { handleKeyDown } from "@/lib/events/keyboard/handleKeyDown.js";
2
+ import { handleKeyUp } from "@/lib/events/keyboard/handleKeyUp.js";
3
+ import { handleMouseDown } from "@/lib/events/mouse/handleMouseDown.js";
4
+ import { handleMouseLeave } from "@/lib/events/mouse/handleMouseLeave.js";
5
+ import { handleMouseMove } from "@/lib/events/mouse/handleMouseMove.js";
6
+ import { handleMouseUp } from "@/lib/events/mouse/handleMouseUp.js";
7
+ import { resetDragState } from "@/lib/events/utils/resetDragState.js";
8
+ import { updateCursor } from "@/lib/events/utils/updateCursor.js";
9
+ import type { MouseDragControls } from "@/types/events.js";
10
+ import type { Canvas, MarkupCanvasConfig } from "@/types/index.js";
11
+
12
+ export function setupMouseEvents(canvas: Canvas, config: Required<MarkupCanvasConfig>, withControls: true): MouseDragControls;
13
+ export function setupMouseEvents(canvas: Canvas, config: Required<MarkupCanvasConfig>, withControls: false): () => void;
14
+ export function setupMouseEvents(
15
+ canvas: Canvas,
16
+ config: Required<MarkupCanvasConfig>,
17
+ withControls: boolean = true
18
+ ): MouseDragControls | (() => void) {
19
+ // State management
20
+ let isDragEnabled = true;
21
+ let isDragging = false;
22
+ let lastMouseX = 0;
23
+ let lastMouseY = 0;
24
+ let dragButton = -1;
25
+ let isSpacePressed = false;
26
+
27
+ // Click-to-zoom tracking
28
+ let mouseDownTime = 0;
29
+ let mouseDownX = 0;
30
+ let mouseDownY = 0;
31
+ let hasDragged = false;
32
+
33
+ // State setters for passing to functions
34
+ const setters = {
35
+ setIsDragging: (value: boolean) => {
36
+ isDragging = value;
37
+ },
38
+ setDragButton: (value: number) => {
39
+ dragButton = value;
40
+ },
41
+ setIsSpacePressed: (value: boolean) => {
42
+ isSpacePressed = value;
43
+ },
44
+ setMouseDownTime: (value: number) => {
45
+ mouseDownTime = value;
46
+ },
47
+ setMouseDownX: (value: number) => {
48
+ mouseDownX = value;
49
+ },
50
+ setMouseDownY: (value: number) => {
51
+ mouseDownY = value;
52
+ },
53
+ setHasDragged: (value: boolean) => {
54
+ hasDragged = value;
55
+ },
56
+ setLastMouseX: (value: number) => {
57
+ lastMouseX = value;
58
+ },
59
+ setLastMouseY: (value: number) => {
60
+ lastMouseY = value;
61
+ },
62
+ };
63
+
64
+ // Event handler wrappers
65
+ const keyDownHandler = (event: KeyboardEvent) => {
66
+ handleKeyDown(event, canvas, config, isDragEnabled, isDragging, {
67
+ setIsSpacePressed: setters.setIsSpacePressed,
68
+ });
69
+ };
70
+
71
+ const keyUpHandler = (event: KeyboardEvent) => {
72
+ handleKeyUp(event, canvas, config, isDragEnabled, isDragging, {
73
+ setIsSpacePressed: setters.setIsSpacePressed,
74
+ setIsDragging: setters.setIsDragging,
75
+ setDragButton: setters.setDragButton,
76
+ });
77
+ };
78
+
79
+ const mouseDownHandler = (event: MouseEvent) => {
80
+ handleMouseDown(event, canvas, config, isDragEnabled, isSpacePressed, setters);
81
+ };
82
+
83
+ const mouseMoveHandler = (event: MouseEvent) => {
84
+ handleMouseMove(event, canvas, isDragEnabled, isDragging, mouseDownTime, mouseDownX, mouseDownY, lastMouseX, lastMouseY, {
85
+ setHasDragged: setters.setHasDragged,
86
+ setIsDragging: setters.setIsDragging,
87
+ setLastMouseX: setters.setLastMouseX,
88
+ setLastMouseY: setters.setLastMouseY,
89
+ });
90
+ };
91
+
92
+ const mouseUpHandler = (event: MouseEvent) => {
93
+ handleMouseUp(event, canvas, config, isDragEnabled, isSpacePressed, isDragging, dragButton, mouseDownTime, hasDragged, {
94
+ setIsDragging: setters.setIsDragging,
95
+ setDragButton: setters.setDragButton,
96
+ setMouseDownTime: setters.setMouseDownTime,
97
+ setHasDragged: setters.setHasDragged,
98
+ });
99
+ };
100
+
101
+ const mouseLeaveHandler = () => {
102
+ handleMouseLeave(canvas, config, isDragEnabled, isSpacePressed, isDragging, {
103
+ setIsDragging: setters.setIsDragging,
104
+ setDragButton: setters.setDragButton,
105
+ });
106
+ };
107
+
108
+ // Set up event listeners
109
+ canvas.container.addEventListener("mousedown", mouseDownHandler);
110
+ document.addEventListener("mousemove", mouseMoveHandler);
111
+ document.addEventListener("mouseup", mouseUpHandler);
112
+ canvas.container.addEventListener("mouseleave", mouseLeaveHandler);
113
+
114
+ if (config.requireSpaceForMouseDrag) {
115
+ document.addEventListener("keydown", keyDownHandler);
116
+ document.addEventListener("keyup", keyUpHandler);
117
+ }
118
+
119
+ updateCursor(canvas, config, isDragEnabled, isSpacePressed, isDragging);
120
+
121
+ const cleanup = () => {
122
+ canvas.container.removeEventListener("mousedown", mouseDownHandler);
123
+ document.removeEventListener("mousemove", mouseMoveHandler);
124
+ document.removeEventListener("mouseup", mouseUpHandler);
125
+ canvas.container.removeEventListener("mouseleave", mouseLeaveHandler);
126
+
127
+ if (config.requireSpaceForMouseDrag) {
128
+ document.removeEventListener("keydown", keyDownHandler);
129
+ document.removeEventListener("keyup", keyUpHandler);
130
+ }
131
+ };
132
+
133
+ if (withControls) {
134
+ return {
135
+ cleanup,
136
+ enable: () => {
137
+ isDragEnabled = true;
138
+ updateCursor(canvas, config, isDragEnabled, isSpacePressed, isDragging);
139
+ return true;
140
+ },
141
+ disable: () => {
142
+ isDragEnabled = false;
143
+ // Stop any current dragging
144
+ if (isDragging) {
145
+ resetDragState(canvas, config, isDragEnabled, isSpacePressed, {
146
+ setIsDragging: setters.setIsDragging,
147
+ setDragButton: setters.setDragButton,
148
+ });
149
+ }
150
+ updateCursor(canvas, config, isDragEnabled, isSpacePressed, isDragging);
151
+ return true;
152
+ },
153
+ isEnabled: () => isDragEnabled,
154
+ };
155
+ }
156
+
157
+ return cleanup;
158
+ }
@@ -0,0 +1,6 @@
1
+ export function getTouchCenter(touch1: Touch, touch2: Touch): { x: number; y: number } {
2
+ return {
3
+ x: (touch1.clientX + touch2.clientX) / 2,
4
+ y: (touch1.clientY + touch2.clientY) / 2,
5
+ };
6
+ }
@@ -0,0 +1,5 @@
1
+ export function getTouchDistance(touch1: Touch, touch2: Touch): number {
2
+ const dx = touch1.clientX - touch2.clientX;
3
+ const dy = touch1.clientY - touch2.clientY;
4
+ return Math.sqrt(dx * dx + dy * dy);
5
+ }
@@ -0,0 +1,9 @@
1
+ import type { TouchState } from "@/types/index.js";
2
+
3
+ export function handleTouchEnd(event: TouchEvent, touchState: TouchState): void {
4
+ touchState.touches = Array.from(event.touches);
5
+
6
+ if (touchState.touches.length < 2) {
7
+ touchState.lastDistance = 0;
8
+ }
9
+ }
@@ -0,0 +1,58 @@
1
+ import { getTouchCenter } from "@/lib/events/touch/getTouchCenter.js";
2
+ import { getTouchDistance } from "@/lib/events/touch/getTouchDistance.js";
3
+ import { withRAFThrottle, withRulerOffsetObject } from "@/lib/helpers/index.js";
4
+ import { applyZoomToCanvas } from "@/lib/transform/applyZoomToCanvas.js";
5
+ import type { Canvas, TouchState, Transform } from "@/types/index.js";
6
+
7
+ export function handleTouchMove(event: TouchEvent, canvas: Canvas, touchState: TouchState): void {
8
+ event.preventDefault();
9
+
10
+ const currentTouches = Array.from(event.touches);
11
+
12
+ // Enhanced RAF-throttled touch move handler for smooth gesture performance
13
+ const handleTouchMoveThrottled = withRAFThrottle((...args: unknown[]) => {
14
+ const touches = args[0] as Touch[];
15
+ if (touches.length === 1) {
16
+ // Single touch pan
17
+ if (touchState.touches.length === 1) {
18
+ const deltaX = touches[0].clientX - touchState.touches[0].clientX;
19
+ const deltaY = touches[0].clientY - touchState.touches[0].clientY;
20
+
21
+ const newTransform: Partial<Transform> = {
22
+ translateX: canvas.transform.translateX + deltaX,
23
+ translateY: canvas.transform.translateY + deltaY,
24
+ };
25
+
26
+ canvas.updateTransform(newTransform);
27
+ }
28
+ } else if (touches.length === 2) {
29
+ // Two finger pinch zoom
30
+ const currentDistance = getTouchDistance(touches[0], touches[1]);
31
+ const currentCenter = getTouchCenter(touches[0], touches[1]);
32
+
33
+ if (touchState.lastDistance > 0) {
34
+ const rawZoomFactor = currentDistance / touchState.lastDistance;
35
+
36
+ // Get center relative to canvas content area (accounting for rulers)
37
+ const rect = canvas.container.getBoundingClientRect();
38
+ let centerX = currentCenter.x - rect.left;
39
+ let centerY = currentCenter.y - rect.top;
40
+
41
+ // Account for ruler offset if rulers are present
42
+ const adjustedCenter = withRulerOffsetObject(canvas, { x: centerX, y: centerY }, (adjusted) => adjusted);
43
+ centerX = adjustedCenter.x;
44
+ centerY = adjustedCenter.y;
45
+
46
+ // Touch zoom uses global transition settings
47
+ applyZoomToCanvas(canvas, rawZoomFactor, centerX, centerY);
48
+ }
49
+
50
+ touchState.lastDistance = currentDistance;
51
+ touchState.lastCenter = currentCenter;
52
+ }
53
+
54
+ touchState.touches = touches;
55
+ });
56
+
57
+ handleTouchMoveThrottled(currentTouches);
58
+ }
@@ -0,0 +1,14 @@
1
+ import { getTouchCenter } from "@/lib/events/touch/getTouchCenter.js";
2
+ import { getTouchDistance } from "@/lib/events/touch/getTouchDistance.js";
3
+ import type { TouchState } from "@/types/index.js";
4
+
5
+ export function handleTouchStart(event: TouchEvent, touchState: TouchState): void {
6
+ event.preventDefault();
7
+
8
+ touchState.touches = Array.from(event.touches);
9
+
10
+ if (touchState.touches.length === 2) {
11
+ touchState.lastDistance = getTouchDistance(touchState.touches[0], touchState.touches[1]);
12
+ touchState.lastCenter = getTouchCenter(touchState.touches[0], touchState.touches[1]);
13
+ }
14
+ }
@@ -0,0 +1,40 @@
1
+ import { handleTouchEnd } from "@/lib/events/touch/handleTouchEnd.js";
2
+ import { handleTouchMove } from "@/lib/events/touch/handleTouchMove.js";
3
+ import { handleTouchStart } from "@/lib/events/touch/handleTouchStart.js";
4
+ import type { Canvas, TouchState } from "@/types/index.js";
5
+
6
+ export function setupTouchEvents(canvas: Canvas): () => void {
7
+ const touchState: TouchState = {
8
+ touches: [],
9
+ lastDistance: 0,
10
+ lastCenter: { x: 0, y: 0 },
11
+ };
12
+
13
+ const touchStartHandler = (event: TouchEvent) => {
14
+ handleTouchStart(event, touchState);
15
+ };
16
+
17
+ const touchMoveHandler = (event: TouchEvent) => {
18
+ handleTouchMove(event, canvas, touchState);
19
+ };
20
+
21
+ const touchEndHandler = (event: TouchEvent) => {
22
+ handleTouchEnd(event, touchState);
23
+ };
24
+
25
+ canvas.container.addEventListener("touchstart", touchStartHandler, {
26
+ passive: false,
27
+ });
28
+ canvas.container.addEventListener("touchmove", touchMoveHandler, {
29
+ passive: false,
30
+ });
31
+ canvas.container.addEventListener("touchend", touchEndHandler, {
32
+ passive: false,
33
+ });
34
+
35
+ return () => {
36
+ canvas.container.removeEventListener("touchstart", touchStartHandler);
37
+ canvas.container.removeEventListener("touchmove", touchMoveHandler);
38
+ canvas.container.removeEventListener("touchend", touchEndHandler);
39
+ };
40
+ }