@sapui5/sap.suite.ui.commons 1.148.2 → 1.148.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 (47) hide show
  1. package/package.json +1 -1
  2. package/src/sap/suite/ui/commons/.library +1 -1
  3. package/src/sap/suite/ui/commons/AriaProperties.js +1 -1
  4. package/src/sap/suite/ui/commons/CalculationBuilder.js +1 -1
  5. package/src/sap/suite/ui/commons/CalculationBuilderExpression.js +1 -1
  6. package/src/sap/suite/ui/commons/CalculationBuilderFunction.js +1 -1
  7. package/src/sap/suite/ui/commons/CalculationBuilderGroup.js +1 -1
  8. package/src/sap/suite/ui/commons/CalculationBuilderItem.js +1 -1
  9. package/src/sap/suite/ui/commons/CalculationBuilderValidationResult.js +1 -1
  10. package/src/sap/suite/ui/commons/CalculationBuilderVariable.js +1 -1
  11. package/src/sap/suite/ui/commons/CloudFilePicker.js +1 -1
  12. package/src/sap/suite/ui/commons/MicroProcessFlow.js +1 -1
  13. package/src/sap/suite/ui/commons/MicroProcessFlowItem.js +1 -1
  14. package/src/sap/suite/ui/commons/flexibility/changeHandler/PropertyChangeMapper.js +1 -1
  15. package/src/sap/suite/ui/commons/imageeditor/CropCustomShapeHistoryItem.js +1 -1
  16. package/src/sap/suite/ui/commons/imageeditor/CropEllipseHistoryItem.js +1 -1
  17. package/src/sap/suite/ui/commons/imageeditor/CropRectangleHistoryItem.js +1 -1
  18. package/src/sap/suite/ui/commons/imageeditor/CustomSizeItem.js +1 -1
  19. package/src/sap/suite/ui/commons/imageeditor/FilterHistoryItem.js +1 -1
  20. package/src/sap/suite/ui/commons/imageeditor/FlipHistoryItem.js +1 -1
  21. package/src/sap/suite/ui/commons/imageeditor/HistoryItem.js +1 -1
  22. package/src/sap/suite/ui/commons/imageeditor/ImageEditor.js +1 -1
  23. package/src/sap/suite/ui/commons/imageeditor/ImageEditorContainer.js +1 -1
  24. package/src/sap/suite/ui/commons/imageeditor/ImageEditorResponsiveContainer.js +1 -1
  25. package/src/sap/suite/ui/commons/imageeditor/ResizeHistoryItem.js +1 -1
  26. package/src/sap/suite/ui/commons/imageeditor/RotateHistoryItem.js +1 -1
  27. package/src/sap/suite/ui/commons/library.js +1 -1
  28. package/src/sap/suite/ui/commons/networkgraph/Graph.js +38 -8
  29. package/src/sap/suite/ui/commons/networkgraph/util/ConnectionPathUtils.js +315 -69
  30. package/src/sap/suite/ui/commons/networkgraph/util/DependencyLayoutHelper.js +21 -6
  31. package/src/sap/suite/ui/commons/statusindicator/Circle.js +1 -1
  32. package/src/sap/suite/ui/commons/statusindicator/CustomShape.js +1 -1
  33. package/src/sap/suite/ui/commons/statusindicator/DiscreteThreshold.js +1 -1
  34. package/src/sap/suite/ui/commons/statusindicator/FillingOption.js +1 -1
  35. package/src/sap/suite/ui/commons/statusindicator/LibraryShape.js +1 -1
  36. package/src/sap/suite/ui/commons/statusindicator/Path.js +1 -1
  37. package/src/sap/suite/ui/commons/statusindicator/PropertyThreshold.js +1 -1
  38. package/src/sap/suite/ui/commons/statusindicator/Rectangle.js +1 -1
  39. package/src/sap/suite/ui/commons/statusindicator/Shape.js +1 -1
  40. package/src/sap/suite/ui/commons/statusindicator/ShapeGroup.js +1 -1
  41. package/src/sap/suite/ui/commons/statusindicator/SimpleShape.js +1 -1
  42. package/src/sap/suite/ui/commons/statusindicator/StatusIndicator.js +1 -1
  43. package/src/sap/suite/ui/commons/taccount/TAccount.js +1 -1
  44. package/src/sap/suite/ui/commons/taccount/TAccountGroup.js +1 -1
  45. package/src/sap/suite/ui/commons/taccount/TAccountItem.js +1 -1
  46. package/src/sap/suite/ui/commons/taccount/TAccountItemProperty.js +1 -1
  47. package/src/sap/suite/ui/commons/taccount/TAccountPanel.js +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sapui5/sap.suite.ui.commons",
3
- "version": "1.148.2",
3
+ "version": "1.148.3",
4
4
  "description": "SAPUI5 Library sap.suite.ui.commons",
5
5
  "homepage": "https://sap.github.io/ui5-tooling/pages/SAPUI5/",
6
6
  "author": "SAP SE (https://www.sap.com)",
@@ -7,7 +7,7 @@
7
7
  SAP UI development toolkit for HTML5 (SAPUI5)
8
8
  (c) Copyright 2009-2015 SAP SE. All rights reserved
9
9
  </copyright>
10
- <version>1.148.2</version>
10
+ <version>1.148.3</version>
11
11
 
12
12
  <documentation>SAP UI library: sap.suite.ui.commons</documentation>
13
13
 
