@sapui5/sap.suite.ui.commons 1.145.1 → 1.147.0

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 (214) 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 +83 -4
  5. package/src/sap/suite/ui/commons/CalculationBuilderExpression.js +87 -14
  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/CalculationBuilderInput.js +11 -0
  9. package/src/sap/suite/ui/commons/CalculationBuilderItem.js +16 -3
  10. package/src/sap/suite/ui/commons/CalculationBuilderValidationResult.js +1 -1
  11. package/src/sap/suite/ui/commons/CalculationBuilderVariable.js +1 -1
  12. package/src/sap/suite/ui/commons/CloudFilePicker.js +1 -1
  13. package/src/sap/suite/ui/commons/ControlProxy.js +7 -4
  14. package/src/sap/suite/ui/commons/MicroProcessFlow.js +1 -1
  15. package/src/sap/suite/ui/commons/MicroProcessFlowItem.js +1 -1
  16. package/src/sap/suite/ui/commons/ProcessFlowRenderer.js +3 -0
  17. package/src/sap/suite/ui/commons/collaboration/ContactHelper.js +5 -1
  18. package/src/sap/suite/ui/commons/collaboration/ContactPopover.fragment.xml +10 -10
  19. package/src/sap/suite/ui/commons/collaboration/MinimalContactPopover.fragment.xml +12 -10
  20. package/src/sap/suite/ui/commons/collaboration/ServiceContainer.js +12 -2
  21. package/src/sap/suite/ui/commons/flexibility/changeHandler/PropertyChangeMapper.js +1 -1
  22. package/src/sap/suite/ui/commons/imageeditor/CropCustomShapeHistoryItem.js +1 -1
  23. package/src/sap/suite/ui/commons/imageeditor/CropEllipseHistoryItem.js +1 -1
  24. package/src/sap/suite/ui/commons/imageeditor/CropRectangleHistoryItem.js +1 -1
  25. package/src/sap/suite/ui/commons/imageeditor/CustomSizeItem.js +1 -1
  26. package/src/sap/suite/ui/commons/imageeditor/FilterHistoryItem.js +1 -1
  27. package/src/sap/suite/ui/commons/imageeditor/FlipHistoryItem.js +1 -1
  28. package/src/sap/suite/ui/commons/imageeditor/HistoryItem.js +1 -1
  29. package/src/sap/suite/ui/commons/imageeditor/ImageEditor.js +1 -1
  30. package/src/sap/suite/ui/commons/imageeditor/ImageEditorContainer.js +1 -1
  31. package/src/sap/suite/ui/commons/imageeditor/ImageEditorResponsiveContainer.js +9 -6
  32. package/src/sap/suite/ui/commons/imageeditor/ResizeHistoryItem.js +1 -1
  33. package/src/sap/suite/ui/commons/imageeditor/RotateHistoryItem.js +1 -1
  34. package/src/sap/suite/ui/commons/library.js +4 -4
  35. package/src/sap/suite/ui/commons/messagebundle.properties +16 -8
  36. package/src/sap/suite/ui/commons/messagebundle_ar.properties +11 -5
  37. package/src/sap/suite/ui/commons/messagebundle_bg.properties +11 -5
  38. package/src/sap/suite/ui/commons/messagebundle_ca.properties +10 -4
  39. package/src/sap/suite/ui/commons/messagebundle_cnr.properties +11 -5
  40. package/src/sap/suite/ui/commons/messagebundle_cs.properties +7 -1
  41. package/src/sap/suite/ui/commons/messagebundle_cy.properties +11 -5
  42. package/src/sap/suite/ui/commons/messagebundle_da.properties +10 -4
  43. package/src/sap/suite/ui/commons/messagebundle_de.properties +8 -2
  44. package/src/sap/suite/ui/commons/messagebundle_el.properties +11 -5
  45. package/src/sap/suite/ui/commons/messagebundle_en.properties +11 -5
  46. package/src/sap/suite/ui/commons/messagebundle_en_GB.properties +11 -5
  47. package/src/sap/suite/ui/commons/messagebundle_en_US_saprigi.properties +9 -5
  48. package/src/sap/suite/ui/commons/messagebundle_es.properties +11 -5
  49. package/src/sap/suite/ui/commons/messagebundle_es_MX.properties +10 -4
  50. package/src/sap/suite/ui/commons/messagebundle_et.properties +6 -0
  51. package/src/sap/suite/ui/commons/messagebundle_fi.properties +10 -4
  52. package/src/sap/suite/ui/commons/messagebundle_fr.properties +11 -5
  53. package/src/sap/suite/ui/commons/messagebundle_fr_CA.properties +11 -5
  54. package/src/sap/suite/ui/commons/messagebundle_hi.properties +11 -5
  55. package/src/sap/suite/ui/commons/messagebundle_hr.properties +10 -4
  56. package/src/sap/suite/ui/commons/messagebundle_hu.properties +9 -3
  57. package/src/sap/suite/ui/commons/messagebundle_id.properties +12 -6
  58. package/src/sap/suite/ui/commons/messagebundle_it.properties +11 -5
  59. package/src/sap/suite/ui/commons/messagebundle_iw.properties +11 -5
  60. package/src/sap/suite/ui/commons/messagebundle_ja.properties +9 -3
  61. package/src/sap/suite/ui/commons/messagebundle_kk.properties +10 -4
  62. package/src/sap/suite/ui/commons/messagebundle_ko.properties +10 -4
  63. package/src/sap/suite/ui/commons/messagebundle_lt.properties +11 -5
  64. package/src/sap/suite/ui/commons/messagebundle_lv.properties +11 -5
  65. package/src/sap/suite/ui/commons/messagebundle_mk.properties +9 -3
  66. package/src/sap/suite/ui/commons/messagebundle_ms.properties +11 -5
  67. package/src/sap/suite/ui/commons/messagebundle_nl.properties +8 -2
  68. package/src/sap/suite/ui/commons/messagebundle_no.properties +10 -4
  69. package/src/sap/suite/ui/commons/messagebundle_pl.properties +11 -5
  70. package/src/sap/suite/ui/commons/messagebundle_pt.properties +11 -5
  71. package/src/sap/suite/ui/commons/messagebundle_pt_PT.properties +11 -5
  72. package/src/sap/suite/ui/commons/messagebundle_ro.properties +11 -5
  73. package/src/sap/suite/ui/commons/messagebundle_ru.properties +12 -6
  74. package/src/sap/suite/ui/commons/messagebundle_sh.properties +11 -5
  75. package/src/sap/suite/ui/commons/messagebundle_sk.properties +10 -4
  76. package/src/sap/suite/ui/commons/messagebundle_sl.properties +10 -4
  77. package/src/sap/suite/ui/commons/messagebundle_sr.properties +11 -5
  78. package/src/sap/suite/ui/commons/messagebundle_sv.properties +10 -4
  79. package/src/sap/suite/ui/commons/messagebundle_th.properties +11 -5
  80. package/src/sap/suite/ui/commons/messagebundle_tr.properties +8 -2
  81. package/src/sap/suite/ui/commons/messagebundle_uk.properties +14 -8
  82. package/src/sap/suite/ui/commons/messagebundle_vi.properties +11 -5
  83. package/src/sap/suite/ui/commons/messagebundle_zh_CN.properties +11 -5
  84. package/src/sap/suite/ui/commons/messagebundle_zh_TW.properties +11 -5
  85. package/src/sap/suite/ui/commons/networkgraph/Graph.js +149 -40
  86. package/src/sap/suite/ui/commons/networkgraph/GraphMap.js +25 -3
  87. package/src/sap/suite/ui/commons/networkgraph/KeyboardNavigator.js +332 -13
  88. package/src/sap/suite/ui/commons/networkgraph/Line.js +110 -12
  89. package/src/sap/suite/ui/commons/networkgraph/Node.js +67 -5
  90. package/src/sap/suite/ui/commons/networkgraph/Utils.js +249 -10
  91. package/src/sap/suite/ui/commons/networkgraph/layout/NoopLayout.js +55 -8
  92. package/src/sap/suite/ui/commons/networkgraph/util/ConnectionPathUtils.js +34 -4
  93. package/src/sap/suite/ui/commons/networkgraph/util/DependencyLayoutHelper.js +230 -82
  94. package/src/sap/suite/ui/commons/networkgraph/util/DragDropManager.js +52 -50
  95. package/src/sap/suite/ui/commons/networkgraph/util/PortManager.js +12 -3
  96. package/src/sap/suite/ui/commons/statusindicator/Circle.js +1 -1
  97. package/src/sap/suite/ui/commons/statusindicator/CustomShape.js +1 -1
  98. package/src/sap/suite/ui/commons/statusindicator/DiscreteThreshold.js +1 -1
  99. package/src/sap/suite/ui/commons/statusindicator/FillingOption.js +1 -1
  100. package/src/sap/suite/ui/commons/statusindicator/LibraryShape.js +1 -1
  101. package/src/sap/suite/ui/commons/statusindicator/Path.js +1 -1
  102. package/src/sap/suite/ui/commons/statusindicator/PropertyThreshold.js +1 -1
  103. package/src/sap/suite/ui/commons/statusindicator/Rectangle.js +1 -1
  104. package/src/sap/suite/ui/commons/statusindicator/Shape.js +1 -1
  105. package/src/sap/suite/ui/commons/statusindicator/ShapeGroup.js +1 -1
  106. package/src/sap/suite/ui/commons/statusindicator/SimpleShape.js +1 -1
  107. package/src/sap/suite/ui/commons/statusindicator/StatusIndicator.js +1 -1
  108. package/src/sap/suite/ui/commons/taccount/TAccount.js +1 -1
  109. package/src/sap/suite/ui/commons/taccount/TAccountGroup.js +1 -1
  110. package/src/sap/suite/ui/commons/taccount/TAccountItem.js +1 -1
  111. package/src/sap/suite/ui/commons/taccount/TAccountItemProperty.js +1 -1
  112. package/src/sap/suite/ui/commons/taccount/TAccountPanel.js +1 -1
  113. package/src/sap/suite/ui/commons/themes/base/BusinessCard.less +5 -4
  114. package/src/sap/suite/ui/commons/themes/base/CalculationBuilder.less +33 -24
  115. package/src/sap/suite/ui/commons/themes/base/CalculationBuilderExpression.less +40 -31
  116. package/src/sap/suite/ui/commons/themes/base/CalculationBuilderInput.less +26 -10
  117. package/src/sap/suite/ui/commons/themes/base/CalculationBuilderItem.less +76 -63
  118. package/src/sap/suite/ui/commons/themes/base/ChartContainer.less +8 -11
  119. package/src/sap/suite/ui/commons/themes/base/ChartTile.less +5 -8
  120. package/src/sap/suite/ui/commons/themes/base/CollaborationPopover.less +116 -106
  121. package/src/sap/suite/ui/commons/themes/base/DateRangeScroller.less +5 -5
  122. package/src/sap/suite/ui/commons/themes/base/DateRangeSliderInternal.less +9 -10
  123. package/src/sap/suite/ui/commons/themes/base/FacetOverview.less +9 -10
  124. package/src/sap/suite/ui/commons/themes/base/FeedItemHeader.less +9 -12
  125. package/src/sap/suite/ui/commons/themes/base/FeedTile.less +23 -27
  126. package/src/sap/suite/ui/commons/themes/base/GenericTile2X2.less +15 -15
  127. package/src/sap/suite/ui/commons/themes/base/HeaderCell.less +7 -6
  128. package/src/sap/suite/ui/commons/themes/base/HeaderContainer.less +35 -37
  129. package/src/sap/suite/ui/commons/themes/base/ImageEditor.less +28 -10
  130. package/src/sap/suite/ui/commons/themes/base/ImageEditorContainer.less +29 -22
  131. package/src/sap/suite/ui/commons/themes/base/InfoTile.less +10 -4
  132. package/src/sap/suite/ui/commons/themes/base/InfoTileSize.less +5 -4
  133. package/src/sap/suite/ui/commons/themes/base/KpiTile.less +10 -11
  134. package/src/sap/suite/ui/commons/themes/base/LaunchTile.less +9 -8
  135. package/src/sap/suite/ui/commons/themes/base/LinkActionSheet.less +14 -43
  136. package/src/sap/suite/ui/commons/themes/base/MicroProcessFlow.less +51 -47
  137. package/src/sap/suite/ui/commons/themes/base/MonitoringContent.less +5 -5
  138. package/src/sap/suite/ui/commons/themes/base/NetworkGraph.less +57 -62
  139. package/src/sap/suite/ui/commons/themes/base/NetworkGroup.less +61 -65
  140. package/src/sap/suite/ui/commons/themes/base/NetworkLine.less +58 -55
  141. package/src/sap/suite/ui/commons/themes/base/NetworkNode.less +3 -0
  142. package/src/sap/suite/ui/commons/themes/base/NetworkTooltip.less +21 -25
  143. package/src/sap/suite/ui/commons/themes/base/NoteTaker.less +13 -18
  144. package/src/sap/suite/ui/commons/themes/base/NoteTakerCard.less +26 -27
  145. package/src/sap/suite/ui/commons/themes/base/NoteTakerFeeder.less +22 -44
  146. package/src/sap/suite/ui/commons/themes/base/NumericTile.less +7 -5
  147. package/src/sap/suite/ui/commons/themes/base/PictureZoomIn.less +6 -5
  148. package/src/sap/suite/ui/commons/themes/base/ProcessFlow.less +57 -76
  149. package/src/sap/suite/ui/commons/themes/base/ProcessFlowConnection.less +71 -17
  150. package/src/sap/suite/ui/commons/themes/base/ProcessFlowConnectionLabel.less +9 -13
  151. package/src/sap/suite/ui/commons/themes/base/ProcessFlowLaneHeader.less +37 -96
  152. package/src/sap/suite/ui/commons/themes/base/ProcessFlowNode.less +219 -238
  153. package/src/sap/suite/ui/commons/themes/base/SemanticColorMixins.less +55 -0
  154. package/src/sap/suite/ui/commons/themes/base/SplitButton.less +7 -20
  155. package/src/sap/suite/ui/commons/themes/base/StatusIndicator.less +10 -9
  156. package/src/sap/suite/ui/commons/themes/base/TAccount.less +78 -73
  157. package/src/sap/suite/ui/commons/themes/base/TargetFilter.less +50 -58
  158. package/src/sap/suite/ui/commons/themes/base/ThingCollection.less +18 -13
  159. package/src/sap/suite/ui/commons/themes/base/ThreePanelThingViewer.less +14 -14
  160. package/src/sap/suite/ui/commons/themes/base/TileContent2X2.less +9 -13
  161. package/src/sap/suite/ui/commons/themes/base/Timeline.less +16 -19
  162. package/src/sap/suite/ui/commons/themes/base/TimelineItem.less +95 -103
  163. package/src/sap/suite/ui/commons/themes/base/UnifiedThingGroup.less +7 -9
  164. package/src/sap/suite/ui/commons/themes/base/UnifiedThingInspector.less +12 -9
  165. package/src/sap/suite/ui/commons/themes/base/VerticalNavigationBar.less +10 -15
  166. package/src/sap/suite/ui/commons/themes/base/ViewRepeater.less +8 -9
  167. package/src/sap/suite/ui/commons/themes/base/library.source.less +0 -2
  168. package/src/sap/suite/ui/commons/themes/sap_fiori_3/MicroProcessFlow.less +9 -3
  169. package/src/sap/suite/ui/commons/themes/sap_fiori_3/ProcessFlowConnection.less +14 -7
  170. package/src/sap/suite/ui/commons/themes/sap_fiori_3/ProcessFlowConnectionLabel.less +38 -35
  171. package/src/sap/suite/ui/commons/themes/sap_fiori_3/ProcessFlowLaneHeader.less +29 -30
  172. package/src/sap/suite/ui/commons/themes/sap_fiori_3/ProcessFlowNode.less +158 -102
  173. package/src/sap/suite/ui/commons/themes/sap_fiori_3_dark/MicroProcessFlow.less +9 -3
  174. package/src/sap/suite/ui/commons/themes/sap_fiori_3_dark/ProcessFlowConnection.less +14 -7
  175. package/src/sap/suite/ui/commons/themes/sap_fiori_3_dark/ProcessFlowConnectionLabel.less +38 -35
  176. package/src/sap/suite/ui/commons/themes/sap_fiori_3_dark/ProcessFlowLaneHeader.less +30 -31
  177. package/src/sap/suite/ui/commons/themes/sap_fiori_3_dark/ProcessFlowNode.less +157 -101
  178. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcb/ProcessFlowConnection.less +14 -7
  179. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcb/ProcessFlowConnectionLabel.less +38 -35
  180. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcb/ProcessFlowLaneHeader.less +30 -31
  181. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcb/ProcessFlowNode.less +147 -101
  182. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcw/ProcessFlowConnection.less +14 -7
  183. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcw/ProcessFlowConnectionLabel.less +38 -35
  184. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcw/ProcessFlowLaneHeader.less +30 -31
  185. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcw/ProcessFlowNode.less +158 -102
  186. package/src/sap/suite/ui/commons/themes/sap_horizon/MicroProcessFlow.less +5 -0
  187. package/src/sap/suite/ui/commons/themes/sap_horizon/NetworkLine.less +12 -8
  188. package/src/sap/suite/ui/commons/themes/sap_horizon/ProcessFlowConnection.less +17 -10
  189. package/src/sap/suite/ui/commons/themes/sap_horizon/ProcessFlowConnectionLabel.less +154 -133
  190. package/src/sap/suite/ui/commons/themes/sap_horizon/ProcessFlowLaneHeader.less +30 -32
  191. package/src/sap/suite/ui/commons/themes/sap_horizon/ProcessFlowNode.less +113 -214
  192. package/src/sap/suite/ui/commons/themes/sap_horizon/TimelineItem.less +43 -39
  193. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/MicroProcessFlow.less +5 -0
  194. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/NetworkLine.less +12 -12
  195. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/ProcessFlowConnection.less +17 -10
  196. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/ProcessFlowConnectionLabel.less +154 -133
  197. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/ProcessFlowLaneHeader.less +30 -31
  198. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/ProcessFlowNode.less +114 -216
  199. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/TimelineItem.less +41 -39
  200. package/src/sap/suite/ui/commons/themes/sap_horizon_hcb/MicroProcessFlow.less +4 -4
  201. package/src/sap/suite/ui/commons/themes/sap_horizon_hcb/ProcessFlowConnection.less +17 -10
  202. package/src/sap/suite/ui/commons/themes/sap_horizon_hcb/ProcessFlowConnectionLabel.less +153 -131
  203. package/src/sap/suite/ui/commons/themes/sap_horizon_hcb/ProcessFlowLaneHeader.less +29 -30
  204. package/src/sap/suite/ui/commons/themes/sap_horizon_hcb/ProcessFlowNode.less +159 -103
  205. package/src/sap/suite/ui/commons/themes/sap_horizon_hcb/TimelineItem.less +41 -39
  206. package/src/sap/suite/ui/commons/themes/sap_horizon_hcw/MicroProcessFlow.less +4 -4
  207. package/src/sap/suite/ui/commons/themes/sap_horizon_hcw/ProcessFlowConnection.less +16 -9
  208. package/src/sap/suite/ui/commons/themes/sap_horizon_hcw/ProcessFlowConnectionLabel.less +153 -131
  209. package/src/sap/suite/ui/commons/themes/sap_horizon_hcw/ProcessFlowLaneHeader.less +29 -30
  210. package/src/sap/suite/ui/commons/themes/sap_horizon_hcw/ProcessFlowNode.less +157 -101
  211. package/src/sap/suite/ui/commons/themes/sap_horizon_hcw/TimelineItem.less +41 -39
  212. package/src/sap/suite/ui/commons/windowmessages/CollaborationMessageConsumer.js +3 -3
  213. package/src/sap/suite/ui/commons/themes/base/DateRangeSlider.less +0 -3
  214. package/src/sap/suite/ui/commons/themes/base/MonitoringTile.less +0 -3
