@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,94 @@
1
+ import type { RulerCanvas as Canvas, MarkupCanvasConfig, RulerSystem } from "@/types/index.js";
2
+ import { createRulerElements } from "./createRulerElements.js";
3
+ import type { RulerElements } from "./RulerElements.js";
4
+ import { setupRulerEvents } from "./setupRulerEvents.js";
5
+ import { updateRulers } from "./updateRulers.js";
6
+
7
+ export function createRulers(canvas: Canvas, config: Required<MarkupCanvasConfig>): RulerSystem | null {
8
+ if (!canvas?.container) {
9
+ console.error("Invalid canvas provided to createRulers");
10
+ return null;
11
+ }
12
+
13
+ let elements: RulerElements;
14
+ let cleanupEvents: (() => void) | null = null;
15
+ let isDestroyed = false;
16
+
17
+ const rulerOptions = {
18
+ backgroundColor: config.rulerBackgroundColor,
19
+ borderColor: config.rulerBorderColor,
20
+ textColor: config.rulerTextColor,
21
+ majorTickColor: config.rulerMajorTickColor,
22
+ minorTickColor: config.rulerMinorTickColor,
23
+ fontSize: config.rulerFontSize,
24
+ fontFamily: config.rulerFontFamily,
25
+ showGrid: config.enableGrid,
26
+ gridColor: config.gridColor,
27
+ units: config.rulerUnits,
28
+ };
29
+
30
+ const safeUpdate = (): void => {
31
+ if (isDestroyed || !elements.horizontalRuler || !elements.verticalRuler) return;
32
+ updateRulers(canvas, elements.horizontalRuler, elements.verticalRuler, elements.gridOverlay, rulerOptions);
33
+ };
34
+
35
+ try {
36
+ elements = createRulerElements(canvas.container, rulerOptions);
37
+ cleanupEvents = setupRulerEvents(canvas, safeUpdate);
38
+
39
+ safeUpdate();
40
+
41
+ return {
42
+ horizontalRuler: elements.horizontalRuler,
43
+ verticalRuler: elements.verticalRuler,
44
+ cornerBox: elements.cornerBox,
45
+ gridOverlay: elements.gridOverlay,
46
+
47
+ update: safeUpdate,
48
+
49
+ show: () => {
50
+ if (elements.horizontalRuler) elements.horizontalRuler.style.display = "block";
51
+ if (elements.verticalRuler) elements.verticalRuler.style.display = "block";
52
+ if (elements.cornerBox) elements.cornerBox.style.display = "flex";
53
+ if (elements.gridOverlay) elements.gridOverlay.style.display = "block";
54
+ },
55
+
56
+ hide: () => {
57
+ if (elements.horizontalRuler) elements.horizontalRuler.style.display = "none";
58
+ if (elements.verticalRuler) elements.verticalRuler.style.display = "none";
59
+ if (elements.cornerBox) elements.cornerBox.style.display = "none";
60
+ if (elements.gridOverlay) elements.gridOverlay.style.display = "none";
61
+ },
62
+
63
+ toggleGrid: () => {
64
+ if (elements.gridOverlay) {
65
+ const isVisible = elements.gridOverlay.style.display !== "none";
66
+ elements.gridOverlay.style.display = isVisible ? "none" : "block";
67
+ }
68
+ },
69
+
70
+ destroy: () => {
71
+ isDestroyed = true;
72
+ if (cleanupEvents) {
73
+ cleanupEvents();
74
+ }
75
+
76
+ if (elements.horizontalRuler?.parentNode) {
77
+ elements.horizontalRuler.parentNode.removeChild(elements.horizontalRuler);
78
+ }
79
+ if (elements.verticalRuler?.parentNode) {
80
+ elements.verticalRuler.parentNode.removeChild(elements.verticalRuler);
81
+ }
82
+ if (elements.cornerBox?.parentNode) {
83
+ elements.cornerBox.parentNode.removeChild(elements.cornerBox);
84
+ }
85
+ if (elements.gridOverlay?.parentNode) {
86
+ elements.gridOverlay.parentNode.removeChild(elements.gridOverlay);
87
+ }
88
+ },
89
+ };
90
+ } catch (error) {
91
+ console.error("Failed to create rulers:", error);
92
+ return null;
93
+ }
94
+ }
@@ -0,0 +1,24 @@
1
+ import type { RulerOptions } from "@/types/index.js";
2
+ import { RULER_SIZE, RULER_Z_INDEX } from "./constants";
3
+
4
+ export function createVerticalRuler(config: Required<RulerOptions>): HTMLElement {
5
+ const ruler = document.createElement("div");
6
+ ruler.className = "canvas-ruler vertical-ruler";
7
+ ruler.style.cssText = `
8
+ position: absolute;
9
+ top: ${RULER_SIZE}px;
10
+ left: 0;
11
+ bottom: 0;
12
+ width: ${RULER_SIZE}px;
13
+ background: ${config.backgroundColor};
14
+ border-right: 1px solid ${config.borderColor};
15
+ border-bottom: 1px solid ${config.borderColor};
16
+ z-index: ${RULER_Z_INDEX.RULERS};
17
+ pointer-events: none;
18
+ font-family: ${config.fontFamily};
19
+ font-size: ${config.fontSize}px;
20
+ color: ${config.textColor};
21
+ overflow: hidden;
22
+ `;
23
+ return ruler;
24
+ }
@@ -0,0 +1,2 @@
1
+ export type { RulerOptions, RulerSystem } from "@/types/index.js";
2
+ export { createRulers } from "./createRulers.js";
@@ -0,0 +1,23 @@
1
+ import { withRAFThrottle } from "@/lib/helpers/index.js";
2
+ import type { RulerCanvas as Canvas, Transform } from "@/types/index.js";
3
+
4
+ export function setupRulerEvents(canvas: Canvas, updateCallback: () => void): () => void {
5
+ const throttledUpdateCallback = withRAFThrottle(updateCallback);
6
+
7
+ const originalUpdateTransform = canvas.updateTransform;
8
+ canvas.updateTransform = function (newTransform: Partial<Transform>): boolean {
9
+ const result = originalUpdateTransform.call(this, newTransform);
10
+ throttledUpdateCallback();
11
+ return result;
12
+ };
13
+
14
+ const resizeHandler = withRAFThrottle(updateCallback);
15
+ window.addEventListener("resize", resizeHandler);
16
+
17
+ return () => {
18
+ window.removeEventListener("resize", resizeHandler);
19
+ canvas.updateTransform = originalUpdateTransform;
20
+ throttledUpdateCallback.cleanup();
21
+ resizeHandler.cleanup();
22
+ };
23
+ }
@@ -0,0 +1,15 @@
1
+ export function calculateTickSpacing(contentSize: number, canvasSize: number): number {
2
+ const targetTicks = Math.max(5, Math.min(20, canvasSize / 50));
3
+ const rawSpacing = contentSize / targetTicks;
4
+
5
+ const magnitude = 10 ** Math.floor(Math.log10(rawSpacing));
6
+ const normalized = rawSpacing / magnitude;
7
+
8
+ let niceSpacing: number;
9
+ if (normalized <= 1) niceSpacing = 1;
10
+ else if (normalized <= 2) niceSpacing = 2;
11
+ else if (normalized <= 5) niceSpacing = 5;
12
+ else niceSpacing = 10;
13
+
14
+ return niceSpacing * magnitude;
15
+ }
@@ -0,0 +1,41 @@
1
+ import { TICK_SETTINGS } from "@/lib/rulers/constants.js";
2
+ import type { RulerOptions } from "@/types/index.js";
3
+
4
+ export function createHorizontalTick(
5
+ container: HTMLElement | DocumentFragment,
6
+ position: number,
7
+ pixelPos: number,
8
+ tickSpacing: number,
9
+ config: Required<RulerOptions>
10
+ ): void {
11
+ const tick = document.createElement("div");
12
+ const isMajor = position % (tickSpacing * TICK_SETTINGS.MAJOR_MULTIPLIER) === 0;
13
+ const tickHeight = isMajor ? TICK_SETTINGS.MAJOR_HEIGHT : TICK_SETTINGS.MINOR_HEIGHT;
14
+
15
+ tick.style.cssText = `
16
+ position: absolute;
17
+ left: ${pixelPos}px;
18
+ bottom: 0;
19
+ width: 1px;
20
+ height: ${tickHeight}px;
21
+ background: ${isMajor ? config.majorTickColor : config.minorTickColor};
22
+ `;
23
+
24
+ container.appendChild(tick);
25
+
26
+ const shouldShowLabel = isMajor || position % TICK_SETTINGS.LABEL_INTERVAL === 0;
27
+ if (shouldShowLabel) {
28
+ const label = document.createElement("div");
29
+ label.style.cssText = `
30
+ position: absolute;
31
+ left: ${pixelPos}px;
32
+ bottom: ${tickHeight}px;
33
+ font-size: ${config.fontSize}px;
34
+ color: ${config.textColor};
35
+ white-space: nowrap;
36
+ pointer-events: none;
37
+ `;
38
+ label.textContent = Math.round(position).toString();
39
+ container.appendChild(label);
40
+ }
41
+ }
@@ -0,0 +1,43 @@
1
+ import { TICK_SETTINGS } from "@/lib/rulers/constants.js";
2
+ import type { RulerOptions } from "@/types/index.js";
3
+
4
+ export function createVerticalTick(
5
+ container: HTMLElement | DocumentFragment,
6
+ position: number,
7
+ pixelPos: number,
8
+ tickSpacing: number,
9
+ config: Required<RulerOptions>
10
+ ): void {
11
+ const tick = document.createElement("div");
12
+ const isMajor = position % (tickSpacing * TICK_SETTINGS.MAJOR_MULTIPLIER) === 0;
13
+ const tickWidth = isMajor ? TICK_SETTINGS.MAJOR_WIDTH : TICK_SETTINGS.MINOR_WIDTH;
14
+
15
+ tick.style.cssText = `
16
+ position: absolute;
17
+ top: ${pixelPos}px;
18
+ right: 0;
19
+ width: ${tickWidth}px;
20
+ height: 1px;
21
+ background: ${isMajor ? config.majorTickColor : config.minorTickColor};
22
+ `;
23
+
24
+ container.appendChild(tick);
25
+
26
+ const shouldShowLabel = isMajor || position % TICK_SETTINGS.LABEL_INTERVAL === 0;
27
+ if (shouldShowLabel) {
28
+ const label = document.createElement("div");
29
+ label.style.cssText = `
30
+ position: absolute;
31
+ top: ${pixelPos - 6}px;
32
+ right: ${tickWidth + 6}px;
33
+ font-size: ${config.fontSize}px;
34
+ color: ${config.textColor};
35
+ white-space: nowrap;
36
+ pointer-events: none;
37
+ transform: rotate(-90deg);
38
+ transform-origin: right center;
39
+ `;
40
+ label.textContent = Math.round(position).toString();
41
+ container.appendChild(label);
42
+ }
43
+ }
@@ -0,0 +1,3 @@
1
+ export { calculateTickSpacing } from "./calculateTickSpacing.js";
2
+ export { createHorizontalTick } from "./createHorizontalTick.js";
3
+ export { createVerticalTick } from "./createVerticalTick.js";
@@ -0,0 +1,11 @@
1
+ import { GRID_SETTINGS } from "@/lib/rulers/constants.js";
2
+
3
+ export function updateGrid(gridOverlay: HTMLElement, scale: number, translateX: number, translateY: number): void {
4
+ let gridSize = GRID_SETTINGS.BASE_SIZE * scale;
5
+
6
+ while (gridSize < GRID_SETTINGS.MIN_SIZE) gridSize *= 2;
7
+ while (gridSize > GRID_SETTINGS.MAX_SIZE) gridSize /= 2;
8
+
9
+ gridOverlay.style.backgroundSize = `${gridSize}px ${gridSize}px`;
10
+ gridOverlay.style.backgroundPosition = `${translateX % gridSize}px ${translateY % gridSize}px`;
11
+ }
@@ -0,0 +1,32 @@
1
+ import type { RulerOptions } from "@/types/index.js";
2
+ import { calculateTickSpacing } from "./ticks/calculateTickSpacing.js";
3
+ import { createHorizontalTick } from "./ticks/createHorizontalTick.js";
4
+
5
+ export function updateHorizontalRuler(
6
+ ruler: HTMLElement,
7
+ contentLeft: number,
8
+ contentRight: number,
9
+ canvasWidth: number,
10
+ scale: number,
11
+ config: Required<RulerOptions>
12
+ ): void {
13
+ const rulerWidth = canvasWidth;
14
+ const contentWidth = contentRight - contentLeft;
15
+
16
+ const tickSpacing = calculateTickSpacing(contentWidth, rulerWidth);
17
+ const fragment = document.createDocumentFragment();
18
+
19
+ const startTick = Math.floor(contentLeft / tickSpacing) * tickSpacing;
20
+ const endTick = Math.ceil(contentRight / tickSpacing) * tickSpacing;
21
+
22
+ for (let pos = startTick; pos <= endTick; pos += tickSpacing) {
23
+ const pixelPos = (pos - contentLeft) * scale;
24
+
25
+ if (pixelPos >= -50 && pixelPos <= rulerWidth + 50) {
26
+ createHorizontalTick(fragment, pos, pixelPos, tickSpacing, config);
27
+ }
28
+ }
29
+
30
+ ruler.innerHTML = "";
31
+ ruler.appendChild(fragment);
32
+ }
@@ -0,0 +1,33 @@
1
+ import { updateGrid } from "@/lib/rulers/updateGrid.js";
2
+ import type { RulerCanvas as Canvas, RulerOptions } from "@/types/index.js";
3
+ import { RULER_SIZE } from "./constants";
4
+ import { updateHorizontalRuler } from "./updateHorizontalRuler.js";
5
+ import { updateVerticalRuler } from "./updateVerticalRuler.js";
6
+
7
+ export function updateRulers(
8
+ canvas: Canvas,
9
+ horizontalRuler: HTMLElement,
10
+ verticalRuler: HTMLElement,
11
+ gridOverlay: HTMLElement | undefined,
12
+ config: Required<RulerOptions>
13
+ ): void {
14
+ const bounds = canvas.getBounds();
15
+ const scale = bounds.scale || 1;
16
+ const translateX = bounds.translateX || 0;
17
+ const translateY = bounds.translateY || 0;
18
+
19
+ const canvasWidth = bounds.width - RULER_SIZE;
20
+ const canvasHeight = bounds.height - RULER_SIZE;
21
+
22
+ const contentLeft = -translateX / scale;
23
+ const contentTop = -translateY / scale;
24
+ const contentRight = contentLeft + canvasWidth / scale;
25
+ const contentBottom = contentTop + canvasHeight / scale;
26
+
27
+ updateHorizontalRuler(horizontalRuler, contentLeft, contentRight, canvasWidth, scale, config);
28
+ updateVerticalRuler(verticalRuler, contentTop, contentBottom, canvasHeight, scale, config);
29
+
30
+ if (gridOverlay) {
31
+ updateGrid(gridOverlay, scale, translateX, translateY);
32
+ }
33
+ }
@@ -0,0 +1,31 @@
1
+ import { calculateTickSpacing, createVerticalTick } from "@/lib/rulers/ticks";
2
+ import type { RulerOptions } from "@/types/index.js";
3
+
4
+ export function updateVerticalRuler(
5
+ ruler: HTMLElement,
6
+ contentTop: number,
7
+ contentBottom: number,
8
+ canvasHeight: number,
9
+ scale: number,
10
+ config: Required<RulerOptions>
11
+ ): void {
12
+ const rulerHeight = canvasHeight;
13
+ const contentHeight = contentBottom - contentTop;
14
+
15
+ const tickSpacing = calculateTickSpacing(contentHeight, rulerHeight);
16
+ const fragment = document.createDocumentFragment();
17
+
18
+ const startTick = Math.floor(contentTop / tickSpacing) * tickSpacing;
19
+ const endTick = Math.ceil(contentBottom / tickSpacing) * tickSpacing;
20
+
21
+ for (let pos = startTick; pos <= endTick; pos += tickSpacing) {
22
+ const pixelPos = (pos - contentTop) * scale;
23
+
24
+ if (pixelPos >= -50 && pixelPos <= rulerHeight + 50) {
25
+ createVerticalTick(fragment, pos, pixelPos, tickSpacing, config);
26
+ }
27
+ }
28
+
29
+ ruler.innerHTML = "";
30
+ ruler.appendChild(fragment);
31
+ }
@@ -0,0 +1,15 @@
1
+ import { createMatrixString } from "@/lib/matrix/createMatrixString";
2
+
3
+ export function applyTransform(element: HTMLElement, matrix: DOMMatrix): boolean {
4
+ if (!element?.style || !matrix) {
5
+ return false;
6
+ }
7
+
8
+ try {
9
+ element.style.transform = createMatrixString(matrix);
10
+ return true;
11
+ } catch (error) {
12
+ console.warn("Transform application failed:", error);
13
+ return false;
14
+ }
15
+ }
@@ -0,0 +1,7 @@
1
+ import { getZoomToMouseTransform } from "@/lib/matrix";
2
+ import type { Canvas } from "@/types";
3
+
4
+ export function applyZoomToCanvas(canvas: Canvas, rawZoomFactor: number, centerX: number, centerY: number): boolean {
5
+ const newTransform = getZoomToMouseTransform(centerX, centerY, canvas.transform, rawZoomFactor, canvas.config);
6
+ return canvas.updateTransform(newTransform);
7
+ }
@@ -0,0 +1,11 @@
1
+ export function enableHardwareAcceleration(element: HTMLElement): boolean {
2
+ try {
3
+ // Set CSS properties for hardware acceleration
4
+ element.style.transform = element.style.transform || "translateZ(0)";
5
+ element.style.backfaceVisibility = "hidden";
6
+ return true;
7
+ } catch (error) {
8
+ console.error("Failed to enable hardware acceleration:", error);
9
+ return false;
10
+ }
11
+ }
@@ -0,0 +1,2 @@
1
+ export { applyTransform } from "./applyTransform.js";
2
+ export { enableHardwareAcceleration } from "./hardware-acceleration.js";
@@ -0,0 +1,33 @@
1
+ import { FALLBACK_TRANSITION_DURATION } from "@/lib/constants";
2
+ import { withDebounce } from "@/lib/helpers/index.js";
3
+ import type { MarkupCanvasConfig } from "@/types";
4
+
5
+ declare global {
6
+ interface Window {
7
+ __markupCanvasTransitionTimeout?: number;
8
+ }
9
+ }
10
+
11
+ export function disableTransition(element: HTMLElement, config: MarkupCanvasConfig): boolean {
12
+ try {
13
+ if (config.enableTransition) {
14
+ if (window.__markupCanvasTransitionTimeout) {
15
+ clearTimeout(window.__markupCanvasTransitionTimeout);
16
+ window.__markupCanvasTransitionTimeout = undefined;
17
+ }
18
+
19
+ const delay = (config.transitionDuration ?? FALLBACK_TRANSITION_DURATION) * 1000;
20
+ withDebounce("disableTransition", delay, () => {
21
+ element.style.transition = "none";
22
+ window.__markupCanvasTransitionTimeout = undefined;
23
+ });
24
+
25
+ return true;
26
+ }
27
+
28
+ return false;
29
+ } catch (error) {
30
+ console.error("Failed to disable transitions:", error);
31
+ return true;
32
+ }
33
+ }
@@ -0,0 +1,26 @@
1
+ import type { MarkupCanvasConfig } from "@/types";
2
+
3
+ declare global {
4
+ interface Window {
5
+ __markupCanvasTransitionTimeout?: number;
6
+ }
7
+ }
8
+
9
+ export function enableTransition(element: HTMLElement, config: MarkupCanvasConfig): boolean {
10
+ try {
11
+ if (config.enableTransition) {
12
+ if (window.__markupCanvasTransitionTimeout) {
13
+ clearTimeout(window.__markupCanvasTransitionTimeout);
14
+ window.__markupCanvasTransitionTimeout = undefined;
15
+ }
16
+
17
+ element.style.transition = `transform ${config.transitionDuration}s linear`;
18
+ return true;
19
+ }
20
+
21
+ return false;
22
+ } catch (error) {
23
+ console.error("Failed to enable transitions:", error);
24
+ return false;
25
+ }
26
+ }
@@ -0,0 +1,3 @@
1
+ export { disableTransition } from "./disableTransition.js";
2
+ export { enableTransition } from "./enableTransition.js";
3
+ export { withTransition } from "./withTransition.js";
@@ -0,0 +1,13 @@
1
+ import { disableTransition } from "@/lib/transition/disableTransition.js";
2
+ import { enableTransition } from "@/lib/transition/enableTransition.js";
3
+ import type { MarkupCanvasConfig } from "@/types";
4
+
5
+ export function withTransition<T>(element: HTMLElement, config: MarkupCanvasConfig, operation: () => T): T {
6
+ enableTransition(element, config);
7
+ try {
8
+ const result = operation();
9
+ return result;
10
+ } finally {
11
+ disableTransition(element, config);
12
+ }
13
+ }
@@ -0,0 +1,89 @@
1
+ import type { MarkupCanvasConfig } from "./config";
2
+
3
+ export interface Transform {
4
+ scale: number;
5
+ translateX: number;
6
+ translateY: number;
7
+ }
8
+
9
+ export interface CanvasOptions {
10
+ width?: number;
11
+ height?: number;
12
+ enableAcceleration?: boolean;
13
+ enableEventHandling?: boolean;
14
+ onTransformUpdate?: (transform: Transform) => void;
15
+ }
16
+
17
+ export interface CanvasBounds {
18
+ width: number;
19
+ height: number;
20
+ contentWidth: number;
21
+ contentHeight: number;
22
+ scale: number;
23
+ translateX: number;
24
+ translateY: number;
25
+ visibleArea: {
26
+ x: number;
27
+ y: number;
28
+ width: number;
29
+ height: number;
30
+ };
31
+ scaledContentWidth: number;
32
+ scaledContentHeight: number;
33
+ canPanLeft: boolean;
34
+ canPanRight: boolean;
35
+ canPanUp: boolean;
36
+ canPanDown: boolean;
37
+ canZoomIn: boolean;
38
+ canZoomOut: boolean;
39
+ }
40
+
41
+ export interface BaseCanvas {
42
+ container: HTMLElement;
43
+ transformLayer: HTMLElement;
44
+ contentLayer: HTMLElement;
45
+ config: Required<MarkupCanvasConfig>;
46
+ transform: Transform;
47
+ getBounds: () => CanvasBounds;
48
+ updateTransform: (newTransform: Partial<Transform>) => boolean;
49
+ reset: () => boolean;
50
+ handleResize: () => boolean;
51
+ setZoom: (zoomLevel: number) => boolean;
52
+ canvasToContent: (x: number, y: number) => { x: number; y: number };
53
+ zoomToPoint: (x: number, y: number, targetScale: number) => boolean;
54
+ resetView: () => boolean;
55
+ zoomToFitContent: () => boolean;
56
+ }
57
+
58
+ // Legacy Canvas interface - kept for backwards compatibility with other parts of the codebase
59
+ export interface Canvas extends BaseCanvas {
60
+ cleanup?: () => void;
61
+ // Exposed control functions for custom keyboard implementation
62
+ panLeft: (distance?: number) => boolean;
63
+ panRight: (distance?: number) => boolean;
64
+ panUp: (distance?: number) => boolean;
65
+ panDown: (distance?: number) => boolean;
66
+ zoomIn: (factor?: number) => boolean;
67
+ zoomOut: (factor?: number) => boolean;
68
+ resetZoom: (duration?: number) => boolean;
69
+ // Mouse drag control functions
70
+ enableMouseDrag: () => boolean;
71
+ disableMouseDrag: () => boolean;
72
+ isMouseDragEnabled: () => boolean;
73
+ // Grid control functions
74
+ toggleGrid?: () => boolean;
75
+ showGrid?: () => boolean;
76
+ hideGrid?: () => boolean;
77
+ isGridVisible?: () => boolean;
78
+ // Ruler control functions
79
+ toggleRulers?: () => boolean;
80
+ showRulers?: () => boolean;
81
+ hideRulers?: () => boolean;
82
+ areRulersVisible?: () => boolean;
83
+ // Additional utility functions
84
+ centerContent: () => boolean;
85
+ fitToScreen: () => boolean;
86
+ getVisibleArea: () => { x: number; y: number; width: number; height: number };
87
+ isPointVisible: (x: number, y: number) => boolean;
88
+ scrollToPoint: (x: number, y: number, duration?: number) => boolean;
89
+ }
@@ -0,0 +1,54 @@
1
+ import type { Transform } from "./canvas";
2
+
3
+ export interface MarkupCanvasConfig {
4
+ // Canvas dimensions
5
+ width?: number;
6
+ height?: number;
7
+ enableAcceleration?: boolean;
8
+
9
+ // Interaction controls
10
+ enableZoom?: boolean;
11
+ enablePan?: boolean;
12
+ enableTouch?: boolean;
13
+ enableKeyboard?: boolean;
14
+ limitKeyboardEventsToCanvas?: boolean;
15
+
16
+ // Zoom behavior
17
+ zoomSpeed?: number;
18
+ minZoom?: number;
19
+ maxZoom?: number;
20
+ enableTransition?: boolean;
21
+ transitionDuration?: number;
22
+ enableAdaptiveSpeed?: boolean;
23
+
24
+ // Pan behavior
25
+ enableLeftDrag?: boolean;
26
+ enableMiddleDrag?: boolean;
27
+ requireSpaceForMouseDrag?: boolean;
28
+ keyboardPanStep?: number;
29
+ keyboardFastMultiplier?: number;
30
+ keyboardZoomStep?: number;
31
+
32
+ // Click-to-zoom
33
+ enableClickToZoom?: boolean;
34
+ clickZoomLevel?: number;
35
+ requireOptionForClickZoom?: boolean;
36
+
37
+ // Visual elements
38
+ enableRulers?: boolean;
39
+ enableGrid?: boolean;
40
+ gridColor?: string;
41
+
42
+ // Ruler styling
43
+ rulerBackgroundColor?: string;
44
+ rulerBorderColor?: string;
45
+ rulerTextColor?: string;
46
+ rulerMajorTickColor?: string;
47
+ rulerMinorTickColor?: string;
48
+ rulerFontSize?: number;
49
+ rulerFontFamily?: string;
50
+ rulerUnits?: string;
51
+
52
+ // Callbacks
53
+ onTransformUpdate?: (transform: Transform) => void;
54
+ }
@@ -0,0 +1,31 @@
1
+ import type { MarkupCanvas } from "@/lib/MarkupCanvas.js";
2
+ import type { Transform } from "@/types/canvas.js";
3
+
4
+ export interface MarkupCanvasEvents {
5
+ transform: Transform;
6
+ zoom: number;
7
+ pan: { x: number; y: number };
8
+ ready: MarkupCanvas;
9
+ [key: string]: unknown;
10
+ }
11
+
12
+ export interface TouchState {
13
+ touches: Touch[];
14
+ lastDistance: number;
15
+ lastCenter: { x: number; y: number };
16
+ }
17
+
18
+ export interface GestureInfo {
19
+ isTrackpad: boolean;
20
+ isMouseWheel: boolean;
21
+ isTrackpadScroll: boolean;
22
+ isTrackpadPinch: boolean;
23
+ isZoomGesture: boolean;
24
+ }
25
+
26
+ export interface MouseDragControls {
27
+ cleanup: () => void;
28
+ enable: () => boolean;
29
+ disable: () => boolean;
30
+ isEnabled: () => boolean;
31
+ }