@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 @@
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).MarkupCanvas=e()}(this,function(){"use strict";const t="canvas-container",e="transform-layer",n="content-layer";const r=24,o=100,a=1e3,s=1001,i=6,l=4,c=8,u=4,d=5,h=100,m=100,f=20,g=200;function p(t,r){const o=Array.from(t.children);let a=t.querySelector(`.${e}`);a||(a=document.createElement("div"),a.className=e,t.appendChild(a)),function(t,e){t.style.position="absolute",t.style.top="24px",t.style.left="24px",t.style.width=`${e.width}px`,t.style.height=`${e.height}px`,t.style.transformOrigin="0 0"}(a,r);let s=a.querySelector(`.${n}`);return s||(s=document.createElement("div"),s.className=n,a.appendChild(s),function(t,n,r){t.forEach(t=>{t===r||t.classList.contains(e)||n.appendChild(t)})}(o,s,a)),function(t){t.style.position="relative",t.style.width="100%",t.style.height="100%",t.style.pointerEvents="auto"}(s),{transformLayer:a,contentLayer:s}}function v(t,e,n){if(!n?.inverse)return{x:t,y:e};try{const r=n.inverse(),o=new DOMPoint(t,e).matrixTransform(r);return{x:o.x,y:o.y}}catch(n){return console.warn("Canvas to content conversion failed:",n),{x:t,y:e}}}function y(t,e){return Math.max(e.minZoom,Math.min(e.maxZoom,t))}function b(t,e,n){return new DOMMatrix([t,0,0,t,e,n])}function C(t,e,n,r,o){const a=o.enableRulers?-24:0,s=n||{scale:1,translateX:a,translateY:a},{scale:i,translateX:l,translateY:c}=s,u=y(i*r,o);if(Math.abs(u-i)<.001)return{scale:i,translateX:l,translateY:c};return{scale:u,translateX:t-(t-l)/i*u,translateY:e-(e-c)/i*u}}function w(t,e){return e(e=>y(e,t))}const x=new Map;function T(t,e,n){return t[e]?n():null}function k(t){let e=null,n=null;const r=(...r)=>{n=r,null===e&&(e=requestAnimationFrame(()=>{n&&t(...n),e=null,n=null}))};return r.cleanup=()=>{null!==e&&(cancelAnimationFrame(e),e=null,n=null)},r}function D(t,e){return e(null!==t.container.querySelector(".canvas-ruler")?r:0)}function M(t,e,n,o){const a=null!==t.container.querySelector(".canvas-ruler");return o(a?e-r:e,a?n-r:n)}const L={width:8e3,height:8e3,enableAcceleration:!0,enableZoom:!0,enablePan:!0,enableTouch:!0,enableKeyboard:!0,limitKeyboardEventsToCanvas:!1,zoomSpeed:1.5,minZoom:.05,maxZoom:80,enableTransition:!0,transitionDuration:.2,enableAdaptiveSpeed:!0,enableLeftDrag:!0,enableMiddleDrag:!0,requireSpaceForMouseDrag:!1,keyboardPanStep:50,keyboardFastMultiplier:20,keyboardZoomStep:.2,enableClickToZoom:!0,clickZoomLevel:1,requireOptionForClickZoom:!1,enableRulers:!0,enableGrid:!0,gridColor:"rgba(0, 123, 255, 0.1)",rulerBackgroundColor:"rgba(255, 255, 255, 0.95)",rulerBorderColor:"#ddd",rulerTextColor:"#666",rulerMajorTickColor:"#999",rulerMinorTickColor:"#ccc",rulerFontSize:10,rulerFontFamily:"Monaco, Menlo, monospace",rulerUnits:"px",onTransformUpdate:()=>{}};function S(t){try{const e=t.container,n=t.config,r=t.transform||{scale:1,translateX:0,translateY:0},o=e.getBoundingClientRect(),a=o.width||e.clientWidth||0,s=o.height||e.clientHeight||0,i=D({container:e},t=>Math.max(0,a-t)),l=D({container:e},t=>Math.max(0,s-t)),c=n.width||L.width,u=n.height||L.height,d=function(t,e,n,r,o){const a=v(0,0,b(o.scale,o.translateX,o.translateY)),s=v(t,e,b(o.scale,o.translateX,o.translateY));return{x:Math.max(0,Math.min(n,a.x)),y:Math.max(0,Math.min(r,a.y)),width:Math.max(0,Math.min(n-a.x,s.x-a.x)),height:Math.max(0,Math.min(r-a.y,s.y-a.y))}}(i,l,c,u,r);return{width:i,height:l,contentWidth:c,contentHeight:u,scale:r.scale,translateX:r.translateX,translateY:r.translateY,visibleArea:d,scaledContentWidth:c*r.scale,scaledContentHeight:u*r.scale,canPanLeft:r.translateX<0,canPanRight:r.translateX+c*r.scale>i,canPanUp:r.translateY<0,canPanDown:r.translateY+u*r.scale>l,canZoomIn:r.scale<3.5,canZoomOut:r.scale>.1}}catch(t){return console.error("Failed to calculate canvas bounds:",t),{width:0,height:0,contentWidth:0,contentHeight:0,scale:1,translateX:0,translateY:0,visibleArea:{x:0,y:0,width:0,height:0},scaledContentWidth:0,scaledContentHeight:0,canPanLeft:!1,canPanRight:!1,canPanUp:!1,canPanDown:!1,canZoomIn:!1,canZoomOut:!1}}}function Y(t,e){if(!t?.style||!e)return!1;try{return t.style.transform=function(t){return`matrix3d(${t.m11}, ${t.m12}, ${t.m13}, ${t.m14}, ${t.m21}, ${t.m22}, ${t.m23}, ${t.m24}, ${t.m31}, ${t.m32}, ${t.m33}, ${t.m34}, ${t.m41}, ${t.m42}, ${t.m43}, ${t.m44})`}(e),!0}catch(t){return console.warn("Transform application failed:",t),!1}}function E(t,e){try{if(e.enableTransition){window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0);return function(t,e,n){const r=x.get(t);r&&clearTimeout(r);const o=window.setTimeout(()=>{n(),x.delete(t)},e);x.set(t,o)}("disableTransition",1e3*(e.transitionDuration??.2),()=>{t.style.transition="none",window.__markupCanvasTransitionTimeout=void 0}),!0}return!1}catch(t){return console.error("Failed to disable transitions:",t),!0}}function X(t,e,n){!function(t,e){try{return!!e.enableTransition&&(window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0),t.style.transition=`transform ${e.transitionDuration}s linear`,!0)}catch(t){return console.error("Failed to enable transitions:",t),!1}}(t,e);try{return n()}finally{E(t,e)}}function $(e){"static"===getComputedStyle(e).position&&(e.style.position="relative"),e.style.overflow="hidden",e.style.cursor="grab",e.style.overscrollBehavior="none",e.hasAttribute("tabindex")||e.setAttribute("tabindex","0"),function(t){const e=t.getBoundingClientRect(),n=getComputedStyle(t);0===e.height&&"auto"===n.height&&console.error("MarkupCanvas: Container height is 0. Please set a height on your container element using CSS.","Examples: height: 100vh, height: 500px, or use flexbox/grid layout.",t),0===e.width&&"auto"===n.width&&console.error("MarkupCanvas: Container width is 0. Please set a width on your container element using CSS.","Examples: width: 100vw, width: 800px, or use flexbox/grid layout.",t)}(e),e.classList.contains(t)||e.classList.add(t)}function z(t,e){if(!t?.appendChild)return console.error("Invalid container element provided to createCanvas"),null;try{$(t);const{transformLayer:n,contentLayer:r}=p(t,e);e.enableAcceleration&&function(t){try{return t.style.transform=t.style.transform||"translateZ(0)",t.style.backfaceVisibility="hidden",!0}catch(t){return console.error("Failed to enable hardware acceleration:",t),!1}}(n);const o=e.enableRulers?-24:0,a={scale:1,translateX:o,translateY:o};Y(n,b(a.scale,a.translateX,a.translateY));return{container:t,transformLayer:n,contentLayer:r,config:e,transform:a,getBounds:function(){return S(this)},updateTransform:function(t){this.transform={...this.transform,...t};const e=b(this.transform.scale,this.transform.translateX,this.transform.translateY),n=Y(this.transformLayer,e);return T(this.config,"onTransformUpdate",()=>{this.config.onTransformUpdate(this.transform)}),n},reset:function(){return this.updateTransform({scale:1,translateX:0,translateY:0})},handleResize:function(){return this.container.getBoundingClientRect(),!0},setZoom:function(t){const e=w(this.config,e=>e(t));return this.updateTransform({scale:e})},canvasToContent:function(t,e){return v(t,e,b(this.transform.scale,this.transform.translateX,this.transform.translateY))},zoomToPoint:function(t,e,n){return X(this.transformLayer,this.config,()=>{const r=C(t,e,this.transform,n/this.transform.scale,this.config);return this.updateTransform(r)})},resetView:function(){return X(this.transformLayer,this.config,()=>D(this,t=>{const e={scale:1,translateX:-1*t,translateY:-1*t};return this.updateTransform(e)}))},zoomToFitContent:function(){return X(this.transformLayer,this.config,()=>{const t=this.getBounds(),e=t.width/this.config.width,n=t.height/this.config.height,r=w(this.config,t=>t(.9*Math.min(e,n))),o=this.config.width*r,a=this.config.height*r,s=(t.width-o)/2,i=(t.height-a)/2;return this.updateTransform({scale:r,translateX:s,translateY:i})})}}}catch(t){return console.error("Failed to create canvas:",t),null}}function R(t={}){const e={...L,...t};return("number"!=typeof e.width||e.width<=0)&&(console.warn("Invalid width, using default"),e.width=L.width),("number"!=typeof e.height||e.height<=0)&&(console.warn("Invalid height, using default"),e.height=L.height),("number"!=typeof e.zoomSpeed||e.zoomSpeed<=0)&&(console.warn("Invalid zoomSpeed, using default"),e.zoomSpeed=L.zoomSpeed),("number"!=typeof e.minZoom||e.minZoom<=0)&&(console.warn("Invalid minZoom, using default"),e.minZoom=L.minZoom),("number"!=typeof e.maxZoom||e.maxZoom<=e.minZoom)&&(console.warn("Invalid maxZoom, using default"),e.maxZoom=L.maxZoom),("number"!=typeof e.keyboardPanStep||e.keyboardPanStep<=0)&&(console.warn("Invalid keyboardPanStep, using default"),e.keyboardPanStep=L.keyboardPanStep),("number"!=typeof e.keyboardFastMultiplier||e.keyboardFastMultiplier<=0)&&(console.warn("Invalid keyboardFastMultiplier, using default"),e.keyboardFastMultiplier=L.keyboardFastMultiplier),("number"!=typeof e.clickZoomLevel||e.clickZoomLevel<=0)&&(console.warn("Invalid clickZoomLevel, using default"),e.clickZoomLevel=L.clickZoomLevel),("number"!=typeof e.rulerFontSize||e.rulerFontSize<=0)&&(console.warn("Invalid rulerFontSize, using default"),e.rulerFontSize=L.rulerFontSize),e}class F{constructor(){this.listeners=new Map}on(t,e){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e)}off(t,e){const n=this.listeners.get(t);n&&n.delete(e)}emit(t,e){const n=this.listeners.get(t);n&&n.forEach(n=>{try{n(e)}catch(e){console.error(`Error in event handler for "${String(t)}":`,e)}})}removeAllListeners(){this.listeners.clear()}}const B=300,Z=5;function I(t,e){if(!t?.getBounds)return e;try{const n=t.getBounds(),r=n.width*n.height;return e*(r/2073600)**1}catch(t){return console.warn("Failed to calculate adaptive zoom speed, using base speed:",t),e}}function P(t,e){let n=0,r=0;function o(e){const o=t.container.getBoundingClientRect(),a=e.clientX-o.left,s=e.clientY-o.top;!function(t,e,n,r){D(t,t=>r(e-t,n-t))}(t,a,s,(t,e)=>{n=t,r=e})}function a(o){if(!(o instanceof KeyboardEvent))return;if(e.limitKeyboardEventsToCanvas&&document.activeElement!==t.container)return;const a=o.shiftKey,s=e.keyboardPanStep*(a?e.keyboardFastMultiplier:1);let i=!1;const l={};switch(o.key){case"ArrowLeft":l.translateX=t.transform.translateX+s,i=!0;break;case"ArrowRight":l.translateX=t.transform.translateX-s,i=!0;break;case"ArrowUp":l.translateY=t.transform.translateY+s,i=!0;break;case"ArrowDown":l.translateY=t.transform.translateY-s,i=!0;break;case"=":case"+":{const n=e.enableAdaptiveSpeed?I(t,e.keyboardZoomStep):e.keyboardZoomStep;l.scale=y(t.transform.scale*(1+n),e),i=!0}break;case"-":{const n=e.enableAdaptiveSpeed?I(t,e.keyboardZoomStep):e.keyboardZoomStep;l.scale=y(t.transform.scale*(1-n),e),i=!0}break;case"0":if(o.metaKey||o.ctrlKey){const o=1/t.transform.scale,a=C(n,r,t.transform,o,e);Object.assign(l,a),i=!0}break;case"g":case"G":t.toggleGrid&&t.toggleGrid(),i=!0;break;case"r":case"R":o.metaKey||o.ctrlKey||o.altKey||!t.toggleRulers||(t.toggleRulers(),i=!0)}i&&(o.preventDefault(),Object.keys(l).length>0&&t.updateTransform(l))}const s=e.limitKeyboardEventsToCanvas?t.container:document;return s.addEventListener("keydown",a),t.container.addEventListener("mousemove",o),()=>{s.removeEventListener("keydown",a),t.container.removeEventListener("mousemove",o)}}function O(t,e,n,r,o){n?e.requireSpaceForMouseDrag?t.container.style.cursor=r?"grab":"default":t.container.style.cursor=o?"grabbing":"grab":t.container.style.cursor="default"}function A(t,e,n,r,o){o.setIsDragging(!1),o.setDragButton(-1),O(t,e,n,r,!1)}function _(t,e,n,r,o,a,s,i,l,c){a&&t.button===s&&A(e,n,r,o,{setIsDragging:c.setIsDragging,setDragButton:c.setDragButton}),r&&0===t.button&&n.enableClickToZoom&&i>0&&function(t,e,n,r,o,a){const s=Date.now()-r,i=t.altKey,l=!n.requireOptionForClickZoom||i;if(s<B&&!o&&!a&&l){t.preventDefault();const r=e.container.getBoundingClientRect(),o=t.clientX-r.left,a=t.clientY-r.top,{clickX:s,clickY:i}=M(e,o,a,(t,e)=>({clickX:t,clickY:e})),l=e.canvasToContent(s,i),c=r.width/2,u=r.height/2,d=n.clickZoomLevel,h={scale:d,translateX:c-l.x*d,translateY:u-l.y*d};X(e.transformLayer,e.config,()=>{e.updateTransform(h)})}}(t,e,n,i,l,a),0===t.button&&function(t){t.setMouseDownTime(0),t.setHasDragged(!1)}({setMouseDownTime:c.setMouseDownTime,setHasDragged:c.setHasDragged})}function H(t,e,n=!0){let r=!0,o=!1,a=0,s=0,i=-1,l=!1,c=0,u=0,d=0,h=!1;const m={setIsDragging:t=>{o=t},setDragButton:t=>{i=t},setIsSpacePressed:t=>{l=t},setMouseDownTime:t=>{c=t},setMouseDownX:t=>{u=t},setMouseDownY:t=>{d=t},setHasDragged:t=>{h=t},setLastMouseX:t=>{a=t},setLastMouseY:t=>{s=t}},f=n=>{!function(t,e,n,r,o,a){n.requireSpaceForMouseDrag&&" "===t.key&&(a.setIsSpacePressed(!0),O(e,n,r,!0,o))}(n,t,e,r,o,{setIsSpacePressed:m.setIsSpacePressed})},g=n=>{!function(t,e,n,r,o,a){n.requireSpaceForMouseDrag&&" "===t.key&&(a.setIsSpacePressed(!1),O(e,n,r,!1,o),o&&A(e,n,r,!1,{setIsDragging:a.setIsDragging,setDragButton:a.setDragButton}))}(n,t,e,r,o,{setIsSpacePressed:m.setIsSpacePressed,setIsDragging:m.setIsDragging,setDragButton:m.setDragButton})},p=n=>{!function(t,e,n,r,o,a){const s=0===t.button,i=1===t.button;if(s&&(a.setMouseDownTime(Date.now()),a.setMouseDownX(t.clientX),a.setMouseDownY(t.clientY),a.setHasDragged(!1)),!r)return;(!n.requireSpaceForMouseDrag||o)&&(s&&n.enableLeftDrag||i&&n.enableMiddleDrag)&&(t.preventDefault(),a.setDragButton(t.button),a.setLastMouseX(t.clientX),a.setLastMouseY(t.clientY),O(e,n,r,o,!1))}(n,t,e,r,l,m)},v=e=>{!function(t,e,n,r,o,a,s,i,l,c){if(o>0){const e=Math.abs(t.clientX-a),o=Math.abs(t.clientY-s);(e>Z||o>Z)&&(c.setHasDragged(!0),!r&&n&&c.setIsDragging(!0))}if(!r||!n)return;t.preventDefault(),k((...t)=>{const o=t[0];if(!r||!n)return;const a=o.clientX-i,s=o.clientY-l,u={translateX:e.transform.translateX+a,translateY:e.transform.translateY+s};e.updateTransform(u),c.setLastMouseX(o.clientX),c.setLastMouseY(o.clientY)})(t)}(e,t,r,o,c,u,d,a,s,{setHasDragged:m.setHasDragged,setIsDragging:m.setIsDragging,setLastMouseX:m.setLastMouseX,setLastMouseY:m.setLastMouseY})},y=n=>{_(n,t,e,r,l,o,i,c,h,{setIsDragging:m.setIsDragging,setDragButton:m.setDragButton,setMouseDownTime:m.setMouseDownTime,setHasDragged:m.setHasDragged})},b=()=>{!function(t,e,n,r,o,a){o&&A(t,e,n,r,a)}(t,e,r,l,o,{setIsDragging:m.setIsDragging,setDragButton:m.setDragButton})};t.container.addEventListener("mousedown",p),document.addEventListener("mousemove",v),document.addEventListener("mouseup",y),t.container.addEventListener("mouseleave",b),e.requireSpaceForMouseDrag&&(document.addEventListener("keydown",f),document.addEventListener("keyup",g)),O(t,e,r,l,o);const C=()=>{t.container.removeEventListener("mousedown",p),document.removeEventListener("mousemove",v),document.removeEventListener("mouseup",y),t.container.removeEventListener("mouseleave",b),e.requireSpaceForMouseDrag&&(document.removeEventListener("keydown",f),document.removeEventListener("keyup",g))};return n?{cleanup:C,enable:()=>(r=!0,O(t,e,r,l,o),!0),disable:()=>(r=!1,o&&A(t,e,r,l,{setIsDragging:m.setIsDragging,setDragButton:m.setDragButton}),O(t,e,r,l,o),!0),isEnabled:()=>r}:C}function q(t,e){return{x:(t.clientX+e.clientX)/2,y:(t.clientY+e.clientY)/2}}function G(t,e){const n=t.clientX-e.clientX,r=t.clientY-e.clientY;return Math.sqrt(n*n+r*r)}function K(t,e,n,r){const o=C(n,r,t.transform,e,t.config);return t.updateTransform(o)}function N(t,e,n){t.preventDefault();const r=Array.from(t.touches);k((...t)=>{const r=t[0];if(1===r.length){if(1===n.touches.length){const t=r[0].clientX-n.touches[0].clientX,o=r[0].clientY-n.touches[0].clientY,a={translateX:e.transform.translateX+t,translateY:e.transform.translateY+o};e.updateTransform(a)}}else if(2===r.length){const t=G(r[0],r[1]),o=q(r[0],r[1]);if(n.lastDistance>0){const r=t/n.lastDistance,a=e.container.getBoundingClientRect();let s=o.x-a.left,i=o.y-a.top;const l=function(t,e,n){return D(t,t=>{const r={...e,x:e.x-t,y:e.y-t};return n(r)})}(e,{x:s,y:i},t=>t);s=l.x,i=l.y,K(e,r,s,i)}n.lastDistance=t,n.lastCenter=o}n.touches=r})(r)}function V(t){const e={touches:[],lastDistance:0,lastCenter:{}},n=t=>{!function(t,e){t.preventDefault(),e.touches=Array.from(t.touches),2===e.touches.length&&(e.lastDistance=G(e.touches[0],e.touches[1]),e.lastCenter=q(e.touches[0],e.touches[1]))}(t,e)},r=n=>{N(n,t,e)},o=t=>{!function(t,e){e.touches=Array.from(t.touches),e.touches.length<2&&(e.lastDistance=0)}(t,e)};return t.container.addEventListener("touchstart",n,{passive:!1}),t.container.addEventListener("touchmove",r,{passive:!1}),t.container.addEventListener("touchend",o,{passive:!1}),()=>{t.container.removeEventListener("touchstart",n),t.container.removeEventListener("touchmove",r),t.container.removeEventListener("touchend",o)}}function j(t){const e=t.ctrlKey||t.metaKey,n=[0===t.deltaMode,Math.abs(t.deltaY)<50,t.deltaY%1!=0,Math.abs(t.deltaX)>0&&Math.abs(t.deltaY)>0].filter(Boolean).length>=2;return{isTrackpad:n,isMouseWheel:!n,isTrackpadScroll:n&&!e,isTrackpadPinch:n&&e,isZoomGesture:e}}function U(t,e){const n=(t=>k((...e)=>{const n=e[0];if(!n||!t?.updateTransform)return!1;try{const e=t.transform,r=1,o=n.deltaX*r,a=n.deltaY*r,s={scale:e.scale,translateX:e.translateX-o,translateY:e.translateY-a};return E(t.transformLayer,t.config),t.updateTransform(s)}catch(t){return console.error("Error handling trackpad pan:",t),!1}}))(t),r=r=>j(r).isTrackpadScroll?n(r):function(t,e,n){if(!t||"number"!=typeof t.deltaY)return console.warn("Invalid wheel event provided"),!1;if(!e?.updateTransform)return console.warn("Invalid canvas provided to handleWheelEvent"),!1;try{t.preventDefault();const r=e.container.getBoundingClientRect(),o=t.clientX-r.left,a=t.clientY-r.top,{mouseX:s,mouseY:i}=M(e,o,a,(t,e)=>({mouseX:t,mouseY:e})),l=n.zoomSpeed,c=j(t);if(!c.isZoomGesture)return!1;let u=n.enableAdaptiveSpeed?I(e,l):l;if(c.isTrackpadPinch){const t=.05*n.zoomSpeed;u=n.enableAdaptiveSpeed?I(e,t):t}return K(e,(t.deltaY<0?1:-1)>0?1+u:1/(1+u),s,i)}catch(t){return console.error("Error handling wheel event:",t),!1}}(r,t,e);return t.container.addEventListener("wheel",r,{passive:!1}),()=>{t.container.removeEventListener("wheel",r)}}function W(t,e){const n=function(t){const e=document.createElement("div");return e.className="canvas-ruler horizontal-ruler",e.style.cssText=`\n\tposition: absolute;\n\ttop: 0;\n\tleft: 24px;\n\tright: 0;\n\theight: 24px;\n\tbackground: ${t.backgroundColor};\n\tborder-bottom: 1px solid ${t.borderColor};\n\tborder-right: 1px solid ${t.borderColor};\n\tz-index: ${a};\n\tpointer-events: none;\n\tfont-family: ${t.fontFamily};\n\tfont-size: ${t.fontSize}px;\n\tcolor: ${t.textColor};\n\toverflow: hidden;\n `,e}(e),r=function(t){const e=document.createElement("div");return e.className="canvas-ruler vertical-ruler",e.style.cssText=`\n\tposition: absolute;\n\ttop: 24px;\n\tleft: 0;\n\tbottom: 0;\n\twidth: 24px;\n\tbackground: ${t.backgroundColor};\n\tborder-right: 1px solid ${t.borderColor};\n\tborder-bottom: 1px solid ${t.borderColor};\n\tz-index: ${a};\n\tpointer-events: none;\n\tfont-family: ${t.fontFamily};\n\tfont-size: ${t.fontSize}px;\n\tcolor: ${t.textColor};\n\toverflow: hidden;\n `,e}(e),i=function(t){const e=document.createElement("div");return e.className="canvas-ruler corner-box",e.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tleft: 0;\n\t\twidth: 24px;\n\t\theight: 24px;\n\t\tbackground: ${t.backgroundColor};\n\t\tborder-right: 1px solid ${t.borderColor};\n\t\tborder-bottom: 1px solid ${t.borderColor};\n\t\tz-index: ${s};\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tfont-family: ${t.fontFamily};\n\t\tfont-size: ${t.fontSize-2}px;\n\t\tcolor: ${t.textColor};\n\t\tpointer-events: none;\n\t`,e.textContent=t.units,e}(e),l=e.showGrid?function(t){const e=document.createElement("div");return e.className="canvas-ruler grid-overlay",e.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: 24px;\n\t\tleft: 24px;\n\t\tright: 0;\n\t\tbottom: 0;\n\t\tpointer-events: none;\n\t\tz-index: ${o};\n\t\tbackground-image: \n\t\t\tlinear-gradient(${t.gridColor} 1px, transparent 1px),\n\t\t\tlinear-gradient(90deg, ${t.gridColor} 1px, transparent 1px);\n\t\tbackground-size: 100px 100px;\n\t\topacity: 0.5;\n\t`,e}(e):void 0;return t.appendChild(n),t.appendChild(r),t.appendChild(i),l&&t.appendChild(l),{horizontalRuler:n,verticalRuler:r,cornerBox:i,gridOverlay:l}}function J(t,e){const n=t/Math.max(5,Math.min(20,e/50)),r=10**Math.floor(Math.log10(n)),o=n/r;let a;return a=o<=1?1:o<=2?2:o<=5?5:10,a*r}function Q(t,e,n,r,o){const a=document.createElement("div"),s=e%(r*d)===0,c=s?i:l;a.style.cssText=`\n\t\tposition: absolute;\n\t\tleft: ${n}px;\n\t\tbottom: 0;\n\t\twidth: 1px;\n\t\theight: ${c}px;\n\t\tbackground: ${s?o.majorTickColor:o.minorTickColor};\n\t`,t.appendChild(a);if(s||e%h===0){const r=document.createElement("div");r.style.cssText=`\n\t\t\tposition: absolute;\n\t\t\tleft: ${n}px;\n\t\t\tbottom: ${c}px;\n\t\t\tfont-size: ${o.fontSize}px;\n\t\t\tcolor: ${o.textColor};\n\t\t\twhite-space: nowrap;\n\t\t\tpointer-events: none;\n\t\t`,r.textContent=Math.round(e).toString(),t.appendChild(r)}}function tt(t,e,n,r,o){const a=document.createElement("div"),s=e%(r*d)===0,i=s?c:u;a.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${n}px;\n\t\tright: 0;\n\t\twidth: ${i}px;\n\t\theight: 1px;\n\t\tbackground: ${s?o.majorTickColor:o.minorTickColor};\n\t`,t.appendChild(a);if(s||e%h===0){const r=document.createElement("div");r.style.cssText=`\n\t\t\tposition: absolute;\n\t\t\ttop: ${n-6}px;\n\t\t\tright: ${i+6}px;\n\t\t\tfont-size: ${o.fontSize}px;\n\t\t\tcolor: ${o.textColor};\n\t\t\twhite-space: nowrap;\n\t\t\tpointer-events: none;\n\t\t\ttransform: rotate(-90deg);\n\t\t\ttransform-origin: right center;\n\t\t`,r.textContent=Math.round(e).toString(),t.appendChild(r)}}function et(t,e,n,o,a){const s=t.getBounds(),i=s.scale||1,l=s.translateX||0,c=s.translateY||0,u=s.width-r,d=s.height-r,h=-l/i,p=-c/i,v=p+d/i;!function(t,e,n,r,o,a){const s=r,i=J(n-e,s),l=document.createDocumentFragment(),c=Math.floor(e/i)*i,u=Math.ceil(n/i)*i;for(let t=c;t<=u;t+=i){const n=(t-e)*o;n>=-50&&n<=s+50&&Q(l,t,n,i,a)}t.innerHTML="",t.appendChild(l)}(e,h,h+u/i,u,i,a),function(t,e,n,r,o,a){const s=r,i=J(n-e,s),l=document.createDocumentFragment(),c=Math.floor(e/i)*i,u=Math.ceil(n/i)*i;for(let t=c;t<=u;t+=i){const n=(t-e)*o;n>=-50&&n<=s+50&&tt(l,t,n,i,a)}t.innerHTML="",t.appendChild(l)}(n,p,v,d,i,a),o&&function(t,e,n,r){let o=m*e;for(;o<f;)o*=2;for(;o>g;)o/=2;t.style.backgroundSize=`${o}px ${o}px`,t.style.backgroundPosition=`${n%o}px ${r%o}px`}(o,i,l,c)}function nt(t,e){if(!t?.container)return console.error("Invalid canvas provided to createRulers"),null;let n,r=null,o=!1;const a={backgroundColor:e.rulerBackgroundColor,borderColor:e.rulerBorderColor,textColor:e.rulerTextColor,majorTickColor:e.rulerMajorTickColor,minorTickColor:e.rulerMinorTickColor,fontSize:e.rulerFontSize,fontFamily:e.rulerFontFamily,showGrid:e.enableGrid,gridColor:e.gridColor,units:e.rulerUnits},s=()=>{!o&&n.horizontalRuler&&n.verticalRuler&&et(t,n.horizontalRuler,n.verticalRuler,n.gridOverlay,a)};try{return n=W(t.container,a),r=function(t,e){const n=k(e),r=t.updateTransform;t.updateTransform=function(t){const e=r.call(this,t);return n(),e};const o=k(e);return window.addEventListener("resize",o),()=>{window.removeEventListener("resize",o),t.updateTransform=r,n.cleanup(),o.cleanup()}}(t,s),s(),{horizontalRuler:n.horizontalRuler,verticalRuler:n.verticalRuler,cornerBox:n.cornerBox,gridOverlay:n.gridOverlay,update:s,show:()=>{n.horizontalRuler&&(n.horizontalRuler.style.display="block"),n.verticalRuler&&(n.verticalRuler.style.display="block"),n.cornerBox&&(n.cornerBox.style.display="flex"),n.gridOverlay&&(n.gridOverlay.style.display="block")},hide:()=>{n.horizontalRuler&&(n.horizontalRuler.style.display="none"),n.verticalRuler&&(n.verticalRuler.style.display="none"),n.cornerBox&&(n.cornerBox.style.display="none"),n.gridOverlay&&(n.gridOverlay.style.display="none")},toggleGrid:()=>{if(n.gridOverlay){const t="none"!==n.gridOverlay.style.display;n.gridOverlay.style.display=t?"none":"block"}},destroy:()=>{o=!0,r&&r(),n.horizontalRuler?.parentNode&&n.horizontalRuler.parentNode.removeChild(n.horizontalRuler),n.verticalRuler?.parentNode&&n.verticalRuler.parentNode.removeChild(n.verticalRuler),n.cornerBox?.parentNode&&n.cornerBox.parentNode.removeChild(n.cornerBox),n.gridOverlay?.parentNode&&n.gridOverlay.parentNode.removeChild(n.gridOverlay)}}}catch(t){return console.error("Failed to create rulers:",t),null}}return class{constructor(t,e={}){if(this.cleanupFunctions=[],this.rulers=null,this.dragSetup=null,this._isReady=!1,this.listen=new F,!t)throw new Error("Container element is required");this.config=R(e);const n=z(t,this.config);if(!n)throw new Error("Failed to create canvas");this.baseCanvas=n,this.setupEventHandlers(),this._isReady=!0,this.listen.emit("ready",this)}setupEventHandlers(){try{T(this.config,"enableZoom",()=>{const t=U(this,this.config);this.cleanupFunctions.push(t)}),(this.config.enablePan||this.config.enableClickToZoom)&&(this.dragSetup=H(this,this.config,!0),this.cleanupFunctions.push(this.dragSetup.cleanup)),T(this.config,"enableKeyboard",()=>{const t=P(this,this.config);this.cleanupFunctions.push(t)}),T(this.config,"enableTouch",()=>{const t=V(this);this.cleanupFunctions.push(t)}),T(this.config,"enableRulers",()=>{this.rulers=nt(this.baseCanvas,this.config),this.cleanupFunctions.push(()=>{this.rulers&&this.rulers.destroy()})})}catch(t){throw console.error("Failed to set up event handlers:",t),this.cleanup(),t}}get container(){return this.baseCanvas.container}get transformLayer(){return this.baseCanvas.transformLayer}get contentLayer(){return this.baseCanvas.contentLayer}get transform(){return this.baseCanvas.transform}get isReady(){return this._isReady}get isTransforming(){return this.dragSetup?.isEnabled()||!1}get visibleBounds(){return this.getVisibleArea()}getBounds(){return this.baseCanvas.getBounds()}updateTransform(t){const e=this.baseCanvas.updateTransform(t);return e&&this.emitTransformEvents(),e}emitTransformEvents(){const t=this.baseCanvas.transform;this.listen.emit("transform",t),this.listen.emit("zoom",t.scale),this.listen.emit("pan",{x:t.translateX,y:t.translateY})}reset(){return this.baseCanvas.reset()}handleResize(){return this.baseCanvas.handleResize()}setZoom(t){return this.baseCanvas.setZoom(t)}canvasToContent(t,e){return this.baseCanvas.canvasToContent(t,e)}zoomToPoint(t,e,n){return X(this.transformLayer,this.config,()=>{const r=this.baseCanvas.zoomToPoint(t,e,n);return r&&this.emitTransformEvents(),r})}resetView(){return X(this.transformLayer,this.config,()=>{const t=!!this.baseCanvas.resetView&&this.baseCanvas.resetView();return t&&this.emitTransformEvents(),t})}zoomToFitContent(){return X(this.transformLayer,this.config,()=>{const t=this.baseCanvas.zoomToFitContent();return t&&this.emitTransformEvents(),t})}panLeft(t){const e=t??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX+e};return this.updateTransform(n)}panRight(t){const e=t??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX-e};return this.updateTransform(n)}panUp(t){const e=t??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY+e};return this.updateTransform(n)}panDown(t){const e=t??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY-e};return this.updateTransform(n)}zoomIn(t=.1){return X(this.transformLayer,this.config,()=>w(this.config,e=>{const n={scale:e(this.baseCanvas.transform.scale*(1+t))};return this.updateTransform(n)}))}zoomOut(t=.1){return X(this.transformLayer,this.config,()=>w(this.config,e=>{const n={scale:e(this.baseCanvas.transform.scale*(1-t))};return this.updateTransform(n)}))}resetZoom(){return this.resetView()}enableMouseDrag(){return this.dragSetup?.enable()??!1}disableMouseDrag(){return this.dragSetup?.disable()??!1}isMouseDragEnabled(){return this.dragSetup?.isEnabled()??!1}toggleGrid(){return!!this.rulers?.toggleGrid&&(this.rulers.toggleGrid(),!0)}showGrid(){return!!this.rulers?.gridOverlay&&(this.rulers.gridOverlay.style.display="block",!0)}hideGrid(){return!!this.rulers?.gridOverlay&&(this.rulers.gridOverlay.style.display="none",!0)}isGridVisible(){return!!this.rulers?.gridOverlay&&"none"!==this.rulers.gridOverlay.style.display}toggleRulers(){if(this.rulers){return this.areRulersVisible()?this.rulers.hide():this.rulers.show(),!0}return!1}showRulers(){return!!this.rulers&&(this.rulers.show(),!0)}hideRulers(){return!!this.rulers&&(this.rulers.hide(),!0)}areRulersVisible(){return!!this.rulers?.horizontalRuler&&"none"!==this.rulers.horizontalRuler.style.display}centerContent(){return X(this.transformLayer,this.config,()=>{const t=this.baseCanvas.getBounds(),e=(t.width-t.contentWidth*this.baseCanvas.transform.scale)/2,n=(t.height-t.contentHeight*this.baseCanvas.transform.scale)/2;return this.updateTransform({translateX:e,translateY:n})})}fitToScreen(){return X(this.transformLayer,this.config,()=>{const t=this.baseCanvas.zoomToFitContent();return t&&this.emitTransformEvents(),t})}getVisibleArea(){return this.baseCanvas.getBounds().visibleArea}isPointVisible(t,e){const n=this.getVisibleArea();return t>=n.x&&t<=n.x+n.width&&e>=n.y&&e<=n.y+n.height}scrollToPoint(t,e){return X(this.transformLayer,this.config,()=>{const n=this.baseCanvas.getBounds(),r=n.width/2,o=n.height/2,a=r-t*this.baseCanvas.transform.scale,s=o-e*this.baseCanvas.transform.scale;return this.updateTransform({translateX:a,translateY:s})})}getConfig(){return{...this.config}}updateConfig(t){this.config=R({...this.config,...t})}cleanup(){this.cleanupFunctions.forEach(t=>{try{t()}catch(t){console.warn("Error during cleanup:",t)}}),this.cleanupFunctions=[],this.removeAllListeners()}on(t,e){this.listen.on(t,e)}off(t,e){this.listen.off(t,e)}emit(t,e){this.listen.emit(t,e)}removeAllListeners(){this.listen.removeAllListeners()}destroy(){this.cleanup(),window.__markupCanvasTransitionTimeout&&clearTimeout(window.__markupCanvasTransitionTimeout)}}});
@@ -0,0 +1,86 @@
1
+ import type { MarkupCanvasConfig } from "./config";
2
+ export interface Transform {
3
+ scale: number;
4
+ translateX: number;
5
+ translateY: number;
6
+ }
7
+ export interface CanvasOptions {
8
+ width?: number;
9
+ height?: number;
10
+ enableAcceleration?: boolean;
11
+ enableEventHandling?: boolean;
12
+ onTransformUpdate?: (transform: Transform) => void;
13
+ }
14
+ export interface CanvasBounds {
15
+ width: number;
16
+ height: number;
17
+ contentWidth: number;
18
+ contentHeight: number;
19
+ scale: number;
20
+ translateX: number;
21
+ translateY: number;
22
+ visibleArea: {
23
+ x: number;
24
+ y: number;
25
+ width: number;
26
+ height: number;
27
+ };
28
+ scaledContentWidth: number;
29
+ scaledContentHeight: number;
30
+ canPanLeft: boolean;
31
+ canPanRight: boolean;
32
+ canPanUp: boolean;
33
+ canPanDown: boolean;
34
+ canZoomIn: boolean;
35
+ canZoomOut: boolean;
36
+ }
37
+ export interface BaseCanvas {
38
+ container: HTMLElement;
39
+ transformLayer: HTMLElement;
40
+ contentLayer: HTMLElement;
41
+ config: Required<MarkupCanvasConfig>;
42
+ transform: Transform;
43
+ getBounds: () => CanvasBounds;
44
+ updateTransform: (newTransform: Partial<Transform>) => boolean;
45
+ reset: () => boolean;
46
+ handleResize: () => boolean;
47
+ setZoom: (zoomLevel: number) => boolean;
48
+ canvasToContent: (x: number, y: number) => {
49
+ x: number;
50
+ y: number;
51
+ };
52
+ zoomToPoint: (x: number, y: number, targetScale: number) => boolean;
53
+ resetView: () => boolean;
54
+ zoomToFitContent: () => boolean;
55
+ }
56
+ export interface Canvas extends BaseCanvas {
57
+ cleanup?: () => void;
58
+ panLeft: (distance?: number) => boolean;
59
+ panRight: (distance?: number) => boolean;
60
+ panUp: (distance?: number) => boolean;
61
+ panDown: (distance?: number) => boolean;
62
+ zoomIn: (factor?: number) => boolean;
63
+ zoomOut: (factor?: number) => boolean;
64
+ resetZoom: (duration?: number) => boolean;
65
+ enableMouseDrag: () => boolean;
66
+ disableMouseDrag: () => boolean;
67
+ isMouseDragEnabled: () => boolean;
68
+ toggleGrid?: () => boolean;
69
+ showGrid?: () => boolean;
70
+ hideGrid?: () => boolean;
71
+ isGridVisible?: () => boolean;
72
+ toggleRulers?: () => boolean;
73
+ showRulers?: () => boolean;
74
+ hideRulers?: () => boolean;
75
+ areRulersVisible?: () => boolean;
76
+ centerContent: () => boolean;
77
+ fitToScreen: () => boolean;
78
+ getVisibleArea: () => {
79
+ x: number;
80
+ y: number;
81
+ width: number;
82
+ height: number;
83
+ };
84
+ isPointVisible: (x: number, y: number) => boolean;
85
+ scrollToPoint: (x: number, y: number, duration?: number) => boolean;
86
+ }
@@ -0,0 +1,38 @@
1
+ import type { Transform } from "./canvas";
2
+ export interface MarkupCanvasConfig {
3
+ width?: number;
4
+ height?: number;
5
+ enableAcceleration?: boolean;
6
+ enableZoom?: boolean;
7
+ enablePan?: boolean;
8
+ enableTouch?: boolean;
9
+ enableKeyboard?: boolean;
10
+ limitKeyboardEventsToCanvas?: boolean;
11
+ zoomSpeed?: number;
12
+ minZoom?: number;
13
+ maxZoom?: number;
14
+ enableTransition?: boolean;
15
+ transitionDuration?: number;
16
+ enableAdaptiveSpeed?: boolean;
17
+ enableLeftDrag?: boolean;
18
+ enableMiddleDrag?: boolean;
19
+ requireSpaceForMouseDrag?: boolean;
20
+ keyboardPanStep?: number;
21
+ keyboardFastMultiplier?: number;
22
+ keyboardZoomStep?: number;
23
+ enableClickToZoom?: boolean;
24
+ clickZoomLevel?: number;
25
+ requireOptionForClickZoom?: boolean;
26
+ enableRulers?: boolean;
27
+ enableGrid?: boolean;
28
+ gridColor?: string;
29
+ rulerBackgroundColor?: string;
30
+ rulerBorderColor?: string;
31
+ rulerTextColor?: string;
32
+ rulerMajorTickColor?: string;
33
+ rulerMinorTickColor?: string;
34
+ rulerFontSize?: number;
35
+ rulerFontFamily?: string;
36
+ rulerUnits?: string;
37
+ onTransformUpdate?: (transform: Transform) => void;
38
+ }
@@ -0,0 +1,33 @@
1
+ import type { MarkupCanvas } from "@/lib/MarkupCanvas.js";
2
+ import type { Transform } from "@/types/canvas.js";
3
+ export interface MarkupCanvasEvents {
4
+ transform: Transform;
5
+ zoom: number;
6
+ pan: {
7
+ x: number;
8
+ y: number;
9
+ };
10
+ ready: MarkupCanvas;
11
+ [key: string]: unknown;
12
+ }
13
+ export interface TouchState {
14
+ touches: Touch[];
15
+ lastDistance: number;
16
+ lastCenter: {
17
+ x: number;
18
+ y: number;
19
+ };
20
+ }
21
+ export interface GestureInfo {
22
+ isTrackpad: boolean;
23
+ isMouseWheel: boolean;
24
+ isTrackpadScroll: boolean;
25
+ isTrackpadPinch: boolean;
26
+ isZoomGesture: boolean;
27
+ }
28
+ export interface MouseDragControls {
29
+ cleanup: () => void;
30
+ enable: () => boolean;
31
+ disable: () => boolean;
32
+ isEnabled: () => boolean;
33
+ }
@@ -0,0 +1,5 @@
1
+ export type { BaseCanvas, Canvas, CanvasBounds, CanvasOptions, Transform, } from "./canvas.js";
2
+ export type { MarkupCanvasConfig } from "./config.js";
3
+ export type { GestureInfo, MarkupCanvasEvents, MouseDragControls, TouchState } from "./events.js";
4
+ export type { Point, ZoomBoundaryOptions, ZoomBoundaryResult, } from "./matrix.js";
5
+ export type { RulerCanvas, RulerOptions, RulerSystem, } from "./rulers.js";
@@ -0,0 +1,17 @@
1
+ export interface Point {
2
+ x: number;
3
+ y: number;
4
+ }
5
+ export interface ZoomBoundaryResult {
6
+ scale: number;
7
+ clamped: boolean;
8
+ hitBoundary: "min" | "max" | "invalid" | null;
9
+ message: string | null;
10
+ }
11
+ export interface ZoomBoundaryOptions {
12
+ minZoom?: number;
13
+ maxZoom?: number;
14
+ provideFeedback?: boolean;
15
+ logBoundaryHits?: boolean;
16
+ onBoundaryHit?: (boundary: string, originalScale: number, clampedScale: number) => void;
17
+ }
@@ -0,0 +1,31 @@
1
+ import type { CanvasBounds, Transform } from "./canvas.js";
2
+ export interface RulerOptions {
3
+ backgroundColor?: string;
4
+ borderColor?: string;
5
+ textColor?: string;
6
+ majorTickColor?: string;
7
+ minorTickColor?: string;
8
+ fontSize?: number;
9
+ fontFamily?: string;
10
+ showGrid?: boolean;
11
+ gridColor?: string;
12
+ units?: string;
13
+ }
14
+ export interface RulerSystem {
15
+ horizontalRuler: HTMLElement;
16
+ verticalRuler: HTMLElement;
17
+ cornerBox: HTMLElement;
18
+ gridOverlay?: HTMLElement;
19
+ update: () => void;
20
+ show: () => void;
21
+ hide: () => void;
22
+ toggleGrid: () => void;
23
+ destroy: () => void;
24
+ }
25
+ export interface RulerCanvas {
26
+ container: HTMLElement;
27
+ transformLayer?: HTMLElement;
28
+ transform: Transform;
29
+ updateTransform: (newTransform: Partial<Transform>) => boolean;
30
+ getBounds: () => CanvasBounds;
31
+ }
package/dist/umd.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { MarkupCanvas as default } from "./lib/MarkupCanvas.js";
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@markup-canvas/core",
3
+ "version": "1.0.0",
4
+ "description": "High-performance canvas-like container with pan and zoom capabilities",
5
+ "type": "module",
6
+ "main": "dist/markup-canvas.cjs.js",
7
+ "module": "dist/markup-canvas.esm.js",
8
+ "browser": "dist/markup-canvas.umd.js",
9
+ "types": "dist/index.d.ts",
10
+ "files": [
11
+ "dist/",
12
+ "src/",
13
+ "README.md"
14
+ ],
15
+ "scripts": {
16
+ "build": "rollup -c",
17
+ "dev": "rollup -c -w",
18
+ "clean": "rm -rf dist",
19
+ "lint": "biome check .",
20
+ "format": "biome format --write .",
21
+ "prepublishOnly": "npm run clean && npm run build"
22
+ },
23
+ "keywords": [
24
+ "zoom",
25
+ "pan",
26
+ "canvas",
27
+ "transform",
28
+ "interactive",
29
+ "html",
30
+ "dom"
31
+ ],
32
+ "author": "Fritz Benning <mail@fritzbenning.de>",
33
+ "license": "CC-BY-NC-4.0",
34
+ "devDependencies": {
35
+ "@biomejs/biome": "2.2.6",
36
+ "@rollup/plugin-alias": "^5.1.1",
37
+ "@rollup/plugin-commonjs": "^28.0.8",
38
+ "@rollup/plugin-node-resolve": "^15.2.3",
39
+ "@rollup/plugin-terser": "^0.4.4",
40
+ "@rollup/plugin-typescript": "^11.1.6",
41
+ "glob": "^11.0.3",
42
+ "rollup": "^4.52.4",
43
+ "tslib": "^2.8.1",
44
+ "ttypescript": "^1.5.15",
45
+ "typescript": "^5.9.3",
46
+ "typescript-transform-paths": "^3.5.5"
47
+ },
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/fritzbenning/markup-canvas"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/fritzbenning/markup-canvas/issues"
54
+ },
55
+ "homepage": "https://github.com/fritzbenning/markup-canvas#readme"
56
+ }
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ export { MarkupCanvas, MarkupCanvas as default } from "./lib/MarkupCanvas.js";
2
+ export type {
3
+ BaseCanvas,
4
+ Canvas,
5
+ CanvasBounds,
6
+ CanvasOptions,
7
+ GestureInfo,
8
+ MarkupCanvasConfig,
9
+ MarkupCanvasEvents,
10
+ MouseDragControls,
11
+ Point,
12
+ RulerCanvas,
13
+ RulerOptions,
14
+ RulerSystem,
15
+ TouchState,
16
+ Transform,
17
+ ZoomBoundaryOptions,
18
+ ZoomBoundaryResult,
19
+ } from "./types/index.js";