@@ -7,16 +7,36 @@
7
7
  sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (library, Log) {
8
8
  "use strict";
9
9
 
10
- const ConnectionType = library.networkgraph.ConnectionType,
11
- ComponentArrangement = library.networkgraph.ComponentArrangement;
10
+ const oConnectionType = library.networkgraph.ConnectionType,
11
+ oComponentArrangement = library.networkgraph.ComponentArrangement;
12
12
  const DEFAULT_HORIZONTAL_SPACING = 240;
13
13
  const DEFAULT_VERTICAL_SPACING = 100;
14
+ const DRAG_DETECTION_THRESHOLD = 10;
14
15
 
15
16
  /**
16
17
  * Utility class for calculating node positions based on dependency relationships.
17
18
  * This utility arranges nodes based on their dependency relationships using topological sorting,
18
19
  * cycle detection, and layer-based positioning.
19
20
  *
21
+ * Why a node may be positioned incorrectly after recalculation:
22
+ * After each run, node positions are cached with a flag indicating whether the user moved
23
+ * them (<code>bIsUserPositioned</code>). On the next call, nodes flagged as user-positioned
24
+ * are preserved as-is, and the algorithm-placed nodes are recalculated. A node appears in the
25
+ * wrong place if it is unexpectedly treated as user-positioned, most commonly because
26
+ * <code>clearCache: true</code> was passed. It wipes the cache, but leaves the coordinates intact.
27
+ * So every node with non-zero coordinates is immediately re-classified as user-positioned
28
+ * and freezes, rather than being recalculated.
29
+ *
30
+ * Drag and drop recalculation - correct pattern:
31
+ * Call <code>resetNode(oNode)</code> for each dragged node, then call
32
+ * <code>calculatePositions</code> without <code>clearCache</code> (default <code>false</code>).
33
+ * <code>resetNode</code> evicts the node from the cache and zeros its coordinates, so only
34
+ * that node is repositioned; all the other nodes remain stable.
35
+ *
36
+ * Sentinel value:
37
+ * Coordinates <code>(0, 0)</code> mean "unpositioned". To place a node at the canvas origin,
38
+ * use <code>(0.1, 0.1)</code> or <code>(1, 1)</code> instead.
39
+ *
20
40
  * @namespace sap.suite.ui.commons.networkgraph.util.DependencyLayoutHelper
21
41
  * @public
22
42
  * @since 1.144
@@ -28,16 +48,41 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
28
48
  */
29
49
  _lastCalculated: new WeakMap(),
30
50
 
51
+ /**
52
+ * Evicts one or more nodes from the position cache and zeros their coordinates so the next
53
+ * <code>calculatePositions</code> call repositions them while all other nodes remain stable.
54
+ * To understand the use for drag and drop recalculation, see the class-level JSDoc for the full pattern.
55
+ *
56
+ * @param {sap.suite.ui.commons.networkgraph.Node|sap.suite.ui.commons.networkgraph.Node[]} vNode - A single node or an array of nodes to reset
57
+ * @public
58
+ */
59
+ resetNode: function (vNode) {
60
+ const aNodes = Array.isArray(vNode) ? vNode : [vNode];
61
+ aNodes.forEach((oNode) => {
62
+ if (!oNode || !oNode.setX || !oNode.setY) {
63
+ return;
64
+ }
65
+ this._lastCalculated.delete(oNode);
66
+ oNode.setX(0);
67
+ oNode.setY(0);
68
+ });
69
+ },
70
+
31
71
  /**
32
72
  * Calculates and sets positions for nodes without coordinates based on their dependency relationships.
33
73
  * Uses topological sorting (Kahn's algorithm) and DFS-based cycle detection to arrange nodes
34
74
  * in layers from left to right, ensuring no overlaps and minimal line crossings.
75
+ * Note: Drag and drop functionality is only available for nodes. Groups cannot be dragged and dropped, even when NoopLayout is used and enableDragAndDrop is set to true.
35
76
  *
36
- * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The network graph instance
77
+ * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The graph instance
37
78
  * @param {object} [mConfig] - Configuration object
38
79
  * @param {number} [mConfig.horizontalSpacing=240] - Horizontal spacing between layers
39
80
  * @param {number} [mConfig.verticalSpacing=100] - Vertical spacing between nodes
40
81
  * @param {sap.suite.ui.commons.networkgraph.ComponentArrangement} [mConfig.componentArrangement=Horizontal] - Component arrangement: "Horizontal" or "Vertical"
82
+ * @param {boolean} [mConfig.clearCache=false] - Wipes the position cache; node coordinates are not modified.
83
+ * <code>_detectUserChanges</code> then re-classifies every non-zero node as user-positioned and freezes it.
84
+ * Use only to discard all layout history (e.g. switching <code>componentArrangement</code>).
85
+ * For drag and drop recalculation, use <code>resetNode()</code> with the default <code>clearCache: false</code>.
41
86
  * @public
42
87
  */
43
88
  calculatePositions: function (oGraph, mConfig) {
@@ -49,19 +94,29 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
49
94
  {
50
95
  horizontalSpacing: DEFAULT_HORIZONTAL_SPACING,
51
96
  verticalSpacing: DEFAULT_VERTICAL_SPACING,
52
- componentArrangement: ComponentArrangement.Horizontal,
97
+ componentArrangement: oComponentArrangement.Horizontal,
98
+ clearCache: false
53
99
  },
54
100
  mConfig || {}
55
101
  );
102
+
103
+ // Clear cache if requested (e.g., when changing arrangement)
104
+ if (config.clearCache) {
105
+ this._lastCalculated = new WeakMap();
106
+ }
107
+
56
108
  try {
57
- this._detectUserChanges(oGraph);
109
+ this._detectUserChanges(oGraph, config.horizontalSpacing);
58
110
  this._buildDependencyGraph(oGraph);
59
111
  const aComponents = this._identifyComponents(oGraph);
60
112
  this._processComponents(aComponents, config);
61
113
  this._calculateCoordinates(oGraph, config);
62
114
  this._adjustHorizontalSpacingForNodeWidths(oGraph, config);
63
115
  this._adjustVerticalSpacingForNodeHeights(oGraph, config);
64
- this._storeFinalPositions(oGraph);
116
+ if (oGraph._bIsRtl) {
117
+ this._horizontalMirror(oGraph, config.horizontalSpacing);
118
+ }
119
+ this._storeFinalPositions(oGraph, config.horizontalSpacing);
65
120
  } catch (e) {
66
121
  Log.error("DependencyLayoutHelper error: " + e.message, e);
67
122
  throw new Error("DependencyLayoutHelper error: " + e.message);
@@ -87,11 +142,6 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
87
142
 
88
143
  aNodes.forEach((oNode) => {
89
144
  mNodeMap.set(oNode.getKey(), oNode);
90
- const fX = oNode.getX();
91
- const fY = oNode.getY();
92
- const oLastPos = this._lastCalculated.get(oNode);
93
- // Only preserve coordinates if they were original or user-dragged
94
- const bShouldPreserve = oLastPos?.bIsUserPositioned;
95
145
 
96
146
  oNode._layoutData = {
97
147
  layer: -1,
@@ -101,10 +151,7 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
101
151
  parallelNodes: [],
102
152
  backwardEdges: [],
103
153
  backwardEdgeLines: [],
104
- isBackwardSource: false,
105
- bShouldPreserve,
106
- userX: fX,
107
- userY: fY
154
+ isBackwardSource: false
108
155
  };
109
156
  });
110
157
 
@@ -129,13 +176,13 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
129
176
  this._lineMap.get(sFromKey).set(sToKey, oLine);
130
177
 
131
178
  switch (sConnectionType) {
132
- case ConnectionType.RightToRight:
133
- case ConnectionType.LeftToLeft:
179
+ case oConnectionType.RightToRight:
180
+ case oConnectionType.LeftToLeft:
134
181
  oFromNode._layoutData.parallelNodes.push(oToNode);
135
182
  oToNode._layoutData.parallelNodes.push(oFromNode);
136
183
  break;
137
- case ConnectionType.RightToLeft:
138
- case ConnectionType.LeftToRight:
184
+ case oConnectionType.RightToLeft:
185
+ case oConnectionType.LeftToRight:
139
186
  default:
140
187
  oFromNode._layoutData.outgoing.push(oToNode);
141
188
  oToNode._layoutData.incoming.push(oFromNode);
@@ -270,8 +317,7 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
270
317
  const bIsIsolated =
271
318
  aComponentNodes.length === 1 &&
272
319
  aComponentNodes[0]._layoutData.incoming.length === 0 &&
273
- aComponentNodes[0]._layoutData.outgoing.length === 0 &&
274
- !aComponentNodes[0]._layoutData.bShouldPreserve;
320
+ aComponentNodes[0]._layoutData.outgoing.length === 0;
275
321
 
276
322
  if (bIsIsolated) {
277
323
  aIsolatedNodes.push(aComponentNodes[0]);
@@ -279,7 +325,7 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
279
325
  aConnectedComponents.push(aComponentNodes);
280
326
  }
281
327
  });
282
- const bSideBySide = componentArrangement !== ComponentArrangement.Vertical;
328
+ const bSideBySide = componentArrangement !== oComponentArrangement.Vertical;
283
329
  let iMaxVerticalPosition = 0;
284
330
  let iMaxLayerAcrossAll = 0;
285
331
 
@@ -326,18 +372,17 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
326
372
  });
