@markup-canvas/core 1.0.8 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/MarkupCanvas.d.ts +3 -0
- package/dist/lib/events/EventEmitter.d.ts +2 -0
- package/dist/markup-canvas.cjs.js +65 -6
- package/dist/markup-canvas.esm.js +65 -6
- package/dist/markup-canvas.umd.js +60 -4
- package/dist/markup-canvas.umd.min.js +1 -1
- package/dist/types/config.d.ts +3 -1
- package/package.json +1 -1
- package/src/lib/MarkupCanvas.ts +65 -0
- package/src/lib/config/constants.ts +5 -1
- package/src/lib/config/presets/editor-preset.ts +6 -2
- package/src/lib/events/EventEmitter.ts +7 -0
- package/src/lib/events/keyboard/setupKeyboardEvents.ts +2 -2
- package/src/lib/events/keyboard/setupKeyboardNavigation.ts +2 -2
- package/src/types/config.ts +5 -1
|
@@ -13,6 +13,9 @@ export declare class MarkupCanvas implements Canvas {
|
|
|
13
13
|
private _isReady;
|
|
14
14
|
private listen;
|
|
15
15
|
constructor(container: HTMLElement, options?: MarkupCanvasConfig);
|
|
16
|
+
private setupGlobalBinding;
|
|
17
|
+
private cleanupGlobalBinding;
|
|
18
|
+
private broadcastEvent;
|
|
16
19
|
private setupEventHandlers;
|
|
17
20
|
get container(): HTMLElement;
|
|
18
21
|
get transformLayer(): HTMLElement;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare class EventEmitter<Events extends Record<string, unknown>> {
|
|
2
2
|
private listeners;
|
|
3
|
+
private emitInterceptor?;
|
|
4
|
+
setEmitInterceptor(interceptor: (event: keyof Events, data: unknown) => void): void;
|
|
3
5
|
on<K extends keyof Events>(event: K, handler: (data: Events[K]) => void): void;
|
|
4
6
|
off<K extends keyof Events>(event: K, handler: (data: Events[K]) => void): void;
|
|
5
7
|
emit<K extends keyof Events>(event: K, data: Events[K]): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 1.0.
|
|
4
|
+
* @version 1.0.9
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -17,7 +17,7 @@ const EDITOR_PRESET = {
|
|
|
17
17
|
enablePan: true,
|
|
18
18
|
enableTouch: true,
|
|
19
19
|
enableKeyboard: false,
|
|
20
|
-
|
|
20
|
+
bindKeyboardEventsTo: "canvas",
|
|
21
21
|
// Zoom behavior
|
|
22
22
|
zoomSpeed: 1.5,
|
|
23
23
|
minZoom: 0.05,
|
|
@@ -50,7 +50,7 @@ const EDITOR_PRESET = {
|
|
|
50
50
|
canvasBackgroundColor: "transparent",
|
|
51
51
|
canvasBackgroundColorDark: "transparent",
|
|
52
52
|
// Ruler styling
|
|
53
|
-
rulerBackgroundColor: "oklch(100% 0 0 / 0.
|
|
53
|
+
rulerBackgroundColor: "oklch(100% 0 0 / 0.96)",
|
|
54
54
|
rulerBorderColor: "oklch(96.7% 0.001 286.375)",
|
|
55
55
|
rulerTextColor: "oklch(70.5% 0.015 286.067)",
|
|
56
56
|
rulerTickColor: "oklch(92% 0.004 286.32)",
|
|
@@ -63,6 +63,9 @@ const EDITOR_PRESET = {
|
|
|
63
63
|
gridColorDark: "rgba(232, 86, 193, 0.5)",
|
|
64
64
|
// Theme
|
|
65
65
|
themeMode: "light",
|
|
66
|
+
// Global Binding & Instance Access
|
|
67
|
+
bindToWindow: false,
|
|
68
|
+
name: "markupCanvas",
|
|
66
69
|
// Callbacks
|
|
67
70
|
onTransformUpdate: () => { },
|
|
68
71
|
};
|
|
@@ -315,7 +318,7 @@ const DEFAULT_CONFIG = {
|
|
|
315
318
|
enablePan: true,
|
|
316
319
|
enableTouch: true,
|
|
317
320
|
enableKeyboard: true,
|
|
318
|
-
|
|
321
|
+
bindKeyboardEventsTo: "canvas",
|
|
319
322
|
// Zoom behavior
|
|
320
323
|
zoomSpeed: 1.5,
|
|
321
324
|
minZoom: 0.05,
|
|
@@ -361,6 +364,9 @@ const DEFAULT_CONFIG = {
|
|
|
361
364
|
gridColorDark: "rgba(232, 86, 193, 0.5)",
|
|
362
365
|
// Theme
|
|
363
366
|
themeMode: "light",
|
|
367
|
+
// Global Binding & Instance Access
|
|
368
|
+
bindToWindow: false,
|
|
369
|
+
name: "markupCanvas",
|
|
364
370
|
// Callbacks
|
|
365
371
|
onTransformUpdate: () => { },
|
|
366
372
|
};
|
|
@@ -708,6 +714,9 @@ class EventEmitter {
|
|
|
708
714
|
constructor() {
|
|
709
715
|
this.listeners = new Map();
|
|
710
716
|
}
|
|
717
|
+
setEmitInterceptor(interceptor) {
|
|
718
|
+
this.emitInterceptor = interceptor;
|
|
719
|
+
}
|
|
711
720
|
on(event, handler) {
|
|
712
721
|
if (!this.listeners.has(event)) {
|
|
713
722
|
this.listeners.set(event, new Set());
|
|
@@ -721,6 +730,7 @@ class EventEmitter {
|
|
|
721
730
|
}
|
|
722
731
|
}
|
|
723
732
|
emit(event, data) {
|
|
733
|
+
this.emitInterceptor?.(event, data);
|
|
724
734
|
const handlers = this.listeners.get(event);
|
|
725
735
|
if (handlers) {
|
|
726
736
|
handlers.forEach((handler) => {
|
|
@@ -779,7 +789,7 @@ function setupKeyboardEvents(canvas, config) {
|
|
|
779
789
|
function handleKeyDown(event) {
|
|
780
790
|
if (!(event instanceof KeyboardEvent))
|
|
781
791
|
return;
|
|
782
|
-
if (config.
|
|
792
|
+
if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container)
|
|
783
793
|
return;
|
|
784
794
|
const isFastPan = event.shiftKey;
|
|
785
795
|
const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
|
|
@@ -852,7 +862,7 @@ function setupKeyboardEvents(canvas, config) {
|
|
|
852
862
|
}
|
|
853
863
|
}
|
|
854
864
|
}
|
|
855
|
-
const keyboardTarget = config.
|
|
865
|
+
const keyboardTarget = config.bindKeyboardEventsTo === "canvas" ? canvas.container : document;
|
|
856
866
|
keyboardTarget.addEventListener("keydown", handleKeyDown);
|
|
857
867
|
canvas.container.addEventListener("mousemove", handleMouseMove);
|
|
858
868
|
return () => {
|
|
@@ -1783,11 +1793,59 @@ class MarkupCanvas {
|
|
|
1783
1793
|
throw new Error("Failed to create canvas");
|
|
1784
1794
|
}
|
|
1785
1795
|
this.baseCanvas = canvas;
|
|
1796
|
+
if (this.config.bindToWindow) {
|
|
1797
|
+
this.listen.setEmitInterceptor((event, data) => {
|
|
1798
|
+
this.broadcastEvent(event, data);
|
|
1799
|
+
});
|
|
1800
|
+
this.setupGlobalBinding();
|
|
1801
|
+
}
|
|
1786
1802
|
this.setupEventHandlers();
|
|
1787
1803
|
this._isReady = true;
|
|
1788
1804
|
// Emit ready event
|
|
1789
1805
|
this.listen.emit("ready", this);
|
|
1790
1806
|
}
|
|
1807
|
+
setupGlobalBinding() {
|
|
1808
|
+
if (typeof window === "undefined") {
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1811
|
+
const canvasName = this.config.name || "markupCanvas";
|
|
1812
|
+
const windowObj = window;
|
|
1813
|
+
// Bind instance to window
|
|
1814
|
+
windowObj[canvasName] = this;
|
|
1815
|
+
// Track all instances
|
|
1816
|
+
if (!windowObj.__markupCanvasInstances) {
|
|
1817
|
+
windowObj.__markupCanvasInstances = new Map();
|
|
1818
|
+
}
|
|
1819
|
+
windowObj.__markupCanvasInstances.set(canvasName, this);
|
|
1820
|
+
}
|
|
1821
|
+
cleanupGlobalBinding() {
|
|
1822
|
+
if (typeof window === "undefined") {
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
const canvasName = this.config.name || "markupCanvas";
|
|
1826
|
+
const windowObj = window;
|
|
1827
|
+
delete windowObj[canvasName];
|
|
1828
|
+
if (windowObj.__markupCanvasInstances) {
|
|
1829
|
+
windowObj.__markupCanvasInstances.delete(canvasName);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
broadcastEvent(event, data) {
|
|
1833
|
+
if (typeof window === "undefined") {
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
// Receivers can get the instance from the window binding
|
|
1837
|
+
let broadcastData = data;
|
|
1838
|
+
if (event === "ready") {
|
|
1839
|
+
broadcastData = { ready: true };
|
|
1840
|
+
}
|
|
1841
|
+
window.postMessage({
|
|
1842
|
+
source: "markup-canvas",
|
|
1843
|
+
event,
|
|
1844
|
+
data: broadcastData,
|
|
1845
|
+
timestamp: Date.now(),
|
|
1846
|
+
canvasName: this.config.name,
|
|
1847
|
+
}, "*");
|
|
1848
|
+
}
|
|
1791
1849
|
setupEventHandlers() {
|
|
1792
1850
|
try {
|
|
1793
1851
|
// Wheel zoom
|
|
@@ -2099,6 +2157,7 @@ class MarkupCanvas {
|
|
|
2099
2157
|
}
|
|
2100
2158
|
// Cleanup method
|
|
2101
2159
|
cleanup() {
|
|
2160
|
+
this.cleanupGlobalBinding();
|
|
2102
2161
|
this.cleanupFunctions.forEach((cleanup) => {
|
|
2103
2162
|
try {
|
|
2104
2163
|
cleanup();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 1.0.
|
|
4
|
+
* @version 1.0.9
|
|
5
5
|
*/
|
|
6
6
|
const EDITOR_PRESET = {
|
|
7
7
|
// Canvas dimensions
|
|
@@ -13,7 +13,7 @@ const EDITOR_PRESET = {
|
|
|
13
13
|
enablePan: true,
|
|
14
14
|
enableTouch: true,
|
|
15
15
|
enableKeyboard: false,
|
|
16
|
-
|
|
16
|
+
bindKeyboardEventsTo: "canvas",
|
|
17
17
|
// Zoom behavior
|
|
18
18
|
zoomSpeed: 1.5,
|
|
19
19
|
minZoom: 0.05,
|
|
@@ -46,7 +46,7 @@ const EDITOR_PRESET = {
|
|
|
46
46
|
canvasBackgroundColor: "transparent",
|
|
47
47
|
canvasBackgroundColorDark: "transparent",
|
|
48
48
|
// Ruler styling
|
|
49
|
-
rulerBackgroundColor: "oklch(100% 0 0 / 0.
|
|
49
|
+
rulerBackgroundColor: "oklch(100% 0 0 / 0.96)",
|
|
50
50
|
rulerBorderColor: "oklch(96.7% 0.001 286.375)",
|
|
51
51
|
rulerTextColor: "oklch(70.5% 0.015 286.067)",
|
|
52
52
|
rulerTickColor: "oklch(92% 0.004 286.32)",
|
|
@@ -59,6 +59,9 @@ const EDITOR_PRESET = {
|
|
|
59
59
|
gridColorDark: "rgba(232, 86, 193, 0.5)",
|
|
60
60
|
// Theme
|
|
61
61
|
themeMode: "light",
|
|
62
|
+
// Global Binding & Instance Access
|
|
63
|
+
bindToWindow: false,
|
|
64
|
+
name: "markupCanvas",
|
|
62
65
|
// Callbacks
|
|
63
66
|
onTransformUpdate: () => { },
|
|
64
67
|
};
|
|
@@ -311,7 +314,7 @@ const DEFAULT_CONFIG = {
|
|
|
311
314
|
enablePan: true,
|
|
312
315
|
enableTouch: true,
|
|
313
316
|
enableKeyboard: true,
|
|
314
|
-
|
|
317
|
+
bindKeyboardEventsTo: "canvas",
|
|
315
318
|
// Zoom behavior
|
|
316
319
|
zoomSpeed: 1.5,
|
|
317
320
|
minZoom: 0.05,
|
|
@@ -357,6 +360,9 @@ const DEFAULT_CONFIG = {
|
|
|
357
360
|
gridColorDark: "rgba(232, 86, 193, 0.5)",
|
|
358
361
|
// Theme
|
|
359
362
|
themeMode: "light",
|
|
363
|
+
// Global Binding & Instance Access
|
|
364
|
+
bindToWindow: false,
|
|
365
|
+
name: "markupCanvas",
|
|
360
366
|
// Callbacks
|
|
361
367
|
onTransformUpdate: () => { },
|
|
362
368
|
};
|
|
@@ -704,6 +710,9 @@ class EventEmitter {
|
|
|
704
710
|
constructor() {
|
|
705
711
|
this.listeners = new Map();
|
|
706
712
|
}
|
|
713
|
+
setEmitInterceptor(interceptor) {
|
|
714
|
+
this.emitInterceptor = interceptor;
|
|
715
|
+
}
|
|
707
716
|
on(event, handler) {
|
|
708
717
|
if (!this.listeners.has(event)) {
|
|
709
718
|
this.listeners.set(event, new Set());
|
|
@@ -717,6 +726,7 @@ class EventEmitter {
|
|
|
717
726
|
}
|
|
718
727
|
}
|
|
719
728
|
emit(event, data) {
|
|
729
|
+
this.emitInterceptor?.(event, data);
|
|
720
730
|
const handlers = this.listeners.get(event);
|
|
721
731
|
if (handlers) {
|
|
722
732
|
handlers.forEach((handler) => {
|
|
@@ -775,7 +785,7 @@ function setupKeyboardEvents(canvas, config) {
|
|
|
775
785
|
function handleKeyDown(event) {
|
|
776
786
|
if (!(event instanceof KeyboardEvent))
|
|
777
787
|
return;
|
|
778
|
-
if (config.
|
|
788
|
+
if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container)
|
|
779
789
|
return;
|
|
780
790
|
const isFastPan = event.shiftKey;
|
|
781
791
|
const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
|
|
@@ -848,7 +858,7 @@ function setupKeyboardEvents(canvas, config) {
|
|
|
848
858
|
}
|
|
849
859
|
}
|
|
850
860
|
}
|
|
851
|
-
const keyboardTarget = config.
|
|
861
|
+
const keyboardTarget = config.bindKeyboardEventsTo === "canvas" ? canvas.container : document;
|
|
852
862
|
keyboardTarget.addEventListener("keydown", handleKeyDown);
|
|
853
863
|
canvas.container.addEventListener("mousemove", handleMouseMove);
|
|
854
864
|
return () => {
|
|
@@ -1779,11 +1789,59 @@ class MarkupCanvas {
|
|
|
1779
1789
|
throw new Error("Failed to create canvas");
|
|
1780
1790
|
}
|
|
1781
1791
|
this.baseCanvas = canvas;
|
|
1792
|
+
if (this.config.bindToWindow) {
|
|
1793
|
+
this.listen.setEmitInterceptor((event, data) => {
|
|
1794
|
+
this.broadcastEvent(event, data);
|
|
1795
|
+
});
|
|
1796
|
+
this.setupGlobalBinding();
|
|
1797
|
+
}
|
|
1782
1798
|
this.setupEventHandlers();
|
|
1783
1799
|
this._isReady = true;
|
|
1784
1800
|
// Emit ready event
|
|
1785
1801
|
this.listen.emit("ready", this);
|
|
1786
1802
|
}
|
|
1803
|
+
setupGlobalBinding() {
|
|
1804
|
+
if (typeof window === "undefined") {
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
const canvasName = this.config.name || "markupCanvas";
|
|
1808
|
+
const windowObj = window;
|
|
1809
|
+
// Bind instance to window
|
|
1810
|
+
windowObj[canvasName] = this;
|
|
1811
|
+
// Track all instances
|
|
1812
|
+
if (!windowObj.__markupCanvasInstances) {
|
|
1813
|
+
windowObj.__markupCanvasInstances = new Map();
|
|
1814
|
+
}
|
|
1815
|
+
windowObj.__markupCanvasInstances.set(canvasName, this);
|
|
1816
|
+
}
|
|
1817
|
+
cleanupGlobalBinding() {
|
|
1818
|
+
if (typeof window === "undefined") {
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
const canvasName = this.config.name || "markupCanvas";
|
|
1822
|
+
const windowObj = window;
|
|
1823
|
+
delete windowObj[canvasName];
|
|
1824
|
+
if (windowObj.__markupCanvasInstances) {
|
|
1825
|
+
windowObj.__markupCanvasInstances.delete(canvasName);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
broadcastEvent(event, data) {
|
|
1829
|
+
if (typeof window === "undefined") {
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
// Receivers can get the instance from the window binding
|
|
1833
|
+
let broadcastData = data;
|
|
1834
|
+
if (event === "ready") {
|
|
1835
|
+
broadcastData = { ready: true };
|
|
1836
|
+
}
|
|
1837
|
+
window.postMessage({
|
|
1838
|
+
source: "markup-canvas",
|
|
1839
|
+
event,
|
|
1840
|
+
data: broadcastData,
|
|
1841
|
+
timestamp: Date.now(),
|
|
1842
|
+
canvasName: this.config.name,
|
|
1843
|
+
}, "*");
|
|
1844
|
+
}
|
|
1787
1845
|
setupEventHandlers() {
|
|
1788
1846
|
try {
|
|
1789
1847
|
// Wheel zoom
|
|
@@ -2095,6 +2153,7 @@ class MarkupCanvas {
|
|
|
2095
2153
|
}
|
|
2096
2154
|
// Cleanup method
|
|
2097
2155
|
cleanup() {
|
|
2156
|
+
this.cleanupGlobalBinding();
|
|
2098
2157
|
this.cleanupFunctions.forEach((cleanup) => {
|
|
2099
2158
|
try {
|
|
2100
2159
|
cleanup();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 1.0.
|
|
4
|
+
* @version 1.0.9
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
7
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
@@ -257,7 +257,7 @@
|
|
|
257
257
|
enablePan: true,
|
|
258
258
|
enableTouch: true,
|
|
259
259
|
enableKeyboard: true,
|
|
260
|
-
|
|
260
|
+
bindKeyboardEventsTo: "canvas",
|
|
261
261
|
// Zoom behavior
|
|
262
262
|
zoomSpeed: 1.5,
|
|
263
263
|
minZoom: 0.05,
|
|
@@ -303,6 +303,9 @@
|
|
|
303
303
|
gridColorDark: "rgba(232, 86, 193, 0.5)",
|
|
304
304
|
// Theme
|
|
305
305
|
themeMode: "light",
|
|
306
|
+
// Global Binding & Instance Access
|
|
307
|
+
bindToWindow: false,
|
|
308
|
+
name: "markupCanvas",
|
|
306
309
|
// Callbacks
|
|
307
310
|
onTransformUpdate: () => { },
|
|
308
311
|
};
|
|
@@ -650,6 +653,9 @@
|
|
|
650
653
|
constructor() {
|
|
651
654
|
this.listeners = new Map();
|
|
652
655
|
}
|
|
656
|
+
setEmitInterceptor(interceptor) {
|
|
657
|
+
this.emitInterceptor = interceptor;
|
|
658
|
+
}
|
|
653
659
|
on(event, handler) {
|
|
654
660
|
if (!this.listeners.has(event)) {
|
|
655
661
|
this.listeners.set(event, new Set());
|
|
@@ -663,6 +669,7 @@
|
|
|
663
669
|
}
|
|
664
670
|
}
|
|
665
671
|
emit(event, data) {
|
|
672
|
+
this.emitInterceptor?.(event, data);
|
|
666
673
|
const handlers = this.listeners.get(event);
|
|
667
674
|
if (handlers) {
|
|
668
675
|
handlers.forEach((handler) => {
|
|
@@ -721,7 +728,7 @@
|
|
|
721
728
|
function handleKeyDown(event) {
|
|
722
729
|
if (!(event instanceof KeyboardEvent))
|
|
723
730
|
return;
|
|
724
|
-
if (config.
|
|
731
|
+
if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container)
|
|
725
732
|
return;
|
|
726
733
|
const isFastPan = event.shiftKey;
|
|
727
734
|
const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
|
|
@@ -794,7 +801,7 @@
|
|
|
794
801
|
}
|
|
795
802
|
}
|
|
796
803
|
}
|
|
797
|
-
const keyboardTarget = config.
|
|
804
|
+
const keyboardTarget = config.bindKeyboardEventsTo === "canvas" ? canvas.container : document;
|
|
798
805
|
keyboardTarget.addEventListener("keydown", handleKeyDown);
|
|
799
806
|
canvas.container.addEventListener("mousemove", handleMouseMove);
|
|
800
807
|
return () => {
|
|
@@ -1725,11 +1732,59 @@
|
|
|
1725
1732
|
throw new Error("Failed to create canvas");
|
|
1726
1733
|
}
|
|
1727
1734
|
this.baseCanvas = canvas;
|
|
1735
|
+
if (this.config.bindToWindow) {
|
|
1736
|
+
this.listen.setEmitInterceptor((event, data) => {
|
|
1737
|
+
this.broadcastEvent(event, data);
|
|
1738
|
+
});
|
|
1739
|
+
this.setupGlobalBinding();
|
|
1740
|
+
}
|
|
1728
1741
|
this.setupEventHandlers();
|
|
1729
1742
|
this._isReady = true;
|
|
1730
1743
|
// Emit ready event
|
|
1731
1744
|
this.listen.emit("ready", this);
|
|
1732
1745
|
}
|
|
1746
|
+
setupGlobalBinding() {
|
|
1747
|
+
if (typeof window === "undefined") {
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
const canvasName = this.config.name || "markupCanvas";
|
|
1751
|
+
const windowObj = window;
|
|
1752
|
+
// Bind instance to window
|
|
1753
|
+
windowObj[canvasName] = this;
|
|
1754
|
+
// Track all instances
|
|
1755
|
+
if (!windowObj.__markupCanvasInstances) {
|
|
1756
|
+
windowObj.__markupCanvasInstances = new Map();
|
|
1757
|
+
}
|
|
1758
|
+
windowObj.__markupCanvasInstances.set(canvasName, this);
|
|
1759
|
+
}
|
|
1760
|
+
cleanupGlobalBinding() {
|
|
1761
|
+
if (typeof window === "undefined") {
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1764
|
+
const canvasName = this.config.name || "markupCanvas";
|
|
1765
|
+
const windowObj = window;
|
|
1766
|
+
delete windowObj[canvasName];
|
|
1767
|
+
if (windowObj.__markupCanvasInstances) {
|
|
1768
|
+
windowObj.__markupCanvasInstances.delete(canvasName);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
broadcastEvent(event, data) {
|
|
1772
|
+
if (typeof window === "undefined") {
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1775
|
+
// Receivers can get the instance from the window binding
|
|
1776
|
+
let broadcastData = data;
|
|
1777
|
+
if (event === "ready") {
|
|
1778
|
+
broadcastData = { ready: true };
|
|
1779
|
+
}
|
|
1780
|
+
window.postMessage({
|
|
1781
|
+
source: "markup-canvas",
|
|
1782
|
+
event,
|
|
1783
|
+
data: broadcastData,
|
|
1784
|
+
timestamp: Date.now(),
|
|
1785
|
+
canvasName: this.config.name,
|
|
1786
|
+
}, "*");
|
|
1787
|
+
}
|
|
1733
1788
|
setupEventHandlers() {
|
|
1734
1789
|
try {
|
|
1735
1790
|
// Wheel zoom
|
|
@@ -2041,6 +2096,7 @@
|
|
|
2041
2096
|
}
|
|
2042
2097
|
// Cleanup method
|
|
2043
2098
|
cleanup() {
|
|
2099
|
+
this.cleanupGlobalBinding();
|
|
2044
2100
|
this.cleanupFunctions.forEach((cleanup) => {
|
|
2045
2101
|
try {
|
|
2046
2102
|
cleanup();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).MarkupCanvas=t()}(this,function(){"use strict";const e="canvas-container",t="transform-layer",n="content-layer";function r(e,r){const o=Array.from(e.children);let a=e.querySelector(`.${t}`);a||(a=document.createElement("div"),a.className=t,e.appendChild(a)),function(e,t){e.style.position="absolute";const n=t.rulerSize;e.style.top=`${n}px`,e.style.left=`${n}px`,e.style.width=`${t.width}px`,e.style.height=`${t.height}px`,e.style.transformOrigin="0 0"}(a,r);let s=a.querySelector(`.${n}`);return s||(s=document.createElement("div"),s.className=n,a.appendChild(s),function(e,n,r){e.forEach(e=>{e===r||e.classList.contains(t)||n.appendChild(e)})}(o,s,a)),function(e){e.style.position="relative",e.style.width="100%",e.style.height="100%",e.style.pointerEvents="auto"}(s),{transformLayer:a,contentLayer:s}}function o(e,t,n){if(!n?.inverse)return{x:e,y:t};try{const r=n.inverse(),o=new DOMPoint(e,t).matrixTransform(r);return{x:o.x,y:o.y}}catch(n){return console.warn("Canvas to content conversion failed:",n),{x:e,y:t}}}function a(e,t){return Math.max(t.minZoom,Math.min(t.maxZoom,e))}function s(e,t,n){return new DOMMatrix([e,0,0,e,t,n])}function i(e,t,n,r,o){const s=o.enableRulers?-o.rulerSize:0,i=n||{scale:1,translateX:s,translateY:s},{scale:l,translateX:c,translateY:u}=i,d=a(l*r,o);if(Math.abs(d-l)<.001)return{scale:l,translateX:c,translateY:u};return{scale:d,translateX:e-(e-c)/l*d,translateY:t-(t-u)/l*d}}function l(e,t){return t(t=>a(t,e))}const c=new Map;function u(e,t,n){return e[t]?n():null}function d(e){let t=null,n=null;const r=(...r)=>{n=r,null===t&&(t=requestAnimationFrame(()=>{n&&e(...n),t=null,n=null}))};return r.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null,n=null)},r}function h(e,t,n){return n(null!==e.container.querySelector(".canvas-ruler")?t:0)}function m(e,t,n,r,o){const a=null!==e.container.querySelector(".canvas-ruler");return o(a?t-r:t,a?n-r:n)}function g(e,t){if("dark"===e.themeMode){return e[`${t}Dark`]}return e[t]}const f={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:!1,showRulers:!0,showGrid:!1,rulerFontSize:9,rulerFontFamily:"Monaco, Menlo, monospace",rulerUnits:"px",rulerSize:20,canvasBackgroundColor:"rgba(250, 250, 250, 1)",canvasBackgroundColorDark:"rgba(40, 40, 40, 1)",rulerBackgroundColor:"rgba(255, 255, 255, 0.95)",rulerBorderColor:"rgba(240, 240, 240, 1)",rulerTextColor:"rgba(102, 102, 102, 1)",rulerTickColor:"rgba(204, 204, 204, 1)",gridColor:"rgba(232, 86, 193, 0.5)",rulerBackgroundColorDark:"rgba(30, 30, 30, 0.95)",rulerBorderColorDark:"rgba(68, 68, 68, 1)",rulerTextColorDark:"rgba(170, 170, 170, 1)",rulerTickColorDark:"rgba(104, 104, 104, 1)",gridColorDark:"rgba(232, 86, 193, 0.5)",themeMode:"light",onTransformUpdate:()=>{}};function p(e){try{const t=e.container,n=e.config,r=e.transform||{scale:1,translateX:0,translateY:0},a=t.getBoundingClientRect(),i=a.width||t.clientWidth||0,l=a.height||t.clientHeight||0,c=h({container:t},n.rulerSize,e=>Math.max(0,i-e)),u=h({container:t},n.rulerSize,e=>Math.max(0,l-e)),d=n.width||f.width,m=n.height||f.height,g=function(e,t,n,r,a){const i=o(0,0,s(a.scale,a.translateX,a.translateY)),l=o(e,t,s(a.scale,a.translateX,a.translateY));return{x:Math.max(0,Math.min(n,i.x)),y:Math.max(0,Math.min(r,i.y)),width:Math.max(0,Math.min(n-i.x,l.x-i.x)),height:Math.max(0,Math.min(r-i.y,l.y-i.y))}}(c,u,d,m,r);return{width:c,height:u,contentWidth:d,contentHeight:m,scale:r.scale,translateX:r.translateX,translateY:r.translateY,visibleArea:g,scaledContentWidth:d*r.scale,scaledContentHeight:m*r.scale,canPanLeft:r.translateX<0,canPanRight:r.translateX+d*r.scale>c,canPanUp:r.translateY<0,canPanDown:r.translateY+m*r.scale>u,canZoomIn:r.scale<3.5,canZoomOut:r.scale>.1}}catch(e){return console.error("Failed to calculate canvas bounds:",e),{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 v(e,t){if(!e?.style||!t)return!1;try{return e.style.transform=function(e){return`matrix3d(${e.m11}, ${e.m12}, ${e.m13}, ${e.m14}, ${e.m21}, ${e.m22}, ${e.m23}, ${e.m24}, ${e.m31}, ${e.m32}, ${e.m33}, ${e.m34}, ${e.m41}, ${e.m42}, ${e.m43}, ${e.m44})`}(t),!0}catch(e){return console.warn("Transform application failed:",e),!1}}function y(e,t){try{if(t.enableTransition){window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0);return function(e,t,n){const r=c.get(e);r&&clearTimeout(r);const o=window.setTimeout(()=>{n(),c.delete(e)},t);c.set(e,o)}("disableTransition",1e3*(t.transitionDuration??.2),()=>{e.style.transition="none",window.__markupCanvasTransitionTimeout=void 0}),!0}return!1}catch(e){return console.error("Failed to disable transitions:",e),!0}}function b(e,t,n){!function(e,t){try{return!!t.enableTransition&&(window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0),e.style.transition=`transform ${t.transitionDuration}s linear`,!0)}catch(e){return console.error("Failed to enable transitions:",e),!1}}(e,t);try{return n()}finally{y(e,t)}}function w(t,n){if("static"===getComputedStyle(t).position&&(t.style.position="relative"),t.style.overflow="hidden",t.style.cursor="grab",t.style.overscrollBehavior="none",n){const e=g(n,"canvasBackgroundColor");t.style.backgroundColor=e}t.hasAttribute("tabindex")||t.setAttribute("tabindex","0"),function(e){const t=e.getBoundingClientRect(),n=getComputedStyle(e);0===t.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.",e),0===t.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.",e)}(t),t.classList.contains(e)||t.classList.add(e)}function x(e,t){if(!e?.appendChild)return console.error("Invalid container element provided to createCanvas"),null;try{w(e,t);const{transformLayer:n,contentLayer:a}=r(e,t);t.enableAcceleration&&function(e){try{return e.style.transform=e.style.transform||"translateZ(0)",e.style.backfaceVisibility="hidden",!0}catch(e){return console.error("Failed to enable hardware acceleration:",e),!1}}(n);const c=t.enableRulers?-t.rulerSize:0,d={scale:1,translateX:c,translateY:c};v(n,s(d.scale,d.translateX,d.translateY));return{container:e,transformLayer:n,contentLayer:a,config:t,transform:d,getBounds:function(){return p(this)},updateTransform:function(e){this.transform={...this.transform,...e};const t=s(this.transform.scale,this.transform.translateX,this.transform.translateY),n=v(this.transformLayer,t);return u(this.config,"onTransformUpdate",()=>{this.config.onTransformUpdate(this.transform)}),n},reset:function(){return this.updateTransform({scale:1,translateX:0,translateY:0})},handleResize:function(){return!0},setZoom:function(e){const t=l(this.config,t=>t(e));return this.updateTransform({scale:t})},canvasToContent:function(e,t){return o(e,t,s(this.transform.scale,this.transform.translateX,this.transform.translateY))},zoomToPoint:function(e,t,n){return b(this.transformLayer,this.config,()=>{const r=i(e,t,this.transform,n/this.transform.scale,this.config);return this.updateTransform(r)})},resetView:function(){return b(this.transformLayer,this.config,()=>h(this,this.config.rulerSize,e=>{const t={scale:1,translateX:-1*e,translateY:-1*e};return this.updateTransform(t)}))},zoomToFitContent:function(){return b(this.transformLayer,this.config,()=>{const e=this.getBounds(),t=e.width/this.config.width,n=e.height/this.config.height,r=l(this.config,e=>e(.9*Math.min(t,n))),o=this.config.width*r,a=this.config.height*r,s=(e.width-o)/2,i=(e.height-a)/2;return this.updateTransform({scale:r,translateX:s,translateY:i})})}}}catch(e){return console.error("Failed to create canvas:",e),null}}function C(e={}){const t={...f,...e};return("number"!=typeof t.width||t.width<=0)&&(console.warn("Invalid width, using default"),t.width=f.width),("number"!=typeof t.height||t.height<=0)&&(console.warn("Invalid height, using default"),t.height=f.height),("number"!=typeof t.zoomSpeed||t.zoomSpeed<=0)&&(console.warn("Invalid zoomSpeed, using default"),t.zoomSpeed=f.zoomSpeed),("number"!=typeof t.minZoom||t.minZoom<=0)&&(console.warn("Invalid minZoom, using default"),t.minZoom=f.minZoom),("number"!=typeof t.maxZoom||t.maxZoom<=t.minZoom)&&(console.warn("Invalid maxZoom, using default"),t.maxZoom=f.maxZoom),("number"!=typeof t.keyboardPanStep||t.keyboardPanStep<=0)&&(console.warn("Invalid keyboardPanStep, using default"),t.keyboardPanStep=f.keyboardPanStep),("number"!=typeof t.keyboardFastMultiplier||t.keyboardFastMultiplier<=0)&&(console.warn("Invalid keyboardFastMultiplier, using default"),t.keyboardFastMultiplier=f.keyboardFastMultiplier),("number"!=typeof t.clickZoomLevel||t.clickZoomLevel<=0)&&(console.warn("Invalid clickZoomLevel, using default"),t.clickZoomLevel=f.clickZoomLevel),("number"!=typeof t.rulerFontSize||t.rulerFontSize<=0)&&(console.warn("Invalid rulerFontSize, using default"),t.rulerFontSize=f.rulerFontSize),("number"!=typeof t.rulerSize||t.rulerSize<=0)&&(console.warn("Invalid rulerSize, using default"),t.rulerSize=f.rulerSize),t}class k{constructor(){this.listeners=new Map}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t)}off(e,t){const n=this.listeners.get(e);n&&n.delete(t)}emit(e,t){const n=this.listeners.get(e);n&&n.forEach(n=>{try{n(t)}catch(t){console.error(`Error in event handler for "${String(e)}":`,t)}})}removeAllListeners(){this.listeners.clear()}}const T=300,S=5;function D(e,t){if(!e?.getBounds)return t;try{const n=e.getBounds(),r=n.width*n.height;return t*(r/2073600)**1}catch(e){return console.warn("Failed to calculate adaptive zoom speed, using base speed:",e),t}}function z(e,t){let n=0,r=0;function o(o){const a=e.container.getBoundingClientRect(),s=o.clientX-a.left,i=o.clientY-a.top;!function(e,t,n,r,o){h(e,t,e=>o(n-e,r-e))}(e,t.rulerSize,s,i,(e,t)=>{n=e,r=t})}function s(o){if(!(o instanceof KeyboardEvent))return;if(t.limitKeyboardEventsToCanvas&&document.activeElement!==e.container)return;const s=o.shiftKey,l=t.keyboardPanStep*(s?t.keyboardFastMultiplier:1);let c=!1;const u={};switch(o.key){case"ArrowLeft":u.translateX=e.transform.translateX+l,c=!0;break;case"ArrowRight":u.translateX=e.transform.translateX-l,c=!0;break;case"ArrowUp":u.translateY=e.transform.translateY+l,c=!0;break;case"ArrowDown":u.translateY=e.transform.translateY-l,c=!0;break;case"=":case"+":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;u.scale=a(e.transform.scale*(1+n),t),c=!0}break;case"-":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;u.scale=a(e.transform.scale*(1-n),t),c=!0}break;case"0":if(o.metaKey||o.ctrlKey){const o=1/e.transform.scale,a=i(n,r,e.transform,o,t);Object.assign(u,a),c=!0}break;case"g":case"G":e.toggleGrid&&e.toggleGrid(),c=!0;break;case"r":case"R":o.metaKey||o.ctrlKey||o.altKey||!e.toggleRulers||(e.toggleRulers(),c=!0)}c&&(o.preventDefault(),Object.keys(u).length>0&&e.updateTransform(u))}const l=t.limitKeyboardEventsToCanvas?e.container:document;return l.addEventListener("keydown",s),e.container.addEventListener("mousemove",o),()=>{l.removeEventListener("keydown",s),e.container.removeEventListener("mousemove",o)}}function M(e,t,n,r,o){n?t.requireSpaceForMouseDrag?e.container.style.cursor=r?"grab":"default":e.container.style.cursor=o?"grabbing":"grab":e.container.style.cursor="default"}function L(e,t,n,r,o){o.setIsDragging(!1),o.setDragButton(-1),M(e,t,n,r,!1)}function R(e,t,n,r,o,a,s,i,l,c){a&&e.button===s&&L(t,n,r,o,{setIsDragging:c.setIsDragging,setDragButton:c.setDragButton}),r&&0===e.button&&n.enableClickToZoom&&i>0&&function(e,t,n,r,o,a){const s=Date.now()-r,i=e.altKey,l=!n.requireOptionForClickZoom||i;if(s<T&&!o&&!a&&l){e.preventDefault();const r=t.container.getBoundingClientRect(),o=e.clientX-r.left,a=e.clientY-r.top,{clickX:s,clickY:i}=m(t,o,a,n.rulerSize,(e,t)=>({clickX:e,clickY:t})),l=t.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};b(t.transformLayer,t.config,()=>{t.updateTransform(h)})}}(e,t,n,i,l,a),0===e.button&&function(e){e.setMouseDownTime(0),e.setHasDragged(!1)}({setMouseDownTime:c.setMouseDownTime,setHasDragged:c.setHasDragged})}function Y(e,t,n=!0){let r=!0,o=!1,a=0,s=0,i=-1,l=!1,c=0,u=0,h=0,m=!1;const g={setIsDragging:e=>{o=e},setDragButton:e=>{i=e},setIsSpacePressed:e=>{l=e},setMouseDownTime:e=>{c=e},setMouseDownX:e=>{u=e},setMouseDownY:e=>{h=e},setHasDragged:e=>{m=e},setLastMouseX:e=>{a=e},setLastMouseY:e=>{s=e}},f=n=>{!function(e,t,n,r,o,a){n.requireSpaceForMouseDrag&&" "===e.key&&(a.setIsSpacePressed(!0),M(t,n,r,!0,o))}(n,e,t,r,o,{setIsSpacePressed:g.setIsSpacePressed})},p=n=>{!function(e,t,n,r,o,a){n.requireSpaceForMouseDrag&&" "===e.key&&(a.setIsSpacePressed(!1),M(t,n,r,!1,o),o&&L(t,n,r,!1,{setIsDragging:a.setIsDragging,setDragButton:a.setDragButton}))}(n,e,t,r,o,{setIsSpacePressed:g.setIsSpacePressed,setIsDragging:g.setIsDragging,setDragButton:g.setDragButton})},v=n=>{!function(e,t,n,r,o,a){const s=0===e.button,i=1===e.button;if(s&&(a.setMouseDownTime(Date.now()),a.setMouseDownX(e.clientX),a.setMouseDownY(e.clientY),a.setHasDragged(!1)),!r)return;(!n.requireSpaceForMouseDrag||o)&&(s&&n.enableLeftDrag||i&&n.enableMiddleDrag)&&(e.preventDefault(),a.setDragButton(e.button),a.setLastMouseX(e.clientX),a.setLastMouseY(e.clientY),M(t,n,r,o,!1))}(n,e,t,r,l,g)},y=t=>{!function(e,t,n,r,o,a,s,i,l,c){if(o>0){const t=Math.abs(e.clientX-a),o=Math.abs(e.clientY-s);(t>S||o>S)&&(c.setHasDragged(!0),!r&&n&&c.setIsDragging(!0))}if(!r||!n)return;e.preventDefault(),d((...e)=>{const o=e[0];if(!r||!n)return;const a=o.clientX-i,s=o.clientY-l,u={translateX:t.transform.translateX+a,translateY:t.transform.translateY+s};t.updateTransform(u),c.setLastMouseX(o.clientX),c.setLastMouseY(o.clientY)})(e)}(t,e,r,o,c,u,h,a,s,{setHasDragged:g.setHasDragged,setIsDragging:g.setIsDragging,setLastMouseX:g.setLastMouseX,setLastMouseY:g.setLastMouseY})},b=n=>{R(n,e,t,r,l,o,i,c,m,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton,setMouseDownTime:g.setMouseDownTime,setHasDragged:g.setHasDragged})},w=()=>{!function(e,t,n,r,o,a){o&&L(e,t,n,r,a)}(e,t,r,l,o,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton})};e.container.addEventListener("mousedown",v),document.addEventListener("mousemove",y),document.addEventListener("mouseup",b),e.container.addEventListener("mouseleave",w),t.requireSpaceForMouseDrag&&(document.addEventListener("keydown",f),document.addEventListener("keyup",p)),M(e,t,r,l,o);const x=()=>{e.container.removeEventListener("mousedown",v),document.removeEventListener("mousemove",y),document.removeEventListener("mouseup",b),e.container.removeEventListener("mouseleave",w),t.requireSpaceForMouseDrag&&(document.removeEventListener("keydown",f),document.removeEventListener("keyup",p))};return n?{cleanup:x,enable:()=>(r=!0,M(e,t,r,l,o),!0),disable:()=>(r=!1,o&&L(e,t,r,l,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton}),M(e,t,r,l,o),!0),isEnabled:()=>r}:x}function E(e,t){return{x:(e.clientX+t.clientX)/2,y:(e.clientY+t.clientY)/2}}function X(e,t){const n=e.clientX-t.clientX,r=e.clientY-t.clientY;return Math.sqrt(n*n+r*r)}function F(e,t,n,r){const o=i(n,r,e.transform,t,e.config);return e.updateTransform(o)}function B(e,t,n){e.preventDefault();const r=Array.from(e.touches);d((...e)=>{const r=e[0];if(1===r.length){if(1===n.touches.length){const e=r[0].clientX-n.touches[0].clientX,o=r[0].clientY-n.touches[0].clientY,a={translateX:t.transform.translateX+e,translateY:t.transform.translateY+o};t.updateTransform(a)}}else if(2===r.length){const e=X(r[0],r[1]),o=E(r[0],r[1]);if(n.lastDistance>0){const r=e/n.lastDistance,a=t.container.getBoundingClientRect();let s=o.x-a.left,i=o.y-a.top;const l=function(e,t,n,r){return h(e,t,e=>{const t={...n,x:n.x-e,y:n.y-e};return r(t)})}(t,t.config.rulerSize,{x:s,y:i},e=>e);s=l.x,i=l.y,F(t,r,s,i)}n.lastDistance=e,n.lastCenter=o}n.touches=r})(r)}function $(e){const t={touches:[],lastDistance:0,lastCenter:{}},n=e=>{!function(e,t){e.preventDefault(),t.touches=Array.from(e.touches),2===t.touches.length&&(t.lastDistance=X(t.touches[0],t.touches[1]),t.lastCenter=E(t.touches[0],t.touches[1]))}(e,t)},r=n=>{B(n,e,t)},o=e=>{!function(e,t){t.touches=Array.from(e.touches),t.touches.length<2&&(t.lastDistance=0)}(e,t)};return e.container.addEventListener("touchstart",n,{passive:!1}),e.container.addEventListener("touchmove",r,{passive:!1}),e.container.addEventListener("touchend",o,{passive:!1}),()=>{e.container.removeEventListener("touchstart",n),e.container.removeEventListener("touchmove",r),e.container.removeEventListener("touchend",o)}}function P(e){const t=e.ctrlKey||e.metaKey,n=[0===e.deltaMode,Math.abs(e.deltaY)<50,e.deltaY%1!=0,Math.abs(e.deltaX)>0&&Math.abs(e.deltaY)>0].filter(Boolean).length>=2;return{isTrackpad:n,isMouseWheel:!n,isTrackpadScroll:n&&!t,isTrackpadPinch:n&&t,isZoomGesture:t}}function Z(e,t){const n=(e=>d((...t)=>{const n=t[0];if(!n||!e?.updateTransform)return!1;try{const t=e.transform,r=1,o=n.deltaX*r,a=n.deltaY*r,s={scale:t.scale,translateX:t.translateX-o,translateY:t.translateY-a};return y(e.transformLayer,e.config),e.updateTransform(s)}catch(e){return console.error("Error handling trackpad pan:",e),!1}}))(e),r=r=>P(r).isTrackpadScroll?n(r):function(e,t,n){if(!e||"number"!=typeof e.deltaY)return console.warn("Invalid wheel event provided"),!1;if(!t?.updateTransform)return console.warn("Invalid canvas provided to handleWheelEvent"),!1;try{e.preventDefault();const r=t.container.getBoundingClientRect(),o=e.clientX-r.left,a=e.clientY-r.top,{mouseX:s,mouseY:i}=m(t,o,a,n.rulerSize,(e,t)=>({mouseX:e,mouseY:t})),l=n.zoomSpeed,c=P(e);if(!c.isZoomGesture)return!1;let u=n.enableAdaptiveSpeed?D(t,l):l;if(c.isTrackpadPinch){const e=.05*n.zoomSpeed;u=n.enableAdaptiveSpeed?D(t,e):e}return F(t,(e.deltaY<0?1:-1)>0?1+u:1/(1+u),s,i)}catch(e){return console.error("Error handling wheel event:",e),!1}}(r,e,t);return e.container.addEventListener("wheel",r,{passive:!1}),()=>{e.container.removeEventListener("wheel",r)}}const I=100,O=1e3,A=1001,_=4,H=4,q=100,G=100,N=20,K=200;function V(e,t){const n=function(e){const t=document.createElement("div");return t.className="canvas-ruler horizontal-ruler",t.style.cssText=`\n\tposition: absolute;\n\ttop: 0;\n\tleft: ${e.rulerSize}px;\n\tright: 0;\n\theight: ${e.rulerSize}px;\n\tbackground: var(--ruler-background-color);\n\tborder-bottom: 1px solid var(--ruler-border-color);\n\tborder-right: 1px solid var(--ruler-border-color);\n\tz-index: ${O};\n\tpointer-events: none;\n\tfont-family: ${e.rulerFontFamily};\n\tfont-size: ${e.rulerFontSize}px;\n\tcolor: var(--ruler-text-color);\n\toverflow: hidden;\n `,t}(t),r=function(e){const t=document.createElement("div");return t.className="canvas-ruler vertical-ruler",t.style.cssText=`\n\tposition: absolute;\n\ttop: ${e.rulerSize}px;\n\tleft: 0;\n\tbottom: 0;\n\twidth: ${e.rulerSize}px;\n\tbackground: var(--ruler-background-color);\n\tborder-right: 1px solid var(--ruler-border-color);\n\tborder-bottom: 1px solid var(--ruler-border-color);\n\tz-index: ${O};\n\tpointer-events: none;\n\tfont-family: ${e.rulerFontFamily};\n\tfont-size: ${e.rulerFontSize}px;\n\tcolor: var(--ruler-text-color);\n\toverflow: hidden;\n `,t}(t),o=function(e){const t=document.createElement("div");return t.className="canvas-ruler corner-box",t.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tleft: 0;\n\t\twidth: ${e.rulerSize}px;\n\t\theight: ${e.rulerSize}px;\n\t\tbackground: var(--ruler-background-color);\n\t\tborder-right: 1px solid var(--ruler-border-color);\n\t\tborder-bottom: 1px solid var(--ruler-border-color);\n\t\tz-index: ${A};\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tfont-family: ${e.rulerFontFamily};\n\t\tfont-size: ${e.rulerFontSize-2}px;\n\t\tcolor: var(--ruler-text-color);\n\t\tpointer-events: none;\n\t`,t.textContent=e.rulerUnits,t}(t),a=t.enableGrid?function(e){const t=document.createElement("div");return t.className="canvas-ruler grid-overlay",t.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${e.rulerSize}px;\n\t\tleft: ${e.rulerSize}px;\n\t\tright: 0;\n\t\tbottom: 0;\n\t\tpointer-events: none;\n\t\tz-index: ${I};\n\t\tbackground-image: \n\t\t\tlinear-gradient(var(--grid-color) 1px, transparent 1px),\n\t\t\tlinear-gradient(90deg, var(--grid-color) 1px, transparent 1px);\n\t\tbackground-size: 100px 100px;\n\t\topacity: 0.5;\n\t`,t}(t):void 0;return e.appendChild(n),e.appendChild(r),e.appendChild(o),a&&e.appendChild(a),{horizontalRuler:n,verticalRuler:r,cornerBox:o,gridOverlay:a}}function U(e,t){const n=e/Math.max(5,Math.min(20,t/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 W(e,t,n,r,o){const a=document.createElement("div");a.className="tick",a.style.cssText=`\n\t\tposition: absolute;\n\t\tleft: ${n}px;\n\t\tbottom: 0;\n\t\twidth: 1px;\n\t\theight: ${_}px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(a);if(t%q===0){const r=document.createElement("div");r.style.cssText=`\n\t\t\tposition: absolute;\n\t\t\tleft: ${n}px;\n\t\t\tbottom: ${_+2}px;\n\t\t\tfont-size: ${o.rulerFontSize}px;\n\t\t\tline-height: 1;\n\t\t\tcolor: var(--ruler-text-color);\n\t\t\twhite-space: nowrap;\n\t\t\tpointer-events: none;\n\t\t`,r.textContent=Math.round(t).toString(),e.appendChild(r)}}function j(e,t,n,r,o){const a=document.createElement("div");a.className="tick",a.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${n}px;\n\t\tright: 0;\n\t\twidth: ${H}px;\n\t\theight: 1px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(a);if(t%q===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: ${H+6}px;\n\t\t\tfont-size: ${o.rulerFontSize}px;\n\t\t\tline-height: 1;\n\t\t\tcolor: var(--ruler-text-color);\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(t).toString(),e.appendChild(r)}}function J(e,t,n,r,o){const a=e.getBounds(),s=a.scale||1,i=a.translateX||0,l=a.translateY||0,c=a.width-o.rulerSize,u=a.height-o.rulerSize,d=-i/s,h=-l/s,m=h+u/s;!function(e,t,n,r,o,a){const s=r,i=U(n-t,s),l=document.createDocumentFragment(),c=Math.floor(t/i)*i,u=Math.ceil(n/i)*i;for(let e=c;e<=u;e+=i){const n=(e-t)*o;n>=-50&&n<=s+50&&W(l,e,n,0,a)}e.innerHTML="",e.appendChild(l)}(t,d,d+c/s,c,s,o),function(e,t,n,r,o,a){const s=r,i=U(n-t,s),l=document.createDocumentFragment(),c=Math.floor(t/i)*i,u=Math.ceil(n/i)*i;for(let e=c;e<=u;e+=i){const n=(e-t)*o;n>=-50&&n<=s+50&&j(l,e,n,0,a)}e.innerHTML="",e.appendChild(l)}(n,h,m,u,s,o),r&&function(e,t,n,r){let o=G*t;for(;o<N;)o*=2;for(;o>K;)o/=2;e.style.backgroundSize=`${o}px ${o}px`,e.style.backgroundPosition=`${n%o}px ${r%o}px`}(r,s,i,l)}function Q(e,t){const n=g(t,"rulerBackgroundColor"),r=g(t,"rulerBorderColor"),o=g(t,"rulerTextColor"),a=g(t,"rulerTickColor"),s=g(t,"gridColor");e.horizontalRuler&&(e.horizontalRuler.style.setProperty("--ruler-background-color",n),e.horizontalRuler.style.setProperty("--ruler-border-color",r),e.horizontalRuler.style.setProperty("--ruler-text-color",o),e.horizontalRuler.style.setProperty("--ruler-tick-color",a)),e.verticalRuler&&(e.verticalRuler.style.setProperty("--ruler-background-color",n),e.verticalRuler.style.setProperty("--ruler-border-color",r),e.verticalRuler.style.setProperty("--ruler-text-color",o),e.verticalRuler.style.setProperty("--ruler-tick-color",a)),e.cornerBox&&(e.cornerBox.style.setProperty("--ruler-background-color",n),e.cornerBox.style.setProperty("--ruler-border-color",r),e.cornerBox.style.setProperty("--ruler-text-color",o)),e.gridOverlay&&e.gridOverlay.style.setProperty("--grid-color",s)}function ee(e,t){if(!e?.container)return console.error("Invalid canvas provided to createRulers"),null;let n,r=null,o=!1;const a=()=>{!o&&n.horizontalRuler&&n.verticalRuler&&J(e,n.horizontalRuler,n.verticalRuler,n.gridOverlay,t)};try{return n=V(e.container,t),r=function(e,t){const n=d(t),r=e.updateTransform;e.updateTransform=function(e){const t=r.call(this,e);return n(),t};const o=d(t);return window.addEventListener("resize",o),()=>{window.removeEventListener("resize",o),e.updateTransform=r,n.cleanup(),o.cleanup()}}(e,a),Q(n,t),a(),t.showRulers||(n.horizontalRuler.style.display="none",n.verticalRuler.style.display="none",n.cornerBox.style.display="none"),!t.showGrid&&n.gridOverlay&&(n.gridOverlay.style.display="none"),{horizontalRuler:n.horizontalRuler,verticalRuler:n.verticalRuler,cornerBox:n.cornerBox,gridOverlay:n.gridOverlay,update:a,updateTheme:e=>{o||Q(n,e)},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 e="none"!==n.gridOverlay.style.display;n.gridOverlay.style.display=e?"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(e){return console.error("Failed to create rulers:",e),null}}return class{constructor(e,t={}){if(this.cleanupFunctions=[],this.rulers=null,this.dragSetup=null,this._isReady=!1,this.listen=new k,!e)throw new Error("Container element is required");this.config=C(t);const n=x(e,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{u(this.config,"enableZoom",()=>{const e=Z(this,this.config);this.cleanupFunctions.push(e)}),(this.config.enablePan||this.config.enableClickToZoom)&&(this.dragSetup=Y(this,this.config,!0),this.cleanupFunctions.push(this.dragSetup.cleanup)),u(this.config,"enableKeyboard",()=>{const e=z(this,this.config);this.cleanupFunctions.push(e)}),u(this.config,"enableTouch",()=>{const e=$(this);this.cleanupFunctions.push(e)}),u(this.config,"enableRulers",()=>{this.rulers=ee(this.baseCanvas,this.config),this.cleanupFunctions.push(()=>{this.rulers&&this.rulers.destroy()})})}catch(e){throw console.error("Failed to set up event handlers:",e),this.cleanup(),e}}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(e){const t=this.baseCanvas.updateTransform(e);return t&&this.emitTransformEvents(),t}emitTransformEvents(){const e=this.baseCanvas.transform;this.listen.emit("transform",e),this.listen.emit("zoom",e.scale),this.listen.emit("pan",{x:e.translateX,y:e.translateY})}reset(){return this.baseCanvas.reset()}handleResize(){return this.baseCanvas.handleResize()}setZoom(e){return this.baseCanvas.setZoom(e)}canvasToContent(e,t){return this.baseCanvas.canvasToContent(e,t)}zoomToPoint(e,t,n){return b(this.transformLayer,this.config,()=>{const r=this.baseCanvas.zoomToPoint(e,t,n);return r&&this.emitTransformEvents(),r})}resetView(){return b(this.transformLayer,this.config,()=>{const e=!!this.baseCanvas.resetView&&this.baseCanvas.resetView();return e&&this.emitTransformEvents(),e})}zoomToFitContent(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.zoomToFitContent();return e&&this.emitTransformEvents(),e})}panLeft(e){const t=e??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX+t};return this.updateTransform(n)}panRight(e){const t=e??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX-t};return this.updateTransform(n)}panUp(e){const t=e??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY+t};return this.updateTransform(n)}panDown(e){const t=e??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY-t};return this.updateTransform(n)}zoomIn(e=.1){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(this.baseCanvas.transform.scale*(1+e))};return this.updateTransform(n)}))}zoomOut(e=.1){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(this.baseCanvas.transform.scale*(1-e))};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 b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.getBounds(),t=(e.width-e.contentWidth*this.baseCanvas.transform.scale)/2,n=(e.height-e.contentHeight*this.baseCanvas.transform.scale)/2;return this.updateTransform({translateX:t,translateY:n})})}fitToScreen(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.zoomToFitContent();return e&&this.emitTransformEvents(),e})}getVisibleArea(){return this.baseCanvas.getBounds().visibleArea}isPointVisible(e,t){const n=this.getVisibleArea();return e>=n.x&&e<=n.x+n.width&&t>=n.y&&t<=n.y+n.height}scrollToPoint(e,t){return b(this.transformLayer,this.config,()=>{const n=this.baseCanvas.getBounds(),r=n.width/2,o=n.height/2,a=r-e*this.baseCanvas.transform.scale,s=o-t*this.baseCanvas.transform.scale;return this.updateTransform({translateX:a,translateY:s})})}getConfig(){return{...this.config}}updateConfig(e){this.config=C({...this.config,...e})}updateThemeMode(e){const t={...this.config,themeMode:e};this.config=C(t);const n=g(this.config,"canvasBackgroundColor");this.baseCanvas.container.style.backgroundColor=n,this.rulers&&this.rulers.updateTheme(this.config)}cleanup(){this.cleanupFunctions.forEach(e=>{try{e()}catch(e){console.warn("Error during cleanup:",e)}}),this.cleanupFunctions=[],this.removeAllListeners()}on(e,t){this.listen.on(e,t)}off(e,t){this.listen.off(e,t)}emit(e,t){this.listen.emit(e,t)}removeAllListeners(){this.listen.removeAllListeners()}destroy(){this.cleanup(),window.__markupCanvasTransitionTimeout&&clearTimeout(window.__markupCanvasTransitionTimeout)}}});
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).MarkupCanvas=t()}(this,function(){"use strict";const e="canvas-container",t="transform-layer",n="content-layer";function r(e,r){const o=Array.from(e.children);let a=e.querySelector(`.${t}`);a||(a=document.createElement("div"),a.className=t,e.appendChild(a)),function(e,t){e.style.position="absolute";const n=t.rulerSize;e.style.top=`${n}px`,e.style.left=`${n}px`,e.style.width=`${t.width}px`,e.style.height=`${t.height}px`,e.style.transformOrigin="0 0"}(a,r);let s=a.querySelector(`.${n}`);return s||(s=document.createElement("div"),s.className=n,a.appendChild(s),function(e,n,r){e.forEach(e=>{e===r||e.classList.contains(t)||n.appendChild(e)})}(o,s,a)),function(e){e.style.position="relative",e.style.width="100%",e.style.height="100%",e.style.pointerEvents="auto"}(s),{transformLayer:a,contentLayer:s}}function o(e,t,n){if(!n?.inverse)return{x:e,y:t};try{const r=n.inverse(),o=new DOMPoint(e,t).matrixTransform(r);return{x:o.x,y:o.y}}catch(n){return console.warn("Canvas to content conversion failed:",n),{x:e,y:t}}}function a(e,t){return Math.max(t.minZoom,Math.min(t.maxZoom,e))}function s(e,t,n){return new DOMMatrix([e,0,0,e,t,n])}function i(e,t,n,r,o){const s=o.enableRulers?-o.rulerSize:0,i=n||{scale:1,translateX:s,translateY:s},{scale:l,translateX:c,translateY:u}=i,d=a(l*r,o);if(Math.abs(d-l)<.001)return{scale:l,translateX:c,translateY:u};return{scale:d,translateX:e-(e-c)/l*d,translateY:t-(t-u)/l*d}}function l(e,t){return t(t=>a(t,e))}const c=new Map;function u(e,t,n){return e[t]?n():null}function d(e){let t=null,n=null;const r=(...r)=>{n=r,null===t&&(t=requestAnimationFrame(()=>{n&&e(...n),t=null,n=null}))};return r.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null,n=null)},r}function h(e,t,n){return n(null!==e.container.querySelector(".canvas-ruler")?t:0)}function m(e,t,n,r,o){const a=null!==e.container.querySelector(".canvas-ruler");return o(a?t-r:t,a?n-r:n)}function g(e,t){if("dark"===e.themeMode){return e[`${t}Dark`]}return e[t]}const f={width:8e3,height:8e3,enableAcceleration:!0,enableZoom:!0,enablePan:!0,enableTouch:!0,enableKeyboard:!0,bindKeyboardEventsTo:"canvas",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:!1,showRulers:!0,showGrid:!1,rulerFontSize:9,rulerFontFamily:"Monaco, Menlo, monospace",rulerUnits:"px",rulerSize:20,canvasBackgroundColor:"rgba(250, 250, 250, 1)",canvasBackgroundColorDark:"rgba(40, 40, 40, 1)",rulerBackgroundColor:"rgba(255, 255, 255, 0.95)",rulerBorderColor:"rgba(240, 240, 240, 1)",rulerTextColor:"rgba(102, 102, 102, 1)",rulerTickColor:"rgba(204, 204, 204, 1)",gridColor:"rgba(232, 86, 193, 0.5)",rulerBackgroundColorDark:"rgba(30, 30, 30, 0.95)",rulerBorderColorDark:"rgba(68, 68, 68, 1)",rulerTextColorDark:"rgba(170, 170, 170, 1)",rulerTickColorDark:"rgba(104, 104, 104, 1)",gridColorDark:"rgba(232, 86, 193, 0.5)",themeMode:"light",bindToWindow:!1,name:"markupCanvas",onTransformUpdate:()=>{}};function p(e){try{const t=e.container,n=e.config,r=e.transform||{scale:1,translateX:0,translateY:0},a=t.getBoundingClientRect(),i=a.width||t.clientWidth||0,l=a.height||t.clientHeight||0,c=h({container:t},n.rulerSize,e=>Math.max(0,i-e)),u=h({container:t},n.rulerSize,e=>Math.max(0,l-e)),d=n.width||f.width,m=n.height||f.height,g=function(e,t,n,r,a){const i=o(0,0,s(a.scale,a.translateX,a.translateY)),l=o(e,t,s(a.scale,a.translateX,a.translateY));return{x:Math.max(0,Math.min(n,i.x)),y:Math.max(0,Math.min(r,i.y)),width:Math.max(0,Math.min(n-i.x,l.x-i.x)),height:Math.max(0,Math.min(r-i.y,l.y-i.y))}}(c,u,d,m,r);return{width:c,height:u,contentWidth:d,contentHeight:m,scale:r.scale,translateX:r.translateX,translateY:r.translateY,visibleArea:g,scaledContentWidth:d*r.scale,scaledContentHeight:m*r.scale,canPanLeft:r.translateX<0,canPanRight:r.translateX+d*r.scale>c,canPanUp:r.translateY<0,canPanDown:r.translateY+m*r.scale>u,canZoomIn:r.scale<3.5,canZoomOut:r.scale>.1}}catch(e){return console.error("Failed to calculate canvas bounds:",e),{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 v(e,t){if(!e?.style||!t)return!1;try{return e.style.transform=function(e){return`matrix3d(${e.m11}, ${e.m12}, ${e.m13}, ${e.m14}, ${e.m21}, ${e.m22}, ${e.m23}, ${e.m24}, ${e.m31}, ${e.m32}, ${e.m33}, ${e.m34}, ${e.m41}, ${e.m42}, ${e.m43}, ${e.m44})`}(t),!0}catch(e){return console.warn("Transform application failed:",e),!1}}function y(e,t){try{if(t.enableTransition){window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0);return function(e,t,n){const r=c.get(e);r&&clearTimeout(r);const o=window.setTimeout(()=>{n(),c.delete(e)},t);c.set(e,o)}("disableTransition",1e3*(t.transitionDuration??.2),()=>{e.style.transition="none",window.__markupCanvasTransitionTimeout=void 0}),!0}return!1}catch(e){return console.error("Failed to disable transitions:",e),!0}}function b(e,t,n){!function(e,t){try{return!!t.enableTransition&&(window.__markupCanvasTransitionTimeout&&(clearTimeout(window.__markupCanvasTransitionTimeout),window.__markupCanvasTransitionTimeout=void 0),e.style.transition=`transform ${t.transitionDuration}s linear`,!0)}catch(e){return console.error("Failed to enable transitions:",e),!1}}(e,t);try{return n()}finally{y(e,t)}}function w(t,n){if("static"===getComputedStyle(t).position&&(t.style.position="relative"),t.style.overflow="hidden",t.style.cursor="grab",t.style.overscrollBehavior="none",n){const e=g(n,"canvasBackgroundColor");t.style.backgroundColor=e}t.hasAttribute("tabindex")||t.setAttribute("tabindex","0"),function(e){const t=e.getBoundingClientRect(),n=getComputedStyle(e);0===t.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.",e),0===t.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.",e)}(t),t.classList.contains(e)||t.classList.add(e)}function x(e,t){if(!e?.appendChild)return console.error("Invalid container element provided to createCanvas"),null;try{w(e,t);const{transformLayer:n,contentLayer:a}=r(e,t);t.enableAcceleration&&function(e){try{return e.style.transform=e.style.transform||"translateZ(0)",e.style.backfaceVisibility="hidden",!0}catch(e){return console.error("Failed to enable hardware acceleration:",e),!1}}(n);const c=t.enableRulers?-t.rulerSize:0,d={scale:1,translateX:c,translateY:c};v(n,s(d.scale,d.translateX,d.translateY));return{container:e,transformLayer:n,contentLayer:a,config:t,transform:d,getBounds:function(){return p(this)},updateTransform:function(e){this.transform={...this.transform,...e};const t=s(this.transform.scale,this.transform.translateX,this.transform.translateY),n=v(this.transformLayer,t);return u(this.config,"onTransformUpdate",()=>{this.config.onTransformUpdate(this.transform)}),n},reset:function(){return this.updateTransform({scale:1,translateX:0,translateY:0})},handleResize:function(){return!0},setZoom:function(e){const t=l(this.config,t=>t(e));return this.updateTransform({scale:t})},canvasToContent:function(e,t){return o(e,t,s(this.transform.scale,this.transform.translateX,this.transform.translateY))},zoomToPoint:function(e,t,n){return b(this.transformLayer,this.config,()=>{const r=i(e,t,this.transform,n/this.transform.scale,this.config);return this.updateTransform(r)})},resetView:function(){return b(this.transformLayer,this.config,()=>h(this,this.config.rulerSize,e=>{const t={scale:1,translateX:-1*e,translateY:-1*e};return this.updateTransform(t)}))},zoomToFitContent:function(){return b(this.transformLayer,this.config,()=>{const e=this.getBounds(),t=e.width/this.config.width,n=e.height/this.config.height,r=l(this.config,e=>e(.9*Math.min(t,n))),o=this.config.width*r,a=this.config.height*r,s=(e.width-o)/2,i=(e.height-a)/2;return this.updateTransform({scale:r,translateX:s,translateY:i})})}}}catch(e){return console.error("Failed to create canvas:",e),null}}function C(e={}){const t={...f,...e};return("number"!=typeof t.width||t.width<=0)&&(console.warn("Invalid width, using default"),t.width=f.width),("number"!=typeof t.height||t.height<=0)&&(console.warn("Invalid height, using default"),t.height=f.height),("number"!=typeof t.zoomSpeed||t.zoomSpeed<=0)&&(console.warn("Invalid zoomSpeed, using default"),t.zoomSpeed=f.zoomSpeed),("number"!=typeof t.minZoom||t.minZoom<=0)&&(console.warn("Invalid minZoom, using default"),t.minZoom=f.minZoom),("number"!=typeof t.maxZoom||t.maxZoom<=t.minZoom)&&(console.warn("Invalid maxZoom, using default"),t.maxZoom=f.maxZoom),("number"!=typeof t.keyboardPanStep||t.keyboardPanStep<=0)&&(console.warn("Invalid keyboardPanStep, using default"),t.keyboardPanStep=f.keyboardPanStep),("number"!=typeof t.keyboardFastMultiplier||t.keyboardFastMultiplier<=0)&&(console.warn("Invalid keyboardFastMultiplier, using default"),t.keyboardFastMultiplier=f.keyboardFastMultiplier),("number"!=typeof t.clickZoomLevel||t.clickZoomLevel<=0)&&(console.warn("Invalid clickZoomLevel, using default"),t.clickZoomLevel=f.clickZoomLevel),("number"!=typeof t.rulerFontSize||t.rulerFontSize<=0)&&(console.warn("Invalid rulerFontSize, using default"),t.rulerFontSize=f.rulerFontSize),("number"!=typeof t.rulerSize||t.rulerSize<=0)&&(console.warn("Invalid rulerSize, using default"),t.rulerSize=f.rulerSize),t}class k{constructor(){this.listeners=new Map}setEmitInterceptor(e){this.emitInterceptor=e}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t)}off(e,t){const n=this.listeners.get(e);n&&n.delete(t)}emit(e,t){this.emitInterceptor?.(e,t);const n=this.listeners.get(e);n&&n.forEach(n=>{try{n(t)}catch(t){console.error(`Error in event handler for "${String(e)}":`,t)}})}removeAllListeners(){this.listeners.clear()}}const T=300,S=5;function D(e,t){if(!e?.getBounds)return t;try{const n=e.getBounds(),r=n.width*n.height;return t*(r/2073600)**1}catch(e){return console.warn("Failed to calculate adaptive zoom speed, using base speed:",e),t}}function z(e,t){let n=0,r=0;function o(o){const a=e.container.getBoundingClientRect(),s=o.clientX-a.left,i=o.clientY-a.top;!function(e,t,n,r,o){h(e,t,e=>o(n-e,r-e))}(e,t.rulerSize,s,i,(e,t)=>{n=e,r=t})}function s(o){if(!(o instanceof KeyboardEvent))return;if("canvas"===t.bindKeyboardEventsTo&&document.activeElement!==e.container)return;const s=o.shiftKey,l=t.keyboardPanStep*(s?t.keyboardFastMultiplier:1);let c=!1;const u={};switch(o.key){case"ArrowLeft":u.translateX=e.transform.translateX+l,c=!0;break;case"ArrowRight":u.translateX=e.transform.translateX-l,c=!0;break;case"ArrowUp":u.translateY=e.transform.translateY+l,c=!0;break;case"ArrowDown":u.translateY=e.transform.translateY-l,c=!0;break;case"=":case"+":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;u.scale=a(e.transform.scale*(1+n),t),c=!0}break;case"-":{const n=t.enableAdaptiveSpeed?D(e,t.keyboardZoomStep):t.keyboardZoomStep;u.scale=a(e.transform.scale*(1-n),t),c=!0}break;case"0":if(o.metaKey||o.ctrlKey){const o=1/e.transform.scale,a=i(n,r,e.transform,o,t);Object.assign(u,a),c=!0}break;case"g":case"G":e.toggleGrid&&e.toggleGrid(),c=!0;break;case"r":case"R":o.metaKey||o.ctrlKey||o.altKey||!e.toggleRulers||(e.toggleRulers(),c=!0)}c&&(o.preventDefault(),Object.keys(u).length>0&&e.updateTransform(u))}const l="canvas"===t.bindKeyboardEventsTo?e.container:document;return l.addEventListener("keydown",s),e.container.addEventListener("mousemove",o),()=>{l.removeEventListener("keydown",s),e.container.removeEventListener("mousemove",o)}}function M(e,t,n,r,o){n?t.requireSpaceForMouseDrag?e.container.style.cursor=r?"grab":"default":e.container.style.cursor=o?"grabbing":"grab":e.container.style.cursor="default"}function L(e,t,n,r,o){o.setIsDragging(!1),o.setDragButton(-1),M(e,t,n,r,!1)}function E(e,t,n,r,o,a,s,i,l,c){a&&e.button===s&&L(t,n,r,o,{setIsDragging:c.setIsDragging,setDragButton:c.setDragButton}),r&&0===e.button&&n.enableClickToZoom&&i>0&&function(e,t,n,r,o,a){const s=Date.now()-r,i=e.altKey,l=!n.requireOptionForClickZoom||i;if(s<T&&!o&&!a&&l){e.preventDefault();const r=t.container.getBoundingClientRect(),o=e.clientX-r.left,a=e.clientY-r.top,{clickX:s,clickY:i}=m(t,o,a,n.rulerSize,(e,t)=>({clickX:e,clickY:t})),l=t.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};b(t.transformLayer,t.config,()=>{t.updateTransform(h)})}}(e,t,n,i,l,a),0===e.button&&function(e){e.setMouseDownTime(0),e.setHasDragged(!1)}({setMouseDownTime:c.setMouseDownTime,setHasDragged:c.setHasDragged})}function R(e,t,n=!0){let r=!0,o=!1,a=0,s=0,i=-1,l=!1,c=0,u=0,h=0,m=!1;const g={setIsDragging:e=>{o=e},setDragButton:e=>{i=e},setIsSpacePressed:e=>{l=e},setMouseDownTime:e=>{c=e},setMouseDownX:e=>{u=e},setMouseDownY:e=>{h=e},setHasDragged:e=>{m=e},setLastMouseX:e=>{a=e},setLastMouseY:e=>{s=e}},f=n=>{!function(e,t,n,r,o,a){n.requireSpaceForMouseDrag&&" "===e.key&&(a.setIsSpacePressed(!0),M(t,n,r,!0,o))}(n,e,t,r,o,{setIsSpacePressed:g.setIsSpacePressed})},p=n=>{!function(e,t,n,r,o,a){n.requireSpaceForMouseDrag&&" "===e.key&&(a.setIsSpacePressed(!1),M(t,n,r,!1,o),o&&L(t,n,r,!1,{setIsDragging:a.setIsDragging,setDragButton:a.setDragButton}))}(n,e,t,r,o,{setIsSpacePressed:g.setIsSpacePressed,setIsDragging:g.setIsDragging,setDragButton:g.setDragButton})},v=n=>{!function(e,t,n,r,o,a){const s=0===e.button,i=1===e.button;if(s&&(a.setMouseDownTime(Date.now()),a.setMouseDownX(e.clientX),a.setMouseDownY(e.clientY),a.setHasDragged(!1)),!r)return;(!n.requireSpaceForMouseDrag||o)&&(s&&n.enableLeftDrag||i&&n.enableMiddleDrag)&&(e.preventDefault(),a.setDragButton(e.button),a.setLastMouseX(e.clientX),a.setLastMouseY(e.clientY),M(t,n,r,o,!1))}(n,e,t,r,l,g)},y=t=>{!function(e,t,n,r,o,a,s,i,l,c){if(o>0){const t=Math.abs(e.clientX-a),o=Math.abs(e.clientY-s);(t>S||o>S)&&(c.setHasDragged(!0),!r&&n&&c.setIsDragging(!0))}if(!r||!n)return;e.preventDefault(),d((...e)=>{const o=e[0];if(!r||!n)return;const a=o.clientX-i,s=o.clientY-l,u={translateX:t.transform.translateX+a,translateY:t.transform.translateY+s};t.updateTransform(u),c.setLastMouseX(o.clientX),c.setLastMouseY(o.clientY)})(e)}(t,e,r,o,c,u,h,a,s,{setHasDragged:g.setHasDragged,setIsDragging:g.setIsDragging,setLastMouseX:g.setLastMouseX,setLastMouseY:g.setLastMouseY})},b=n=>{E(n,e,t,r,l,o,i,c,m,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton,setMouseDownTime:g.setMouseDownTime,setHasDragged:g.setHasDragged})},w=()=>{!function(e,t,n,r,o,a){o&&L(e,t,n,r,a)}(e,t,r,l,o,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton})};e.container.addEventListener("mousedown",v),document.addEventListener("mousemove",y),document.addEventListener("mouseup",b),e.container.addEventListener("mouseleave",w),t.requireSpaceForMouseDrag&&(document.addEventListener("keydown",f),document.addEventListener("keyup",p)),M(e,t,r,l,o);const x=()=>{e.container.removeEventListener("mousedown",v),document.removeEventListener("mousemove",y),document.removeEventListener("mouseup",b),e.container.removeEventListener("mouseleave",w),t.requireSpaceForMouseDrag&&(document.removeEventListener("keydown",f),document.removeEventListener("keyup",p))};return n?{cleanup:x,enable:()=>(r=!0,M(e,t,r,l,o),!0),disable:()=>(r=!1,o&&L(e,t,r,l,{setIsDragging:g.setIsDragging,setDragButton:g.setDragButton}),M(e,t,r,l,o),!0),isEnabled:()=>r}:x}function Y(e,t){return{x:(e.clientX+t.clientX)/2,y:(e.clientY+t.clientY)/2}}function X(e,t){const n=e.clientX-t.clientX,r=e.clientY-t.clientY;return Math.sqrt(n*n+r*r)}function B(e,t,n,r){const o=i(n,r,e.transform,t,e.config);return e.updateTransform(o)}function F(e,t,n){e.preventDefault();const r=Array.from(e.touches);d((...e)=>{const r=e[0];if(1===r.length){if(1===n.touches.length){const e=r[0].clientX-n.touches[0].clientX,o=r[0].clientY-n.touches[0].clientY,a={translateX:t.transform.translateX+e,translateY:t.transform.translateY+o};t.updateTransform(a)}}else if(2===r.length){const e=X(r[0],r[1]),o=Y(r[0],r[1]);if(n.lastDistance>0){const r=e/n.lastDistance,a=t.container.getBoundingClientRect();let s=o.x-a.left,i=o.y-a.top;const l=function(e,t,n,r){return h(e,t,e=>{const t={...n,x:n.x-e,y:n.y-e};return r(t)})}(t,t.config.rulerSize,{x:s,y:i},e=>e);s=l.x,i=l.y,B(t,r,s,i)}n.lastDistance=e,n.lastCenter=o}n.touches=r})(r)}function $(e){const t={touches:[],lastDistance:0,lastCenter:{}},n=e=>{!function(e,t){e.preventDefault(),t.touches=Array.from(e.touches),2===t.touches.length&&(t.lastDistance=X(t.touches[0],t.touches[1]),t.lastCenter=Y(t.touches[0],t.touches[1]))}(e,t)},r=n=>{F(n,e,t)},o=e=>{!function(e,t){t.touches=Array.from(e.touches),t.touches.length<2&&(t.lastDistance=0)}(e,t)};return e.container.addEventListener("touchstart",n,{passive:!1}),e.container.addEventListener("touchmove",r,{passive:!1}),e.container.addEventListener("touchend",o,{passive:!1}),()=>{e.container.removeEventListener("touchstart",n),e.container.removeEventListener("touchmove",r),e.container.removeEventListener("touchend",o)}}function P(e){const t=e.ctrlKey||e.metaKey,n=[0===e.deltaMode,Math.abs(e.deltaY)<50,e.deltaY%1!=0,Math.abs(e.deltaX)>0&&Math.abs(e.deltaY)>0].filter(Boolean).length>=2;return{isTrackpad:n,isMouseWheel:!n,isTrackpadScroll:n&&!t,isTrackpadPinch:n&&t,isZoomGesture:t}}function I(e,t){const n=(e=>d((...t)=>{const n=t[0];if(!n||!e?.updateTransform)return!1;try{const t=e.transform,r=1,o=n.deltaX*r,a=n.deltaY*r,s={scale:t.scale,translateX:t.translateX-o,translateY:t.translateY-a};return y(e.transformLayer,e.config),e.updateTransform(s)}catch(e){return console.error("Error handling trackpad pan:",e),!1}}))(e),r=r=>P(r).isTrackpadScroll?n(r):function(e,t,n){if(!e||"number"!=typeof e.deltaY)return console.warn("Invalid wheel event provided"),!1;if(!t?.updateTransform)return console.warn("Invalid canvas provided to handleWheelEvent"),!1;try{e.preventDefault();const r=t.container.getBoundingClientRect(),o=e.clientX-r.left,a=e.clientY-r.top,{mouseX:s,mouseY:i}=m(t,o,a,n.rulerSize,(e,t)=>({mouseX:e,mouseY:t})),l=n.zoomSpeed,c=P(e);if(!c.isZoomGesture)return!1;let u=n.enableAdaptiveSpeed?D(t,l):l;if(c.isTrackpadPinch){const e=.05*n.zoomSpeed;u=n.enableAdaptiveSpeed?D(t,e):e}return B(t,(e.deltaY<0?1:-1)>0?1+u:1/(1+u),s,i)}catch(e){return console.error("Error handling wheel event:",e),!1}}(r,e,t);return e.container.addEventListener("wheel",r,{passive:!1}),()=>{e.container.removeEventListener("wheel",r)}}const Z=100,O=1e3,_=1001,A=4,G=4,H=100,N=100,q=20,K=200;function V(e,t){const n=function(e){const t=document.createElement("div");return t.className="canvas-ruler horizontal-ruler",t.style.cssText=`\n\tposition: absolute;\n\ttop: 0;\n\tleft: ${e.rulerSize}px;\n\tright: 0;\n\theight: ${e.rulerSize}px;\n\tbackground: var(--ruler-background-color);\n\tborder-bottom: 1px solid var(--ruler-border-color);\n\tborder-right: 1px solid var(--ruler-border-color);\n\tz-index: ${O};\n\tpointer-events: none;\n\tfont-family: ${e.rulerFontFamily};\n\tfont-size: ${e.rulerFontSize}px;\n\tcolor: var(--ruler-text-color);\n\toverflow: hidden;\n `,t}(t),r=function(e){const t=document.createElement("div");return t.className="canvas-ruler vertical-ruler",t.style.cssText=`\n\tposition: absolute;\n\ttop: ${e.rulerSize}px;\n\tleft: 0;\n\tbottom: 0;\n\twidth: ${e.rulerSize}px;\n\tbackground: var(--ruler-background-color);\n\tborder-right: 1px solid var(--ruler-border-color);\n\tborder-bottom: 1px solid var(--ruler-border-color);\n\tz-index: ${O};\n\tpointer-events: none;\n\tfont-family: ${e.rulerFontFamily};\n\tfont-size: ${e.rulerFontSize}px;\n\tcolor: var(--ruler-text-color);\n\toverflow: hidden;\n `,t}(t),o=function(e){const t=document.createElement("div");return t.className="canvas-ruler corner-box",t.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tleft: 0;\n\t\twidth: ${e.rulerSize}px;\n\t\theight: ${e.rulerSize}px;\n\t\tbackground: var(--ruler-background-color);\n\t\tborder-right: 1px solid var(--ruler-border-color);\n\t\tborder-bottom: 1px solid var(--ruler-border-color);\n\t\tz-index: ${_};\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tfont-family: ${e.rulerFontFamily};\n\t\tfont-size: ${e.rulerFontSize-2}px;\n\t\tcolor: var(--ruler-text-color);\n\t\tpointer-events: none;\n\t`,t.textContent=e.rulerUnits,t}(t),a=t.enableGrid?function(e){const t=document.createElement("div");return t.className="canvas-ruler grid-overlay",t.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${e.rulerSize}px;\n\t\tleft: ${e.rulerSize}px;\n\t\tright: 0;\n\t\tbottom: 0;\n\t\tpointer-events: none;\n\t\tz-index: ${Z};\n\t\tbackground-image: \n\t\t\tlinear-gradient(var(--grid-color) 1px, transparent 1px),\n\t\t\tlinear-gradient(90deg, var(--grid-color) 1px, transparent 1px);\n\t\tbackground-size: 100px 100px;\n\t\topacity: 0.5;\n\t`,t}(t):void 0;return e.appendChild(n),e.appendChild(r),e.appendChild(o),a&&e.appendChild(a),{horizontalRuler:n,verticalRuler:r,cornerBox:o,gridOverlay:a}}function W(e,t){const n=e/Math.max(5,Math.min(20,t/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 U(e,t,n,r,o){const a=document.createElement("div");a.className="tick",a.style.cssText=`\n\t\tposition: absolute;\n\t\tleft: ${n}px;\n\t\tbottom: 0;\n\t\twidth: 1px;\n\t\theight: ${A}px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(a);if(t%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: ${A+2}px;\n\t\t\tfont-size: ${o.rulerFontSize}px;\n\t\t\tline-height: 1;\n\t\t\tcolor: var(--ruler-text-color);\n\t\t\twhite-space: nowrap;\n\t\t\tpointer-events: none;\n\t\t`,r.textContent=Math.round(t).toString(),e.appendChild(r)}}function j(e,t,n,r,o){const a=document.createElement("div");a.className="tick",a.style.cssText=`\n\t\tposition: absolute;\n\t\ttop: ${n}px;\n\t\tright: 0;\n\t\twidth: ${G}px;\n\t\theight: 1px;\n\t\tbackground: var(--ruler-tick-color);\n\t`,e.appendChild(a);if(t%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: ${G+6}px;\n\t\t\tfont-size: ${o.rulerFontSize}px;\n\t\t\tline-height: 1;\n\t\t\tcolor: var(--ruler-text-color);\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(t).toString(),e.appendChild(r)}}function J(e,t,n,r,o){const a=e.getBounds(),s=a.scale||1,i=a.translateX||0,l=a.translateY||0,c=a.width-o.rulerSize,u=a.height-o.rulerSize,d=-i/s,h=-l/s,m=h+u/s;!function(e,t,n,r,o,a){const s=r,i=W(n-t,s),l=document.createDocumentFragment(),c=Math.floor(t/i)*i,u=Math.ceil(n/i)*i;for(let e=c;e<=u;e+=i){const n=(e-t)*o;n>=-50&&n<=s+50&&U(l,e,n,0,a)}e.innerHTML="",e.appendChild(l)}(t,d,d+c/s,c,s,o),function(e,t,n,r,o,a){const s=r,i=W(n-t,s),l=document.createDocumentFragment(),c=Math.floor(t/i)*i,u=Math.ceil(n/i)*i;for(let e=c;e<=u;e+=i){const n=(e-t)*o;n>=-50&&n<=s+50&&j(l,e,n,0,a)}e.innerHTML="",e.appendChild(l)}(n,h,m,u,s,o),r&&function(e,t,n,r){let o=N*t;for(;o<q;)o*=2;for(;o>K;)o/=2;e.style.backgroundSize=`${o}px ${o}px`,e.style.backgroundPosition=`${n%o}px ${r%o}px`}(r,s,i,l)}function Q(e,t){const n=g(t,"rulerBackgroundColor"),r=g(t,"rulerBorderColor"),o=g(t,"rulerTextColor"),a=g(t,"rulerTickColor"),s=g(t,"gridColor");e.horizontalRuler&&(e.horizontalRuler.style.setProperty("--ruler-background-color",n),e.horizontalRuler.style.setProperty("--ruler-border-color",r),e.horizontalRuler.style.setProperty("--ruler-text-color",o),e.horizontalRuler.style.setProperty("--ruler-tick-color",a)),e.verticalRuler&&(e.verticalRuler.style.setProperty("--ruler-background-color",n),e.verticalRuler.style.setProperty("--ruler-border-color",r),e.verticalRuler.style.setProperty("--ruler-text-color",o),e.verticalRuler.style.setProperty("--ruler-tick-color",a)),e.cornerBox&&(e.cornerBox.style.setProperty("--ruler-background-color",n),e.cornerBox.style.setProperty("--ruler-border-color",r),e.cornerBox.style.setProperty("--ruler-text-color",o)),e.gridOverlay&&e.gridOverlay.style.setProperty("--grid-color",s)}function ee(e,t){if(!e?.container)return console.error("Invalid canvas provided to createRulers"),null;let n,r=null,o=!1;const a=()=>{!o&&n.horizontalRuler&&n.verticalRuler&&J(e,n.horizontalRuler,n.verticalRuler,n.gridOverlay,t)};try{return n=V(e.container,t),r=function(e,t){const n=d(t),r=e.updateTransform;e.updateTransform=function(e){const t=r.call(this,e);return n(),t};const o=d(t);return window.addEventListener("resize",o),()=>{window.removeEventListener("resize",o),e.updateTransform=r,n.cleanup(),o.cleanup()}}(e,a),Q(n,t),a(),t.showRulers||(n.horizontalRuler.style.display="none",n.verticalRuler.style.display="none",n.cornerBox.style.display="none"),!t.showGrid&&n.gridOverlay&&(n.gridOverlay.style.display="none"),{horizontalRuler:n.horizontalRuler,verticalRuler:n.verticalRuler,cornerBox:n.cornerBox,gridOverlay:n.gridOverlay,update:a,updateTheme:e=>{o||Q(n,e)},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 e="none"!==n.gridOverlay.style.display;n.gridOverlay.style.display=e?"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(e){return console.error("Failed to create rulers:",e),null}}return class{constructor(e,t={}){if(this.cleanupFunctions=[],this.rulers=null,this.dragSetup=null,this._isReady=!1,this.listen=new k,!e)throw new Error("Container element is required");this.config=C(t);const n=x(e,this.config);if(!n)throw new Error("Failed to create canvas");this.baseCanvas=n,this.config.bindToWindow&&(this.listen.setEmitInterceptor((e,t)=>{this.broadcastEvent(e,t)}),this.setupGlobalBinding()),this.setupEventHandlers(),this._isReady=!0,this.listen.emit("ready",this)}setupGlobalBinding(){if("undefined"==typeof window)return;const e=this.config.name||"markupCanvas",t=window;t[e]=this,t.__markupCanvasInstances||(t.__markupCanvasInstances=new Map),t.__markupCanvasInstances.set(e,this)}cleanupGlobalBinding(){if("undefined"==typeof window)return;const e=this.config.name||"markupCanvas",t=window;delete t[e],t.__markupCanvasInstances&&t.__markupCanvasInstances.delete(e)}broadcastEvent(e,t){if("undefined"==typeof window)return;let n=t;"ready"===e&&(n={ready:!0}),window.postMessage({source:"markup-canvas",event:e,data:n,timestamp:Date.now(),canvasName:this.config.name},"*")}setupEventHandlers(){try{u(this.config,"enableZoom",()=>{const e=I(this,this.config);this.cleanupFunctions.push(e)}),(this.config.enablePan||this.config.enableClickToZoom)&&(this.dragSetup=R(this,this.config,!0),this.cleanupFunctions.push(this.dragSetup.cleanup)),u(this.config,"enableKeyboard",()=>{const e=z(this,this.config);this.cleanupFunctions.push(e)}),u(this.config,"enableTouch",()=>{const e=$(this);this.cleanupFunctions.push(e)}),u(this.config,"enableRulers",()=>{this.rulers=ee(this.baseCanvas,this.config),this.cleanupFunctions.push(()=>{this.rulers&&this.rulers.destroy()})})}catch(e){throw console.error("Failed to set up event handlers:",e),this.cleanup(),e}}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(e){const t=this.baseCanvas.updateTransform(e);return t&&this.emitTransformEvents(),t}emitTransformEvents(){const e=this.baseCanvas.transform;this.listen.emit("transform",e),this.listen.emit("zoom",e.scale),this.listen.emit("pan",{x:e.translateX,y:e.translateY})}reset(){return this.baseCanvas.reset()}handleResize(){return this.baseCanvas.handleResize()}setZoom(e){return this.baseCanvas.setZoom(e)}canvasToContent(e,t){return this.baseCanvas.canvasToContent(e,t)}zoomToPoint(e,t,n){return b(this.transformLayer,this.config,()=>{const r=this.baseCanvas.zoomToPoint(e,t,n);return r&&this.emitTransformEvents(),r})}resetView(){return b(this.transformLayer,this.config,()=>{const e=!!this.baseCanvas.resetView&&this.baseCanvas.resetView();return e&&this.emitTransformEvents(),e})}zoomToFitContent(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.zoomToFitContent();return e&&this.emitTransformEvents(),e})}panLeft(e){const t=e??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX+t};return this.updateTransform(n)}panRight(e){const t=e??this.config.keyboardPanStep,n={translateX:this.baseCanvas.transform.translateX-t};return this.updateTransform(n)}panUp(e){const t=e??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY+t};return this.updateTransform(n)}panDown(e){const t=e??this.config.keyboardPanStep,n={translateY:this.baseCanvas.transform.translateY-t};return this.updateTransform(n)}zoomIn(e=.1){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(this.baseCanvas.transform.scale*(1+e))};return this.updateTransform(n)}))}zoomOut(e=.1){return b(this.transformLayer,this.config,()=>l(this.config,t=>{const n={scale:t(this.baseCanvas.transform.scale*(1-e))};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 b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.getBounds(),t=(e.width-e.contentWidth*this.baseCanvas.transform.scale)/2,n=(e.height-e.contentHeight*this.baseCanvas.transform.scale)/2;return this.updateTransform({translateX:t,translateY:n})})}fitToScreen(){return b(this.transformLayer,this.config,()=>{const e=this.baseCanvas.zoomToFitContent();return e&&this.emitTransformEvents(),e})}getVisibleArea(){return this.baseCanvas.getBounds().visibleArea}isPointVisible(e,t){const n=this.getVisibleArea();return e>=n.x&&e<=n.x+n.width&&t>=n.y&&t<=n.y+n.height}scrollToPoint(e,t){return b(this.transformLayer,this.config,()=>{const n=this.baseCanvas.getBounds(),r=n.width/2,o=n.height/2,a=r-e*this.baseCanvas.transform.scale,s=o-t*this.baseCanvas.transform.scale;return this.updateTransform({translateX:a,translateY:s})})}getConfig(){return{...this.config}}updateConfig(e){this.config=C({...this.config,...e})}updateThemeMode(e){const t={...this.config,themeMode:e};this.config=C(t);const n=g(this.config,"canvasBackgroundColor");this.baseCanvas.container.style.backgroundColor=n,this.rulers&&this.rulers.updateTheme(this.config)}cleanup(){this.cleanupGlobalBinding(),this.cleanupFunctions.forEach(e=>{try{e()}catch(e){console.warn("Error during cleanup:",e)}}),this.cleanupFunctions=[],this.removeAllListeners()}on(e,t){this.listen.on(e,t)}off(e,t){this.listen.off(e,t)}emit(e,t){this.listen.emit(e,t)}removeAllListeners(){this.listen.removeAllListeners()}destroy(){this.cleanup(),window.__markupCanvasTransitionTimeout&&clearTimeout(window.__markupCanvasTransitionTimeout)}}});
|
package/dist/types/config.d.ts
CHANGED
|
@@ -3,11 +3,13 @@ export interface MarkupCanvasConfig {
|
|
|
3
3
|
width?: number;
|
|
4
4
|
height?: number;
|
|
5
5
|
enableAcceleration?: boolean;
|
|
6
|
+
bindToWindow?: boolean;
|
|
7
|
+
name?: string;
|
|
6
8
|
enableZoom?: boolean;
|
|
7
9
|
enablePan?: boolean;
|
|
8
10
|
enableTouch?: boolean;
|
|
9
11
|
enableKeyboard?: boolean;
|
|
10
|
-
|
|
12
|
+
bindKeyboardEventsTo?: "canvas" | "document";
|
|
11
13
|
zoomSpeed?: number;
|
|
12
14
|
minZoom?: number;
|
|
13
15
|
maxZoom?: number;
|
package/package.json
CHANGED
package/src/lib/MarkupCanvas.ts
CHANGED
|
@@ -43,6 +43,14 @@ export class MarkupCanvas implements Canvas {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
this.baseCanvas = canvas;
|
|
46
|
+
|
|
47
|
+
if (this.config.bindToWindow) {
|
|
48
|
+
this.listen.setEmitInterceptor((event, data) => {
|
|
49
|
+
this.broadcastEvent(event as string, data);
|
|
50
|
+
});
|
|
51
|
+
this.setupGlobalBinding();
|
|
52
|
+
}
|
|
53
|
+
|
|
46
54
|
this.setupEventHandlers();
|
|
47
55
|
this._isReady = true;
|
|
48
56
|
|
|
@@ -50,6 +58,62 @@ export class MarkupCanvas implements Canvas {
|
|
|
50
58
|
this.listen.emit("ready", this);
|
|
51
59
|
}
|
|
52
60
|
|
|
61
|
+
private setupGlobalBinding(): void {
|
|
62
|
+
if (typeof window === "undefined") {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const canvasName = this.config.name || "markupCanvas";
|
|
67
|
+
const windowObj = window as unknown as Record<string, unknown>;
|
|
68
|
+
|
|
69
|
+
// Bind instance to window
|
|
70
|
+
windowObj[canvasName] = this;
|
|
71
|
+
|
|
72
|
+
// Track all instances
|
|
73
|
+
if (!windowObj.__markupCanvasInstances) {
|
|
74
|
+
windowObj.__markupCanvasInstances = new Map();
|
|
75
|
+
}
|
|
76
|
+
(windowObj.__markupCanvasInstances as unknown as Map<string, MarkupCanvas>).set(canvasName, this);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private cleanupGlobalBinding(): void {
|
|
80
|
+
if (typeof window === "undefined") {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const canvasName = this.config.name || "markupCanvas";
|
|
85
|
+
const windowObj = window as unknown as Record<string, unknown>;
|
|
86
|
+
|
|
87
|
+
delete windowObj[canvasName];
|
|
88
|
+
if (windowObj.__markupCanvasInstances) {
|
|
89
|
+
(windowObj.__markupCanvasInstances as unknown as Map<string, MarkupCanvas>).delete(canvasName);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private broadcastEvent(event: string, data: unknown): void {
|
|
94
|
+
if (typeof window === "undefined") {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Receivers can get the instance from the window binding
|
|
99
|
+
let broadcastData = data;
|
|
100
|
+
|
|
101
|
+
if (event === "ready") {
|
|
102
|
+
broadcastData = { ready: true };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
window.postMessage(
|
|
106
|
+
{
|
|
107
|
+
source: "markup-canvas",
|
|
108
|
+
event,
|
|
109
|
+
data: broadcastData,
|
|
110
|
+
timestamp: Date.now(),
|
|
111
|
+
canvasName: this.config.name,
|
|
112
|
+
},
|
|
113
|
+
"*"
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
53
117
|
private setupEventHandlers(): void {
|
|
54
118
|
try {
|
|
55
119
|
// Wheel zoom
|
|
@@ -412,6 +476,7 @@ export class MarkupCanvas implements Canvas {
|
|
|
412
476
|
|
|
413
477
|
// Cleanup method
|
|
414
478
|
cleanup(): void {
|
|
479
|
+
this.cleanupGlobalBinding();
|
|
415
480
|
this.cleanupFunctions.forEach((cleanup) => {
|
|
416
481
|
try {
|
|
417
482
|
cleanup();
|
|
@@ -11,7 +11,7 @@ export const DEFAULT_CONFIG: Required<MarkupCanvasConfig> = {
|
|
|
11
11
|
enablePan: true,
|
|
12
12
|
enableTouch: true,
|
|
13
13
|
enableKeyboard: true,
|
|
14
|
-
|
|
14
|
+
bindKeyboardEventsTo: "canvas",
|
|
15
15
|
|
|
16
16
|
// Zoom behavior
|
|
17
17
|
zoomSpeed: 1.5,
|
|
@@ -67,6 +67,10 @@ export const DEFAULT_CONFIG: Required<MarkupCanvasConfig> = {
|
|
|
67
67
|
// Theme
|
|
68
68
|
themeMode: "light",
|
|
69
69
|
|
|
70
|
+
// Global Binding & Instance Access
|
|
71
|
+
bindToWindow: false,
|
|
72
|
+
name: "markupCanvas",
|
|
73
|
+
|
|
70
74
|
// Callbacks
|
|
71
75
|
onTransformUpdate: () => {},
|
|
72
76
|
};
|
|
@@ -11,7 +11,7 @@ export const EDITOR_PRESET: Required<MarkupCanvasConfig> = {
|
|
|
11
11
|
enablePan: true,
|
|
12
12
|
enableTouch: true,
|
|
13
13
|
enableKeyboard: false,
|
|
14
|
-
|
|
14
|
+
bindKeyboardEventsTo: "canvas",
|
|
15
15
|
|
|
16
16
|
// Zoom behavior
|
|
17
17
|
zoomSpeed: 1.5,
|
|
@@ -52,7 +52,7 @@ export const EDITOR_PRESET: Required<MarkupCanvasConfig> = {
|
|
|
52
52
|
canvasBackgroundColorDark: "transparent",
|
|
53
53
|
|
|
54
54
|
// Ruler styling
|
|
55
|
-
rulerBackgroundColor: "oklch(100% 0 0 / 0.
|
|
55
|
+
rulerBackgroundColor: "oklch(100% 0 0 / 0.96)",
|
|
56
56
|
rulerBorderColor: "oklch(96.7% 0.001 286.375)",
|
|
57
57
|
rulerTextColor: "oklch(70.5% 0.015 286.067)",
|
|
58
58
|
rulerTickColor: "oklch(92% 0.004 286.32)",
|
|
@@ -68,6 +68,10 @@ export const EDITOR_PRESET: Required<MarkupCanvasConfig> = {
|
|
|
68
68
|
// Theme
|
|
69
69
|
themeMode: "light",
|
|
70
70
|
|
|
71
|
+
// Global Binding & Instance Access
|
|
72
|
+
bindToWindow: false,
|
|
73
|
+
name: "markupCanvas",
|
|
74
|
+
|
|
71
75
|
// Callbacks
|
|
72
76
|
onTransformUpdate: () => {},
|
|
73
77
|
};
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export class EventEmitter<Events extends Record<string, unknown>> {
|
|
2
2
|
private listeners: Map<keyof Events, Set<(data: unknown) => void>> = new Map();
|
|
3
|
+
private emitInterceptor?: (event: keyof Events, data: unknown) => void;
|
|
4
|
+
|
|
5
|
+
setEmitInterceptor(interceptor: (event: keyof Events, data: unknown) => void): void {
|
|
6
|
+
this.emitInterceptor = interceptor;
|
|
7
|
+
}
|
|
3
8
|
|
|
4
9
|
on<K extends keyof Events>(event: K, handler: (data: Events[K]) => void): void {
|
|
5
10
|
if (!this.listeners.has(event)) {
|
|
@@ -16,6 +21,8 @@ export class EventEmitter<Events extends Record<string, unknown>> {
|
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
emit<K extends keyof Events>(event: K, data: Events[K]): void {
|
|
24
|
+
this.emitInterceptor?.(event, data);
|
|
25
|
+
|
|
19
26
|
const handlers = this.listeners.get(event);
|
|
20
27
|
if (handlers) {
|
|
21
28
|
handlers.forEach((handler) => {
|
|
@@ -23,7 +23,7 @@ export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanva
|
|
|
23
23
|
function handleKeyDown(event: Event): void {
|
|
24
24
|
if (!(event instanceof KeyboardEvent)) return;
|
|
25
25
|
|
|
26
|
-
if (config.
|
|
26
|
+
if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container) return;
|
|
27
27
|
|
|
28
28
|
const isFastPan = event.shiftKey;
|
|
29
29
|
const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
|
|
@@ -102,7 +102,7 @@ export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanva
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
const keyboardTarget = config.
|
|
105
|
+
const keyboardTarget = config.bindKeyboardEventsTo === "canvas" ? canvas.container : document;
|
|
106
106
|
|
|
107
107
|
keyboardTarget.addEventListener("keydown", handleKeyDown);
|
|
108
108
|
canvas.container.addEventListener("mousemove", handleMouseMove);
|
|
@@ -24,7 +24,7 @@ export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanva
|
|
|
24
24
|
function handleKeyDown(event: Event): void {
|
|
25
25
|
if (!(event instanceof KeyboardEvent)) return;
|
|
26
26
|
|
|
27
|
-
if (config.
|
|
27
|
+
if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container) return;
|
|
28
28
|
|
|
29
29
|
const isFastPan = event.shiftKey;
|
|
30
30
|
const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
|
|
@@ -103,7 +103,7 @@ export function setupKeyboardEvents(canvas: Canvas, config: Required<MarkupCanva
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
const keyboardTarget = config.
|
|
106
|
+
const keyboardTarget = config.bindKeyboardEventsTo ? canvas.container : document;
|
|
107
107
|
|
|
108
108
|
keyboardTarget.addEventListener("keydown", handleKeyDown);
|
|
109
109
|
canvas.container.addEventListener("mousemove", handleMouseMove);
|
package/src/types/config.ts
CHANGED
|
@@ -6,12 +6,16 @@ export interface MarkupCanvasConfig {
|
|
|
6
6
|
height?: number;
|
|
7
7
|
enableAcceleration?: boolean;
|
|
8
8
|
|
|
9
|
+
// Global Binding & Instance Access
|
|
10
|
+
bindToWindow?: boolean;
|
|
11
|
+
name?: string;
|
|
12
|
+
|
|
9
13
|
// Interaction controls
|
|
10
14
|
enableZoom?: boolean;
|
|
11
15
|
enablePan?: boolean;
|
|
12
16
|
enableTouch?: boolean;
|
|
13
17
|
enableKeyboard?: boolean;
|
|
14
|
-
|
|
18
|
+
bindKeyboardEventsTo?: "canvas" | "document";
|
|
15
19
|
|
|
16
20
|
// Zoom behavior
|
|
17
21
|
zoomSpeed?: number;
|