@sapui5/sap.suite.ui.commons 1.143.0 → 1.145.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 (126) 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 +26 -14
  5. package/src/sap/suite/ui/commons/CalculationBuilderExpression.js +2 -2
  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 +5 -5
  9. package/src/sap/suite/ui/commons/CalculationBuilderItem.js +1 -1
  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 -3
  13. package/src/sap/suite/ui/commons/MicroProcessFlow.js +77 -73
  14. package/src/sap/suite/ui/commons/MicroProcessFlowItem.js +3 -3
  15. package/src/sap/suite/ui/commons/Timeline.js +9 -0
  16. package/src/sap/suite/ui/commons/TimelineNavigator.js +37 -5
  17. package/src/sap/suite/ui/commons/TimelineRenderer.js +3 -3
  18. package/src/sap/suite/ui/commons/collaboration/BaseHelperService.js +0 -1
  19. package/src/sap/suite/ui/commons/collaboration/CollaborationHelper.js +0 -3
  20. package/src/sap/suite/ui/commons/collaboration/CollaborationManagerService.js +0 -1
  21. package/src/sap/suite/ui/commons/collaboration/ContactHelper.js +7 -0
  22. package/src/sap/suite/ui/commons/collaboration/ContactPopover.fragment.xml +4 -1
  23. package/src/sap/suite/ui/commons/collaboration/MinimalContactPopover.fragment.xml +2 -1
  24. package/src/sap/suite/ui/commons/collaboration/TeamsHelperService.js +0 -6
  25. package/src/sap/suite/ui/commons/collaboration/channels/MessageChannel.js +0 -2
  26. package/src/sap/suite/ui/commons/collaboration/flpplugins/msplugin/Component-preload.js +1 -1
  27. package/src/sap/suite/ui/commons/collaboration/flpplugins/msplugin/Component.js +1 -2
  28. package/src/sap/suite/ui/commons/flexibility/changeHandler/PropertyChangeMapper.js +1 -1
  29. package/src/sap/suite/ui/commons/imageeditor/CropCustomShapeHistoryItem.js +1 -1
  30. package/src/sap/suite/ui/commons/imageeditor/CropEllipseHistoryItem.js +1 -1
  31. package/src/sap/suite/ui/commons/imageeditor/CropRectangleHistoryItem.js +1 -1
  32. package/src/sap/suite/ui/commons/imageeditor/CustomSizeItem.js +1 -1
  33. package/src/sap/suite/ui/commons/imageeditor/FilterHistoryItem.js +1 -1
  34. package/src/sap/suite/ui/commons/imageeditor/FlipHistoryItem.js +1 -1
  35. package/src/sap/suite/ui/commons/imageeditor/HistoryItem.js +1 -1
  36. package/src/sap/suite/ui/commons/imageeditor/ImageEditor.js +1 -1
  37. package/src/sap/suite/ui/commons/imageeditor/ImageEditorContainer.js +1 -1
  38. package/src/sap/suite/ui/commons/imageeditor/ImageEditorResponsiveContainer.js +1 -1
  39. package/src/sap/suite/ui/commons/imageeditor/ResizeHistoryItem.js +1 -1
  40. package/src/sap/suite/ui/commons/imageeditor/RotateHistoryItem.js +1 -1
  41. package/src/sap/suite/ui/commons/library.js +100 -3
  42. package/src/sap/suite/ui/commons/messagebundle.properties +76 -10
  43. package/src/sap/suite/ui/commons/messagebundle_ar.properties +51 -5
  44. package/src/sap/suite/ui/commons/messagebundle_bg.properties +62 -16
  45. package/src/sap/suite/ui/commons/messagebundle_ca.properties +52 -6
  46. package/src/sap/suite/ui/commons/messagebundle_cnr.properties +52 -6
  47. package/src/sap/suite/ui/commons/messagebundle_cs.properties +51 -5
  48. package/src/sap/suite/ui/commons/messagebundle_cy.properties +51 -5
  49. package/src/sap/suite/ui/commons/messagebundle_da.properties +51 -5
  50. package/src/sap/suite/ui/commons/messagebundle_de.properties +52 -6
  51. package/src/sap/suite/ui/commons/messagebundle_el.properties +52 -6
  52. package/src/sap/suite/ui/commons/messagebundle_en.properties +50 -7
  53. package/src/sap/suite/ui/commons/messagebundle_en_GB.properties +52 -6
  54. package/src/sap/suite/ui/commons/messagebundle_en_US_saprigi.properties +54 -6
  55. package/src/sap/suite/ui/commons/messagebundle_es.properties +53 -7
  56. package/src/sap/suite/ui/commons/messagebundle_es_MX.properties +54 -8
  57. package/src/sap/suite/ui/commons/messagebundle_et.properties +51 -5
  58. package/src/sap/suite/ui/commons/messagebundle_fi.properties +51 -5
  59. package/src/sap/suite/ui/commons/messagebundle_fr.properties +52 -6
  60. package/src/sap/suite/ui/commons/messagebundle_fr_CA.properties +52 -6
  61. package/src/sap/suite/ui/commons/messagebundle_hi.properties +52 -6
  62. package/src/sap/suite/ui/commons/messagebundle_hr.properties +52 -6
  63. package/src/sap/suite/ui/commons/messagebundle_hu.properties +51 -5
  64. package/src/sap/suite/ui/commons/messagebundle_id.properties +60 -14
  65. package/src/sap/suite/ui/commons/messagebundle_it.properties +53 -7
  66. package/src/sap/suite/ui/commons/messagebundle_iw.properties +51 -5
  67. package/src/sap/suite/ui/commons/messagebundle_ja.properties +52 -6
  68. package/src/sap/suite/ui/commons/messagebundle_kk.properties +51 -5
  69. package/src/sap/suite/ui/commons/messagebundle_ko.properties +51 -5
  70. package/src/sap/suite/ui/commons/messagebundle_lt.properties +51 -5
  71. package/src/sap/suite/ui/commons/messagebundle_lv.properties +52 -6
  72. package/src/sap/suite/ui/commons/messagebundle_mk.properties +51 -5
  73. package/src/sap/suite/ui/commons/messagebundle_ms.properties +59 -13
  74. package/src/sap/suite/ui/commons/messagebundle_nl.properties +51 -5
  75. package/src/sap/suite/ui/commons/messagebundle_no.properties +52 -6
  76. package/src/sap/suite/ui/commons/messagebundle_pl.properties +51 -5
  77. package/src/sap/suite/ui/commons/messagebundle_pt.properties +52 -6
  78. package/src/sap/suite/ui/commons/messagebundle_pt_PT.properties +51 -5
  79. package/src/sap/suite/ui/commons/messagebundle_ro.properties +51 -5
  80. package/src/sap/suite/ui/commons/messagebundle_ru.properties +51 -5
  81. package/src/sap/suite/ui/commons/messagebundle_sh.properties +52 -6
  82. package/src/sap/suite/ui/commons/messagebundle_sk.properties +51 -5
  83. package/src/sap/suite/ui/commons/messagebundle_sl.properties +51 -5
  84. package/src/sap/suite/ui/commons/messagebundle_sr.properties +52 -6
  85. package/src/sap/suite/ui/commons/messagebundle_sv.properties +51 -5
  86. package/src/sap/suite/ui/commons/messagebundle_th.properties +52 -6
  87. package/src/sap/suite/ui/commons/messagebundle_tr.properties +51 -5
  88. package/src/sap/suite/ui/commons/messagebundle_uk.properties +53 -7
  89. package/src/sap/suite/ui/commons/messagebundle_vi.properties +55 -9
  90. package/src/sap/suite/ui/commons/messagebundle_zh_CN.properties +51 -5
  91. package/src/sap/suite/ui/commons/messagebundle_zh_TW.properties +53 -7
  92. package/src/sap/suite/ui/commons/networkgraph/ElementBase.js +19 -1
  93. package/src/sap/suite/ui/commons/networkgraph/Graph.js +371 -29
  94. package/src/sap/suite/ui/commons/networkgraph/GraphRenderer.js +23 -10
  95. package/src/sap/suite/ui/commons/networkgraph/Group.js +43 -22
  96. package/src/sap/suite/ui/commons/networkgraph/KeyboardNavigator.js +54 -6
  97. package/src/sap/suite/ui/commons/networkgraph/Line.js +736 -31
  98. package/src/sap/suite/ui/commons/networkgraph/Node.js +546 -96
  99. package/src/sap/suite/ui/commons/networkgraph/Tooltip.js +5 -0
  100. package/src/sap/suite/ui/commons/networkgraph/layout/NoopLayout.js +28 -5
  101. package/src/sap/suite/ui/commons/networkgraph/util/ConnectionPathUtils.js +1144 -0
  102. package/src/sap/suite/ui/commons/networkgraph/util/CreateConnectionPopover.js +374 -0
  103. package/src/sap/suite/ui/commons/networkgraph/util/DependencyLayoutHelper.js +870 -0
  104. package/src/sap/suite/ui/commons/networkgraph/util/DragDropManager.js +563 -41
  105. package/src/sap/suite/ui/commons/networkgraph/util/PortManager.js +573 -0
  106. package/src/sap/suite/ui/commons/statusindicator/Circle.js +1 -1
  107. package/src/sap/suite/ui/commons/statusindicator/CustomShape.js +1 -1
  108. package/src/sap/suite/ui/commons/statusindicator/DiscreteThreshold.js +1 -1
  109. package/src/sap/suite/ui/commons/statusindicator/FillingOption.js +1 -1
  110. package/src/sap/suite/ui/commons/statusindicator/LibraryShape.js +1 -1
  111. package/src/sap/suite/ui/commons/statusindicator/Path.js +1 -1
  112. package/src/sap/suite/ui/commons/statusindicator/PropertyThreshold.js +1 -1
  113. package/src/sap/suite/ui/commons/statusindicator/Rectangle.js +1 -1
  114. package/src/sap/suite/ui/commons/statusindicator/Shape.js +1 -1
  115. package/src/sap/suite/ui/commons/statusindicator/ShapeGroup.js +1 -1
  116. package/src/sap/suite/ui/commons/statusindicator/SimpleShape.js +1 -1
  117. package/src/sap/suite/ui/commons/statusindicator/StatusIndicator.js +1 -1
  118. package/src/sap/suite/ui/commons/taccount/TAccount.js +1 -1
  119. package/src/sap/suite/ui/commons/taccount/TAccountGroup.js +1 -1
  120. package/src/sap/suite/ui/commons/taccount/TAccountItem.js +1 -1
  121. package/src/sap/suite/ui/commons/taccount/TAccountItemProperty.js +1 -1
  122. package/src/sap/suite/ui/commons/taccount/TAccountPanel.js +1 -1
  123. package/src/sap/suite/ui/commons/themes/base/NetworkGraph.less +13 -13
  124. package/src/sap/suite/ui/commons/themes/base/NetworkGroup.less +34 -2
  125. package/src/sap/suite/ui/commons/themes/base/NetworkLine.less +58 -13
  126. package/src/sap/suite/ui/commons/themes/base/NetworkNode.less +206 -1
