@sapui5/sap.suite.ui.commons 1.146.0 → 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 (157) 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 +56 -2
  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/CalculationBuilderItem.js +16 -3
  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/collaboration/ContactPopover.fragment.xml +8 -8
  15. package/src/sap/suite/ui/commons/collaboration/MinimalContactPopover.fragment.xml +5 -5
  16. package/src/sap/suite/ui/commons/flexibility/changeHandler/PropertyChangeMapper.js +1 -1
  17. package/src/sap/suite/ui/commons/imageeditor/CropCustomShapeHistoryItem.js +1 -1
  18. package/src/sap/suite/ui/commons/imageeditor/CropEllipseHistoryItem.js +1 -1
  19. package/src/sap/suite/ui/commons/imageeditor/CropRectangleHistoryItem.js +1 -1
  20. package/src/sap/suite/ui/commons/imageeditor/CustomSizeItem.js +1 -1
  21. package/src/sap/suite/ui/commons/imageeditor/FilterHistoryItem.js +1 -1
  22. package/src/sap/suite/ui/commons/imageeditor/FlipHistoryItem.js +1 -1
  23. package/src/sap/suite/ui/commons/imageeditor/HistoryItem.js +1 -1
  24. package/src/sap/suite/ui/commons/imageeditor/ImageEditor.js +1 -1
  25. package/src/sap/suite/ui/commons/imageeditor/ImageEditorContainer.js +1 -1
  26. package/src/sap/suite/ui/commons/imageeditor/ImageEditorResponsiveContainer.js +1 -1
  27. package/src/sap/suite/ui/commons/imageeditor/ResizeHistoryItem.js +1 -1
  28. package/src/sap/suite/ui/commons/imageeditor/RotateHistoryItem.js +1 -1
  29. package/src/sap/suite/ui/commons/library.js +4 -4
  30. package/src/sap/suite/ui/commons/messagebundle.properties +12 -8
  31. package/src/sap/suite/ui/commons/messagebundle_mk.properties +1 -1
  32. package/src/sap/suite/ui/commons/networkgraph/Graph.js +138 -27
  33. package/src/sap/suite/ui/commons/networkgraph/GraphMap.js +25 -3
  34. package/src/sap/suite/ui/commons/networkgraph/KeyboardNavigator.js +332 -13
  35. package/src/sap/suite/ui/commons/networkgraph/Line.js +5 -1
  36. package/src/sap/suite/ui/commons/networkgraph/Node.js +67 -5
  37. package/src/sap/suite/ui/commons/networkgraph/Utils.js +10 -0
  38. package/src/sap/suite/ui/commons/networkgraph/util/ConnectionPathUtils.js +34 -4
  39. package/src/sap/suite/ui/commons/networkgraph/util/DependencyLayoutHelper.js +213 -74
  40. package/src/sap/suite/ui/commons/statusindicator/Circle.js +1 -1
  41. package/src/sap/suite/ui/commons/statusindicator/CustomShape.js +1 -1
  42. package/src/sap/suite/ui/commons/statusindicator/DiscreteThreshold.js +1 -1
  43. package/src/sap/suite/ui/commons/statusindicator/FillingOption.js +1 -1
  44. package/src/sap/suite/ui/commons/statusindicator/LibraryShape.js +1 -1
  45. package/src/sap/suite/ui/commons/statusindicator/Path.js +1 -1
  46. package/src/sap/suite/ui/commons/statusindicator/PropertyThreshold.js +1 -1
  47. package/src/sap/suite/ui/commons/statusindicator/Rectangle.js +1 -1
  48. package/src/sap/suite/ui/commons/statusindicator/Shape.js +1 -1
  49. package/src/sap/suite/ui/commons/statusindicator/ShapeGroup.js +1 -1
  50. package/src/sap/suite/ui/commons/statusindicator/SimpleShape.js +1 -1
  51. package/src/sap/suite/ui/commons/statusindicator/StatusIndicator.js +1 -1
  52. package/src/sap/suite/ui/commons/taccount/TAccount.js +1 -1
  53. package/src/sap/suite/ui/commons/taccount/TAccountGroup.js +1 -1
  54. package/src/sap/suite/ui/commons/taccount/TAccountItem.js +1 -1
  55. package/src/sap/suite/ui/commons/taccount/TAccountItemProperty.js +1 -1
  56. package/src/sap/suite/ui/commons/taccount/TAccountPanel.js +1 -1
  57. package/src/sap/suite/ui/commons/themes/base/BusinessCard.less +5 -4
  58. package/src/sap/suite/ui/commons/themes/base/CalculationBuilder.less +33 -24
  59. package/src/sap/suite/ui/commons/themes/base/CalculationBuilderExpression.less +40 -31
  60. package/src/sap/suite/ui/commons/themes/base/CalculationBuilderInput.less +26 -10
  61. package/src/sap/suite/ui/commons/themes/base/CalculationBuilderItem.less +76 -63
  62. package/src/sap/suite/ui/commons/themes/base/ChartContainer.less +8 -11
  63. package/src/sap/suite/ui/commons/themes/base/ChartTile.less +5 -8
  64. package/src/sap/suite/ui/commons/themes/base/CollaborationPopover.less +116 -106
  65. package/src/sap/suite/ui/commons/themes/base/DateRangeScroller.less +5 -5
  66. package/src/sap/suite/ui/commons/themes/base/DateRangeSliderInternal.less +9 -10
  67. package/src/sap/suite/ui/commons/themes/base/FacetOverview.less +9 -10
  68. package/src/sap/suite/ui/commons/themes/base/FeedItemHeader.less +9 -12
  69. package/src/sap/suite/ui/commons/themes/base/FeedTile.less +23 -27
  70. package/src/sap/suite/ui/commons/themes/base/GenericTile2X2.less +15 -15
  71. package/src/sap/suite/ui/commons/themes/base/HeaderCell.less +7 -6
  72. package/src/sap/suite/ui/commons/themes/base/HeaderContainer.less +35 -37
  73. package/src/sap/suite/ui/commons/themes/base/ImageEditor.less +28 -10
  74. package/src/sap/suite/ui/commons/themes/base/ImageEditorContainer.less +29 -22
  75. package/src/sap/suite/ui/commons/themes/base/InfoTile.less +10 -4
  76. package/src/sap/suite/ui/commons/themes/base/InfoTileSize.less +5 -4
  77. package/src/sap/suite/ui/commons/themes/base/KpiTile.less +10 -11
  78. package/src/sap/suite/ui/commons/themes/base/LaunchTile.less +9 -8
  79. package/src/sap/suite/ui/commons/themes/base/LinkActionSheet.less +14 -43
  80. package/src/sap/suite/ui/commons/themes/base/MicroProcessFlow.less +51 -47
  81. package/src/sap/suite/ui/commons/themes/base/MonitoringContent.less +5 -5
  82. package/src/sap/suite/ui/commons/themes/base/NetworkGraph.less +57 -62
  83. package/src/sap/suite/ui/commons/themes/base/NetworkGroup.less +61 -65
  84. package/src/sap/suite/ui/commons/themes/base/NetworkLine.less +58 -55
  85. package/src/sap/suite/ui/commons/themes/base/NetworkNode.less +3 -0
  86. package/src/sap/suite/ui/commons/themes/base/NetworkTooltip.less +21 -25
  87. package/src/sap/suite/ui/commons/themes/base/NoteTaker.less +13 -18
  88. package/src/sap/suite/ui/commons/themes/base/NoteTakerCard.less +26 -27
  89. package/src/sap/suite/ui/commons/themes/base/NoteTakerFeeder.less +22 -44
  90. package/src/sap/suite/ui/commons/themes/base/NumericTile.less +7 -5
  91. package/src/sap/suite/ui/commons/themes/base/PictureZoomIn.less +6 -5
  92. package/src/sap/suite/ui/commons/themes/base/ProcessFlow.less +57 -76
  93. package/src/sap/suite/ui/commons/themes/base/ProcessFlowConnection.less +71 -17
  94. package/src/sap/suite/ui/commons/themes/base/ProcessFlowConnectionLabel.less +9 -13
  95. package/src/sap/suite/ui/commons/themes/base/ProcessFlowLaneHeader.less +37 -96
  96. package/src/sap/suite/ui/commons/themes/base/ProcessFlowNode.less +219 -238
  97. package/src/sap/suite/ui/commons/themes/base/SemanticColorMixins.less +55 -0
  98. package/src/sap/suite/ui/commons/themes/base/SplitButton.less +7 -20
  99. package/src/sap/suite/ui/commons/themes/base/StatusIndicator.less +10 -9
  100. package/src/sap/suite/ui/commons/themes/base/TAccount.less +78 -73
  101. package/src/sap/suite/ui/commons/themes/base/TargetFilter.less +50 -58
  102. package/src/sap/suite/ui/commons/themes/base/ThingCollection.less +18 -13
  103. package/src/sap/suite/ui/commons/themes/base/ThreePanelThingViewer.less +14 -14
  104. package/src/sap/suite/ui/commons/themes/base/TileContent2X2.less +9 -13
  105. package/src/sap/suite/ui/commons/themes/base/Timeline.less +16 -19
  106. package/src/sap/suite/ui/commons/themes/base/TimelineItem.less +95 -103
  107. package/src/sap/suite/ui/commons/themes/base/UnifiedThingGroup.less +7 -9
  108. package/src/sap/suite/ui/commons/themes/base/UnifiedThingInspector.less +12 -9
  109. package/src/sap/suite/ui/commons/themes/base/VerticalNavigationBar.less +10 -15
  110. package/src/sap/suite/ui/commons/themes/base/ViewRepeater.less +8 -9
  111. package/src/sap/suite/ui/commons/themes/base/library.source.less +0 -2
  112. package/src/sap/suite/ui/commons/themes/sap_fiori_3/MicroProcessFlow.less +9 -3
  113. package/src/sap/suite/ui/commons/themes/sap_fiori_3/ProcessFlowConnection.less +14 -7
  114. package/src/sap/suite/ui/commons/themes/sap_fiori_3/ProcessFlowConnectionLabel.less +38 -35
  115. package/src/sap/suite/ui/commons/themes/sap_fiori_3/ProcessFlowLaneHeader.less +29 -30
  116. package/src/sap/suite/ui/commons/themes/sap_fiori_3/ProcessFlowNode.less +158 -102
  117. package/src/sap/suite/ui/commons/themes/sap_fiori_3_dark/MicroProcessFlow.less +9 -3
  118. package/src/sap/suite/ui/commons/themes/sap_fiori_3_dark/ProcessFlowConnection.less +14 -7
  119. package/src/sap/suite/ui/commons/themes/sap_fiori_3_dark/ProcessFlowConnectionLabel.less +38 -35
  120. package/src/sap/suite/ui/commons/themes/sap_fiori_3_dark/ProcessFlowLaneHeader.less +30 -31
  121. package/src/sap/suite/ui/commons/themes/sap_fiori_3_dark/ProcessFlowNode.less +157 -101
  122. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcb/ProcessFlowConnection.less +14 -7
  123. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcb/ProcessFlowConnectionLabel.less +38 -35
  124. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcb/ProcessFlowLaneHeader.less +30 -31
  125. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcb/ProcessFlowNode.less +147 -101
  126. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcw/ProcessFlowConnection.less +14 -7
  127. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcw/ProcessFlowConnectionLabel.less +38 -35
  128. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcw/ProcessFlowLaneHeader.less +30 -31
  129. package/src/sap/suite/ui/commons/themes/sap_fiori_3_hcw/ProcessFlowNode.less +158 -102
  130. package/src/sap/suite/ui/commons/themes/sap_horizon/MicroProcessFlow.less +5 -0
  131. package/src/sap/suite/ui/commons/themes/sap_horizon/NetworkLine.less +12 -8
  132. package/src/sap/suite/ui/commons/themes/sap_horizon/ProcessFlowConnection.less +17 -10
  133. package/src/sap/suite/ui/commons/themes/sap_horizon/ProcessFlowConnectionLabel.less +154 -133
  134. package/src/sap/suite/ui/commons/themes/sap_horizon/ProcessFlowLaneHeader.less +30 -32
  135. package/src/sap/suite/ui/commons/themes/sap_horizon/ProcessFlowNode.less +113 -214
  136. package/src/sap/suite/ui/commons/themes/sap_horizon/TimelineItem.less +43 -39
  137. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/MicroProcessFlow.less +5 -0
  138. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/NetworkLine.less +12 -12
  139. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/ProcessFlowConnection.less +17 -10
  140. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/ProcessFlowConnectionLabel.less +154 -133
  141. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/ProcessFlowLaneHeader.less +30 -31
  142. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/ProcessFlowNode.less +114 -216
  143. package/src/sap/suite/ui/commons/themes/sap_horizon_dark/TimelineItem.less +41 -39
  144. package/src/sap/suite/ui/commons/themes/sap_horizon_hcb/MicroProcessFlow.less +4 -4
  145. package/src/sap/suite/ui/commons/themes/sap_horizon_hcb/ProcessFlowConnection.less +17 -10
  146. package/src/sap/suite/ui/commons/themes/sap_horizon_hcb/ProcessFlowConnectionLabel.less +153 -131
  147. package/src/sap/suite/ui/commons/themes/sap_horizon_hcb/ProcessFlowLaneHeader.less +29 -30
  148. package/src/sap/suite/ui/commons/themes/sap_horizon_hcb/ProcessFlowNode.less +159 -103
  149. package/src/sap/suite/ui/commons/themes/sap_horizon_hcb/TimelineItem.less +41 -39
  150. package/src/sap/suite/ui/commons/themes/sap_horizon_hcw/MicroProcessFlow.less +4 -4
  151. package/src/sap/suite/ui/commons/themes/sap_horizon_hcw/ProcessFlowConnection.less +16 -9
  152. package/src/sap/suite/ui/commons/themes/sap_horizon_hcw/ProcessFlowConnectionLabel.less +153 -131
  153. package/src/sap/suite/ui/commons/themes/sap_horizon_hcw/ProcessFlowLaneHeader.less +29 -30
  154. package/src/sap/suite/ui/commons/themes/sap_horizon_hcw/ProcessFlowNode.less +157 -101
  155. package/src/sap/suite/ui/commons/themes/sap_horizon_hcw/TimelineItem.less +41 -39
  156. package/src/sap/suite/ui/commons/themes/base/DateRangeSlider.less +0 -3
  157. package/src/sap/suite/ui/commons/themes/base/MonitoringTile.less +0 -3
