@markup-canvas/core 1.1.7 → 1.2.1

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 (161) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/lib/MarkupCanvas.d.ts +9 -12
  3. package/dist/lib/actions/config/getConfig.d.ts +2 -0
  4. package/dist/lib/actions/index.d.ts +3 -0
  5. package/dist/lib/actions/pan/centerContent.d.ts +2 -0
  6. package/dist/lib/actions/pan/index.d.ts +6 -0
  7. package/dist/lib/actions/pan/panDown.d.ts +2 -0
  8. package/dist/lib/actions/pan/panLeft.d.ts +2 -0
  9. package/dist/lib/actions/pan/panRight.d.ts +2 -0
  10. package/dist/lib/actions/pan/panUp.d.ts +2 -0
  11. package/dist/lib/actions/pan/scrollToPoint.d.ts +2 -0
  12. package/dist/lib/actions/transform/index.d.ts +2 -0
  13. package/dist/lib/actions/transform/resetTransform.d.ts +2 -0
  14. package/dist/lib/actions/transform/updateTransform.d.ts +2 -0
  15. package/dist/lib/actions/ui/grid/hideGrid.d.ts +2 -0
  16. package/dist/lib/actions/ui/grid/index.d.ts +4 -0
  17. package/dist/lib/actions/ui/grid/isGridVisible.d.ts +2 -0
  18. package/dist/lib/actions/ui/grid/showGrid.d.ts +2 -0
  19. package/dist/lib/actions/ui/grid/toggleGrid.d.ts +2 -0
  20. package/dist/lib/actions/ui/index.d.ts +4 -0
  21. package/dist/lib/actions/ui/rulers/areRulersVisible.d.ts +2 -0
  22. package/dist/lib/actions/ui/rulers/hideRulers.d.ts +2 -0
  23. package/dist/lib/actions/ui/rulers/index.d.ts +4 -0
  24. package/dist/lib/actions/ui/rulers/showRulers.d.ts +2 -0
  25. package/dist/lib/actions/ui/rulers/toggleRulers.d.ts +2 -0
  26. package/dist/lib/actions/ui/toggleTransition.d.ts +1 -0
  27. package/dist/lib/actions/ui/updateThemeMode.d.ts +2 -0
  28. package/dist/lib/actions/zoom/index.d.ts +6 -0
  29. package/dist/lib/actions/zoom/resetView.d.ts +2 -0
  30. package/dist/lib/actions/zoom/resetViewToCenter.d.ts +3 -0
  31. package/dist/lib/actions/zoom/setZoom.d.ts +3 -0
  32. package/dist/lib/actions/zoom/zoomIn.d.ts +3 -0
  33. package/dist/lib/actions/zoom/zoomOut.d.ts +3 -0
  34. package/dist/lib/actions/zoom/zoomToPoint.d.ts +2 -0
  35. package/dist/lib/canvas/createCanvas.d.ts +2 -2
  36. package/dist/lib/canvas/fitToScreen.d.ts +2 -0
  37. package/dist/lib/canvas/getCanvasBounds.d.ts +2 -2
  38. package/dist/lib/canvas/index.d.ts +1 -1
  39. package/dist/lib/events/emitTransformEvents.d.ts +3 -0
  40. package/dist/lib/events/keyboard/handleKeyDown.d.ts +3 -2
  41. package/dist/lib/events/keyboard/handleKeyUp.d.ts +3 -2
  42. package/dist/lib/events/keyboard/setupKeyboardEvents.d.ts +3 -2
  43. package/dist/lib/events/mouse/createMouseDragControls.d.ts +7 -0
  44. package/dist/lib/events/mouse/handleClickToZoom.d.ts +3 -2
  45. package/dist/lib/events/mouse/handleMouseDown.d.ts +3 -2
  46. package/dist/lib/events/mouse/handleMouseLeave.d.ts +3 -2
  47. package/dist/lib/events/mouse/handleMouseMove.d.ts +3 -2
  48. package/dist/lib/events/mouse/handleMouseUp.d.ts +3 -2
  49. package/dist/lib/events/mouse/setupMouseDrag.d.ts +4 -3
  50. package/dist/lib/events/mouse/setupMouseEvents.d.ts +4 -3
  51. package/dist/lib/events/touch/handleTouchMove.d.ts +3 -2
  52. package/dist/lib/events/touch/setupTouchEvents.d.ts +2 -2
  53. package/dist/lib/events/trackpad/createTrackpadPanHandler.d.ts +2 -2
  54. package/dist/lib/events/utils/getAdaptiveZoomSpeed.d.ts +2 -2
  55. package/dist/lib/events/utils/getViewportCenter.d.ts +2 -2
  56. package/dist/lib/events/utils/resetDragState.d.ts +3 -2
  57. package/dist/lib/events/utils/updateCursor.d.ts +3 -2
  58. package/dist/lib/events/wheel/handleWheel.d.ts +3 -2
  59. package/dist/lib/events/wheel/setupWheelEvents.d.ts +3 -2
  60. package/dist/lib/events/wheel/setupWheelHandler.d.ts +3 -2
  61. package/dist/lib/helpers/getVisibleArea.d.ts +7 -0
  62. package/dist/lib/helpers/index.d.ts +2 -0
  63. package/dist/lib/helpers/isPointVisible.d.ts +2 -0
  64. package/dist/lib/transform/applyZoomToCanvas.d.ts +2 -2
  65. package/dist/lib/window/bindCanvasToWindow.d.ts +3 -0
  66. package/dist/lib/window/broadcastEvent.d.ts +2 -0
  67. package/dist/lib/window/cleanupWindowBinding.d.ts +2 -0
  68. package/dist/lib/window/index.d.ts +3 -0
  69. package/dist/markup-canvas.cjs.js +689 -529
  70. package/dist/markup-canvas.esm.js +689 -529
  71. package/dist/markup-canvas.umd.js +687 -524
  72. package/dist/markup-canvas.umd.min.js +1 -1
  73. package/dist/types/canvas.d.ts +1 -48
  74. package/dist/types/config.d.ts +0 -3
  75. package/dist/types/events.d.ts +4 -1
  76. package/dist/types/index.d.ts +3 -2
  77. package/dist/types/window.d.ts +84 -0
  78. package/package.json +1 -1
  79. package/src/index.ts +1 -1
  80. package/src/lib/MarkupCanvas.ts +139 -315
  81. package/src/lib/actions/config/getConfig.ts +5 -0
  82. package/src/lib/actions/index.ts +6 -0
  83. package/src/lib/actions/pan/centerContent.ts +21 -0
  84. package/src/lib/actions/pan/index.ts +6 -0
  85. package/src/lib/actions/pan/panDown.ts +13 -0
  86. package/src/lib/actions/pan/panLeft.ts +13 -0
  87. package/src/lib/actions/pan/panRight.ts +13 -0
  88. package/src/lib/actions/pan/panUp.ts +13 -0
  89. package/src/lib/actions/pan/scrollToPoint.ts +27 -0
  90. package/src/lib/actions/transform/index.ts +2 -0
  91. package/src/lib/actions/transform/resetTransform.ts +11 -0
  92. package/src/lib/actions/transform/updateTransform.ts +9 -0
  93. package/src/lib/actions/ui/grid/hideGrid.ts +9 -0
  94. package/src/lib/actions/ui/grid/index.ts +4 -0
  95. package/src/lib/actions/ui/grid/isGridVisible.ts +8 -0
  96. package/src/lib/actions/ui/grid/showGrid.ts +9 -0
  97. package/src/lib/actions/ui/grid/toggleGrid.ts +9 -0
  98. package/src/lib/actions/ui/index.ts +4 -0
  99. package/src/lib/actions/ui/rulers/areRulersVisible.ts +8 -0
  100. package/src/lib/actions/ui/rulers/hideRulers.ts +9 -0
  101. package/src/lib/actions/ui/rulers/index.ts +4 -0
  102. package/src/lib/actions/ui/rulers/showRulers.ts +9 -0
  103. package/src/lib/actions/ui/rulers/toggleRulers.ts +14 -0
  104. package/src/lib/actions/ui/toggleTransition.ts +3 -0
  105. package/src/lib/actions/ui/updateThemeMode.ts +25 -0
  106. package/src/lib/actions/zoom/index.ts +6 -0
  107. package/src/lib/actions/zoom/resetView.ts +17 -0
  108. package/src/lib/actions/zoom/resetViewToCenter.ts +21 -0
  109. package/src/lib/actions/zoom/setZoom.ts +22 -0
  110. package/src/lib/actions/zoom/zoomIn.ts +21 -0
  111. package/src/lib/actions/zoom/zoomOut.ts +21 -0
  112. package/src/lib/actions/zoom/zoomToPoint.ts +18 -0
  113. package/src/lib/canvas/createCanvas.ts +6 -14
  114. package/src/lib/canvas/fitToScreen.ts +27 -0
  115. package/src/lib/canvas/getCanvasBounds.ts +3 -4
  116. package/src/lib/canvas/index.ts +1 -1
  117. package/src/lib/config/constants.ts +2 -6
  118. package/src/lib/config/presets/editor-preset.ts +2 -6
  119. package/src/lib/events/emitTransformEvents.ts +9 -0
  120. package/src/lib/events/keyboard/handleKeyDown.ts +3 -2
  121. package/src/lib/events/keyboard/handleKeyUp.ts +3 -2
  122. package/src/lib/events/keyboard/setupKeyboardEvents.ts +3 -2
  123. package/src/lib/events/mouse/createMouseDragControls.ts +21 -0
  124. package/src/lib/events/mouse/handleClickToZoom.ts +3 -2
  125. package/src/lib/events/mouse/handleMouseDown.ts +3 -2
  126. package/src/lib/events/mouse/handleMouseLeave.ts +3 -2
  127. package/src/lib/events/mouse/handleMouseMove.ts +3 -2
  128. package/src/lib/events/mouse/handleMouseUp.ts +3 -2
  129. package/src/lib/events/mouse/setupMouseDrag.ts +5 -4
  130. package/src/lib/events/mouse/setupMouseEvents.ts +5 -4
  131. package/src/lib/events/postMessage/setupPostMessageEvents.ts +10 -0
  132. package/src/lib/events/touch/handleTouchMove.ts +3 -2
  133. package/src/lib/events/touch/setupTouchEvents.ts +3 -2
  134. package/src/lib/events/trackpad/createTrackpadPanHandler.ts +3 -2
  135. package/src/lib/events/utils/getAdaptiveZoomSpeed.ts +2 -2
  136. package/src/lib/events/utils/getViewportCenter.ts +2 -2
  137. package/src/lib/events/utils/resetDragState.ts +3 -2
  138. package/src/lib/events/utils/updateCursor.ts +3 -2
  139. package/src/lib/events/wheel/handleWheel.ts +3 -2
  140. package/src/lib/events/wheel/setupWheelEvents.ts +3 -2
  141. package/src/lib/events/wheel/setupWheelHandler.ts +3 -2
  142. package/src/lib/helpers/getVisibleArea.ts +6 -0
  143. package/src/lib/helpers/index.ts +2 -0
  144. package/src/lib/helpers/isPointVisible.ts +7 -0
  145. package/src/lib/rulers/createRulers.ts +0 -1
  146. package/src/lib/transform/applyZoomToCanvas.ts +2 -2
  147. package/src/lib/window/bindCanvasToWindow.ts +128 -0
  148. package/src/lib/window/broadcastEvent.ts +38 -0
  149. package/src/lib/window/cleanupWindowBinding.ts +15 -0
  150. package/src/lib/window/index.ts +3 -0
  151. package/src/types/canvas.ts +1 -48
  152. package/src/types/config.ts +1 -7
  153. package/src/types/events.ts +7 -1
  154. package/src/types/index.ts +4 -2
  155. package/src/types/window.ts +77 -0
  156. package/dist/lib/canvas/config.d.ts +0 -2
  157. package/dist/lib/canvas/getCanvasMethods.d.ts +0 -12
  158. package/src/lib/canvas/config.ts +0 -29
  159. package/src/lib/canvas/getCanvasMethods.ts +0 -102
  160. /package/dist/lib/canvas/{calcVisibleArea.d.ts → calculateVisibleArea.d.ts} +0 -0
  161. /package/src/lib/canvas/{calcVisibleArea.ts → calculateVisibleArea.ts} +0 -0
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Markup Canvas
3
3
  * High-performance markup canvas with zoom and pan capabilities
