@markup-canvas/core 1.0.7 → 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.
@@ -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;
@@ -5,9 +5,4 @@ export interface RulerThemeUpdater {
5
5
  cornerBox?: HTMLElement;
6
6
  gridOverlay?: HTMLElement;
7
7
  }
8
- /**
9
- * Updates all ruler elements with new theme colors
10
- * @param elements - The ruler elements to update
11
- * @param config - The canvas config containing theme and color settings
12
- */
13
8
  export declare function updateRulerTheme(elements: RulerThemeUpdater, config: Required<MarkupCanvasConfig>): 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.7
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
- limitKeyboardEventsToCanvas: false,
20
+ bindKeyboardEventsTo: "canvas",
21
21
  // Zoom behavior
22
22
  zoomSpeed: 1.5,
23
23
  minZoom: 0.05,
@@ -47,22 +47,25 @@ const EDITOR_PRESET = {
47
47
  rulerUnits: "px",
48
48
  rulerSize: 20,
49
49
  // Canvas styling
50
- canvasBackgroundColor: "oklch(98.5% 0 0)",
51
- canvasBackgroundColorDark: "oklch(21% 0.006 285.885)",
50
+ canvasBackgroundColor: "transparent",
51
+ canvasBackgroundColorDark: "transparent",
52
52
  // Ruler styling
53
- rulerBackgroundColor: "oklch(100% 0 0 / 0.4)",
54
- rulerBorderColor: "oklch(98.5% 0 0)",
53
+ rulerBackgroundColor: "oklch(100% 0 0 / 0.96)",
54
+ rulerBorderColor: "oklch(96.7% 0.001 286.375)",
55
55
  rulerTextColor: "oklch(70.5% 0.015 286.067)",
56
- rulerTickColor: "oklch(70.5% 0.015 286.067)",
56
+ rulerTickColor: "oklch(92% 0.004 286.32)",
57
57
  gridColor: "rgba(232, 86, 193, 0.5)",
58
58
  // Ruler styling (dark theme)
59
59
  rulerBackgroundColorDark: "oklch(27.4% 0.006 286.033)",
60
- rulerBorderColorDark: "oklch(37% 0.013 285.805)",
60
+ rulerBorderColorDark: "oklch(44.2% 0.017 285.786)",
61
61
  rulerTextColorDark: "oklch(55.2% 0.016 285.938)",
62
62
  rulerTickColorDark: "oklch(55.2% 0.016 285.938)",
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
- limitKeyboardEventsToCanvas: false,
321
+ bindKeyboardEventsTo: "canvas",
319
322
  // Zoom behavior
320
323
  zoomSpeed: 1.5,
321
324
  minZoom: 0.05,
@@ -349,7 +352,7 @@ const DEFAULT_CONFIG = {
349
352
  canvasBackgroundColorDark: "rgba(40, 40, 40, 1)",
350
353
  // Ruler styling (light theme)
351
354
  rulerBackgroundColor: "rgba(255, 255, 255, 0.95)",
352
- rulerBorderColor: "rgba(221, 221, 221, 1)",
355
+ rulerBorderColor: "rgba(240, 240, 240, 1)",
353
356
  rulerTextColor: "rgba(102, 102, 102, 1)",
354
357
  rulerTickColor: "rgba(204, 204, 204, 1)",
355
358
  gridColor: "rgba(232, 86, 193, 0.5)",
@@ -357,10 +360,13 @@ const DEFAULT_CONFIG = {
357
360
  rulerBackgroundColorDark: "rgba(30, 30, 30, 0.95)",
358
361
  rulerBorderColorDark: "rgba(68, 68, 68, 1)",
359
362
  rulerTextColorDark: "rgba(170, 170, 170, 1)",
360
- rulerTickColorDark: "rgba(56, 56, 56, 1)",
363
+ rulerTickColorDark: "rgba(104, 104, 104, 1)",
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.limitKeyboardEventsToCanvas && document.activeElement !== canvas.container)
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.limitKeyboardEventsToCanvas ? canvas.container : document;
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 () => {
@@ -1380,25 +1390,22 @@ const GRID_SETTINGS = {
1380
1390
  function createCornerBox(config) {
1381
1391
  const corner = document.createElement("div");
1382
1392
  corner.className = "canvas-ruler corner-box";
1383
- const backgroundColor = getThemeValue(config, "rulerBackgroundColor");
1384
- const borderColor = getThemeValue(config, "rulerBorderColor");
1385
- const textColor = getThemeValue(config, "rulerTextColor");
1386
1393
  corner.style.cssText = `
1387
1394
  position: absolute;
1388
1395
  top: 0;
1389
1396
  left: 0;
1390
1397
  width: ${config.rulerSize}px;
1391
1398
  height: ${config.rulerSize}px;
1392
- background: ${backgroundColor};
1393
- border-right: 1px solid ${borderColor};
1394
- border-bottom: 1px solid ${borderColor};
1399
+ background: var(--ruler-background-color);
1400
+ border-right: 1px solid var(--ruler-border-color);
1401
+ border-bottom: 1px solid var(--ruler-border-color);
1395
1402
  z-index: ${RULER_Z_INDEX.CORNER};
1396
1403
  display: flex;
1397
1404
  align-items: center;
1398
1405
  justify-content: center;
1399
1406
  font-family: ${config.rulerFontFamily};
1400
1407
  font-size: ${config.rulerFontSize - 2}px;
1401
- color: ${textColor};
1408
+ color: var(--ruler-text-color);
1402
1409
  pointer-events: none;
1403
1410
  `;
1404
1411
  corner.textContent = config.rulerUnits;
@@ -1408,7 +1415,6 @@ function createCornerBox(config) {
1408
1415
  function createGridOverlay(config) {
1409
1416
  const grid = document.createElement("div");
1410
1417
  grid.className = "canvas-ruler grid-overlay";
1411
- const gridColor = getThemeValue(config, "gridColor");
1412
1418
  grid.style.cssText = `
1413
1419
  position: absolute;
1414
1420
  top: ${config.rulerSize}px;
@@ -1418,8 +1424,8 @@ function createGridOverlay(config) {
1418
1424
  pointer-events: none;
1419
1425
  z-index: ${RULER_Z_INDEX.GRID};
1420
1426
  background-image:
1421
- linear-gradient(${gridColor} 1px, transparent 1px),
1422
- linear-gradient(90deg, ${gridColor} 1px, transparent 1px);
1427
+ linear-gradient(var(--grid-color) 1px, transparent 1px),
1428
+ linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
1423
1429
  background-size: 100px 100px;
1424
1430
  opacity: 0.5;
1425
1431
  `;
@@ -1429,23 +1435,20 @@ function createGridOverlay(config) {
1429
1435
  function createHorizontalRuler(config) {
1430
1436
  const ruler = document.createElement("div");
1431
1437
  ruler.className = "canvas-ruler horizontal-ruler";
1432
- const backgroundColor = getThemeValue(config, "rulerBackgroundColor");
1433
- const borderColor = getThemeValue(config, "rulerBorderColor");
1434
- const textColor = getThemeValue(config, "rulerTextColor");
1435
1438
  ruler.style.cssText = `
1436
1439
  position: absolute;
1437
1440
  top: 0;
1438
1441
  left: ${config.rulerSize}px;
1439
1442
  right: 0;
1440
1443
  height: ${config.rulerSize}px;
1441
- background: ${backgroundColor};
1442
- border-bottom: 1px solid ${borderColor};
1443
- border-right: 1px solid ${borderColor};
1444
+ background: var(--ruler-background-color);
1445
+ border-bottom: 1px solid var(--ruler-border-color);
1446
+ border-right: 1px solid var(--ruler-border-color);
1444
1447
  z-index: ${RULER_Z_INDEX.RULERS};
1445
1448
  pointer-events: none;
1446
1449
  font-family: ${config.rulerFontFamily};
1447
1450
  font-size: ${config.rulerFontSize}px;
1448
- color: ${textColor};
1451
+ color: var(--ruler-text-color);
1449
1452
  overflow: hidden;
1450
1453
  `;
1451
1454
  return ruler;
@@ -1454,23 +1457,20 @@ function createHorizontalRuler(config) {
1454
1457
  function createVerticalRuler(config) {
1455
1458
  const ruler = document.createElement("div");
1456
1459
  ruler.className = "canvas-ruler vertical-ruler";
1457
- const backgroundColor = getThemeValue(config, "rulerBackgroundColor");
1458
- const borderColor = getThemeValue(config, "rulerBorderColor");
1459
- const textColor = getThemeValue(config, "rulerTextColor");
1460
1460
  ruler.style.cssText = `
1461
1461
  position: absolute;
1462
1462
  top: ${config.rulerSize}px;
1463
1463
  left: 0;
1464
1464
  bottom: 0;
1465
1465
  width: ${config.rulerSize}px;
1466
- background: ${backgroundColor};
1467
- border-right: 1px solid ${borderColor};
1468
- border-bottom: 1px solid ${borderColor};
1466
+ background: var(--ruler-background-color);
1467
+ border-right: 1px solid var(--ruler-border-color);
1468
+ border-bottom: 1px solid var(--ruler-border-color);
1469
1469
  z-index: ${RULER_Z_INDEX.RULERS};
1470
1470
  pointer-events: none;
1471
1471
  font-family: ${config.rulerFontFamily};
1472
1472
  font-size: ${config.rulerFontSize}px;
1473
- color: ${textColor};
1473
+ color: var(--ruler-text-color);
1474
1474
  overflow: hidden;
1475
1475
  `;
1476
1476
  return ruler;
@@ -1542,27 +1542,26 @@ function calculateTickSpacing(contentSize, canvasSize) {
1542
1542
 
1543
1543
  function createHorizontalTick(container, position, pixelPos, _tickSpacing, config) {
1544
1544
  const tick = document.createElement("div");
1545
- const tickColor = getThemeValue(config, "rulerTickColor");
1545
+ tick.className = "tick";
1546
1546
  tick.style.cssText = `
1547
1547
  position: absolute;
1548
1548
  left: ${pixelPos}px;
1549
1549
  bottom: 0;
1550
1550
  width: 1px;
1551
1551
  height: ${TICK_SETTINGS.TICK_HEIGHT}px;
1552
- background: ${tickColor};
1552
+ background: var(--ruler-tick-color);
1553
1553
  `;
1554
1554
  container.appendChild(tick);
1555
1555
  const shouldShowLabel = position % TICK_SETTINGS.TICK_LABEL_INTERVAL === 0;
1556
1556
  if (shouldShowLabel) {
1557
1557
  const label = document.createElement("div");
1558
- const textColor = getThemeValue(config, "rulerTextColor");
1559
1558
  label.style.cssText = `
1560
1559
  position: absolute;
1561
1560
  left: ${pixelPos}px;
1562
1561
  bottom: ${TICK_SETTINGS.TICK_HEIGHT + 2}px;
1563
1562
  font-size: ${config.rulerFontSize}px;
1564
1563
  line-height: 1;
1565
- color: ${textColor};
1564
+ color: var(--ruler-text-color);
1566
1565
  white-space: nowrap;
1567
1566
  pointer-events: none;
1568
1567
  `;
@@ -1590,27 +1589,26 @@ function updateHorizontalRuler(ruler, contentLeft, contentRight, canvasWidth, sc
1590
1589
 
1591
1590
  function createVerticalTick(container, position, pixelPos, _tickSpacing, config) {
1592
1591
  const tick = document.createElement("div");
1593
- const tickColor = getThemeValue(config, "rulerTickColor");
1592
+ tick.className = "tick";
1594
1593
  tick.style.cssText = `
1595
1594
  position: absolute;
1596
1595
  top: ${pixelPos}px;
1597
1596
  right: 0;
1598
1597
  width: ${TICK_SETTINGS.TICK_WIDTH}px;
1599
1598
  height: 1px;
1600
- background: ${tickColor};
1599
+ background: var(--ruler-tick-color);
1601
1600
  `;
1602
1601
  container.appendChild(tick);
1603
1602
  const shouldShowLabel = position % TICK_SETTINGS.TICK_LABEL_INTERVAL === 0;
1604
1603
  if (shouldShowLabel) {
1605
1604
  const label = document.createElement("div");
1606
- const textColor = getThemeValue(config, "rulerTextColor");
1607
1605
  label.style.cssText = `
1608
1606
  position: absolute;
1609
1607
  top: ${pixelPos - 6}px;
1610
1608
  right: ${TICK_SETTINGS.TICK_WIDTH + 6}px;
1611
1609
  font-size: ${config.rulerFontSize}px;
1612
1610
  line-height: 1;
1613
- color: ${textColor};
1611
+ color: var(--ruler-text-color);
1614
1612
  white-space: nowrap;
1615
1613
  pointer-events: none;
1616
1614
  transform: rotate(-90deg);
@@ -1656,44 +1654,36 @@ function updateRulers(canvas, horizontalRuler, verticalRuler, gridOverlay, confi
1656
1654
  }
1657
1655
  }
1658
1656
 
1659
- /**
1660
- * Updates all ruler elements with new theme colors
1661
- * @param elements - The ruler elements to update
1662
- * @param config - The canvas config containing theme and color settings
1663
- */
1664
1657
  function updateRulerTheme(elements, config) {
1665
1658
  // Get theme-aware colors
1666
1659
  const backgroundColor = getThemeValue(config, "rulerBackgroundColor");
1667
1660
  const borderColor = getThemeValue(config, "rulerBorderColor");
1668
1661
  const textColor = getThemeValue(config, "rulerTextColor");
1662
+ const tickColor = getThemeValue(config, "rulerTickColor");
1669
1663
  const gridColor = getThemeValue(config, "gridColor");
1670
- // Update horizontal ruler
1664
+ // Update horizontal ruler with CSS variables
1671
1665
  if (elements.horizontalRuler) {
1672
- elements.horizontalRuler.style.background = backgroundColor;
1673
- elements.horizontalRuler.style.borderBottomColor = borderColor;
1674
- elements.horizontalRuler.style.borderRightColor = borderColor;
1675
- elements.horizontalRuler.style.color = textColor;
1666
+ elements.horizontalRuler.style.setProperty("--ruler-background-color", backgroundColor);
1667
+ elements.horizontalRuler.style.setProperty("--ruler-border-color", borderColor);
1668
+ elements.horizontalRuler.style.setProperty("--ruler-text-color", textColor);
1669
+ elements.horizontalRuler.style.setProperty("--ruler-tick-color", tickColor);
1676
1670
  }
1677
- // Update vertical ruler
1671
+ // Update vertical ruler with CSS variables
1678
1672
  if (elements.verticalRuler) {
1679
- elements.verticalRuler.style.background = backgroundColor;
1680
- elements.verticalRuler.style.borderRightColor = borderColor;
1681
- elements.verticalRuler.style.borderBottomColor = borderColor;
1682
- elements.verticalRuler.style.color = textColor;
1673
+ elements.verticalRuler.style.setProperty("--ruler-background-color", backgroundColor);
1674
+ elements.verticalRuler.style.setProperty("--ruler-border-color", borderColor);
1675
+ elements.verticalRuler.style.setProperty("--ruler-text-color", textColor);
1676
+ elements.verticalRuler.style.setProperty("--ruler-tick-color", tickColor);
1683
1677
  }
1684
- // Update corner box
1678
+ // Update corner box with CSS variables
1685
1679
  if (elements.cornerBox) {
1686
- elements.cornerBox.style.background = backgroundColor;
1687
- elements.cornerBox.style.borderRightColor = borderColor;
1688
- elements.cornerBox.style.borderBottomColor = borderColor;
1689
- elements.cornerBox.style.color = textColor;
1680
+ elements.cornerBox.style.setProperty("--ruler-background-color", backgroundColor);
1681
+ elements.cornerBox.style.setProperty("--ruler-border-color", borderColor);
1682
+ elements.cornerBox.style.setProperty("--ruler-text-color", textColor);
1690
1683
  }
1691
- // Update grid overlay
1684
+ // Update grid overlay with CSS variables
1692
1685
  if (elements.gridOverlay) {
1693
- elements.gridOverlay.style.backgroundImage = `
1694
- linear-gradient(${gridColor} 1px, transparent 1px),
1695
- linear-gradient(90deg, ${gridColor} 1px, transparent 1px)
1696
- `;
1686
+ elements.gridOverlay.style.setProperty("--grid-color", gridColor);
1697
1687
  }
1698
1688
  }
1699
1689
 
@@ -1713,6 +1703,7 @@ function createRulers(canvas, config) {
1713
1703
  try {
1714
1704
  elements = createRulerElements(canvas.container, config);
1715
1705
  cleanupEvents = setupRulerEvents(canvas, safeUpdate);
1706
+ updateRulerTheme(elements, config);
1716
1707
  safeUpdate();
1717
1708
  if (!config.showRulers) {
1718
1709
  elements.horizontalRuler.style.display = "none";
@@ -1733,8 +1724,6 @@ function createRulers(canvas, config) {
1733
1724
  return;
1734
1725
  // Update all ruler theme colors
1735
1726
  updateRulerTheme(elements, newConfig);
1736
- // Re-render rulers to update tick colors
1737
- safeUpdate();
1738
1727
  },
1739
1728
  show: () => {
1740
1729
  if (elements.horizontalRuler)
@@ -1804,11 +1793,59 @@ class MarkupCanvas {
1804
1793
  throw new Error("Failed to create canvas");
1805
1794
  }
1806
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
+ }
1807
1802
  this.setupEventHandlers();
1808
1803
  this._isReady = true;
1809
1804
  // Emit ready event
1810
1805
  this.listen.emit("ready", this);
1811
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
+ }
1812
1849
  setupEventHandlers() {
1813
1850
  try {
1814
1851
  // Wheel zoom
@@ -2120,6 +2157,7 @@ class MarkupCanvas {
2120
2157
  }
2121
2158
  // Cleanup method
2122
2159
  cleanup() {
2160
+ this.cleanupGlobalBinding();
2123
2161
  this.cleanupFunctions.forEach((cleanup) => {
2124
2162
  try {
2125
2163
  cleanup();