@@ -14,11 +14,12 @@ sap.ui.define([
14
14
  "sap/ui/events/KeyCodes",
15
15
  "sap/ui/dom/containsOrEquals",
16
16
  "sap/base/Log",
17
- "sap/ui/core/Lib",
18
- "sap/ui/core/Element"
19
- ], function (jQuery, BaseObject, Group, Node, Line, KeyCodes, containsOrEquals, Log, CoreLib, Element) {
17
+ "sap/ui/core/Lib"
18
+ ], function (jQuery, BaseObject, Group, Node, Line, KeyCodes, containsOrEquals, Log, CoreLib) {
20
19
  "use strict";
21
20
 
21
+ const oResourceBundle = CoreLib.getResourceBundleFor("sap.suite.ui.commons");
22
+
22
23
  var mDirections = {
23
24
  LEFT: "left",
24
25
  RIGHT: "right",
@@ -74,6 +75,13 @@ sap.ui.define([
74
75
  this._oFocusPosition = null;
75
76
  this._oWrapperDom = null;
76
77
  this.isFromFullscreen = null;
78
+ this._oAltNode = null;
79
+ this._oAltConnectors = null;
80
+ this._oAltAnnouncement = null;
81
+ this._oAssociatedControl = null;
82
+ this._oAssociatedContainer = null;
83
+ this._oAssociatedDelegate = null;
84
+ this._bCtrlAltDown = false;
77
85
  }
78
86
  });
79
87
 
@@ -124,6 +132,14 @@ sap.ui.define([
124
132
  this._iColumns = aRow.length;
125
133
  }
126
134
  }, this);
135
+
136
+ const oControl = this._oGraph.getAssociatedControl();
137
+ if (oControl !== this._oAssociatedControl) {
138
+ this._updateAssociatedDelegate(oControl);
139
+ }
140
+ this._oAltNode = null;
141
+ this._oAltConnectors = null;
142
+ this._oAltAnnouncement = null;
127
143
  };
