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