327
373
 
328
374
  if (aIsolatedNodes.length > 0) {
329
- const iRightSideOffset = iMaxLayerAcrossAll + 1;
330
-
331
- aIsolatedNodes.forEach((oNode, idx) => {
332
- if (bSideBySide) {
333
- oNode._layoutData.layer = Math.floor(idx / 2);
334
- oNode._layoutData.position = idx % 2;
335
- oNode._layoutData.componentOffset = iMaxLayerAcrossAll;
336
- } else {
337
- oNode._layoutData.layer = idx % 2;
338
- oNode._layoutData.position = Math.floor(idx / 2);
339
- oNode._layoutData.componentOffset = iRightSideOffset;
375
+ const iRightSideOffset = bSideBySide ? iMaxLayerAcrossAll : iMaxLayerAcrossAll + 1;
376
+ let iIsolatedIdx = 0;
377
+ aIsolatedNodes.forEach((oNode) => {
378
+ if (this._lastCalculated.get(oNode)?.bIsUserPositioned) {
379
+ return;
340
380
  }
381
+ const idx = iIsolatedIdx++;
382
+
383
+ oNode._layoutData.layer = Math.floor(idx / 2);
384
+ oNode._layoutData.position = idx % 2;
385
+ oNode._layoutData.componentOffset = iRightSideOffset;
341
386
  });
342
387
  }
343
388
  },
@@ -702,10 +747,10 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
702
747
  });