128
144
 
129
145
  KeyboardNavigator.prototype.setWrapperDom = function (oDom) {
@@ -229,6 +245,12 @@ sap.ui.define([
229
245
  KeyboardNavigator.prototype.onkeydown = function (oEvent) {
230
246
  var oItem, oBtn,
231
247
  oFocus = this.getFocus();
248
+
249
+ // On Windows, Ctrl+Alt = AltGr — browsers strip ctrlKey/altKey from the character keydown.
250
+ if (oEvent.keyCode === KeyCodes.ALT && oEvent.ctrlKey) {
251
+ this._bCtrlAltDown = true;
252
+ }
253
+
232
254
  if (!oFocus) {
233
255
  return;
234
256
  }
@@ -256,11 +278,7 @@ sap.ui.define([
256
278
  }
257
279
  oEvent.stopPropagation();
258
280
  } else if (oEvent.keyCode === KeyCodes.F6) {
259
- if (oItem && oBtn) {
260
- oFocus.button = null;
261
- this._oGraph.setFocus(oFocus);
262
- }
263
- this._handleArrow(oEvent, oEvent.shiftKey ? mDirections.LEFT : mDirections.RIGHT);
281
+ this._handleF6(oEvent);
264
282
  } else if (oEvent.keyCode === KeyCodes.F7 && !oEvent.shiftKey) {
265
283
  if (oItem && oBtn) {
266
284
  oFocus.button = null;
@@ -272,12 +290,24 @@ sap.ui.define([
272
290
  this._onCtrlPlus(oEvent);
273
291
  } else if (oEvent.ctrlKey && (oEvent.keyCode === KeyCodes.SLASH || oEvent.keyCode === KeyCodes.NUMPAD_MINUS)) {
274
292
  this._onCtrlMinus(oEvent);
275
- } else if(oEvent.ctrlKey && oEvent.altKey && oEvent.keyCode === KeyCodes.P) {
276
- var oAssociatedControl = this._oGraph.getAssociatedControl();
293
+ } else if (this._bCtrlAltDown && oEvent.keyCode === KeyCodes.P) {
294
+ this._bCtrlAltDown = false;
295
+ const oAssociatedControl = this._oGraph.getAssociatedControl();
277
296
  if (oAssociatedControl) {
278
297
  oAssociatedControl.focus();
279
298
  oItem._setFocus(false);
280
299
  }
300
+ oEvent.preventDefault();
301
+ oEvent.stopPropagation();
302
+ } else if (oEvent.altKey && !oEvent.ctrlKey && !oEvent.shiftKey && oItem instanceof Node) {
303
+ if (oEvent.keyCode === KeyCodes.PLUS || oEvent.keyCode === KeyCodes.NUMPAD_PLUS) {
304
+ this._handleAltAnnounce(oEvent, oItem);
305
+ } else {
306
+ const iDigitIndex = this._getAltDigitIndex(oEvent.keyCode);
307
+ if (iDigitIndex >= 0) {
308
+ this._handleAltConnectorNavigation(oEvent, oItem, iDigitIndex);
309
+ }
310
+ }
281
311
  }
282
312
  };
283
313
 
@@ -285,6 +315,59 @@ sap.ui.define([
285
315
  /* Private methods */
286
316
  /* =========================================================== */
287
317
 
318
+ KeyboardNavigator.prototype._getAltDigitIndex = function (iKeyCode) {
319
+ if (iKeyCode >= KeyCodes.DIGIT_1 && iKeyCode <= KeyCodes.DIGIT_9) {
320
+ return iKeyCode - KeyCodes.DIGIT_1;
321
+ }
322
+ if (iKeyCode >= KeyCodes.NUMPAD_1 && iKeyCode <= KeyCodes.NUMPAD_9) {
323
+ return iKeyCode - KeyCodes.NUMPAD_1;
324
+ }
325
+ return -1;
326
+ };
327
+
328
+ /**
329
+ * Handles F6 / Shift+F6 — skip to next/previous visible group regardless of what is focused.
330
+ * @private
331
+ */
332
+ KeyboardNavigator.prototype._handleF6 = function (oEvent) {
333
+ const bBackward = oEvent.shiftKey,
334
+ oFocus = this.getFocus(),
335
+ oItem = oFocus ? oFocus.item : null,
336
+ bIsRtl = this._oGraph._bIsRtl,
337
+ aGroups = this._oGraph.getGroups().filter(function (oGroup) {
338
+ return !oGroup.isHidden() && oGroup.getVisible();
339
+ }).sort(function (oA, oB) {
340
+ // Top-to-bottom, then by reading direction (RTL: descending x, LTR: ascending x)
341
+ const iDeltaY = oA.getY() - oB.getY();
342
+ if (iDeltaY !== 0) { return iDeltaY; }
343
+ const iDeltaX = oA.getX() - oB.getX();
344
+ return bIsRtl ? -iDeltaX : iDeltaX;
345
+ });
346
+
347
+ if (aGroups.length === 0) {
348
+ return;
349
+ }
350
+
351
+ let iCurrentIndex = -1;
352
+ if (oItem instanceof Group) {
353
+ iCurrentIndex = aGroups.indexOf(oItem);
354
+ } else if (oItem instanceof Node && oItem._oGroup) {
355
+ iCurrentIndex = aGroups.indexOf(oItem._oGroup);
356
+ }
357
+
358
+ const iTargetIndex = bBackward
359
+ ? (iCurrentIndex > 0 ? iCurrentIndex - 1 : aGroups.length - 1)
360
+ : (iCurrentIndex < aGroups.length - 1 ? iCurrentIndex + 1 : 0);
361
+
362
+ // Ensure setFocus lands on the group header, not the menu button
363
+ if (this._oGraph._oFocus) {
364
+ this._oGraph._oFocus.groupInFocused = false;
365
+ }
366
+ this._oGraph.setFocus({ item: aGroups[iTargetIndex], button: null });
367
+ oEvent.preventDefault();
368
+ oEvent.stopPropagation();
369
+ };
370
+
288
371
  KeyboardNavigator.prototype._handleEnter = function () {
289
372
  var oItem, oBtn,
290
373
  oFocus = this.getFocus();
@@ -372,9 +455,32 @@ sap.ui.define([
372
455
  }
373
456
  };
374
457
 
458
+ /**
459
+ * Fires the nodeAdded event and shifts focus to the newly added node after re-render.
460
+ * Only active when DnD is enabled.
461
+ * @private
462
+ */
463
+ KeyboardNavigator.prototype._handleAddNode = function (oEvent) {
464
+ if (!this._oGraph._isDnDEnabled()) {
465
+ return;
466
+ }
467
+ this._oGraph.fireEvent("nodeAdded", {}, false);
468
+ const fnFocus = () => {
469
+ this._oGraph.detachGraphReady(fnFocus);
470
+ const aNodes = this._oGraph.getNodes();
471
+ if (aNodes.length) {
472
+ this._oGraph.setFocus({ item: aNodes.at(-1), button: null });
473
+ }
474
+ };
475
+ this._oGraph.attachGraphReady(fnFocus);
476
+ oEvent.preventDefault();
477
+ oEvent.stopPropagation();
478
+ };
479
+
375
480
  KeyboardNavigator.prototype._handleTab = function (oEvent, sDirection) {
376
- var oResourceBundle = CoreLib.getResourceBundleFor("sap.suite.ui.commons");
377
- this._oWrapperDom.setAttribute("aria-live","assertive");
481
+ const oFocus = this.getFocus(),
482
+ bBackward = sDirection === mDirections.LEFT;
483
+ this._oWrapperDom.setAttribute("aria-live", "assertive");
378
484
  this._oGraph._setAriaLabelForWrapper(oResourceBundle.getText("NETWORK_GRAPH_ACCESSIBILITY_LABEL"));
379
485
  if (this._ignoreEvent(oEvent)) {
380
486
  this._oWrapperDom.setAttribute("aria-live","off");
@@ -386,6 +492,12 @@ sap.ui.define([
386
492
  }
387
493
  return;
388
494
  } else {
495
+ if (sDirection === mDirections.LEFT && this._bWrapperHighlighted && !this.getFocus()) {
496
+ this._oWrapperDom.classList.add("sapSuiteUiCommonsNetworkGraphContentFocusHidden");
497
+ this._bWrapperHighlighted = false;
498
+ return;
499
+ }
500
+ this._bWrapperHighlighted = false;
389
501
  this._oWrapperDom.classList.add("sapSuiteUiCommonsNetworkGraphContentFocusHidden");
390
502
  }
391
503
 
@@ -396,7 +508,118 @@ sap.ui.define([
396
508
  return;
397
509
  }
398
510
 
399
- this._moveItemFocus(oEvent, sDirection);
511
+ if (oFocus?.item instanceof Node) {
512
+ const bMoved = this._navigateFromNode(oFocus.item, bBackward);
513
+ if (!bMoved && bBackward) {
514
+ const oGroup = oFocus.item._oGroup;
515
+ if (oGroup) {
516
+ this._oGraph.setFocus({ item: oGroup, button: Group.BUTTONS.COLLAPSE });
517
+ oEvent.preventDefault();
518
+ oEvent.stopPropagation();
519
+ } else {
520
+ this._moveItemFocus(oEvent, mDirections.LEFT);
521
+ }
522
+ return;
523
+ }
524
+ } else if (oFocus?.item instanceof Line) {
525
+ this._navigateFromConnector(oFocus.item, bBackward);
526
+ } else {
527
+ this._moveItemFocus(oEvent, sDirection);
528
+ return;
529
+ }
530
+
531
+ oEvent.preventDefault();
532
+ oEvent.stopPropagation();
533
+ };
534
+
535
+ /**
536
+ * Navigate from a node to its first connector.
537
+ * @returns {boolean} True if focus moved to a connector, false otherwise
538
+ * @private
539
+ */
540
+ KeyboardNavigator.prototype._navigateFromNode = function (oNode, bBackward) {
541
+ const aLines = bBackward ? oNode.getParentLines() : oNode.getChildLines();
542
+ const aVisibleLines = this._getVisibleLines(aLines, bBackward);
543
+
544
+ if (aVisibleLines.length > 0) {
545
+ this._oGraph.setFocus({
546
+ item: aVisibleLines[0],
547
+ button: null
548
+ });
549
+ return true;
550
+ }
551
+ return false;
552
+ };
553
+
554
+ /**
555
+ * Navigate from a connector to its connected node
556
+ * @param {Line} oLine - The connector to navigate from
557
+ * @param {boolean} bBackward - True for source node, false for target node
558
+ * @private
559
+ */
560
+ KeyboardNavigator.prototype._navigateFromConnector = function (oLine, bBackward) {
561
+ const oNode = bBackward ? oLine.getFromNode() : oLine.getToNode();
562
+
563
+ if (oNode && !oNode.isHidden()) {
564
+ this._oGraph.setFocus({
565
+ item: oNode,
566
+ button: null
567
+ });
568
+ }
569
+ };
570
+
571
+ /**
572
+ * Returns visible lines sorted top-to-bottom.
573
+ * @param {Array} aLines - Array of lines to filter and sort
574
+ * @param {boolean} bSortBySource - If true, sort by source node Y position; otherwise by target node
575
+ * @private
576
+ */
577
+ KeyboardNavigator.prototype._getVisibleLines = function (aLines, bSortBySource) {
578
+ if (!aLines || aLines.length === 0) {
579
+ return [];
580
+ }
581
+
582
+ const aVisibleLines = aLines.filter(function (oLine) {
583
+ return !oLine.isHidden() && !oLine._isIgnored() && oLine.getVisible();
584
+ });
585
+
586
+ const fnGetSortY = (oLine) => {
587
+ const oNode = bSortBySource ? oLine.getFromNode() : oLine.getToNode();
588
+ if (oNode && !oNode.isHidden()) {
589
+ return oNode.getY();
590
+ }
591
+ const aCoords = oLine.getCoordinates();
592
+ return (aCoords && aCoords.length > 0) ? aCoords[0].getY() : 0;
593
+ };
594
+ return aVisibleLines.sort((oLine1, oLine2) => fnGetSortY(oLine1) - fnGetSortY(oLine2));
595
+ };
596
+
597
+ KeyboardNavigator.prototype._handleAltAnnounce = function (oEvent, oNode) {
598
+ if (this._oAltNode !== oNode) {
599
+ this._oAltNode = oNode;
600
+ this._oAltConnectors = oNode._getAltNavigationConnectors();
601
+ const { aIncomingConnectors, aOutgoingConnectors, aConnectors } = this._oAltConnectors;
602
+ this._oAltAnnouncement = (aIncomingConnectors.length >= 2 || aOutgoingConnectors.length >= 2)
603
+ ? oNode._getAltConnectorsAnnouncement(aConnectors)
604
+ : null;
605
+ }
606
+ if (!this._oAltAnnouncement) { return; }
607
+ this._oGraph._setAccessibilityTitle(this._oAltAnnouncement);
608
+ oEvent.preventDefault();
609
+ oEvent.stopPropagation();
610
+ };
611
+
612
+ KeyboardNavigator.prototype._handleAltConnectorNavigation = function (oEvent, oNode, iIndex) {
613
+ if (this._oAltNode !== oNode || !this._oAltConnectors) {
614
+ this._oAltNode = oNode;
615
+ this._oAltConnectors = oNode._getAltNavigationConnectors();
616
+ }
617
+ const { aConnectors } = this._oAltConnectors;
618
+ if (iIndex < aConnectors.length) {
619
+ this._oGraph.setFocus({ item: aConnectors[iIndex].line, button: null });
620
+ oEvent.preventDefault();
621
+ oEvent.stopPropagation();
622
+ }
400
623
  };
401
624
 
402
625
  KeyboardNavigator.prototype._handleTabOverLinesWithButtons = function (oEvent, sDirection) {
@@ -624,12 +847,64 @@ sap.ui.define([
624
847
  return;
625
848
  }
626
849
 
850
+ const oFocus = this.getFocus();
851
+ const oItem = oFocus.item;
852
+ if (oItem instanceof Line) {
853
+ if (sDirection === mDirections.UP || sDirection === mDirections.DOWN) {
854
+ this._navigateBetweenConnectors(oEvent, sDirection);
855
+ return;
856
+ }
857
+ }
858
+
859
+ // For all other cases, use default behavior
627
860
  this._moveItemFocus(oEvent, sDirection, true);
628
861
  };
629
862
 
863
+ /**
864
+ * Navigate between multiple connectors from the same source node
865
+ * @private
866
+ */
867
+ KeyboardNavigator.prototype._navigateBetweenConnectors = function (oEvent, sDirection) {
868
+ const oFocus = this.getFocus(),
869
+ oLine = oFocus.item;
870
+
871
+ if (!(oLine instanceof Line)) {
872
+ return;
873
+ }
874
+
875
+ // ↑/↓ navigates among the outgoing connectors of this line's source node,
876
+ // giving a consistent vertical ordering regardless of which connector is focused.
877
+ const oSourceNode = oLine.getFromNode();
878
+ if (!oSourceNode) {
879
+ return;
880
+ }
881
+
882
+ const aVisibleLines = this._getVisibleLines(oSourceNode.getChildLines());
883
+ if (aVisibleLines.length <= 1) {
884
+ return;
885
+ }
886
+
887
+ const iCurrentIndex = aVisibleLines.indexOf(oLine);
888
+ if (iCurrentIndex === -1) {
889
+ return;
890
+ }
891
+
892
+ const iTargetIndex = sDirection === mDirections.DOWN ? iCurrentIndex + 1 : iCurrentIndex - 1;
893
+ if (iTargetIndex >= 0 && iTargetIndex < aVisibleLines.length) {
894
+ this._oGraph.setFocus({
895
+ item: aVisibleLines[iTargetIndex],
896
+ button: null
897
+ });
898
+ }
899
+
900
+ oEvent.preventDefault();
901
+ oEvent.stopPropagation();
902
+ };
903
+
630
904
  KeyboardNavigator.prototype._moveItemFocus = function (oEvent, sDirection, bStopOnLast) {
631
905
  var oFocus,
632
906
  bBackTab = (sDirection === mDirections.LEFT && oEvent.key === "Tab"),
907
+ bHadFocus = !!this.getFocus(),
633
908
  oPosition,
634
909
  oNewItem,
635
910
  aBtns;
@@ -667,6 +942,11 @@ sap.ui.define([
667
942
  if (oEvent && oNewItem) {
668
943
  oEvent.preventDefault();
669
944
  oEvent.stopPropagation();
945
+ } else if (bBackTab && !oNewItem && bHadFocus) {
946
+ this._oWrapperDom.classList.remove("sapSuiteUiCommonsNetworkGraphContentFocusHidden");
947
+ this._bWrapperHighlighted = true;
948
+ oEvent.preventDefault();
949
+ oEvent.stopPropagation();
670
950
  }
671
951
  };
672
952
 
@@ -815,6 +1095,45 @@ sap.ui.define([
815
1095
  oEvent.stopPropagation();
816
1096
  };
817
1097
 
1098
+ KeyboardNavigator.prototype._updateAssociatedDelegate = function (oControl) {
1099
+ if (this._oAssociatedContainer) {
1100
+ this._oAssociatedContainer.removeEventDelegate(this._oAssociatedDelegate);
1101
+ this._oAssociatedContainer = null;
1102
+ this._oAssociatedDelegate = null;
1103
+ }
1104
+ const oContainer = oControl && oControl.getParent();
1105
+ if (oContainer) {
1106
+ this._oAssociatedDelegate = {
1107
+ onkeydown: (oEvent) => {
1108
+ if (oEvent.keyCode === KeyCodes.ALT && oEvent.ctrlKey) {
1109
+ this._bCtrlAltDown = true;
1110
+ }
1111
+
1112
+ if (oEvent.shiftKey && oEvent.keyCode === KeyCodes.F10) {
1113
+ this._oGraph._restoreFocusToGraph();
1114
+ oEvent.preventDefault();
1115
+ oEvent.stopPropagation();
1116
+ } else if (oEvent.keyCode === KeyCodes.INSERT || (this._bCtrlAltDown && oEvent.keyCode === KeyCodes.N)) {
1117
+ this._bCtrlAltDown = false;
1118
+ this._handleAddNode(oEvent);
1119
+ }
1120
+ }
1121
+ };
1122
+ this._oAssociatedContainer = oContainer;
1123
+ oContainer.addEventDelegate(this._oAssociatedDelegate);
1124
+ }
1125
+ this._oAssociatedControl = oControl;
1126
+ };
1127
+
1128
+ KeyboardNavigator.prototype.destroy = function () {
1129
+ if (this._oAssociatedContainer) {
1130
+ this._oAssociatedContainer.removeEventDelegate(this._oAssociatedDelegate);
1131
+ this._oAssociatedContainer = null;
1132
+ this._oAssociatedDelegate = null;
1133
+ }
1134
+ BaseObject.prototype.destroy.apply(this, arguments);
1135
+ };
1136
+
818
1137
  KeyboardNavigator.prototype._ignoreEvent = function (oEvent) {
819
1138
  return !containsOrEquals(this._oWrapperDom, oEvent.target);
820
1139
  };
@@ -883,7 +883,11 @@ sap.ui.define([
883
883
  Line.prototype._getAccessibilityLabel = function (oGraph) {
884
884
  const aSentenceParts = [];
885
885
  const sConnectionType = this.getConnectionType();
886
- const sConnectionTypeText = oGraph.getConnectionTypeMapping()[sConnectionType];
886
+ const oMapping = oGraph.getConnectionTypeMapping();
887
+ // In RTL mode, left and right sides of nodes are semantically swapped
888
+ const bRTL = this.getParent()._bIsRtl;
889
+ const sResolvedType = bRTL ? (Utils.mRTLSwap[sConnectionType] || sConnectionType) : sConnectionType;
890
+ const sConnectionTypeText = oMapping[sResolvedType];
887
891
  const sFromNodeTitle = this.getFromNode().getTitle();
888
892
  const sFromNodeText = sFromNodeTitle ? sFromNodeTitle : this.getFromNode().getAltText();
889
893
  const sToNodeTitle = this.getToNode().getTitle();
@@ -1001,10 +1001,10 @@ sap.ui.define([
1001
1001
  }, false);
1002
1002
 
1003
1003
  let { style: sBorderColor, class: sBorderClass } = this._getStatusStyle({
1004
- "border-color": ElementBase.ColorType.Border,
1004
+ "outline-color": ElementBase.ColorType.Border,
1005
1005
  "background-color": this.getSelected() ? ElementBase.ColorType.SelectedBackground : "",
1006
- "border-width": ElementBase.ColorType.BorderWidth,
1007
- "border-style": ElementBase.ColorType.BorderStyle
1006
+ "outline-width": ElementBase.ColorType.BorderWidth,
1007
+ "outline-style": ElementBase.ColorType.BorderStyle
1008
1008
  }, false);
1009
1009
 
1010
1010
  mOptions.renderManager.openStart("div", sId + "-wrapperwithbuttons");
@@ -2754,9 +2754,71 @@ sap.ui.define([
2754
2754
  aSentenceParts.push(oResourceBundle.getText("NETWORK_GRAPH_NAVIGATION_SHIFT_TAB_TO_INCOMING"));
2755
2755
  }
2756
2756
 
2757
+ const { aIncomingConnectors, aOutgoingConnectors } = this._getAltNavigationConnectors();
2758
+ if (aIncomingConnectors.length >= 2 || aOutgoingConnectors.length >= 2) {
2759
+ aSentenceParts.push(oResourceBundle.getText("NETWORK_GRAPH_NAVIGATION_ALT_CONNECTORS"));
2760
+ }
2761
+
2757
2762
  return `${sLabel}. ${aSentenceParts.join(". ")}.`;
2758
2763
  };
2759
2764
 
2765
+ Node.prototype._getAltNavigationConnectors = function () {
2766
+ const iThisX = this.getX();
2767
+ const fnDir = (oNode) => oNode?.getX() < iThisX ? "left" : "right";
2768
+ const fnActive = (oLine) => oLine.getVisible() && !oLine.isHidden() && !oLine._isIgnored();
2769
+
2770
+ // Incoming: all valid lines, sorted top-to-bottom by source node Y, then by node key for stability
2771
+ const aIncomingConnectors = this.getParentLines()
2772
+ .filter((oLine) => fnActive(oLine) && oLine.getFromNode())
2773
+ .sort((a, b) => {
2774
+ const iY = a.getFromNode().getY() - b.getFromNode().getY();
2775
+ return iY !== 0 ? iY : a.getFromNode().getKey().localeCompare(b.getFromNode().getKey());
2776
+ })
2777
+ .map((oLine) => ({ line: oLine, type: "incoming", direction: fnDir(oLine.getFromNode()) }));
2778
+
2779
+ // Outgoing: all valid lines, sorted top-to-bottom by target node Y, then by node key for stability
2780
+ const aOutgoingConnectors = this.getChildLines()
2781
+ .filter((oLine) => fnActive(oLine) && oLine.getToNode())
2782
+ .sort((a, b) => {
2783
+ const iY = a.getToNode().getY() - b.getToNode().getY();
2784
+ return iY !== 0 ? iY : a.getToNode().getKey().localeCompare(b.getToNode().getKey());
2785
+ })
2786
+ .map((oLine) => ({ line: oLine, type: "outgoing", direction: fnDir(oLine.getToNode()) }));
2787
+
2788
+ const aConnectors = ["left", "right"].flatMap((sDir) => [
2789
+ ...aIncomingConnectors.filter((o) => o.direction === sDir),
2790
+ ...aOutgoingConnectors.filter((o) => o.direction === sDir)
2791
+ ]);
2792
+
2793
+ return { aIncomingConnectors, aOutgoingConnectors, aConnectors };
2794
+ };
2795
+
2796
+ Node.prototype._getAltConnectorsAnnouncement = function (aConnectors) {
2797
+ const oGraph = this.getParent();
2798
+ const oMapping = oGraph.getConnectionTypeMapping();
2799
+ const bRTL = oGraph._bIsRtl;
2800
+ return aConnectors.map((oConnector, i) => {
2801
+ const oLine = oConnector.line;
2802
+ const sConnectionType = oLine.getConnectionType();
2803
+ const sResolvedType = bRTL ? (Utils.mRTLSwap[sConnectionType] || sConnectionType) : sConnectionType;
2804
+ const sConnectionTypeText = oMapping[sResolvedType] || sResolvedType;
2805
+ const sFromNodeTitle = oLine.getFromNode().getTitle() || oLine.getFromNode().getAltText();
2806
+ const sToNodeTitle = oLine.getToNode().getTitle() || oLine.getToNode().getAltText();
2807
+ // Determine the physical port side from connection type (Left key = left port, Right key = right port)
2808
+ const sSide = oConnector.type === "outgoing"
2809
+ ? (sResolvedType.startsWith("Left") ? "left" : "right")
2810
+ : (sResolvedType.endsWith("Left") ? "left" : "right");
2811
+ return oResourceBundle.getText("NETWORK_GRAPH_NAVIGATION_ALT_CONNECTOR_ANNOUNCE", [
2812
+ oConnector.type,
2813
+ sSide,
2814
+ sConnectionTypeText,
2815
+ sFromNodeTitle,
2816
+ sToNodeTitle,
2817
+ i + 1
2818
+ ]);
2819
+ }).join(". ");
2820
+ };
2821
+
2760
2822
  Node.prototype._setStatusColors = function(sType) {
2761
2823
  var fnSetAttrColor = function(item, sStatus) {
2762
2824
  if (sStatus && item) {
@@ -2818,10 +2880,10 @@ sap.ui.define([
2818
2880
  $titleText = this.$().find(".sapSuiteUiCommonsNetworkGraphDivNodeTitleText");
2819
2881
 
2820
2882
  if(SemanticColorType.hasOwnProperty(sBackgroundColor)){
2821
- $wrapper.addClass(Utils.SEMANTIC_CLASS_NAME.BORDER + sBackgroundColor);
2883
+ $wrapper.addClass(Utils.SEMANTIC_CLASS_NAME.OUTLINE + sBackgroundColor);
2822
2884
  } else {
2823
2885
  /** @deprecated As of 1.120 */
2824
- $wrapper.css("border-color", sBorderColor);
2886
+ $wrapper.css("outline-color", sBorderColor);
2825
2887
  }
2826
2888
 
2827
2889
  let sFocusColor = this._getColor(ElementBase.ColorType[sType + "Focus"])
@@ -24,6 +24,8 @@ sap.ui.define(["sap/base/Log"], function (Log) {
24
24
  'BACKGROUND-COLOR': 'backgroundSemanticColor',
25
25
  BORDER: 'borderSemanticColor',
26
26
  'BORDER-COLOR': 'borderSemanticColor',
27
+ OUTLINE: 'outlineSemanticColor',
28
+ 'OUTLINE-COLOR': 'outlineSemanticColor',
27
29
  TEXT: 'textSemanticColor',
28
30
  FILL: 'fillSemanticColor',
29
31
  STROKE: 'strokeSemanticColor',
@@ -312,5 +314,13 @@ sap.ui.define(["sap/base/Log"], function (Log) {
312
314
  }, delay);
313
315
  };
314
316
 
317
+ // RTL swap map for connection types: mirrors Left↔Right side labels.
318
+ Utils.mRTLSwap = {
319
+ "LeftToLeft": "RightToRight",
320
+ "LeftToRight": "RightToLeft",
321
+ "RightToLeft": "LeftToRight",
322
+ "RightToRight": "LeftToLeft"
323
+ };
324
+
315
325
  return Utils;
316
326
  }, true);
@@ -21,6 +21,31 @@ sap.ui.define([
21
21
  * @since 1.144
22
22
  */
23
23
  var ConnectionPathUtils = {
24
+ /**
25
+ * Gets the effective connection type for path calculation based on RTL mode.
26
+ * In RTL mode, semantic directions are reversed since nodes are already mirrored.
27
+ *
28
+ * @param {string} sConnectionType - The configured connection type
29
+ * @param {boolean} bIsRTL - Whether RTL mode is enabled
30
+ * @returns {string} The effective connection type to use for calculations
31
+ * @private
32
+ */
33
+ _getEffectiveConnectionType: function (sConnectionType, bIsRTL) {
34
+ if (!bIsRTL) {
35
+ return sConnectionType;
36
+ }
37
+
38
+ // In RTL mode, semantic directions are reversed (nodes are already mirrored)
39
+ const rtlMapping = {
40
+ [ConnectionType.RightToLeft]: ConnectionType.LeftToRight,
41
+ [ConnectionType.LeftToRight]: ConnectionType.RightToLeft,
42
+ [ConnectionType.LeftToLeft]: ConnectionType.RightToRight,
43
+ [ConnectionType.RightToRight]: ConnectionType.LeftToLeft
44
+ };
45
+
46
+ return rtlMapping[sConnectionType] || sConnectionType;
47
+ },
48
+
24
49
  /**
25
50
  * Normalizes lines in a network graph by calculating path coordinates
26
51
  * between source and target nodes based on connection types.
@@ -40,20 +65,25 @@ sap.ui.define([
40
65
  // Enable multiple directed arrows feature when using ConnectionPathUtils
41
66
  oGraph._enableMultipleDirectedArrows = true;
42
67
 
68
+ // Detect RTL mode from graph
69
+ const bIsRTL = oGraph._bIsRtl || false;
70
+
43
71
  // Iterate over all lines in the graph
44
72
  oGraph.getLines().forEach((oLine) => {
45
- const connectionType = oLine.getConnectionType(); // Get the connection type, if not configured at Line level then the default is Right to Left from source to target.
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);
46
76
  const oSourceNode = oLine.getFromNode();
47
77
  const oTargetNode = oLine.getToNode();
48
78
  // Get anchor coordinates for source and target nodes, usually the center of the respective sides
49
- const oSourceAnchor = this._getNodeAnchorCoordinates(oLine.getFromNode(), connectionType, "source");
50
- const oTargetAnchor = this._getNodeAnchorCoordinates(oLine.getToNode(), connectionType, "target");
79
+ const oSourceAnchor = this._getNodeAnchorCoordinates(oLine.getFromNode(), effectiveConnectionType, "source");
80
+ const oTargetAnchor = this._getNodeAnchorCoordinates(oLine.getToNode(), effectiveConnectionType, "target");
51
81
  const mParams = {
52
82
  sourceAnchor: oSourceAnchor,
53
83
  targetAnchor: oTargetAnchor,
54
84
  sourceNode: oSourceNode,
55
85
  targetNode: oTargetNode,
56
- sConnectionType: connectionType,
86
+ sConnectionType: effectiveConnectionType,
57
87
  gridSize: config.gridSize,
58
88
  minEdgeLength: config.minEdgeLength
59
89
  };