orcasvn-react-diagrams 0.2.9 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [0.2.10] - 2026-05-27
6
+
7
+ ### Changed
8
+ - `ElementData.visible = false` now hides only the element body from built-in render, hit testing, selection persistence, and fit/export helpers instead of suppressing descendant elements through hidden ancestors.
9
+ - Visible descendants under hidden parents now remain rendered, selectable, draggable, and included in fit/export bounds, while direct-owned parent ports/texts still follow the hidden parent and links remain visible when their endpoint ports are visible.
10
+
11
+ ### Docs
12
+ - Updated release highlights, API/invariant docs, integration guidance, commands/events notes, and machine-readable contract artifacts for `v0.2.10`.
13
+
5
14
  ## [0.2.9] - 2026-05-27
6
15
 
7
16
  ### Added
package/README.md CHANGED
@@ -30,13 +30,14 @@ editor.addElement({
30
30
  });
31
31
  ```
32
32
 
33
- ## Release Highlights (v0.2.9)
33
+ ## Release Highlights (v0.2.10)
34
34
 
35
- - Includes all completed `v0.2.1` through `v0.2.8` work, plus `v0.2.9` updates.
36
- - `v0.2.9` element visibility/selectability policy:
37
- - `ElementData.visible = false` keeps the element in state while removing it from built-in render, hit testing, selection, and fit/export bounds
35
+ - Includes all completed `v0.2.1` through `v0.2.9` work, plus `v0.2.10` updates.
36
+ - `v0.2.10` element visibility/selectability policy:
37
+ - `ElementData.visible = false` keeps the element in state while removing its own body from built-in render, hit testing, selection, and fit/export bounds
38
+ - visible descendant elements under a hidden parent remain rendered and interactive if their own `visible` flag stays enabled
39
+ - direct-owned parent ports/texts still follow the hidden parent, and links remain visible when their endpoint ports are visible
38
40
  - `ElementData.selectable = false` keeps the element visible while preventing built-in element-body click/marquee/programmatic selection from retaining that element id
39
- - owned ports/texts and endpoint-dependent links now follow the same hidden-scene visibility rules
40
41
 
41
42
  ## API Docs
42
43
 
@@ -227,7 +227,9 @@
227
227
  },
228
228
  "semantics": [
229
229
  "visible=false hides the element body from built-in render, hit testing, marquee selection, persisted selection, zoomToFitElements, and exportImage fit-to-content bounds",
230
- "hidden-scene propagation also hides owned ports, owned texts, and links whose endpoint ports belong to hidden elements",
230
+ "descendant elements keep resolving visibility from their own visible flag even when an ancestor element is hidden",
231
+ "direct-owned ports and direct-owned texts still hide with their hidden parent element",
232
+ "links remain visible whenever both endpoint ports remain visible, including links attached to visible descendants under hidden parents",
231
233
  "selectable=false blocks built-in element-body click selection, marquee selection, and programmatic setSelection/toggleSelection from retaining that element id",
232
234
  "selectable=false does not hide the element"
233
235
  ]
@@ -15,7 +15,7 @@
15
15
  "entityRules": {
16
16
  "element": [
17
17
  "child element positions are parent-relative when parentId is set",
18
- "element visible-state is enabled by default and hidden ancestors suppress descendant element visibility",
18
+ "element visible-state is enabled by default and visible=false hides only that element body while descendants resolve visibility from their own visible flag",
19
19
  "element selectable-state is enabled by default and selectable=false prevents the element id from persisting in selection state",
20
20
  "deleting an element removes its ports, links via ports, and owned texts"
21
21
  ],
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "eventRules": [
41
41
  "mutations emit change with patches and full state snapshot",
42
- "setSelection/toggleSelection normalize away hidden scene members and selectable=false element ids before emitting selection",
42
+ "setSelection/toggleSelection normalize away hidden element bodies, direct-owned hidden artifacts, and selectable=false element ids before emitting selection",
43
43
  "setSelection emits selection plus derived selected entity events",
44
44
  "setViewport emits viewport patch without immediate render"
45
45
  ],
@@ -1164,13 +1164,21 @@ var createElementVisibilitySelectionState = function () { return ({
1164
1164
  style: { fill: '#e2e8f0', stroke: '#334155', strokeWidth: 2 },
1165
1165
  },
1166
1166
  {
1167
- id: 'policy-hidden',
1168
- position: { x: 700, y: 140 },
1169
- size: { width: 180, height: 120 },
1167
+ id: 'policy-hidden-parent',
1168
+ position: { x: 660, y: 110 },
1169
+ size: { width: 220, height: 170 },
1170
1170
  shapeId: 'default',
1171
1171
  visible: false,
1172
1172
  style: { fill: '#fee2e2', stroke: '#dc2626', strokeWidth: 2 },
1173
1173
  },
1174
+ {
1175
+ id: 'policy-hidden-child',
1176
+ parentId: 'policy-hidden-parent',
1177
+ position: { x: 50, y: 58 },
1178
+ size: { width: 120, height: 72 },
1179
+ shapeId: 'default',
1180
+ style: { fill: '#dcfce7', stroke: '#16a34a', strokeWidth: 2 },
1181
+ },
1174
1182
  ],
1175
1183
  ports: [
1176
1184
  {
@@ -1186,9 +1194,15 @@ var createElementVisibilitySelectionState = function () { return ({
1186
1194
  shapeId: 'port-circle',
1187
1195
  },
1188
1196
  {
1189
- id: 'policy-hidden-port',
1190
- elementId: 'policy-hidden',
1191
- position: { x: 0, y: 60 },
1197
+ id: 'policy-hidden-parent-port',
1198
+ elementId: 'policy-hidden-parent',
1199
+ position: { x: 0, y: 85 },
1200
+ shapeId: 'port-circle',
1201
+ },
1202
+ {
1203
+ id: 'policy-hidden-child-port',
1204
+ elementId: 'policy-hidden-child',
1205
+ position: { x: 120, y: 36 },
1192
1206
  shapeId: 'port-circle',
1193
1207
  },
1194
1208
  ],
@@ -1204,22 +1218,32 @@ var createElementVisibilitySelectionState = function () { return ({
1204
1218
  style: { stroke: '#2563eb', strokeWidth: 3 },
1205
1219
  },
1206
1220
  {
1207
- id: 'policy-hidden-link',
1221
+ id: 'policy-hidden-parent-link',
1208
1222
  sourcePortId: 'policy-normal-port',
1209
- targetPortId: 'policy-hidden-port',
1223
+ targetPortId: 'policy-hidden-parent-port',
1210
1224
  points: [
1211
1225
  { x: 300, y: 220 },
1212
- { x: 700, y: 200 },
1226
+ { x: 660, y: 195 },
1213
1227
  ],
1214
1228
  style: { stroke: '#dc2626', strokeWidth: 3 },
1215
1229
  },
1230
+ {
1231
+ id: 'policy-visible-child-link',
1232
+ sourcePortId: 'policy-normal-port',
1233
+ targetPortId: 'policy-hidden-child-port',
1234
+ points: [
1235
+ { x: 300, y: 220 },
1236
+ { x: 830, y: 204 },
1237
+ ],
1238
+ style: { stroke: '#16a34a', strokeWidth: 3 },
1239
+ },
1216
1240
  ],
1217
1241
  texts: [
1218
1242
  {
1219
1243
  id: 'policy-tip',
1220
- content: 'Blue node is normal. Slate node is visible but its body ignores click and marquee. Its port stays interactive. Hidden node, its port, and its attached red link stay in state but are suppressed from scene, selection, zoom-to-fit, and fit-to-content export.',
1244
+ content: 'Blue node is normal. Slate node is visible but its body ignores click and marquee. Its port stays interactive. Red parent container is hidden, but its green child stays visible and interactive. Parent-owned port/text and the red parent link stay suppressed, while the green child link remains in scene, selection, zoom-to-fit, and fit-to-content export.',
1221
1245
  position: { x: 80, y: 72 },
1222
- size: { width: 760, height: 44 },
1246
+ size: { width: 810, height: 60 },
1223
1247
  style: { fontSize: 14, fill: '#0f172a' },
1224
1248
  },
1225
1249
  {
@@ -1236,16 +1260,22 @@ var createElementVisibilitySelectionState = function () { return ({
1236
1260
  },
1237
1261
  {
1238
1262
  id: 'policy-hidden-label',
1239
- content: 'Hidden in state',
1263
+ content: 'Hidden parent body',
1240
1264
  position: { x: 16, y: 14 },
1241
- ownerId: 'policy-hidden',
1265
+ ownerId: 'policy-hidden-parent',
1266
+ },
1267
+ {
1268
+ id: 'policy-hidden-child-label',
1269
+ content: 'Visible child inside hidden parent',
1270
+ position: { x: 10, y: 12 },
1271
+ ownerId: 'policy-hidden-child',
1242
1272
  },
1243
1273
  ],
1244
1274
  }); };
1245
1275
  var elementVisibilitySelectionDemoConfig = {
1246
1276
  id: 'element-visibility-selection',
1247
1277
  title: 'Element Visibility + Selectability',
1248
- description: 'QA scene for visible-but-unselectable element bodies, hidden-in-state elements, and port/link propagation. Shift + drag marquee to confirm only eligible items are collected.',
1278
+ description: 'QA scene for visible-but-unselectable bodies, hidden parent containers, and visible descendants that survive hidden ancestors. Shift + drag marquee to confirm only eligible items are collected.',
1249
1279
  createState: createElementVisibilitySelectionState,
1250
1280
  elementShapes: baseElementShapes,
1251
1281
  portShapes: basePortShapes,
@@ -1259,9 +1289,14 @@ var elementVisibilitySelectionDemoConfig = {
1259
1289
  editor.setSelection([
1260
1290
  'policy-normal',
1261
1291
  'policy-unselectable',
1262
- 'policy-hidden',
1292
+ 'policy-hidden-parent',
1293
+ 'policy-hidden-child',
1263
1294
  'policy-unselectable-port',
1264
- 'policy-hidden-link',
1295
+ 'policy-hidden-parent-port',
1296
+ 'policy-hidden-child-port',
1297
+ 'policy-hidden-parent-link',
1298
+ 'policy-visible-child-link',
1299
+ 'policy-hidden-child-label',
1265
1300
  ]);
1266
1301
  },
1267
1302
  },
@@ -5183,18 +5218,7 @@ var DiagramModel = /** @class */ (function () {
5183
5218
  var element = this.elements.get(id);
5184
5219
  if (!element)
5185
5220
  return false;
5186
- if (element.visible === false)
5187
- return false;
5188
- var currentParentId = element.parentId;
5189
- while (currentParentId) {
5190
- var parent_1 = this.elements.get(currentParentId);
5191
- if (!parent_1)
5192
- break;
5193
- if (parent_1.visible === false)
5194
- return false;
5195
- currentParentId = parent_1.parentId;
5196
- }
5197
- return true;
5221
+ return element.visible !== false;
5198
5222
  };
5199
5223
  DiagramModel.prototype.isElementSelectable = function (id) {
5200
5224
  var element = this.elements.get(id);
@@ -5465,18 +5489,18 @@ var DiagramModel = /** @class */ (function () {
5465
5489
  }
5466
5490
  var current = element;
5467
5491
  while (current === null || current === void 0 ? void 0 : current.parentId) {
5468
- var parent_2 = this.elements.get(current.parentId);
5469
- if (!parent_2)
5492
+ var parent_1 = this.elements.get(current.parentId);
5493
+ if (!parent_1)
5470
5494
  break;
5471
- var parentX = parent_2.position.x;
5472
- var parentY = parent_2.position.y;
5473
- if (parent_2.anchorCenter) {
5474
- parentX -= parent_2.size.width / 2;
5475
- parentY -= parent_2.size.height / 2;
5495
+ var parentX = parent_1.position.x;
5496
+ var parentY = parent_1.position.y;
5497
+ if (parent_1.anchorCenter) {
5498
+ parentX -= parent_1.size.width / 2;
5499
+ parentY -= parent_1.size.height / 2;
5476
5500
  }
5477
5501
  x += parentX;
5478
5502
  y += parentY;
5479
- current = parent_2;
5503
+ current = parent_1;
5480
5504
  }
5481
5505
  return { x: x, y: y };
5482
5506
  };
@@ -13591,22 +13615,10 @@ var resolveViewportFitOptions = function (options) {
13591
13615
  };
13592
13616
  var clamp = function (value, min, max) { return Math.max(min, Math.min(max, value)); };
13593
13617
  var isElementVisibleInState = function (elementId, elementsById) {
13594
- var _a, _b;
13595
13618
  var element = elementsById.get(elementId);
13596
13619
  if (!element)
13597
13620
  return false;
13598
- if (element.visible === false)
13599
- return false;
13600
- var currentParentId = (_a = element.parentId) !== null && _a !== void 0 ? _a : null;
13601
- while (currentParentId) {
13602
- var parent_1 = elementsById.get(currentParentId);
13603
- if (!parent_1)
13604
- break;
13605
- if (parent_1.visible === false)
13606
- return false;
13607
- currentParentId = (_b = parent_1.parentId) !== null && _b !== void 0 ? _b : null;
13608
- }
13609
- return true;
13621
+ return element.visible !== false;
13610
13622
  };
13611
13623
  var isPortVisibleInState = function (portId, portsById, elementsById) {
13612
13624
  var port = portsById.get(portId);
@@ -13635,6 +13647,103 @@ var isTextVisibleInState = function (text, elementsById, portsById, linksById) {
13635
13647
  return isLinkVisibleInState(text.ownerId, linksById, portsById, elementsById);
13636
13648
  return true;
13637
13649
  };
13650
+ var resolveElementWorldPositionInState = function (elementId, elementsById) {
13651
+ var element = elementsById.get(elementId);
13652
+ if (!element)
13653
+ return null;
13654
+ var x = element.position.x;
13655
+ var y = element.position.y;
13656
+ if (element.anchorCenter) {
13657
+ x -= element.size.width / 2;
13658
+ y -= element.size.height / 2;
13659
+ }
13660
+ var current = element;
13661
+ while (current.parentId) {
13662
+ var parent_1 = elementsById.get(current.parentId);
13663
+ if (!parent_1)
13664
+ break;
13665
+ var parentX = parent_1.position.x;
13666
+ var parentY = parent_1.position.y;
13667
+ if (parent_1.anchorCenter) {
13668
+ parentX -= parent_1.size.width / 2;
13669
+ parentY -= parent_1.size.height / 2;
13670
+ }
13671
+ x += parentX;
13672
+ y += parentY;
13673
+ current = parent_1;
13674
+ }
13675
+ return { x: x, y: y };
13676
+ };
13677
+ var resolvePortWorldPositionInState = function (portId, portsById, elementsById) {
13678
+ var port = portsById.get(portId);
13679
+ if (!port)
13680
+ return null;
13681
+ var elementPosition = resolveElementWorldPositionInState(port.elementId, elementsById);
13682
+ if (!elementPosition)
13683
+ return __assign({}, port.position);
13684
+ return {
13685
+ x: elementPosition.x + port.position.x,
13686
+ y: elementPosition.y + port.position.y,
13687
+ };
13688
+ };
13689
+ var resolveLinkMidpointInState = function (link) {
13690
+ var points = link.points;
13691
+ if (points.length === 0)
13692
+ return { x: 0, y: 0 };
13693
+ if (points.length === 1)
13694
+ return __assign({}, points[0]);
13695
+ var total = 0;
13696
+ for (var i = 1; i < points.length; i += 1) {
13697
+ total += distance(points[i - 1], points[i]);
13698
+ }
13699
+ var halfway = total / 2;
13700
+ var traveled = 0;
13701
+ for (var i = 1; i < points.length; i += 1) {
13702
+ var segment = distance(points[i - 1], points[i]);
13703
+ if (traveled + segment >= halfway) {
13704
+ var ratio = segment === 0 ? 0 : (halfway - traveled) / segment;
13705
+ return {
13706
+ x: points[i - 1].x + (points[i].x - points[i - 1].x) * ratio,
13707
+ y: points[i - 1].y + (points[i].y - points[i - 1].y) * ratio,
13708
+ };
13709
+ }
13710
+ traveled += segment;
13711
+ }
13712
+ return __assign({}, points[points.length - 1]);
13713
+ };
13714
+ var resolveTextWorldPositionInState = function (text, elementsById, portsById, linksById) {
13715
+ if (!text.ownerId)
13716
+ return __assign({}, text.position);
13717
+ if (elementsById.has(text.ownerId)) {
13718
+ var elementPosition = resolveElementWorldPositionInState(text.ownerId, elementsById);
13719
+ if (!elementPosition)
13720
+ return __assign({}, text.position);
13721
+ return {
13722
+ x: elementPosition.x + text.position.x,
13723
+ y: elementPosition.y + text.position.y,
13724
+ };
13725
+ }
13726
+ if (portsById.has(text.ownerId)) {
13727
+ var portPosition = resolvePortWorldPositionInState(text.ownerId, portsById, elementsById);
13728
+ if (!portPosition)
13729
+ return __assign({}, text.position);
13730
+ return {
13731
+ x: portPosition.x + text.position.x,
13732
+ y: portPosition.y + text.position.y,
13733
+ };
13734
+ }
13735
+ if (linksById.has(text.ownerId)) {
13736
+ var link = linksById.get(text.ownerId);
13737
+ if (!link)
13738
+ return __assign({}, text.position);
13739
+ var midpoint = resolveLinkMidpointInState(link);
13740
+ return {
13741
+ x: midpoint.x + text.position.x,
13742
+ y: midpoint.y + text.position.y,
13743
+ };
13744
+ }
13745
+ return __assign({}, text.position);
13746
+ };
13638
13747
  var resolveStateWorldBounds = function (state) {
13639
13748
  var bounds = createBounds();
13640
13749
  var elementsById = new Map(state.elements.map(function (element) { return [element.id, element]; }));
@@ -13643,20 +13752,26 @@ var resolveStateWorldBounds = function (state) {
13643
13752
  state.elements.forEach(function (element) {
13644
13753
  if (!isElementVisibleInState(element.id, elementsById))
13645
13754
  return;
13646
- includeRect(bounds, element.position.x, element.position.y, element.size.width, element.size.height);
13755
+ var position = resolveElementWorldPositionInState(element.id, elementsById);
13756
+ if (!position)
13757
+ return;
13758
+ includeRect(bounds, position.x, position.y, element.size.width, element.size.height);
13647
13759
  });
13648
13760
  state.ports.forEach(function (port) {
13649
13761
  var _a;
13650
13762
  if (!isPortVisibleInState(port.id, portsById, elementsById))
13651
13763
  return;
13764
+ var position = resolvePortWorldPositionInState(port.id, portsById, elementsById);
13765
+ if (!position)
13766
+ return;
13652
13767
  var size = port.size;
13653
13768
  if (!size) {
13654
- expandBounds(bounds, port.position.x, port.position.y);
13769
+ expandBounds(bounds, position.x, position.y);
13655
13770
  return;
13656
13771
  }
13657
13772
  var anchorCenter = (_a = port.anchorCenter) !== null && _a !== void 0 ? _a : true;
13658
- var x = anchorCenter ? port.position.x - size.width / 2 : port.position.x;
13659
- var y = anchorCenter ? port.position.y - size.height / 2 : port.position.y;
13773
+ var x = anchorCenter ? position.x - size.width / 2 : position.x;
13774
+ var y = anchorCenter ? position.y - size.height / 2 : position.y;
13660
13775
  includeRect(bounds, x, y, size.width, size.height);
13661
13776
  });
13662
13777
  state.links.forEach(function (link) {
@@ -13671,7 +13786,8 @@ var resolveStateWorldBounds = function (state) {
13671
13786
  if (!isTextVisibleInState(text, elementsById, portsById, linksById))
13672
13787
  return;
13673
13788
  var offset = (_a = text.displayOffset) !== null && _a !== void 0 ? _a : { x: 0, y: 0 };
13674
- var position = { x: text.position.x + offset.x, y: text.position.y + offset.y };
13789
+ var worldPosition = resolveTextWorldPositionInState(text, elementsById, portsById, linksById);
13790
+ var position = { x: worldPosition.x + offset.x, y: worldPosition.y + offset.y };
13675
13791
  var clipSize = text.displayClipSize;
13676
13792
  var size = clipSize !== null && clipSize !== void 0 ? clipSize : text.size;
13677
13793
  if (size) {
@@ -13688,7 +13804,10 @@ var resolveElementWorldBounds = function (state) {
13688
13804
  state.elements.forEach(function (element) {
13689
13805
  if (!isElementVisibleInState(element.id, elementsById))
13690
13806
  return;
13691
- includeRect(bounds, element.position.x, element.position.y, element.size.width, element.size.height);
13807
+ var position = resolveElementWorldPositionInState(element.id, elementsById);
13808
+ if (!position)
13809
+ return;
13810
+ includeRect(bounds, position.x, position.y, element.size.width, element.size.height);
13692
13811
  });
13693
13812
  return hasBounds(bounds) ? bounds : null;
13694
13813
  };
package/dist/cjs/index.js CHANGED
@@ -536,18 +536,7 @@ var DiagramModel = /** @class */ (function () {
536
536
  var element = this.elements.get(id);
537
537
  if (!element)
538
538
  return false;
539
- if (element.visible === false)
540
- return false;
541
- var currentParentId = element.parentId;
542
- while (currentParentId) {
543
- var parent_1 = this.elements.get(currentParentId);
544
- if (!parent_1)
545
- break;
546
- if (parent_1.visible === false)
547
- return false;
548
- currentParentId = parent_1.parentId;
549
- }
550
- return true;
539
+ return element.visible !== false;
551
540
  };
552
541
  DiagramModel.prototype.isElementSelectable = function (id) {
553
542
  var element = this.elements.get(id);
@@ -818,18 +807,18 @@ var DiagramModel = /** @class */ (function () {
818
807
  }
819
808
  var current = element;
820
809
  while (current === null || current === void 0 ? void 0 : current.parentId) {
821
- var parent_2 = this.elements.get(current.parentId);
822
- if (!parent_2)
810
+ var parent_1 = this.elements.get(current.parentId);
811
+ if (!parent_1)
823
812
  break;
824
- var parentX = parent_2.position.x;
825
- var parentY = parent_2.position.y;
826
- if (parent_2.anchorCenter) {
827
- parentX -= parent_2.size.width / 2;
828
- parentY -= parent_2.size.height / 2;
813
+ var parentX = parent_1.position.x;
814
+ var parentY = parent_1.position.y;
815
+ if (parent_1.anchorCenter) {
816
+ parentX -= parent_1.size.width / 2;
817
+ parentY -= parent_1.size.height / 2;
829
818
  }
830
819
  x += parentX;
831
820
  y += parentY;
832
- current = parent_2;
821
+ current = parent_1;
833
822
  }
834
823
  return { x: x, y: y };
835
824
  };
@@ -9438,22 +9427,10 @@ var resolveViewportFitOptions = function (options) {
9438
9427
  };
9439
9428
  var clamp = function (value, min, max) { return Math.max(min, Math.min(max, value)); };
9440
9429
  var isElementVisibleInState = function (elementId, elementsById) {
9441
- var _a, _b;
9442
9430
  var element = elementsById.get(elementId);
9443
9431
  if (!element)
9444
9432
  return false;
9445
- if (element.visible === false)
9446
- return false;
9447
- var currentParentId = (_a = element.parentId) !== null && _a !== void 0 ? _a : null;
9448
- while (currentParentId) {
9449
- var parent_1 = elementsById.get(currentParentId);
9450
- if (!parent_1)
9451
- break;
9452
- if (parent_1.visible === false)
9453
- return false;
9454
- currentParentId = (_b = parent_1.parentId) !== null && _b !== void 0 ? _b : null;
9455
- }
9456
- return true;
9433
+ return element.visible !== false;
9457
9434
  };
9458
9435
  var isPortVisibleInState = function (portId, portsById, elementsById) {
9459
9436
  var port = portsById.get(portId);
@@ -9482,6 +9459,103 @@ var isTextVisibleInState = function (text, elementsById, portsById, linksById) {
9482
9459
  return isLinkVisibleInState(text.ownerId, linksById, portsById, elementsById);
9483
9460
  return true;
9484
9461
  };
9462
+ var resolveElementWorldPositionInState = function (elementId, elementsById) {
9463
+ var element = elementsById.get(elementId);
9464
+ if (!element)
9465
+ return null;
9466
+ var x = element.position.x;
9467
+ var y = element.position.y;
9468
+ if (element.anchorCenter) {
9469
+ x -= element.size.width / 2;
9470
+ y -= element.size.height / 2;
9471
+ }
9472
+ var current = element;
9473
+ while (current.parentId) {
9474
+ var parent_1 = elementsById.get(current.parentId);
9475
+ if (!parent_1)
9476
+ break;
9477
+ var parentX = parent_1.position.x;
9478
+ var parentY = parent_1.position.y;
9479
+ if (parent_1.anchorCenter) {
9480
+ parentX -= parent_1.size.width / 2;
9481
+ parentY -= parent_1.size.height / 2;
9482
+ }
9483
+ x += parentX;
9484
+ y += parentY;
9485
+ current = parent_1;
9486
+ }
9487
+ return { x: x, y: y };
9488
+ };
9489
+ var resolvePortWorldPositionInState = function (portId, portsById, elementsById) {
9490
+ var port = portsById.get(portId);
9491
+ if (!port)
9492
+ return null;
9493
+ var elementPosition = resolveElementWorldPositionInState(port.elementId, elementsById);
9494
+ if (!elementPosition)
9495
+ return __assign({}, port.position);
9496
+ return {
9497
+ x: elementPosition.x + port.position.x,
9498
+ y: elementPosition.y + port.position.y,
9499
+ };
9500
+ };
9501
+ var resolveLinkMidpointInState = function (link) {
9502
+ var points = link.points;
9503
+ if (points.length === 0)
9504
+ return { x: 0, y: 0 };
9505
+ if (points.length === 1)
9506
+ return __assign({}, points[0]);
9507
+ var total = 0;
9508
+ for (var i = 1; i < points.length; i += 1) {
9509
+ total += distance(points[i - 1], points[i]);
9510
+ }
9511
+ var halfway = total / 2;
9512
+ var traveled = 0;
9513
+ for (var i = 1; i < points.length; i += 1) {
9514
+ var segment = distance(points[i - 1], points[i]);
9515
+ if (traveled + segment >= halfway) {
9516
+ var ratio = segment === 0 ? 0 : (halfway - traveled) / segment;
9517
+ return {
9518
+ x: points[i - 1].x + (points[i].x - points[i - 1].x) * ratio,
9519
+ y: points[i - 1].y + (points[i].y - points[i - 1].y) * ratio,
9520
+ };
9521
+ }
9522
+ traveled += segment;
9523
+ }
9524
+ return __assign({}, points[points.length - 1]);
9525
+ };
9526
+ var resolveTextWorldPositionInState = function (text, elementsById, portsById, linksById) {
9527
+ if (!text.ownerId)
9528
+ return __assign({}, text.position);
9529
+ if (elementsById.has(text.ownerId)) {
9530
+ var elementPosition = resolveElementWorldPositionInState(text.ownerId, elementsById);
9531
+ if (!elementPosition)
9532
+ return __assign({}, text.position);
9533
+ return {
9534
+ x: elementPosition.x + text.position.x,
9535
+ y: elementPosition.y + text.position.y,
9536
+ };
9537
+ }
9538
+ if (portsById.has(text.ownerId)) {
9539
+ var portPosition = resolvePortWorldPositionInState(text.ownerId, portsById, elementsById);
9540
+ if (!portPosition)
9541
+ return __assign({}, text.position);
9542
+ return {
9543
+ x: portPosition.x + text.position.x,
9544
+ y: portPosition.y + text.position.y,
9545
+ };
9546
+ }
9547
+ if (linksById.has(text.ownerId)) {
9548
+ var link = linksById.get(text.ownerId);
9549
+ if (!link)
9550
+ return __assign({}, text.position);
9551
+ var midpoint = resolveLinkMidpointInState(link);
9552
+ return {
9553
+ x: midpoint.x + text.position.x,
9554
+ y: midpoint.y + text.position.y,
9555
+ };
9556
+ }
9557
+ return __assign({}, text.position);
9558
+ };
9485
9559
  var resolveStateWorldBounds = function (state) {
9486
9560
  var bounds = createBounds();
9487
9561
  var elementsById = new Map(state.elements.map(function (element) { return [element.id, element]; }));
@@ -9490,20 +9564,26 @@ var resolveStateWorldBounds = function (state) {
9490
9564
  state.elements.forEach(function (element) {
9491
9565
  if (!isElementVisibleInState(element.id, elementsById))
9492
9566
  return;
9493
- includeRect(bounds, element.position.x, element.position.y, element.size.width, element.size.height);
9567
+ var position = resolveElementWorldPositionInState(element.id, elementsById);
9568
+ if (!position)
9569
+ return;
9570
+ includeRect(bounds, position.x, position.y, element.size.width, element.size.height);
9494
9571
  });
9495
9572
  state.ports.forEach(function (port) {
9496
9573
  var _a;
9497
9574
  if (!isPortVisibleInState(port.id, portsById, elementsById))
9498
9575
  return;
9576
+ var position = resolvePortWorldPositionInState(port.id, portsById, elementsById);
9577
+ if (!position)
9578
+ return;
9499
9579
  var size = port.size;
9500
9580
  if (!size) {
9501
- expandBounds(bounds, port.position.x, port.position.y);
9581
+ expandBounds(bounds, position.x, position.y);
9502
9582
  return;
9503
9583
  }
9504
9584
  var anchorCenter = (_a = port.anchorCenter) !== null && _a !== void 0 ? _a : true;
9505
- var x = anchorCenter ? port.position.x - size.width / 2 : port.position.x;
9506
- var y = anchorCenter ? port.position.y - size.height / 2 : port.position.y;
9585
+ var x = anchorCenter ? position.x - size.width / 2 : position.x;
9586
+ var y = anchorCenter ? position.y - size.height / 2 : position.y;
9507
9587
  includeRect(bounds, x, y, size.width, size.height);
9508
9588
  });
9509
9589
  state.links.forEach(function (link) {
@@ -9518,7 +9598,8 @@ var resolveStateWorldBounds = function (state) {
9518
9598
  if (!isTextVisibleInState(text, elementsById, portsById, linksById))
9519
9599
  return;
9520
9600
  var offset = (_a = text.displayOffset) !== null && _a !== void 0 ? _a : { x: 0, y: 0 };
9521
- var position = { x: text.position.x + offset.x, y: text.position.y + offset.y };
9601
+ var worldPosition = resolveTextWorldPositionInState(text, elementsById, portsById, linksById);
9602
+ var position = { x: worldPosition.x + offset.x, y: worldPosition.y + offset.y };
9522
9603
  var clipSize = text.displayClipSize;
9523
9604
  var size = clipSize !== null && clipSize !== void 0 ? clipSize : text.size;
9524
9605
  if (size) {
@@ -9535,7 +9616,10 @@ var resolveElementWorldBounds = function (state) {
9535
9616
  state.elements.forEach(function (element) {
9536
9617
  if (!isElementVisibleInState(element.id, elementsById))
9537
9618
  return;
9538
- includeRect(bounds, element.position.x, element.position.y, element.size.width, element.size.height);
9619
+ var position = resolveElementWorldPositionInState(element.id, elementsById);
9620
+ if (!position)
9621
+ return;
9622
+ includeRect(bounds, position.x, position.y, element.size.width, element.size.height);
9539
9623
  });
9540
9624
  return hasBounds(bounds) ? bounds : null;
9541
9625
  };