orcasvn-react-diagrams 0.2.3 → 0.2.4

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 (32) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +15 -11
  3. package/dist/cjs/examples.js +857 -139
  4. package/dist/cjs/index.js +408 -41
  5. package/dist/cjs/types/api/types.d.ts +9 -0
  6. package/dist/cjs/types/displaybox/demos/labelStyleDemo.d.ts +2 -0
  7. package/dist/cjs/types/displaybox/demos/portPositionLimitsDemo.d.ts +2 -0
  8. package/dist/cjs/types/engine/DiagramEngine.d.ts +6 -0
  9. package/dist/cjs/types/engine/LinkRoutingService.d.ts +1 -1
  10. package/dist/cjs/types/renderer/konva/KonvaNodeFactory.d.ts +11 -0
  11. package/dist/cjs/types/renderer/konva/KonvaRenderer.d.ts +2 -0
  12. package/dist/cjs/types/strategies/ObstacleRouter.d.ts +2 -0
  13. package/dist/esm/examples.js +857 -139
  14. package/dist/esm/examples.js.map +1 -1
  15. package/dist/esm/index.js +408 -41
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/types/api/types.d.ts +9 -0
  18. package/dist/esm/types/displaybox/demos/labelStyleDemo.d.ts +2 -0
  19. package/dist/esm/types/displaybox/demos/portPositionLimitsDemo.d.ts +2 -0
  20. package/dist/esm/types/engine/DiagramEngine.d.ts +6 -0
  21. package/dist/esm/types/engine/LinkRoutingService.d.ts +1 -1
  22. package/dist/esm/types/renderer/konva/KonvaNodeFactory.d.ts +11 -0
  23. package/dist/esm/types/renderer/konva/KonvaRenderer.d.ts +2 -0
  24. package/dist/esm/types/strategies/ObstacleRouter.d.ts +2 -0
  25. package/dist/examples.d.ts +9 -0
  26. package/dist/index.d.ts +10 -1
  27. package/package.json +11 -11
  28. package/src/displaybox/demos/ObstacleRoutingDemoTab.tsx +11 -10
  29. package/src/displaybox/demos/index.tsx +27 -13
  30. package/src/displaybox/demos/labelStyleDemo.ts +101 -0
  31. package/src/displaybox/demos/obstacleRoutingDemo.ts +212 -176
  32. package/src/displaybox/demos/portPositionLimitsDemo.ts +211 -0