@@ -11,9 +11,25 @@ sap.ui.define([
11
11
  ], function (DragInfo, DropInfo, Log) {
12
12
  "use strict";
13
13
 
14
+ // constants
15
+ const RESIZE_CONFIG = {
16
+ WIDTH_INCREMENT: 150,
17
+ HEIGHT_INCREMENT: 150,
18
+ PROXIMITY_THRESHOLD: 50, // TODO: update according to figma take node's width /height into consideration
19
+ RESIZE_DEBOUNCE_DELAY: 150,
20
+ // Auto-scroll configuration
21
+ AUTO_SCROLL_THRESHOLD: 50, // Distance from scroller edge to trigger auto-scroll
22
+ AUTO_SCROLL_SPEED: 10, // Pixels to scroll per frame (16ms)
23
+ };
24
+
14
25
  // Instance object: Only one will exist
15
26
  var instance = null;
16
27
  var sDragSessionKey = "networkGraphDragSession";
28
+ var oUtilityFunctions = {
29
+ diff: function (a, b) {
30
+ return Math.abs(a - b);
31
+ }
32
+ };
17
33
 
18
34
  /**
19
35
  * Constructor for DragDropManager.
@@ -26,12 +42,218 @@ sap.ui.define([
26
42
  *
27
43
  * @private
28
44
  */
29
- function DragDropManager() { }
45
+ function DragDropManager() {
46
+ this.bIsDragging = false;
47
+ this._iWidthResizeDebounceTimeout = null;
48
+ this._iHeightResizeDebounceTimeout = null;
49
+ this._iRequestAnimationFrameId = null;
50
+ this._iLastWidthResizeTime = 0;
51
+ this._iLastHeightResizeTime = 0;
52
+ // Auto-scroll state
53
+ this._iAutoScrollIntervalId = null;
54
+ this._oAutoScrollDirection = { x: 0, y: 0 };
55
+ }
56
+
57
+ /**
58
+ * Debounced function to resize the inner scroller width when dragging near right edge.
59
+ * Uses debouncing to prevent excessive resize operations during drag operations.
60
+ *
61
+ * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The graph control
62
+ * @param {number} incrementBy - The amount in pixels to increment the width
63
+ * @private
64
+ */
65
+ DragDropManager.prototype._resizeInnerScrollerWidth = function (oGraph, incrementBy) {
66
+ const currentTime = Date.now();
67
+
68
+ // Clear any existing timeout
69
+ if (this._iWidthResizeDebounceTimeout) {
70
+ clearTimeout(this._iWidthResizeDebounceTimeout);
71
+ }
72
+
73
+ // If enough time has passed since last resize, execute immediately
74
+ if (currentTime - this._iLastWidthResizeTime > RESIZE_CONFIG.RESIZE_DEBOUNCE_DELAY) {
75
+ this._executeWidthResize(oGraph, incrementBy);
76
+ this._iLastWidthResizeTime = currentTime;
77
+ } else {
78
+ // Otherwise, debounce the call
79
+ this._iWidthResizeDebounceTimeout = setTimeout(() => {
80
+ this._executeWidthResize(oGraph, incrementBy);
81
+ this._iLastWidthResizeTime = Date.now();
82
+ this._iWidthResizeDebounceTimeout = null;
83
+ }, RESIZE_CONFIG.RESIZE_DEBOUNCE_DELAY);
84
+ }
85
+ };
86
+
87
+ /**
88
+ * Executes the actual width resize in the DOM.
89
+ * Forces a reflow and updates the width-related CSS properties of the inner scroller.
90
+ *
91
+ * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The graph control
92
+ * @param {number} incrementBy - The amount in pixels to increment the width
93
+ * @private
94
+ */
95
+ DragDropManager.prototype._executeWidthResize = function (oGraph, incrementBy) {
96
+ // Guard check: Ensure graph and css for innerscroller exist before proceeding
97
+ if (!oGraph || !oGraph._$innerscroller || !oGraph._$innerscroller.css) {
98
+ return;
99
+ }
100
+
101
+ /**
102
+ * Force a reflow to get the most current width
103
+ * When you read a calculated property like clientHeight, offsetWidth etc.,
104
+ * the browser has to pause the JavaScript code and reflow the page so it can return an accurate number.”
105
+ **/
106
+ oGraph._$innerscroller[0].offsetWidth;
107
+
108
+ const currentWidth = parseFloat(oGraph._$innerscroller.css("width")) ||
109
+ oGraph._$innerscroller[0].getBoundingClientRect().width;
110
+ const newWidth = currentWidth + incrementBy;
111
+
112
+ oGraph._$innerscroller[0].style.setProperty("width", newWidth + "px");
113
+ oGraph._$innerscroller[0].style.setProperty("min-width", newWidth + "px");
114
+ oGraph._$innerscroller[0].style.setProperty("max-width", "none");
115
+ };
116
+
117
+ /**
118
+ * Debounced function to resize the inner scroller height when dragging near bottom edge.
119
+ * Uses debouncing to prevent excessive resize operations during drag operations.
120
+ *
121
+ * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The graph control
122
+ * @param {number} incrementBy - The amount in pixels to increment the height
123
+ * @private
124
+ */
125
+ DragDropManager.prototype._resizeInnerScrollerHeight = function (oGraph, incrementBy) {
126
+ const currentTime = Date.now();
127
+
128
+ // Clear any existing timeout
129
+ if (this._iHeightResizeDebounceTimeout) {
130
+ clearTimeout(this._iHeightResizeDebounceTimeout);
131
+ }
132
+
133
+ // If enough time has passed since last resize, execute immediately
134
+ if (currentTime - this._iLastHeightResizeTime > RESIZE_CONFIG.RESIZE_DEBOUNCE_DELAY) {
135
+ this._executeHeightResize(oGraph, incrementBy);
136
+ this._iLastHeightResizeTime = currentTime;
137
+ } else {
138
+ // Otherwise, debounce the call
139
+ this._iHeightResizeDebounceTimeout = setTimeout(() => {
140
+ this._executeHeightResize(oGraph, incrementBy);
141
+ this._iLastHeightResizeTime = Date.now();
142
+ this._iHeightResizeDebounceTimeout = null;
143
+ }, RESIZE_CONFIG.RESIZE_DEBOUNCE_DELAY);
144
+ }
145
+ };
146
+
147
+ /**
148
+ * Executes the actual height resize in the DOM.
149
+ * Forces a reflow and updates the height-related CSS properties of the inner scroller.
150
+ *
151
+ * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The graph control
152
+ * @param {number} incrementBy - The amount in pixels to increment the height
153
+ * @private
154
+ */
155
+ DragDropManager.prototype._executeHeightResize = function (oGraph, incrementBy) {
156
+ // Guard check: Ensure graph and css for innerscroller exist before proceeding
157
+ if (!oGraph || !oGraph._$innerscroller || !oGraph._$innerscroller.css) {
158
+ return;
159
+ }
160
+
161
+ // Force a reflow to get the most current height
162
+ oGraph._$innerscroller[0].offsetHeight;
163
+
164
+ const currentHeight = parseFloat(oGraph._$innerscroller.css("height")) ||
165
+ oGraph._$innerscroller[0].getBoundingClientRect().height;
166
+ const newHeight = currentHeight + incrementBy;
167
+
168
+ oGraph._$innerscroller[0].style.setProperty("height", newHeight + "px");
169
+ oGraph._$innerscroller[0].style.setProperty("min-height", newHeight + "px");
170
+ oGraph._$innerscroller[0].style.setProperty("max-height", "none");
171
+ };
172
+
173
+ /**
174
+ * Checks if the scroller can scroll in the given direction.
175
+ *
176
+ * @param {HTMLElement} scrollerElement - The scroller DOM element
177
+ * @param {string} direction - Direction to check: 'up', 'down', 'left', 'right'
178
+ * @returns {boolean} True if scrolling is possible in that direction
179
+ * @private
180
+ */
181
+ DragDropManager.prototype._canScroll = function (scrollerElement, direction) {
182
+ if (!scrollerElement) {
183
+ return false;
184
+ }
185
+
186
+ switch (direction) {
187
+ case 'up':
188
+ return scrollerElement.scrollTop > 0;
189
+ case 'down':
190
+ return scrollerElement.scrollTop < (scrollerElement.scrollHeight - scrollerElement.clientHeight);
191
+ case 'left':
192
+ return scrollerElement.scrollLeft > 0;
193
+ case 'right':
194
+ return scrollerElement.scrollLeft < (scrollerElement.scrollWidth - scrollerElement.clientWidth);
195
+ default:
196
+ return false;
197
+ }
198
+ };
199
+
200
+ /**
201
+ * Starts or updates auto-scrolling of the viewport.
202
+ *
203
+ * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The graph control
204
+ * @param {number} scrollX - Horizontal scroll direction (-1: left, 0: none, 1: right)
205
+ * @param {number} scrollY - Vertical scroll direction (-1: up, 0: none, 1: down)
206
+ * @private
207
+ */
208
+ DragDropManager.prototype._startAutoScroll = function (oGraph, scrollX, scrollY) {
209
+ // Update scroll direction
210
+ this._oAutoScrollDirection = { x: scrollX, y: scrollY };
211
+
212
+ // If already scrolling, direction is updated, no need to restart interval
213
+ if (this._iAutoScrollIntervalId) {
214
+ return;
215
+ }
216
+
217
+ // Start auto-scroll loop at ~60fps
218
+ this._iAutoScrollIntervalId = setInterval(() => {
219
+ if (!this.bIsDragging) {
220
+ this._stopAutoScroll();
221
+ return;
222
+ }
223
+
224
+ const scrollerElement = oGraph.$scroller?.[0];
225
+ if (!scrollerElement) {
226
+ this._stopAutoScroll();
227
+ return;
228
+ }
229
+
230
+ // Apply scrolling
231
+ if (this._oAutoScrollDirection.x !== 0) {
232
+ scrollerElement.scrollLeft += this._oAutoScrollDirection.x * RESIZE_CONFIG.AUTO_SCROLL_SPEED;
233
+ }
234
+ if (this._oAutoScrollDirection.y !== 0) {
235
+ scrollerElement.scrollTop += this._oAutoScrollDirection.y * RESIZE_CONFIG.AUTO_SCROLL_SPEED;
236
+ }
237
+ }, 16); // ~60fps
238
+ };
239
+
240
+ /**
241
+ * Stops auto-scrolling.
242
+ * @private
243
+ */
244
+ DragDropManager.prototype._stopAutoScroll = function () {
245
+ if (this._iAutoScrollIntervalId) {
246
+ clearInterval(this._iAutoScrollIntervalId);
247
+ this._iAutoScrollIntervalId = null;
248
+ }
249
+ this._oAutoScrollDirection = { x: 0, y: 0 };
250
+ };
30
251
 
31
252
  /**
32
253
  * Validates if the given control is supported for drag and drop operations.
254
+ * Checks if the control is a valid Graph or Node instance.
33
255
  *
34
- * @param oControl - The control to validate
256
+ * @param {object} oControl - The control to validate
35
257
  * @returns {boolean} True if the control is eligible for drag and drop, otherwise false
36
258
  * @private
37
259
  */
@@ -54,6 +276,45 @@ sap.ui.define([
54
276
  oControl.isA("sap.suite.ui.commons.networkgraph.Node");
55
277
  }
56
278
 
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
+
57
318
  /**
58
319
  * Handles the drag start event for nodes.
59
320
  *
@@ -63,10 +324,36 @@ sap.ui.define([
63
324
  DragDropManager.prototype._handleDragStart = function (oEvent) {
64
325
  let dragSession = oEvent.getParameter("dragSession");
65
326
  let browserEvent = oEvent.getParameter("browserEvent");
327
+ let oGraph = oEvent.getParameter("target")?.getParent();
328
+ let boundingRectScroller = oGraph?.$scroller?.[0]?.getBoundingClientRect() || { x: 0, y: 0 };
329
+ let boundingRectInnerScroller = oGraph?._$innerscroller?.[0]?.getBoundingClientRect() || { x: 0, y: 0 };
330
+
331
+ // Capture initial scroll positions
332
+ let scrollLeft = oGraph?._$innerscroller?.[0]?.scrollLeft || 0;
333
+ let scrollTop = oGraph?._$innerscroller?.[0]?.scrollTop || 0;
334
+ this.bIsDragging = true;
335
+
336
+ // Stop any existing auto-scroll from previous drag operations
337
+ this._stopAutoScroll();
338
+
66
339
  dragSession.setComplexData(sDragSessionKey, {
340
+ // Mouse cursor position when drag starts (in viewport coordinates)
67
341
  cursorX: browserEvent.clientX,
68
- cursorY: browserEvent.clientY
342
+ cursorY: browserEvent.clientY,
343
+ // Position of the inner scroller container when drag starts (relative to viewport)
344
+ // This changes if the graph content is scrolled during drag
345
+ innerScrollerX: boundingRectInnerScroller.x,
346
+ innerScrollerY: boundingRectInnerScroller.y,
347
+ // Position of the outer scroller container when drag starts (relative to viewport)
348
+ // This is usually stable but included for completeness
349
+ scrollerX: boundingRectScroller.x,
350
+ scrollerY: boundingRectScroller.y,
351
+ // How much the graph content was already scrolled when drag starts
352
+ // Used to maintain scroll position after drop to prevent viewport jumping
353
+ initialScrollLeft: scrollLeft,
354
+ initialScrollTop: scrollTop
69
355
  });
356
+ this._iRequestAnimationFrameId && cancelAnimationFrame(this._iRequestAnimationFrameId);
70
357
  }
71
358
 
72
359
  /**
@@ -76,13 +363,12 @@ sap.ui.define([
76
363
  * @private
77
364
  */
78
365
  DragDropManager.prototype._handleDragEnd = function (oEvent) {
79
-
80
- let oBrowserEvent = oEvent.getParameter("browserEvent");
81
-
82
- if (!oBrowserEvent) {
83
- Log.warn("No native browser event available in drag end event.");
366
+ if (!this._validateEvent(oEvent)) {
84
367
  return;
85
368
  }
369
+
370
+ // Stop auto-scrolling when drag ends
371
+ this._stopAutoScroll();
86
372
  };
87
373
 
88
374
  /**
@@ -92,13 +378,256 @@ sap.ui.define([
92
378
  * @private
93
379
  */
94
380
  DragDropManager.prototype._handleDragOver = function (oEvent) {
95
- // Optional: Implement visual feedback of drag ghost for Lasso selection
96
- let oBrowserEvent = oEvent.getParameter("browserEvent");
381
+ if (!this.bIsDragging) {
382
+ // not dragging - skip dragover handling
383
+ return;
384
+ }
385
+ if (!this._validateEvent(oEvent)) {
386
+ return;
387
+ }
97
388
 
98
- if (!oBrowserEvent) {
99
- Log.warn("No native browser event available in drag over event.");
389
+ const oGraph = oEvent.getParameter("target");
390
+ const oBrowserEvent = oEvent.getParameter("browserEvent");
391
+ const oScrollerElement = oGraph?.$scroller?.[0];
392
+ const oInnerScrollerElement = oGraph?._$innerscroller?.[0];
393
+
394
+ if (!oScrollerElement || !oInnerScrollerElement) {
100
395
  return;
101
396
  }
397
+
398
+ // Get cursor position
399
+ const iCursorX = oBrowserEvent?.clientX;
400
+ const iCursorY = oBrowserEvent?.clientY;
401
+
402
+ // Get scroller's bounding rect to know viewport edges
403
+ const oScrollerRect = oScrollerElement.getBoundingClientRect();
404
+
405
+ // ========================================
406
+ // AUTO-SCROLL LOGIC (Decision Tree)
407
+ // ========================================
408
+
409
+ let scrollX = 0;
410
+ let scrollY = 0;
411
+
412
+ // Check horizontal auto-scroll
413
+ const distanceFromScrollerLeft = iCursorX - oScrollerRect.left;
414
+ const distanceFromScrollerRight = oScrollerRect.right - iCursorX;
415
+
416
+ if (distanceFromScrollerRight <= RESIZE_CONFIG.AUTO_SCROLL_THRESHOLD &&
417
+ this._canScroll(oScrollerElement, 'right')) {
418
+ scrollX = 1; // Scroll right
419
+ } else if (distanceFromScrollerLeft <= RESIZE_CONFIG.AUTO_SCROLL_THRESHOLD &&
420
+ this._canScroll(oScrollerElement, 'left')) {
421
+ scrollX = -1; // Scroll left
422
+ }
423
+
424
+ // Check vertical auto-scroll
425
+ const distanceFromScrollerTop = iCursorY - oScrollerRect.top;
426
+ const distanceFromScrollerBottom = oScrollerRect.bottom - iCursorY;
427
+
428
+ if (distanceFromScrollerBottom <= RESIZE_CONFIG.AUTO_SCROLL_THRESHOLD &&
429
+ this._canScroll(oScrollerElement, 'down')) {
430
+ scrollY = 1; // Scroll down
431
+ } else if (distanceFromScrollerTop <= RESIZE_CONFIG.AUTO_SCROLL_THRESHOLD &&
432
+ this._canScroll(oScrollerElement, 'up')) {
433
+ scrollY = -1; // Scroll up
434
+ }
435
+
436
+ // Start/update/stop auto-scroll based on calculated direction
437
+ if (scrollX !== 0 || scrollY !== 0) {
438
+ this._startAutoScroll(oGraph, scrollX, scrollY);
439
+ } else {
440
+ this._stopAutoScroll();
441
+ }
442
+
443
+ // Get dimensions and scroll positions
444
+ const iScrollerWidth = oScrollerElement.clientWidth;
445
+ const iScrollerHeight = oScrollerElement.clientHeight;
446
+ const iInnerScrollerWidth = oInnerScrollerElement.scrollWidth || oInnerScrollerElement.clientWidth;
447
+ const iInnerScrollerHeight = oInnerScrollerElement.scrollHeight || oInnerScrollerElement.clientHeight;
448
+ const iScrollLeft = oScrollerElement.scrollLeft;
449
+ const iScrollTop = oScrollerElement.scrollTop;
450
+
451
+ // Check proximity to right edge for width expansion
452
+ // TODO: Consider adding a maximum width/height limit to prevent excessive growth
453
+ // TODO: Consider adding factor of cursor position being at the end of the screen
454
+ const bShouldExpandWidth = this._shouldExpandDimension(
455
+ iInnerScrollerWidth,
456
+ iScrollerWidth,
457
+ iScrollLeft,
458
+ iCursorX - oScrollerRect.left, // Cursor X relative to scroller
459
+ RESIZE_CONFIG.PROXIMITY_THRESHOLD
460
+ );
461
+
462
+ if (bShouldExpandWidth) {
463
+ this._resizeInnerScrollerWidth(oGraph, RESIZE_CONFIG.WIDTH_INCREMENT);
464
+ }
465
+
466
+ // Check proximity to bottom edge for height expansion
467
+ const bShouldExpandHeight = this._shouldExpandDimension(
468
+ iInnerScrollerHeight,
469
+ iScrollerHeight,
470
+ iScrollTop,
471
+ iCursorY - oScrollerRect.top, // Cursor Y relative to scroller
472
+ RESIZE_CONFIG.PROXIMITY_THRESHOLD
473
+ );
474
+
475
+ if (bShouldExpandHeight) {
476
+ this._resizeInnerScrollerHeight(oGraph, RESIZE_CONFIG.HEIGHT_INCREMENT);
477
+ }
478
+ };
479
+
480
+ /**
481
+ * Determines if the inner scroller dimension should be expanded based on cursor proximity to viewport edge
482
+ * and available content space.
483
+ * Handles two cases:
484
+ * Case 1: InnerScroller smaller than Scroller (not scrolled) - cursor must be near viewport edge AND near innerScroller's edge
485
+ * Case 2: InnerScroller larger than Scroller (scrolled) - cursor must be near viewport edge AND running out of content
486
+ *
487
+ * @param {number} iInnerDimension - The total dimension of inner scroller (width or height)
488
+ * @param {number} iScrollerDimension - The viewport dimension of scroller (width or height)
489
+ * @param {number} iScrollPosition - Current scroll position (scrollLeft or scrollTop)
490
+ * @param {number} iCursorPosition - Cursor position relative to scroller (clientX/Y - scrollerRect.left/top)
491
+ * @param {number} iProximityThreshold - Distance from edge to trigger expansion
492
+ * @returns {boolean} True if should expand, false otherwise
493
+ * @private
494
+ */
495
+ DragDropManager.prototype._shouldExpandDimension = function (iInnerDimension, iScrollerDimension, iScrollPosition, iCursorPosition, iProximityThreshold) {
496
+ // Case 1: InnerScroller is smaller than or equal to Scroller (not scrollable or minimal scroll)
497
+ // In this case, cursor proximity is checked against innerScroller's edge, not viewport edge
498
+ if (iInnerDimension <= iScrollerDimension) {
499
+ // Check if cursor is near the innerScroller's content edge
500
+ const bCursorNearInnerScrollerEdge = iCursorPosition >= (iInnerDimension - iProximityThreshold);
501
+ return bCursorNearInnerScrollerEdge;
502
+ }
503
+
504
+ // Case 2: InnerScroller is larger than Scroller (content is scrollable)
505
+ // Check if cursor is near viewport edge AND we're running out of content
506
+ const bCursorNearViewportEdge = iCursorPosition >= (iScrollerDimension - iProximityThreshold);
507
+
508
+ if (!bCursorNearViewportEdge) {
509
+ // Cursor is not near the viewport edge, no need to expand
510
+ return false;
511
+ }
512
+
513
+ // Cursor is near viewport edge, now check if we're running out of content
514
+ const iVisibleEdgePosition = iScrollPosition + iScrollerDimension;
515
+ const iContentEdgeThreshold = iInnerDimension - iProximityThreshold;
516
+
517
+ return iVisibleEdgePosition >= iContentEdgeThreshold;
518
+ };
519
+
520
+ /**
521
+ * Handles the drop event for node elements.
522
+ * Calculates the new position of the node based on cursor movement and scroll changes,
523
+ * then fires the nodeDropped event with the updated coordinates.
524
+ *
525
+ * @param {sap.ui.base.Event} oEvent - The drop event
526
+ * @param {object} dropData - The extracted drop data containing drag information
527
+ * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The graph control
528
+ * @private
529
+ */
530
+ DragDropManager.prototype._handleNodeDrop = function (oEvent, dropData, oGraph) {
531
+ let dragSessionComplexData = dropData.dragSessionComplexData,
532
+ deltaX = dropData.clientX - (dragSessionComplexData?.cursorX || 0), // Calculate the change in X position for the node being dragged in viewport
533
+ deltaY = dropData.clientY - (dragSessionComplexData?.cursorY || 0), // Calculate the change in Y position for the node being dragged in viewport
534
+ prevInnerScrollerX = dragSessionComplexData?.innerScrollerX || 0, // Previous X position of the inner scroller at drag start
535
+ prevInnerScrollerY = dragSessionComplexData?.innerScrollerY || 0; // Previous Y position of the inner scroller at drag start
536
+
537
+ let boundingRectInnerScroller = oGraph?._$innerscroller[0]?.getBoundingClientRect() || { x: 0, y: 0 },
538
+ deltaInnerScrollerX = oUtilityFunctions.diff(prevInnerScrollerX, boundingRectInnerScroller.x || 0), // Change in X position of the inner scroller during drag, to track scrolling
539
+ deltaInnerScrollerY = oUtilityFunctions.diff(prevInnerScrollerY, boundingRectInnerScroller.y || 0), // Change in Y position of the inner scroller during drag, to track scrolling
540
+ deltaSignatureX = prevInnerScrollerX >= boundingRectInnerScroller.x ? 1 : -1, // Determine scroll direction in X axis
541
+ deltaSignatureY = prevInnerScrollerY >= boundingRectInnerScroller.y ? 1 : -1; // Determine scroll direction in Y axis
542
+
543
+ let newX = dropData.draggedControl.getX() + deltaX + (deltaSignatureX * deltaInnerScrollerX),
544
+ newY = dropData.draggedControl.getY() + deltaY + (deltaSignatureY * deltaInnerScrollerY);
545
+
546
+ // Store current scroll position before any DOM changes
547
+ let currentScrollLeft = oGraph?.$scroller?.[0]?.scrollLeft || 0;
548
+ let currentScrollTop = oGraph?.$scroller?.[0]?.scrollTop || 0;
549
+
550
+ oGraph.fireEvent("nodeDropped", {
551
+ ...oEvent.getParameters(),
552
+ node: dropData.draggedControl,
553
+ newX: newX,
554
+ newY: newY
555
+ });
556
+ // Restore the scroll position using the dedicated method
557
+ // TODO: udpate promise based
558
+ setTimeout(() => {
559
+ this._retainScrollPosition(oGraph, currentScrollLeft, currentScrollTop);
560
+ }, 0);
561
+ };
562
+
563
+ /**
564
+ * Handles the drop event for external items (non-node elements).
565
+ * Calculates the drop position relative to the inner scroller and fires the nodeDropped event.
566
+ *
567
+ * @param {sap.ui.base.Event} oEvent - The drop event
568
+ * @param {object} dropData - The extracted drop data containing drag information
569
+ * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The graph control
570
+ * @private
571
+ */
572
+ DragDropManager.prototype._handleExternalItemDrop = function (oEvent, dropData, oGraph) {
573
+ let boundingRect = oGraph?._$innerscroller[0]?.getBoundingClientRect() || { left: 0, top: 0 },
574
+ x = dropData.clientX - boundingRect.left,
575
+ y = dropData.clientY - boundingRect.top;
576
+
577
+ oGraph.fireEvent("nodeDropped", {
578
+ ...oEvent.getParameters(),
579
+ node: dropData.draggedControl,
580
+ newX: x,
581
+ newY: y
582
+ });
583
+ };
584
+
585
+ /**
586
+ * Validates if the event contains required browser event data and if DnD is enabled.
587
+ * Logs a warning if validation fails.
588
+ *
589
+ * @param {sap.ui.base.Event} oEvent - The event to validate
590
+ * @param {sap.suite.ui.commons.networkgraph.Graph} [oGraph] - Optional graph control to check if DnD is enabled
591
+ * @returns {boolean} True if the event is valid, false otherwise
592
+ * @private
593
+ */
594
+ DragDropManager.prototype._validateEvent = function (oEvent, oGraph = undefined) {
595
+ let oBrowserEvent = oEvent.getParameter("browserEvent") || undefined;
596
+ if (!oBrowserEvent || (!!oGraph && !oGraph._isDnDEnabled())) {
597
+ Log.warning("No native browser event available in drop event.");
598
+ return false;
599
+ }
600
+ return true;
601
+ };
602
+
603
+ /**
604
+ * Checks if the given control is a Node type.
605
+ *
606
+ * @param {object} [control={}] - The control to check
607
+ * @returns {boolean} True if the control is a Node, false otherwise
608
+ * @private
609
+ */
610
+ DragDropManager.prototype._isTypeNode = function (control = {}) {
611
+ return control?.isA && control.isA("sap.suite.ui.commons.networkgraph.Node");
612
+ };
613
+
614
+ /**
615
+ * Extracts and returns the relevant drop data from the event.
616
+ * Includes dragged control, cursor position, and drag session data.
617
+ *
618
+ * @param {sap.ui.base.Event} oEvent - The drop event
619
+ * @param {sap.suite.ui.commons.networkgraph.Graph} oGraph - The graph control
620
+ * @returns {object} Object containing draggedControl, clientX, clientY, and dragSessionComplexData
621
+ * @private
622
+ */
623
+ DragDropManager.prototype._extractDropData = function (oEvent, oGraph) {
624
+ let oBrowserEvent = oEvent.getParameter("browserEvent") || {};
625
+ return {
626
+ draggedControl: oEvent.getParameter("draggedControl") || {},
627
+ clientX: oBrowserEvent.clientX || 0,
628
+ clientY: oBrowserEvent.clientY || 0,
629
+ dragSessionComplexData: oEvent.getParameter("dragSession")?.getComplexData(sDragSessionKey) || {},
630
+ };
102
631
  };
103
632
 
104
633
  /**
@@ -109,39 +638,27 @@ sap.ui.define([
109
638
  * @private
110
639
  */
111
640
  DragDropManager.prototype._handleDrop = function (oEvent, oGraph) {
112
- let oBrowserEvent = oEvent.getParameter("browserEvent") || {};
113
- let { clientX = 0, clientY = 0 } = oBrowserEvent,
114
- oDraggedControl = oEvent.getParameter("draggedControl");
115
- if (!oBrowserEvent) {
116
- Log.warn("No native browser event available in drop event.");
641
+ this.bIsDragging = false;
642
+ if (!this._validateEvent(oEvent, oGraph)) {
117
643
  return;
118
644
  }
119
645
 
120
- if (oEvent.getParameter("draggedControl")?.isA("sap.suite.ui.commons.networkgraph.Node")) {
121
- let dragSessionComplexData = oEvent.getParameter("dragSession").getComplexData(sDragSessionKey),
122
- deltaX = clientX - (dragSessionComplexData?.cursorX || 0),
123
- deltaY = clientY - (dragSessionComplexData?.cursorY || 0);
124
-
125
- oGraph.fireEvent("nodeDropped", {
126
- node: oDraggedControl,
127
- newX: oDraggedControl.getX() + deltaX,
128
- newY: oDraggedControl.getY() + deltaY
129
- });
646
+ let dropData = this._extractDropData(oEvent, oGraph);
647
+ if (this._isTypeNode(dropData.draggedControl)) {
648
+ this._handleNodeDrop(oEvent, dropData, oGraph);
130
649
  } else {
131
- let boundingRect = oGraph?._$innerscroller[0]?.getBoundingClientRect() || { left: 0, top: 0 },
132
- x = clientX - boundingRect.left,
133
- y = clientY - boundingRect.top;
134
-
135
- oGraph.fireEvent("nodeDropped", {
136
- ...oEvent.getParameters(),
137
- node: oDraggedControl,
138
- newX: x,
139
- newY: y
140
- });
650
+ this._handleExternalItemDrop(oEvent, dropData, oGraph);
141
651
  }
142
652
  };
143
653
 
144
- // Add drag and drop config if needed
654
+ /**
655
+ * Injects drag and drop configuration into the given control.
656
+ * Adds appropriate DragInfo and DropInfo based on control type (Graph or Node).
657
+ *
658
+ * @param {sap.ui.core.Control} oControl - The control to inject DnD functionality into
659
+ * @throws {Error} If the control is not valid or not supported for DnD
660
+ * @public
661
+ */
145
662
  DragDropManager.prototype.injectDnD = function (oControl) {
146
663
  if (!this.isValidControl(oControl)) {
147
664
  throw new Error("DragDropManager: Control is not valid for DnD");
@@ -154,11 +671,16 @@ sap.ui.define([
154
671
  dragEnd: this._handleDragEnd.bind(this)
155
672
  });
156
673
  var dropInfo = new DropInfo({
157
- dropPosition: "Between",
674
+ dropPosition: "On",
158
675
  drop: function (oEvent) {
159
676
  this._handleDrop(oEvent, oControl);
160
677
  }.bind(this),
161
- dragOver: this._handleDragOver.bind(this)
678
+ dragOver: this._handleDragOver.bind(this),
679
+ dragEnter: function (oEvent) {
680
+ oEvent.getParameter("dragSession").setIndicatorConfig({
681
+ display: "none"
682
+ });
683
+ }
162
684
  });
163
685
 
164
686
  oControl.addDragDropConfig(dragInfo);
@@ -194,4 +716,4 @@ sap.ui.define([
194
716
  }
195
717
 
196
718
  return instance;
197
- }, /* bExport= */true);
719
+ }, /* bExport= */true);