@@ -19,7 +19,7 @@ sap.ui.define([
19
19
  * @extends sap.ui.core.Element
20
20
  *
21
21
  * @author SAP SE
22
- * @version 1.148.2
22
+ * @version 1.148.3
23
23
  * @since 1.65.0
24
24
  *
25
25
  * @constructor
@@ -134,7 +134,7 @@ sap.ui.define([
134
134
  * @extends sap.ui.core.Control
135
135
  *
136
136
  * @author SAP SE
137
- * @version 1.148.2
137
+ * @version 1.148.3
138
138
  * @since 1.56.0
139
139
  *
140
140
  * @constructor
@@ -148,7 +148,7 @@ sap.ui.define([
148
148
  * @extends sap.ui.core.Control
149
149
  *
150
150
  * @author SAP SE
151
- * @version 1.148.2
151
+ * @version 1.148.3
152
152
  * @since 1.56.0
153
153
  *
154
154
  * @constructor
@@ -26,7 +26,7 @@ sap.ui.define([
26
26
  * @extends sap.ui.core.Control
27
27
  *
28
28
  * @author SAP SE
29
- * @version 1.148.2
29
+ * @version 1.148.3
30
30
  * @since 1.56.0
31
31
  *
32
32
  * @constructor
@@ -22,7 +22,7 @@ sap.ui.define([
22
22
  * @extends sap.ui.core.Control
23
23
  *
24
24
  * @author SAP SE
25
- * @version 1.148.2
25
+ * @version 1.148.3
26
26
  * @since 1.60.0
27
27
  *
28
28
  * @constructor
@@ -40,7 +40,7 @@ sap.ui.define([
40
40
  * @extends sap.ui.core.Control
41
41
  *
42
42
  * @author SAP SE
43
- * @version 1.148.2
43
+ * @version 1.148.3
44
44
  * @since 1.56.0
45
45
  *
46
46
  * @constructor
@@ -21,7 +21,7 @@ sap.ui.define([
21
21
  * @extends sap.ui.base.ManagedObject
22
22
  *
23
23
  * @author SAP SE
24
- * @version 1.148.2
24
+ * @version 1.148.3
25
25
  * @since 1.56.0
26
26
  *
27
27
  * @constructor
@@ -23,7 +23,7 @@ sap.ui.define([
23
23
  * @extends sap.ui.core.Control
24
24
  *
25
25
  * @author SAP SE
26
- * @version 1.148.2
26
+ * @version 1.148.3
27
27
  * @since 1.56.0
28
28
  *
29
29
  * @constructor
@@ -118,7 +118,7 @@ sap.ui.define([
118
118
  * @class
119
119
  * @public
120
120
  * @internal
121
- * @version 1.148.2
121
+ * @version 1.148.3
122
122
  */
123
123
  var CloudFilePicker = Dialog.extend("sap.suite.ui.commons.CloudFilePicker", {
124
124
  metadata: {
@@ -50,7 +50,7 @@ sap.ui.define([
50
50
  * @extends sap.ui.core.Control
51
51
  *
52
52
  * @author SAP SE
53
- * @version 1.148.2
53
+ * @version 1.148.3
54
54
  *
55
55
  * @constructor
56
56
  * @public
@@ -38,7 +38,7 @@ sap.ui.define([
38
38
  * @extends sap.ui.core.Control
39
39
  *
40
40
  * @author SAP SE
41
- * @version 1.148.2
41
+ * @version 1.148.3
42
42
  *
43
43
  * @constructor
44
44
  * @public
@@ -22,7 +22,7 @@ sap.ui.define([
22
22
  *
23
23
  * @constructor
24
24
  * @alias sap.suite.ui.commons.flexibility.changeHandler.PropertyChangeMapper
25
- * @version 1.148.2
25
+ * @version 1.148.3
26
26
  * @since 1.50
27
27
  * @private
28
28
  */
@@ -28,7 +28,7 @@ sap.ui.define([
28
28
  * @extends sap.suite.ui.commons.imageeditor.HistoryItem
29
29
  *
30
30
  * @author SAP SE
31
- * @version 1.148.2
31
+ * @version 1.148.3
32
32
  * @since 1.67.0
33
33
  *
34
34
  * @constructor
@@ -30,7 +30,7 @@ sap.ui.define([
30
30
  * @extends sap.suite.ui.commons.imageeditor.HistoryItem
31
31
  *
32
32
  * @author SAP SE
33
- * @version 1.148.2
33
+ * @version 1.148.3
34
34
  * @since 1.66.0
35
35
  *
36
36
  * @constructor
@@ -28,7 +28,7 @@ sap.ui.define([
28
28
  * @extends sap.suite.ui.commons.imageeditor.HistoryItem
29
29
  *
30
30
  * @author SAP SE
31
- * @version 1.148.2
31
+ * @version 1.148.3
32
32
  * @since 1.66.0
33
33
  *
34
34
  * @constructor
@@ -22,7 +22,7 @@ sap.ui.define([
22
22
  * @extends sap.ui.core.Element
23
23
  *
24
24
  * @author SAP SE
25
- * @version 1.148.2
25
+ * @version 1.148.3
26
26
  * @since 1.66.0
27
27
  *
28
28
  * @constructor
@@ -28,7 +28,7 @@ sap.ui.define([
28
28
  * @extends sap.suite.ui.commons.imageeditor.HistoryItem
29
29
  *
30
30
  * @author SAP SE
31
- * @version 1.148.2
31
+ * @version 1.148.3
32
32
  * @since 1.66.0
33
33
  *
34
34
  * @constructor
@@ -25,7 +25,7 @@ sap.ui.define([
25
25
  * @extends sap.suite.ui.commons.imageeditor.HistoryItem
26
26
  *
27
27
  * @author SAP SE
28
- * @version 1.148.2
28
+ * @version 1.148.3
29
29
  * @since 1.66.0
30
30
  *
31
31
  * @constructor
@@ -18,7 +18,7 @@ sap.ui.define([
18
18
  * @extends sap.ui.base.Object
19
19
  *
20
20
  * @author SAP SE
21
- * @version 1.148.2
21
+ * @version 1.148.3
22
22
  * @since 1.66.0
23
23
  *
24
24
  * @abstract
@@ -50,7 +50,7 @@ sap.ui.define([
50
50
  * @extends sap.ui.core.Control
51
51
  *
52
52
  * @author SAP SE
53
- * @version 1.148.2
53
+ * @version 1.148.3
54
54
  * @since 1.66.0
55
55
  *
56
56
  * @constructor
@@ -162,7 +162,7 @@ sap.ui.define([
162
162
  * @extends sap.ui.core.Control
163
163
  *
164
164
  * @author SAP SE
165
- * @version 1.148.2
165
+ * @version 1.148.3
166
166
  * @since 1.66.0
167
167
  *
168
168
  * @constructor
@@ -34,7 +34,7 @@ sap.ui.define([
34
34
  * @extends sap.suite.ui.commons.imageeditor.ImageEditorContainer
35
35
  *
36
36
  * @author SAP SE
37
- * @version 1.148.2
37
+ * @version 1.148.3
38
38
  * @since 1.68.0
39
39
  *
40
40
  * @constructor
@@ -28,7 +28,7 @@ sap.ui.define([
28
28
  * @extends sap.suite.ui.commons.imageeditor.HistoryItem
29
29
  *
30
30
  * @author SAP SE
31
- * @version 1.148.2
31
+ * @version 1.148.3
32
32
  * @since 1.66.0
33
33
  *
34
34
  * @constructor
@@ -23,7 +23,7 @@ sap.ui.define([
23
23
  * @extends sap.suite.ui.commons.imageeditor.HistoryItem
24
24
  *
25
25
  * @author SAP SE
26
- * @version 1.148.2
26
+ * @version 1.148.3
27
27
  * @since 1.66.0
28
28
  *
29
29
  * @constructor
@@ -198,7 +198,7 @@ sap.ui.define([
198
198
  "sap.suite.ui.commons.TargetFilterMeasureColumn",
199
199
  "sap.suite.ui.commons.AriaProperties"
200
200
  ],
201
- version: "1.148.2",
201
+ version: "1.148.3",
202
202
  extensions: {
203
203
  flChangeHandlers: {
204
204
  "sap.suite.ui.commons.Timeline": "sap/suite/ui/commons/flexibility/Timeline"
@@ -295,7 +295,24 @@ sap.ui.define([
295
295
  "LeftToRight": "Start to Finish",
296
296
  "RightToLeft": "Finish to Start",
297
297
  "RightToRight": "Finish to Finish"
298
- }
298
+ }
299
+ },
300
+ /**
301
+ * Enables enhanced line routing for the network graph.
302
+ * When set to <code>true</code>, overlapping connections are visually separated.
303
+ * @public
304
+ */
305
+ enableEnhancedLineRouting: {
306
+ type: "boolean", group: "Behavior", defaultValue: true
307
+ },
308
+ /**
309
+ * Custom labels for element types displayed in the SearchField suggestions and Legend section headers.
310
+ * Supported keys: nodeLabel, nodeLabelPlural, lineLabel, lineLabelPlural, groupLabel, groupLabelPlural.
311
+ * Reverts to the default values for keys that are not set.
312
+ * @public
313
+ */
314
+ labels: {
315
+ type: "object", group: "Appearance", defaultValue: {}
299
316
  }
300
317
  },
301
318
  aggregations: {
@@ -1015,6 +1032,13 @@ sap.ui.define([
1015
1032
  this._createLegend();
1016
1033
  };
1017
1034
 
1035
+ Graph.prototype.setLabels = function (oValue) {
1036
+ this.setProperty("labels", oValue, true);
1037
+ this._createSearchSuggestItems();
1038
+ this._createLegend();
1039
+ return this;
1040
+ };
1041
+
1018
1042
 
1019
1043
  /**
1020
1044
  * Zooms in or out of the graph.
@@ -2889,6 +2913,11 @@ sap.ui.define([
2889
2913
  // indicates current count of suggestion items when they are rendered
2890
2914
  this._oSuggestionItemsModel.setSizeLimit(SUGGESTION_ITEMS_LIMIT);
2891
2915
 
2916
+ const oLabels = this.getLabels() || {};
2917
+ const sNodeLabel = oLabels.nodeLabel || oResourceBundle.getText("NETWORK_GRAPH_NODE");
2918
+ const sLineLabel = oLabels.lineLabel || oResourceBundle.getText("NETWORK_GRAPH_LINE");
2919
+ const sGroupLabel = oLabels.groupLabel || oResourceBundle.getText("NETWORK_GRAPH_GROUP");
2920
+
2892
2921
  aNodes.forEach(function (oNode) {
2893
2922
  var sTitle = Utils.trimText(oNode.getTitle(), TITLE_LENGTH);
2894
2923
  oData.items.push({
@@ -2896,7 +2925,7 @@ sap.ui.define([
2896
2925
  type: SUGGESTIONS.Node,
2897
2926
  icon: oNode.getIcon(),
2898
2927
  key: oNode.getKey(),
2899
- description: "(" + oResourceBundle.getText("NETWORK_GRAPH_NODE") + ")"
2928
+ description: "(" + sNodeLabel + ")"
2900
2929
  });
2901
2930
  });
2902
2931
 
@@ -2909,7 +2938,7 @@ sap.ui.define([
2909
2938
  text: oLine._createSuggestionHelpText(),
2910
2939
  type: SUGGESTIONS.Line,
2911
2940
  key: oLine._getLineId(),
2912
- description: "(" + oResourceBundle.getText("NETWORK_GRAPH_LINE") + ")"
2941
+ description: "(" + sLineLabel + ")"
2913
2942
  });
2914
2943
  }
2915
2944
  });
@@ -2921,7 +2950,7 @@ sap.ui.define([
2921
2950
  text: sTitle ? sTitle : oGroup.getKey(),
2922
2951
  key: oGroup.getKey(),
2923
2952
  type: SUGGESTIONS.Group,
2924
- description: "(" + oResourceBundle.getText("NETWORK_GRAPH_GROUP") + ")"
2953
+ description: "(" + sGroupLabel + ")"
2925
2954
  });
2926
2955
  }
2927
2956
  });
@@ -3088,7 +3117,7 @@ sap.ui.define([
3088
3117
  if (fnHasStatus(oCollection)) {
3089
3118
  oRm.openStart("div").class(sClass).openEnd();
3090
3119
  oRm.openStart("label").class("sapSuiteUiCommonsNetworkGraphLegendTitle").openEnd();
3091
- oRm.text(oResourceBundle.getText(sTitle));
3120
+ oRm.text(sTitle);
3092
3121
  oRm.close("label");
3093
3122
  oRm.close("div");
3094
3123
  // get the status labels for sorting (some statuses can have custom labels)
@@ -3123,12 +3152,13 @@ sap.ui.define([
3123
3152
  return;
3124
3153
  }
3125
3154
 
3155
+ const oLabels = this.getLabels() || {};
3126
3156
  fnProcessElements(this.getNodes(), oNodeStatuses, "sapSuiteUiCommonsNetworkGraphLegendTitleNode",
3127
- "NETWORK_GRAPH_NODES", StatusType.Node);
3157
+ oLabels.nodeLabelPlural || oResourceBundle.getText("NETWORK_GRAPH_NODES"), StatusType.Node);
3128
3158
  fnProcessElements(this.getLines(), oLineStatuses, "sapSuiteUiCommonsNetworkGraphLegendTitleLine",
3129
- "NETWORK_GRAPH_LINES", StatusType.Line);
3159
+ oLabels.lineLabelPlural || oResourceBundle.getText("NETWORK_GRAPH_LINES"), StatusType.Line);
3130
3160
  fnProcessElements(this.getGroups(), oGroupStatuses, "sapSuiteUiCommonsNetworkGraphLegendTitleLine",
3131
- "NETWORK_GRAPH_GROUPS", StatusType.Group);
3161
+ oLabels.groupLabelPlural || oResourceBundle.getText("NETWORK_GRAPH_GROUPS"), StatusType.Group);
3132
3162
  oRm.flush($legend[0]);
3133
3163
  oRm.destroy();
3134
3164
  // $legend.html(sHtml);
@@ -67,46 +67,89 @@ sap.ui.define([
67
67
 
68
68
  // Detect RTL mode from graph
69
69
  const bIsRTL = oGraph._bIsRtl || false;
70
+ const bEnhanced = oGraph.getEnableEnhancedLineRouting ? oGraph.getEnableEnhancedLineRouting() : false;
70
71
 
71
- // Iterate over all lines in the graph
72
- oGraph.getLines().forEach((oLine) => {
73
- const configuredConnectionType = oLine.getConnectionType(); // Get the connection type, if not configured at Line level then the default is Right to Left from source to target.
74
- // In RTL mode, use effective connection type (semantically reversed)
75
- const effectiveConnectionType = this._getEffectiveConnectionType(configuredConnectionType, bIsRTL);
76
- const oSourceNode = oLine.getFromNode();
77
- const oTargetNode = oLine.getToNode();
78
- // Get anchor coordinates for source and target nodes, usually the center of the respective sides
79
- const oSourceAnchor = this._getNodeAnchorCoordinates(oLine.getFromNode(), effectiveConnectionType, "source");
80
- const oTargetAnchor = this._getNodeAnchorCoordinates(oLine.getToNode(), effectiveConnectionType, "target");
81
- const mParams = {
82
- sourceAnchor: oSourceAnchor,
83
- targetAnchor: oTargetAnchor,
84
- sourceNode: oSourceNode,
85
- targetNode: oTargetNode,
86
- sConnectionType: effectiveConnectionType,
87
- gridSize: config.gridSize,
88
- minEdgeLength: config.minEdgeLength
89
- };
90
- // Calculate the path coordinates between the source and target anchors
91
- const aCoordinates = this._calculatePathCoordinates(mParams);
92
- // set the anchor points of the source and target nodes on the line as source and target for drawing the path.
93
- oLine.setSource({
94
- x: oSourceAnchor.x,
95
- y: oSourceAnchor.y
96
- });
97
- oLine.setTarget({
98
- x: oTargetAnchor.x,
99
- y: oTargetAnchor.y
100
- });
101
- // Set the calculated bend points on the line
102
- oLine.clearBends();
103
- // Set the calculated bend points on the line, determined by the connection type.
104
- if (aCoordinates && aCoordinates.length) {
105
- aCoordinates.forEach((oBend) => {
106
- oLine.addBend(oBend);
107
- });
72
+ try {
73
+ if (bEnhanced) {
74
+ this._routingRowCache = this._buildRoutingRowCache(oGraph.getNodes(), config.gridSize);
75
+
76
+ // Precompute per-node connection counts once to avoid rescanning all lines on each call.
77
+ // Keys are normalized to strings on both write and read — node keys may be numeric
78
+ // while Line#getFrom/getTo return them as-is, and Map.get() uses strict key identity.
79
+ this._analysisCache = new Map();
80
+ oGraph.getNodes().forEach(function(oNode) {
81
+ this._analysisCache.set(String(oNode.getKey()), { leftIncoming: 0, leftOutgoing: 0, rightIncoming: 0, rightOutgoing: 0 });
82
+ }, this);
83
+ oGraph.getLines().forEach(function(oLine) {
84
+ if (!oLine.getVisible() || oLine._isIgnored()) { return; }
85
+ var sFrom = oLine.getFrom();
86
+ var sTo = oLine.getTo();
87
+ var sType = oLine.getConnectionType();
88
+ var oFrom = this._analysisCache.get(String(sFrom));
89
+ var oTo = this._analysisCache.get(String(sTo));
90
+ if (oFrom) {
91
+ switch (sType) {
92
+ case "LeftToLeft": case "LeftToRight": oFrom.leftOutgoing++; break;
93
+ default: oFrom.rightOutgoing++; break;
94
+ }
95
+ }
96
+ if (oTo) {
97
+ switch (sType) {
98
+ case "LeftToLeft": case "RightToLeft": oTo.leftIncoming++; break;
99
+ default: oTo.rightIncoming++; break;
100
+ }
101
+ }
102
+ }, this);
108
103
  }
109
- });
104
+ // Iterate over all lines in the graph
105
+ oGraph.getLines().forEach((oLine) => {
106
+ const configuredConnectionType = oLine.getConnectionType(); // Get the connection type, if not configured at Line level then the default is Right to Left from source to target.
107
+ // In RTL mode, use effective connection type (semantically reversed)
108
+ const effectiveConnectionType = this._getEffectiveConnectionType(configuredConnectionType, bIsRTL);
109
+ const oSourceNode = oLine.getFromNode();
110
+ const oTargetNode = oLine.getToNode();
111
+ // Get anchor coordinates for source and target nodes, usually the center of the respective sides
112
+ const oSourceAnchor = this._getNodeAnchorCoordinates(oLine.getFromNode(), effectiveConnectionType, "source");
113
+ const oTargetAnchor = this._getNodeAnchorCoordinates(oLine.getToNode(), effectiveConnectionType, "target");
114
+
115
+ const mParams = {
116
+ sourceAnchor: oSourceAnchor,
117
+ targetAnchor: oTargetAnchor,
118
+ sourceNode: oSourceNode,
119
+ targetNode: oTargetNode,
120
+ sConnectionType: effectiveConnectionType,
121
+ gridSize: config.gridSize,
122
+ minEdgeLength: config.minEdgeLength,
123
+ bEnhanced: bEnhanced
124
+ };
125
+ // Calculate the path coordinates between the source and target anchors
126
+ const aCoordinates = this._calculatePathCoordinates(mParams);
127
+ // set the anchor points of the source and target nodes on the line as source and target for drawing the path.
128
+ oLine.setSource({
129
+ x: oSourceAnchor.x,
130
+ y: oSourceAnchor.y
131
+ });
132
+ oLine.setTarget({
133
+ x: oTargetAnchor.x,
134
+ y: oTargetAnchor.y
135
+ });
136
+ // Set the calculated bend points on the line
137
+ oLine.clearBends();
138
+ // Set the calculated bend points on the line, determined by the connection type.
139
+ if (aCoordinates && aCoordinates.length) {
140
+ aCoordinates.forEach((oBend) => {
141
+ oLine.addBend(oBend);
142
+ });
143
+ }
144
+ });
145
+ } finally {
146
+ this._routingRowCache = null;
147
+ this._analysisCache = null;
148
+ }
149
+
150
+ if (bEnhanced) {
151
+ this._resolveLineNodeCollisions(oGraph.getLines(), oGraph.getNodes(), config.gridSize);
152
+ }
110
153
  },
111
154
  /**
112
155
  * Calculates path coordinates between source and target anchors based on connection type.
@@ -161,7 +204,7 @@ sap.ui.define([
161
204
  * @private
162
205
  */
163
206
  _handleRightToLeftConnection: function (mParams) {
164
- const { sourceNode, gridSize = GRID_SIZE, minEdgeLength = MIN_EDGE_LENGTH } = mParams;
207
+ const { sourceNode, gridSize = GRID_SIZE, minEdgeLength = MIN_EDGE_LENGTH, bEnhanced } = mParams;
165
208
  const { x1, y1, x2, y2, AX1, AY1, AX2, AY2, dx } = this._transformSourceTargetCoordinates(mParams);
166
209
 
167
210
  // CASE 1: Source and target nodes are on the same horizontal line (y1 === y2)
@@ -176,16 +219,39 @@ sap.ui.define([
176
219
  // Visual: [SRC]----→[TGT] (perfect alignment)
177
220
  return [];
178
221
  } else if (anchorVerticalOffset > 5) {
179
- // Visual: [SRC]→ ←[TGT] (offset anchors - add horizontal correction)
180
- // | |
181
- // └─────┘
222
+ // Offset anchors due to different node heights in the same row.
223
+ // For skip-layer (intermediate nodes exist), route using corridor to avoid
224
+ // the vertical Z-bend passing through intermediate nodes.
225
+ if (bEnhanced) {
226
+ const iNodeWidthOff = sourceNode._iWidth || 0;
227
+ const iHorizontalGapOff = x2 - (x1 + iNodeWidthOff);
228
+ if (iHorizontalGapOff > Math.max(iNodeWidthOff, gridSize)) {
229
+ const xDrop = this.getRightGridIntersectionPoint(x1 + iNodeWidthOff, AY1, gridSize, minEdgeLength).x;
230
+ const xRise = this.getLeftGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
231
+ const y3 = this._getRoutingYBelow(sourceNode, gridSize);
232
+ return [{ x: xDrop, y: AY1 }, { x: xDrop, y: y3 }, { x: xRise, y: y3 }, { x: xRise, y: AY2 }];
233
+ }
234
+ }
235
+ // Adjacent nodes — simple Z-bend through the gap between them
182
236
  const midX = AX1 + (AX2 - AX1) / 2;
183
237
  return [
184
238
  { x: midX, y: AY1 },
185
239
  { x: midX, y: AY2 }
186
240
  ];
187
241
  } else {
188
- // Multiple connections - use straight line anyway for now
242
+ // Multiple connections from same side.
243
+ if (bEnhanced) {
244
+ // If the gap is large enough to suggest intermediate nodes, route below
245
+ // to avoid the line passing through them visually.
246
+ const iNodeWidth = sourceNode._iWidth || 0;
247
+ const iHorizontalGap = x2 - (x1 + iNodeWidth);
248
+ if (iHorizontalGap > Math.max(iNodeWidth, gridSize)) {
249
+ const xDrop = this.getRightGridIntersectionPoint(x1 + iNodeWidth, AY1, gridSize, minEdgeLength).x;
250
+ const xRise = this.getLeftGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
251
+ const y3 = this._getRoutingYBelow(sourceNode, gridSize);
252
+ return [{ x: xDrop, y: AY1 }, { x: xDrop, y: y3 }, { x: xRise, y: y3 }, { x: xRise, y: AY2 }];
253
+ }
254
+ }
189
255
  return [];
190
256
  }
191
257
  } else if (x1 >= x2) {
@@ -197,7 +263,7 @@ sap.ui.define([
197
263
  // Create U-shaped path going around both nodes
198
264
  const x3 = this.getRightGridIntersectionPoint(AX1, AY1, gridSize, minEdgeLength).x;
199
265
  const x4 = this.getLeftGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
200
- const y3 = this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
266
+ const y3 = bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
201
267
 
202
268
  return [
203
269
  { x: x3, y: AY1 }, // Right from source
@@ -219,13 +285,42 @@ sap.ui.define([
219
285
  const x3 = AX1 + (x2 - (x1 + sourceNode._iWidth)) / 2;
220
286
  return [{ x: x3, y: AY1 }, { x: x3, y: AY2 }];
221
287
  } else {
222
- // SCENARIO 2A2: Nodes are far apart - use grid-aligned Z-bend
223
- // Visual: [SRC]→ |
224
- // |
225
- //
226
- // ←[TGT]
227
- const x3 = this.getRightGridIntersectionPoint(x1 + sourceNode._iWidth, AY1, gridSize, minEdgeLength).x;
228
- return [{ x: x3, y: AY1 }, { x: x3, y: AY2 }];
288
+ // SCENARIO 2A2: Nodes are far apart
289
+ // If the horizontal gap is larger than one node width, intermediate nodes
290
+ // likely occupy the space — route the vertical segments at source/target
291
+ // column boundaries to avoid passing through them.
292
+ const iGap = x2 - (x1 + sourceNode._iWidth);
293
+ if (bEnhanced && iGap > sourceNode._iWidth) {
294
+ // SCENARIO 2A2-SKIP: Skip-layer connection.
295
+ // First go right to the nearest grid point (so the line visually exits the
296
+ // source from the center-right side), then drop/rise outside intermediate
297
+ // columns, then go horizontally to the target.
298
+ // Visual (y1 ≤ y2): [SRC]→——| Visual (y1 > y2): [SRC]→——|
299
+ // | ↑
300
+ // ↓————————→ | ←———————————|
301
+ // ↑
302
+ // ←[TGT]
303
+ const xDrop = this.getRightGridIntersectionPoint(x1 + sourceNode._iWidth, AY1, gridSize, minEdgeLength).x;
304
+ const xRise = this.getLeftGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
305
+ let y3;
306
+ if (y1 <= y2) {
307
+ y3 = this._getRoutingYBelow(sourceNode, gridSize);
308
+ } else {
309
+ y3 = this.getTopGridIntersectionPoint(xDrop, Math.min(y1, y2), gridSize, minEdgeLength).y;
310
+ if (y3 <= 0) {
311
+ y3 = this._getRoutingYBelow(sourceNode, gridSize);
312
+ }
313
+ }
314
+ return [{ x: xDrop, y: AY1 }, { x: xDrop, y: y3 }, { x: xRise, y: y3 }, { x: xRise, y: AY2 }];
315
+ } else {
316
+ // SCENARIO 2A2: Adjacent layers — grid-aligned Z-bend
317
+ // Visual: [SRC]→ |
318
+ // |
319
+ // ↓
320
+ // ←[TGT]
321
+ const x3 = this.getRightGridIntersectionPoint(x1 + sourceNode._iWidth, AY1, gridSize, minEdgeLength).x;
322
+ return [{ x: x3, y: AY1 }, { x: x3, y: AY2 }];
323
+ }
229
324
  }
230
325
  } else if (dx <= minEdgeLength + sourceNode._iWidth) {
231
326
  // SCENARIO 2B: Nodes are too close - create S-shaped path
@@ -239,7 +334,7 @@ sap.ui.define([
239
334
  // ←[TGT]
240
335
  const x3 = this.getRightGridIntersectionPoint(x1 + sourceNode._iWidth, y1, gridSize, minEdgeLength).x;
241
336
  const x4 = this.getLeftGridIntersectionPoint(x2, AY2, gridSize, minEdgeLength).x;
242
- const y3 = this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
337
+ const y3 = bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
243
338
  return [{ x: x3, y: AY1 }, { x: x3, y: y3 }, { x: x4, y: y3 }, { x: x4, y: AY2 }];
244
339
  } else if (y1 > y2) {
245
340
  // SCENARIO 2B2: Source below target - S-bend going up
@@ -265,7 +360,7 @@ sap.ui.define([
265
360
  // ←[TGT] ←---------
266
361
  const x3 = this.getRightGridIntersectionPoint(AX1, AY1, gridSize, minEdgeLength).x;
267
362
  const x4 = this.getLeftGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
268
- const y3 = this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
363
+ const y3 = bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
269
364
  return [
270
365
  { x: x3, y: AY1 }, // Right from source
271
366
  { x: x3, y: y3 }, // Down past both nodes
@@ -310,7 +405,7 @@ sap.ui.define([
310
405
  * @private
311
406
  */
312
407
  _handleLeftToRightConnection: function (mParams) {
313
- const { sourceNode, targetNode, gridSize = GRID_SIZE, minEdgeLength = MIN_EDGE_LENGTH } = mParams;
408
+ const { sourceNode, targetNode, gridSize = GRID_SIZE, minEdgeLength = MIN_EDGE_LENGTH, bEnhanced } = mParams;
314
409
  const { x1, y1, x2, y2, AX1, AY1, AX2, AY2, dx } = this._transformSourceTargetCoordinates(mParams);
315
410
  // CASE 1: Source and target nodes are on the same horizontal line (y1 === y2)
316
411
  if (y1 === y2) {
@@ -345,7 +440,7 @@ sap.ui.define([
345
440
  // Create U-shaped path going around both nodes
346
441
  const x3 = this.getLeftGridIntersectionPoint(AX1, AY1, gridSize, minEdgeLength).x;
347
442
  const x4 = this.getRightGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
348
- const y3 = this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
443
+ const y3 = bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
349
444
 
350
445
  return [
351
446
  { x: x3, y: AY1 }, // Right from source
@@ -388,7 +483,7 @@ sap.ui.define([
388
483
  // [TGT]→
389
484
  const x3 = this.getLeftGridIntersectionPoint(AX1, AY1, gridSize, minEdgeLength).x;
390
485
  const x4 = this.getRightGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
391
- const y3 = this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
486
+ const y3 = bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
392
487
  return [{ x: x3, y: AY1 }, { x: x3, y: y3 }, { x: x4, y: y3 }, { x: x4, y: AY2 }];
393
488
  } else if (y1 > y2) {
394
489
  // SCENARIO 2B2: Source below target - S-bend going up
@@ -414,7 +509,7 @@ sap.ui.define([
414
509
  // --------→[TGT]
415
510
  const x3 = this.getLeftGridIntersectionPoint(AX1, AY1, gridSize, minEdgeLength).x;
416
511
  const x4 = this.getRightGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
417
- const y3 = this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
512
+ const y3 = bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
418
513
  return [{ x: x3, y: AY1 }, { x: x3, y: y3 }, { x: x4, y: y3 }, { x: x4, y: AY2 }];
419
514
  } else if (y1 > y2) {
420
515
  // SCENARIO 3B: Source below and left of target - S-bend up and right
@@ -454,7 +549,7 @@ sap.ui.define([
454
549
  * @private
455
550
  */
456
551
  _handleLeftToLeftConnection: function (mParams) {
457
- const { sourceNode, targetNode, gridSize = GRID_SIZE, minEdgeLength = MIN_EDGE_LENGTH } = mParams;
552
+ const { sourceNode, targetNode, gridSize = GRID_SIZE, minEdgeLength = MIN_EDGE_LENGTH, bEnhanced } = mParams;
458
553
  const { x1, y1, x2, y2, AX1, AY1, AX2, AY2 } = this._transformSourceTargetCoordinates(mParams);
459
554
  // CASE 1: Source and target nodes are on the same horizontal line (y1 === y2)
460
555
  if (y1 === y2) {
@@ -469,7 +564,7 @@ sap.ui.define([
469
564
  (AX1 + (x2 + targetNode._iWidth)) / 2 :
470
565
  this.getLeftGridIntersectionPoint(AX1, AY1, gridSize, minEdgeLength).x;
471
566
  const x4 = this.getLeftGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
472
- const y3 = y1 <= gridSize ? this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y : this.getTopGridIntersectionPoint(x3, y1, gridSize, minEdgeLength).y;
567
+ const y3 = y1 <= gridSize ? (bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y) : this.getTopGridIntersectionPoint(x3, y1, gridSize, minEdgeLength).y;
473
568
  return [
474
569
  { x: x3, y: AY1 }, // Left from source
475
570
  { x: x3, y: y3 }, // Down/up to avoid nodes
@@ -486,7 +581,7 @@ sap.ui.define([
486
581
  const x4 = Math.abs(x1 + sourceNode._iWidth - x2) <= gridSize ?
487
582
  (AX2 + x2) / 2 :
488
583
  this.getLeftGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
489
- const y3 = y1 <= gridSize ? this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y : this.getTopGridIntersectionPoint(x3, y1, gridSize, minEdgeLength).y;
584
+ const y3 = y1 <= gridSize ? (bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y) : this.getTopGridIntersectionPoint(x3, y1, gridSize, minEdgeLength).y;
490
585
  return [{ x: x3, y: AY1 }, { x: x3, y: y3 }, { x: x4, y: y3 }, { x: x4, y: AY2 }];
491
586
  }
492
587
  } else if (y1 != y2) {
@@ -553,7 +648,7 @@ sap.ui.define([
553
648
  x3 = this.getLeftGridIntersectionPoint(AX1, AY1, gridSize, minEdgeLength).x;
554
649
  }
555
650
  const x4 = this.getLeftGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
556
- const y3 = this.getBottomGridIntersectionPoint(x4, y2 + targetNode._iHeight, gridSize, minEdgeLength).y;
651
+ const y3 = bEnhanced ? this._getRoutingYBelow(targetNode, gridSize) : this.getBottomGridIntersectionPoint(x4, y2 + targetNode._iHeight, gridSize, minEdgeLength).y;
557
652
  return [{ x: x3, y: AY1 }, { x: x3, y: y3 }, { x: x4, y: y3 }, { x: x4, y: AY2 }];
558
653
  }
559
654
  }
@@ -574,7 +669,7 @@ sap.ui.define([
574
669
  } else {
575
670
  x4 = this.getLeftGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
576
671
  }
577
- const y3 = (AY2 > (y1 + sourceNode._iHeight)) ? AY2 : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y;
672
+ const y3 = (AY2 > (y1 + sourceNode._iHeight)) ? AY2 : (bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y);
578
673
  const aCoordinates = AY2 > (y1 + sourceNode._iHeight) ? [{ x: x3, y: AY1 }, { x: x3, y: y3 }] : [{ x: x3, y: AY1 }, { x: x3, y: y3 }, { x: x4, y: y3 }, { x: x4, y: AY2 }];
579
674
  return aCoordinates;
580
675
  } else if (y1 > y2) {
@@ -623,7 +718,7 @@ sap.ui.define([
623
718
  * @private
624
719
  */
625
720
  _handleRightToRightConnection: function (mParams) {
626
- const { sourceNode, targetNode, gridSize = GRID_SIZE, minEdgeLength = MIN_EDGE_LENGTH } = mParams;
721
+ const { sourceNode, targetNode, gridSize = GRID_SIZE, minEdgeLength = MIN_EDGE_LENGTH, bEnhanced } = mParams;
627
722
  const { x1, y1, x2, y2, AX1, AY1, AX2, AY2 } = this._transformSourceTargetCoordinates(mParams);
628
723
  // CASE 1: Source and target nodes are on the same horizontal line (y1 === y2)
629
724
  if (y1 === y2) {
@@ -636,7 +731,7 @@ sap.ui.define([
636
731
  const x3 = this.getRightGridIntersectionPoint(AX1, AY1, gridSize, minEdgeLength).x;
637
732
  const iDiff = Math.abs(x1 - AX2);
638
733
  const x4 = iDiff <= gridSize ? (x1 + x2) / 2 : this.getRightGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
639
- const y3 = y1 < gridSize ? this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y : this.getTopGridIntersectionPoint(x3, y1, gridSize, minEdgeLength).y;
734
+ const y3 = y1 < gridSize ? (bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y) : this.getTopGridIntersectionPoint(x3, y1, gridSize, minEdgeLength).y;
640
735
  return [{ x: x3, y: AY1 }, { x: x3, y: y3 }, { x: x4, y: y3 }, { x: x4, y: AY2 }];
641
736
  } else if (x1 <= x2) {
642
737
  // SCENARIO 1B: Source to the left of target - create C-shaped path on right side
@@ -647,7 +742,7 @@ sap.ui.define([
647
742
  const iDff = Math.abs(AX1 - x2);
648
743
  const x3 = iDff <= gridSize ? (AX1 + x2) / 2 : this.getRightGridIntersectionPoint(AX1, AY1, gridSize, minEdgeLength).x;
649
744
  const x4 = this.getRightGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
650
- const y3 = y1 < gridSize ? this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y : this.getTopGridIntersectionPoint(x3, y1, gridSize, minEdgeLength).y;
745
+ const y3 = y1 < gridSize ? (bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y) : this.getTopGridIntersectionPoint(x3, y1, gridSize, minEdgeLength).y;
651
746
  return [{ x: x3, y: AY1 }, { x: x3, y: y3 }, { x: x4, y: y3 }, { x: x4, y: AY2 }];
652
747
  }
653
748
  } else if (y1 != y2) {
@@ -669,7 +764,7 @@ sap.ui.define([
669
764
  } else {
670
765
  x4 = this.getRightGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
671
766
  }
672
- const y3 = y1 <= gridSize ? this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y : this.getTopGridIntersectionPoint(x3, y1, gridSize, minEdgeLength).y;
767
+ const y3 = y1 <= gridSize ? (bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y) : this.getTopGridIntersectionPoint(x3, y1, gridSize, minEdgeLength).y;
673
768
  if (AY2 > y1 + sourceNode._iHeight) {
674
769
  // Simple vertical connection if target anchor is below source node
675
770
  // Visual: [SRC]→
@@ -702,7 +797,7 @@ sap.ui.define([
702
797
  } else {
703
798
  x4 = this.getRightGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
704
799
  }
705
- const y3 = y2 <= gridSize ? this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength) : this.getTopGridIntersectionPoint(x4, y2, gridSize, minEdgeLength).y;
800
+ const y3 = y2 <= gridSize ? (bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength)) : this.getTopGridIntersectionPoint(x4, y2, gridSize, minEdgeLength).y;
706
801
  if (AY1 > y2 + targetNode._iHeight) {
707
802
  // Simple vertical connection if target anchor is below source node
708
803
  // Visual: [SRC]→
@@ -738,7 +833,7 @@ sap.ui.define([
738
833
  } else {
739
834
  x4 = this.getRightGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
740
835
  }
741
- const y3 = y1 <= gridSize ? this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y : this.getTopGridIntersectionPoint(x3, y1, gridSize, minEdgeLength).y;
836
+ const y3 = y1 <= gridSize ? (bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength).y) : this.getTopGridIntersectionPoint(x3, y1, gridSize, minEdgeLength).y;
742
837
  if (AY2 > y1 + sourceNode._iHeight) {
743
838
  // Simple vertical connection if target anchor is below source node
744
839
  // Visual: [SRC]→
@@ -773,7 +868,7 @@ sap.ui.define([
773
868
  } else {
774
869
  x4 = this.getRightGridIntersectionPoint(AX2, AY2, gridSize, minEdgeLength).x;
775
870
  }
776
- const y3 = y2 <= gridSize ? this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength) : this.getTopGridIntersectionPoint(x4, y2, gridSize, minEdgeLength).y;
871
+ const y3 = y2 <= gridSize ? (bEnhanced ? this._getRoutingYBelow(sourceNode, gridSize) : this.getBottomGridIntersectionPoint(x3, y1 + sourceNode._iHeight, gridSize, minEdgeLength)) : this.getTopGridIntersectionPoint(x4, y2, gridSize, minEdgeLength).y;
777
872
  if (AY1 > y2 + targetNode._iHeight) {
778
873
  // Simple vertical connection if target anchor is below source node
779
874
  // Visual: [SRC]→
@@ -867,6 +962,150 @@ sap.ui.define([
867
962
  }
868
963
  return { x: x, y: yIntersection };
869
964
  },
965
+ /**
966
+ * Post-processing pass: detects horizontal line segments that pass through a node's
967
+ * bounding box and shifts them below the collision.
968
+ * Only interior segments are adjusted, source and target anchors are never moved.
969
+ *
970
+ * @param {sap.suite.ui.commons.networkgraph.Line[]} aLines
971
+ * @param {sap.suite.ui.commons.networkgraph.Node[]} aNodes
972
+ * @param {number} gridSize
973
+ * @private
974
+ */
975
+ _resolveLineNodeCollisions: function(aLines, aNodes, gridSize) {
976
+ var aBounds = aNodes.map(function(oNode) {
977
+ return {
978
+ x: oNode.getX(),
979
+ y: oNode.getY(),
980
+ r: oNode.getX() + (oNode._iWidth || 0),
981
+ b: oNode.getY() + (oNode._iHeight || 0)
982
+ };
983
+ }).filter(function(b) { return b.r > b.x && b.b > b.y; });
984
+
985
+ aLines.forEach(function(oLine) {
986
+ if (oLine.getVisible && !oLine.getVisible()) { return; }
987
+ if (oLine._isIgnored && oLine._isIgnored()) { return; }
988
+
989
+ var aBends = oLine.getBends();
990
+ var aPoints = [{ x: oLine.getSource().getX(), y: oLine.getSource().getY() }];
991
+ aBends.forEach(function(oBend) { aPoints.push({ x: oBend.getX(), y: oBend.getY() }); });
992
+ aPoints.push({ x: oLine.getTarget().getX(), y: oLine.getTarget().getY() });
993
+
994
+ var iPointCount = aPoints.length;
995
+ var bChanged = false;
996
+
997
+ // Only check interior segments — never move source (0) or target (iPointCount-1)
998
+ for (var i = 1; i < iPointCount - 2; i++) {
999
+ var p1 = aPoints[i], p2 = aPoints[i + 1];
1000
+ if (p1.y !== p2.y) { continue; } // skip vertical segments
1001
+
1002
+ var y = p1.y;
1003
+ var xMin = Math.min(p1.x, p2.x);
1004
+ var xMax = Math.max(p1.x, p2.x);
1005
+
1006
+ var iMaxBottom = -Infinity;
1007
+ var bCollision = false;
1008
+ aBounds.forEach(function(oBound) {
1009
+ if (y > oBound.y && y <= oBound.b && xMin < oBound.r && xMax > oBound.x) {
1010
+ bCollision = true;
1011
+ if (oBound.b > iMaxBottom) { iMaxBottom = oBound.b; }
1012
+ }
1013
+ });
1014
+
1015
+ if (bCollision) {
1016
+ var iNewY = iMaxBottom + Math.round(gridSize * 0.5);
1017
+ aPoints[i].y = iNewY;
1018
+ aPoints[i + 1].y = iNewY;
1019
+ bChanged = true;
1020
+ }
1021
+ }
1022
+
1023
+ if (bChanged) {
1024
+ oLine.clearBends();
1025
+ for (var j = 1; j < iPointCount - 1; j++) {
1026
+ oLine.addBend(aPoints[j]);
1027
+ }
1028
+ }
1029
+ });
1030
+ },
1031
+
1032
+ /**
1033
+ * Builds a WeakMap of per-node row boundary data used by _getRoutingYBelow.
1034
+ * Uses two passes to avoid the single-pass bug where iRowBottom is not yet finalised
1035
+ * when a next-row candidate is evaluated.
1036
+ *
1037
+ * @param {object[]} aNodes - All graph nodes
1038
+ * @param {number} gridSize - Grid size used for same-row proximity threshold
1039
+ * @returns {WeakMap} node → { rowBottom, nextRowTop }
1040
+ * @private
1041
+ */
1042
+ _buildRoutingRowCache: function(aNodes, gridSize) {
1043
+ var mCache = new WeakMap();
1044
+ if (!aNodes.length) { return mCache; }
1045
+
1046
+ // Pass 1: group nodes by row (center within gridSize/2) and compute rowBottom per group.
1047
+ // The representative centerY is intentionally kept as the first node's center, a deliberate
1048
+ // approximation that avoids updating all prior comparisons when new nodes join.
1049
+ var aRows = [];
1050
+ aNodes.forEach(function(oNode) {
1051
+ var h = oNode._iHeight || 0;
1052
+ var centerY = oNode.getY() + h / 2;
1053
+ var oRow = null;
1054
+ for (var i = 0; i < aRows.length; i++) {
1055
+ if (Math.abs(aRows[i].centerY - centerY) < gridSize / 2) {
1056
+ oRow = aRows[i];
1057
+ break;
1058
+ }
1059
+ }
1060
+ if (oRow) {
1061
+ oRow.nodes.push(oNode);
1062
+ oRow.rowBottom = Math.max(oRow.rowBottom, oNode.getY() + h);
1063
+ oRow.rowTop = Math.min(oRow.rowTop, oNode.getY());
1064
+ } else {
1065
+ aRows.push({ centerY: centerY, nodes: [oNode], rowBottom: oNode.getY() + h, rowTop: oNode.getY() });
1066
+ }
1067
+ });
1068
+
1069
+ // Pass 2: for each row, find the nearest next rowTop. rowTop isn't monotonic with
1070
+ // rowBottom if the heights vary, so the scan can't stop at the first match. It must
1071
+ // keep the minimum qualifying rowTop across all subsequent rows.
1072
+ aRows.sort(function(a, b) { return a.rowBottom - b.rowBottom; });
1073
+ aRows.forEach(function(oRow, iRowIdx) {
1074
+ var iNextRowTop = Infinity;
1075
+ for (var k = iRowIdx + 1; k < aRows.length; k++) {
1076
+ if (aRows[k].rowTop > oRow.rowBottom && aRows[k].rowTop < iNextRowTop) {
1077
+ iNextRowTop = aRows[k].rowTop;
1078
+ }
1079
+ }
1080
+ oRow.nodes.forEach(function(oNode) {
1081
+ mCache.set(oNode, { rowBottom: oRow.rowBottom, nextRowTop: iNextRowTop });
1082
+ });
1083
+ });
1084
+
1085
+ return mCache;
1086
+ },
1087
+
1088
+ /**
1089
+ * Returns the Y coordinate of the horizontal routing corridor below a node's row.
1090
+ * Uses pre-computed row boundary data from _buildRoutingRowCache (O(1) lookup).
1091
+ * Falls back to node-bottom + 0.75 * gridSize when called outside normalizeLines.
1092
+ *
1093
+ * @param {object} oNode - Source/target node
1094
+ * @param {number} gridSize - Grid size for fallback spacing
1095
+ * @returns {number} Y coordinate of the routing corridor
1096
+ * @private
1097
+ */
1098
+ _getRoutingYBelow: function(oNode, gridSize) {
1099
+ var oData = this._routingRowCache && this._routingRowCache.get(oNode);
1100
+ var iRowBottom = oData ? oData.rowBottom : oNode.getY() + (oNode._iHeight || 0);
1101
+ var iNextRowTop = oData ? oData.nextRowTop : Infinity;
1102
+
1103
+ if (iNextRowTop === Infinity) {
1104
+ return iRowBottom + Math.round(gridSize * 0.75);
1105
+ }
1106
+ return Math.round((iRowBottom + iNextRowTop) / 2);
1107
+ },
1108
+
870
1109
  /**
871
1110
  * Gets the anchor coordinates for a node based on connection type and anchor type.
872
1111
  * Enhanced to handle center positioning for single connections and offset positioning for mixed connections.
@@ -1012,6 +1251,13 @@ sap.ui.define([
1012
1251
  return { leftIncoming: 0, leftOutgoing: 0, rightIncoming: 0, rightOutgoing: 0 };
1013
1252
  }
1014
1253
 
1254
+ if (this._analysisCache) {
1255
+ var sCachedKey = oNode.getKey && oNode.getKey();
1256
+ if (sCachedKey !== undefined && this._analysisCache.has(String(sCachedKey))) {
1257
+ return this._analysisCache.get(String(sCachedKey));
1258
+ }
1259
+ }
1260
+
1015
1261
  const oGraph = oNode.getParent();
1016
1262
  const aLines = oGraph.getLines();
1017
1263
  const sNodeKey = oNode.getKey();
@@ -95,7 +95,8 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
95
95
  horizontalSpacing: DEFAULT_HORIZONTAL_SPACING,
96
96
  verticalSpacing: DEFAULT_VERTICAL_SPACING,
97
97
  componentArrangement: oComponentArrangement.Horizontal,
98
- clearCache: false
98
+ clearCache: false,
99
+ bEnhancedLineRouting: oGraph.getEnableEnhancedLineRouting ? oGraph.getEnableEnhancedLineRouting() : false
99
100
  },
100
101
  mConfig || {}
101
102
  );
@@ -113,9 +114,11 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
113
114
  this._calculateCoordinates(oGraph, config);
114
115
  this._adjustHorizontalSpacingForNodeWidths(oGraph, config);
115
116
  this._adjustVerticalSpacingForNodeHeights(oGraph, config);
117
+
116
118
  if (oGraph._bIsRtl) {
117
119
  this._horizontalMirror(oGraph, config.horizontalSpacing);
118
120
  }
121
+
119
122
  this._storeFinalPositions(oGraph, config.horizontalSpacing);
120
123
  } catch (e) {
121
124
  Log.error("DependencyLayoutHelper error: " + e.message, e);
@@ -309,7 +312,7 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
309
312
  * @param {object} config - Configuration object
310
313
  * @private
311
314
  */
312
- _processComponents: function (aComponents, { componentArrangement }) {
315
+ _processComponents: function (aComponents, { componentArrangement, bEnhancedLineRouting }) {
313
316
  const aIsolatedNodes = [];
314
317
  const aConnectedComponents = [];
315
318
 
@@ -330,7 +333,7 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
330
333
  let iMaxLayerAcrossAll = 0;
331
334
 
332
335
  aConnectedComponents.forEach((aComponentNodes, iCompIndex) => {
333
- this._assignLayersForComponent(aComponentNodes, iCompIndex);
336
+ this._assignLayersForComponent(aComponentNodes, iCompIndex, bEnhancedLineRouting);
334
337
  this._adjustParallelLayersForComponent(aComponentNodes);
335
338
  this._assignVerticalPositionsForComponent(aComponentNodes);
336
339
 
@@ -394,7 +397,7 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
394
397
  * @param {number} iComponentId - Component identifier
395
398
  * @private
396
399
  */
397
- _assignLayersForComponent: function (aComponentNodes, iComponentId) {
400
+ _assignLayersForComponent: function (aComponentNodes, iComponentId, bEnhancedLineRouting) {
398
401
  const aQueue = [];
399
402
  const mInDegree = {};
400
403
 
@@ -464,18 +467,30 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
464
467
  ) {
465
468
  const iCurrentLayer = oNode._layoutData.layer;
466
469
  let iMaxTargetLayer = -1;
470
+ let iMinTargetLayer = Infinity;
467
471
  oNode._layoutData.outgoing.forEach((t) => {
468
472
  if (t._layoutData.layer > iMaxTargetLayer) {
469
473
  iMaxTargetLayer = t._layoutData.layer;
470
474
  }
475
+ if (t._layoutData.layer < iMinTargetLayer) {
476
+ iMinTargetLayer = t._layoutData.layer;
477
+ }
471
478
  });
472
479
 
473
480
  const bTargetHasOtherIncoming = oNode._layoutData.outgoing.some(
474
481
  (t) => t._layoutData.incoming.filter((n) => n !== oNode).length > 0
475
482
  );
476
483
 
477
- if (iMaxTargetLayer > iCurrentLayer && bTargetHasOtherIncoming) {
478
- oNode._layoutData.layer = Math.max(0, iMaxTargetLayer - 1);
484
+ // Guard: skip if any target is at or below the proposed layer that would create same-layer edges.
485
+ const iProposedLayer = Math.max(0, iMaxTargetLayer - 1);
486
+ if (bEnhancedLineRouting) {
487
+ if (iMaxTargetLayer > iCurrentLayer && bTargetHasOtherIncoming && iMinTargetLayer > iProposedLayer) {
488
+ oNode._layoutData.layer = iProposedLayer;
489
+ }
490
+ } else {
491
+ if (iMaxTargetLayer > iCurrentLayer && bTargetHasOtherIncoming) {
492
+ oNode._layoutData.layer = iProposedLayer;
493
+ }
479
494
  }
480
495
  }
481
496
  });
@@ -30,7 +30,7 @@ sap.ui.define([
30
30
  * @extends sap.suite.ui.commons.statusindicator.SimpleShape
31
31
  *
32
32
  * @author SAP SE
33
- * @version 1.148.2
33
+ * @version 1.148.3
34
34
  * @since 1.50
35
35
  *
36
36
  * @constructor
@@ -36,7 +36,7 @@ sap.ui.define([
36
36
  * @extends sap.suite.ui.commons.statusindicator.Shape
37
37
  *
38
38
  * @author SAP SE
39
- * @version 1.148.2
39
+ * @version 1.148.3
40
40
  * @since 1.50
41
41
  *
42
42
  * @constructor
@@ -21,7 +21,7 @@ sap.ui.define([
21
21
  * @extends sap.ui.core.Control
22
22
  *
23
23
  * @author SAP SE
24
- * @version 1.148.2
24
+ * @version 1.148.3
25
25
  * @since 1.50
26
26
  *
27
27
  * @constructor
@@ -23,7 +23,7 @@ sap.ui.define([
23
23
  * @extends sap.ui.core.Control
24
24
  *
25
25
  * @author SAP SE
26
- * @version 1.148.2
26
+ * @version 1.148.3
27
27
  * @since 1.50
28
28
  *
29
29
  * @constructor
@@ -23,7 +23,7 @@ sap.ui.define([
23
23
  * @extends sap.suite.ui.commons.statusindicator.CustomShape
24
24
  *
25
25
  * @author SAP SE
26
- * @version 1.148.2
26
+ * @version 1.148.3
27
27
  * @since 1.60.0
28
28
  *
29
29
  * @constructor
@@ -29,7 +29,7 @@ sap.ui.define([
29
29
  * @extends sap.suite.ui.commons.statusindicator.SimpleShape
30
30
  *
31
31
  * @author SAP SE
32
- * @version 1.148.2
32
+ * @version 1.148.3
33
33
  * @since 1.50
34
34
  *
35
35
  * @constructor
@@ -28,7 +28,7 @@ sap.ui.define([
28
28
  * @extends sap.ui.core.Control
29
29
  *
30
30
  * @author SAP SE
31
- * @version 1.148.2
31
+ * @version 1.148.3
32
32
  * @since 1.50
33
33
  *
34
34
  * @constructor
@@ -29,7 +29,7 @@ sap.ui.define(
29
29
  * @extends sap.suite.ui.commons.statusindicator.SimpleShape
30
30
  *
31
31
  * @author SAP SE
32
- * @version 1.148.2
32
+ * @version 1.148.3
33
33
  * @since 1.50
34
34
  *
35
35
  * @constructor
@@ -42,7 +42,7 @@ sap.ui.define([
42
42
  * @extends sap.ui.core.Control
43
43
  *
44
44
  * @author SAP SE
45
- * @version 1.148.2
45
+ * @version 1.148.3
46
46
  * @since 1.50
47
47
  *
48
48
  * @constructor
@@ -30,7 +30,7 @@ sap.ui.define([
30
30
  * @extends sap.ui.core.Control
31
31
  *
32
32
  * @author SAP SE
33
- * @version 1.148.2
33
+ * @version 1.148.3
34
34
  * @since 1.50
35
35
  *
36
36
  * @constructor
@@ -30,7 +30,7 @@ sap.ui.define([
30
30
  * @extends sap.suite.ui.commons.statusindicator.Shape
31
31
  *
32
32
  * @author SAP SE
33
- * @version 1.148.2
33
+ * @version 1.148.3
34
34
  * @since 1.50
35
35
  *
36
36
  * @constructor
@@ -49,7 +49,7 @@ sap.ui.define([
49
49
  * @extends sap.ui.core.Control
50
50
  *
51
51
  * @author SAP SE
52
- * @version 1.148.2
52
+ * @version 1.148.3
53
53
  * @since 1.50
54
54
  *
55
55
  * @constructor
@@ -56,7 +56,7 @@ sap.ui.define([
56
56
  * @extends sap.ui.core.Control
57
57
  *
58
58
  * @author SAP SE
59
- * @version 1.148.2
59
+ * @version 1.148.3
60
60
  * @since 1.58.0
61
61
  *
62
62
  * @constructor
@@ -41,7 +41,7 @@ sap.ui.define([
41
41
  * @extends sap.ui.core.Control
42
42
  *
43
43
  * @author SAP SE
44
- * @version 1.148.2
44
+ * @version 1.148.3
45
45
  * @since 1.58.0
46
46
  *
47
47
  * @constructor
@@ -41,7 +41,7 @@ sap.ui.define([
41
41
  * @extends sap.ui.core.Control
42
42
  *
43
43
  * @author SAP SE
44
- * @version 1.148.2
44
+ * @version 1.148.3
45
45
  * @since 1.58.0
46
46
  *
47
47
  * @constructor
@@ -24,7 +24,7 @@ sap.ui.define([
24
24
  * @extends sap.ui.core.Control
25
25
  *
26
26
  * @author SAP SE
27
- * @version 1.148.2
27
+ * @version 1.148.3
28
28
  * @since 1.58.0
29
29
  *
30
30
  * @constructor
@@ -65,7 +65,7 @@ sap.ui.define([
65
65
  * @extends sap.m.Panel
66
66
  *
67
67
  * @author SAP SE
68
- * @version 1.148.2
68
+ * @version 1.148.3
69
69
  * @since 1.58.0
70
70
  *
71
71
  * @constructor