package/dist/esm/index.js CHANGED
@@ -1281,21 +1281,42 @@ var ObstacleRouter = /** @class */ (function () {
1281
1281
  ObstacleRouter.prototype.computeStubEndpoint = function (point, endpoint, bounds, obstacles) {
1282
1282
  if (!(endpoint === null || endpoint === void 0 ? void 0 : endpoint.onEdgeSide) || !endpoint.rect)
1283
1283
  return null;
1284
- var normal = this.getNormal(endpoint.onEdgeSide);
1284
+ var outwardNormal = this.getNormal(endpoint.onEdgeSide);
1285
+ var selectedNormal = outwardNormal;
1286
+ var maxLength = this.computeAvailableStubLength(point, outwardNormal, bounds, obstacles, endpoint.elementId);
1287
+ // If the outward normal is fully blocked by route bounds on the endpoint host, fall back inward
1288
+ // to preserve a visible perpendicular stub for bounded parent-child routes.
1289
+ if (maxLength <= this.tolerance && bounds && this.rectsEqual(bounds, endpoint.rect)) {
1290
+ var inwardNormal = { x: -outwardNormal.x, y: -outwardNormal.y };
1291
+ var inwardLength = this.computeAvailableStubLength(point, inwardNormal, bounds, obstacles, endpoint.elementId);
1292
+ if (inwardLength > maxLength) {
1293
+ selectedNormal = inwardNormal;
1294
+ maxLength = inwardLength;
1295
+ }
1296
+ }
1297
+ if (maxLength <= 0)
1298
+ return null;
1299
+ return {
1300
+ x: point.x + selectedNormal.x * maxLength,
1301
+ y: point.y + selectedNormal.y * maxLength,
1302
+ };
1303
+ };
1304
+ ObstacleRouter.prototype.computeAvailableStubLength = function (point, normal, bounds, obstacles, ignoreId) {
1285
1305
  var maxLength = this.stubLength;
1286
1306
  if (bounds) {
1287
1307
  maxLength = Math.min(maxLength, this.distanceToBounds(point, normal, bounds));
1288
1308
  }
1289
- var obstacleLimit = this.distanceToObstacles(point, normal, obstacles, endpoint.elementId);
1309
+ var obstacleLimit = this.distanceToObstacles(point, normal, obstacles, ignoreId);
1290
1310
  if (obstacleLimit !== null) {
1291
1311
  maxLength = Math.min(maxLength, obstacleLimit);
1292
1312
  }
1293
- if (maxLength <= 0)
1294
- return null;
1295
- return {
1296
- x: point.x + normal.x * maxLength,
1297
- y: point.y + normal.y * maxLength,
1298
- };
1313
+ return maxLength;
1314
+ };
1315
+ ObstacleRouter.prototype.rectsEqual = function (a, b) {
1316
+ return (Math.abs(a.x - b.x) <= this.tolerance &&
1317
+ Math.abs(a.y - b.y) <= this.tolerance &&
1318
+ Math.abs(a.width - b.width) <= this.tolerance &&
1319
+ Math.abs(a.height - b.height) <= this.tolerance);
1299
1320
  };
1300
1321
  ObstacleRouter.prototype.getNormal = function (side) {
1301
1322
  switch (side) {
@@ -1592,7 +1613,7 @@ var borderSideToInwardRotation = function (side) {
1592
1613
  return 0;
1593
1614
  };
1594
1615
 
1595
- var POSITION_EPSILON$1 = 1e-6;
1616
+ var POSITION_EPSILON$2 = 1e-6;
1596
1617
  var MIN_TRUNCATION_WIDTH_SAFETY_EPSILON = 0.5;
1597
1618
  var FONT_SIZE_TRUNCATION_SAFETY_RATIO = 1.4;
1598
1619
  var FONT_SIZE_TRUNCATION_SAFETY_BASE = 2;
@@ -1618,7 +1639,7 @@ var TextLayoutService = /** @class */ (function () {
1618
1639
  var textRenderPadding = this.resolveTextRenderPadding(text.style);
1619
1640
  var horizontalRenderInset = textRenderPadding * 2;
1620
1641
  var verticalRenderInset = textRenderPadding * 2;
1621
- var zeroPaddingInset = padding <= POSITION_EPSILON$1 && this.shouldTrackOwnerBoundLayout(text) ? 0.5 : 0;
1642
+ var zeroPaddingInset = padding <= POSITION_EPSILON$2 && this.shouldTrackOwnerBoundLayout(text) ? 0.5 : 0;
1622
1643
  var drawableWidth = maxWidth !== undefined ? Math.max(0, maxWidth - zeroPaddingInset * 2 - horizontalRenderInset) : undefined;
1623
1644
  var drawableHeight = maxHeight !== undefined ? Math.max(0, maxHeight - zeroPaddingInset * 2 - verticalRenderInset) : undefined;
1624
1645
  var lineHeight = this.textMeasurer.measure(__assign(__assign({}, text), { content: 'M' })).height;
@@ -2316,6 +2337,7 @@ var resolvePortWorldTransform = function (options) {
2316
2337
  };
2317
2338
 
2318
2339
  var EDGE_TOLERANCE = 0.5;
2340
+ var POSITION_EPSILON$1 = 1e-6;
2319
2341
  var LinkRoutingService = /** @class */ (function () {
2320
2342
  function LinkRoutingService(config) {
2321
2343
  this.model = config.model;
@@ -2424,11 +2446,27 @@ var LinkRoutingService = /** @class */ (function () {
2424
2446
  if (points.length < 2) {
2425
2447
  return [__assign({}, source), __assign({}, target)];
2426
2448
  }
2449
+ var sourceDelta = {
2450
+ x: source.x - points[0].x,
2451
+ y: source.y - points[0].y,
2452
+ };
2453
+ var targetDelta = {
2454
+ x: target.x - points[points.length - 1].x,
2455
+ y: target.y - points[points.length - 1].y,
2456
+ };
2457
+ var shouldTranslateAllPoints = Math.abs(sourceDelta.x - targetDelta.x) <= POSITION_EPSILON$1 &&
2458
+ Math.abs(sourceDelta.y - targetDelta.y) <= POSITION_EPSILON$1;
2427
2459
  return points.map(function (point, index) {
2428
2460
  if (index === 0)
2429
2461
  return __assign({}, source);
2430
2462
  if (index === points.length - 1)
2431
2463
  return __assign({}, target);
2464
+ if (shouldTranslateAllPoints) {
2465
+ return {
2466
+ x: point.x + sourceDelta.x,
2467
+ y: point.y + sourceDelta.y,
2468
+ };
2469
+ }
2432
2470
  return __assign({}, point);
2433
2471
  });
2434
2472
  };
@@ -2594,16 +2632,13 @@ var LinkRoutingService = /** @class */ (function () {
2594
2632
  var oppositeElementId = (_b = this.model.getPort(oppositePortId)) === null || _b === void 0 ? void 0 : _b.elementId;
2595
2633
  if (!elementId || !oppositeElementId)
2596
2634
  return 'external';
2597
- return this.hasAncestorRelation(elementId, oppositeElementId) ? 'internal' : 'external';
2635
+ return this.isAncestorOf(elementId, oppositeElementId) ? 'internal' : 'external';
2598
2636
  };
2599
- LinkRoutingService.prototype.hasAncestorRelation = function (sourceElementId, targetElementId) {
2600
- if (sourceElementId === targetElementId)
2637
+ LinkRoutingService.prototype.isAncestorOf = function (candidateAncestorId, candidateDescendantId) {
2638
+ if (candidateAncestorId === candidateDescendantId)
2601
2639
  return false;
2602
- var sourceChain = this.getAncestorChain(sourceElementId);
2603
- if (sourceChain.includes(targetElementId))
2604
- return true;
2605
- var targetChain = this.getAncestorChain(targetElementId);
2606
- return targetChain.includes(sourceElementId);
2640
+ var descendantChain = this.getAncestorChain(candidateDescendantId);
2641
+ return descendantChain.includes(candidateAncestorId);
2607
2642
  };
2608
2643
  LinkRoutingService.prototype.getAncestorChain = function (elementId) {
2609
2644
  var _a;
@@ -2763,11 +2798,17 @@ var DiagramEngine = /** @class */ (function () {
2763
2798
  DiagramEngine.prototype.load = function (state) {
2764
2799
  var _this = this;
2765
2800
  var patches = this.commandQueue.run(createLoadCommand(state), this.model);
2766
- var allPatches = this.mutationPipeline.run({
2801
+ var mutationPatches = this.mutationPipeline.run({
2767
2802
  basePatches: patches,
2768
2803
  layoutSteps: [function () { return _this.applyAllLayouts(); }],
2769
2804
  includeEmptyLinkRouting: true,
2770
2805
  });
2806
+ var normalizedPorts = this.normalizePortsForHostPolicies();
2807
+ var normalizedLinkPatches = normalizedPorts.movedPortIds.length > 0
2808
+ ? this.updateLinksForPorts(normalizedPorts.movedPortIds)
2809
+ : [];
2810
+ var textPresentationPatches = this.resolveAllTextPresentationPatches(false);
2811
+ var allPatches = __spreadArray(__spreadArray(__spreadArray(__spreadArray([], mutationPatches, true), normalizedPorts.patches, true), normalizedLinkPatches, true), textPresentationPatches, true);
2771
2812
  this.emitChange(allPatches);
2772
2813
  };
2773
2814
  DiagramEngine.prototype.getState = function () {
@@ -3359,7 +3400,7 @@ var DiagramEngine = /** @class */ (function () {
3359
3400
  if (!host)
3360
3401
  return;
3361
3402
  host.portIds.forEach(function (portId) {
3362
- var _a;
3403
+ var _a, _b, _c;
3363
3404
  var port = _this.model.getPort(portId);
3364
3405
  if (!port)
3365
3406
  return;
@@ -3382,11 +3423,16 @@ var DiagramEngine = /** @class */ (function () {
3382
3423
  return;
3383
3424
  }
3384
3425
  var projected = _this.resolveBorderPortResizeProjection(port.position, host.shapeId, sizeInfo, host.size);
3385
- var unchanged = Math.abs(projected.x - port.position.x) <= POSITION_EPSILON &&
3386
- Math.abs(projected.y - port.position.y) <= POSITION_EPSILON;
3426
+ var constrained = _this.resolveConstrainedPortRelativePosition(port, host, projected);
3427
+ var unchanged = Math.abs(constrained.position.x - port.position.x) <= POSITION_EPSILON &&
3428
+ Math.abs(constrained.position.y - port.position.y) <= POSITION_EPSILON &&
3429
+ ((_b = constrained.currentAnchorId) !== null && _b !== void 0 ? _b : null) === ((_c = port.currentAnchorId) !== null && _c !== void 0 ? _c : null);
3387
3430
  if (unchanged)
3388
3431
  return;
3389
- var patchesForPort = _this.commandQueue.run(createMovePortCommand(port.id, { position: projected, currentAnchorId: null }), _this.model);
3432
+ var patchesForPort = _this.commandQueue.run(createMovePortCommand(port.id, {
3433
+ position: constrained.position,
3434
+ currentAnchorId: constrained.currentAnchorId,
3435
+ }), _this.model);
3390
3436
  reprojectionPatches.push.apply(reprojectionPatches, patchesForPort);
3391
3437
  movedPortIds.add(port.id);
3392
3438
  });
@@ -3443,10 +3489,10 @@ var DiagramEngine = /** @class */ (function () {
3443
3489
  return this.projectPointToHostBorder(projectedTarget, shapeId, nextSize);
3444
3490
  };
3445
3491
  DiagramEngine.prototype.resolveConstrainedPortRelativePosition = function (port, element, requestedPosition) {
3446
- var _a, _b, _c, _d, _e;
3492
+ var _a, _b, _c, _d, _e, _f, _g;
3447
3493
  var position = __assign({}, requestedPosition);
3448
3494
  var effectiveMoveMode = this.resolveEffectivePortMoveMode(port, element);
3449
- if (effectiveMoveMode && effectiveMoveMode !== 'free' && effectiveMoveMode !== 'anchors') {
3495
+ if (effectiveMoveMode === 'inside' || effectiveMoveMode === 'border') {
3450
3496
  var widthLimit = Math.max(0, element.size.width - ((_b = (_a = port.size) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : 0));
3451
3497
  var heightLimit = Math.max(0, element.size.height - ((_d = (_c = port.size) === null || _c === void 0 ? void 0 : _c.height) !== null && _d !== void 0 ? _d : 0));
3452
3498
  var bounds = {
@@ -3470,9 +3516,12 @@ var DiagramEngine = /** @class */ (function () {
3470
3516
  height: Math.max(0, element.size.height),
3471
3517
  };
3472
3518
  }
3473
- position = effectiveMoveMode === 'inside'
3474
- ? clampToRect(position, bounds)
3475
- : this.constrainPortToHostBorder(position, element);
3519
+ if (effectiveMoveMode === 'inside') {
3520
+ position = clampToRect(position, bounds);
3521
+ }
3522
+ else {
3523
+ position = this.constrainPortToHostBorder(position, element);
3524
+ }
3476
3525
  }
3477
3526
  var style = port.style;
3478
3527
  if ((style === null || style === void 0 ? void 0 : style.moveAxis) === 'horizontal') {
@@ -3486,18 +3535,134 @@ var DiagramEngine = /** @class */ (function () {
3486
3535
  position.x = clamp(position.x, bounds.x, bounds.x + bounds.width);
3487
3536
  position.y = clamp(position.y, bounds.y, bounds.y + bounds.height);
3488
3537
  }
3538
+ if (effectiveMoveMode === 'border') {
3539
+ var resolvedBorder = this.resolveBorderPositionWithLimits(position, element, (_e = element.portMovement) === null || _e === void 0 ? void 0 : _e.positionLimits);
3540
+ position = resolvedBorder !== null && resolvedBorder !== void 0 ? resolvedBorder : this.constrainPortToHostBorder(position, element);
3541
+ }
3542
+ else {
3543
+ position = this.applyPortPositionLimits(position, (_f = element.portMovement) === null || _f === void 0 ? void 0 : _f.positionLimits);
3544
+ }
3489
3545
  if (effectiveMoveMode === 'anchors') {
3490
- var anchor = this.resolveNearestPortAnchor(element, position);
3546
+ var anchor = this.resolveNearestPortAnchor(element, position, port.currentAnchorId);
3491
3547
  if (anchor) {
3492
3548
  return {
3493
3549
  position: __assign({}, anchor.position),
3494
3550
  currentAnchorId: anchor.id,
3495
3551
  };
3496
3552
  }
3497
- return { position: position, currentAnchorId: (_e = port.currentAnchorId) !== null && _e !== void 0 ? _e : null };
3553
+ return { position: __assign({}, port.position), currentAnchorId: (_g = port.currentAnchorId) !== null && _g !== void 0 ? _g : null };
3498
3554
  }
3499
3555
  return { position: position, currentAnchorId: null };
3500
3556
  };
3557
+ DiagramEngine.prototype.applyPortPositionLimits = function (position, limits) {
3558
+ if (!limits)
3559
+ return __assign({}, position);
3560
+ var x = position.x;
3561
+ var y = position.y;
3562
+ if (limits.x) {
3563
+ if (typeof limits.x.min === 'number') {
3564
+ x = Math.max(x, limits.x.min);
3565
+ }
3566
+ if (typeof limits.x.max === 'number') {
3567
+ x = Math.min(x, limits.x.max);
3568
+ }
3569
+ }
3570
+ if (limits.y) {
3571
+ if (typeof limits.y.min === 'number') {
3572
+ y = Math.max(y, limits.y.min);
3573
+ }
3574
+ if (typeof limits.y.max === 'number') {
3575
+ y = Math.min(y, limits.y.max);
3576
+ }
3577
+ }
3578
+ return { x: x, y: y };
3579
+ };
3580
+ DiagramEngine.prototype.resolveBorderPositionWithLimits = function (position, element, limits) {
3581
+ var _this = this;
3582
+ var _a, _b, _c, _d;
3583
+ var borderBase = this.constrainPortToHostBorder(position, element);
3584
+ if (!limits)
3585
+ return borderBase;
3586
+ if (this.isPointWithinPositionLimits(borderBase, limits))
3587
+ return borderBase;
3588
+ var target = this.applyPortPositionLimits(borderBase, limits);
3589
+ var xValues = new Set([borderBase.x, target.x, 0, Math.max(0, element.size.width)]);
3590
+ var yValues = new Set([borderBase.y, target.y, 0, Math.max(0, element.size.height)]);
3591
+ if (typeof ((_a = limits.x) === null || _a === void 0 ? void 0 : _a.min) === 'number')
3592
+ xValues.add(limits.x.min);
3593
+ if (typeof ((_b = limits.x) === null || _b === void 0 ? void 0 : _b.max) === 'number')
3594
+ xValues.add(limits.x.max);
3595
+ if (typeof ((_c = limits.y) === null || _c === void 0 ? void 0 : _c.min) === 'number')
3596
+ yValues.add(limits.y.min);
3597
+ if (typeof ((_d = limits.y) === null || _d === void 0 ? void 0 : _d.max) === 'number')
3598
+ yValues.add(limits.y.max);
3599
+ var seeds = [];
3600
+ xValues.forEach(function (x) { return yValues.forEach(function (y) { return seeds.push({ x: x, y: y }); }); });
3601
+ seeds.push({ x: borderBase.x, y: target.y }, { x: target.x, y: borderBase.y });
3602
+ var candidates = [];
3603
+ var seen = new Set();
3604
+ seeds.forEach(function (seed) {
3605
+ var candidate = _this.constrainPortToHostBorder(seed, element);
3606
+ var key = "".concat(candidate.x.toFixed(6), ":").concat(candidate.y.toFixed(6));
3607
+ if (seen.has(key))
3608
+ return;
3609
+ seen.add(key);
3610
+ candidates.push(candidate);
3611
+ });
3612
+ var valid = candidates.filter(function (candidate) { return _this.isPointWithinPositionLimits(candidate, limits); });
3613
+ if (valid.length === 0)
3614
+ return null;
3615
+ return valid.reduce(function (best, candidate) {
3616
+ var bestDistance = Math.pow((best.x - target.x), 2) + Math.pow((best.y - target.y), 2);
3617
+ var candidateDistance = Math.pow((candidate.x - target.x), 2) + Math.pow((candidate.y - target.y), 2);
3618
+ if (candidateDistance < bestDistance)
3619
+ return candidate;
3620
+ if (candidateDistance > bestDistance)
3621
+ return best;
3622
+ if (candidate.x < best.x)
3623
+ return candidate;
3624
+ if (candidate.x > best.x)
3625
+ return best;
3626
+ return candidate.y < best.y ? candidate : best;
3627
+ }, valid[0]);
3628
+ };
3629
+ DiagramEngine.prototype.isPointWithinPositionLimits = function (position, limits) {
3630
+ if (!limits)
3631
+ return true;
3632
+ if (limits.x) {
3633
+ if (typeof limits.x.min === 'number' && position.x < limits.x.min)
3634
+ return false;
3635
+ if (typeof limits.x.max === 'number' && position.x > limits.x.max)
3636
+ return false;
3637
+ }
3638
+ if (limits.y) {
3639
+ if (typeof limits.y.min === 'number' && position.y < limits.y.min)
3640
+ return false;
3641
+ if (typeof limits.y.max === 'number' && position.y > limits.y.max)
3642
+ return false;
3643
+ }
3644
+ return true;
3645
+ };
3646
+ DiagramEngine.prototype.filterAnchorsByPositionLimits = function (anchors, limits) {
3647
+ if (!limits)
3648
+ return anchors;
3649
+ return anchors.filter(function (anchor) {
3650
+ var position = anchor.position;
3651
+ if (limits.x) {
3652
+ if (typeof limits.x.min === 'number' && position.x < limits.x.min)
3653
+ return false;
3654
+ if (typeof limits.x.max === 'number' && position.x > limits.x.max)
3655
+ return false;
3656
+ }
3657
+ if (limits.y) {
3658
+ if (typeof limits.y.min === 'number' && position.y < limits.y.min)
3659
+ return false;
3660
+ if (typeof limits.y.max === 'number' && position.y > limits.y.max)
3661
+ return false;
3662
+ }
3663
+ return true;
3664
+ });
3665
+ };
3501
3666
  DiagramEngine.prototype.resolveEffectivePortMoveMode = function (port, element) {
3502
3667
  var _a, _b;
3503
3668
  return (_b = (_a = element.portMovement) === null || _a === void 0 ? void 0 : _a.moveMode) !== null && _b !== void 0 ? _b : port.moveMode;
@@ -3532,7 +3697,8 @@ var DiagramEngine = /** @class */ (function () {
3532
3697
  return [];
3533
3698
  };
3534
3699
  DiagramEngine.prototype.resolveNearestPortAnchor = function (element, target, preferredAnchorId) {
3535
- var anchors = this.resolvePortAnchorsForElement(element);
3700
+ var _a;
3701
+ var anchors = this.filterAnchorsByPositionLimits(this.resolvePortAnchorsForElement(element), (_a = element.portMovement) === null || _a === void 0 ? void 0 : _a.positionLimits);
3536
3702
  if (anchors.length === 0)
3537
3703
  return null;
3538
3704
  if (preferredAnchorId) {
@@ -3597,6 +3763,46 @@ var DiagramEngine = /** @class */ (function () {
3597
3763
  DiagramEngine.prototype.resolveTextPresentation = function (text) {
3598
3764
  return this.textLayoutService.resolveTextPresentation(text);
3599
3765
  };
3766
+ DiagramEngine.prototype.resolveAllTextPresentationPatches = function (emitTextUpdated) {
3767
+ var _this = this;
3768
+ var textPatches = [];
3769
+ this.model.texts.forEach(function (text) {
3770
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
3771
+ var current = text.toData();
3772
+ var resolved = _this.resolveTextPresentation(current);
3773
+ var currentDisplay = (_a = current.displayContent) !== null && _a !== void 0 ? _a : current.content;
3774
+ var sizeChanged = !current.size ||
3775
+ current.size.width !== resolved.size.width ||
3776
+ current.size.height !== resolved.size.height;
3777
+ var displayChanged = currentDisplay !== resolved.displayContent;
3778
+ var offsetChanged = ((_c = (_b = current.displayOffset) === null || _b === void 0 ? void 0 : _b.x) !== null && _c !== void 0 ? _c : 0) !== ((_e = (_d = resolved.displayOffset) === null || _d === void 0 ? void 0 : _d.x) !== null && _e !== void 0 ? _e : 0) ||
3779
+ ((_g = (_f = current.displayOffset) === null || _f === void 0 ? void 0 : _f.y) !== null && _g !== void 0 ? _g : 0) !== ((_j = (_h = resolved.displayOffset) === null || _h === void 0 ? void 0 : _h.y) !== null && _j !== void 0 ? _j : 0);
3780
+ var clipChanged = ((_l = (_k = current.displayClipSize) === null || _k === void 0 ? void 0 : _k.width) !== null && _l !== void 0 ? _l : 0) !== ((_o = (_m = resolved.displayClipSize) === null || _m === void 0 ? void 0 : _m.width) !== null && _o !== void 0 ? _o : 0) ||
3781
+ ((_q = (_p = current.displayClipSize) === null || _p === void 0 ? void 0 : _p.height) !== null && _q !== void 0 ? _q : 0) !== ((_s = (_r = resolved.displayClipSize) === null || _r === void 0 ? void 0 : _r.height) !== null && _s !== void 0 ? _s : 0);
3782
+ if (!sizeChanged && !displayChanged && !offsetChanged && !clipChanged)
3783
+ return;
3784
+ text.setSize(resolved.size);
3785
+ text.setDisplayContent(resolved.displayContent);
3786
+ text.setDisplayOffset(resolved.displayOffset);
3787
+ text.setDisplayClipSize(resolved.displayClipSize);
3788
+ textPatches.push(patchUpdate('text', text.id, {
3789
+ size: resolved.size,
3790
+ displayContent: resolved.displayContent,
3791
+ displayOffset: resolved.displayOffset,
3792
+ displayClipSize: resolved.displayClipSize,
3793
+ }));
3794
+ if (emitTextUpdated) {
3795
+ _this.events.emit('textUpdated', {
3796
+ textId: text.id,
3797
+ ownerId: text.ownerId,
3798
+ content: text.content,
3799
+ displayContent: resolved.displayContent,
3800
+ reason: 'layout',
3801
+ });
3802
+ }
3803
+ });
3804
+ return textPatches;
3805
+ };
3600
3806
  DiagramEngine.prototype.emitSelection = function () {
3601
3807
  var _this = this;
3602
3808
  var selectedIds = this.selection.get();
@@ -3686,6 +3892,40 @@ var DiagramEngine = /** @class */ (function () {
3686
3892
  DiagramEngine.prototype.routeLinksWithEmptyPoints = function () {
3687
3893
  return this.linkRoutingService.routeLinksWithEmptyPoints();
3688
3894
  };
3895
+ DiagramEngine.prototype.normalizePortsForHostPolicies = function (portIds) {
3896
+ var _this = this;
3897
+ var ids = portIds !== null && portIds !== void 0 ? portIds : Array.from(this.model.ports.keys());
3898
+ var normalizedPatches = [];
3899
+ var movedPortIds = new Set();
3900
+ ids.forEach(function (portId) {
3901
+ var _a, _b;
3902
+ var port = _this.model.getPort(portId);
3903
+ if (!port)
3904
+ return;
3905
+ var element = _this.model.getElement(port.elementId);
3906
+ if (!element)
3907
+ return;
3908
+ var constrained = _this.resolveConstrainedPortRelativePosition(port, element, port.position);
3909
+ var previousAnchorId = (_a = port.currentAnchorId) !== null && _a !== void 0 ? _a : null;
3910
+ var shouldPersistAnchorId = previousAnchorId !== null || constrained.currentAnchorId === null;
3911
+ var nextAnchorId = shouldPersistAnchorId ? ((_b = constrained.currentAnchorId) !== null && _b !== void 0 ? _b : null) : previousAnchorId;
3912
+ var unchanged = Math.abs(constrained.position.x - port.position.x) <= POSITION_EPSILON &&
3913
+ Math.abs(constrained.position.y - port.position.y) <= POSITION_EPSILON &&
3914
+ previousAnchorId === nextAnchorId;
3915
+ if (unchanged)
3916
+ return;
3917
+ var update = {
3918
+ position: constrained.position,
3919
+ };
3920
+ if (shouldPersistAnchorId) {
3921
+ update.currentAnchorId = constrained.currentAnchorId;
3922
+ }
3923
+ var patches = _this.commandQueue.run(createMovePortCommand(port.id, update), _this.model);
3924
+ normalizedPatches.push.apply(normalizedPatches, patches);
3925
+ movedPortIds.add(port.id);
3926
+ });
3927
+ return { patches: normalizedPatches, movedPortIds: Array.from(movedPortIds) };
3928
+ };
3689
3929
  DiagramEngine.prototype.computeRemovalDiff = function (before) {
3690
3930
  var after = this.model.toState();
3691
3931
  var removedPatches = [];
@@ -3931,6 +4171,21 @@ var KonvaNodeFactory = /** @class */ (function () {
3931
4171
  }
3932
4172
  return node;
3933
4173
  };
4174
+ KonvaNodeFactory.prototype.createTextBackgroundNode = function (config) {
4175
+ return new this.konva.Rect({
4176
+ id: config.id,
4177
+ x: config.x,
4178
+ y: config.y,
4179
+ width: config.width,
4180
+ height: config.height,
4181
+ fill: config.fill,
4182
+ stroke: config.stroke,
4183
+ strokeWidth: config.strokeWidth,
4184
+ cornerRadius: config.cornerRadius,
4185
+ name: 'text-background',
4186
+ listening: false,
4187
+ });
4188
+ };
3934
4189
  KonvaNodeFactory.prototype.createHandleNode = function (config) {
3935
4190
  return new this.konva.Rect({
3936
4191
  id: config.id,
@@ -4111,6 +4366,7 @@ var KonvaRenderer = /** @class */ (function () {
4111
4366
  this.portNodes = new Map();
4112
4367
  this.linkNodes = new Map();
4113
4368
  this.textNodes = new Map();
4369
+ this.textBackgroundNodes = new Map();
4114
4370
  this.selectedIds = new Set();
4115
4371
  this.tempLinkNode = null;
4116
4372
  this.tempPortNode = null;
@@ -4410,6 +4666,8 @@ var KonvaRenderer = /** @class */ (function () {
4410
4666
  this.elementNodes.clear();
4411
4667
  this.portNodes.clear();
4412
4668
  this.linkNodes.clear();
4669
+ this.textBackgroundNodes.forEach(function (node) { var _a; return (_a = node.destroy) === null || _a === void 0 ? void 0 : _a.call(node); });
4670
+ this.textBackgroundNodes.clear();
4413
4671
  this.textNodes.clear();
4414
4672
  this.resizeHandleNodes.clear();
4415
4673
  this.linkHandleNodes.clear();
@@ -4616,7 +4874,7 @@ var KonvaRenderer = /** @class */ (function () {
4616
4874
  var _this = this;
4617
4875
  var texts = Array.from(model.texts.values());
4618
4876
  texts.forEach(function (text) {
4619
- var _a, _b, _c, _d, _e, _f, _g, _h;
4877
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
4620
4878
  var node = _this.textNodes.get(text.id);
4621
4879
  if (!node) {
4622
4880
  node = _this.nodeFactory.createTextNode(text.toData());
@@ -4625,32 +4883,124 @@ var KonvaRenderer = /** @class */ (function () {
4625
4883
  }
4626
4884
  var position = (_c = model.getTextWorldPosition(text.id)) !== null && _c !== void 0 ? _c : text.position;
4627
4885
  var displayOffset = (_d = text.displayOffset) !== null && _d !== void 0 ? _d : { x: 0, y: 0 };
4628
- _this.updatePosition(node, {
4886
+ var textPosition = {
4629
4887
  x: position.x + displayOffset.x,
4630
4888
  y: position.y + displayOffset.y,
4889
+ };
4890
+ _this.updatePosition(node, {
4891
+ x: textPosition.x,
4892
+ y: textPosition.y,
4631
4893
  });
4632
4894
  if (node.setAttrs) {
4633
- var style = text.style;
4634
- var defaults = resolveTextStyleDefaults(style);
4635
- var baseFill = (_g = (_f = (_e = style === null || style === void 0 ? void 0 : style.fill) !== null && _e !== void 0 ? _e : _this.getNodeAttr(node, '__baseFill')) !== null && _f !== void 0 ? _f : _this.getNodeAttr(node, 'fill')) !== null && _g !== void 0 ? _g : 'black';
4895
+ var style_1 = text.style;
4896
+ var defaults = resolveTextStyleDefaults(style_1);
4897
+ var baseFill = (_g = (_f = (_e = style_1 === null || style_1 === void 0 ? void 0 : style_1.fill) !== null && _e !== void 0 ? _e : _this.getNodeAttr(node, '__baseFill')) !== null && _f !== void 0 ? _f : _this.getNodeAttr(node, 'fill')) !== null && _g !== void 0 ? _g : 'black';
4636
4898
  var fill = _this.selectedIds.has(text.id) ? '#ff7a00' : baseFill;
4637
- var clip = text.displayClipSize;
4638
- node.setAttrs(__assign(__assign({ text: (_h = text.displayContent) !== null && _h !== void 0 ? _h : text.content, fontSize: defaults.fontSize, fontFamily: defaults.fontFamily, lineHeight: defaults.lineHeight }, (style !== null && style !== void 0 ? style : {})), { width: clip === null || clip === void 0 ? void 0 : clip.width, height: clip === null || clip === void 0 ? void 0 : clip.height, wrap: clip ? 'none' : undefined, ellipsis: false, fill: fill, __baseFill: baseFill,
4899
+ var clip_1 = text.displayClipSize;
4900
+ node.setAttrs(__assign(__assign({ text: (_h = text.displayContent) !== null && _h !== void 0 ? _h : text.content, fontSize: defaults.fontSize, fontFamily: defaults.fontFamily, lineHeight: defaults.lineHeight }, (style_1 !== null && style_1 !== void 0 ? style_1 : {})), { width: clip_1 === null || clip_1 === void 0 ? void 0 : clip_1.width, height: clip_1 === null || clip_1 === void 0 ? void 0 : clip_1.height, wrap: clip_1 ? 'none' : undefined, ellipsis: false, fill: fill, __baseFill: baseFill,
4639
4901
  // Keep metadata for tests/debugging; Konva clip attrs do not apply on Text nodes.
4640
- clipX: clip ? 0 : undefined, clipY: clip ? 0 : undefined, clipWidth: clip === null || clip === void 0 ? void 0 : clip.width, clipHeight: clip === null || clip === void 0 ? void 0 : clip.height }));
4902
+ clipX: clip_1 ? 0 : undefined, clipY: clip_1 ? 0 : undefined, clipWidth: clip_1 === null || clip_1 === void 0 ? void 0 : clip_1.width, clipHeight: clip_1 === null || clip_1 === void 0 ? void 0 : clip_1.height }));
4903
+ }
4904
+ var style = text.style;
4905
+ var backgroundFill = style === null || style === void 0 ? void 0 : style.backgroundFill;
4906
+ var clip = text.displayClipSize;
4907
+ var backgroundWidth = (_j = clip === null || clip === void 0 ? void 0 : clip.width) !== null && _j !== void 0 ? _j : (_k = text.size) === null || _k === void 0 ? void 0 : _k.width;
4908
+ var backgroundHeight = (_l = clip === null || clip === void 0 ? void 0 : clip.height) !== null && _l !== void 0 ? _l : (_m = text.size) === null || _m === void 0 ? void 0 : _m.height;
4909
+ var backgroundMeta = _this.resolveTextBackgroundMeta(model, text.toData());
4910
+ var hasBackground = typeof backgroundFill === 'string' &&
4911
+ backgroundFill.length > 0 &&
4912
+ typeof backgroundWidth === 'number' &&
4913
+ typeof backgroundHeight === 'number' &&
4914
+ backgroundWidth > 0 &&
4915
+ backgroundHeight > 0;
4916
+ if (hasBackground) {
4917
+ var baseX = textPosition.x + backgroundMeta.inset;
4918
+ var baseY = textPosition.y + backgroundMeta.inset;
4919
+ var width = Math.max(0, backgroundWidth - backgroundMeta.inset * 2);
4920
+ var height = Math.max(0, backgroundHeight - backgroundMeta.inset);
4921
+ var backgroundStroke = typeof (style === null || style === void 0 ? void 0 : style.backgroundStroke) === 'string' && style.backgroundStroke.length > 0
4922
+ ? style.backgroundStroke
4923
+ : undefined;
4924
+ var backgroundStrokeWidth = typeof (style === null || style === void 0 ? void 0 : style.backgroundStrokeWidth) === 'number'
4925
+ ? Math.max(0, style.backgroundStrokeWidth)
4926
+ : undefined;
4927
+ var backgroundNode = _this.textBackgroundNodes.get(text.id);
4928
+ if (!backgroundNode) {
4929
+ backgroundNode = _this.nodeFactory.createTextBackgroundNode({
4930
+ id: "text-background:".concat(text.id),
4931
+ x: baseX,
4932
+ y: baseY,
4933
+ width: width,
4934
+ height: height,
4935
+ fill: backgroundFill,
4936
+ stroke: backgroundStroke,
4937
+ strokeWidth: backgroundStrokeWidth,
4938
+ cornerRadius: backgroundMeta.cornerRadius,
4939
+ });
4940
+ _this.textBackgroundNodes.set(text.id, backgroundNode);
4941
+ (_p = (_o = _this.layers.getLayer('texts')) === null || _o === void 0 ? void 0 : _o.add) === null || _p === void 0 ? void 0 : _p.call(_o, backgroundNode);
4942
+ }
4943
+ else {
4944
+ _this.updatePosition(backgroundNode, { x: baseX, y: baseY });
4945
+ (_q = backgroundNode.setAttrs) === null || _q === void 0 ? void 0 : _q.call(backgroundNode, {
4946
+ width: width,
4947
+ height: height,
4948
+ fill: backgroundFill,
4949
+ stroke: backgroundStroke,
4950
+ strokeWidth: backgroundStrokeWidth,
4951
+ cornerRadius: backgroundMeta.cornerRadius,
4952
+ listening: false,
4953
+ name: 'text-background',
4954
+ });
4955
+ }
4956
+ (_r = node.moveToTop) === null || _r === void 0 ? void 0 : _r.call(node);
4957
+ }
4958
+ else {
4959
+ var backgroundNode = _this.textBackgroundNodes.get(text.id);
4960
+ (_s = backgroundNode === null || backgroundNode === void 0 ? void 0 : backgroundNode.destroy) === null || _s === void 0 ? void 0 : _s.call(backgroundNode);
4961
+ _this.textBackgroundNodes.delete(text.id);
4641
4962
  }
4642
4963
  });
4643
4964
  Array.from(this.textNodes.keys()).forEach(function (id) {
4644
- var _a;
4965
+ var _a, _b;
4645
4966
  if (!model.texts.has(id)) {
4967
+ var backgroundNode = _this.textBackgroundNodes.get(id);
4968
+ (_a = backgroundNode === null || backgroundNode === void 0 ? void 0 : backgroundNode.destroy) === null || _a === void 0 ? void 0 : _a.call(backgroundNode);
4969
+ _this.textBackgroundNodes.delete(id);
4646
4970
  var node = _this.textNodes.get(id);
4647
- (_a = node === null || node === void 0 ? void 0 : node.destroy) === null || _a === void 0 ? void 0 : _a.call(node);
4971
+ (_b = node === null || node === void 0 ? void 0 : node.destroy) === null || _b === void 0 ? void 0 : _b.call(node);
4648
4972
  _this.textNodes.delete(id);
4649
4973
  _this.layers.markDirty('texts');
4650
4974
  }
4651
4975
  });
4652
4976
  this.layers.markDirty('texts');
4653
4977
  };
4978
+ KonvaRenderer.prototype.resolveTextBackgroundMeta = function (model, text) {
4979
+ var _a;
4980
+ var ownerId = (_a = text.ownerId) !== null && _a !== void 0 ? _a : null;
4981
+ if (!ownerId)
4982
+ return { inset: 0 };
4983
+ var owner = model.getElement(ownerId);
4984
+ if (!owner)
4985
+ return { inset: 0 };
4986
+ var ownerStyle = owner.style;
4987
+ var strokeWidth = typeof (ownerStyle === null || ownerStyle === void 0 ? void 0 : ownerStyle.strokeWidth) === 'number' ? Math.max(0, ownerStyle.strokeWidth) : 0;
4988
+ var inset = strokeWidth > 0 ? strokeWidth / 2 : 0;
4989
+ var rawCornerRadius = ownerStyle === null || ownerStyle === void 0 ? void 0 : ownerStyle.cornerRadius;
4990
+ var isTopLabel = text.position.y <= 1;
4991
+ if (!isTopLabel)
4992
+ return { inset: inset };
4993
+ if (typeof rawCornerRadius === 'number' && rawCornerRadius > 0) {
4994
+ var topRadius = Math.max(0, rawCornerRadius - inset);
4995
+ return { inset: inset, cornerRadius: [topRadius, topRadius, 0, 0] };
4996
+ }
4997
+ if (Array.isArray(rawCornerRadius) && rawCornerRadius.length === 4) {
4998
+ var tl = typeof rawCornerRadius[0] === 'number' ? Math.max(0, rawCornerRadius[0] - inset) : 0;
4999
+ var tr = typeof rawCornerRadius[1] === 'number' ? Math.max(0, rawCornerRadius[1] - inset) : 0;
5000
+ return { inset: inset, cornerRadius: [tl, tr, 0, 0] };
5001
+ }
5002
+ return { inset: inset };
5003
+ };
4654
5004
  KonvaRenderer.prototype.updatePosition = function (node, position) {
4655
5005
  if (node.position) {
4656
5006
  node.position({ x: position.x, y: position.y });
@@ -6676,14 +7026,19 @@ var KonvaInteraction = /** @class */ (function () {
6676
7026
  this.engine.setSelection(Array.from(selected));
6677
7027
  };
6678
7028
  KonvaInteraction.prototype.applyPortConstraints = function (portId, worldTarget) {
7029
+ var _a;
6679
7030
  var port = this.getPortById(portId);
6680
7031
  if (!port)
6681
7032
  return worldTarget;
7033
+ var element = this.getElementById(port.elementId);
7034
+ if (!element)
7035
+ return worldTarget;
6682
7036
  var elementPos = this.engine.getElementWorldPosition(port.elementId);
6683
7037
  if (!elementPos)
6684
7038
  return worldTarget;
6685
7039
  var relative = { x: worldTarget.x - elementPos.x, y: worldTarget.y - elementPos.y };
6686
7040
  var style = port.style;
7041
+ var limits = (_a = element.portMovement) === null || _a === void 0 ? void 0 : _a.positionLimits;
6687
7042
  var constrained = __assign({}, relative);
6688
7043
  if ((style === null || style === void 0 ? void 0 : style.moveAxis) === 'horizontal') {
6689
7044
  constrained.y = port.position.y;
@@ -6696,6 +7051,18 @@ var KonvaInteraction = /** @class */ (function () {
6696
7051
  constrained.x = Math.min(bounds.x + bounds.width, Math.max(bounds.x, constrained.x));
6697
7052
  constrained.y = Math.min(bounds.y + bounds.height, Math.max(bounds.y, constrained.y));
6698
7053
  }
7054
+ if (limits === null || limits === void 0 ? void 0 : limits.x) {
7055
+ if (typeof limits.x.min === 'number')
7056
+ constrained.x = Math.max(constrained.x, limits.x.min);
7057
+ if (typeof limits.x.max === 'number')
7058
+ constrained.x = Math.min(constrained.x, limits.x.max);
7059
+ }
7060
+ if (limits === null || limits === void 0 ? void 0 : limits.y) {
7061
+ if (typeof limits.y.min === 'number')
7062
+ constrained.y = Math.max(constrained.y, limits.y.min);
7063
+ if (typeof limits.y.max === 'number')
7064
+ constrained.y = Math.min(constrained.y, limits.y.max);
7065
+ }
6699
7066
  return { x: elementPos.x + constrained.x, y: elementPos.y + constrained.y };
6700
7067
  };
6701
7068
  KonvaInteraction.prototype.resolveLinkPreviewSource = function (sourcePortId, pointer, hit) {