orcasvn-react-diagrams 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +11 -3
  3. package/dist/cjs/examples.js +1768 -161
  4. package/dist/cjs/index.js +786 -120
  5. package/dist/cjs/types/api/createDiagramEditor.d.ts +19 -1
  6. package/dist/cjs/types/api/index.d.ts +1 -1
  7. package/dist/cjs/types/api/types.d.ts +32 -0
  8. package/dist/cjs/types/displaybox/DisplayBoxControls.d.ts +5 -1
  9. package/dist/cjs/types/displaybox/demos/AsymmetricPortMultiAnchorDemoTab.d.ts +3 -0
  10. package/dist/cjs/types/displaybox/demos/LayoutLabelReservedSpaceDemoTab.d.ts +3 -0
  11. package/dist/cjs/types/displaybox/demos/VertexControlLinkSessionDemoTab.d.ts +3 -0
  12. package/dist/cjs/types/displaybox/demos/asymmetricPortMultiAnchorDemo.d.ts +31 -0
  13. package/dist/cjs/types/displaybox/demos/layoutLabelReservedSpaceDemo.d.ts +11 -0
  14. package/dist/cjs/types/displaybox/demos/vertexControlLinkSessionDemo.d.ts +12 -0
  15. package/dist/cjs/types/displaybox/useDemoControls.d.ts +4 -0
  16. package/dist/cjs/types/engine/AutoLayoutService.d.ts +2 -0
  17. package/dist/cjs/types/engine/DiagramEngine.d.ts +5 -0
  18. package/dist/cjs/types/engine/LinkRoutingService.d.ts +9 -1
  19. package/dist/cjs/types/models/PortModel.d.ts +5 -0
  20. package/dist/cjs/types/renderer/RenderTypes.d.ts +3 -1
  21. package/dist/cjs/types/renderer/konva/KonvaInteraction.d.ts +14 -0
  22. package/dist/cjs/types/renderer/konva/KonvaNodeFactory.d.ts +1 -0
  23. package/dist/cjs/types/renderer/konva/KonvaRenderer.d.ts +0 -1
  24. package/dist/cjs/types/shapes/BuiltInShapes.d.ts +3 -1
  25. package/dist/cjs/types/utils/__tests__/portGeometry.test.d.ts +1 -0
  26. package/dist/cjs/types/utils/portGeometry.d.ts +44 -0
  27. package/dist/esm/examples.js +1769 -162
  28. package/dist/esm/examples.js.map +1 -1
  29. package/dist/esm/index.js +786 -120
  30. package/dist/esm/index.js.map +1 -1
  31. package/dist/esm/types/api/createDiagramEditor.d.ts +19 -1
  32. package/dist/esm/types/api/index.d.ts +1 -1
  33. package/dist/esm/types/api/types.d.ts +32 -0
  34. package/dist/esm/types/displaybox/DisplayBoxControls.d.ts +5 -1
  35. package/dist/esm/types/displaybox/demos/AsymmetricPortMultiAnchorDemoTab.d.ts +3 -0
  36. package/dist/esm/types/displaybox/demos/LayoutLabelReservedSpaceDemoTab.d.ts +3 -0
  37. package/dist/esm/types/displaybox/demos/VertexControlLinkSessionDemoTab.d.ts +3 -0
  38. package/dist/esm/types/displaybox/demos/asymmetricPortMultiAnchorDemo.d.ts +31 -0
  39. package/dist/esm/types/displaybox/demos/layoutLabelReservedSpaceDemo.d.ts +11 -0
  40. package/dist/esm/types/displaybox/demos/vertexControlLinkSessionDemo.d.ts +12 -0
  41. package/dist/esm/types/displaybox/useDemoControls.d.ts +4 -0
  42. package/dist/esm/types/engine/AutoLayoutService.d.ts +2 -0
  43. package/dist/esm/types/engine/DiagramEngine.d.ts +5 -0
  44. package/dist/esm/types/engine/LinkRoutingService.d.ts +9 -1
  45. package/dist/esm/types/models/PortModel.d.ts +5 -0
  46. package/dist/esm/types/renderer/RenderTypes.d.ts +3 -1
  47. package/dist/esm/types/renderer/konva/KonvaInteraction.d.ts +14 -0
  48. package/dist/esm/types/renderer/konva/KonvaNodeFactory.d.ts +1 -0
  49. package/dist/esm/types/renderer/konva/KonvaRenderer.d.ts +0 -1
  50. package/dist/esm/types/shapes/BuiltInShapes.d.ts +3 -1
  51. package/dist/esm/types/utils/__tests__/portGeometry.test.d.ts +1 -0
  52. package/dist/esm/types/utils/portGeometry.d.ts +44 -0
  53. package/dist/examples.d.ts +50 -0
  54. package/dist/index.d.ts +58 -1
  55. package/package.json +11 -10
  56. package/src/displaybox/demos/AsymmetricPortMultiAnchorDemoTab.tsx +269 -0
  57. package/src/displaybox/demos/AutoLayoutDemoTab.tsx +113 -11
  58. package/src/displaybox/demos/DeletionEventsDemoTab.tsx +6 -1
  59. package/src/displaybox/demos/EngineEventsDemoTab.tsx +5 -0
  60. package/src/displaybox/demos/EventHandlersDemoTab.tsx +5 -0
  61. package/src/displaybox/demos/ExternalDragDropDemoTab.tsx +5 -0
  62. package/src/displaybox/demos/LayoutLabelReservedSpaceDemoTab.tsx +291 -0
  63. package/src/displaybox/demos/LinkCancelDemoTab.tsx +5 -0
  64. package/src/displaybox/demos/ShapeHoverControlsDemoTab.tsx +6 -1
  65. package/src/displaybox/demos/SimpleDemo.tsx +5 -0
  66. package/src/displaybox/demos/SvgPathDemoTab.tsx +5 -0
  67. package/src/displaybox/demos/TextLayoutDemoTab.tsx +6 -1
  68. package/src/displaybox/demos/VertexControlLinkSessionDemoTab.tsx +302 -0
  69. package/src/displaybox/demos/asymmetricPortMultiAnchorDemo.ts +357 -0
  70. package/src/displaybox/demos/autoLayoutDemo.ts +23 -5
  71. package/src/displaybox/demos/index.tsx +91 -75
  72. package/src/displaybox/demos/layoutLabelReservedSpaceDemo.ts +121 -0
  73. package/src/displaybox/demos/vertexControlLinkSessionDemo.ts +145 -0
@@ -1,4 +1,4 @@
1
- import React, { useMemo, useCallback, createContext, useContext, useRef, useState, useEffect } from 'react';
1
+ import React, { useMemo, useState, useCallback, createContext, useContext, useRef, useEffect } from 'react';
2
2
 