4
- * @version 1.1.7
4
+ * @version 1.2.1
5
5
  */
6
6
  (function (global, factory) {
7
7
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
@@ -9,68 +9,6 @@
9
9
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.MarkupCanvas = factory());
10
10
  })(this, (function () { 'use strict';
11
11
 
12
- // Default transform values
13
- const DEFAULT_ZOOM = 1.0;
14
- // Validation thresholds
15
- const ZOOM_CHANGE_THRESHOLD = 0.001;
16
- // CSS transition values
17
- const FALLBACK_TRANSITION_DURATION = 0.2;
18
- // Zoom to fit padding factor
19
- const ZOOM_FIT_PADDING = 0.9;
20
- // CSS class names
21
- const CANVAS_CONTAINER_CLASS = "canvas-container";
22
- const TRANSFORM_LAYER_CLASS = "transform-layer";
23
- const CONTENT_LAYER_CLASS = "content-layer";
24
-
25
- function moveExistingContent(existingContent, contentLayer, transformLayer) {
26
- existingContent.forEach((child) => {
27
- if (child !== transformLayer && !child.classList.contains(TRANSFORM_LAYER_CLASS)) {
28
- contentLayer.appendChild(child);
29
- }
30
- });
31
- }
32
-
33
- function setupContentLayer(contentLayer) {
34
- contentLayer.style.position = "relative";
35
- contentLayer.style.width = "100%";
36
- contentLayer.style.height = "100%";
37
- contentLayer.style.pointerEvents = "auto";
38
- }
39
-
40
- // Sets up the transform layer with proper styles and dimensions
41
- function setupTransformLayer(transformLayer, config) {
42
- transformLayer.style.position = "absolute";
43
- const rulerOffset = config.rulerSize;
44
- transformLayer.style.top = `${rulerOffset}px`;
45
- transformLayer.style.left = `${rulerOffset}px`;
46
- transformLayer.style.width = `${config.width}px`;
47
- transformLayer.style.height = `${config.height}px`;
48
- transformLayer.style.transformOrigin = "0 0";
49
- }
50
-
51
- function createCanvasLayers(container, config) {
52
- const existingContent = Array.from(container.children);
53
- // Create or find transform layer
54
- let transformLayer = container.querySelector(`.${TRANSFORM_LAYER_CLASS}`);
55
- if (!transformLayer) {
56
- transformLayer = document.createElement("div");
57
- transformLayer.className = TRANSFORM_LAYER_CLASS;
58
- container.appendChild(transformLayer);
59
- }
60
- setupTransformLayer(transformLayer, config);
61
- // Create or find content layer
62
- let contentLayer = transformLayer.querySelector(`.${CONTENT_LAYER_CLASS}`);
63
- if (!contentLayer) {
64
- contentLayer = document.createElement("div");
65
- contentLayer.className = CONTENT_LAYER_CLASS;
66
- transformLayer.appendChild(contentLayer);
67
- moveExistingContent(existingContent, contentLayer, transformLayer);
68
- }
69
- // Set content layer properties
70
- setupContentLayer(contentLayer);
71
- return { transformLayer, contentLayer };
72
- }
73
-
74
12
  function canvasToContent(canvasX, canvasY, matrix) {
75
13
  if (!matrix?.inverse) {
76
14
  return { x: canvasX, y: canvasY };
@@ -98,6 +36,19 @@
98
36
  return new DOMMatrix([scale, 0, 0, scale, translateX, translateY]);
99
37
  }
100
38
 
39
+ // Default transform values
40
+ const DEFAULT_ZOOM = 1.0;
41
+ // Validation thresholds
42
+ const ZOOM_CHANGE_THRESHOLD = 0.001;
43
+ // CSS transition values
44
+ const FALLBACK_TRANSITION_DURATION = 0.2;
45
+ // Zoom to fit padding factor
46
+ const ZOOM_FIT_PADDING = 0.9;
47
+ // CSS class names
48
+ const CANVAS_CONTAINER_CLASS = "canvas-container";
49
+ const TRANSFORM_LAYER_CLASS = "transform-layer";
50
+ const CONTENT_LAYER_CLASS = "content-layer";
51
+
101
52
  function getZoomToMouseTransform(mouseX, mouseY, currentTransform, zoomFactor, config) {
102
53
  const rulerOffset = config.enableRulers ? -config.rulerSize : 0;
103
54
  const transform = currentTransform || {
@@ -159,6 +110,16 @@
159
110
  };
160
111
  }
161
112
 
113
+ function getVisibleArea(canvas) {
114
+ const bounds = canvas.getBounds();
115
+ return bounds.visibleArea;
116
+ }
117
+
118
+ function isPointVisible(canvas, x, y) {
119
+ const visibleArea = getVisibleArea(canvas);
120
+ return x >= visibleArea.x && x <= visibleArea.x + visibleArea.width && y >= visibleArea.y && y <= visibleArea.y + visibleArea.height;
121
+ }
122
+
162
123
  function withClampedZoom(config, operation) {
163
124
  const clampFunction = (scale) => clampZoom(scale, config);
164
125
  return operation(clampFunction);
@@ -245,8 +206,7 @@
245
206
  width: 8000,
246
207
  height: 8000,
247
208
  enableAcceleration: true,
248
- // Global Binding & Instance Access
249
- bindToWindow: false,
209
+ // Global Instance Access
250
210
  name: "markupCanvas",
251
211
  enablePostMessageAPI: false,
252
212
  // Interaction controls
@@ -254,7 +214,7 @@
254
214
  enablePan: true,
255
215
  enableTouch: true,
256
216
  enableKeyboard: true,
257
- bindKeyboardEventsTo: "canvas",
217
+ bindKeyboardEventsTo: "document",
258
218
  // Zoom behavior
259
219
  zoomSpeed: 1.5,
260
220
  minZoom: 0.05,
@@ -300,14 +260,11 @@
300
260
  gridColorDark: "rgba(232, 86, 193, 0.5)",
301
261
  // Theme
302
262
  themeMode: "light",
303
- // Callbacks
304
- onTransformUpdate: () => { },
305
263
  };
306
264
 
307
- function getCanvasBounds(canvas) {
265
+ function getCanvasBounds(canvas, config) {
308
266
  try {
309
267
  const container = canvas.container;
310
- const config = canvas.config;
311
268
  const transform = canvas.transform || {
312
269
  scale: 1.0,
313
270
  translateX: 0,
@@ -357,37 +314,6 @@
357
314
  }
358
315
  }
359
316
 
360
- function createMatrixString(matrix) {
361
- return `matrix3d(${matrix.m11}, ${matrix.m12}, ${matrix.m13}, ${matrix.m14}, ${matrix.m21}, ${matrix.m22}, ${matrix.m23}, ${matrix.m24}, ${matrix.m31}, ${matrix.m32}, ${matrix.m33}, ${matrix.m34}, ${matrix.m41}, ${matrix.m42}, ${matrix.m43}, ${matrix.m44})`;
362
- }
363
-
364
- function applyTransform(element, matrix) {
365
- if (!element?.style || !matrix) {
366
- return false;
367
- }
368
- try {
369
- element.style.transform = createMatrixString(matrix);
370
- return true;
371
- }
372
- catch (error) {
373
- console.warn("Transform application failed:", error);
374
- return false;
375
- }
376
- }
377
-
378
- function enableHardwareAcceleration(element) {
379
- try {
380
- // Set CSS properties for hardware acceleration
381
- element.style.transform = element.style.transform || "translateZ(0)";
382
- element.style.backfaceVisibility = "hidden";
383
- return true;
384
- }
385
- catch (error) {
386
- console.error("Failed to enable hardware acceleration:", error);
387
- return false;
388
- }
389
- }
390
-
391
317
  function disableTransition(element, config) {
392
318
  try {
393
319
  if (config.enableTransition) {
@@ -439,85 +365,386 @@
439
365
  }
440
366
  }
441
367
 
442
- function getCanvasMethods() {
443
- return {
444
- // Utility methods
445
- getBounds: function () {
446
- return getCanvasBounds(this);
447
- },
448
- // Transform methods
449
- updateTransform: function (newTransform) {
450
- this.transform = { ...this.transform, ...newTransform };
451
- const matrix = createMatrix(this.transform.scale, this.transform.translateX, this.transform.translateY);
452
- const result = applyTransform(this.transformLayer, matrix);
453
- withFeatureEnabled(this.config, "onTransformUpdate", () => {
454
- this.config.onTransformUpdate(this.transform);
455
- });
456
- return result;
457
- },
458
- // Reset method
459
- reset: function () {
460
- const resetTransform = {
461
- scale: 1.0,
462
- translateX: 0,
463
- translateY: 0,
464
- };
465
- return this.updateTransform(resetTransform);
466
- },
467
- // Handle canvas resize
468
- handleResize: function () {
469
- return true;
470
- },
471
- // Set zoom level
472
- setZoom: function (zoomLevel) {
473
- const newScale = withClampedZoom(this.config, (clamp) => clamp(zoomLevel));
474
- return this.updateTransform({ scale: newScale });
475
- },
476
- // Convert canvas coordinates to content coordinates
477
- canvasToContent: function (x, y) {
478
- const matrix = createMatrix(this.transform.scale, this.transform.translateX, this.transform.translateY);
479
- return canvasToContent(x, y, matrix);
480
- },
481
- // Zoom to a specific point with animation
482
- zoomToPoint: function (x, y, targetScale) {
483
- return withTransition(this.transformLayer, this.config, () => {
484
- const newTransform = getZoomToMouseTransform(x, y, this.transform, targetScale / this.transform.scale, this.config);
485
- return this.updateTransform(newTransform);
486
- });
487
- },
488
- // Reset view with animation
489
- resetView: function () {
490
- return withTransition(this.transformLayer, this.config, () => {
491
- return withRulerSize(this, this.config.rulerSize, (rulerSize) => {
492
- const resetTransform = {
493
- scale: 1.0,
494
- translateX: rulerSize * -1,
495
- translateY: rulerSize * -1,
496
- };
497
- return this.updateTransform(resetTransform);
498
- });
499
- });
500
- },
501
- // Zoom to fit content in canvas
502
- zoomToFitContent: function () {
503
- return withTransition(this.transformLayer, this.config, () => {
504
- const bounds = this.getBounds();
505
- const scaleX = bounds.width / this.config.width;
506
- const scaleY = bounds.height / this.config.height;
507
- const fitScale = withClampedZoom(this.config, (clamp) => clamp(Math.min(scaleX, scaleY) * ZOOM_FIT_PADDING));
508
- // Center the content
509
- const scaledWidth = this.config.width * fitScale;
510
- const scaledHeight = this.config.height * fitScale;
511
- const centerX = (bounds.width - scaledWidth) / 2;
512
- const centerY = (bounds.height - scaledHeight) / 2;
513
- return this.updateTransform({
514
- scale: fitScale,
515
- translateX: centerX,
516
- translateY: centerY,
517
- });
518
- });
519
- },
520
- };
368
+ function centerContent(canvas, config, updateTransformFn, transformLayer) {
369
+ return withTransition(transformLayer, config, () => {
370
+ const bounds = getCanvasBounds(canvas, config);
371
+ const centerX = (bounds.width - bounds.contentWidth * canvas.transform.scale) / 2;
372
+ const centerY = (bounds.height - bounds.contentHeight * canvas.transform.scale) / 2;
373
+ return updateTransformFn({
374
+ translateX: centerX,
375
+ translateY: centerY,
376
+ });
377
+ });
378
+ }
379
+
380
+ function panDown(canvas, config, updateTransform) {
381
+ const panDistance = config.keyboardPanStep;
382
+ const newTransform = {
383
+ translateY: canvas.transform.translateY - panDistance,
384
+ };
385
+ return updateTransform(newTransform);
386
+ }
387
+
388
+ function panLeft(canvas, config, updateTransform) {
389
+ const panDistance = config.keyboardPanStep;
390
+ const newTransform = {
391
+ translateX: canvas.transform.translateX + panDistance,
392
+ };
393
+ return updateTransform(newTransform);
394
+ }
395
+
396
+ function panRight(canvas, config, updateTransform) {
397
+ const panDistance = config.keyboardPanStep;
398
+ const newTransform = {
399
+ translateX: canvas.transform.translateX - panDistance,
400
+ };
401
+ return updateTransform(newTransform);
402
+ }
403
+
404
+ function panUp(canvas, config, updateTransform) {
405
+ const panDistance = config.keyboardPanStep;
406
+ const newTransform = {
407
+ translateY: canvas.transform.translateY + panDistance,
408
+ };
409
+ return updateTransform(newTransform);
410
+ }
411
+
412
+ function scrollToPoint(canvas, config, x, y, updateTransform, transformLayer) {
413
+ return withTransition(transformLayer, config, () => {
414
+ const bounds = getCanvasBounds(canvas, config);
415
+ const centerX = bounds.width / 2;
416
+ const centerY = bounds.height / 2;
417
+ // Calculate new translation to center the point
418
+ const newTranslateX = centerX - x * canvas.transform.scale;
419
+ const newTranslateY = centerY - y * canvas.transform.scale;
420
+ return updateTransform({
421
+ translateX: newTranslateX,
422
+ translateY: newTranslateY,
423
+ });
424
+ });
425
+ }
426
+
427
+ function createMatrixString(matrix) {
428
+ return `matrix3d(${matrix.m11}, ${matrix.m12}, ${matrix.m13}, ${matrix.m14}, ${matrix.m21}, ${matrix.m22}, ${matrix.m23}, ${matrix.m24}, ${matrix.m31}, ${matrix.m32}, ${matrix.m33}, ${matrix.m34}, ${matrix.m41}, ${matrix.m42}, ${matrix.m43}, ${matrix.m44})`;
429
+ }
430
+
431
+ function applyTransform(element, matrix) {
432
+ if (!element?.style || !matrix) {
433
+ return false;
434
+ }
435
+ try {
436
+ element.style.transform = createMatrixString(matrix);
437
+ return true;
438
+ }
439
+ catch (error) {
440
+ console.warn("Transform application failed:", error);
441
+ return false;
442
+ }
443
+ }
444
+
445
+ function enableHardwareAcceleration(element) {
446
+ try {
447
+ // Set CSS properties for hardware acceleration
448
+ element.style.transform = element.style.transform || "translateZ(0)";
449
+ element.style.backfaceVisibility = "hidden";
450
+ return true;
451
+ }
452
+ catch (error) {
453
+ console.error("Failed to enable hardware acceleration:", error);
454
+ return false;
455
+ }
456
+ }
457
+
458
+ function updateTransform(canvas, newTransform) {
459
+ canvas.transform = { ...canvas.transform, ...newTransform };
460
+ const matrix = createMatrix(canvas.transform.scale, canvas.transform.translateX, canvas.transform.translateY);
461
+ return applyTransform(canvas.transformLayer, matrix);
462
+ }
463
+
464
+ function resetTransform(canvas) {
465
+ const resetTransformData = {
466
+ scale: 1.0,
467
+ translateX: 0,
468
+ translateY: 0,
469
+ };
470
+ return updateTransform(canvas, resetTransformData);
471
+ }
472
+
473
+ function hideGrid(rulers) {
474
+ if (rulers?.gridOverlay) {
475
+ rulers.gridOverlay.style.display = "none";
476
+ return true;
477
+ }
478
+ return false;
479
+ }
480
+
481
+ function isGridVisible(rulers) {
482
+ if (rulers?.gridOverlay) {
483
+ return rulers.gridOverlay.style.display !== "none";
484
+ }
485
+ return false;
486
+ }
487
+
488
+ function showGrid(rulers) {
489
+ if (rulers?.gridOverlay) {
490
+ rulers.gridOverlay.style.display = "block";
491
+ return true;
492
+ }
493
+ return false;
494
+ }
495
+
496
+ function toggleGrid(rulers) {
497
+ if (rulers?.toggleGrid) {
498
+ rulers.toggleGrid();
499
+ return true;
500
+ }
501
+ return false;
502
+ }
503
+
504
+ function areRulersVisible(rulers) {
505
+ if (rulers?.horizontalRuler) {
506
+ return rulers.horizontalRuler.style.display !== "none";
507
+ }
508
+ return false;
509
+ }
510
+
511
+ function hideRulers(rulers) {
512
+ if (rulers) {
513
+ rulers.hide();
514
+ return true;
515
+ }
516
+ return false;
517
+ }
518
+
519
+ function showRulers(rulers) {
520
+ if (rulers) {
521
+ rulers.show();
522
+ return true;
523
+ }
524
+ return false;
525
+ }
526
+
527
+ function toggleRulers(rulers, areRulersVisible) {
528
+ if (rulers) {
529
+ const isVisible = areRulersVisible();
530
+ if (isVisible) {
531
+ rulers.hide();
532
+ }
533
+ else {
534
+ rulers.show();
535
+ }
536
+ return true;
537
+ }
538
+ return false;
539
+ }
540
+
541
+ function toggleTransition(enableTransition) {
542
+ return !enableTransition;
543
+ }
544
+
545
+ function createMarkupCanvasConfig(options = {}) {
546
+ const config = {
547
+ ...DEFAULT_CONFIG,
548
+ ...options,
549
+ };
550
+ if (typeof config.width !== "number" || config.width <= 0) {
551
+ console.warn("Invalid width, using default");
552
+ config.width = DEFAULT_CONFIG.width;
553
+ }
554
+ if (typeof config.height !== "number" || config.height <= 0) {
555
+ console.warn("Invalid height, using default");
556
+ config.height = DEFAULT_CONFIG.height;
557
+ }
558
+ if (typeof config.zoomSpeed !== "number" || config.zoomSpeed <= 0) {
559
+ console.warn("Invalid zoomSpeed, using default");
560
+ config.zoomSpeed = DEFAULT_CONFIG.zoomSpeed;
561
+ }
562
+ if (typeof config.minZoom !== "number" || config.minZoom <= 0) {
563
+ console.warn("Invalid minZoom, using default");
564
+ config.minZoom = DEFAULT_CONFIG.minZoom;
565
+ }
566
+ if (typeof config.maxZoom !== "number" || config.maxZoom <= config.minZoom) {
567
+ console.warn("Invalid maxZoom, using default");
568
+ config.maxZoom = DEFAULT_CONFIG.maxZoom;
569
+ }
570
+ if (typeof config.keyboardPanStep !== "number" || config.keyboardPanStep <= 0) {
571
+ console.warn("Invalid keyboardPanStep, using default");
572
+ config.keyboardPanStep = DEFAULT_CONFIG.keyboardPanStep;
573
+ }
574
+ if (typeof config.keyboardFastMultiplier !== "number" || config.keyboardFastMultiplier <= 0) {
575
+ console.warn("Invalid keyboardFastMultiplier, using default");
576
+ config.keyboardFastMultiplier = DEFAULT_CONFIG.keyboardFastMultiplier;
577
+ }
578
+ if (typeof config.clickZoomLevel !== "number" || config.clickZoomLevel <= 0) {
579
+ console.warn("Invalid clickZoomLevel, using default");
580
+ config.clickZoomLevel = DEFAULT_CONFIG.clickZoomLevel;
581
+ }
582
+ if (typeof config.rulerFontSize !== "number" || config.rulerFontSize <= 0) {
583
+ console.warn("Invalid rulerFontSize, using default");
584
+ config.rulerFontSize = DEFAULT_CONFIG.rulerFontSize;
585
+ }
586
+ if (typeof config.rulerSize !== "number" || config.rulerSize <= 0) {
587
+ console.warn("Invalid rulerSize, using default");
588
+ config.rulerSize = DEFAULT_CONFIG.rulerSize;
589
+ }
590
+ return config;
591
+ }
592
+
593
+ function updateThemeMode(canvasContainer, config, rulers, mode) {
594
+ const newConfig = {
595
+ ...config,
596
+ themeMode: mode,
597
+ };
598
+ const updatedConfig = createMarkupCanvasConfig(newConfig);
599
+ // Update canvas background color
600
+ const backgroundColor = getThemeValue(updatedConfig, "canvasBackgroundColor");
601
+ canvasContainer.style.backgroundColor = backgroundColor;
602
+ // Update rulers if they exist
603
+ if (rulers) {
604
+ rulers.updateTheme(updatedConfig);
605
+ }
606
+ }
607
+
608
+ function resetView(canvas, transformLayer, config) {
609
+ return withTransition(transformLayer, config, () => {
610
+ return withRulerSize(canvas, config.rulerSize, (rulerSize) => {
611
+ const resetTransformData = {
612
+ scale: 1.0,
613
+ translateX: rulerSize * -1,
614
+ translateY: rulerSize * -1,
615
+ };
616
+ return updateTransform(canvas, resetTransformData);
617
+ });
618
+ });
619
+ }
620
+
621
+ function getViewportCenter(canvas) {
622
+ try {
623
+ const bounds = canvas.getBounds();
624
+ return {
625
+ x: bounds.width / 2,
626
+ y: bounds.height / 2,
627
+ };
628
+ }
629
+ catch (error) {
630
+ console.warn("Failed to calculate viewport center:", error);
631
+ return { x: 0, y: 0 };
632
+ }
633
+ }
634
+
635
+ function resetViewToCenter(canvas, transformLayer, config, zoomToPoint) {
636
+ return withTransition(transformLayer, config, () => {
637
+ return withClampedZoom(config, (clamp) => {
638
+ const newScale = clamp(1.0);
639
+ const center = getViewportCenter(canvas);
640
+ return zoomToPoint(center.x, center.y, newScale);
641
+ });
642
+ });
643
+ }
644
+
645
+ function setZoom(canvas, transformLayer, config, zoomToPoint, zoomLevel) {
646
+ return withTransition(transformLayer, config, () => {
647
+ return withClampedZoom(config, (clamp) => {
648
+ const newScale = clamp(zoomLevel);
649
+ const center = getViewportCenter(canvas);
650
+ return zoomToPoint(center.x, center.y, newScale);
651
+ });
652
+ });
653
+ }
654
+
655
+ function zoomIn(canvas, transformLayer, config, zoomToPoint, factor = 0.5) {
656
+ return withTransition(transformLayer, config, () => {
657
+ return withClampedZoom(config, (clamp) => {
658
+ const newScale = clamp(canvas.transform.scale * (1 + factor));
659
+ const center = getViewportCenter(canvas);
660
+ return zoomToPoint(center.x, center.y, newScale);
661
+ });
662
+ });
663
+ }
664
+
665
+ function zoomOut(canvas, transformLayer, config, zoomToPoint, factor = 0.5) {
666
+ return withTransition(transformLayer, config, () => {
667
+ return withClampedZoom(config, (clamp) => {
668
+ const newScale = clamp(canvas.transform.scale * (1 - factor));
669
+ const center = getViewportCenter(canvas);
670
+ return zoomToPoint(center.x, center.y, newScale);
671
+ });
672
+ });
673
+ }
674
+
675
+ function zoomToPoint(canvas, transformLayer, config, x, y, targetScale) {
676
+ return withTransition(transformLayer, config, () => {
677
+ const newTransform = getZoomToMouseTransform(x, y, canvas.transform, targetScale / canvas.transform.scale, config);
678
+ return updateTransform(canvas, newTransform);
679
+ });
680
+ }
681
+
682
+ function fitToScreen(canvas, transformLayer, config) {
683
+ return withTransition(transformLayer, config, () => {
684
+ const bounds = getCanvasBounds(canvas, config);
685
+ const scaleX = bounds.width / config.width;
686
+ const scaleY = bounds.height / config.height;
687
+ const fitScale = withClampedZoom(config, (clamp) => clamp(Math.min(scaleX, scaleY) * ZOOM_FIT_PADDING));
688
+ // Center the content
689
+ const scaledWidth = config.width * fitScale;
690
+ const scaledHeight = config.height * fitScale;
691
+ const centerX = (bounds.width - scaledWidth) / 2;
692
+ const centerY = (bounds.height - scaledHeight) / 2;
693
+ return updateTransform(canvas, {
694
+ scale: fitScale,
695
+ translateX: centerX,
696
+ translateY: centerY,
697
+ });
698
+ });
699
+ }
700
+
701
+ function moveExistingContent(existingContent, contentLayer, transformLayer) {
702
+ existingContent.forEach((child) => {
703
+ if (child !== transformLayer && !child.classList.contains(TRANSFORM_LAYER_CLASS)) {
704
+ contentLayer.appendChild(child);
705
+ }
706
+ });
707
+ }
708
+
709
+ function setupContentLayer(contentLayer) {
710
+ contentLayer.style.position = "relative";
711
+ contentLayer.style.width = "100%";
712
+ contentLayer.style.height = "100%";
713
+ contentLayer.style.pointerEvents = "auto";
714
+ }
715
+
716
+ // Sets up the transform layer with proper styles and dimensions
717
+ function setupTransformLayer(transformLayer, config) {
718
+ transformLayer.style.position = "absolute";
719
+ const rulerOffset = config.rulerSize;
720
+ transformLayer.style.top = `${rulerOffset}px`;
721
+ transformLayer.style.left = `${rulerOffset}px`;
722
+ transformLayer.style.width = `${config.width}px`;
723
+ transformLayer.style.height = `${config.height}px`;
724
+ transformLayer.style.transformOrigin = "0 0";
725
+ }
726
+
727
+ function createCanvasLayers(container, config) {
728
+ const existingContent = Array.from(container.children);
729
+ // Create or find transform layer
730
+ let transformLayer = container.querySelector(`.${TRANSFORM_LAYER_CLASS}`);
731
+ if (!transformLayer) {
732
+ transformLayer = document.createElement("div");
733
+ transformLayer.className = TRANSFORM_LAYER_CLASS;
734
+ container.appendChild(transformLayer);
735
+ }
736
+ setupTransformLayer(transformLayer, config);
737
+ // Create or find content layer
738
+ let contentLayer = transformLayer.querySelector(`.${CONTENT_LAYER_CLASS}`);
739
+ if (!contentLayer) {
740
+ contentLayer = document.createElement("div");
741
+ contentLayer.className = CONTENT_LAYER_CLASS;
742
+ transformLayer.appendChild(contentLayer);
743
+ moveExistingContent(existingContent, contentLayer, transformLayer);
744
+ }
745
+ // Set content layer properties
746
+ setupContentLayer(contentLayer);
747
+ return { transformLayer, contentLayer };
521
748
  }
522
749
 
523
750
  function checkContainerDimensions(container) {
@@ -553,7 +780,6 @@
553
780
  }
554
781
  }
555
782
 
556
- // Creates and initializes a canvas with the required DOM structure
557
783
  function createCanvas(container, config) {
558
784
  if (!container?.appendChild) {
559
785
  console.error("Invalid container element provided to createCanvas");
@@ -562,10 +788,9 @@
562
788
  try {
563
789
  setupCanvasContainer(container, config);
564
790
  const { transformLayer, contentLayer } = createCanvasLayers(container, config);
565
- // Enable hardware acceleration if requested
566
- if (config.enableAcceleration) {
791
+ withFeatureEnabled(config, "enableAcceleration", () => {
567
792
  enableHardwareAcceleration(transformLayer);
568
- }
793
+ });
569
794
  const rulerOffset = config.enableRulers ? -config.rulerSize : 0;
570
795
  const initialTransform = {
571
796
  scale: DEFAULT_ZOOM,
@@ -580,12 +805,8 @@
580
805
  container,
581
806
  transformLayer,
582
807
  contentLayer,
583
- // Configuration
584
- config: config,
585
808
  // Current state
586
809
  transform: initialTransform,
587
- // Add all canvas methods
588
- ...getCanvasMethods(),
589
810
  };
590
811
  return canvas;
591
812
  }
@@ -595,54 +816,6 @@
595
816
  }
596
817
  }
597
818
 
598
- function createMarkupCanvasConfig(options = {}) {
599
- const config = {
600
- ...DEFAULT_CONFIG,
601
- ...options,
602
- };
603
- if (typeof config.width !== "number" || config.width <= 0) {
604
- console.warn("Invalid width, using default");
605
- config.width = DEFAULT_CONFIG.width;
606
- }
607
- if (typeof config.height !== "number" || config.height <= 0) {
608
- console.warn("Invalid height, using default");
609
- config.height = DEFAULT_CONFIG.height;
610
- }
611
- if (typeof config.zoomSpeed !== "number" || config.zoomSpeed <= 0) {
612
- console.warn("Invalid zoomSpeed, using default");
613
- config.zoomSpeed = DEFAULT_CONFIG.zoomSpeed;
614
- }
615
- if (typeof config.minZoom !== "number" || config.minZoom <= 0) {
616
- console.warn("Invalid minZoom, using default");
617
- config.minZoom = DEFAULT_CONFIG.minZoom;
618
- }
619
- if (typeof config.maxZoom !== "number" || config.maxZoom <= config.minZoom) {
620
- console.warn("Invalid maxZoom, using default");
621
- config.maxZoom = DEFAULT_CONFIG.maxZoom;
622
- }
623
- if (typeof config.keyboardPanStep !== "number" || config.keyboardPanStep <= 0) {
624
- console.warn("Invalid keyboardPanStep, using default");
625
- config.keyboardPanStep = DEFAULT_CONFIG.keyboardPanStep;
626
- }
627
- if (typeof config.keyboardFastMultiplier !== "number" || config.keyboardFastMultiplier <= 0) {
628
- console.warn("Invalid keyboardFastMultiplier, using default");
629
- config.keyboardFastMultiplier = DEFAULT_CONFIG.keyboardFastMultiplier;
630
- }
631
- if (typeof config.clickZoomLevel !== "number" || config.clickZoomLevel <= 0) {
632
- console.warn("Invalid clickZoomLevel, using default");
633
- config.clickZoomLevel = DEFAULT_CONFIG.clickZoomLevel;
634
- }
635
- if (typeof config.rulerFontSize !== "number" || config.rulerFontSize <= 0) {
636
- console.warn("Invalid rulerFontSize, using default");
637
- config.rulerFontSize = DEFAULT_CONFIG.rulerFontSize;
638
- }
639
- if (typeof config.rulerSize !== "number" || config.rulerSize <= 0) {
640
- console.warn("Invalid rulerSize, using default");
641
- config.rulerSize = DEFAULT_CONFIG.rulerSize;
642
- }
643
- return config;
644
- }
645
-
646
819
  class EventEmitter {
647
820
  constructor() {
648
821
  this.listeners = new Map();
@@ -681,6 +854,13 @@
681
854
  }
682
855
  }
683
856
 
857
+ function emitTransformEvents(listen, canvas) {
858
+ const transform = canvas.transform;
859
+ listen.emit("transform", transform);
860
+ listen.emit("zoom", transform.scale);
861
+ listen.emit("pan", { x: transform.translateX, y: transform.translateY });
862
+ }
863
+
684
864
  const REFERENCE_DISPLAY_AREA = 1920 * 1080;
685
865
  const TRACKPAD_PINCH_SPEED_FACTOR = 0.05;
686
866
  const ADAPTIVE_ZOOM_FACTOR = 1;
@@ -1175,6 +1355,17 @@
1175
1355
  const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
1176
1356
  canvas.updateThemeMode(newMode);
1177
1357
  }
1358
+ // Transition methods
1359
+ else if (action === "updateTransition") {
1360
+ const enabled = args[0];
1361
+ if (typeof enabled !== "boolean") {
1362
+ throw new Error(`Invalid transition enabled value: ${enabled}. Must be a boolean.`);
1363
+ }
1364
+ canvas.updateTransition(enabled);
1365
+ }
1366
+ else if (action === "toggleTransitionMode") {
1367
+ canvas.toggleTransitionMode();
1368
+ }
1178
1369
  else {
1179
1370
  throw new Error(`Unknown action: ${action}`);
1180
1371
  }
@@ -1410,20 +1601,6 @@
1410
1601
  };
1411
1602
  }
1412
1603
 
1413
- function getViewportCenter(canvas) {
1414
- try {
1415
- const bounds = canvas.getBounds();
1416
- return {
1417
- x: bounds.width / 2,
1418
- y: bounds.height / 2,
1419
- };
1420
- }
1421
- catch (error) {
1422
- console.warn("Failed to calculate viewport center:", error);
1423
- return { x: 0, y: 0 };
1424
- }
1425
- }
1426
-
1427
1604
  // Rulers
1428
1605
  const RULER_Z_INDEX = {
1429
1606
  GRID: 100,
@@ -1786,8 +1963,6 @@
1786
1963
  elements.verticalRuler.style.display = "block";
1787
1964
  if (elements.cornerBox)
1788
1965
  elements.cornerBox.style.display = "flex";
1789
- if (elements.gridOverlay)
1790
- elements.gridOverlay.style.display = "block";
1791
1966
  },
1792
1967
  hide: () => {
1793
1968
  if (elements.horizontalRuler)
@@ -1831,14 +2006,163 @@
1831
2006
  }
1832
2007
  }
1833
2008
 
2009
+ function broadcastEvent(event, data, config) {
2010
+ if (typeof window === "undefined") {
2011
+ return;
2012
+ }
2013
+ // Receivers can get the instance from the window binding
2014
+ let broadcastData = data;
2015
+ if (event === "ready") {
2016
+ broadcastData = { ready: true };
2017
+ }
2018
+ window.postMessage({
2019
+ source: "markup-canvas",
2020
+ event,
2021
+ data: broadcastData,
2022
+ timestamp: Date.now(),
2023
+ canvasName: config.name,
2024
+ }, "*");
2025
+ if (window.parent) {
2026
+ window.parent.postMessage({
2027
+ source: "markup-canvas",
2028
+ event,
2029
+ data: broadcastData,
2030
+ timestamp: Date.now(),
2031
+ canvasName: config.name,
2032
+ }, "*");
2033
+ }
2034
+ }
2035
+
2036
+ function cleanupWindowBinding(config) {
2037
+ if (typeof window === "undefined") {
2038
+ return;
2039
+ }
2040
+ const canvasName = config.name || "markupCanvas";
2041
+ const windowObj = window;
2042
+ delete windowObj[canvasName];
2043
+ if (windowObj.__markupCanvasInstances) {
2044
+ windowObj.__markupCanvasInstances.delete(canvasName);
2045
+ }
2046
+ }
2047
+
2048
+ function bindCanvasToWindow(canvas, config) {
2049
+ if (typeof window === "undefined") {
2050
+ return;
2051
+ }
2052
+ const canvasName = config.name || "markupCanvas";
2053
+ const windowObj = window;
2054
+ const api = {
2055
+ config: {
2056
+ get current() {
2057
+ return canvas.config;
2058
+ },
2059
+ get: canvas.getConfig.bind(canvas),
2060
+ update: canvas.updateConfig.bind(canvas),
2061
+ },
2062
+ // Transform group
2063
+ transform: {
2064
+ update: canvas.updateTransform.bind(canvas),
2065
+ reset: canvas.reset.bind(canvas),
2066
+ },
2067
+ // Zoom group
2068
+ zoom: {
2069
+ set: canvas.setZoom.bind(canvas),
2070
+ toPoint: canvas.zoomToPoint.bind(canvas),
2071
+ in: canvas.zoomIn.bind(canvas),
2072
+ out: canvas.zoomOut.bind(canvas),
2073
+ reset: canvas.resetZoom.bind(canvas),
2074
+ resetToCenter: canvas.resetViewToCenter.bind(canvas),
2075
+ fitToScreen: canvas.fitToScreen.bind(canvas),
2076
+ },
2077
+ // Pan group
2078
+ pan: {
2079
+ left: canvas.panLeft.bind(canvas),
2080
+ right: canvas.panRight.bind(canvas),
2081
+ up: canvas.panUp.bind(canvas),
2082
+ down: canvas.panDown.bind(canvas),
2083
+ toPoint: canvas.scrollToPoint.bind(canvas),
2084
+ toCenter: canvas.centerContent.bind(canvas),
2085
+ },
2086
+ // Mouse drag group
2087
+ mouseDrag: {
2088
+ enable: canvas.enableMouseDrag.bind(canvas),
2089
+ disable: canvas.disableMouseDrag.bind(canvas),
2090
+ isEnabled: canvas.isMouseDragEnabled.bind(canvas),
2091
+ },
2092
+ // Grid group
2093
+ grid: {
2094
+ toggle: canvas.toggleGrid.bind(canvas),
2095
+ show: canvas.showGrid.bind(canvas),
2096
+ hide: canvas.hideGrid.bind(canvas),
2097
+ isVisible: canvas.isGridVisible.bind(canvas),
2098
+ },
2099
+ // Ruler group
2100
+ rulers: {
2101
+ toggle: canvas.toggleRulers.bind(canvas),
2102
+ show: canvas.showRulers.bind(canvas),
2103
+ hide: canvas.hideRulers.bind(canvas),
2104
+ isVisible: canvas.areRulersVisible.bind(canvas),
2105
+ },
2106
+ // Utility group
2107
+ canvas: {
2108
+ canvasToContent: canvas.canvasToContent.bind(canvas),
2109
+ getVisibleArea: canvas.getVisibleArea.bind(canvas),
2110
+ isPointVisible: canvas.isPointVisible.bind(canvas),
2111
+ getBounds: canvas.getBounds.bind(canvas),
2112
+ },
2113
+ theme: {
2114
+ get current() {
2115
+ return canvas.config.themeMode;
2116
+ },
2117
+ update: canvas.updateThemeMode.bind(canvas),
2118
+ toggle: canvas.toggleThemeMode.bind(canvas),
2119
+ },
2120
+ transition: {
2121
+ get current() {
2122
+ return canvas.config.enableTransition;
2123
+ },
2124
+ set: canvas.updateTransition.bind(canvas),
2125
+ toggle: canvas.toggleTransitionMode.bind(canvas),
2126
+ },
2127
+ // Event group
2128
+ event: canvas.event,
2129
+ // Lifecycle group
2130
+ lifecycle: {
2131
+ cleanup: canvas.cleanup.bind(canvas),
2132
+ destroy: canvas.destroy.bind(canvas),
2133
+ },
2134
+ // Properties/State group
2135
+ state: {
2136
+ get isReady() {
2137
+ return canvas.isReady;
2138
+ },
2139
+ get isTransforming() {
2140
+ return canvas.isTransforming;
2141
+ },
2142
+ get visibleBounds() {
2143
+ return canvas.visibleBounds;
2144
+ },
2145
+ get transform() {
2146
+ return canvas.transform;
2147
+ },
2148
+ },
2149
+ };
2150
+ // Bind public API to window
2151
+ windowObj[canvasName] = api;
2152
+ // Track all instances
2153
+ if (!windowObj.__markupCanvasInstances) {
2154
+ windowObj.__markupCanvasInstances = new Map();
2155
+ }
2156
+ windowObj.__markupCanvasInstances.set(canvasName, api);
2157
+ }
2158
+
1834
2159
  class MarkupCanvas {
1835
2160
  constructor(container, options = {}) {
1836
- this.cleanupFunctions = [];
2161
+ this.cleanupCallbacks = [];
1837
2162
  this.rulers = null;
1838
2163
  this.dragSetup = null;
2164
+ this.event = new EventEmitter();
1839
2165
  this._isReady = false;
1840
- this.listen = new EventEmitter();
1841
- this.postMessageCleanup = null;
1842
2166
  if (!container) {
1843
2167
  throw new Error("Container element is required");
1844
2168
  }
@@ -1847,100 +2171,48 @@
1847
2171
  if (!canvas) {
1848
2172
  throw new Error("Failed to create canvas");
1849
2173
  }
1850
- this.baseCanvas = canvas;
1851
- if (this.config.bindToWindow) {
1852
- this.listen.setEmitInterceptor((event, data) => {
1853
- this.broadcastEvent(event, data);
1854
- });
1855
- this.setupGlobalBinding();
1856
- // Set up postMessage listener
1857
- if (this.config.enablePostMessageAPI) {
1858
- this.postMessageCleanup = setupPostMessageEvents(this);
1859
- }
2174
+ this.canvas = canvas;
2175
+ // Always bind canvas to window
2176
+ this.event.setEmitInterceptor((event, data) => {
2177
+ broadcastEvent(event, data, this.config);
2178
+ });
2179
+ bindCanvasToWindow(this, this.config);
2180
+ // Set up postMessage listener
2181
+ if (this.config.enablePostMessageAPI) {
2182
+ const postMessageCleanup = setupPostMessageEvents(this);
2183
+ this.cleanupCallbacks.push(postMessageCleanup);
1860
2184
  }
1861
2185
  this.setupEventHandlers();
1862
2186
  this._isReady = true;
1863
2187
  // Emit ready event
1864
- this.listen.emit("ready", this);
1865
- }
1866
- setupGlobalBinding() {
1867
- if (typeof window === "undefined") {
1868
- return;
1869
- }
1870
- const canvasName = this.config.name || "markupCanvas";
1871
- const windowObj = window;
1872
- // Bind instance to window
1873
- windowObj[canvasName] = this;
1874
- // Track all instances
1875
- if (!windowObj.__markupCanvasInstances) {
1876
- windowObj.__markupCanvasInstances = new Map();
1877
- }
1878
- windowObj.__markupCanvasInstances.set(canvasName, this);
1879
- }
1880
- cleanupGlobalBinding() {
1881
- if (typeof window === "undefined") {
1882
- return;
1883
- }
1884
- const canvasName = this.config.name || "markupCanvas";
1885
- const windowObj = window;
1886
- delete windowObj[canvasName];
1887
- if (windowObj.__markupCanvasInstances) {
1888
- windowObj.__markupCanvasInstances.delete(canvasName);
1889
- }
1890
- }
1891
- broadcastEvent(event, data) {
1892
- if (typeof window === "undefined") {
1893
- return;
1894
- }
1895
- // Receivers can get the instance from the window binding
1896
- let broadcastData = data;
1897
- if (event === "ready") {
1898
- broadcastData = { ready: true };
1899
- }
1900
- window.postMessage({
1901
- source: "markup-canvas",
1902
- event,
1903
- data: broadcastData,
1904
- timestamp: Date.now(),
1905
- canvasName: this.config.name,
1906
- }, "*");
1907
- if (window.parent) {
1908
- window.parent.postMessage({
1909
- source: "markup-canvas",
1910
- event,
1911
- data: broadcastData,
1912
- timestamp: Date.now(),
1913
- canvasName: this.config.name,
1914
- }, "*");
1915
- }
2188
+ this.event.emit("ready", this);
1916
2189
  }
1917
2190
  setupEventHandlers() {
1918
2191
  try {
1919
- // Wheel zoom
2192
+ // Wheel events
1920
2193
  withFeatureEnabled(this.config, "enableZoom", () => {
1921
2194
  const wheelCleanup = setupWheelEvents(this, this.config);
1922
- this.cleanupFunctions.push(wheelCleanup);
2195
+ this.cleanupCallbacks.push(wheelCleanup);
1923
2196
  });
1924
- // Mouse events (drag and click-to-zoom)
1925
- // Set up mouse events if either pan or click-to-zoom is enabled
2197
+ // Mouse events
1926
2198
  if (this.config.enablePan || this.config.enableClickToZoom) {
1927
2199
  this.dragSetup = setupMouseEvents(this, this.config, true);
1928
- this.cleanupFunctions.push(this.dragSetup.cleanup);
2200
+ this.cleanupCallbacks.push(this.dragSetup.cleanup);
1929
2201
  }
1930
- // Keyboard navigation
2202
+ // Keyboard events
1931
2203
  withFeatureEnabled(this.config, "enableKeyboard", () => {
1932
2204
  const keyboardCleanup = setupKeyboardEvents(this, this.config);
1933
- this.cleanupFunctions.push(keyboardCleanup);
2205
+ this.cleanupCallbacks.push(keyboardCleanup);
1934
2206
  });
1935
- // Touch events (if enabled)
2207
+ // Touch events
1936
2208
  withFeatureEnabled(this.config, "enableTouch", () => {
1937
2209
  const touchCleanup = setupTouchEvents(this);
1938
- this.cleanupFunctions.push(touchCleanup);
2210
+ this.cleanupCallbacks.push(touchCleanup);
1939
2211
  });
1940
2212
  // Set up rulers and grid
1941
2213
  withFeatureEnabled(this.config, "enableRulers", () => {
1942
- this.rulers = createRulers(this.baseCanvas, this.config);
1943
- this.cleanupFunctions.push(() => {
2214
+ this.rulers = createRulers(this, this.config);
2215
+ this.cleanupCallbacks.push(() => {
1944
2216
  if (this.rulers) {
1945
2217
  this.rulers.destroy();
1946
2218
  }
@@ -1953,20 +2225,18 @@
1953
2225
  throw error;
1954
2226
  }
1955
2227
  }
1956
- // Base canvas properties and methods
1957
2228
  get container() {
1958
- return this.baseCanvas.container;
2229
+ return this.canvas.container;
1959
2230
  }
1960
2231
  get transformLayer() {
1961
- return this.baseCanvas.transformLayer;
2232
+ return this.canvas.transformLayer;
1962
2233
  }
1963
2234
  get contentLayer() {
1964
- return this.baseCanvas.contentLayer;
2235
+ return this.canvas.contentLayer;
1965
2236
  }
1966
2237
  get transform() {
1967
- return this.baseCanvas.transform;
2238
+ return this.canvas.transform;
1968
2239
  }
1969
- // State management getters for React integration
1970
2240
  get isReady() {
1971
2241
  return this._isReady;
1972
2242
  }
@@ -1974,131 +2244,66 @@
1974
2244
  return this.dragSetup?.isEnabled() || false;
1975
2245
  }
1976
2246
  get visibleBounds() {
1977
- return this.getVisibleArea();
2247
+ return getVisibleArea(this);
1978
2248
  }
1979
2249
  getBounds() {
1980
- return this.baseCanvas.getBounds();
2250
+ return getCanvasBounds(this.canvas, this.config);
1981
2251
  }
1982
2252
  updateTransform(newTransform) {
1983
- const result = this.baseCanvas.updateTransform(newTransform);
2253
+ const result = updateTransform(this.canvas, newTransform);
1984
2254
  if (result) {
1985
- this.emitTransformEvents();
2255
+ emitTransformEvents(this.event, this.canvas);
1986
2256
  }
1987
2257
  return result;
1988
2258
  }
1989
- emitTransformEvents() {
1990
- const transform = this.baseCanvas.transform;
1991
- this.listen.emit("transform", transform);
1992
- this.listen.emit("zoom", transform.scale);
1993
- this.listen.emit("pan", { x: transform.translateX, y: transform.translateY });
1994
- }
1995
2259
  reset() {
1996
- return this.baseCanvas.reset();
1997
- }
1998
- handleResize() {
1999
- return this.baseCanvas.handleResize();
2260
+ const result = resetTransform(this.canvas);
2261
+ if (result) {
2262
+ emitTransformEvents(this.event, this.canvas);
2263
+ }
2264
+ return result;
2000
2265
  }
2001
2266
  setZoom(zoomLevel) {
2002
- return withTransition(this.transformLayer, this.config, () => {
2003
- return withClampedZoom(this.config, (clamp) => {
2004
- const newScale = clamp(zoomLevel);
2005
- const newTransform = {
2006
- scale: newScale,
2007
- };
2008
- return this.updateTransform(newTransform);
2009
- });
2010
- });
2267
+ return setZoom(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), zoomLevel);
2011
2268
  }
2012
2269
  canvasToContent(x, y) {
2013
- return this.baseCanvas.canvasToContent(x, y);
2270
+ const matrix = createMatrix(this.canvas.transform.scale, this.canvas.transform.translateX, this.canvas.transform.translateY);
2271
+ return canvasToContent(x, y, matrix);
2014
2272
  }
2015
2273
  zoomToPoint(x, y, targetScale) {
2016
- return withTransition(this.transformLayer, this.config, () => {
2017
- const result = this.baseCanvas.zoomToPoint(x, y, targetScale);
2018
- if (result) {
2019
- this.emitTransformEvents();
2020
- }
2021
- return result;
2022
- });
2023
- }
2024
- resetView() {
2025
- const result = this.baseCanvas.resetView ? this.baseCanvas.resetView() : false;
2274
+ const result = zoomToPoint(this.canvas, this.transformLayer, this.config, x, y, targetScale);
2026
2275
  if (result) {
2027
- this.emitTransformEvents();
2276
+ emitTransformEvents(this.event, this.canvas);
2028
2277
  }
2029
2278
  return result;
2030
2279
  }
2031
- resetViewToCenter() {
2032
- return withTransition(this.transformLayer, this.config, () => {
2033
- return withClampedZoom(this.config, (clamp) => {
2034
- const newScale = clamp(1.0);
2035
- const center = getViewportCenter(this);
2036
- const result = this.zoomToPoint(center.x, center.y, newScale);
2037
- if (result) {
2038
- this.emitTransformEvents();
2039
- }
2040
- return result;
2041
- });
2042
- });
2280
+ resetView() {
2281
+ return resetView(this.canvas, this.transformLayer, this.config);
2043
2282
  }
2044
- zoomToFitContent() {
2045
- return withTransition(this.transformLayer, this.config, () => {
2046
- const result = this.baseCanvas.zoomToFitContent();
2047
- if (result) {
2048
- this.emitTransformEvents();
2049
- }
2050
- return result;
2051
- });
2283
+ resetViewToCenter() {
2284
+ return resetViewToCenter(this, this.transformLayer, this.config, this.zoomToPoint.bind(this));
2052
2285
  }
2053
- // Pan methods
2054
2286
  panLeft(distance) {
2055
- const panDistance = distance ?? this.config.keyboardPanStep;
2056
- const newTransform = {
2057
- translateX: this.baseCanvas.transform.translateX + panDistance,
2058
- };
2059
- return this.updateTransform(newTransform);
2287
+ return (panLeft(this.canvas, this.config, this.updateTransform.bind(this)) ||
2288
+ (distance ? panLeft(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
2060
2289
  }
2061
2290
  panRight(distance) {
2062
- const panDistance = distance ?? this.config.keyboardPanStep;
2063
- const newTransform = {
2064
- translateX: this.baseCanvas.transform.translateX - panDistance,
2065
- };
2066
- return this.updateTransform(newTransform);
2291
+ return (panRight(this.canvas, this.config, this.updateTransform.bind(this)) ||
2292
+ (distance ? panRight(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
2067
2293
  }
2068
2294
  panUp(distance) {
2069
- const panDistance = distance ?? this.config.keyboardPanStep;
2070
- const newTransform = {
2071
- translateY: this.baseCanvas.transform.translateY + panDistance,
2072
- };
2073
- return this.updateTransform(newTransform);
2295
+ return (panUp(this.canvas, this.config, this.updateTransform.bind(this)) ||
2296
+ (distance ? panUp(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
2074
2297
  }
2075
2298
  panDown(distance) {
2076
- const panDistance = distance ?? this.config.keyboardPanStep;
2077
- const newTransform = {
2078
- translateY: this.baseCanvas.transform.translateY - panDistance,
2079
- };
2080
- return this.updateTransform(newTransform);
2299
+ return (panDown(this.canvas, this.config, this.updateTransform.bind(this)) ||
2300
+ (distance ? panDown(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
2081
2301
  }
2082
- // Zoom methods
2083
2302
  zoomIn(factor = 0.5) {
2084
- return withTransition(this.transformLayer, this.config, () => {
2085
- return withClampedZoom(this.config, (clamp) => {
2086
- const newScale = clamp(this.baseCanvas.transform.scale * (1 + factor));
2087
- // Get the center of the viewport
2088
- const center = getViewportCenter(this);
2089
- return this.zoomToPoint(center.x, center.y, newScale);
2090
- });
2091
- });
2303
+ return zoomIn(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), factor);
2092
2304
  }
2093
2305
  zoomOut(factor = 0.5) {
2094
- return withTransition(this.transformLayer, this.config, () => {
2095
- return withClampedZoom(this.config, (clamp) => {
2096
- const newScale = clamp(this.baseCanvas.transform.scale * (1 - factor));
2097
- // Get the center of the viewport
2098
- const center = getViewportCenter(this);
2099
- return this.zoomToPoint(center.x, center.y, newScale);
2100
- });
2101
- });
2306
+ return zoomOut(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), factor);
2102
2307
  }
2103
2308
  resetZoom() {
2104
2309
  return this.resetViewToCenter();
@@ -2113,110 +2318,68 @@
2113
2318
  isMouseDragEnabled() {
2114
2319
  return this.dragSetup?.isEnabled() ?? false;
2115
2320
  }
2116
- // Grid control methods
2117
2321
  toggleGrid() {
2118
- if (this.rulers?.toggleGrid) {
2119
- this.rulers.toggleGrid();
2120
- return true;
2322
+ const result = toggleGrid(this.rulers);
2323
+ if (result) {
2324
+ this.event.emit("gridVisibility", this.isGridVisible());
2121
2325
  }
2122
- return false;
2326
+ return result;
2123
2327
  }
2124
2328
  showGrid() {
2125
- if (this.rulers?.gridOverlay) {
2126
- this.rulers.gridOverlay.style.display = "block";
2127
- return true;
2329
+ const result = showGrid(this.rulers);
2330
+ if (result) {
2331
+ this.event.emit("gridVisibility", true);
2128
2332
  }
2129
- return false;
2333
+ return result;
2130
2334
  }
2131
2335
  hideGrid() {
2132
- if (this.rulers?.gridOverlay) {
2133
- this.rulers.gridOverlay.style.display = "none";
2134
- return true;
2336
+ const result = hideGrid(this.rulers);
2337
+ if (result) {
2338
+ this.event.emit("gridVisibility", false);
2135
2339
  }
2136
- return false;
2340
+ return result;
2137
2341
  }
2138
2342
  isGridVisible() {
2139
- if (this.rulers?.gridOverlay) {
2140
- return this.rulers.gridOverlay.style.display !== "none";
2141
- }
2142
- return false;
2343
+ return isGridVisible(this.rulers);
2143
2344
  }
2144
- // Ruler control methods
2145
2345
  toggleRulers() {
2146
- if (this.rulers) {
2147
- const areVisible = this.areRulersVisible();
2148
- if (areVisible) {
2149
- this.rulers.hide();
2150
- }
2151
- else {
2152
- this.rulers.show();
2153
- }
2154
- return true;
2346
+ const result = toggleRulers(this.rulers, () => this.areRulersVisible());
2347
+ if (result) {
2348
+ this.event.emit("rulersVisibility", this.areRulersVisible());
2155
2349
  }
2156
- return false;
2350
+ return result;
2157
2351
  }
2158
2352
  showRulers() {
2159
- if (this.rulers) {
2160
- this.rulers.show();
2161
- return true;
2353
+ const result = showRulers(this.rulers);
2354
+ if (result) {
2355
+ this.event.emit("rulersVisibility", true);
2162
2356
  }
2163
- return false;
2357
+ return result;
2164
2358
  }
2165
2359
  hideRulers() {
2166
- if (this.rulers) {
2167
- this.rulers.hide();
2168
- return true;
2360
+ const result = hideRulers(this.rulers);
2361
+ if (result) {
2362
+ this.event.emit("rulersVisibility", false);
2169
2363
  }
2170
- return false;
2364
+ return result;
2171
2365
  }
2172
2366
  areRulersVisible() {
2173
- if (this.rulers?.horizontalRuler) {
2174
- return this.rulers.horizontalRuler.style.display !== "none";
2175
- }
2176
- return false;
2367
+ return areRulersVisible(this.rulers);
2177
2368
  }
2178
- // Utility methods
2179
2369
  centerContent() {
2180
- return withTransition(this.transformLayer, this.config, () => {
2181
- const bounds = this.baseCanvas.getBounds();
2182
- const centerX = (bounds.width - bounds.contentWidth * this.baseCanvas.transform.scale) / 2;
2183
- const centerY = (bounds.height - bounds.contentHeight * this.baseCanvas.transform.scale) / 2;
2184
- return this.updateTransform({
2185
- translateX: centerX,
2186
- translateY: centerY,
2187
- });
2188
- });
2370
+ return centerContent(this.canvas, this.config, this.updateTransform.bind(this), this.transformLayer);
2189
2371
  }
2190
2372
  fitToScreen() {
2191
- return withTransition(this.transformLayer, this.config, () => {
2192
- const result = this.baseCanvas.zoomToFitContent();
2193
- if (result) {
2194
- this.emitTransformEvents();
2195
- }
2196
- return result;
2197
- });
2373
+ return fitToScreen(this.canvas, this.transformLayer, this.config);
2198
2374
  }
2199
2375
  getVisibleArea() {
2200
- const bounds = this.baseCanvas.getBounds();
2201
- return bounds.visibleArea;
2376
+ return getVisibleArea(this);
2202
2377
  }
2203
2378
  isPointVisible(x, y) {
2204
- const visibleArea = this.getVisibleArea();
2205
- return x >= visibleArea.x && x <= visibleArea.x + visibleArea.width && y >= visibleArea.y && y <= visibleArea.y + visibleArea.height;
2379
+ return isPointVisible(this, x, y);
2206
2380
  }
2207
2381
  scrollToPoint(x, y) {
2208
- return withTransition(this.transformLayer, this.config, () => {
2209
- const bounds = this.baseCanvas.getBounds();
2210
- const centerX = bounds.width / 2;
2211
- const centerY = bounds.height / 2;
2212
- // Calculate new translation to center the point
2213
- const newTranslateX = centerX - x * this.baseCanvas.transform.scale;
2214
- const newTranslateY = centerY - y * this.baseCanvas.transform.scale;
2215
- return this.updateTransform({
2216
- translateX: newTranslateX,
2217
- translateY: newTranslateY,
2218
- });
2219
- });
2382
+ return scrollToPoint(this.canvas, this.config, x, y, this.updateTransform.bind(this), this.transformLayer);
2220
2383
  }
2221
2384
  // Configuration access
2222
2385
  getConfig() {
@@ -2227,28 +2390,28 @@
2227
2390
  }
2228
2391
  // Theme management
2229
2392
  updateThemeMode(mode) {
2230
- const newConfig = {
2231
- ...this.config,
2232
- themeMode: mode,
2233
- };
2234
- this.config = createMarkupCanvasConfig(newConfig);
2235
- // Update canvas background color
2236
- const backgroundColor = getThemeValue(this.config, "canvasBackgroundColor");
2237
- this.baseCanvas.container.style.backgroundColor = backgroundColor;
2238
- // Update rulers if they exist
2239
- if (this.rulers) {
2240
- this.rulers.updateTheme(this.config);
2241
- }
2393
+ this.config = createMarkupCanvasConfig({ ...this.config, themeMode: mode });
2394
+ updateThemeMode(this.canvas.container, this.config, this.rulers, mode);
2395
+ }
2396
+ toggleThemeMode() {
2397
+ const currentMode = this.config.themeMode;
2398
+ const newMode = currentMode === "light" ? "dark" : "light";
2399
+ this.updateThemeMode(newMode);
2400
+ return newMode;
2401
+ }
2402
+ // Transition management
2403
+ updateTransition(enabled) {
2404
+ this.config = createMarkupCanvasConfig({ ...this.config, enableTransition: enabled });
2405
+ }
2406
+ toggleTransitionMode() {
2407
+ const newEnableTransition = toggleTransition(this.config.enableTransition);
2408
+ this.updateTransition(newEnableTransition);
2409
+ return newEnableTransition;
2242
2410
  }
2243
2411
  // Cleanup method
2244
2412
  cleanup() {
2245
- this.cleanupGlobalBinding();
2246
- // Cleanup postMessage listener
2247
- if (this.postMessageCleanup) {
2248
- this.postMessageCleanup();
2249
- this.postMessageCleanup = null;
2250
- }
2251
- this.cleanupFunctions.forEach((cleanup) => {
2413
+ cleanupWindowBinding(this.config);
2414
+ this.cleanupCallbacks.forEach((cleanup) => {
2252
2415
  try {
2253
2416
  cleanup();
2254
2417
  }
@@ -2256,22 +2419,22 @@
2256
2419
  console.warn("Error during cleanup:", cleanupError);
2257
2420
  }
2258
2421
  });
2259
- this.cleanupFunctions = [];
2422
+ this.cleanupCallbacks = [];
2260
2423
  // Remove all event listeners
2261
2424
  this.removeAllListeners();
2262
2425
  }
2263
2426
  // Event emitter delegation methods
2264
2427
  on(event, handler) {
2265
- this.listen.on(event, handler);
2428
+ this.event.on(event, handler);
2266
2429
  }
2267
2430
  off(event, handler) {
2268
- this.listen.off(event, handler);
2431
+ this.event.off(event, handler);
2269
2432
  }
2270
2433
  emit(event, data) {
2271
- this.listen.emit(event, data);
2434
+ this.event.emit(event, data);
2272
2435
  }
2273
2436
  removeAllListeners() {
2274
- this.listen.removeAllListeners();
2437
+ this.event.removeAllListeners();
2275
2438
  }
2276
2439
  destroy() {
2277
2440
  this.cleanup();