703
748
 
704
749
  aNodes.forEach((oNode) => {
705
- if (oNode._layoutData.bShouldPreserve) {
706
- const { userX, userY } = oNode._layoutData;
707
- oNode.setX(userX);
708
- oNode.setY(userY);
750
+ const oLastPos = this._lastCalculated.get(oNode);
751
+ if (oLastPos?.bIsUserPositioned) {
752
+ oNode.setX(oLastPos.x);
753
+ oNode.setY(oLastPos.y);
709
754
  }
710
755
  });
711
756
  },
@@ -722,16 +767,16 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
722
767
  let iMaxLayer = -1;
723
768
 
724
769
  aNodes.forEach((oNode) => {
725
- if (!oNode._layoutData.bShouldPreserve) {
726
- const iLayer =
727
- oNode._layoutData.layer + (oNode._layoutData.componentOffset || 0);
728
- iMaxLayer = Math.max(iMaxLayer, iLayer);
729
-
730
- if (oNode._iWidth) {
731
- const iWidth = oNode._iWidth;
732
- if (!mLayerWidths[iLayer] || mLayerWidths[iLayer] < iWidth) {
733
- mLayerWidths[iLayer] = iWidth;
734
- }
770
+ const iLayer =
771
+ oNode._layoutData.layer + (oNode._layoutData.componentOffset || 0);
772
+ // Skip nodes with unassigned layout
773
+ if (iLayer < 0) return;
774
+ iMaxLayer = Math.max(iMaxLayer, iLayer);
775
+
776
+ if (oNode._iWidth) {
777
+ const iWidth = oNode._iWidth;
778
+ if (!mLayerWidths[iLayer] || mLayerWidths[iLayer] < iWidth) {
779
+ mLayerWidths[iLayer] = iWidth;
735
780
  }
736
781
  }
737
782
  });
@@ -748,7 +793,7 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
748
793
  }
749
794
 
750
795
  aNodes.forEach((oNode) => {
751
- if (!oNode._layoutData.bShouldPreserve) {
796
+ if (!this._lastCalculated.get(oNode)?.bIsUserPositioned) {
752
797
  const iLayer =
753
798
  oNode._layoutData.layer + (oNode._layoutData.componentOffset || 0);
754
799
  const iOffset = mLayerOffsets[iLayer] || 0;
@@ -769,18 +814,18 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
769
814
  const mAllPositions = {};
770
815
 
771
816
  aNodes.forEach((oNode) => {
772
- if (!oNode._layoutData.bShouldPreserve) {
773
- const iPosition = oNode._layoutData.position;
774
- mAllPositions[iPosition] = true;
817
+ const iPosition = oNode._layoutData.position;
818
+ // Skip nodes with unassigned layout
819
+ if (iPosition < 0) return;
820
+ mAllPositions[iPosition] = true;
775
821
 
776
- if (oNode._iHeight) {
777
- const iHeight = oNode._iHeight;
778
- if (
779
- !mPositionHeights[iPosition] ||
780
- mPositionHeights[iPosition] < iHeight
781
- ) {
782
- mPositionHeights[iPosition] = iHeight;
783
- }
822
+ if (oNode._iHeight) {
823
+ const iHeight = oNode._iHeight;
824
+ if (
825
+ !mPositionHeights[iPosition] ||
826
+ mPositionHeights[iPosition] < iHeight
827
+ ) {
828
+ mPositionHeights[iPosition] = iHeight;
784
829
  }
785
830
  }
786
831
  });
@@ -800,7 +845,7 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
800
845
  });
801
846
 
802
847
  aNodes.forEach((oNode) => {
803
- if (!oNode._layoutData.bShouldPreserve) {
848
+ if (!this._lastCalculated.get(oNode)?.bIsUserPositioned) {
804
849
  const iPosition = oNode._layoutData.position;
805
850
  const iOffset = mPositionOffsets[iPosition] || 0;
806
851
  oNode.setY(oNode.getY() + iOffset);
@@ -813,19 +858,38 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
813
858
  * Only stores positions for nodes that were calculated (not original/dragged).
814
859
  *
815
860
  * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The network graph instance
861
+ * @param {number} iHSpacing - Horizontal spacing used as RTL mirror offset
816
862
  * @private
817
863
  */
818
- _storeFinalPositions: function (oGraph) {
864
+ _storeFinalPositions: function (oGraph, iHSpacing) {
819
865
  const aNodes = oGraph.getNodes();
866
+ const bIsRtl = oGraph._bIsRtl || false;
867
+
868
+ // Reuse fMaxX from _horizontalMirror to ensure consistent mirror/unmirror
869
+ let fMaxX = 0;
870
+ if (bIsRtl) {
871
+ fMaxX = oGraph._rtlMirrorMaxX || 0;
872
+ }
820
873
 
821
874
  aNodes.forEach((oNode) => {
822
- if (!oNode._layoutData.bShouldPreserve) {
823
- this._lastCalculated.set(oNode, {
824
- x: oNode.getX(),
825
- y: oNode.getY(),
826
- bIsUserPositioned: false
827
- });
875
+ const fCurrentX = oNode.getX();
876
+ const fCurrentY = oNode.getY();
877
+
878
+ // Always store in LTR space
879
+ let fLtrX = fCurrentX;
880
+ if (bIsRtl) {
881
+ const fNodeWidth = oNode._iWidth || 0;
882
+ fLtrX = fMaxX - fCurrentX - fNodeWidth + iHSpacing;
828
883
  }
884
+
885
+ const bIsUserPositioned = this._lastCalculated.get(oNode)?.bIsUserPositioned ?? false;
886
+
887
+ this._lastCalculated.set(oNode, {
888
+ x: fLtrX,
889
+ y: fCurrentY,
890
+ lastRtl: bIsRtl,
891
+ bIsUserPositioned
892
+ });
829
893
  });
830
894
  },
831
895
 
@@ -834,36 +898,120 @@ sap.ui.define(["sap/suite/ui/commons/library", "sap/base/Log"], function (librar
834
898
  * Marks nodes with original coordinates and tracks drag operations.
835
899
  *
836
900
  * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The network graph instance
901
+ * @param {number} iHSpacing - Horizontal spacing used as RTL mirror offset
837
902
  * @private
838
903
  */
839
- _detectUserChanges: function (oGraph) {
904
+ _detectUserChanges: function (oGraph, iHSpacing) {
840
905
  const aNodes = oGraph.getNodes();
906
+ const bIsRtl = oGraph._bIsRtl || false;
907
+
908
+ // Use stored fMaxX from previous mirror operation if available, otherwise calculate.
909
+ // We need it both when in RTL mode AND when switching RTL→LTR (to detect pre-switch drags).
910
+ let fMaxX = oGraph._rtlMirrorMaxX || 0;
911
+ if (bIsRtl && !fMaxX) {
912
+ aNodes.forEach((n) => {
913
+ const fRight = n.getX() + (n._iWidth || 0);
914
+ if (fRight > fMaxX) fMaxX = fRight;
915
+ });
916
+ }
841
917
 
842
918
  aNodes.forEach((oNode) => {
843
- const fX = oNode.getX();
844
- const fY = oNode.getY();
845
- // Constraint: Nodes with coordinates (0,0) or negative values are treated as unpositioned.
846
- // For origin positioning, use (0.1, 0.1) or (1, 1).
847
- const bHasCoordinates = fX !== undefined && fY !== undefined && (fX !== 0 || fY !== 0);
919
+ const fCurrentX = oNode.getX();
920
+ const fCurrentY = oNode.getY();
921
+ // (0,0) is the sentinel for "unpositioned": resetNode() sets coordinates to (0,0)
922
+ // so the algorithm treats the node as if it were never placed.
923
+ // To legitimately position a node at the canvas origin, use (0.1, 0.1) or (1, 1).
924
+ const bHasCoordinates = fCurrentX !== undefined && fCurrentY !== undefined && (fCurrentX !== 0 || fCurrentY !== 0);
848
925
  const oLastPos = this._lastCalculated.get(oNode);
849
926
 
850
- if (!oLastPos && bHasCoordinates) {
851
- // First time seeing this node with coordinates - mark as original
927
+ // Convert current position to LTR coordinates for comparison
928
+ let fLtrX = fCurrentX;
929
+ if (bIsRtl) {
930
+ const fNodeWidth = oNode._iWidth || 0;
931
+ fLtrX = fMaxX - fCurrentX - fNodeWidth + iHSpacing;
932
+ }
933
+
934
+ if (oLastPos && !bHasCoordinates) {
935
+ // Coordinates were cleared (sentinel value 0,0) — evict cache so the algorithm
936
+ // repositions this node on the next layout pass.
937
+ this._lastCalculated.delete(oNode);
938
+ } else if (!oLastPos && bHasCoordinates) {
939
+ // No cache entry + non-zero coords: node has model-provided position (first call).
940
+ // Treated as user-positioned → preserved. Use resetNode() to opt a node out.
852
941
  this._lastCalculated.set(oNode, {
853
- x: fX,
854
- y: fY,
942
+ x: fLtrX,
943
+ y: fCurrentY,
944
+ lastRtl: bIsRtl,
855
945
  bIsUserPositioned: true
856
946
  });
857
947
  } else if (oLastPos) {
858
- // Check if user dragged it
859
- if (fX !== oLastPos.x || fY !== oLastPos.y) {
860
- oLastPos.x = fX;
861
- oLastPos.y = fY;
862
- oLastPos.bIsUserPositioned = true;
948
+ const bModeChanged = (oLastPos.lastRtl !== bIsRtl);
949
+
950
+ if (bModeChanged) {
951
+ const bPrevRtl = oLastPos.lastRtl;
952
+ let fLtrXBeforeSwitch;
953
+
954
+ if (bPrevRtl) {
955
+ const fNodeWidth = oNode._iWidth || 0;
956
+ fLtrXBeforeSwitch = fMaxX - fCurrentX - fNodeWidth + iHSpacing;
957
+ } else {
958
+ fLtrXBeforeSwitch = fCurrentX;
959
+ }
960
+
961
+ const fDeltaX = Math.abs(fLtrXBeforeSwitch - oLastPos.x);
962
+ const fDeltaY = Math.abs(fCurrentY - oLastPos.y);
963
+
964
+ if (fDeltaX > DRAG_DETECTION_THRESHOLD || fDeltaY > DRAG_DETECTION_THRESHOLD) {
965
+ oLastPos.x = fLtrXBeforeSwitch;
966
+ oLastPos.y = fCurrentY;
967
+ oLastPos.bIsUserPositioned = true;
968
+ }
969
+
970
+ oLastPos.lastRtl = bIsRtl;
971
+ } else {
972
+ const fDeltaX = Math.abs(fLtrX - oLastPos.x);
973
+ const fDeltaY = Math.abs(fCurrentY - oLastPos.y);
974
+
975
+ if (fDeltaX > DRAG_DETECTION_THRESHOLD || fDeltaY > DRAG_DETECTION_THRESHOLD) {
976
+ oLastPos.x = fLtrX;
977
+ oLastPos.y = fCurrentY;
978
+ oLastPos.bIsUserPositioned = true;
979
+ }
863
980
  }
864
981
  }
865
982
  });
866
983
  },
984
+
985
+ /**
986
+ * Mirrors all node X positions horizontally for RTL layout.
987
+ *
988
+ * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The network graph instance
989
+ * @param {number} iHSpacing - Horizontal spacing used as left-side padding offset after mirroring
990
+ * @private
991
+ */
992
+ _horizontalMirror: function (oGraph, iHSpacing) {
993
+ const aNodes = oGraph.getNodes();
994
+
995
+ // Find the rightmost edge of all nodes
996
+ let fMaxX = 0;
997
+ aNodes.forEach((oNode) => {
998
+ const fX = oNode.getX();
999
+ const fWidth = oNode._iWidth || 0;
1000
+ const fRightEdge = fX + fWidth;
1001
+ if (fRightEdge > fMaxX) {
1002
+ fMaxX = fRightEdge;
1003
+ }
1004
+ });
1005
+
1006
+ oGraph._rtlMirrorMaxX = fMaxX;
1007
+
1008
+ aNodes.forEach((oNode) => {
1009
+ const fCurrentX = oNode.getX();
1010
+ const fWidth = oNode._iWidth || 0;
1011
+ const fMirroredX = fMaxX - fCurrentX - fWidth + iHSpacing;
1012
+ oNode.setX(fMirroredX);
1013
+ });
1014
+ }
867
1015
  };
868
1016
 
869
1017
  return DependencyLayoutHelper;
@@ -7,8 +7,9 @@
7
7
  sap.ui.define([
8
8
  "sap/ui/core/dnd/DragInfo",
9
9
  "sap/ui/core/dnd/DropInfo",
10
- "sap/base/Log"
11
- ], function (DragInfo, DropInfo, Log) {
10
+ "sap/base/Log",
11
+ "sap/suite/ui/commons/networkgraph/Utils"
12
+ ], function (DragInfo, DropInfo, Log, Utils) {
12
13
  "use strict";
13
14
 
14
15
  // constants
@@ -22,12 +23,32 @@ sap.ui.define([
22
23
  AUTO_SCROLL_SPEED: 10, // Pixels to scroll per frame (16ms)
23
24
  };
24
25
 
26
+ /**
27
+ * Minimum position offsets to ensure action buttons remain visible.
28
+ * LEFT: 60px accounts for line and port
29
+ * TOP: 40px accounts for top action buttons (-2.25rem ≈ 36px) plus margin
30
+ */
31
+ const POSITION_CLAMP_OFFSET = {
32
+ LEFT: 60,
33
+ TOP: 40
34
+ };
35
+
25
36
  // Instance object: Only one will exist
26
37
  var instance = null;
27
38
  var sDragSessionKey = "networkGraphDragSession";
28
39
  var oUtilityFunctions = {
29
40
  diff: function (a, b) {
30
41
  return Math.abs(a - b);
42
+ },
43
+ clampPosition: function (x, y) {
44
+ /**
45
+ * Clamps position coordinates to minimum thresholds.
46
+ * Prevents nodes from being positioned where action buttons would be off-screen.
47
+ */
48
+ return {
49
+ x: Math.max(POSITION_CLAMP_OFFSET.LEFT, x),
50
+ y: Math.max(POSITION_CLAMP_OFFSET.TOP, y)
51
+ };
31
52
  }
32
53
  };
33
54
 
@@ -276,45 +297,6 @@ sap.ui.define([
276
297
  oControl.isA("sap.suite.ui.commons.networkgraph.Node");
277
298
  }
278
299
 
279
- /**
280
- * Retains the scroll position of the graph after node drop.
281
- * This method ensures that the viewport remains stable after drag and drop operations.
282
- *
283
- * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The graph control
284
- * @param {number} scrollLeft - The horizontal scroll position to maintain
285
- * @param {number} scrollTop - The vertical scroll position to maintain
286
- * @private
287
- */
288
- DragDropManager.prototype._retainScrollPosition = function (oGraph, scrollLeft, scrollTop) {
289
- if (!oGraph || !oGraph.$scroller || !oGraph.$scroller[0]) {
290
- Log.warning("DragDropManager: Invalid graph or scroller element for scroll position retention.");
291
- return;
292
- }
293
-
294
- const scrollerElement = oGraph.$scroller[0];
295
-
296
- // Use requestAnimationFrame for better performance and timing
297
- this._iRequestAnimationFrameId = requestAnimationFrame(() => {
298
- try {
299
- scrollerElement.scrollLeft = scrollLeft;
300
- scrollerElement.scrollTop = scrollTop;
301
-
302
- // Fallback verification - ensure the scroll position was actually set
303
- if (Math.abs(scrollerElement.scrollLeft - scrollLeft) > 1 ||
304
- Math.abs(scrollerElement.scrollTop - scrollTop) > 1) {
305
-
306
- // If the scroll position wasn't set correctly, try again after DOM updates
307
- setTimeout(() => {
308
- scrollerElement.scrollLeft = scrollLeft;
309
- scrollerElement.scrollTop = scrollTop;
310
- }, 10);
311
- }
312
- } catch (error) {
313
- Log.error("DragDropManager: Failed to retain scroll position", error);
314
- }
315
- });
316
- }
317
-
318
300
  /**
319
301
  * Handles the drag start event for nodes.
320
302
  *
@@ -325,6 +307,7 @@ sap.ui.define([
325
307
  let dragSession = oEvent.getParameter("dragSession");
326
308
  let browserEvent = oEvent.getParameter("browserEvent");
327
309
  let oGraph = oEvent.getParameter("target")?.getParent();
310
+ let oNode = oEvent.getParameter("target");
328
311
  let boundingRectScroller = oGraph?.$scroller?.[0]?.getBoundingClientRect() || { x: 0, y: 0 };
329
312
  let boundingRectInnerScroller = oGraph?._$innerscroller?.[0]?.getBoundingClientRect() || { x: 0, y: 0 };
330
313
 
@@ -336,6 +319,9 @@ sap.ui.define([
336
319
  // Stop any existing auto-scroll from previous drag operations
337
320
  this._stopAutoScroll();
338
321
 
322
+ // Create custom drag image that includes the border
323
+ this._oDragImageNode = Utils.createCustomDragImage(oNode, browserEvent);
324
+
339
325
  dragSession.setComplexData(sDragSessionKey, {
340
326
  // Mouse cursor position when drag starts (in viewport coordinates)
341
327
  cursorX: browserEvent.clientX,
@@ -369,6 +355,12 @@ sap.ui.define([
369
355
 
370
356
  // Stop auto-scrolling when drag ends
371
357
  this._stopAutoScroll();
358
+
359
+ // Clean up the drag image node
360
+ if (this._oDragImageNode && this._oDragImageNode.parentNode) {
361
+ this._oDragImageNode.parentNode.removeChild(this._oDragImageNode);
362
+ this._oDragImageNode = null;
363
+ }
372
364
  };
373
365
 
374
366
  /**
@@ -543,6 +535,9 @@ sap.ui.define([
543
535
  let newX = dropData.draggedControl.getX() + deltaX + (deltaSignatureX * deltaInnerScrollerX),
544
536
  newY = dropData.draggedControl.getY() + deltaY + (deltaSignatureY * deltaInnerScrollerY);
545
537
 
538
+ // Clamp position to prevent nodes from going off-screen (especially for action buttons)
539
+ var oClampedPosition = oUtilityFunctions.clampPosition(newX, newY);
540
+
546
541
  // Store current scroll position before any DOM changes
547
542
  let currentScrollLeft = oGraph?.$scroller?.[0]?.scrollLeft || 0;
548
543
  let currentScrollTop = oGraph?.$scroller?.[0]?.scrollTop || 0;
@@ -550,14 +545,11 @@ sap.ui.define([
550
545
  oGraph.fireEvent("nodeDropped", {
551
546
  ...oEvent.getParameters(),
552
547
  node: dropData.draggedControl,
553
- newX: newX,
554
- newY: newY
548
+ newX: oClampedPosition.x,
549
+ newY: oClampedPosition.y
555
550
  });
556
- // Restore the scroll position using the dedicated method
557
- // TODO: udpate promise based
558
- setTimeout(() => {
559
- this._retainScrollPosition(oGraph, currentScrollLeft, currentScrollTop);
560
- }, 0);
551
+ // Preserve scroll position after event
552
+ Utils.preserveScrollPosition(oGraph, currentScrollLeft, currentScrollTop, 0);
561
553
  };
562
554
 
563
555
  /**
@@ -574,12 +566,22 @@ sap.ui.define([
574
566
  x = dropData.clientX - boundingRect.left,
575
567
  y = dropData.clientY - boundingRect.top;
576
568
 
569
+ // Clamp position to prevent nodes from going off-screen (especially for action buttons)
570
+ var oClampedPosition = oUtilityFunctions.clampPosition(x, y);
571
+
572
+ // Store current scroll position before any DOM changes
573
+ let currentScrollLeft = oGraph?.$scroller?.[0]?.scrollLeft || 0;
574
+ let currentScrollTop = oGraph?.$scroller?.[0]?.scrollTop || 0;
575
+
577
576
  oGraph.fireEvent("nodeDropped", {
578
577
  ...oEvent.getParameters(),
579
578
  node: dropData.draggedControl,
580
- newX: x,
581
- newY: y
579
+ newX: oClampedPosition.x,
580
+ newY: oClampedPosition.y
582
581
  });
582
+
583
+ // Preserve scroll position after event
584
+ Utils.preserveScrollPosition(oGraph, currentScrollLeft, currentScrollTop, 0);
583
585
  };
584
586
 
585
587
  /**
@@ -6,8 +6,9 @@
6
6
  */
7
7
  sap.ui.define([
8
8
  "sap/ui/thirdparty/jquery",
9
- "sap/suite/ui/commons/library"
10
- ], function (jQuery, Library) {
9
+ "sap/suite/ui/commons/library",
10
+ "sap/suite/ui/commons/networkgraph/Utils"
11
+ ], function (jQuery, Library, Utils) {
11
12
  "use strict";
12
13
  /**
13
14
  * PortManager class for managing network graph ports.
@@ -485,6 +486,11 @@ sap.ui.define([
485
486
  const targetPortSide = target.dataset.portSide;
486
487
  // Check if the target node is valid
487
488
  if (targetNode) {
489
+ const oGraph = sourceNode.getParent();
490
+ // Capture current scroll position before firing event
491
+ const scrollerElement = oGraph.$scroller[0];
492
+ const currentScrollLeft = scrollerElement.scrollLeft || 0;
493
+ const currentScrollTop = scrollerElement.scrollTop || 0;
488
494
  const mParameters = {
489
495
  from: sourceNode.getKey(),
490
496
  to: targetNode.getKey(),
@@ -493,7 +499,10 @@ sap.ui.define([
493
499
  toNode: targetNode
494
500
  };
495
501
  // Fire the connectionCreated event
496
- sourceNode.getParent().fireConnectionCreated(mParameters);
502
+ oGraph.fireConnectionCreated(mParameters);
503
+
504
+ // Preserve scroll position after event
505
+ Utils.preserveScrollPosition(oGraph, currentScrollLeft, currentScrollTop, 0);
497
506
  }
498
507
  }
499
508
  // Remove inlet ports from other nodes