3
3
  var fallbackId = function () {
4
4
  return "id-".concat(Math.random().toString(36).slice(2, 10), "-").concat(Date.now().toString(36));
@@ -851,16 +851,34 @@ var createAutoLayoutState = function () { return ({
851
851
  ports: [],
852
852
  links: [],
853
853
  texts: [
854
- { id: 'label-layout-row', content: 'Row parent', position: { x: 8, y: -18 }, ownerId: 'layout-row' },
854
+ {
855
+ id: 'label-layout-row',
856
+ content: 'Row parent label lane',
857
+ position: { x: 8, y: 6 },
858
+ ownerId: 'layout-row',
859
+ layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
860
+ },
855
861
  { id: 'label-row-a', content: 'row-a', position: { x: 6, y: -14 }, ownerId: 'row-a' },
856
862
  { id: 'label-row-b', content: 'row-b', position: { x: 6, y: -14 }, ownerId: 'row-b' },
857
863
  { id: 'label-row-c', content: 'row-c', position: { x: 6, y: -14 }, ownerId: 'row-c' },
858
864
  { id: 'label-row-nested-owner', content: 'owns children', position: { x: 6, y: -16 }, ownerId: 'row-nested-owner' },
859
- { id: 'label-layout-column', content: 'Column parent', position: { x: 8, y: -18 }, ownerId: 'layout-column' },
865
+ {
866
+ id: 'label-layout-column',
867
+ content: 'Column parent label lane',
868
+ position: { x: 8, y: 6 },
869
+ ownerId: 'layout-column',
870
+ layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
871
+ },
860
872
  { id: 'label-col-a', content: 'col-a', position: { x: 6, y: -14 }, ownerId: 'col-a' },
861
873
  { id: 'label-col-b', content: 'col-b', position: { x: 6, y: -14 }, ownerId: 'col-b' },
862
874
  { id: 'label-col-c', content: 'col-c', position: { x: 6, y: -14 }, ownerId: 'col-c' },
863
- { id: 'label-layout-nested', content: 'Row + nested column', position: { x: 6, y: -18 }, ownerId: 'layout-nested' },
875
+ {
876
+ id: 'label-layout-nested',
877
+ content: 'Row + nested column lane',
878
+ position: { x: 6, y: 6 },
879
+ ownerId: 'layout-nested',
880
+ layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
881
+ },
864
882
  { id: 'label-nested-stack', content: 'nested stack', position: { x: 6, y: -16 }, ownerId: 'nested-stack' },
865
883
  { id: 'label-stack-a', content: 'stack-a', position: { x: 6, y: -14 }, ownerId: 'stack-a' },
866
884
  { id: 'label-stack-b', content: 'stack-b', position: { x: 6, y: -14 }, ownerId: 'stack-b' },
@@ -873,8 +891,8 @@ var createAutoLayoutState = function () { return ({
873
891
  }); };
874
892
  var autoLayoutDemoConfig = ({
875
893
  id: 'auto-layout',
876
- title: 'Element Auto-Layout',
877
- description: 'Arrange children with padding/alignment plus fit options (main-axis distribute, cross-axis stretch).',
894
+ title: 'Element Auto-Layout + Label Lane',
895
+ description: 'Consolidated auto-layout verification with baseline/nested containers, fit options, and label reserved-space modes.',
878
896
  createState: createAutoLayoutState,
879
897
  elementShapes: baseElementShapes,
880
898
  portShapes: basePortShapes,
@@ -1486,6 +1504,302 @@ var multipleElementsDemoConfig = ({
1486
1504
  actions: [],
1487
1505
  });
1488
1506
 
1507
+ var asymmetricPortMultiAnchorDemoId = 'asymmetric-port-multi-anchor';
1508
+ var asymmetricPortDefaultVariantId = 'classic';
1509
+ var asymmetricPortShapeVariants = [
1510
+ {
1511
+ id: asymmetricPortDefaultVariantId,
1512
+ label: 'Classic arm',
1513
+ description: 'Baseline square-line-circle asymmetric glyph.',
1514
+ shapeId: 'displaybox-asymmetric-anchor-port-classic',
1515
+ svgPath: 'M2 6 H10 V14 H2 Z M10 10 H24 M28 10 m-4 0 a4 4 0 1 0 8 0 a4 4 0 1 0 -8 0',
1516
+ svgSize: { width: 36, height: 20 },
1517
+ placementPoint: { x: 6, y: 10 },
1518
+ externalLinkAttachPoint: { x: 28, y: 10 },
1519
+ internalLinkAttachPoint: { x: 6, y: 10 },
1520
+ rotationPivot: { x: 6, y: 10 },
1521
+ },
1522
+ {
1523
+ id: 'forked-arm',
1524
+ label: 'Forked arm',
1525
+ description: 'Connector splits before reaching the terminal circle.',
1526
+ shapeId: 'displaybox-asymmetric-anchor-port-forked',
1527
+ svgPath: 'M2 6 H10 V14 H2 Z M10 10 H18 M18 10 L24 6 M18 10 L24 14 M30 10 m-4 0 a4 4 0 1 0 8 0 a4 4 0 1 0 -8 0',
1528
+ svgSize: { width: 38, height: 20 },
1529
+ placementPoint: { x: 6, y: 10 },
1530
+ externalLinkAttachPoint: { x: 30, y: 10 },
1531
+ internalLinkAttachPoint: { x: 6, y: 10 },
1532
+ rotationPivot: { x: 6, y: 10 },
1533
+ },
1534
+ {
1535
+ id: 'zigzag-arm',
1536
+ label: 'Zigzag arm',
1537
+ description: 'Connector uses a zigzag body before the endpoint marker.',
1538
+ shapeId: 'displaybox-asymmetric-anchor-port-zigzag',
1539
+ svgPath: 'M2 6 H10 V14 H2 Z M10 10 L15 6 L20 14 L25 6 L28 10 M34 10 m-4 0 a4 4 0 1 0 8 0 a4 4 0 1 0 -8 0',
1540
+ svgSize: { width: 42, height: 20 },
1541
+ placementPoint: { x: 6, y: 10 },
1542
+ externalLinkAttachPoint: { x: 34, y: 10 },
1543
+ internalLinkAttachPoint: { x: 6, y: 10 },
1544
+ rotationPivot: { x: 6, y: 10 },
1545
+ },
1546
+ {
1547
+ id: 'dual-bar',
1548
+ label: 'Dual bar',
1549
+ description: 'Body includes dual bars between pivot and circle endpoint.',
1550
+ shapeId: 'displaybox-asymmetric-anchor-port-dual-bar',
1551
+ svgPath: 'M2 6 H10 V14 H2 Z M10 10 H18 M18 6 V14 M22 6 V14 M22 10 H26 M32 10 m-4 0 a4 4 0 1 0 8 0 a4 4 0 1 0 -8 0',
1552
+ svgSize: { width: 40, height: 20 },
1553
+ placementPoint: { x: 6, y: 10 },
1554
+ externalLinkAttachPoint: { x: 32, y: 10 },
1555
+ internalLinkAttachPoint: { x: 6, y: 10 },
1556
+ rotationPivot: { x: 6, y: 10 },
1557
+ },
1558
+ ];
1559
+ var resolveAsymmetricPortShapeVariant = function (variantId) {
1560
+ var _a;
1561
+ if (!variantId)
1562
+ return asymmetricPortShapeVariants[0];
1563
+ return (_a = asymmetricPortShapeVariants.find(function (variant) { return variant.id === variantId; })) !== null && _a !== void 0 ? _a : asymmetricPortShapeVariants[0];
1564
+ };
1565
+ resolveAsymmetricPortShapeVariant(asymmetricPortDefaultVariantId).shapeId;
1566
+ resolveAsymmetricPortShapeVariant(asymmetricPortDefaultVariantId).svgPath;
1567
+ resolveAsymmetricPortShapeVariant(asymmetricPortDefaultVariantId).svgSize;
1568
+ var multiAnchorHostId = 'multi-anchor-host';
1569
+ var multiAnchorExternalPortId = 'multi-anchor-port-external';
1570
+ var multiAnchorStroke = '#1f4d99';
1571
+ var legacyStroke = '#9b2c2c';
1572
+ var createAsymmetricPort = function (options) {
1573
+ var basePort = {
1574
+ id: options.id,
1575
+ elementId: options.elementId,
1576
+ position: options.position,
1577
+ shapeId: options.shapeVariant.shapeId,
1578
+ size: __assign({}, options.shapeVariant.svgSize),
1579
+ moveMode: 'border',
1580
+ anchorCenter: true,
1581
+ orientToHostBorder: true,
1582
+ style: {
1583
+ stroke: options.stroke,
1584
+ strokeWidth: 2,
1585
+ fill: 'transparent',
1586
+ },
1587
+ };
1588
+ if (options.mode === 'legacy') {
1589
+ return basePort;
1590
+ }
1591
+ return __assign(__assign({}, basePort), { placementPoint: __assign({}, options.shapeVariant.placementPoint), externalLinkAttachPoint: __assign({}, options.shapeVariant.externalLinkAttachPoint), internalLinkAttachPoint: __assign({}, options.shapeVariant.internalLinkAttachPoint), rotationPivot: __assign({}, options.shapeVariant.rotationPivot) });
1592
+ };
1593
+ var createComparisonRow = function (options) {
1594
+ var prefix = options.prefix, top = options.top, mode = options.mode, shapeVariant = options.shapeVariant;
1595
+ var hostId = "".concat(prefix, "-host");
1596
+ var peerId = "".concat(prefix, "-peer");
1597
+ var childId = "".concat(prefix, "-child");
1598
+ var externalPortId = "".concat(prefix, "-port-external");
1599
+ var topPortId = "".concat(prefix, "-port-top");
1600
+ var bottomPortId = "".concat(prefix, "-port-bottom");
1601
+ var internalPortId = "".concat(prefix, "-port-internal");
1602
+ var peerPortId = "".concat(prefix, "-peer-port");
1603
+ var childPortId = "".concat(prefix, "-child-port");
1604
+ var stroke = mode === 'multi-anchor' ? multiAnchorStroke : legacyStroke;
1605
+ var title = mode === 'multi-anchor' ? 'Multi-anchor geometry' : 'Legacy/default geometry';
1606
+ var subtitle = mode === 'multi-anchor'
1607
+ ? "".concat(shapeVariant.label, ": square pivot stays pinned to the border; right-side link starts at the circle endpoint.")
1608
+ : 'Same svgPath without placement/link/pivot anchors for quick comparison.';
1609
+ return {
1610
+ elements: [
1611
+ {
1612
+ id: hostId,
1613
+ position: { x: 80, y: top },
1614
+ size: { width: 280, height: 180 },
1615
+ shapeId: 'default',
1616
+ },
1617
+ {
1618
+ id: peerId,
1619
+ position: { x: 470, y: top + 36 },
1620
+ size: { width: 170, height: 108 },
1621
+ shapeId: 'panel',
1622
+ },
1623
+ {
1624
+ id: childId,
1625
+ position: { x: 88, y: 86 },
1626
+ size: { width: 122, height: 72 },
1627
+ shapeId: 'panel',
1628
+ parentId: hostId,
1629
+ },
1630
+ ],
1631
+ ports: [
1632
+ createAsymmetricPort({
1633
+ id: topPortId,
1634
+ elementId: hostId,
1635
+ position: { x: 112, y: 0 },
1636
+ stroke: stroke,
1637
+ mode: mode,
1638
+ shapeVariant: shapeVariant,
1639
+ }),
1640
+ createAsymmetricPort({
1641
+ id: externalPortId,
1642
+ elementId: hostId,
1643
+ position: { x: 280, y: 68 },
1644
+ stroke: stroke,
1645
+ mode: mode,
1646
+ shapeVariant: shapeVariant,
1647
+ }),
1648
+ createAsymmetricPort({
1649
+ id: bottomPortId,
1650
+ elementId: hostId,
1651
+ position: { x: 182, y: 180 },
1652
+ stroke: stroke,
1653
+ mode: mode,
1654
+ shapeVariant: shapeVariant,
1655
+ }),
1656
+ createAsymmetricPort({
1657
+ id: internalPortId,
1658
+ elementId: hostId,
1659
+ position: { x: 0, y: 130 },
1660
+ stroke: stroke,
1661
+ mode: mode,
1662
+ shapeVariant: shapeVariant,
1663
+ }),
1664
+ {
1665
+ id: peerPortId,
1666
+ elementId: peerId,
1667
+ position: { x: 0, y: 54 },
1668
+ shapeId: 'port-dark',
1669
+ moveMode: 'border',
1670
+ anchorCenter: true,
1671
+ },
1672
+ {
1673
+ id: childPortId,
1674
+ elementId: childId,
1675
+ position: { x: 0, y: 36 },
1676
+ shapeId: 'port-circle',
1677
+ moveMode: 'border',
1678
+ anchorCenter: true,
1679
+ },
1680
+ ],
1681
+ links: [
1682
+ {
1683
+ id: "".concat(prefix, "-link-external"),
1684
+ sourcePortId: externalPortId,
1685
+ targetPortId: peerPortId,
1686
+ points: [],
1687
+ style: {
1688
+ stroke: stroke,
1689
+ strokeWidth: 2,
1690
+ },
1691
+ },
1692
+ {
1693
+ id: "".concat(prefix, "-link-internal"),
1694
+ sourcePortId: internalPortId,
1695
+ targetPortId: childPortId,
1696
+ points: [],
1697
+ style: {
1698
+ stroke: mode === 'multi-anchor' ? '#2f7a3d' : '#7a5f2f',
1699
+ strokeWidth: 2,
1700
+ dash: mode === 'multi-anchor' ? [8, 4] : [4, 4],
1701
+ },
1702
+ },
1703
+ ],
1704
+ texts: [
1705
+ {
1706
+ id: "".concat(prefix, "-title"),
1707
+ content: title,
1708
+ position: { x: 74, y: top - 42 },
1709
+ style: { fontStyle: 'bold', fontSize: 20 },
1710
+ },
1711
+ {
1712
+ id: "".concat(prefix, "-subtitle"),
1713
+ content: subtitle,
1714
+ position: { x: 74, y: top - 18 },
1715
+ style: { fontSize: 13, fill: '#334155' },
1716
+ },
1717
+ {
1718
+ id: "".concat(prefix, "-host-label"),
1719
+ content: 'Drag border ports or use the side buttons below.',
1720
+ position: { x: 14, y: -18 },
1721
+ ownerId: hostId,
1722
+ style: { fontSize: 12, fill: '#334155' },
1723
+ },
1724
+ {
1725
+ id: "".concat(prefix, "-top-port-label"),
1726
+ content: 'top',
1727
+ position: { x: 10, y: -18 },
1728
+ ownerId: topPortId,
1729
+ style: { fontSize: 11 },
1730
+ },
1731
+ {
1732
+ id: "".concat(prefix, "-external-port-label"),
1733
+ content: 'external → circle',
1734
+ position: { x: 12, y: -18 },
1735
+ ownerId: externalPortId,
1736
+ style: { fontSize: 11 },
1737
+ },
1738
+ {
1739
+ id: "".concat(prefix, "-bottom-port-label"),
1740
+ content: 'bottom',
1741
+ position: { x: 12, y: 18 },
1742
+ ownerId: bottomPortId,
1743
+ style: { fontSize: 11 },
1744
+ },
1745
+ {
1746
+ id: "".concat(prefix, "-internal-port-label"),
1747
+ content: 'internal → square',
1748
+ position: { x: 12, y: -18 },
1749
+ ownerId: internalPortId,
1750
+ style: { fontSize: 11 },
1751
+ },
1752
+ {
1753
+ id: "".concat(prefix, "-peer-label"),
1754
+ content: 'External peer',
1755
+ position: { x: 12, y: -18 },
1756
+ ownerId: peerId,
1757
+ style: { fontSize: 12 },
1758
+ },
1759
+ {
1760
+ id: "".concat(prefix, "-child-label"),
1761
+ content: 'Child host for internal link',
1762
+ position: { x: 8, y: -18 },
1763
+ ownerId: childId,
1764
+ style: { fontSize: 11 },
1765
+ },
1766
+ ],
1767
+ };
1768
+ };
1769
+ var createAsymmetricPortMultiAnchorState = function (showLegacyComparison, variantId) {
1770
+ if (showLegacyComparison === void 0) { showLegacyComparison = true; }
1771
+ if (variantId === void 0) { variantId = asymmetricPortDefaultVariantId; }
1772
+ var shapeVariant = resolveAsymmetricPortShapeVariant(variantId);
1773
+ var primaryRow = createComparisonRow({ prefix: 'multi-anchor', top: 92, mode: 'multi-anchor', shapeVariant: shapeVariant });
1774
+ var legacyRow = showLegacyComparison
1775
+ ? createComparisonRow({ prefix: 'legacy', top: 356, mode: 'legacy', shapeVariant: shapeVariant })
1776
+ : { elements: [], ports: [], links: [], texts: [] };
1777
+ return {
1778
+ elements: __spreadArray(__spreadArray([], primaryRow.elements, true), legacyRow.elements, true),
1779
+ ports: __spreadArray(__spreadArray([], primaryRow.ports, true), legacyRow.ports, true),
1780
+ links: __spreadArray(__spreadArray([], primaryRow.links, true), legacyRow.links, true),
1781
+ texts: __spreadArray(__spreadArray([
1782
+ {
1783
+ id: 'multi-anchor-legend',
1784
+ content: 'Legend: square = placement/rotation pivot, line = glyph body, circle = external attach endpoint. Internal link uses the square anchor.',
1785
+ position: { x: 72, y: 28 },
1786
+ style: { fontSize: 13, fill: '#1f2937' },
1787
+ }
1788
+ ], primaryRow.texts, true), legacyRow.texts, true),
1789
+ };
1790
+ };
1791
+ var asymmetricPortMultiAnchorDemoConfig = {
1792
+ id: asymmetricPortMultiAnchorDemoId,
1793
+ title: 'Asymmetric Multi-Anchor Ports',
1794
+ description: 'Compares legacy/default origin-based svg glyph behavior with explicit placement, pivot, and external/internal link attach points.',
1795
+ createState: function () { return createAsymmetricPortMultiAnchorState(true, asymmetricPortDefaultVariantId); },
1796
+ elementShapes: baseElementShapes,
1797
+ portShapes: basePortShapes,
1798
+ defaultElementShapeId: 'default',
1799
+ defaultPortShapeId: 'port-circle',
1800
+ actions: [],
1801
+ };
1802
+
1489
1803
  var createPortConstraintState = function () { return ({
1490
1804
  elements: [
1491
1805
  {
@@ -2436,7 +2750,7 @@ var shapeHoverDemoIds = {
2436
2750
  circle: 'hover-circle-host',
2437
2751
  noopRect: 'hover-noop-rect',
2438
2752
  };
2439
- var elementShapes = __spreadArray(__spreadArray([], baseElementShapes, true), [
2753
+ var elementShapes$1 = __spreadArray(__spreadArray([], baseElementShapes, true), [
2440
2754
  {
2441
2755
  id: 'hover-diamond-shape',
2442
2756
  kind: 'diamond',
@@ -2570,7 +2884,7 @@ var shapeHoverControlsDemoConfig = {
2570
2884
  title: 'Shape Hover Controls',
2571
2885
  description: 'Host-configured edge/midpoint/vertex plus ellipse-midpoint controls with trigger modes, selectors, click actions, and drag event logging.',
2572
2886
  createState: createShapeHoverControlsState,
2573
- elementShapes: elementShapes,
2887
+ elementShapes: elementShapes$1,
2574
2888
  portShapes: basePortShapes,
2575
2889
  defaultElementShapeId: 'default',
2576
2890
  defaultPortShapeId: 'port-circle',
@@ -2592,22 +2906,168 @@ var shapeHoverControlsDemoConfig = {
2592
2906
  ],
2593
2907
  };
2594
2908
 
2909
+ var vertexSessionDemoIds = {
2910
+ source: 'vertex-session-source',
2911
+ portTarget: 'vertex-session-port-target',
2912
+ elementTarget: 'vertex-session-element-target',
2913
+ nativeSource: 'vertex-session-native-source',
2914
+ nativeTarget: 'vertex-session-native-target',
2915
+ existingTargetPort: 'vertex-session-existing-target-port',
2916
+ nativeSourcePort: 'vertex-session-native-source-port',
2917
+ nativeTargetPort: 'vertex-session-native-target-port',
2918
+ };
2919
+ var elementShapes = __spreadArray(__spreadArray([], baseElementShapes, true), [
2920
+ {
2921
+ id: 'vertex-session-diamond',
2922
+ kind: 'diamond',
2923
+ style: {
2924
+ fill: '#f5edff',
2925
+ stroke: '#6a3da3',
2926
+ strokeWidth: 2,
2927
+ },
2928
+ },
2929
+ {
2930
+ id: 'vertex-session-target',
2931
+ kind: 'rect',
2932
+ style: {
2933
+ cornerRadius: 10,
2934
+ fill: '#edf7ff',
2935
+ stroke: '#2f6d97',
2936
+ strokeWidth: 2,
2937
+ },
2938
+ },
2939
+ ], false);
2940
+ var createVertexSessionState = function () { return ({
2941
+ elements: [
2942
+ {
2943
+ id: vertexSessionDemoIds.source,
2944
+ position: { x: 80, y: 120 },
2945
+ size: { width: 170, height: 130 },
2946
+ shapeId: 'vertex-session-diamond',
2947
+ },
2948
+ {
2949
+ id: vertexSessionDemoIds.portTarget,
2950
+ position: { x: 420, y: 110 },
2951
+ size: { width: 210, height: 140 },
2952
+ shapeId: 'vertex-session-target',
2953
+ },
2954
+ {
2955
+ id: vertexSessionDemoIds.elementTarget,
2956
+ position: { x: 720, y: 120 },
2957
+ size: { width: 220, height: 160 },
2958
+ shapeId: 'vertex-session-target',
2959
+ },
2960
+ {
2961
+ id: vertexSessionDemoIds.nativeSource,
2962
+ position: { x: 200, y: 360 },
2963
+ size: { width: 180, height: 120 },
2964
+ shapeId: 'default',
2965
+ },
2966
+ {
2967
+ id: vertexSessionDemoIds.nativeTarget,
2968
+ position: { x: 560, y: 360 },
2969
+ size: { width: 180, height: 120 },
2970
+ shapeId: 'default',
2971
+ },
2972
+ ],
2973
+ ports: [
2974
+ {
2975
+ id: vertexSessionDemoIds.existingTargetPort,
2976
+ elementId: vertexSessionDemoIds.portTarget,
2977
+ position: { x: 0, y: 70 },
2978
+ shapeId: 'port-circle',
2979
+ moveMode: 'border',
2980
+ anchorCenter: true,
2981
+ orientToHostBorder: true,
2982
+ },
2983
+ {
2984
+ id: vertexSessionDemoIds.nativeSourcePort,
2985
+ elementId: vertexSessionDemoIds.nativeSource,
2986
+ position: { x: 180, y: 60 },
2987
+ shapeId: 'port-circle',
2988
+ moveMode: 'border',
2989
+ anchorCenter: true,
2990
+ orientToHostBorder: true,
2991
+ },
2992
+ {
2993
+ id: vertexSessionDemoIds.nativeTargetPort,
2994
+ elementId: vertexSessionDemoIds.nativeTarget,
2995
+ position: { x: 0, y: 60 },
2996
+ shapeId: 'port-circle',
2997
+ moveMode: 'border',
2998
+ anchorCenter: true,
2999
+ orientToHostBorder: true,
3000
+ },
3001
+ ],
3002
+ links: [],
3003
+ texts: [
3004
+ {
3005
+ id: 'vertex-session-source-label',
3006
+ ownerId: vertexSessionDemoIds.source,
3007
+ content: 'Drag the vertex control to start programmatic link session',
3008
+ position: { x: 8, y: -18 },
3009
+ style: { fontSize: 13, fontFamily: 'sans-serif' },
3010
+ },
3011
+ {
3012
+ id: 'vertex-session-target-port-label',
3013
+ ownerId: vertexSessionDemoIds.portTarget,
3014
+ content: 'Scenario 1 target: existing port',
3015
+ position: { x: 8, y: -16 },
3016
+ style: { fontSize: 13, fontFamily: 'sans-serif' },
3017
+ },
3018
+ {
3019
+ id: 'vertex-session-target-element-label',
3020
+ ownerId: vertexSessionDemoIds.elementTarget,
3021
+ content: 'Scenario 2 target: element body (auto create destination port)',
3022
+ position: { x: 8, y: -16 },
3023
+ style: { fontSize: 13, fontFamily: 'sans-serif' },
3024
+ },
3025
+ {
3026
+ id: 'vertex-session-native-label',
3027
+ ownerId: vertexSessionDemoIds.nativeSource,
3028
+ content: 'Scenario 5 baseline: native port drag still works',
3029
+ position: { x: 8, y: -16 },
3030
+ style: { fontSize: 12, fontFamily: 'sans-serif' },
3031
+ },
3032
+ ],
3033
+ }); };
3034
+ var vertexControlLinkSessionDemoConfig = {
3035
+ id: 'vertex-control-link-session',
3036
+ title: 'Vertex Control Link Session',
3037
+ description: 'Programmatic link lifecycle driven by vertex-control drag: start, preview update, complete-to-port, complete-to-element, and cancel paths with event log verification.',
3038
+ createState: createVertexSessionState,
3039
+ elementShapes: elementShapes,
3040
+ portShapes: basePortShapes,
3041
+ defaultElementShapeId: 'default',
3042
+ defaultPortShapeId: 'port-circle',
3043
+ actions: [],
3044
+ };
3045
+
2595
3046
  var DisplayBoxControls = function (_a) {
2596
- var actions = _a.actions, snapEnabled = _a.snapEnabled, selectedLinkRouting = _a.selectedLinkRouting, canToggleLinkRouting = _a.canToggleLinkRouting, onReload = _a.onReload, onZoomIn = _a.onZoomIn, onZoomOut = _a.onZoomOut, onResetViewport = _a.onResetViewport, onToggleSnap = _a.onToggleSnap, onManualRender = _a.onManualRender, onToggleLinkRouting = _a.onToggleLinkRouting, onAction = _a.onAction;
2597
- return (React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: 12 } },
2598
- React.createElement("button", { onClick: onReload, style: { padding: '8px 12px' } }, "Reload Demo State"),
2599
- React.createElement("button", { onClick: onZoomIn, style: { padding: '8px 12px' } }, "Zoom In"),
2600
- React.createElement("button", { onClick: onZoomOut, style: { padding: '8px 12px' } }, "Zoom Out"),
2601
- React.createElement("button", { onClick: onResetViewport, style: { padding: '8px 12px' } }, "Reset Viewport"),
2602
- React.createElement("button", { onClick: onToggleSnap, style: { padding: '8px 12px' } },
2603
- "Snap: ",
2604
- snapEnabled ? 'On' : 'Off'),
2605
- React.createElement("button", { onClick: onManualRender, style: { padding: '8px 12px' } }, "Manual Render"),
2606
- React.createElement("button", { onClick: onToggleLinkRouting, style: { padding: '8px 12px' }, disabled: !canToggleLinkRouting },
2607
- "Toggle Link Routing (",
2608
- selectedLinkRouting,
2609
- ")"),
2610
- actions.map(function (action) { return (React.createElement("button", { key: action.id, onClick: function () { return onAction(action); }, style: { padding: '8px 12px' } }, action.label)); })));
3047
+ var actions = _a.actions, snapEnabled = _a.snapEnabled, selectedLinkRouting = _a.selectedLinkRouting, canToggleLinkRouting = _a.canToggleLinkRouting, onReload = _a.onReload, onZoomIn = _a.onZoomIn, onZoomOut = _a.onZoomOut, onResetViewport = _a.onResetViewport, onToggleSnap = _a.onToggleSnap, onManualRender = _a.onManualRender, onToggleLinkRouting = _a.onToggleLinkRouting, onAction = _a.onAction, onExportImage = _a.onExportImage, onClearExportPreview = _a.onClearExportPreview, exportPreviewDataUrl = _a.exportPreviewDataUrl, exportError = _a.exportError;
3048
+ return (React.createElement("div", { style: { marginBottom: 12 } },
3049
+ React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
3050
+ React.createElement("button", { onClick: onReload, style: { padding: '8px 12px' } }, "Reload Demo State"),
3051
+ React.createElement("button", { onClick: onZoomIn, style: { padding: '8px 12px' } }, "Zoom In"),
3052
+ React.createElement("button", { onClick: onZoomOut, style: { padding: '8px 12px' } }, "Zoom Out"),
3053
+ React.createElement("button", { onClick: onResetViewport, style: { padding: '8px 12px' } }, "Reset Viewport"),
3054
+ React.createElement("button", { onClick: onToggleSnap, style: { padding: '8px 12px' } },
3055
+ "Snap: ",
3056
+ snapEnabled ? 'On' : 'Off'),
3057
+ React.createElement("button", { onClick: onManualRender, style: { padding: '8px 12px' } }, "Manual Render"),
3058
+ React.createElement("button", { onClick: onToggleLinkRouting, style: { padding: '8px 12px' }, disabled: !canToggleLinkRouting },
3059
+ "Toggle Link Routing (",
3060
+ selectedLinkRouting,
3061
+ ")"),
3062
+ onExportImage ? (React.createElement("button", { onClick: onExportImage, style: { padding: '8px 12px' } }, "Export PNG (2x)")) : null,
3063
+ onClearExportPreview && (exportPreviewDataUrl || exportError) ? (React.createElement("button", { onClick: onClearExportPreview, style: { padding: '8px 12px' } }, "Clear Export")) : null,
3064
+ actions.map(function (action) { return (React.createElement("button", { key: action.id, onClick: function () { return onAction(action); }, style: { padding: '8px 12px' } }, action.label)); })),
3065
+ exportError ? (React.createElement("div", { style: { marginTop: 8, color: '#a10000', fontSize: 12 } },
3066
+ "Export failed: ",
3067
+ exportError)) : null,
3068
+ exportPreviewDataUrl ? (React.createElement("div", { style: { marginTop: 10 } },
3069
+ React.createElement("div", { style: { fontSize: 12, color: '#444', marginBottom: 6 } }, "Latest export preview:"),
3070
+ React.createElement("img", { src: exportPreviewDataUrl, alt: "Exported diagram preview", style: { maxWidth: 320, width: '100%', height: 'auto', border: '1px solid #d0d0d0', borderRadius: 6 } }))) : null));
2611
3071
  };
2612
3072
 
2613
3073
  var DisplayBoxStage = function (_a) {
@@ -2626,6 +3086,8 @@ var useDemoControls = function (_a) {
2626
3086
  }, [diagramState, selection]);
2627
3087
  var selectedLinkRouting = (_b = selectedLink === null || selectedLink === void 0 ? void 0 : selectedLink.routing) !== null && _b !== void 0 ? _b : 'auto';
2628
3088
  var canToggleLinkRouting = Boolean(selectedLink);
3089
+ var _c = useState(null), exportPreviewDataUrl = _c[0], setExportPreviewDataUrl = _c[1];
3090
+ var _d = useState(null), exportError = _d[0], setExportError = _d[1];
2629
3091
  var handleAction = useCallback(function (action) {
2630
3092
  var editor = editorRef.current;
2631
3093
  if (!editor)
@@ -2669,9 +3131,29 @@ var useDemoControls = function (_a) {
2669
3131
  var next = selectedLinkRouting === 'auto' ? 'manual' : 'auto';
2670
3132
  editor.setLinkRoutingMode(selectedLink.id, next);
2671
3133
  }, [editorRef, selectedLink, selectedLinkRouting]);
3134
+ var handleExportImage = useCallback(function () {
3135
+ var editor = editorRef.current;
3136
+ if (!editor)
3137
+ return;
3138
+ try {
3139
+ var dataUrl = editor.exportImage({ mimeType: 'image/png', pixelRatio: 2 });
3140
+ setExportPreviewDataUrl(dataUrl);
3141
+ setExportError(null);
3142
+ }
3143
+ catch (error) {
3144
+ setExportPreviewDataUrl(null);
3145
+ setExportError(error instanceof Error ? error.message : 'Failed to export image.');
3146
+ }
3147
+ }, [editorRef]);
3148
+ var handleClearExportPreview = useCallback(function () {
3149
+ setExportPreviewDataUrl(null);
3150
+ setExportError(null);
3151
+ }, []);
2672
3152
  return {
2673
3153
  selectedLinkRouting: selectedLinkRouting,
2674
3154
  canToggleLinkRouting: canToggleLinkRouting,
3155
+ exportPreviewDataUrl: exportPreviewDataUrl,
3156
+ exportError: exportError,
2675
3157
  handleAction: handleAction,
2676
3158
  handleReload: handleReload,
2677
3159
  handleZoomIn: function () { return handleZoom(1.1); },
@@ -2680,6 +3162,8 @@ var useDemoControls = function (_a) {
2680
3162
  handleToggleSnap: handleToggleSnap,
2681
3163
  handleManualRender: handleManualRender,
2682
3164
  handleToggleLinkRouting: handleToggleLinkRouting,
3165
+ handleExportImage: handleExportImage,
3166
+ handleClearExportPreview: handleClearExportPreview,
2683
3167
  snapEnabled: snapEnabled,
2684
3168
  };
2685
3169
  };
@@ -2832,6 +3316,11 @@ var PortModel = /** @class */ (function () {
2832
3316
  this.moveMode = data.moveMode;
2833
3317
  this.anchorCenter = (_a = data.anchorCenter) !== null && _a !== void 0 ? _a : true;
2834
3318
  this.orientToHostBorder = (_b = data.orientToHostBorder) !== null && _b !== void 0 ? _b : true;
3319
+ this.placementPoint = data.placementPoint ? __assign({}, data.placementPoint) : undefined;
3320
+ this.linkAttachPoint = data.linkAttachPoint ? __assign({}, data.linkAttachPoint) : undefined;
3321
+ this.externalLinkAttachPoint = data.externalLinkAttachPoint ? __assign({}, data.externalLinkAttachPoint) : undefined;
3322
+ this.internalLinkAttachPoint = data.internalLinkAttachPoint ? __assign({}, data.internalLinkAttachPoint) : undefined;
3323
+ this.rotationPivot = data.rotationPivot ? __assign({}, data.rotationPivot) : undefined;
2835
3324
  this.currentAnchorId = data.currentAnchorId;
2836
3325
  }
2837
3326
  PortModel.prototype.setPosition = function (position) {
@@ -2860,6 +3349,11 @@ var PortModel = /** @class */ (function () {
2860
3349
  moveMode: this.moveMode,
2861
3350
  anchorCenter: this.anchorCenter,
2862
3351
  orientToHostBorder: this.orientToHostBorder,
3352
+ placementPoint: this.placementPoint ? __assign({}, this.placementPoint) : undefined,
3353
+ linkAttachPoint: this.linkAttachPoint ? __assign({}, this.linkAttachPoint) : undefined,
3354
+ externalLinkAttachPoint: this.externalLinkAttachPoint ? __assign({}, this.externalLinkAttachPoint) : undefined,
3355
+ internalLinkAttachPoint: this.internalLinkAttachPoint ? __assign({}, this.internalLinkAttachPoint) : undefined,
3356
+ rotationPivot: this.rotationPivot ? __assign({}, this.rotationPivot) : undefined,
2863
3357
  currentAnchorId: this.currentAnchorId,
2864
3358
  };
2865
3359
  };
@@ -4542,6 +5036,7 @@ var AutoLayoutService = /** @class */ (function () {
4542
5036
  var childFitCrossAxis = (_e = layout.childFitCrossAxis) !== null && _e !== void 0 ? _e : 'none';
4543
5037
  var childFitMinSize = layout.childFitMinSize;
4544
5038
  var childFitMaxSize = layout.childFitMaxSize;
5039
+ var labelReservedTopLane = this.resolveLabelReservedTopLane(parentId, layout);
4545
5040
  var sorted = __spreadArray([], children, true).sort(function (a, b) {
4546
5041
  var aPos = axis === 'horizontal' ? a.position.x : a.position.y;
4547
5042
  var bPos = axis === 'horizontal' ? b.position.x : b.position.y;
@@ -4552,7 +5047,7 @@ var AutoLayoutService = /** @class */ (function () {
4552
5047
  var gapCount = Math.max(0, sorted.length - 1);
4553
5048
  var calculateFittedSizes = function (parentWidth, parentHeight) {
4554
5049
  var availableWidth = Math.max(0, parentWidth - padding.x * 2);
4555
- var availableHeight = Math.max(0, parentHeight - padding.y * 2);
5050
+ var availableHeight = Math.max(0, parentHeight - padding.y * 2 - labelReservedTopLane);
4556
5051
  var availableMain = axis === 'horizontal'
4557
5052
  ? Math.max(0, availableWidth - gap * gapCount)
4558
5053
  : Math.max(0, availableHeight - gap * gapCount);
@@ -4597,12 +5092,12 @@ var AutoLayoutService = /** @class */ (function () {
4597
5092
  if (axis === 'horizontal') {
4598
5093
  return {
4599
5094
  width: Math.max(parent.size.width, padding.x * 2 + totalWidth + gap * gapCount),
4600
- height: Math.max(parent.size.height, padding.y * 2 + maxHeight),
5095
+ height: Math.max(parent.size.height, padding.y * 2 + labelReservedTopLane + maxHeight),
4601
5096
  };
4602
5097
  }
4603
5098
  return {
4604
5099
  width: Math.max(parent.size.width, padding.x * 2 + maxWidth),
4605
- height: Math.max(parent.size.height, padding.y * 2 + totalHeight + gap * gapCount),
5100
+ height: Math.max(parent.size.height, padding.y * 2 + labelReservedTopLane + totalHeight + gap * gapCount),
4606
5101
  };
4607
5102
  };
4608
5103
  var fittedSizes = calculateFittedSizes(parent.size.width, parent.size.height);
@@ -4610,9 +5105,9 @@ var AutoLayoutService = /** @class */ (function () {
4610
5105
  fittedSizes = calculateFittedSizes(newParentWidth, newParentHeight);
4611
5106
  (_a = resolveParentSize(fittedSizes), newParentWidth = _a.width, newParentHeight = _a.height);
4612
5107
  var availableWidth = Math.max(0, newParentWidth - padding.x * 2);
4613
- var availableHeight = Math.max(0, newParentHeight - padding.y * 2);
5108
+ var availableHeight = Math.max(0, newParentHeight - padding.y * 2 - labelReservedTopLane);
4614
5109
  var cursorX = padding.x;
4615
- var cursorY = padding.y;
5110
+ var cursorY = padding.y + labelReservedTopLane;
4616
5111
  var desiredPositions = new Map();
4617
5112
  var desiredSizes = new Map();
4618
5113
  sorted.forEach(function (child, index) {
@@ -4620,10 +5115,10 @@ var AutoLayoutService = /** @class */ (function () {
4620
5115
  desiredSizes.set(child.id, fittedSize);
4621
5116
  if (axis === 'horizontal') {
4622
5117
  var y = align === 'start'
4623
- ? padding.y
5118
+ ? padding.y + labelReservedTopLane
4624
5119
  : align === 'end'
4625
5120
  ? newParentHeight - padding.y - fittedSize.height
4626
- : padding.y + Math.max(0, (availableHeight - fittedSize.height) / 2);
5121
+ : padding.y + labelReservedTopLane + Math.max(0, (availableHeight - fittedSize.height) / 2);
4627
5122
  desiredPositions.set(child.id, { x: cursorX, y: y });
4628
5123
  cursorX += fittedSize.width + (index < sorted.length - 1 ? gap : 0);
4629
5124
  }
@@ -4716,6 +5211,45 @@ var AutoLayoutService = /** @class */ (function () {
4716
5211
  y: (_c = padding.y) !== null && _c !== void 0 ? _c : 12,
4717
5212
  };
4718
5213
  };
5214
+ AutoLayoutService.prototype.resolveLabelReservedTopLane = function (parentId, layout) {
5215
+ var _a, _b, _c, _d;
5216
+ var policy = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace;
5217
+ if (!policy)
5218
+ return 0;
5219
+ var mode = (_a = policy.mode) !== null && _a !== void 0 ? _a : 'none';
5220
+ if (mode === 'none')
5221
+ return 0;
5222
+ if (((_b = policy.placement) !== null && _b !== void 0 ? _b : 'top') !== 'top')
5223
+ return 0;
5224
+ var minSize = Math.max(0, (_c = policy.minSize) !== null && _c !== void 0 ? _c : 0);
5225
+ var maxSize = typeof policy.maxSize === 'number' && Number.isFinite(policy.maxSize)
5226
+ ? Math.max(minSize, policy.maxSize)
5227
+ : Number.POSITIVE_INFINITY;
5228
+ var resolved = 0;
5229
+ if (mode === 'fixed') {
5230
+ resolved = Math.max(0, (_d = policy.size) !== null && _d !== void 0 ? _d : 0);
5231
+ }
5232
+ else {
5233
+ resolved = this.resolveFlexibleLabelLaneFromText(parentId);
5234
+ }
5235
+ return this.clampLayoutSize(resolved, minSize, maxSize);
5236
+ };
5237
+ AutoLayoutService.prototype.resolveFlexibleLabelLaneFromText = function (parentId) {
5238
+ var lane = 0;
5239
+ this.model.texts.forEach(function (text) {
5240
+ var _a, _b, _c;
5241
+ if (text.ownerId !== parentId)
5242
+ return;
5243
+ var offset = (_a = text.displayOffset) !== null && _a !== void 0 ? _a : { x: 0, y: 0 };
5244
+ var size = (_b = text.displayClipSize) !== null && _b !== void 0 ? _b : text.size;
5245
+ var height = (_c = size === null || size === void 0 ? void 0 : size.height) !== null && _c !== void 0 ? _c : 0;
5246
+ var bottom = text.position.y + offset.y + height;
5247
+ if (bottom > lane) {
5248
+ lane = bottom;
5249
+ }
5250
+ });
5251
+ return Math.max(0, lane);
5252
+ };
4719
5253
  AutoLayoutService.prototype.clampLayoutSize = function (value, min, max) {
4720
5254
  var minValue = min !== null && min !== void 0 ? min : 0;
4721
5255
  var maxValue = max !== null && max !== void 0 ? max : Number.POSITIVE_INFINITY;
@@ -4784,6 +5318,104 @@ var AutoLayoutService = /** @class */ (function () {
4784
5318
  return AutoLayoutService;
4785
5319
  }());
4786
5320
 
5321
+ var ZERO_POINT = { x: 0, y: 0 };
5322
+ var clonePoint = function (point) { return ({ x: point.x, y: point.y }); };
5323
+ var borderSideToNormal = function (side) {
5324
+ if (side === 'left')
5325
+ return { x: -1, y: 0 };
5326
+ if (side === 'right')
5327
+ return { x: 1, y: 0 };
5328
+ if (side === 'top')
5329
+ return { x: 0, y: -1 };
5330
+ return { x: 0, y: 1 };
5331
+ };
5332
+ var resolvePortGeometryPoints = function (port, attachMode) {
5333
+ var _a, _b, _c, _d, _e;
5334
+ var placementPoint = clonePoint((_a = port.placementPoint) !== null && _a !== void 0 ? _a : ZERO_POINT);
5335
+ var sharedLinkAttachPoint = clonePoint((_b = port.linkAttachPoint) !== null && _b !== void 0 ? _b : placementPoint);
5336
+ var effectiveLinkAttachPoint = clonePoint(attachMode === 'internal'
5337
+ ? (_c = port.internalLinkAttachPoint) !== null && _c !== void 0 ? _c : sharedLinkAttachPoint
5338
+ : (_d = port.externalLinkAttachPoint) !== null && _d !== void 0 ? _d : sharedLinkAttachPoint);
5339
+ var rotationPivot = clonePoint((_e = port.rotationPivot) !== null && _e !== void 0 ? _e : placementPoint);
5340
+ return {
5341
+ placementPoint: placementPoint,
5342
+ sharedLinkAttachPoint: sharedLinkAttachPoint,
5343
+ effectiveLinkAttachPoint: effectiveLinkAttachPoint,
5344
+ rotationPivot: rotationPivot,
5345
+ };
5346
+ };
5347
+ var resolvePortOrientationContext = function (options) {
5348
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
5349
+ var port = options.port, worldPlacement = options.worldPlacement, host = options.host, shapeRegistry = options.shapeRegistry, attachMode = options.attachMode;
5350
+ var portShape = port.shapeId ? shapeRegistry.get(port.shapeId) : undefined;
5351
+ var localRotation = ((_a = portShape === null || portShape === void 0 ? void 0 : portShape.baseRotation) !== null && _a !== void 0 ? _a : 0) +
5352
+ (typeof ((_b = port.style) === null || _b === void 0 ? void 0 : _b.rotation) === 'number' ? port.style.rotation : 0);
5353
+ var nodeAnchorPoint = port.anchorCenter && (portShape === null || portShape === void 0 ? void 0 : portShape.svgPath)
5354
+ ? {
5355
+ x: ((_f = (_d = (_c = portShape.svgSize) === null || _c === void 0 ? void 0 : _c.width) !== null && _d !== void 0 ? _d : (_e = port.size) === null || _e === void 0 ? void 0 : _e.width) !== null && _f !== void 0 ? _f : 0) / 2,
5356
+ y: ((_k = (_h = (_g = portShape.svgSize) === null || _g === void 0 ? void 0 : _g.height) !== null && _h !== void 0 ? _h : (_j = port.size) === null || _j === void 0 ? void 0 : _j.height) !== null && _k !== void 0 ? _k : 0) / 2,
5357
+ }
5358
+ : __assign({}, ZERO_POINT);
5359
+ if (port.moveMode !== 'border' || port.orientToHostBorder === false || !host) {
5360
+ return { localRotation: localRotation, rotation: 0, offset: __assign({}, ZERO_POINT), nodeAnchorPoint: nodeAnchorPoint };
5361
+ }
5362
+ var hostRect = {
5363
+ x: host.position.x,
5364
+ y: host.position.y,
5365
+ width: host.size.width,
5366
+ height: host.size.height,
5367
+ };
5368
+ var shape = shapeRegistry.get(host.shapeId);
5369
+ var side = (shape === null || shape === void 0 ? void 0 : shape.resolveBorderSide)
5370
+ ? shape.resolveBorderSide(worldPlacement, hostRect)
5371
+ : resolveBoundarySide(worldPlacement, hostRect, 'rect');
5372
+ var normal = (shape === null || shape === void 0 ? void 0 : shape.resolveBorderNormal)
5373
+ ? shape.resolveBorderNormal(worldPlacement, hostRect)
5374
+ : borderSideToNormal(side);
5375
+ var geometry = resolvePortGeometryPoints(port, attachMode);
5376
+ var hookContext = {
5377
+ side: side,
5378
+ normal: normal,
5379
+ hostRect: hostRect,
5380
+ attachMode: attachMode,
5381
+ effectiveLinkAttachPoint: geometry.effectiveLinkAttachPoint,
5382
+ placementPoint: geometry.placementPoint,
5383
+ rotationPivot: geometry.rotationPivot,
5384
+ portSize: port.size,
5385
+ };
5386
+ var hookResult = (_l = shape === null || shape === void 0 ? void 0 : shape.resolvePortBorderTransform) === null || _l === void 0 ? void 0 : _l.call(shape, hookContext);
5387
+ return {
5388
+ localRotation: localRotation,
5389
+ nodeAnchorPoint: nodeAnchorPoint,
5390
+ side: side,
5391
+ normal: normal,
5392
+ rotation: borderSideToInwardRotation(side) + ((_m = hookResult === null || hookResult === void 0 ? void 0 : hookResult.rotation) !== null && _m !== void 0 ? _m : 0),
5393
+ offset: (hookResult === null || hookResult === void 0 ? void 0 : hookResult.offset) ? clonePoint(hookResult.offset) : __assign({}, ZERO_POINT),
5394
+ };
5395
+ };
5396
+ var resolvePortWorldTransform = function (options) {
5397
+ var port = options.port, worldPlacement = options.worldPlacement, attachMode = options.attachMode, orientation = options.orientation;
5398
+ var geometry = resolvePortGeometryPoints(port, attachMode);
5399
+ var totalRotation = orientation.localRotation + orientation.rotation;
5400
+ var rotatedPlacement = rotatePoint({
5401
+ x: geometry.placementPoint.x - orientation.nodeAnchorPoint.x,
5402
+ y: geometry.placementPoint.y - orientation.nodeAnchorPoint.y,
5403
+ }, ZERO_POINT, totalRotation);
5404
+ var rotatedAttach = rotatePoint({
5405
+ x: geometry.effectiveLinkAttachPoint.x - orientation.nodeAnchorPoint.x,
5406
+ y: geometry.effectiveLinkAttachPoint.y - orientation.nodeAnchorPoint.y,
5407
+ }, ZERO_POINT, totalRotation);
5408
+ var nodePosition = {
5409
+ x: worldPlacement.x - rotatedPlacement.x + orientation.offset.x,
5410
+ y: worldPlacement.y - rotatedPlacement.y + orientation.offset.y,
5411
+ };
5412
+ var linkAttachWorld = {
5413
+ x: nodePosition.x + rotatedAttach.x,
5414
+ y: nodePosition.y + rotatedAttach.y,
5415
+ };
5416
+ return { nodePosition: nodePosition, linkAttachWorld: linkAttachWorld };
5417
+ };
5418
+
4787
5419
  var EDGE_TOLERANCE = 0.5;
4788
5420
  var LinkRoutingService = /** @class */ (function () {
4789
5421
  function LinkRoutingService(config) {
@@ -4808,8 +5440,9 @@ var LinkRoutingService = /** @class */ (function () {
4808
5440
  }
4809
5441
  if (updatedLinks.has(link.id))
4810
5442
  return;
4811
- var source = _this.model.getPortWorldPosition(link.sourcePortId);
4812
- var target = _this.model.getPortWorldPosition(link.targetPortId);
5443
+ var endpoints = _this.resolveLinkEndpoints(link);
5444
+ var source = endpoints.source;
5445
+ var target = endpoints.target;
4813
5446
  if (!source || !target)
4814
5447
  return;
4815
5448
  var points = _this.resolveLinkPointsForUpdate(link, source, target);
@@ -4826,8 +5459,9 @@ var LinkRoutingService = /** @class */ (function () {
4826
5459
  var _a;
4827
5460
  if (link.points.length > 0)
4828
5461
  return;
4829
- var source = _this.model.getPortWorldPosition(link.sourcePortId);
4830
- var target = _this.model.getPortWorldPosition(link.targetPortId);
5462
+ var endpoints = _this.resolveLinkEndpoints(link);
5463
+ var source = endpoints.source;
5464
+ var target = endpoints.target;
4831
5465
  if (!source || !target)
4832
5466
  return;
4833
5467
  var points = ((_a = link.routing) !== null && _a !== void 0 ? _a : 'auto') === 'manual'
@@ -4838,6 +5472,31 @@ var LinkRoutingService = /** @class */ (function () {
4838
5472
  });
4839
5473
  return patches;
4840
5474
  };
5475
+ LinkRoutingService.prototype.getPortLinkWorldPosition = function (portId, options) {
5476
+ var _a;
5477
+ var port = this.model.getPort(portId);
5478
+ if (!port)
5479
+ return null;
5480
+ var worldPlacement = this.model.getPortWorldPosition(portId);
5481
+ if (!worldPlacement)
5482
+ return null;
5483
+ var host = this.resolveHostForPort(portId);
5484
+ var attachMode = (_a = options === null || options === void 0 ? void 0 : options.attachMode) !== null && _a !== void 0 ? _a : this.resolveAttachModeForPorts(portId, options === null || options === void 0 ? void 0 : options.oppositePortId);
5485
+ var orientation = resolvePortOrientationContext({
5486
+ port: port.toData(),
5487
+ worldPlacement: worldPlacement,
5488
+ host: host,
5489
+ shapeRegistry: this.shapeRegistry,
5490
+ attachMode: attachMode,
5491
+ });
5492
+ var transformed = resolvePortWorldTransform({
5493
+ port: port.toData(),
5494
+ worldPlacement: worldPlacement,
5495
+ attachMode: attachMode,
5496
+ orientation: orientation,
5497
+ });
5498
+ return transformed.linkAttachWorld;
5499
+ };
4841
5500
  LinkRoutingService.prototype.computeAutoRoute = function (link, source, target) {
4842
5501
  var routed = this.router.route(source, target, this.buildRouteContext(link));
4843
5502
  if (!routed || routed.length < 2) {
@@ -4856,6 +5515,12 @@ var LinkRoutingService = /** @class */ (function () {
4856
5515
  }
4857
5516
  return this.computeAutoRoute(link, source, target);
4858
5517
  };
5518
+ LinkRoutingService.prototype.resolveLinkEndpoints = function (link) {
5519
+ return {
5520
+ source: this.getPortLinkWorldPosition(link.sourcePortId, { oppositePortId: link.targetPortId }),
5521
+ target: this.getPortLinkWorldPosition(link.targetPortId, { oppositePortId: link.sourcePortId }),
5522
+ };
5523
+ };
4859
5524
  LinkRoutingService.prototype.updateManualRoute = function (points, source, target) {
4860
5525
  if (points.length < 2) {
4861
5526
  return [__assign({}, source), __assign({}, target)];
@@ -4875,8 +5540,9 @@ var LinkRoutingService = /** @class */ (function () {
4875
5540
  var targetPort = this.model.getPort(link.targetPortId);
4876
5541
  var sourceElementId = sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.elementId;
4877
5542
  var targetElementId = targetPort === null || targetPort === void 0 ? void 0 : targetPort.elementId;
4878
- var sourcePosition = this.model.getPortWorldPosition(link.sourcePortId);
4879
- var targetPosition = this.model.getPortWorldPosition(link.targetPortId);
5543
+ var endpoints = this.resolveLinkEndpoints(link);
5544
+ var sourcePosition = endpoints.source;
5545
+ var targetPosition = endpoints.target;
4880
5546
  var sourceRect = sourceElementId ? this.getElementRect(sourceElementId) : null;
4881
5547
  var targetRect = targetElementId ? this.getElementRect(targetElementId) : null;
4882
5548
  var sourceEndpointGeometry = sourcePosition && sourceRect
@@ -5006,6 +5672,40 @@ var LinkRoutingService = /** @class */ (function () {
5006
5672
  height: element.size.height,
5007
5673
  };
5008
5674
  };
5675
+ LinkRoutingService.prototype.resolveHostForPort = function (portId) {
5676
+ var _a;
5677
+ var port = this.model.getPort(portId);
5678
+ if (!port)
5679
+ return null;
5680
+ var element = this.model.getElement(port.elementId);
5681
+ if (!element)
5682
+ return null;
5683
+ return {
5684
+ id: element.id,
5685
+ position: (_a = this.model.getElementWorldPosition(element.id)) !== null && _a !== void 0 ? _a : element.position,
5686
+ size: element.size,
5687
+ shapeId: element.shapeId,
5688
+ };
5689
+ };
5690
+ LinkRoutingService.prototype.resolveAttachModeForPorts = function (portId, oppositePortId) {
5691
+ var _a, _b;
5692
+ if (!oppositePortId)
5693
+ return 'external';
5694
+ var elementId = (_a = this.model.getPort(portId)) === null || _a === void 0 ? void 0 : _a.elementId;
5695
+ var oppositeElementId = (_b = this.model.getPort(oppositePortId)) === null || _b === void 0 ? void 0 : _b.elementId;
5696
+ if (!elementId || !oppositeElementId)
5697
+ return 'external';
5698
+ return this.hasAncestorRelation(elementId, oppositeElementId) ? 'internal' : 'external';
5699
+ };
5700
+ LinkRoutingService.prototype.hasAncestorRelation = function (sourceElementId, targetElementId) {
5701
+ if (sourceElementId === targetElementId)
5702
+ return false;
5703
+ var sourceChain = this.getAncestorChain(sourceElementId);
5704
+ if (sourceChain.includes(targetElementId))
5705
+ return true;
5706
+ var targetChain = this.getAncestorChain(targetElementId);
5707
+ return targetChain.includes(sourceElementId);
5708
+ };
5009
5709
  LinkRoutingService.prototype.getAncestorChain = function (elementId) {
5010
5710
  var _a;
5011
5711
  var chain = [];
@@ -5383,8 +6083,12 @@ var DiagramEngine = /** @class */ (function () {
5383
6083
  };
5384
6084
  DiagramEngine.prototype.addLink = function (link) {
5385
6085
  var _a;
5386
- var source = this.model.getPortWorldPosition(link.sourcePortId);
5387
- var target = this.model.getPortWorldPosition(link.targetPortId);
6086
+ var source = this.linkRoutingService.getPortLinkWorldPosition(link.sourcePortId, {
6087
+ oppositePortId: link.targetPortId,
6088
+ });
6089
+ var target = this.linkRoutingService.getPortLinkWorldPosition(link.targetPortId, {
6090
+ oppositePortId: link.sourcePortId,
6091
+ });
5388
6092
  var routing = (_a = link.routing) !== null && _a !== void 0 ? _a : 'auto';
5389
6093
  var points = link.points;
5390
6094
  if (source && target) {
@@ -5408,8 +6112,12 @@ var DiagramEngine = /** @class */ (function () {
5408
6112
  return;
5409
6113
  var update = { routing: mode };
5410
6114
  if (mode === 'auto') {
5411
- var source = this.model.getPortWorldPosition(link.sourcePortId);
5412
- var target = this.model.getPortWorldPosition(link.targetPortId);
6115
+ var source = this.linkRoutingService.getPortLinkWorldPosition(link.sourcePortId, {
6116
+ oppositePortId: link.targetPortId,
6117
+ });
6118
+ var target = this.linkRoutingService.getPortLinkWorldPosition(link.targetPortId, {
6119
+ oppositePortId: link.sourcePortId,
6120
+ });
5413
6121
  if (source && target) {
5414
6122
  update.points = this.computeAutoRoute(link, source, target);
5415
6123
  }
@@ -5443,8 +6151,12 @@ var DiagramEngine = /** @class */ (function () {
5443
6151
  var routing = (_a = link.routing) !== null && _a !== void 0 ? _a : 'auto';
5444
6152
  if (routing === 'manual' && !includeManual)
5445
6153
  return;
5446
- var source = _this.model.getPortWorldPosition(link.sourcePortId);
5447
- var target = _this.model.getPortWorldPosition(link.targetPortId);
6154
+ var source = _this.linkRoutingService.getPortLinkWorldPosition(link.sourcePortId, {
6155
+ oppositePortId: link.targetPortId,
6156
+ });
6157
+ var target = _this.linkRoutingService.getPortLinkWorldPosition(link.targetPortId, {
6158
+ oppositePortId: link.sourcePortId,
6159
+ });
5448
6160
  if (!source || !target)
5449
6161
  return;
5450
6162
  var points = _this.computeAutoRoute(link, source, target);
@@ -5468,8 +6180,10 @@ var DiagramEngine = /** @class */ (function () {
5468
6180
  this.emitChange(patches);
5469
6181
  };
5470
6182
  DiagramEngine.prototype.updateText = function (id, content) {
6183
+ var _this = this;
5471
6184
  var patches = this.commandQueue.run(createUpdateTextCommand(id, content), this.model);
5472
6185
  var text = this.model.getText(id);
6186
+ var layoutOwnerId = (text === null || text === void 0 ? void 0 : text.ownerId) && this.hasFlexibleLabelReservedSpace(text.ownerId) ? text.ownerId : null;
5473
6187
  if (text) {
5474
6188
  var resolved = this.resolveTextPresentation(text.toData());
5475
6189
  text.setSize(resolved.size);
@@ -5490,7 +6204,18 @@ var DiagramEngine = /** @class */ (function () {
5490
6204
  reason: 'content',
5491
6205
  });
5492
6206
  }
5493
- this.emitChange(patches);
6207
+ if (!layoutOwnerId) {
6208
+ this.emitChange(patches);
6209
+ return;
6210
+ }
6211
+ var allPatches = this.mutationPipeline.run({
6212
+ basePatches: patches,
6213
+ layoutSteps: [
6214
+ function () { return _this.applyLayoutForParent(layoutOwnerId); },
6215
+ function () { var _a, _b; return _this.applyLayoutCascade((_b = (_a = _this.model.getElement(layoutOwnerId)) === null || _a === void 0 ? void 0 : _a.parentId) !== null && _b !== void 0 ? _b : null); },
6216
+ ],
6217
+ });
6218
+ this.emitChange(allPatches);
5494
6219
  };
5495
6220
  DiagramEngine.prototype.moveTextTo = function (id, x, y) {
5496
6221
  var text = this.model.getText(id);
@@ -5593,6 +6318,9 @@ var DiagramEngine = /** @class */ (function () {
5593
6318
  DiagramEngine.prototype.getPortWorldPosition = function (id) {
5594
6319
  return this.model.getPortWorldPosition(id);
5595
6320
  };
6321
+ DiagramEngine.prototype.getPortLinkWorldPosition = function (id, options) {
6322
+ return this.linkRoutingService.getPortLinkWorldPosition(id, options);
6323
+ };
5596
6324
  DiagramEngine.prototype.getTextWorldPosition = function (id) {
5597
6325
  return this.model.getTextWorldPosition(id);
5598
6326
  };
@@ -6001,6 +6729,12 @@ var DiagramEngine = /** @class */ (function () {
6001
6729
  DiagramEngine.prototype.applyAllLayouts = function () {
6002
6730
  return this.autoLayoutService.applyAllLayouts();
6003
6731
  };
6732
+ DiagramEngine.prototype.hasFlexibleLabelReservedSpace = function (elementId) {
6733
+ var _a, _b, _c;
6734
+ var element = this.model.getElement(elementId);
6735
+ var mode = (_c = (_b = (_a = element === null || element === void 0 ? void 0 : element.layout) === null || _a === void 0 ? void 0 : _a.labelReservedSpace) === null || _b === void 0 ? void 0 : _b.mode) !== null && _c !== void 0 ? _c : 'none';
6736
+ return mode === 'flexible';
6737
+ };
6004
6738
  DiagramEngine.prototype.updateLinksForPorts = function (portIds) {
6005
6739
  return this.linkRoutingService.updateLinksForPorts(portIds);
6006
6740
  };
@@ -6342,7 +7076,8 @@ var KonvaNodeFactory = /** @class */ (function () {
6342
7076
  KonvaNodeFactory.prototype.createDrawNode = function (model, shape, config) {
6343
7077
  var _a, _b;
6344
7078
  var style = model.style;
6345
- var attrs = __assign(__assign({ id: model.id, x: model.position.x, y: model.position.y, name: config.name }, (style !== null && style !== void 0 ? style : {})), { __shapeDraw: true, __model: model, sceneFunc: function (ctx, node) {
7079
+ var _c = this.resolveShapeRotation(shape, style), resolvedStyle = _c.style, rotation = _c.rotation;
7080
+ var attrs = __assign(__assign({ id: model.id, x: model.position.x, y: model.position.y, name: config.name }, (resolvedStyle !== null && resolvedStyle !== void 0 ? resolvedStyle : {})), { __shapeDraw: true, __model: model, rotation: rotation, sceneFunc: function (ctx, node) {
6346
7081
  var _a, _b, _c;
6347
7082
  var resolvedModel = (_b = (node.getAttr ? node.getAttr('__model') : (_a = node.attrs) === null || _a === void 0 ? void 0 : _a.__model)) !== null && _b !== void 0 ? _b : model;
6348
7083
  (_c = shape.draw) === null || _c === void 0 ? void 0 : _c.call(shape, { ctx: ctx, model: resolvedModel });
@@ -6358,6 +7093,7 @@ var KonvaNodeFactory = /** @class */ (function () {
6358
7093
  KonvaNodeFactory.prototype.createSvgPathNode = function (model, shape, config) {
6359
7094
  var _a, _b, _c, _d, _e, _f, _g;
6360
7095
  var style = model.style;
7096
+ var _h = this.resolveShapeRotation(shape, style), resolvedStyle = _h.style, rotation = _h.rotation;
6361
7097
  var sizeUpdater = function (_a) {
6362
7098
  var _b, _c;
6363
7099
  var nextSize = _a.size, anchorCenter = _a.anchorCenter, updateOffsetX = _a.updateOffsetX, updateOffsetY = _a.updateOffsetY, getNodeAttr = _a.getNodeAttr;
@@ -6379,7 +7115,7 @@ var KonvaNodeFactory = /** @class */ (function () {
6379
7115
  return attrs;
6380
7116
  };
6381
7117
  var size = (_a = config.size) !== null && _a !== void 0 ? _a : { width: 0, height: 0 };
6382
- var node = new this.konva.Path(__assign(__assign({ id: model.id, x: model.position.x, y: model.position.y, data: shape.svgPath, name: config.name }, (style !== null && style !== void 0 ? style : {})), { shapeKind: 'svg-path', __autoOffsetX: true, __autoOffsetY: true }));
7118
+ var node = new this.konva.Path(__assign(__assign({ id: model.id, x: model.position.x, y: model.position.y, rotation: rotation, data: shape.svgPath, name: config.name }, (resolvedStyle !== null && resolvedStyle !== void 0 ? resolvedStyle : {})), { shapeKind: 'svg-path', __autoOffsetX: true, __autoOffsetY: true }));
6383
7119
  var rect = node.getClientRect ? node.getClientRect({ skipTransform: true }) : null;
6384
7120
  var baseWidth = (_d = (_c = (_b = shape.svgSize) === null || _b === void 0 ? void 0 : _b.width) !== null && _c !== void 0 ? _c : rect === null || rect === void 0 ? void 0 : rect.width) !== null && _d !== void 0 ? _d : size.width;
6385
7121
  var baseHeight = (_g = (_f = (_e = shape.svgSize) === null || _e === void 0 ? void 0 : _e.height) !== null && _f !== void 0 ? _f : rect === null || rect === void 0 ? void 0 : rect.height) !== null && _g !== void 0 ? _g : size.height;
@@ -6396,12 +7132,26 @@ var KonvaNodeFactory = /** @class */ (function () {
6396
7132
  }
6397
7133
  return node;
6398
7134
  };
6399
- KonvaNodeFactory.prototype.applyShapeBehaviorAttrs = function (node, shape) {
6400
- if (!node.setAttrs || !shape.sizeUpdater)
6401
- return;
6402
- node.setAttrs({
6403
- __sizeUpdater: shape.sizeUpdater,
6404
- });
7135
+ KonvaNodeFactory.prototype.resolveShapeRotation = function (shape, style) {
7136
+ var _a, _b;
7137
+ if (!style) {
7138
+ return {
7139
+ style: undefined,
7140
+ rotation: (_a = shape.baseRotation) !== null && _a !== void 0 ? _a : 0,
7141
+ };
7142
+ }
7143
+ var rotation = style.rotation, rest = __rest(style, ["rotation"]);
7144
+ return {
7145
+ style: rest,
7146
+ rotation: ((_b = shape.baseRotation) !== null && _b !== void 0 ? _b : 0) + (typeof rotation === 'number' ? rotation : 0),
7147
+ };
7148
+ };
7149
+ KonvaNodeFactory.prototype.applyShapeBehaviorAttrs = function (node, shape) {
7150
+ if (!node.setAttrs || !shape.sizeUpdater)
7151
+ return;
7152
+ node.setAttrs({
7153
+ __sizeUpdater: shape.sizeUpdater,
7154
+ });
6405
7155
  };
6406
7156
  return KonvaNodeFactory;
6407
7157
  }());
@@ -6596,7 +7346,7 @@ var KonvaRenderer = /** @class */ (function () {
6596
7346
  this.drawOverlays();
6597
7347
  };
6598
7348
  KonvaRenderer.prototype.renderPortPlaceholder = function (port, hostElement) {
6599
- var _a, _b, _c, _d;
7349
+ var _a, _b, _c, _d, _e, _f;
6600
7350
  var shapeId = port.shapeId;
6601
7351
  if (!this.tempPortNode || this.tempPortShapeId !== shapeId) {
6602
7352
  (_b = (_a = this.tempPortNode) === null || _a === void 0 ? void 0 : _a.destroy) === null || _b === void 0 ? void 0 : _b.call(_a);
@@ -6615,6 +7365,9 @@ var KonvaRenderer = /** @class */ (function () {
6615
7365
  }
6616
7366
  }
6617
7367
  if (this.tempPortNode) {
7368
+ this.updateSize(this.tempPortNode, (_e = port.size) !== null && _e !== void 0 ? _e : { width: 8, height: 8 }, {
7369
+ anchorCenter: (_f = port.anchorCenter) !== null && _f !== void 0 ? _f : true,
7370
+ });
6618
7371
  this.applyPortOrientation(this.tempPortNode, port, port.position, undefined, hostElement);
6619
7372
  }
6620
7373
  this.drawOverlays();
@@ -6874,7 +7627,7 @@ var KonvaRenderer = /** @class */ (function () {
6874
7627
  var _this = this;
6875
7628
  var ports = Array.from(model.ports.values());
6876
7629
  ports.forEach(function (port) {
6877
- var _a, _b, _c;
7630
+ var _a, _b, _c, _d, _e;
6878
7631
  var data = port.toData();
6879
7632
  var node = _this.portNodes.get(port.id);
6880
7633
  if (!node) {
@@ -6886,6 +7639,7 @@ var KonvaRenderer = /** @class */ (function () {
6886
7639
  node.setAttrs({ __model: data });
6887
7640
  }
6888
7641
  var position = (_c = model.getPortWorldPosition(port.id)) !== null && _c !== void 0 ? _c : port.position;
7642
+ _this.updateSize(node, (_d = data.size) !== null && _d !== void 0 ? _d : { width: 8, height: 8 }, { anchorCenter: (_e = data.anchorCenter) !== null && _e !== void 0 ? _e : true });
6889
7643
  _this.updatePosition(node, position);
6890
7644
  _this.applyPortOrientation(node, data, position, model);
6891
7645
  });
@@ -6980,27 +7734,22 @@ var KonvaRenderer = /** @class */ (function () {
6980
7734
  if (this.getNodeAttr(node, '__baseRotation') === undefined) {
6981
7735
  node.setAttrs({ __baseRotation: baseRotation });
6982
7736
  }
6983
- var orientedRotation = this.resolvePortBorderRotation(port, worldPosition, model, hostElementOverride);
6984
- node.setAttrs({ rotation: baseRotation + (orientedRotation !== null && orientedRotation !== void 0 ? orientedRotation : 0) });
6985
- };
6986
- KonvaRenderer.prototype.resolvePortBorderRotation = function (port, worldPosition, model, hostElementOverride) {
6987
- if (port.moveMode !== 'border' || port.orientToHostBorder === false) {
6988
- return null;
6989
- }
6990
7737
  var host = hostElementOverride !== null && hostElementOverride !== void 0 ? hostElementOverride : this.resolveHostElement(port.elementId, model);
6991
- if (!host)
6992
- return null;
6993
- var hostRect = {
6994
- x: host.position.x,
6995
- y: host.position.y,
6996
- width: host.size.width,
6997
- height: host.size.height,
6998
- };
6999
- var shape = this.shapeRegistry.get(host.shapeId);
7000
- var side = (shape === null || shape === void 0 ? void 0 : shape.resolveBorderSide)
7001
- ? shape.resolveBorderSide(worldPosition, hostRect)
7002
- : resolveBoundarySide(worldPosition, hostRect, 'rect');
7003
- return borderSideToInwardRotation(side);
7738
+ var orientation = resolvePortOrientationContext({
7739
+ port: port,
7740
+ worldPlacement: worldPosition,
7741
+ host: host,
7742
+ shapeRegistry: this.shapeRegistry,
7743
+ attachMode: 'external',
7744
+ });
7745
+ var transform = resolvePortWorldTransform({
7746
+ port: port,
7747
+ worldPlacement: worldPosition,
7748
+ attachMode: 'external',
7749
+ orientation: orientation,
7750
+ });
7751
+ this.updatePosition(node, transform.nodePosition);
7752
+ node.setAttrs({ rotation: baseRotation + orientation.rotation });
7004
7753
  };
7005
7754
  KonvaRenderer.prototype.resolveHostElement = function (elementId, model) {
7006
7755
  var _a;
@@ -7091,6 +7840,7 @@ var KonvaInteraction = /** @class */ (function () {
7091
7840
  if (config === void 0) { config = {}; }
7092
7841
  var _a, _b;
7093
7842
  this.linkDragContext = null;
7843
+ this.programmaticLinkSession = null;
7094
7844
  this.bound = false;
7095
7845
  this.handlers = [];
7096
7846
  this.windowHandlers = [];
@@ -7105,6 +7855,8 @@ var KonvaInteraction = /** @class */ (function () {
7105
7855
  this.textEditor = null;
7106
7856
  this.dragThreshold = 4;
7107
7857
  this.panSpeed = 0.5;
7858
+ this.occupiedVertexTolerance = 2;
7859
+ this.emittingElementLinkEnded = false;
7108
7860
  this.engine = engine;
7109
7861
  this.stage = config.stage;
7110
7862
  this.hitTester = (_a = config.hitTester) !== null && _a !== void 0 ? _a : new KonvaHitTester();
@@ -7125,6 +7877,121 @@ var KonvaInteraction = /** @class */ (function () {
7125
7877
  this.updateShapeHoverControl(this.lastPointerPosition);
7126
7878
  }
7127
7879
  };
7880
+ KonvaInteraction.prototype.startLinkFromPort = function (sourcePortId, pointer) {
7881
+ var _a, _b, _c;
7882
+ var sourceElementId = this.engine.getPortElementId(sourcePortId);
7883
+ if (!sourceElementId)
7884
+ return;
7885
+ var sourcePoint = (_a = this.engine.getPortLinkWorldPosition(sourcePortId)) !== null && _a !== void 0 ? _a : this.engine.getPortWorldPosition(sourcePortId);
7886
+ if (!sourcePoint)
7887
+ return;
7888
+ if (((_b = this.dragState) === null || _b === void 0 ? void 0 : _b.mode) === 'link-drag' || this.programmaticLinkSession) {
7889
+ this.cancelLink();
7890
+ }
7891
+ if (this.dragState &&
7892
+ this.dragState.mode !== 'shape-control-drag' &&
7893
+ this.dragState.mode !== 'link-drag') {
7894
+ return;
7895
+ }
7896
+ var start = __assign({}, sourcePoint);
7897
+ if (((_c = this.dragState) === null || _c === void 0 ? void 0 : _c.mode) === 'shape-control-drag') {
7898
+ this.programmaticLinkSession = {
7899
+ sourcePortId: sourcePortId,
7900
+ sourceElementId: sourceElementId,
7901
+ start: start,
7902
+ current: start,
7903
+ };
7904
+ }
7905
+ else {
7906
+ this.dragState = {
7907
+ mode: 'link-drag',
7908
+ sourcePortId: sourcePortId,
7909
+ sourceElementId: sourceElementId,
7910
+ start: start,
7911
+ current: start,
7912
+ isMulti: false,
7913
+ hasMoved: false,
7914
+ completionBehavior: 'explicit',
7915
+ };
7916
+ }
7917
+ this.linkDragContext = { sourcePortId: sourcePortId, sourceElementId: sourceElementId };
7918
+ this.engine.emitElementLinkStarted({ sourcePortId: sourcePortId, sourceElementId: sourceElementId, startWorld: __assign({}, start) });
7919
+ this.setCursor('crosshair');
7920
+ if (pointer) {
7921
+ this.updateLinkPreview(pointer);
7922
+ }
7923
+ };
7924
+ KonvaInteraction.prototype.updateLinkPreview = function (pointer) {
7925
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
7926
+ if (this.programmaticLinkSession) {
7927
+ this.programmaticLinkSession.current = __assign({}, pointer);
7928
+ }
7929
+ else if (((_a = this.dragState) === null || _a === void 0 ? void 0 : _a.mode) === 'link-drag') {
7930
+ this.dragState.current = __assign({}, pointer);
7931
+ this.dragState.hasMoved = true;
7932
+ }
7933
+ else {
7934
+ return;
7935
+ }
7936
+ var sourcePortId = (_c = (_b = this.programmaticLinkSession) === null || _b === void 0 ? void 0 : _b.sourcePortId) !== null && _c !== void 0 ? _c : (((_d = this.dragState) === null || _d === void 0 ? void 0 : _d.mode) === 'link-drag' ? this.dragState.sourcePortId : null);
7937
+ if (!sourcePortId)
7938
+ return;
7939
+ var source = this.resolveLinkPreviewSource(sourcePortId, pointer);
7940
+ if (source) {
7941
+ (_e = this.renderer) === null || _e === void 0 ? void 0 : _e.renderTempLink([source, pointer]);
7942
+ }
7943
+ var hit = this.resolveHit(pointer);
7944
+ if ((hit === null || hit === void 0 ? void 0 : hit.type) === 'element') {
7945
+ var placeholder = this.createPlaceholderPort(hit.id, pointer, sourcePortId);
7946
+ if (placeholder) {
7947
+ var hostElement = this.getElementById(hit.id);
7948
+ if (hostElement) {
7949
+ var hostWorld = (_f = this.engine.getElementWorldPosition(hit.id)) !== null && _f !== void 0 ? _f : hostElement.position;
7950
+ (_h = (_g = this.renderer) === null || _g === void 0 ? void 0 : _g.renderPortPlaceholder) === null || _h === void 0 ? void 0 : _h.call(_g, placeholder, { id: hostElement.id, position: hostWorld, size: hostElement.size, shapeId: hostElement.shapeId });
7951
+ }
7952
+ else {
7953
+ (_k = (_j = this.renderer) === null || _j === void 0 ? void 0 : _j.renderPortPlaceholder) === null || _k === void 0 ? void 0 : _k.call(_j, placeholder);
7954
+ }
7955
+ }
7956
+ return;
7957
+ }
7958
+ (_m = (_l = this.renderer) === null || _l === void 0 ? void 0 : _l.clearPortPlaceholder) === null || _m === void 0 ? void 0 : _m.call(_l);
7959
+ };
7960
+ KonvaInteraction.prototype.completeLinkToPort = function (targetPortId) {
7961
+ var _a, _b;
7962
+ var session = this.resolveActiveLinkSession();
7963
+ if (!session)
7964
+ return;
7965
+ var completion = this.tryCreateLinkToPort(session.sourcePortId, session.sourceElementId, targetPortId);
7966
+ this.finishLinkDragSession({
7967
+ sourcePortId: session.sourcePortId,
7968
+ sourceElementId: session.sourceElementId,
7969
+ createdLinkId: (_a = completion.createdLinkId) !== null && _a !== void 0 ? _a : undefined,
7970
+ targetPortId: completion.targetPortId,
7971
+ targetElementId: (_b = completion.targetElementId) !== null && _b !== void 0 ? _b : undefined,
7972
+ cancelled: completion.createdLinkId === null,
7973
+ fromProgrammatic: session.fromProgrammatic,
7974
+ });
7975
+ };
7976
+ KonvaInteraction.prototype.completeLinkToElement = function (targetElementId, pointer) {
7977
+ var _a, _b, _c;
7978
+ var session = this.resolveActiveLinkSession();
7979
+ if (!session)
7980
+ return;
7981
+ var completion = this.tryCreateLinkToElement(session.sourcePortId, session.sourceElementId, targetElementId, pointer);
7982
+ this.finishLinkDragSession({
7983
+ sourcePortId: session.sourcePortId,
7984
+ sourceElementId: session.sourceElementId,
7985
+ createdLinkId: (_a = completion.createdLinkId) !== null && _a !== void 0 ? _a : undefined,
7986
+ targetPortId: (_b = completion.targetPortId) !== null && _b !== void 0 ? _b : undefined,
7987
+ targetElementId: (_c = completion.targetElementId) !== null && _c !== void 0 ? _c : undefined,
7988
+ cancelled: completion.createdLinkId === null,
7989
+ fromProgrammatic: session.fromProgrammatic,
7990
+ });
7991
+ };
7992
+ KonvaInteraction.prototype.cancelLink = function () {
7993
+ this.cancelLinkDrag();
7994
+ };
7128
7995
  KonvaInteraction.prototype.buildPointerInfo = function (world, nativeEvent) {
7129
7996
  var evt = nativeEvent !== null && nativeEvent !== void 0 ? nativeEvent : undefined;
7130
7997
  var client = evt ? { x: evt.clientX, y: evt.clientY } : { x: world.x, y: world.y };
@@ -7252,6 +8119,7 @@ var KonvaInteraction = /** @class */ (function () {
7252
8119
  current: point,
7253
8120
  isMulti: isMulti,
7254
8121
  hasMoved: false,
8122
+ completionBehavior: 'hover-or-release',
7255
8123
  };
7256
8124
  _this.linkDragContext = { sourcePortId: hit.id, sourceElementId: elementId };
7257
8125
  _this.engine.emitElementLinkStarted({ sourcePortId: hit.id, sourceElementId: elementId, startWorld: __assign({}, point) });
@@ -7326,22 +8194,29 @@ var KonvaInteraction = /** @class */ (function () {
7326
8194
  }
7327
8195
  _this.clearActiveShapeHoverControl();
7328
8196
  if (_this.dragState.mode === 'shape-control-drag') {
7329
- var delta = { x: point.x - _this.dragState.start.x, y: point.y - _this.dragState.start.y };
8197
+ var dragState = _this.dragState;
8198
+ var delta = { x: point.x - dragState.start.x, y: point.y - dragState.start.y };
7330
8199
  var moved = Math.hypot(delta.x, delta.y) >= _this.dragThreshold;
7331
- if (!_this.dragState.hasMoved && moved) {
7332
- _this.dragState.hasMoved = true;
7333
- _this.emitShapeHoverControlInteraction('drag-start', _this.dragState.control, point, nativeEvent, {
7334
- sessionId: _this.dragState.sessionId,
7335
- startPointer: _this.dragState.startPointer,
8200
+ if (!dragState.hasMoved && moved) {
8201
+ dragState.hasMoved = true;
8202
+ _this.emitShapeHoverControlInteraction('drag-start', dragState.control, point, nativeEvent, {
8203
+ sessionId: dragState.sessionId,
8204
+ startPointer: dragState.startPointer,
7336
8205
  delta: delta,
7337
8206
  });
8207
+ if (_this.dragState !== dragState) {
8208
+ return;
8209
+ }
7338
8210
  }
7339
- if (_this.dragState.hasMoved) {
7340
- _this.emitShapeHoverControlInteraction('drag-move', _this.dragState.control, point, nativeEvent, {
7341
- sessionId: _this.dragState.sessionId,
7342
- startPointer: _this.dragState.startPointer,
8211
+ if (dragState.hasMoved) {
8212
+ _this.emitShapeHoverControlInteraction('drag-move', dragState.control, point, nativeEvent, {
8213
+ sessionId: dragState.sessionId,
8214
+ startPointer: dragState.startPointer,
7343
8215
  delta: delta,
7344
8216
  });
8217
+ if (_this.dragState !== dragState) {
8218
+ return;
8219
+ }
7345
8220
  }
7346
8221
  }
7347
8222
  else if (_this.dragState.mode === 'move') {
@@ -7379,12 +8254,14 @@ var KonvaInteraction = /** @class */ (function () {
7379
8254
  _this.dragState.hasMoved = _this.dragState.hasMoved || moved;
7380
8255
  _this.dragState.current = point;
7381
8256
  if (_this.dragState.hasMoved) {
7382
- var source = _this.engine.getPortWorldPosition(_this.dragState.sourcePortId);
8257
+ var hit = _this.resolveHit(point);
8258
+ var source = _this.resolveLinkPreviewSource(_this.dragState.sourcePortId, point, hit);
7383
8259
  if (source) {
7384
8260
  (_b = _this.renderer) === null || _b === void 0 ? void 0 : _b.renderTempLink([source, point]);
7385
8261
  }
7386
- var hit = _this.resolveHit(point);
7387
- if ((hit === null || hit === void 0 ? void 0 : hit.type) === 'port' && hit.id !== linkDrag.sourcePortId) {
8262
+ if (linkDrag.completionBehavior === 'hover-or-release' &&
8263
+ (hit === null || hit === void 0 ? void 0 : hit.type) === 'port' &&
8264
+ hit.id !== linkDrag.sourcePortId) {
7388
8265
  var completion = _this.tryCreateLinkToPort(linkDrag.sourcePortId, linkDrag.sourceElementId, hit.id);
7389
8266
  if (completion.createdLinkId) {
7390
8267
  var pointerInfo = _this.buildPointerInfo(point, nativeEvent);
@@ -7393,7 +8270,9 @@ var KonvaInteraction = /** @class */ (function () {
7393
8270
  elementId: linkDrag.sourceElementId,
7394
8271
  pointer: pointerInfo,
7395
8272
  });
7396
- _this.engine.emitElementLinkEnded({
8273
+ _this.dragState = null;
8274
+ _this.linkDragContext = null;
8275
+ _this.emitElementLinkEndedSafely({
7397
8276
  sourcePortId: linkDrag.sourcePortId,
7398
8277
  sourceElementId: linkDrag.sourceElementId,
7399
8278
  linkId: completion.createdLinkId,
@@ -7403,8 +8282,6 @@ var KonvaInteraction = /** @class */ (function () {
7403
8282
  });
7404
8283
  (_d = _this.renderer) === null || _d === void 0 ? void 0 : _d.clearTempLink();
7405
8284
  (_f = (_e = _this.renderer) === null || _e === void 0 ? void 0 : _e.clearPortPlaceholder) === null || _f === void 0 ? void 0 : _f.call(_e);
7406
- _this.dragState = null;
7407
- _this.linkDragContext = null;
7408
8285
  _this.setCursor('default');
7409
8286
  return;
7410
8287
  }
@@ -7495,6 +8372,7 @@ var KonvaInteraction = /** @class */ (function () {
7495
8372
  var pointerPoint = _this.getPointerPosition();
7496
8373
  if (((_a = _this.dragState) === null || _a === void 0 ? void 0 : _a.mode) === 'shape-control-drag') {
7497
8374
  var dragState = _this.dragState;
8375
+ _this.dragState = null;
7498
8376
  var point = pointerPoint !== null && pointerPoint !== void 0 ? pointerPoint : dragState.start;
7499
8377
  var delta = { x: point.x - dragState.start.x, y: point.y - dragState.start.y };
7500
8378
  var dragContext = {
@@ -7509,7 +8387,6 @@ var KonvaInteraction = /** @class */ (function () {
7509
8387
  _this.emitShapeHoverControlInteraction('click', dragState.control, point, nativeEvent);
7510
8388
  _this.emitLegacyShapeHoverControlActivation(dragState.control, point, nativeEvent);
7511
8389
  }
7512
- _this.dragState = null;
7513
8390
  if (pointerPoint) {
7514
8391
  _this.lastPointerPosition = __assign({}, pointerPoint);
7515
8392
  _this.updateShapeHoverControl(pointerPoint);
@@ -7522,46 +8399,45 @@ var KonvaInteraction = /** @class */ (function () {
7522
8399
  return;
7523
8400
  }
7524
8401
  if (((_b = _this.dragState) === null || _b === void 0 ? void 0 : _b.mode) === 'link-drag') {
8402
+ var linkState = _this.dragState;
7525
8403
  var point = pointerPoint;
7526
- var pointerInfo = _this.buildPointerInfo(point !== null && point !== void 0 ? point : _this.dragState.start, nativeEvent);
8404
+ var pointerInfo = _this.buildPointerInfo(point !== null && point !== void 0 ? point : linkState.start, nativeEvent);
7527
8405
  var sourcePortPayload = {
7528
- portId: _this.dragState.sourcePortId,
7529
- elementId: _this.dragState.sourceElementId,
8406
+ portId: linkState.sourcePortId,
8407
+ elementId: linkState.sourceElementId,
7530
8408
  pointer: pointerInfo,
7531
8409
  };
7532
8410
  _this.engine.emitPortMouseUp(sourcePortPayload);
7533
8411
  var createdLinkId = null;
7534
8412
  var targetPortId = null;
7535
8413
  var targetElementId = null;
7536
- if (!_this.dragState.hasMoved) {
7537
- _this.handleSelection({ id: _this.dragState.sourcePortId, type: 'port' }, _this.dragState.isMulti);
8414
+ if (!linkState.hasMoved) {
8415
+ _this.handleSelection({ id: linkState.sourcePortId, type: 'port' }, linkState.isMulti);
7538
8416
  }
7539
8417
  else {
7540
8418
  var hit = point ? _this.resolveHit(point) : null;
7541
- if (hit && hit.type === 'port' && hit.id !== _this.dragState.sourcePortId) {
7542
- var completion = _this.tryCreateLinkToPort(_this.dragState.sourcePortId, _this.dragState.sourceElementId, hit.id);
8419
+ if (linkState.completionBehavior === 'hover-or-release' &&
8420
+ hit &&
8421
+ hit.type === 'port' &&
8422
+ hit.id !== linkState.sourcePortId) {
8423
+ var completion = _this.tryCreateLinkToPort(linkState.sourcePortId, linkState.sourceElementId, hit.id);
7543
8424
  createdLinkId = completion.createdLinkId;
7544
8425
  targetPortId = completion.targetPortId;
7545
8426
  targetElementId = completion.targetElementId;
7546
8427
  }
7547
- else if (hit && hit.type === 'element' && point) {
8428
+ else if (linkState.completionBehavior === 'hover-or-release' && hit && hit.type === 'element' && point) {
7548
8429
  targetElementId = hit.id;
7549
- var createdPortId = _this.createPortForLink(hit.id, point, _this.dragState.sourcePortId);
7550
- if (createdPortId) {
7551
- targetPortId = createdPortId;
7552
- createdLinkId = createId();
7553
- _this.engine.addLink({
7554
- id: createdLinkId,
7555
- sourcePortId: _this.dragState.sourcePortId,
7556
- targetPortId: createdPortId,
7557
- points: [],
7558
- });
7559
- }
8430
+ var completion = _this.tryCreateLinkToElement(linkState.sourcePortId, linkState.sourceElementId, hit.id, point);
8431
+ targetPortId = completion.targetPortId;
8432
+ createdLinkId = completion.createdLinkId;
8433
+ targetElementId = completion.targetElementId;
7560
8434
  }
7561
8435
  }
7562
- _this.engine.emitElementLinkEnded({
7563
- sourcePortId: _this.dragState.sourcePortId,
7564
- sourceElementId: _this.dragState.sourceElementId,
8436
+ _this.dragState = null;
8437
+ _this.linkDragContext = null;
8438
+ _this.emitElementLinkEndedSafely({
8439
+ sourcePortId: linkState.sourcePortId,
8440
+ sourceElementId: linkState.sourceElementId,
7565
8441
  linkId: createdLinkId !== null && createdLinkId !== void 0 ? createdLinkId : undefined,
7566
8442
  targetPortId: targetPortId !== null && targetPortId !== void 0 ? targetPortId : undefined,
7567
8443
  targetElementId: targetElementId !== null && targetElementId !== void 0 ? targetElementId : undefined,
@@ -7795,7 +8671,7 @@ var KonvaInteraction = /** @class */ (function () {
7795
8671
  addHit(this.findElementHit(point));
7796
8672
  if (hits.length === 0)
7797
8673
  return null;
7798
- var priority = ['resize-handle', 'link-handle', 'shape-hover-control', 'port', 'link', 'text', 'element'];
8674
+ var priority = ['resize-handle', 'link-handle', 'port', 'shape-hover-control', 'link', 'text', 'element'];
7799
8675
  var _loop_1 = function (i) {
7800
8676
  var candidates = hits.filter(function (hit) { return hit.type === priority[i]; });
7801
8677
  if (candidates.length === 0)
@@ -8241,10 +9117,10 @@ var KonvaInteraction = /** @class */ (function () {
8241
9117
  for (var index = 0; index < controls.length; index += 1) {
8242
9118
  var control = controls[index];
8243
9119
  var targetHoverCandidate = control.visibilityTriggers.includes('target-hover')
8244
- ? this.resolveTargetHoverCandidate(point, transformed, ellipseMidPoints, control)
9120
+ ? this.resolveTargetHoverCandidate(point, transformed, ellipseMidPoints, control, elementId)
8245
9121
  : null;
8246
9122
  var elementHoverCandidate = control.visibilityTriggers.includes('element-hover')
8247
- ? this.resolveElementHoverCandidate(point, transformed, ellipseMidPoints, control)
9123
+ ? this.resolveElementHoverCandidate(point, transformed, ellipseMidPoints, control, elementId)
8248
9124
  : null;
8249
9125
  var candidate = targetHoverCandidate !== null && targetHoverCandidate !== void 0 ? targetHoverCandidate : elementHoverCandidate;
8250
9126
  if (!candidate)
@@ -8260,7 +9136,7 @@ var KonvaInteraction = /** @class */ (function () {
8260
9136
  }
8261
9137
  return null;
8262
9138
  };
8263
- KonvaInteraction.prototype.resolveTargetHoverCandidate = function (point, geometry, ellipseMidPoints, control) {
9139
+ KonvaInteraction.prototype.resolveTargetHoverCandidate = function (point, geometry, ellipseMidPoints, control, elementId) {
8264
9140
  if (control.targetKind === 'ellipse-midpoint') {
8265
9141
  var targets = this.resolveEligibleEllipseMidPoints(control, ellipseMidPoints);
8266
9142
  if (!targets.length || control.tolerance <= 0)
@@ -8281,7 +9157,7 @@ var KonvaInteraction = /** @class */ (function () {
8281
9157
  }
8282
9158
  if (!geometry)
8283
9159
  return null;
8284
- var indices = this.resolveEligibleTargetIndices(control, geometry);
9160
+ var indices = this.resolveEligibleTargetIndices(control, geometry, elementId);
8285
9161
  if (!indices.length || control.tolerance <= 0)
8286
9162
  return null;
8287
9163
  if (control.targetKind === 'vertex') {
@@ -8331,7 +9207,7 @@ var KonvaInteraction = /** @class */ (function () {
8331
9207
  }
8332
9208
  return best ? { targetIndex: best.targetIndex, target: best.target } : null;
8333
9209
  };
8334
- KonvaInteraction.prototype.resolveElementHoverCandidate = function (point, geometry, ellipseMidPoints, control) {
9210
+ KonvaInteraction.prototype.resolveElementHoverCandidate = function (point, geometry, ellipseMidPoints, control, elementId) {
8335
9211
  if (control.targetKind === 'ellipse-midpoint') {
8336
9212
  var targets = this.resolveEligibleEllipseMidPoints(control, ellipseMidPoints);
8337
9213
  if (!targets.length)
@@ -8350,7 +9226,7 @@ var KonvaInteraction = /** @class */ (function () {
8350
9226
  }
8351
9227
  if (!geometry)
8352
9228
  return null;
8353
- var indices = this.resolveEligibleTargetIndices(control, geometry);
9229
+ var indices = this.resolveEligibleTargetIndices(control, geometry, elementId);
8354
9230
  if (!indices.length)
8355
9231
  return null;
8356
9232
  if (control.targetKind === 'vertex') {
@@ -8394,17 +9270,46 @@ var KonvaInteraction = /** @class */ (function () {
8394
9270
  }
8395
9271
  return best ? { targetIndex: best.targetIndex, target: best.target } : null;
8396
9272
  };
8397
- KonvaInteraction.prototype.resolveEligibleTargetIndices = function (control, geometry) {
9273
+ KonvaInteraction.prototype.resolveEligibleTargetIndices = function (control, geometry, elementId) {
8398
9274
  var _a;
8399
9275
  var source = control.targetKind === 'vertex' ? geometry.vertices : geometry.edges;
8400
9276
  if (!source.length)
8401
9277
  return [];
9278
+ var occupiedVertexIndices = this.resolveOccupiedVertexIndices(elementId, geometry);
8402
9279
  if (control.allowAllTargets === true) {
8403
- return Array.from({ length: source.length }, function (_, index) { return index; });
9280
+ return Array.from({ length: source.length }, function (_, index) { return index; }).filter(function (index) {
9281
+ return control.targetKind === 'vertex' ? !occupiedVertexIndices.has(index) : true;
9282
+ });
8404
9283
  }
8405
9284
  if (!((_a = control.targetIndices) === null || _a === void 0 ? void 0 : _a.length))
8406
9285
  return [];
8407
- return control.targetIndices.filter(function (targetIndex) { return targetIndex >= 0 && targetIndex < source.length; });
9286
+ return control.targetIndices.filter(function (targetIndex) {
9287
+ return targetIndex >= 0 &&
9288
+ targetIndex < source.length &&
9289
+ (control.targetKind !== 'vertex' || !occupiedVertexIndices.has(targetIndex));
9290
+ });
9291
+ };
9292
+ KonvaInteraction.prototype.resolveOccupiedVertexIndices = function (elementId, geometry) {
9293
+ var _this = this;
9294
+ if (!geometry.vertices.length)
9295
+ return new Set();
9296
+ var ports = this.engine.getState().ports.filter(function (port) { return port.elementId === elementId; });
9297
+ if (!ports.length)
9298
+ return new Set();
9299
+ var tolerance = this.occupiedVertexTolerance;
9300
+ var occupied = new Set();
9301
+ ports.forEach(function (port) {
9302
+ var world = _this.engine.getPortWorldPosition(port.id);
9303
+ if (!world)
9304
+ return;
9305
+ geometry.vertices.forEach(function (vertex, index) {
9306
+ if (Math.abs(vertex.position.x - world.x) <= tolerance &&
9307
+ Math.abs(vertex.position.y - world.y) <= tolerance) {
9308
+ occupied.add(index);
9309
+ }
9310
+ });
9311
+ });
9312
+ return occupied;
8408
9313
  };
8409
9314
  KonvaInteraction.prototype.resolveEligibleEllipseMidPoints = function (control, targets) {
8410
9315
  var _a;
@@ -8848,6 +9753,64 @@ var KonvaInteraction = /** @class */ (function () {
8848
9753
  }
8849
9754
  return { x: elementPos.x + constrained.x, y: elementPos.y + constrained.y };
8850
9755
  };
9756
+ KonvaInteraction.prototype.resolveLinkPreviewSource = function (sourcePortId, pointer, hit) {
9757
+ var resolvedHit = hit !== null && hit !== void 0 ? hit : this.resolveHit(pointer);
9758
+ if ((resolvedHit === null || resolvedHit === void 0 ? void 0 : resolvedHit.type) === 'port' && resolvedHit.id !== sourcePortId) {
9759
+ return this.engine.getPortLinkWorldPosition(sourcePortId, { oppositePortId: resolvedHit.id });
9760
+ }
9761
+ return this.engine.getPortLinkWorldPosition(sourcePortId);
9762
+ };
9763
+ KonvaInteraction.prototype.resolveActiveLinkSession = function () {
9764
+ var _a;
9765
+ if (this.programmaticLinkSession) {
9766
+ return {
9767
+ sourcePortId: this.programmaticLinkSession.sourcePortId,
9768
+ sourceElementId: this.programmaticLinkSession.sourceElementId,
9769
+ fromProgrammatic: true,
9770
+ };
9771
+ }
9772
+ if (((_a = this.dragState) === null || _a === void 0 ? void 0 : _a.mode) === 'link-drag') {
9773
+ return {
9774
+ sourcePortId: this.dragState.sourcePortId,
9775
+ sourceElementId: this.dragState.sourceElementId,
9776
+ fromProgrammatic: false,
9777
+ };
9778
+ }
9779
+ return null;
9780
+ };
9781
+ KonvaInteraction.prototype.finishLinkDragSession = function (result) {
9782
+ var _a, _b, _c, _d;
9783
+ this.linkDragContext = null;
9784
+ if (result.fromProgrammatic) {
9785
+ this.programmaticLinkSession = null;
9786
+ }
9787
+ else if (((_a = this.dragState) === null || _a === void 0 ? void 0 : _a.mode) === 'link-drag') {
9788
+ this.dragState = null;
9789
+ }
9790
+ (_b = this.renderer) === null || _b === void 0 ? void 0 : _b.clearTempLink();
9791
+ (_d = (_c = this.renderer) === null || _c === void 0 ? void 0 : _c.clearPortPlaceholder) === null || _d === void 0 ? void 0 : _d.call(_c);
9792
+ this.clearActiveShapeHoverControl();
9793
+ this.setCursor('default');
9794
+ this.emitElementLinkEndedSafely({
9795
+ sourcePortId: result.sourcePortId,
9796
+ sourceElementId: result.sourceElementId,
9797
+ linkId: result.createdLinkId,
9798
+ targetPortId: result.targetPortId,
9799
+ targetElementId: result.targetElementId,
9800
+ cancelled: result.cancelled,
9801
+ });
9802
+ };
9803
+ KonvaInteraction.prototype.emitElementLinkEndedSafely = function (event) {
9804
+ if (this.emittingElementLinkEnded)
9805
+ return;
9806
+ this.emittingElementLinkEnded = true;
9807
+ try {
9808
+ this.engine.emitElementLinkEnded(event);
9809
+ }
9810
+ finally {
9811
+ this.emittingElementLinkEnded = false;
9812
+ }
9813
+ };
8851
9814
  KonvaInteraction.prototype.tryCreateLinkToPort = function (sourcePortId, sourceElementId, targetPortId) {
8852
9815
  var targetElementId = this.engine.getPortElementId(targetPortId);
8853
9816
  var cancelledByHost = targetElementId !== null &&
@@ -8871,6 +9834,33 @@ var KonvaInteraction = /** @class */ (function () {
8871
9834
  });
8872
9835
  return { createdLinkId: createdLinkId, targetPortId: targetPortId, targetElementId: targetElementId };
8873
9836
  };
9837
+ KonvaInteraction.prototype.tryCreateLinkToElement = function (sourcePortId, sourceElementId, targetElementId, worldPoint) {
9838
+ var targetPort = this.createPortForLink(targetElementId, worldPoint, sourcePortId);
9839
+ if (!targetPort) {
9840
+ return { createdLinkId: null, targetPortId: null, targetElementId: null };
9841
+ }
9842
+ var cancelledByHost = this.engine.emitElementLinkConnecting({
9843
+ sourcePortId: sourcePortId,
9844
+ sourceElementId: sourceElementId,
9845
+ targetPortId: targetPort.id,
9846
+ targetElementId: targetElementId,
9847
+ cancel: function () { },
9848
+ cancelled: false,
9849
+ });
9850
+ if (cancelledByHost) {
9851
+ return { createdLinkId: null, targetPortId: targetPort.id, targetElementId: targetElementId };
9852
+ }
9853
+ this.engine.addPortToElement(targetElementId, targetPort);
9854
+ this.engine.movePortTo(targetPort.id, worldPoint.x, worldPoint.y);
9855
+ var createdLinkId = createId();
9856
+ this.engine.addLink({
9857
+ id: createdLinkId,
9858
+ sourcePortId: sourcePortId,
9859
+ targetPortId: targetPort.id,
9860
+ points: [],
9861
+ });
9862
+ return { createdLinkId: createdLinkId, targetPortId: targetPort.id, targetElementId: targetElementId };
9863
+ };
8874
9864
  KonvaInteraction.prototype.createPortForLink = function (elementId, worldPoint, sourcePortId) {
8875
9865
  var _a, _b, _c, _d;
8876
9866
  var element = this.getElementById(elementId);
@@ -8885,9 +9875,8 @@ var KonvaInteraction = /** @class */ (function () {
8885
9875
  var destinationMoveMode = ((_b = element.portMovement) === null || _b === void 0 ? void 0 : _b.moveMode) && element.portMovement.moveMode !== 'anchors'
8886
9876
  ? element.portMovement.moveMode
8887
9877
  : undefined;
8888
- var portId = createId();
8889
- this.engine.addPortToElement(elementId, {
8890
- id: portId,
9878
+ return {
9879
+ id: createId(),
8891
9880
  elementId: elementId,
8892
9881
  position: relative,
8893
9882
  shapeId: sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.shapeId,
@@ -8896,9 +9885,12 @@ var KonvaInteraction = /** @class */ (function () {
8896
9885
  moveMode: destinationMoveMode !== null && destinationMoveMode !== void 0 ? destinationMoveMode : sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.moveMode,
8897
9886
  anchorCenter: (_c = sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.anchorCenter) !== null && _c !== void 0 ? _c : true,
8898
9887
  orientToHostBorder: (_d = sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.orientToHostBorder) !== null && _d !== void 0 ? _d : true,
8899
- });
8900
- this.engine.movePortTo(portId, worldPoint.x, worldPoint.y);
8901
- return portId;
9888
+ placementPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.placementPoint) ? __assign({}, sourcePort.placementPoint) : undefined,
9889
+ linkAttachPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.linkAttachPoint) ? __assign({}, sourcePort.linkAttachPoint) : undefined,
9890
+ externalLinkAttachPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.externalLinkAttachPoint) ? __assign({}, sourcePort.externalLinkAttachPoint) : undefined,
9891
+ internalLinkAttachPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.internalLinkAttachPoint) ? __assign({}, sourcePort.internalLinkAttachPoint) : undefined,
9892
+ rotationPivot: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.rotationPivot) ? __assign({}, sourcePort.rotationPivot) : undefined,
9893
+ };
8902
9894
  };
8903
9895
  KonvaInteraction.prototype.createPlaceholderPort = function (elementId, worldPoint, sourcePortId) {
8904
9896
  var _a, _b, _c;
@@ -8917,6 +9909,11 @@ var KonvaInteraction = /** @class */ (function () {
8917
9909
  moveMode: destinationMoveMode !== null && destinationMoveMode !== void 0 ? destinationMoveMode : sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.moveMode,
8918
9910
  anchorCenter: (_b = sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.anchorCenter) !== null && _b !== void 0 ? _b : true,
8919
9911
  orientToHostBorder: (_c = sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.orientToHostBorder) !== null && _c !== void 0 ? _c : true,
9912
+ placementPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.placementPoint) ? __assign({}, sourcePort.placementPoint) : undefined,
9913
+ linkAttachPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.linkAttachPoint) ? __assign({}, sourcePort.linkAttachPoint) : undefined,
9914
+ externalLinkAttachPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.externalLinkAttachPoint) ? __assign({}, sourcePort.externalLinkAttachPoint) : undefined,
9915
+ internalLinkAttachPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.internalLinkAttachPoint) ? __assign({}, sourcePort.internalLinkAttachPoint) : undefined,
9916
+ rotationPivot: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.rotationPivot) ? __assign({}, sourcePort.rotationPivot) : undefined,
8920
9917
  };
8921
9918
  };
8922
9919
  KonvaInteraction.prototype.getElementById = function (id) {
@@ -8960,47 +9957,77 @@ var KonvaInteraction = /** @class */ (function () {
8960
9957
  (_b = stageAny === null || stageAny === void 0 ? void 0 : stageAny.position) === null || _b === void 0 ? void 0 : _b.call(stageAny, this.pan);
8961
9958
  };
8962
9959
  KonvaInteraction.prototype.cancelLinkDrag = function () {
8963
- var _a, _b, _c, _d, _e, _f;
9960
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
8964
9961
  if (((_a = this.dragState) === null || _a === void 0 ? void 0 : _a.mode) === 'shape-control-drag') {
8965
- var point_1 = (_b = this.lastPointerPosition) !== null && _b !== void 0 ? _b : this.dragState.start;
8966
- if (this.dragState.hasMoved) {
8967
- this.emitShapeHoverControlInteraction('drag-end', this.dragState.control, point_1, undefined, {
8968
- sessionId: this.dragState.sessionId,
8969
- startPointer: this.dragState.startPointer,
9962
+ var dragState = this.dragState;
9963
+ var point_1 = (_b = this.lastPointerPosition) !== null && _b !== void 0 ? _b : dragState.start;
9964
+ this.dragState = null;
9965
+ if (dragState.hasMoved) {
9966
+ this.emitShapeHoverControlInteraction('drag-end', dragState.control, point_1, undefined, {
9967
+ sessionId: dragState.sessionId,
9968
+ startPointer: dragState.startPointer,
8970
9969
  delta: {
8971
- x: point_1.x - this.dragState.start.x,
8972
- y: point_1.y - this.dragState.start.y,
9970
+ x: point_1.x - dragState.start.x,
9971
+ y: point_1.y - dragState.start.y,
8973
9972
  },
8974
9973
  });
8975
9974
  }
8976
- this.dragState = null;
9975
+ if (this.programmaticLinkSession) {
9976
+ var session = this.programmaticLinkSession;
9977
+ this.programmaticLinkSession = null;
9978
+ this.linkDragContext = null;
9979
+ (_c = this.renderer) === null || _c === void 0 ? void 0 : _c.clearTempLink();
9980
+ (_e = (_d = this.renderer) === null || _d === void 0 ? void 0 : _d.clearPortPlaceholder) === null || _e === void 0 ? void 0 : _e.call(_d);
9981
+ this.emitElementLinkEndedSafely({
9982
+ sourcePortId: session.sourcePortId,
9983
+ sourceElementId: session.sourceElementId,
9984
+ cancelled: true,
9985
+ });
9986
+ }
9987
+ this.clearActiveShapeHoverControl();
9988
+ this.setCursor('default');
9989
+ return;
9990
+ }
9991
+ if (this.programmaticLinkSession) {
9992
+ var session = this.programmaticLinkSession;
9993
+ this.programmaticLinkSession = null;
9994
+ this.linkDragContext = null;
9995
+ (_f = this.renderer) === null || _f === void 0 ? void 0 : _f.clearTempLink();
9996
+ (_h = (_g = this.renderer) === null || _g === void 0 ? void 0 : _g.clearPortPlaceholder) === null || _h === void 0 ? void 0 : _h.call(_g);
9997
+ this.emitElementLinkEndedSafely({
9998
+ sourcePortId: session.sourcePortId,
9999
+ sourceElementId: session.sourceElementId,
10000
+ cancelled: true,
10001
+ });
8977
10002
  this.clearActiveShapeHoverControl();
8978
10003
  this.setCursor('default');
8979
10004
  return;
8980
10005
  }
8981
10006
  if (!this.dragState || this.dragState.mode !== 'link-drag') {
8982
10007
  if (this.linkDragContext) {
8983
- this.engine.emitElementLinkEnded(__assign(__assign({}, this.linkDragContext), { cancelled: true }));
10008
+ var context = this.linkDragContext;
8984
10009
  this.linkDragContext = null;
10010
+ this.emitElementLinkEndedSafely(__assign(__assign({}, context), { cancelled: true }));
8985
10011
  }
8986
10012
  return;
8987
10013
  }
8988
- var point = (_c = this.dragState.current) !== null && _c !== void 0 ? _c : this.dragState.start;
10014
+ var linkState = this.dragState;
10015
+ var point = (_j = linkState.current) !== null && _j !== void 0 ? _j : linkState.start;
8989
10016
  var pointerInfo = this.buildPointerInfo(point, null);
8990
10017
  this.engine.emitPortMouseUp({
8991
- portId: this.dragState.sourcePortId,
8992
- elementId: this.dragState.sourceElementId,
10018
+ portId: linkState.sourcePortId,
10019
+ elementId: linkState.sourceElementId,
8993
10020
  pointer: pointerInfo,
8994
10021
  });
8995
- this.engine.emitElementLinkEnded({
8996
- sourcePortId: this.dragState.sourcePortId,
8997
- sourceElementId: this.dragState.sourceElementId,
10022
+ this.dragState = null;
10023
+ this.linkDragContext = null;
10024
+ (_k = this.renderer) === null || _k === void 0 ? void 0 : _k.clearTempLink();
10025
+ (_m = (_l = this.renderer) === null || _l === void 0 ? void 0 : _l.clearPortPlaceholder) === null || _m === void 0 ? void 0 : _m.call(_l);
10026
+ this.emitElementLinkEndedSafely({
10027
+ sourcePortId: linkState.sourcePortId,
10028
+ sourceElementId: linkState.sourceElementId,
8998
10029
  cancelled: true,
8999
10030
  });
9000
- this.linkDragContext = null;
9001
- (_d = this.renderer) === null || _d === void 0 ? void 0 : _d.clearTempLink();
9002
- (_f = (_e = this.renderer) === null || _e === void 0 ? void 0 : _e.clearPortPlaceholder) === null || _f === void 0 ? void 0 : _f.call(_e);
9003
- this.dragState = null;
9004
10031
  this.clearActiveShapeHoverControl();
9005
10032
  this.setCursor('default');
9006
10033
  };
@@ -9224,6 +10251,15 @@ var normalizeVector = function (vector) {
9224
10251
  return { x: 0, y: -1 };
9225
10252
  return { x: vector.x / length, y: vector.y / length };
9226
10253
  };
10254
+ var sideToNormal = function (side) {
10255
+ if (side === 'left')
10256
+ return { x: -1, y: 0 };
10257
+ if (side === 'right')
10258
+ return { x: 1, y: 0 };
10259
+ if (side === 'top')
10260
+ return { x: 0, y: -1 };
10261
+ return { x: 0, y: 1 };
10262
+ };
9227
10263
  var resolveCardinalAnchors = function (rect) {
9228
10264
  var cx = rect.x + rect.width / 2;
9229
10265
  var cy = rect.y + rect.height / 2;
@@ -9303,9 +10339,15 @@ var BuiltInShape = /** @class */ (function () {
9303
10339
  BuiltInShape.prototype.resolveBorderSide = function (point, rect) {
9304
10340
  return resolveBoundarySide(point, rect, 'rect');
9305
10341
  };
10342
+ BuiltInShape.prototype.resolveBorderNormal = function (point, rect) {
10343
+ return sideToNormal(this.resolveBorderSide(point, rect));
10344
+ };
9306
10345
  BuiltInShape.prototype.resolvePortAnchors = function (_rect, _options) {
9307
10346
  return [];
9308
10347
  };
10348
+ BuiltInShape.prototype.resolvePortBorderTransform = function (_context) {
10349
+ return undefined;
10350
+ };
9309
10351
  BuiltInShape.prototype.resolveHoverGeometry = function (_rect) {
9310
10352
  return undefined;
9311
10353
  };
@@ -9535,6 +10577,78 @@ var getKonva = function () {
9535
10577
  var module = require('konva');
9536
10578
  return (_a = module.default) !== null && _a !== void 0 ? _a : module;
9537
10579
  };
10580
+ var createBounds = function () { return ({
10581
+ minX: Number.POSITIVE_INFINITY,
10582
+ minY: Number.POSITIVE_INFINITY,
10583
+ maxX: Number.NEGATIVE_INFINITY,
10584
+ maxY: Number.NEGATIVE_INFINITY,
10585
+ }); };
10586
+ var expandBounds = function (bounds, x, y) {
10587
+ if (x < bounds.minX)
10588
+ bounds.minX = x;
10589
+ if (y < bounds.minY)
10590
+ bounds.minY = y;
10591
+ if (x > bounds.maxX)
10592
+ bounds.maxX = x;
10593
+ if (y > bounds.maxY)
10594
+ bounds.maxY = y;
10595
+ };
10596
+ var includeRect = function (bounds, x, y, width, height) {
10597
+ var safeWidth = Math.max(0, width);
10598
+ var safeHeight = Math.max(0, height);
10599
+ expandBounds(bounds, x, y);
10600
+ expandBounds(bounds, x + safeWidth, y + safeHeight);
10601
+ };
10602
+ var hasBounds = function (bounds) {
10603
+ return Number.isFinite(bounds.minX) &&
10604
+ Number.isFinite(bounds.minY) &&
10605
+ Number.isFinite(bounds.maxX) &&
10606
+ Number.isFinite(bounds.maxY) &&
10607
+ bounds.maxX >= bounds.minX &&
10608
+ bounds.maxY >= bounds.minY;
10609
+ };
10610
+ var resolveStateWorldBounds = function (state) {
10611
+ var bounds = createBounds();
10612
+ state.elements.forEach(function (element) {
10613
+ includeRect(bounds, element.position.x, element.position.y, element.size.width, element.size.height);
10614
+ });
10615
+ state.ports.forEach(function (port) {
10616
+ var _a;
10617
+ var size = port.size;
10618
+ if (!size) {
10619
+ expandBounds(bounds, port.position.x, port.position.y);
10620
+ return;
10621
+ }
10622
+ var anchorCenter = (_a = port.anchorCenter) !== null && _a !== void 0 ? _a : true;
10623
+ var x = anchorCenter ? port.position.x - size.width / 2 : port.position.x;
10624
+ var y = anchorCenter ? port.position.y - size.height / 2 : port.position.y;
10625
+ includeRect(bounds, x, y, size.width, size.height);
10626
+ });
10627
+ state.links.forEach(function (link) {
10628
+ link.points.forEach(function (point) {
10629
+ expandBounds(bounds, point.x, point.y);
10630
+ });
10631
+ });
10632
+ state.texts.forEach(function (text) {
10633
+ var _a;
10634
+ var offset = (_a = text.displayOffset) !== null && _a !== void 0 ? _a : { x: 0, y: 0 };
10635
+ var position = { x: text.position.x + offset.x, y: text.position.y + offset.y };
10636
+ var clipSize = text.displayClipSize;
10637
+ var size = clipSize !== null && clipSize !== void 0 ? clipSize : text.size;
10638
+ if (size) {
10639
+ includeRect(bounds, position.x, position.y, size.width, size.height);
10640
+ return;
10641
+ }
10642
+ expandBounds(bounds, position.x, position.y);
10643
+ });
10644
+ return hasBounds(bounds) ? bounds : null;
10645
+ };
10646
+ var resolveFitToContentPadding = function (fitToContent) {
10647
+ if (typeof fitToContent === 'object' && typeof fitToContent.padding === 'number') {
10648
+ return Math.max(0, fitToContent.padding);
10649
+ }
10650
+ return 0;
10651
+ };
9538
10652
  var getNodeAttr = function (node, key) {
9539
10653
  if (node.getAttr) {
9540
10654
  return node.getAttr(key);
@@ -9573,7 +10687,13 @@ var registerSimpleShapes = function (registry, shapes, isPort) {
9573
10687
  : undefined,
9574
10688
  projectToBorder: function (point, rect) { return behavior.projectToBorder(point, rect); },
9575
10689
  resolveBorderSide: function (point, rect) { return behavior.resolveBorderSide(point, rect); },
10690
+ resolveBorderNormal: behavior.resolveBorderNormal
10691
+ ? function (point, rect) { return behavior.resolveBorderNormal(point, rect); }
10692
+ : undefined,
9576
10693
  resolvePortAnchors: function (rect, options) { return behavior.resolvePortAnchors(rect, options); },
10694
+ resolvePortBorderTransform: behavior.resolvePortBorderTransform
10695
+ ? function (context) { return behavior.resolvePortBorderTransform(context); }
10696
+ : undefined,
9577
10697
  resolveHoverGeometry: function (rect) { return behavior.resolveHoverGeometry(rect); },
9578
10698
  resolveEllipseMidPoints: function (rect) { return behavior.resolveEllipseMidPoints(rect); },
9579
10699
  createNode: function (model) {
@@ -9647,7 +10767,7 @@ var createDiagramEditor = function (config) {
9647
10767
  .filter(function (hit) { return Boolean(hit); });
9648
10768
  if (hits.length === 0)
9649
10769
  return { id: '', type: 'none' };
9650
- var priority = ['resize-handle', 'link-handle', 'shape-hover-control', 'port', 'link', 'text', 'element'];
10770
+ var priority = ['resize-handle', 'link-handle', 'port', 'shape-hover-control', 'link', 'text', 'element'];
9651
10771
  var _loop_1 = function (i) {
9652
10772
  var candidates = hits.filter(function (hit) { return hit.type === priority[i]; });
9653
10773
  if (candidates.length === 0)
@@ -9738,6 +10858,36 @@ var createDiagramEditor = function (config) {
9738
10858
  setSnapping: function (snapper) { return engine.setSnapping(snapper); },
9739
10859
  registerShape: function (shape) { return engine.registerShape(shape); },
9740
10860
  render: function () { return engine.render(); },
10861
+ startLinkFromPort: function (sourcePortId, pointer) { return interaction.startLinkFromPort(sourcePortId, pointer); },
10862
+ updateLinkPreview: function (pointer) { return interaction.updateLinkPreview(pointer); },
10863
+ completeLinkToPort: function (targetPortId) { return interaction.completeLinkToPort(targetPortId); },
10864
+ completeLinkToElement: function (targetElementId, pointer) { return interaction.completeLinkToElement(targetElementId, pointer); },
10865
+ cancelLink: function () { return interaction.cancelLink(); },
10866
+ exportImage: function (options) {
10867
+ engine.render();
10868
+ if (typeof stage.toDataURL !== 'function') {
10869
+ throw new Error('Diagram image export is not available on the current stage.');
10870
+ }
10871
+ if (!options) {
10872
+ return stage.toDataURL();
10873
+ }
10874
+ var fitToContent = options.fitToContent, baseOptions = __rest(options, ["fitToContent"]);
10875
+ if (!fitToContent) {
10876
+ return stage.toDataURL(baseOptions);
10877
+ }
10878
+ var padding = resolveFitToContentPadding(fitToContent);
10879
+ var worldBounds = resolveStateWorldBounds(engine.getState());
10880
+ if (!worldBounds) {
10881
+ return stage.toDataURL(baseOptions);
10882
+ }
10883
+ var viewport = engine.getViewport();
10884
+ var safeZoom = viewport.zoom === 0 ? 1 : viewport.zoom;
10885
+ var cropX = worldBounds.minX * safeZoom + viewport.pan.x - padding;
10886
+ var cropY = worldBounds.minY * safeZoom + viewport.pan.y - padding;
10887
+ var cropWidth = Math.max(1, (worldBounds.maxX - worldBounds.minX) * safeZoom + padding * 2);
10888
+ var cropHeight = Math.max(1, (worldBounds.maxY - worldBounds.minY) * safeZoom + padding * 2);
10889
+ return stage.toDataURL(__assign(__assign({}, baseOptions), { x: cropX, y: cropY, width: cropWidth, height: cropHeight }));
10890
+ },
9741
10891
  resize: function (width, height) {
9742
10892
  stage.width(width);
9743
10893
  stage.height(height);
@@ -9927,7 +11077,7 @@ var EventHandlersDemo = function () {
9927
11077
  React.createElement("div", { style: { marginBottom: 12 } },
9928
11078
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
9929
11079
  React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
9930
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
11080
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
9931
11081
  React.createElement("div", { style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 12 } },
9932
11082
  React.createElement("label", { htmlFor: "event-handler-select", style: { fontWeight: 600 } }, "Event"),
9933
11083
  React.createElement("select", { id: "event-handler-select", value: eventDemoEvent, onChange: function (event) { return setEventDemoEvent(event.target.value); }, style: { padding: '6px 8px' } },
@@ -10005,7 +11155,7 @@ var EngineEventsDemo = function () {
10005
11155
  React.createElement("div", { style: { marginBottom: 12 } },
10006
11156
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
10007
11157
  React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
10008
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
11158
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
10009
11159
  React.createElement("div", { style: { display: 'grid', gap: 12, marginBottom: 12 } },
10010
11160
  React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: 12 } },
10011
11161
  React.createElement("label", { htmlFor: "engine-event-select", style: { fontWeight: 600 } }, "Engine Event"),
@@ -10113,7 +11263,7 @@ var LinkCancelDemo = function () {
10113
11263
  " and ",
10114
11264
  React.createElement("code", null, "elementLinkEnded"),
10115
11265
  " appear below.")),
10116
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
11266
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
10117
11267
  React.createElement("div", { style: { display: 'grid', gap: 12, marginBottom: 12 } },
10118
11268
  React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center' } },
10119
11269
  React.createElement("span", { style: __assign(__assign({}, badgeStyle), { background: statusTone, color: statusColor, borderColor: statusColor }) },
@@ -10205,6 +11355,8 @@ var parentOptions = [
10205
11355
  { id: 'layout-nested', label: 'Nested layout' },
10206
11356
  { id: 'layout-manual', label: 'Manual (compare)' },
10207
11357
  ];
11358
+ var shortLabel = 'Parent label lane demo';
11359
+ var longLabel = 'Parent label lane demo with longer content to increase flexible reserved space and push children downward.';
10208
11360
  var AutoLayoutDemo = function () {
10209
11361
  var demo = autoLayoutDemoConfig;
10210
11362
  var _a = useDemoEditor({
@@ -10234,7 +11386,11 @@ var AutoLayoutDemo = function () {
10234
11386
  var _k = useState(''), childFitMinHeight = _k[0], setChildFitMinHeight = _k[1];
10235
11387
  var _l = useState(''), childFitMaxWidth = _l[0], setChildFitMaxWidth = _l[1];
10236
11388
  var _m = useState(''), childFitMaxHeight = _m[0], setChildFitMaxHeight = _m[1];
10237
- var _o = useState('None yet'), lastTrigger = _o[0], setLastTrigger = _o[1];
11389
+ var _o = useState('none'), labelReservedMode = _o[0], setLabelReservedMode = _o[1];
11390
+ var _p = useState(32), labelReservedFixedSize = _p[0], setLabelReservedFixedSize = _p[1];
11391
+ var _q = useState(''), labelReservedMinSize = _q[0], setLabelReservedMinSize = _q[1];
11392
+ var _r = useState(''), labelReservedMaxSize = _r[0], setLabelReservedMaxSize = _r[1];
11393
+ var _s = useState('None yet'), lastTrigger = _s[0], setLastTrigger = _s[1];
10238
11394
  useEffect(function () {
10239
11395
  var editor = editorRef.current;
10240
11396
  if (!editor)
@@ -10294,7 +11450,7 @@ var AutoLayoutDemo = function () {
10294
11450
  return options;
10295
11451
  }, [diagramState, targetElement]);
10296
11452
  useEffect(function () {
10297
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
11453
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
10298
11454
  var parent = diagramState === null || diagramState === void 0 ? void 0 : diagramState.elements.find(function (el) { return el.id === targetId; });
10299
11455
  if (!parent)
10300
11456
  return;
@@ -10314,6 +11470,10 @@ var AutoLayoutDemo = function () {
10314
11470
  setChildFitMinHeight((_l = (_k = layout === null || layout === void 0 ? void 0 : layout.childFitMinSize) === null || _k === void 0 ? void 0 : _k.height) !== null && _l !== void 0 ? _l : '');
10315
11471
  setChildFitMaxWidth((_o = (_m = layout === null || layout === void 0 ? void 0 : layout.childFitMaxSize) === null || _m === void 0 ? void 0 : _m.width) !== null && _o !== void 0 ? _o : '');
10316
11472
  setChildFitMaxHeight((_q = (_p = layout === null || layout === void 0 ? void 0 : layout.childFitMaxSize) === null || _p === void 0 ? void 0 : _p.height) !== null && _q !== void 0 ? _q : '');
11473
+ setLabelReservedMode((_s = (_r = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _r === void 0 ? void 0 : _r.mode) !== null && _s !== void 0 ? _s : 'none');
11474
+ setLabelReservedFixedSize((_u = (_t = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _t === void 0 ? void 0 : _t.size) !== null && _u !== void 0 ? _u : 32);
11475
+ setLabelReservedMinSize((_w = (_v = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _v === void 0 ? void 0 : _v.minSize) !== null && _w !== void 0 ? _w : '');
11476
+ setLabelReservedMaxSize((_y = (_x = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _x === void 0 ? void 0 : _x.maxSize) !== null && _y !== void 0 ? _y : '');
10317
11477
  }, [diagramState, targetId]);
10318
11478
  var childOrder = useMemo(function () {
10319
11479
  var _a;
@@ -10350,6 +11510,9 @@ var AutoLayoutDemo = function () {
10350
11510
  var childFitMaxSize = childFitMaxWidth === '' && childFitMaxHeight === ''
10351
11511
  ? undefined
10352
11512
  : __assign(__assign({}, (childFitMaxWidth === '' ? {} : { width: childFitMaxWidth })), (childFitMaxHeight === '' ? {} : { height: childFitMaxHeight }));
11513
+ var labelReservedSpace = mode === 'manual'
11514
+ ? undefined
11515
+ : __assign(__assign(__assign({ mode: labelReservedMode, placement: 'top' }, (labelReservedMode === 'fixed' ? { size: labelReservedFixedSize } : {})), (labelReservedMinSize === '' ? {} : { minSize: labelReservedMinSize })), (labelReservedMaxSize === '' ? {} : { maxSize: labelReservedMaxSize }));
10353
11516
  var layout = mode === 'manual'
10354
11517
  ? { mode: 'manual' }
10355
11518
  : {
@@ -10361,10 +11524,21 @@ var AutoLayoutDemo = function () {
10361
11524
  childFitCrossAxis: childFitCrossAxis,
10362
11525
  childFitMinSize: childFitMinSize,
10363
11526
  childFitMaxSize: childFitMaxSize,
11527
+ labelReservedSpace: labelReservedSpace,
10364
11528
  };
10365
11529
  editor.setElementLayout(targetElement.id, layout);
10366
11530
  setLastTrigger("layout applied (".concat(mode, ")"));
10367
11531
  };
11532
+ var handleSetLabelContent = function (content) {
11533
+ var editor = editorRef.current;
11534
+ if (!editor || !diagramState || !targetElement)
11535
+ return;
11536
+ var label = diagramState.texts.find(function (text) { return text.ownerId === targetElement.id; });
11537
+ if (!label)
11538
+ return;
11539
+ editor.updateText(label.id, content);
11540
+ setLastTrigger('label updated');
11541
+ };
10368
11542
  var handleAddChild = function () {
10369
11543
  var _a;
10370
11544
  var editor = editorRef.current;
@@ -10399,7 +11573,7 @@ var AutoLayoutDemo = function () {
10399
11573
  React.createElement("div", { style: { marginBottom: 12 } },
10400
11574
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
10401
11575
  React.createElement("p", { style: { marginTop: 0 } }, "Try horizontal, vertical, nested, and manual layout containers. Select a layout parent directly on the canvas or pick it from the list; the controls below always act on the current selection. Drag, resize, add, or remove children to see automatic reflow. Use fit controls to distribute/stretch child sizes with optional min/max guards. Nested column shows fit propagation boundaries to its parent row.")),
10402
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
11576
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
10403
11577
  React.createElement("div", { style: { display: 'grid', gap: 12, marginBottom: 12 } },
10404
11578
  React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center' } },
10405
11579
  React.createElement("label", { htmlFor: "parent-select", style: { fontWeight: 600 } }, "Target element (follows selection)"),
@@ -10440,6 +11614,16 @@ var AutoLayoutDemo = function () {
10440
11614
  React.createElement("div", { style: { display: 'inline-flex', gap: 6 } },
10441
11615
  React.createElement("input", { id: "fit-max-width-input", type: "number", value: childFitMaxWidth, onChange: function (event) { return setChildFitMaxWidth(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "w" }),
10442
11616
  React.createElement("input", { type: "number", value: childFitMaxHeight, onChange: function (event) { return setChildFitMaxHeight(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "h" })),
11617
+ React.createElement("label", { htmlFor: "label-reserved-mode-select", style: { fontWeight: 600 } }, "Label lane"),
11618
+ React.createElement("select", { id: "label-reserved-mode-select", value: labelReservedMode, onChange: function (event) { return setLabelReservedMode(event.target.value); }, style: { padding: '6px 10px', minWidth: 120 }, disabled: mode === 'manual' },
11619
+ React.createElement("option", { value: "none" }, "None"),
11620
+ React.createElement("option", { value: "fixed" }, "Fixed"),
11621
+ React.createElement("option", { value: "flexible" }, "Flexible")),
11622
+ React.createElement("label", { htmlFor: "label-reserved-fixed-input", style: { fontWeight: 600 } }, "Lane fixed/min/max"),
11623
+ React.createElement("div", { style: { display: 'inline-flex', gap: 6 } },
11624
+ React.createElement("input", { id: "label-reserved-fixed-input", type: "number", value: labelReservedFixedSize, onChange: function (event) { return setLabelReservedFixedSize(Math.max(0, Number(event.target.value) || 0)); }, style: { width: 70, padding: '6px 8px' }, min: 0, placeholder: "fixed", disabled: mode === 'manual' }),
11625
+ React.createElement("input", { type: "number", value: labelReservedMinSize, onChange: function (event) { return setLabelReservedMinSize(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "min", disabled: mode === 'manual' }),
11626
+ React.createElement("input", { type: "number", value: labelReservedMaxSize, onChange: function (event) { return setLabelReservedMaxSize(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "max", disabled: mode === 'manual' })),
10443
11627
  React.createElement("button", { type: "button", onClick: handleApplyLayout, style: { padding: '6px 12px', fontWeight: 600 } }, "Apply layout"),
10444
11628
  React.createElement("button", { type: "button", onClick: handleAddChild, style: { padding: '6px 10px' }, disabled: !targetElement }, "Add child"),
10445
11629
  React.createElement("button", { type: "button", onClick: handleRemoveChild, style: { padding: '6px 10px' }, disabled: !targetElement }, "Remove last child"),
@@ -10457,7 +11641,9 @@ var AutoLayoutDemo = function () {
10457
11641
  targetElement ? targetElement.id : '—',
10458
11642
  React.createElement("span", { style: { width: 6, height: 6, borderRadius: '50%', background: '#1f4d99', display: 'inline-block' } }),
10459
11643
  "Last trigger: ",
10460
- React.createElement("strong", null, lastTrigger))),
11644
+ React.createElement("strong", null, lastTrigger)),
11645
+ React.createElement("button", { type: "button", onClick: function () { return handleSetLabelContent(shortLabel); }, style: { padding: '6px 10px' }, disabled: !targetElement || mode === 'manual' }, "Label short"),
11646
+ React.createElement("button", { type: "button", onClick: function () { return handleSetLabelContent(longLabel); }, style: { padding: '6px 10px' }, disabled: !targetElement || mode === 'manual' }, "Label long")),
10461
11647
  React.createElement("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 } },
10462
11648
  React.createElement("div", null,
10463
11649
  React.createElement("div", { style: { fontWeight: 600, marginBottom: 6 } }, "Child order (based on layout axis)"),
@@ -10480,6 +11666,7 @@ var AutoLayoutDemo = function () {
10480
11666
  React.createElement("ul", { style: { marginTop: 0, paddingLeft: 18, fontSize: 13 } },
10481
11667
  React.createElement("li", null, "Horizontal: try center vs bottom alignment and larger padding."),
10482
11668
  React.createElement("li", null, "Vertical: see parent grow taller as children stack."),
11669
+ React.createElement("li", null, "Label lane: compare none/fixed/flexible and observe children start below the reserved lane."),
10483
11670
  React.createElement("li", null, "Fit main-axis distribute fills inner layout space across siblings."),
10484
11671
  React.createElement("li", null, "Fit cross-axis stretch extends children across inner cross axis."),
10485
11672
  React.createElement("li", null, "Min/max fit guards cap distributed or stretched child sizes."),
@@ -10660,7 +11847,7 @@ var ExternalDragDropDemo = function () {
10660
11847
  React.createElement("div", { style: { marginBottom: 12 } },
10661
11848
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
10662
11849
  React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
10663
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
11850
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
10664
11851
  React.createElement("div", { style: { display: 'flex', gap: 16, marginBottom: 12, alignItems: 'stretch' } },
10665
11852
  React.createElement("div", { style: {
10666
11853
  minWidth: 220,
@@ -10817,7 +12004,7 @@ var SvgPathDemo = function () {
10817
12004
  React.createElement("div", { style: { marginBottom: 12 } },
10818
12005
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
10819
12006
  React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
10820
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
12007
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
10821
12008
  React.createElement("div", { style: {
10822
12009
  display: 'grid',
10823
12010
  gridTemplateColumns: 'minmax(0, 1fr) 200px',
@@ -10998,7 +12185,7 @@ var TextLayoutDemo = function () {
10998
12185
  React.createElement("div", { style: { marginBottom: 12 } },
10999
12186
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
11000
12187
  React.createElement("p", { style: { marginTop: 0 } }, "Full text content is preserved in state. Overflow and ellipsis settings only change visual output (`displayContent`) for rendering.")),
11001
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
12188
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
11002
12189
  React.createElement("div", { style: { display: 'grid', gap: 10, marginBottom: 12 } },
11003
12190
  React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center' } },
11004
12191
  React.createElement("label", { htmlFor: "text-target", style: { fontWeight: 600 } }, "Target"),
@@ -11122,7 +12309,7 @@ var SimpleDemo = function (_a) {
11122
12309
  React.createElement("div", { style: { marginBottom: 12 } },
11123
12310
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
11124
12311
  React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
11125
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
12312
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
11126
12313
  beforeStage,
11127
12314
  React.createElement(DisplayBoxStage, { containerRef: containerRef, onDragOver: stageHandlers === null || stageHandlers === void 0 ? void 0 : stageHandlers.onDragOver, onDragLeave: stageHandlers === null || stageHandlers === void 0 ? void 0 : stageHandlers.onDragLeave, onDrop: stageHandlers === null || stageHandlers === void 0 ? void 0 : stageHandlers.onDrop, stageStyle: demo.stageStyle })));
11128
12315
  };
@@ -11204,7 +12391,7 @@ var DeletionEventsDemo = function () {
11204
12391
  "Use direct delete buttons for port/link/text, then delete ",
11205
12392
  React.createElement("code", null, "delete-source"),
11206
12393
  " to trigger cascade removals. Latest payloads are shown below for integration debugging.")),
11207
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
12394
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
11208
12395
  React.createElement("div", { style: { display: 'grid', gap: 12, marginBottom: 12 } },
11209
12396
  React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
11210
12397
  React.createElement("button", { type: "button", onClick: function () { var _a; return (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.removePort('delete-source-free-port'); }, disabled: !hasPort }, "Delete Port"),
@@ -11510,7 +12697,7 @@ var ShapeHoverControlsDemo = function () {
11510
12697
  React.createElement("div", { style: { marginBottom: 12 } },
11511
12698
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
11512
12699
  React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
11513
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
12700
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
11514
12701
  React.createElement("div", { style: {
11515
12702
  marginBottom: 12,
11516
12703
  padding: '10px 12px',
@@ -11597,6 +12784,414 @@ var EllipseMidPointControlSection = function (_a) {
11597
12784
  midPoint)); })))));
11598
12785
  };
11599
12786
 
12787
+ var sidePositions = {
12788
+ top: { x: 112, y: 0 },
12789
+ right: { x: 280, y: 68 },
12790
+ bottom: { x: 182, y: 180 },
12791
+ left: { x: 0, y: 130 },
12792
+ };
12793
+ var sideLabels = ['top', 'right', 'bottom', 'left'];
12794
+ var resolvePortSide = function (port, host) {
12795
+ if (Math.abs(port.position.x) <= 0.001)
12796
+ return 'left';
12797
+ if (Math.abs(port.position.x - host.size.width) <= 0.001)
12798
+ return 'right';
12799
+ if (Math.abs(port.position.y) <= 0.001)
12800
+ return 'top';
12801
+ if (Math.abs(port.position.y - host.size.height) <= 0.001)
12802
+ return 'bottom';
12803
+ return null;
12804
+ };
12805
+ var sideToVector = function (side) {
12806
+ if (side === 'left')
12807
+ return { x: -1, y: 0 };
12808
+ if (side === 'right')
12809
+ return { x: 1, y: 0 };
12810
+ if (side === 'top')
12811
+ return { x: 0, y: -1 };
12812
+ return { x: 0, y: 1 };
12813
+ };
12814
+ var AsymmetricPortMultiAnchorDemo = function () {
12815
+ var demo = asymmetricPortMultiAnchorDemoConfig;
12816
+ var _a = useState(true), showLegacyComparison = _a[0], setShowLegacyComparison = _a[1];
12817
+ var _b = useState(false), showExpectedAnchors = _b[0], setShowExpectedAnchors = _b[1];
12818
+ var _c = useState(asymmetricPortDefaultVariantId), selectedVariantId = _c[0], setSelectedVariantId = _c[1];
12819
+ var overlayHandlesRef = useRef([]);
12820
+ var selectedVariant = useMemo(function () { return resolveAsymmetricPortShapeVariant(selectedVariantId); }, [selectedVariantId]);
12821
+ var attachDistance = selectedVariant.externalLinkAttachPoint.x - selectedVariant.placementPoint.x;
12822
+ var createState = useCallback(function () { return createAsymmetricPortMultiAnchorState(showLegacyComparison, selectedVariantId); }, [showLegacyComparison, selectedVariantId]);
12823
+ var _d = useDemoEditor({
12824
+ createState: createState,
12825
+ elementShapes: demo.elementShapes,
12826
+ portShapes: demo.portShapes,
12827
+ }), containerRef = _d.containerRef, editorRef = _d.editorRef, diagramState = _d.diagramState, selection = _d.selection, snapEnabled = _d.snapEnabled, setSnapEnabled = _d.setSnapEnabled;
12828
+ var nextOffset = useOffsetSequence();
12829
+ var actionHelpers = useMemo(function () { return ({ nextOffset: nextOffset }); }, [nextOffset]);
12830
+ var controls = useDemoControls({
12831
+ demo: demo,
12832
+ editorRef: editorRef,
12833
+ diagramState: diagramState,
12834
+ selection: selection,
12835
+ snapEnabled: snapEnabled,
12836
+ setSnapEnabled: setSnapEnabled,
12837
+ actionHelpers: actionHelpers,
12838
+ });
12839
+ useEffect(function () {
12840
+ var editor = editorRef.current;
12841
+ if (!editor)
12842
+ return;
12843
+ asymmetricPortShapeVariants.forEach(function (variant) {
12844
+ editor.registerShape({
12845
+ id: variant.shapeId,
12846
+ baseRotation: 90,
12847
+ svgPath: variant.svgPath,
12848
+ svgSize: variant.svgSize,
12849
+ });
12850
+ });
12851
+ editor.rerouteAllLinks();
12852
+ }, [editorRef, showLegacyComparison, selectedVariantId]);
12853
+ useEffect(function () {
12854
+ var editor = editorRef.current;
12855
+ var state = diagramState;
12856
+ overlayHandlesRef.current.forEach(function (handle) { return handle.destroy(); });
12857
+ overlayHandlesRef.current = [];
12858
+ if (!showExpectedAnchors || !editor || !state)
12859
+ return;
12860
+ var hosts = new Map(state.elements.map(function (element) { return [element.id, element]; }));
12861
+ var debugPorts = state.ports.filter(function (port) { return port.id.startsWith('multi-anchor-port-'); });
12862
+ debugPorts.forEach(function (port) {
12863
+ var host = hosts.get(port.elementId);
12864
+ if (!host)
12865
+ return;
12866
+ var side = resolvePortSide(port, host);
12867
+ if (!side)
12868
+ return;
12869
+ var vector = sideToVector(side);
12870
+ var placementWorld = {
12871
+ x: host.position.x + port.position.x,
12872
+ y: host.position.y + port.position.y,
12873
+ };
12874
+ overlayHandlesRef.current.push(editor.createOverlayShape({
12875
+ shapeId: 'default',
12876
+ position: placementWorld,
12877
+ size: { width: 8, height: 8 },
12878
+ anchorCenter: true,
12879
+ style: {
12880
+ fill: '#ffffff',
12881
+ stroke: '#d97706',
12882
+ strokeWidth: 2,
12883
+ cornerRadius: 1,
12884
+ opacity: 0.9,
12885
+ },
12886
+ }));
12887
+ if (port.id === multiAnchorExternalPortId || port.id.endsWith('-port-top') || port.id.endsWith('-port-bottom')) {
12888
+ overlayHandlesRef.current.push(editor.createOverlayShape({
12889
+ shapeId: 'port-dark',
12890
+ position: {
12891
+ x: placementWorld.x + vector.x * attachDistance,
12892
+ y: placementWorld.y + vector.y * attachDistance,
12893
+ },
12894
+ size: { width: 8, height: 8 },
12895
+ anchorCenter: true,
12896
+ style: {
12897
+ fill: '#dc2626',
12898
+ stroke: '#dc2626',
12899
+ opacity: 0.85,
12900
+ },
12901
+ }));
12902
+ }
12903
+ });
12904
+ return function () {
12905
+ overlayHandlesRef.current.forEach(function (handle) { return handle.destroy(); });
12906
+ overlayHandlesRef.current = [];
12907
+ };
12908
+ }, [diagramState, editorRef, showExpectedAnchors]);
12909
+ var moveExternalPortToSide = function (side) {
12910
+ var editor = editorRef.current;
12911
+ var host = diagramState === null || diagramState === void 0 ? void 0 : diagramState.elements.find(function (element) { return element.id === multiAnchorHostId; });
12912
+ if (!editor || !host)
12913
+ return;
12914
+ var local = sidePositions[side];
12915
+ editor.movePortTo(multiAnchorExternalPortId, host.position.x + local.x, host.position.y + local.y);
12916
+ editor.setSelection([multiAnchorExternalPortId]);
12917
+ };
12918
+ return (React.createElement("section", null,
12919
+ React.createElement("div", { style: { marginBottom: 12 } },
12920
+ React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
12921
+ React.createElement("p", { style: { marginTop: 0, marginBottom: 8 } }, demo.description),
12922
+ React.createElement("p", { style: { marginTop: 0, marginBottom: 0, fontSize: 13, color: '#475569' } }, "The custom `svgPath` glyph is intentionally non-centered. The multi-anchor row keeps the square pivot pinned to the border while links use the circle for external targets and the square pivot for parent-child internal links."),
12923
+ React.createElement("p", { style: { marginTop: 8, marginBottom: 0, fontSize: 13, color: '#475569' } },
12924
+ "Active glyph variant: ",
12925
+ React.createElement("strong", null, selectedVariant.label),
12926
+ " (",
12927
+ selectedVariant.description,
12928
+ ")")),
12929
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
12930
+ React.createElement("div", { style: {
12931
+ display: 'grid',
12932
+ gridTemplateColumns: 'minmax(0, 1fr) auto',
12933
+ gap: 12,
12934
+ alignItems: 'start',
12935
+ marginBottom: 12,
12936
+ padding: 12,
12937
+ border: '1px solid #d9e2f0',
12938
+ borderRadius: 8,
12939
+ background: '#f8fbff',
12940
+ } },
12941
+ React.createElement("div", { style: { display: 'grid', gap: 8 } },
12942
+ React.createElement("label", { htmlFor: "legacy-comparison-toggle", style: { display: 'flex', alignItems: 'center', gap: 8, fontWeight: 600 } },
12943
+ React.createElement("input", { id: "legacy-comparison-toggle", type: "checkbox", checked: showLegacyComparison, onChange: function (event) { return setShowLegacyComparison(event.target.checked); } }),
12944
+ "Show legacy/default comparison"),
12945
+ React.createElement("label", { htmlFor: "expected-anchor-toggle", style: { display: 'flex', alignItems: 'center', gap: 8, fontWeight: 600 } },
12946
+ React.createElement("input", { id: "expected-anchor-toggle", type: "checkbox", checked: showExpectedAnchors, onChange: function (event) { return setShowExpectedAnchors(event.target.checked); } }),
12947
+ "Show expected anchor markers"),
12948
+ React.createElement("label", { htmlFor: "asymmetric-variant-select", style: { display: 'flex', alignItems: 'center', gap: 8, fontWeight: 600 } },
12949
+ "Port glyph variant",
12950
+ React.createElement("select", { id: "asymmetric-variant-select", value: selectedVariantId, onChange: function (event) { return setSelectedVariantId(event.target.value); } }, asymmetricPortShapeVariants.map(function (variant) { return (React.createElement("option", { key: variant.id, value: variant.id }, variant.label)); }))),
12951
+ React.createElement("div", { style: { fontSize: 13, color: '#334155' } }, "Drag any asymmetric port around the host border or use the side controls to verify top/right/bottom/left orientation."),
12952
+ React.createElement("div", { style: { fontSize: 13, color: '#334155' } }, "Quick visual check: the multi-anchor row should keep the square touching the border; the legacy row may shift because it has no explicit placement or pivot anchors."),
12953
+ React.createElement("div", { style: { fontSize: 13, color: '#334155' } }, "Debug markers: amber square = expected border placement point, red dot = expected external attach point.")),
12954
+ React.createElement("div", { style: { display: 'grid', gap: 8 } },
12955
+ React.createElement("div", { style: { fontSize: 12, fontWeight: 600, color: '#1e293b' } }, "Move the external demo port"),
12956
+ React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8, justifyContent: 'flex-end' } }, sideLabels.map(function (side) { return (React.createElement("button", { key: side, type: "button", onClick: function () { return moveExternalPortToSide(side); }, style: { padding: '6px 10px' } },
12957
+ "External \u2192 ",
12958
+ side[0].toUpperCase() + side.slice(1))); })))),
12959
+ React.createElement(DisplayBoxStage, { containerRef: containerRef })));
12960
+ };
12961
+
12962
+ var VERTEX_CONTROL_ID = 'vertex-session-link';
12963
+ var VERTEX_CONTROL_SVG = 'M2 8 H14 M8 2 V14';
12964
+ var pointInRect = function (point, rect) {
12965
+ return point.x >= rect.x &&
12966
+ point.x <= rect.x + rect.width &&
12967
+ point.y >= rect.y &&
12968
+ point.y <= rect.y + rect.height;
12969
+ };
12970
+ var resolveTargetFromPointer = function (state, pointer, sourcePortId) {
12971
+ var closestPort = null;
12972
+ var _loop_1 = function (port) {
12973
+ if (port.id === sourcePortId)
12974
+ return "continue";
12975
+ var host = state.elements.find(function (element) { return element.id === port.elementId; });
12976
+ if (!host)
12977
+ return "continue";
12978
+ var world = {
12979
+ x: host.position.x + port.position.x,
12980
+ y: host.position.y + port.position.y,
12981
+ };
12982
+ var distance = Math.hypot(world.x - pointer.x, world.y - pointer.y);
12983
+ if (distance > 16)
12984
+ return "continue";
12985
+ if (!closestPort || distance < closestPort.distance) {
12986
+ closestPort = { id: port.id, distance: distance };
12987
+ }
12988
+ };
12989
+ for (var _i = 0, _a = state.ports; _i < _a.length; _i++) {
12990
+ var port = _a[_i];
12991
+ _loop_1(port);
12992
+ }
12993
+ if (closestPort) {
12994
+ return { type: 'port', id: closestPort.id };
12995
+ }
12996
+ var targetOrder = [vertexSessionDemoIds.elementTarget, vertexSessionDemoIds.portTarget];
12997
+ var _loop_2 = function (i) {
12998
+ var element = state.elements.find(function (item) { return item.id === targetOrder[i]; });
12999
+ if (!element)
13000
+ return "continue";
13001
+ if (pointInRect(pointer, {
13002
+ x: element.position.x,
13003
+ y: element.position.y,
13004
+ width: element.size.width,
13005
+ height: element.size.height,
13006
+ })) {
13007
+ return { value: { type: 'element', id: element.id } };
13008
+ }
13009
+ };
13010
+ for (var i = 0; i < targetOrder.length; i += 1) {
13011
+ var state_1 = _loop_2(i);
13012
+ if (typeof state_1 === "object")
13013
+ return state_1.value;
13014
+ }
13015
+ return { type: 'none' };
13016
+ };
13017
+ var createVertexSessionSourcePort = function (editor, sourcePoint) {
13018
+ var portId = "vertex-session-source-".concat(createId());
13019
+ var port = {
13020
+ id: portId,
13021
+ elementId: vertexSessionDemoIds.source,
13022
+ position: { x: 0, y: 0 },
13023
+ shapeId: 'port-dark',
13024
+ moveMode: 'border',
13025
+ anchorCenter: true,
13026
+ orientToHostBorder: true,
13027
+ };
13028
+ editor.addPortToElement(vertexSessionDemoIds.source, port);
13029
+ editor.movePortTo(portId, sourcePoint.x, sourcePoint.y);
13030
+ return portId;
13031
+ };
13032
+ var vertexControls = {
13033
+ controls: [
13034
+ {
13035
+ id: VERTEX_CONTROL_ID,
13036
+ targetKind: 'vertex',
13037
+ allowAllTargets: true,
13038
+ visibilityTriggers: ['target-hover'],
13039
+ tolerance: 14,
13040
+ icon: {
13041
+ svgPath: VERTEX_CONTROL_SVG,
13042
+ size: { width: 16, height: 16 },
13043
+ style: { fill: 'none', stroke: '#6a3da3', strokeWidth: 2, lineCap: 'round' },
13044
+ },
13045
+ },
13046
+ ],
13047
+ };
13048
+ var VertexControlLinkSessionDemo = function () {
13049
+ var _a, _b;
13050
+ var demo = vertexControlLinkSessionDemoConfig;
13051
+ var editorHandleRef = useRef(null);
13052
+ var activeSourcePortRef = useRef(null);
13053
+ var _c = useState(false), cancelOnConnect = _c[0], setCancelOnConnect = _c[1];
13054
+ var _d = useState([]), eventLog = _d[0], setEventLog = _d[1];
13055
+ var _e = useState('none'), lastResolution = _e[0], setLastResolution = _e[1];
13056
+ var _f = useState(false), isLogExpanded = _f[0], setIsLogExpanded = _f[1];
13057
+ var nextOffset = useOffsetSequence();
13058
+ var actionHelpers = useMemo(function () { return ({ nextOffset: nextOffset }); }, [nextOffset]);
13059
+ var onControlInteraction = useCallback(function (event) {
13060
+ var _a, _b;
13061
+ if (event.controlId !== VERTEX_CONTROL_ID || event.elementId !== vertexSessionDemoIds.source)
13062
+ return;
13063
+ var editor = editorHandleRef.current;
13064
+ if (!editor)
13065
+ return;
13066
+ if (event.eventType === 'drag-start') {
13067
+ var sourcePoint = (_b = (_a = event.vertex) === null || _a === void 0 ? void 0 : _a.position) !== null && _b !== void 0 ? _b : event.pointer.world;
13068
+ var sourcePortId = createVertexSessionSourcePort(editor, sourcePoint);
13069
+ activeSourcePortRef.current = sourcePortId;
13070
+ editor.startLinkFromPort(sourcePortId, sourcePoint);
13071
+ setLastResolution('started');
13072
+ return;
13073
+ }
13074
+ if (event.eventType === 'drag-move') {
13075
+ if (!activeSourcePortRef.current)
13076
+ return;
13077
+ editor.updateLinkPreview(event.pointer.world);
13078
+ return;
13079
+ }
13080
+ if (event.eventType === 'drag-end') {
13081
+ var sourcePortId = activeSourcePortRef.current;
13082
+ if (!sourcePortId)
13083
+ return;
13084
+ var state = editor.getState();
13085
+ var target = resolveTargetFromPointer(state, event.pointer.world, sourcePortId);
13086
+ if (target.type === 'port') {
13087
+ editor.completeLinkToPort(target.id);
13088
+ setLastResolution("completeLinkToPort(".concat(target.id, ")"));
13089
+ }
13090
+ else if (target.type === 'element') {
13091
+ editor.completeLinkToElement(target.id, event.pointer.world);
13092
+ setLastResolution("completeLinkToElement(".concat(target.id, ")"));
13093
+ }
13094
+ else {
13095
+ editor.cancelLink();
13096
+ setLastResolution('cancelLink()');
13097
+ }
13098
+ activeSourcePortRef.current = null;
13099
+ }
13100
+ }, []);
13101
+ var _g = useDemoEditor({
13102
+ createState: demo.createState,
13103
+ elementShapes: demo.elementShapes,
13104
+ portShapes: demo.portShapes,
13105
+ elementShapeHoverControls: vertexControls,
13106
+ onElementShapeHoverControlInteraction: onControlInteraction,
13107
+ }), containerRef = _g.containerRef, editorRef = _g.editorRef, diagramState = _g.diagramState, selection = _g.selection, snapEnabled = _g.snapEnabled, setSnapEnabled = _g.setSnapEnabled;
13108
+ editorHandleRef.current = editorRef.current;
13109
+ var controls = useDemoControls({
13110
+ demo: demo,
13111
+ editorRef: editorRef,
13112
+ diagramState: diagramState,
13113
+ selection: selection,
13114
+ snapEnabled: snapEnabled,
13115
+ setSnapEnabled: setSnapEnabled,
13116
+ actionHelpers: actionHelpers,
13117
+ });
13118
+ useEffect(function () {
13119
+ var editor = editorRef.current;
13120
+ if (!editor)
13121
+ return undefined;
13122
+ var append = function (entry) {
13123
+ setEventLog(function (prev) { return __spreadArray([entry], prev, true).slice(0, 16); });
13124
+ };
13125
+ var offStarted = editor.on('elementLinkStarted', function (payload) {
13126
+ append("elementLinkStarted source=".concat(payload.sourcePortId, " start=(").concat(Math.round(payload.startWorld.x), ",").concat(Math.round(payload.startWorld.y), ")"));
13127
+ });
13128
+ var offConnecting = editor.on('elementLinkConnecting', function (payload) {
13129
+ if (cancelOnConnect) {
13130
+ payload.cancel();
13131
+ }
13132
+ append("elementLinkConnecting source=".concat(payload.sourcePortId, " target=").concat(payload.targetPortId, " cancelled=").concat(payload.cancelled || cancelOnConnect));
13133
+ });
13134
+ var offEnded = editor.on('elementLinkEnded', function (payload) {
13135
+ var _a, _b;
13136
+ append("elementLinkEnded source=".concat(payload.sourcePortId, " targetPort=").concat((_a = payload.targetPortId) !== null && _a !== void 0 ? _a : '-', " targetElement=").concat((_b = payload.targetElementId) !== null && _b !== void 0 ? _b : '-', " cancelled=").concat(payload.cancelled));
13137
+ });
13138
+ return function () {
13139
+ offStarted();
13140
+ offConnecting();
13141
+ offEnded();
13142
+ };
13143
+ }, [editorRef, cancelOnConnect]);
13144
+ var linksCount = (_a = diagramState === null || diagramState === void 0 ? void 0 : diagramState.links.length) !== null && _a !== void 0 ? _a : 0;
13145
+ var portsCount = (_b = diagramState === null || diagramState === void 0 ? void 0 : diagramState.ports.length) !== null && _b !== void 0 ? _b : 0;
13146
+ var handleManualCancel = function () {
13147
+ var _a;
13148
+ (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.cancelLink();
13149
+ activeSourcePortRef.current = null;
13150
+ setLastResolution('cancelLink()');
13151
+ };
13152
+ return (React.createElement("section", null,
13153
+ React.createElement("div", { style: { marginBottom: 12 } },
13154
+ React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
13155
+ React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
13156
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
13157
+ React.createElement("div", { style: { marginBottom: 12, padding: 12, border: '1px solid #d9e1ec', borderRadius: 8, background: '#fbfdff' } },
13158
+ React.createElement("div", { style: { fontWeight: 700, marginBottom: 6 } }, "Required scenarios"),
13159
+ React.createElement("ol", { style: { marginTop: 0, marginBottom: 10, paddingLeft: 20, fontSize: 13 } },
13160
+ React.createElement("li", null, "Drag source vertex control to the existing port on the left side of Scenario 1 target."),
13161
+ React.createElement("li", null, "Drag source vertex control to Scenario 2 target body (not its border port)."),
13162
+ React.createElement("li", null, "Enable host cancellation, then repeat a completion drag to verify cancelled end event."),
13163
+ React.createElement("li", null, "Start a drag and press Cancel Active Session to verify temp visuals clear and cancelled end event."),
13164
+ React.createElement("li", null, "Use baseline native ports (bottom row) and drag from one port to the other to verify no regression.")),
13165
+ React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center', marginBottom: 8 } },
13166
+ React.createElement("label", { style: { fontSize: 13 } },
13167
+ React.createElement("input", { type: "checkbox", checked: cancelOnConnect, onChange: function (event) { return setCancelOnConnect(event.target.checked); }, style: { marginRight: 6 } }),
13168
+ "Host cancels on elementLinkConnecting"),
13169
+ React.createElement("button", { type: "button", onClick: handleManualCancel, style: { padding: '6px 10px' } }, "Cancel Active Session"),
13170
+ React.createElement("button", { type: "button", onClick: function () { return setIsLogExpanded(function (prev) { return !prev; }); }, style: { padding: '6px 10px' } }, isLogExpanded ? 'Collapse Lifecycle Log' : 'Expand Lifecycle Log'),
13171
+ React.createElement("span", { style: { fontSize: 13 } },
13172
+ "Ports: ",
13173
+ React.createElement("strong", null, portsCount),
13174
+ " | Links: ",
13175
+ React.createElement("strong", null, linksCount),
13176
+ " | Last resolution: ",
13177
+ React.createElement("strong", null, lastResolution))),
13178
+ React.createElement("div", { style: { fontSize: 12, color: '#2d3a4d' } },
13179
+ React.createElement("strong", null, "Lifecycle log:"),
13180
+ " ",
13181
+ isLogExpanded ? 'expanded' : 'collapsed',
13182
+ isLogExpanded ? (React.createElement("pre", { style: {
13183
+ marginTop: 6,
13184
+ padding: 8,
13185
+ maxHeight: 170,
13186
+ overflow: 'auto',
13187
+ background: '#ffffff',
13188
+ border: '1px solid #d9e1ec',
13189
+ borderRadius: 6,
13190
+ whiteSpace: 'pre-wrap',
13191
+ } }, eventLog.length ? eventLog.join('\n') : 'No events yet')) : null)),
13192
+ React.createElement(DisplayBoxStage, { containerRef: containerRef, stageStyle: gridStageStyle })));
13193
+ };
13194
+
11600
13195
  var wrapSimpleDemo = function (demo) { return function () { return React.createElement(SimpleDemo, { demo: demo }); }; };
11601
13196
  var demoTabs = [
11602
13197
  {
@@ -11695,6 +13290,12 @@ var demoTabs = [
11695
13290
  description: svgPathDemoConfig.description,
11696
13291
  Component: SvgPathDemo,
11697
13292
  },
13293
+ {
13294
+ id: asymmetricPortMultiAnchorDemoConfig.id,
13295
+ title: asymmetricPortMultiAnchorDemoConfig.title,
13296
+ description: asymmetricPortMultiAnchorDemoConfig.description,
13297
+ Component: AsymmetricPortMultiAnchorDemo,
13298
+ },
11698
13299
  {
11699
13300
  id: multiLevelTreeDemoConfig.id,
11700
13301
  title: multiLevelTreeDemoConfig.title,
@@ -11731,6 +13332,12 @@ var demoTabs = [
11731
13332
  description: shapeHoverControlsDemoConfig.description,
11732
13333
  Component: ShapeHoverControlsDemo,
11733
13334
  },
13335
+ {
13336
+ id: vertexControlLinkSessionDemoConfig.id,
13337
+ title: vertexControlLinkSessionDemoConfig.title,
13338
+ description: vertexControlLinkSessionDemoConfig.description,
13339
+ Component: VertexControlLinkSessionDemo,
13340
+ },
11734
13341
  {
11735
13342
  id: childConstraintsDemoConfig.id,
11736
13343
  title: childConstraintsDemoConfig.title,