@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.
- package/package.json +1 -1
- package/src/sap/suite/ui/commons/.library +1 -1
- package/src/sap/suite/ui/commons/AriaProperties.js +1 -1
- package/src/sap/suite/ui/commons/CalculationBuilder.js +26 -14
- package/src/sap/suite/ui/commons/CalculationBuilderExpression.js +2 -2
- package/src/sap/suite/ui/commons/CalculationBuilderFunction.js +1 -1
- package/src/sap/suite/ui/commons/CalculationBuilderGroup.js +1 -1
- package/src/sap/suite/ui/commons/CalculationBuilderInput.js +5 -5
- package/src/sap/suite/ui/commons/CalculationBuilderItem.js +1 -1
- package/src/sap/suite/ui/commons/CalculationBuilderValidationResult.js +1 -1
- package/src/sap/suite/ui/commons/CalculationBuilderVariable.js +1 -1
- package/src/sap/suite/ui/commons/CloudFilePicker.js +1 -3
- package/src/sap/suite/ui/commons/MicroProcessFlow.js +77 -73
- package/src/sap/suite/ui/commons/MicroProcessFlowItem.js +3 -3
- package/src/sap/suite/ui/commons/Timeline.js +9 -0
- package/src/sap/suite/ui/commons/TimelineNavigator.js +37 -5
- package/src/sap/suite/ui/commons/TimelineRenderer.js +3 -3
- package/src/sap/suite/ui/commons/collaboration/BaseHelperService.js +0 -1
- package/src/sap/suite/ui/commons/collaboration/CollaborationHelper.js +0 -3
- package/src/sap/suite/ui/commons/collaboration/CollaborationManagerService.js +0 -1
- package/src/sap/suite/ui/commons/collaboration/ContactHelper.js +7 -0
- package/src/sap/suite/ui/commons/collaboration/ContactPopover.fragment.xml +4 -1
- package/src/sap/suite/ui/commons/collaboration/MinimalContactPopover.fragment.xml +2 -1
- package/src/sap/suite/ui/commons/collaboration/TeamsHelperService.js +0 -6
- package/src/sap/suite/ui/commons/collaboration/channels/MessageChannel.js +0 -2
- package/src/sap/suite/ui/commons/collaboration/flpplugins/msplugin/Component-preload.js +1 -1
- package/src/sap/suite/ui/commons/collaboration/flpplugins/msplugin/Component.js +1 -2
- package/src/sap/suite/ui/commons/flexibility/changeHandler/PropertyChangeMapper.js +1 -1
- package/src/sap/suite/ui/commons/imageeditor/CropCustomShapeHistoryItem.js +1 -1
- package/src/sap/suite/ui/commons/imageeditor/CropEllipseHistoryItem.js +1 -1
- package/src/sap/suite/ui/commons/imageeditor/CropRectangleHistoryItem.js +1 -1
- package/src/sap/suite/ui/commons/imageeditor/CustomSizeItem.js +1 -1
- package/src/sap/suite/ui/commons/imageeditor/FilterHistoryItem.js +1 -1
- package/src/sap/suite/ui/commons/imageeditor/FlipHistoryItem.js +1 -1
- package/src/sap/suite/ui/commons/imageeditor/HistoryItem.js +1 -1
- package/src/sap/suite/ui/commons/imageeditor/ImageEditor.js +1 -1
- package/src/sap/suite/ui/commons/imageeditor/ImageEditorContainer.js +1 -1
- package/src/sap/suite/ui/commons/imageeditor/ImageEditorResponsiveContainer.js +1 -1
- package/src/sap/suite/ui/commons/imageeditor/ResizeHistoryItem.js +1 -1
- package/src/sap/suite/ui/commons/imageeditor/RotateHistoryItem.js +1 -1
- package/src/sap/suite/ui/commons/library.js +100 -3
- package/src/sap/suite/ui/commons/messagebundle.properties +76 -10
- package/src/sap/suite/ui/commons/messagebundle_ar.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_bg.properties +62 -16
- package/src/sap/suite/ui/commons/messagebundle_ca.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_cnr.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_cs.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_cy.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_da.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_de.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_el.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_en.properties +50 -7
- package/src/sap/suite/ui/commons/messagebundle_en_GB.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_en_US_saprigi.properties +54 -6
- package/src/sap/suite/ui/commons/messagebundle_es.properties +53 -7
- package/src/sap/suite/ui/commons/messagebundle_es_MX.properties +54 -8
- package/src/sap/suite/ui/commons/messagebundle_et.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_fi.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_fr.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_fr_CA.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_hi.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_hr.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_hu.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_id.properties +60 -14
- package/src/sap/suite/ui/commons/messagebundle_it.properties +53 -7
- package/src/sap/suite/ui/commons/messagebundle_iw.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_ja.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_kk.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_ko.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_lt.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_lv.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_mk.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_ms.properties +59 -13
- package/src/sap/suite/ui/commons/messagebundle_nl.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_no.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_pl.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_pt.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_pt_PT.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_ro.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_ru.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_sh.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_sk.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_sl.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_sr.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_sv.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_th.properties +52 -6
- package/src/sap/suite/ui/commons/messagebundle_tr.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_uk.properties +53 -7
- package/src/sap/suite/ui/commons/messagebundle_vi.properties +55 -9
- package/src/sap/suite/ui/commons/messagebundle_zh_CN.properties +51 -5
- package/src/sap/suite/ui/commons/messagebundle_zh_TW.properties +53 -7
- package/src/sap/suite/ui/commons/networkgraph/ElementBase.js +19 -1
- package/src/sap/suite/ui/commons/networkgraph/Graph.js +371 -29
- package/src/sap/suite/ui/commons/networkgraph/GraphRenderer.js +23 -10
- package/src/sap/suite/ui/commons/networkgraph/Group.js +43 -22
- package/src/sap/suite/ui/commons/networkgraph/KeyboardNavigator.js +54 -6
- package/src/sap/suite/ui/commons/networkgraph/Line.js +736 -31
- package/src/sap/suite/ui/commons/networkgraph/Node.js +546 -96
- package/src/sap/suite/ui/commons/networkgraph/Tooltip.js +5 -0
- package/src/sap/suite/ui/commons/networkgraph/layout/NoopLayout.js +28 -5
- package/src/sap/suite/ui/commons/networkgraph/util/ConnectionPathUtils.js +1144 -0
- package/src/sap/suite/ui/commons/networkgraph/util/CreateConnectionPopover.js +374 -0
- package/src/sap/suite/ui/commons/networkgraph/util/DependencyLayoutHelper.js +870 -0
- package/src/sap/suite/ui/commons/networkgraph/util/DragDropManager.js +563 -41
- package/src/sap/suite/ui/commons/networkgraph/util/PortManager.js +573 -0
- package/src/sap/suite/ui/commons/statusindicator/Circle.js +1 -1
- package/src/sap/suite/ui/commons/statusindicator/CustomShape.js +1 -1
- package/src/sap/suite/ui/commons/statusindicator/DiscreteThreshold.js +1 -1
- package/src/sap/suite/ui/commons/statusindicator/FillingOption.js +1 -1
- package/src/sap/suite/ui/commons/statusindicator/LibraryShape.js +1 -1
- package/src/sap/suite/ui/commons/statusindicator/Path.js +1 -1
- package/src/sap/suite/ui/commons/statusindicator/PropertyThreshold.js +1 -1
- package/src/sap/suite/ui/commons/statusindicator/Rectangle.js +1 -1
- package/src/sap/suite/ui/commons/statusindicator/Shape.js +1 -1
- package/src/sap/suite/ui/commons/statusindicator/ShapeGroup.js +1 -1
- package/src/sap/suite/ui/commons/statusindicator/SimpleShape.js +1 -1
- package/src/sap/suite/ui/commons/statusindicator/StatusIndicator.js +1 -1
- package/src/sap/suite/ui/commons/taccount/TAccount.js +1 -1
- package/src/sap/suite/ui/commons/taccount/TAccountGroup.js +1 -1
- package/src/sap/suite/ui/commons/taccount/TAccountItem.js +1 -1
- package/src/sap/suite/ui/commons/taccount/TAccountItemProperty.js +1 -1
- package/src/sap/suite/ui/commons/taccount/TAccountPanel.js +1 -1
- package/src/sap/suite/ui/commons/themes/base/NetworkGraph.less +13 -13
- package/src/sap/suite/ui/commons/themes/base/NetworkGroup.less +34 -2
- package/src/sap/suite/ui/commons/themes/base/NetworkLine.less +58 -13
- package/src/sap/suite/ui/commons/themes/base/NetworkNode.less +206 -1
|
@@ -21,12 +21,17 @@ sap.ui.define([
|
|
|
21
21
|
ArrowOrientation = library.networkgraph.LineArrowOrientation,
|
|
22
22
|
Shape = library.networkgraph.NodeShape,
|
|
23
23
|
Orientation = library.networkgraph.Orientation,
|
|
24
|
-
SemanticColorType = library.SemanticColorType
|
|
24
|
+
SemanticColorType = library.SemanticColorType,
|
|
25
|
+
ConnectionType = library.networkgraph.ConnectionType;
|
|
25
26
|
|
|
26
27
|
var BEND_RADIUS = 6, // Bezier 'radius' of smooth bends
|
|
27
28
|
FOCUS_LANE_WIDTH = 5, // Distance of focus shadow line from the main line
|
|
28
29
|
RELATIVE_ARROW_POSITION = 0.45,
|
|
29
30
|
FIXED_ARROW_POSITION = 15,
|
|
31
|
+
MULTIPLE_ARROW_DISTANCE = 44,
|
|
32
|
+
LABEL_BOX_WIDTH = 30,
|
|
33
|
+
LABEL_BOX_HEIGHT = 22,
|
|
34
|
+
ARROW_LENGTH = 11, // Length of arrow from apex to tail
|
|
30
35
|
ZERO_ANGLE_ARROW_POINTS = {
|
|
31
36
|
Apex: {x: 5.5, y: 0},
|
|
32
37
|
Second: {x: -5.5, y: -7.5},
|
|
@@ -117,6 +122,34 @@ sap.ui.define([
|
|
|
117
122
|
*/
|
|
118
123
|
stretchToCenter: {
|
|
119
124
|
type: "boolean", group: "Misc", defaultValue: false
|
|
125
|
+
},
|
|
126
|
+
/**
|
|
127
|
+
* Defines the connection type between the source and target nodes.
|
|
128
|
+
* Can be set to RightToLeft, LeftToRight, TopToBottom, or BottomToTop using {@link sap.suite.ui.commons.networkgraph.ConnectionType ConnectionType} enumeration.
|
|
129
|
+
* This property is used to determine how the line connects the source and target nodes. For example, if set to RightToLeft, the line starts from the right side of the source node and end at the left side of the target node.
|
|
130
|
+
* This property is valid only with the NoopLayout and drag and drop enabled network graph.
|
|
131
|
+
* @public
|
|
132
|
+
* @since 1.144
|
|
133
|
+
*
|
|
134
|
+
*/
|
|
135
|
+
connectionType: {
|
|
136
|
+
type: "sap.suite.ui.commons.networkgraph.ConnectionType", defaultValue: ConnectionType.RightToLeft
|
|
137
|
+
},
|
|
138
|
+
/**
|
|
139
|
+
* Defines the text label to be displayed on the line.
|
|
140
|
+
*
|
|
141
|
+
* When set, displays the label text in a rounded rectangle box positioned near the target arrow.
|
|
142
|
+
* The label is rendered only when all of the following conditions are met:
|
|
143
|
+
* - This property has a non-empty value
|
|
144
|
+
* - The graph is using NoopLayout
|
|
145
|
+
* - The line's arrowPosition is set to "Both"
|
|
146
|
+
* - The line is long enough to accommodate the label
|
|
147
|
+
* If this property is empty or not set, no label is displayed on the line
|
|
148
|
+
* @public
|
|
149
|
+
* @since 1.144.
|
|
150
|
+
*/
|
|
151
|
+
labelName: {
|
|
152
|
+
type: "string", group: "Behavior", defaultValue: ""
|
|
120
153
|
}
|
|
121
154
|
},
|
|
122
155
|
aggregations: {
|
|
@@ -192,7 +225,10 @@ sap.ui.define([
|
|
|
192
225
|
/* =========================================================== */
|
|
193
226
|
Line.prototype._afterRendering = function () {
|
|
194
227
|
this._setupEvents();
|
|
195
|
-
if
|
|
228
|
+
// Only check node visibility if data is fully loaded
|
|
229
|
+
var oFromNode = this.getFromNode();
|
|
230
|
+
var oToNode = this.getToNode();
|
|
231
|
+
if (oFromNode && oToNode && (oFromNode._bIsHidden || oToNode._bIsHidden)) {
|
|
196
232
|
this.$().hide();
|
|
197
233
|
}
|
|
198
234
|
|
|
@@ -231,18 +267,20 @@ sap.ui.define([
|
|
|
231
267
|
}.bind(this);
|
|
232
268
|
|
|
233
269
|
var fnCreateArrowAttr = function (iIndex, aPoints, sArrowId) {
|
|
270
|
+
var sArrowClass = "sapSuiteUiCommonsNetworkLineArrow";
|
|
271
|
+
if (sColorClass) {
|
|
272
|
+
sArrowClass += " " + sColorClass;
|
|
273
|
+
}
|
|
234
274
|
return {
|
|
235
275
|
id: sId + "-" + sArrowId,
|
|
236
|
-
"class":
|
|
276
|
+
"class": sArrowClass,
|
|
237
277
|
style: sColorStyle,
|
|
238
278
|
d: "M " + aPoints[iIndex + 0].x + "," + aPoints[iIndex + 0].y +
|
|
239
279
|
" L " + aPoints[iIndex + 1].x + "," + aPoints[iIndex + 1].y +
|
|
240
280
|
" L " + aPoints[iIndex + 2].x + "," + aPoints[iIndex + 2].y +
|
|
241
281
|
" Z"
|
|
242
282
|
};
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
var fnRenderArrow = function (sOrientation, sPosition, sArrowId) {
|
|
283
|
+
}; var fnRenderArrow = function (sOrientation, sPosition, sArrowId) {
|
|
246
284
|
var aPoints = this._getArrowPoints(sOrientation, sPosition);
|
|
247
285
|
this._renderControl("path", fnCreateArrowAttr(0, aPoints, sArrowId || "arrow"), null, oRm);
|
|
248
286
|
|
|
@@ -300,13 +338,57 @@ sap.ui.define([
|
|
|
300
338
|
"data-sap-ui": sId
|
|
301
339
|
}, false, oRm);
|
|
302
340
|
|
|
341
|
+
// Add title for tooltip showing connection type (always shown)
|
|
342
|
+
if (this.getParent()._isNoopLayout() &&
|
|
343
|
+
this.getArrowPosition() === ArrowPosition.Both) {
|
|
344
|
+
|
|
345
|
+
var sConnectionType = this.getConnectionType();
|
|
346
|
+
var oMapping = this.getParent().getConnectionTypeMapping();
|
|
347
|
+
var sTooltipText = "";
|
|
348
|
+
|
|
349
|
+
if (sConnectionType === ConnectionType.LeftToRight) {
|
|
350
|
+
sTooltipText = oMapping.LeftToRight || ConnectionType.LeftToRight;
|
|
351
|
+
} else if (sConnectionType === ConnectionType.RightToLeft) {
|
|
352
|
+
sTooltipText = oMapping.RightToLeft || ConnectionType.RightToLeft;
|
|
353
|
+
} else if (sConnectionType === ConnectionType.LeftToLeft) {
|
|
354
|
+
sTooltipText = oMapping.LeftToLeft || ConnectionType.LeftToLeft;
|
|
355
|
+
} else if (sConnectionType === ConnectionType.RightToRight) {
|
|
356
|
+
sTooltipText = oMapping.RightToRight || ConnectionType.RightToRight;
|
|
357
|
+
} else {
|
|
358
|
+
sTooltipText = oMapping.RightToLeft || ConnectionType.RightToLeft;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (sTooltipText) {
|
|
362
|
+
oRm.openStart("title");
|
|
363
|
+
oRm.openEnd();
|
|
364
|
+
oRm.text(sTooltipText);
|
|
365
|
+
oRm.close("title");
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
303
369
|
// invisible wrapper for better event handling
|
|
304
370
|
fnRenderPath("sapSuiteUiCommonsNetworkLineInvisibleWrapper", "invisibleWrapper", true);
|
|
305
371
|
|
|
306
372
|
// path itself
|
|
307
|
-
|
|
373
|
+
fnRenderPath("", "path");
|
|
308
374
|
|
|
309
|
-
if (this.
|
|
375
|
+
if (this.getArrowPosition() === ArrowPosition.Both && this._hasMultipleDirectedArrows() && this.getCoordinates().length >= 2 ) {
|
|
376
|
+
// Calculate total line length to determine arrow placement strategy
|
|
377
|
+
var fTotalLineLength = this._calculateTotalLineLength();
|
|
378
|
+
var bCanFitTwoArrows = this._canLineFitTwoArrows(fTotalLineLength);
|
|
379
|
+
|
|
380
|
+
if (bCanFitTwoArrows) {
|
|
381
|
+
// Line is long enough for both arrows - use smart source arrow logic
|
|
382
|
+
if (this._shouldRenderSourceArrow()) {
|
|
383
|
+
fnRenderArrow(ArrowOrientation.ParentOf, "multipleSource", "arrow-source");
|
|
384
|
+
}
|
|
385
|
+
// Always render target arrow (each incoming line gets its own target arrow)
|
|
386
|
+
fnRenderArrow(ArrowOrientation.ParentOf, "multipleTarget", "arrow-target");
|
|
387
|
+
} else {
|
|
388
|
+
// Line is too short for two arrows - only render target arrow
|
|
389
|
+
fnRenderArrow(ArrowOrientation.ParentOf, "multipleTarget", "arrow-target");
|
|
390
|
+
}
|
|
391
|
+
} else if (this.getArrowOrientation() !== ArrowOrientation.None && this.getCoordinates().length >= 2) {
|
|
310
392
|
if (this.getArrowOrientation() === ArrowOrientation.Both) {
|
|
311
393
|
if (this.getArrowPosition() === ArrowPosition.Middle) {
|
|
312
394
|
// middle arrow is rendered "at once" using two groups of points
|
|
@@ -325,18 +407,184 @@ sap.ui.define([
|
|
|
325
407
|
var sColorStyleText = sColorStyle ? sColorStyle : "";
|
|
326
408
|
this._aNipples.forEach(function (oNipple) {
|
|
327
409
|
oRm.openStart("path");
|
|
328
|
-
this.applyStyles(oRm,this.getStyleObject(sColorStyleText));
|
|
410
|
+
this.applyStyles(oRm, this.getStyleObject(sColorStyleText));
|
|
329
411
|
oRm.class("sapSuiteUiCommonsNetworkLineNipple");
|
|
330
412
|
((sColorClass || "").split(" ") || []).forEach((sClass) => {
|
|
331
|
-
|
|
413
|
+
if (sClass) {
|
|
414
|
+
oRm.class(sClass);
|
|
415
|
+
}
|
|
332
416
|
})
|
|
333
|
-
oRm.attr("d",fnCreateArc(oNipple.x, oNipple.y, oNipple.orientation));
|
|
417
|
+
oRm.attr("d", fnCreateArc(oNipple.x, oNipple.y, oNipple.orientation));
|
|
334
418
|
oRm.openEnd();
|
|
335
419
|
oRm.close("path");
|
|
336
420
|
}.bind(this));
|
|
421
|
+
} oRm.close("g");
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Renders the text label for this line. Used by batch text rendering.
|
|
426
|
+
* @param {sap.ui.core.RenderManager} oRm The RenderManager to use for rendering
|
|
427
|
+
* @param {string} [sIdSuffix] Optional suffix for element ID
|
|
428
|
+
* @private
|
|
429
|
+
*/
|
|
430
|
+
Line.prototype._renderLineText = function (oRm, sIdSuffix) {
|
|
431
|
+
// Early return if any condition is not met:
|
|
432
|
+
// - labelName has a non-empty value
|
|
433
|
+
// - Graph must be in NoopLayout
|
|
434
|
+
// - arrowPosition must be Both (labels only shown when both arrows are present)
|
|
435
|
+
if (!this.getLabelName() ||
|
|
436
|
+
!this.getParent()._isNoopLayout() ||
|
|
437
|
+
this.getArrowPosition() !== ArrowPosition.Both) {
|
|
438
|
+
return;
|
|
337
439
|
}
|
|
338
440
|
|
|
339
|
-
|
|
441
|
+
// Always use the base line ID without any suffix to ensure consistency
|
|
442
|
+
// with _hideShowLineText which can't know what suffix was used during rendering
|
|
443
|
+
var sId = this.getId();
|
|
444
|
+
var {style: sStyle, class: sStatusClass} = this._getStatusStyle({
|
|
445
|
+
"stroke": ElementBase.ColorType.Border,
|
|
446
|
+
"stroke-width": ElementBase.ColorType.BorderWidth,
|
|
447
|
+
"stroke-dasharray": ElementBase.ColorType.BorderStyle
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Get the connection type and convert to display text
|
|
451
|
+
var sConnectionType = this.getConnectionType();
|
|
452
|
+
var oMapping = this.getParent().getConnectionTypeMapping();
|
|
453
|
+
var sFullText = ""; // Store the full text for tooltip
|
|
454
|
+
|
|
455
|
+
// Check if custom mapping exists for this connection type
|
|
456
|
+
switch (sConnectionType) {
|
|
457
|
+
case ConnectionType.LeftToRight:
|
|
458
|
+
sFullText = oMapping.LeftToRight || ConnectionType.LeftToRight;
|
|
459
|
+
break;
|
|
460
|
+
case ConnectionType.RightToLeft:
|
|
461
|
+
sFullText = oMapping.RightToLeft || ConnectionType.RightToLeft;
|
|
462
|
+
break;
|
|
463
|
+
case ConnectionType.LeftToLeft:
|
|
464
|
+
sFullText = oMapping.LeftToLeft || ConnectionType.LeftToLeft;
|
|
465
|
+
break;
|
|
466
|
+
case ConnectionType.RightToRight:
|
|
467
|
+
sFullText = oMapping.RightToRight || ConnectionType.RightToRight;
|
|
468
|
+
break;
|
|
469
|
+
default:
|
|
470
|
+
sFullText = oMapping.RightToLeft || ConnectionType.RightToLeft;
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Calculate position near the ingoing arrow (end of the line)
|
|
475
|
+
// When ArrowPosition.Both, the target arrow is positioned at MULTIPLE_ARROW_DISTANCE (44px) from the end
|
|
476
|
+
var oCoords = this.getCoordinates();
|
|
477
|
+
if (oCoords && oCoords.length >= 2) {
|
|
478
|
+
var aFragmentLengthSum = [], fTotalDistance = 0;
|
|
479
|
+
var iLastIndex = oCoords.length - 1;
|
|
480
|
+
|
|
481
|
+
// Calculate cumulative distances along the path
|
|
482
|
+
for (var i = 0; i < iLastIndex; i++) {
|
|
483
|
+
if (oCoords[i].getX() === oCoords[i + 1].getX()) {
|
|
484
|
+
// Vertical line - only Y changes
|
|
485
|
+
fTotalDistance += Math.abs(oCoords[i + 1].getY() - oCoords[i].getY());
|
|
486
|
+
} else if (oCoords[i].getY() === oCoords[i + 1].getY()) {
|
|
487
|
+
// Horizontal line - only X changes
|
|
488
|
+
fTotalDistance += Math.abs(oCoords[i + 1].getX() - oCoords[i].getX());
|
|
489
|
+
} else {
|
|
490
|
+
// Diagonal line - use Pythagorean theorem If Lines are plotted Diagonally
|
|
491
|
+
fTotalDistance += Math.sqrt(
|
|
492
|
+
Math.pow(oCoords[i + 1].getX() - oCoords[i].getX(), 2) +
|
|
493
|
+
Math.pow(oCoords[i + 1].getY() - oCoords[i].getY(), 2)
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
// Store cumulative distance
|
|
497
|
+
aFragmentLengthSum.push(fTotalDistance);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Check if there's enough space for the label box with 8px padding from target arrow
|
|
501
|
+
// When line is long enough for both arrows, check gap between them
|
|
502
|
+
// When line is short, source arrow is hidden and target arrow position is adjusted
|
|
503
|
+
var bCanFitTwoArrows = this._canLineFitTwoArrows(fTotalDistance);
|
|
504
|
+
var fTargetArrowDistance;
|
|
505
|
+
var fAvailableSpace;
|
|
506
|
+
|
|
507
|
+
if (bCanFitTwoArrows) {
|
|
508
|
+
// Both arrows present at fixed positions
|
|
509
|
+
fTargetArrowDistance = MULTIPLE_ARROW_DISTANCE;
|
|
510
|
+
// Available space = total - (source position + source arrow + target position + target arrow + 8px padding)
|
|
511
|
+
fAvailableSpace = fTotalDistance - (MULTIPLE_ARROW_DISTANCE + ARROW_LENGTH + MULTIPLE_ARROW_DISTANCE + ARROW_LENGTH + 8);
|
|
512
|
+
} else {
|
|
513
|
+
// Only target arrow present
|
|
514
|
+
fTargetArrowDistance = Math.min(MULTIPLE_ARROW_DISTANCE, fTotalDistance * 0.3);
|
|
515
|
+
// Available space = total - (target position + target arrow + 8px padding)
|
|
516
|
+
fAvailableSpace = fTotalDistance - (fTargetArrowDistance + ARROW_LENGTH + 8);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
var fRequiredSpace = LABEL_BOX_WIDTH; // 30px box width
|
|
520
|
+
if (fAvailableSpace < fRequiredSpace) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Position label so its end is 8px away from the target arrow
|
|
525
|
+
var fTargetDistance = fTotalDistance - fTargetArrowDistance - 8 - LABEL_BOX_WIDTH; // arrow position + 8px offset + full box width
|
|
526
|
+
var iTargetIndex = 0;
|
|
527
|
+
for (var j = 0; j < iLastIndex; j++) {
|
|
528
|
+
if (aFragmentLengthSum[j] >= fTargetDistance) {
|
|
529
|
+
iTargetIndex = j;
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Calculate the exact position on the segment
|
|
535
|
+
var fSegmentStart = iTargetIndex > 0 ? aFragmentLengthSum[iTargetIndex - 1] : 0;
|
|
536
|
+
var fSegmentLength = aFragmentLengthSum[iTargetIndex] - fSegmentStart;
|
|
537
|
+
var fPositionInSegment = (fTargetDistance - fSegmentStart) / fSegmentLength;
|
|
538
|
+
var fLabelX = oCoords[iTargetIndex].getX() +
|
|
539
|
+
(oCoords[iTargetIndex + 1].getX() - oCoords[iTargetIndex].getX()) * fPositionInSegment;
|
|
540
|
+
var fLabelY = oCoords[iTargetIndex].getY() +
|
|
541
|
+
(oCoords[iTargetIndex + 1].getY() - oCoords[iTargetIndex].getY()) * fPositionInSegment;
|
|
542
|
+
|
|
543
|
+
// Calculate box position (centered on label coordinates)
|
|
544
|
+
var fBoxX = fLabelX - LABEL_BOX_WIDTH / 2;
|
|
545
|
+
var fBoxY = fLabelY - LABEL_BOX_HEIGHT / 2;
|
|
546
|
+
|
|
547
|
+
// Create a group to contain both rectangle and text
|
|
548
|
+
oRm.openStart("g");
|
|
549
|
+
oRm.class("sapSuiteUiCommonsNetworkLineTextBox");
|
|
550
|
+
oRm.attr("id", sId + "-textbox-middle");
|
|
551
|
+
oRm.attr("data-line-id", this._getLineId());
|
|
552
|
+
oRm.openEnd();
|
|
553
|
+
|
|
554
|
+
// Add title for tooltip on the label box group
|
|
555
|
+
oRm.openStart("title");
|
|
556
|
+
oRm.openEnd();
|
|
557
|
+
oRm.text(sFullText);
|
|
558
|
+
oRm.close("title");
|
|
559
|
+
|
|
560
|
+
// Render the rectangle background
|
|
561
|
+
oRm.openStart("rect");
|
|
562
|
+
oRm.class("sapSuiteUiCommonsNetworkLineTextBoxBackground");
|
|
563
|
+
if (sStatusClass) {
|
|
564
|
+
oRm.class(sStatusClass);
|
|
565
|
+
}
|
|
566
|
+
//this.applyStyles(oRm, this.getStyleObject(sStyle)); //ToDo: to be applied if Box should also adhere to status styles
|
|
567
|
+
oRm.attr("x", fBoxX);
|
|
568
|
+
oRm.attr("y", fBoxY);
|
|
569
|
+
oRm.attr("width", LABEL_BOX_WIDTH);
|
|
570
|
+
oRm.attr("height", LABEL_BOX_HEIGHT);
|
|
571
|
+
oRm.attr("rx", 8); // Rounded corners
|
|
572
|
+
oRm.attr("ry", 8);
|
|
573
|
+
oRm.openEnd();
|
|
574
|
+
oRm.close("rect");
|
|
575
|
+
|
|
576
|
+
// Render the text inside the rectangle
|
|
577
|
+
oRm.openStart("text");
|
|
578
|
+
oRm.class("sapSuiteUiCommonsNetworkLineText");
|
|
579
|
+
oRm.attr("x", fLabelX);
|
|
580
|
+
oRm.attr("y", fLabelY); // Center vertically in the box
|
|
581
|
+
oRm.openEnd();
|
|
582
|
+
oRm.text(this.getLabelName());
|
|
583
|
+
oRm.close("text");
|
|
584
|
+
|
|
585
|
+
// Close the group
|
|
586
|
+
oRm.close("g");
|
|
587
|
+
}
|
|
340
588
|
};
|
|
341
589
|
|
|
342
590
|
Line.prototype._renderFocusWrapper = function () {
|
|
@@ -346,7 +594,19 @@ sap.ui.define([
|
|
|
346
594
|
"class": "sapSuiteUiCommonsNetworkLineFocus"
|
|
347
595
|
});
|
|
348
596
|
|
|
349
|
-
|
|
597
|
+
// Find the first text box group to insert before it
|
|
598
|
+
var oContainer = this.getDomRef();
|
|
599
|
+
var oFirstTextBox = oContainer ? oContainer.querySelector(".sapSuiteUiCommonsNetworkLineTextBox") : null;
|
|
600
|
+
|
|
601
|
+
if (oFirstTextBox) {
|
|
602
|
+
// Insert the focus line before the first text box group
|
|
603
|
+
oContainer.insertBefore(oPath, oFirstTextBox);
|
|
604
|
+
} else {
|
|
605
|
+
// Fallback: append to end if no text boxes found
|
|
606
|
+
if (oContainer) {
|
|
607
|
+
oContainer.appendChild(oPath);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
350
610
|
}.bind(this);
|
|
351
611
|
|
|
352
612
|
if (!this._bFocusRendered) {
|
|
@@ -433,6 +693,33 @@ sap.ui.define([
|
|
|
433
693
|
|
|
434
694
|
sPosition = sPosition || this.getArrowPosition();
|
|
435
695
|
|
|
696
|
+
// Handle multiple directed arrows positioning
|
|
697
|
+
if (sPosition === "multipleSource" || sPosition === "multipleTarget") {
|
|
698
|
+
// Check if line is long enough for the requested arrow position
|
|
699
|
+
var fTotalLineLength = this._calculateTotalLineLength();
|
|
700
|
+
var bCanFitTwoArrows = this._canLineFitTwoArrows(fTotalLineLength);
|
|
701
|
+
|
|
702
|
+
// If line is too short and we're trying to render source arrow, skip it
|
|
703
|
+
if (!bCanFitTwoArrows && sPosition === "multipleSource") {
|
|
704
|
+
// Return default fallback to avoid errors
|
|
705
|
+
return {
|
|
706
|
+
center: {x: oCoords[0].getX(), y: oCoords[0].getY()},
|
|
707
|
+
apex: {x: oCoords[1].getX(), y: oCoords[1].getY()}
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
// Find optimal segment by traveling 44px along the line path
|
|
711
|
+
var sDirection = sPosition === "multipleSource" ? "forward" : "backward";
|
|
712
|
+
var fDistance = bCanFitTwoArrows ? MULTIPLE_ARROW_DISTANCE : Math.min(MULTIPLE_ARROW_DISTANCE, fTotalLineLength * 0.3);
|
|
713
|
+
var oSegmentInfo = this._findOptimalArrowSegment(sDirection, fDistance);
|
|
714
|
+
|
|
715
|
+
return {
|
|
716
|
+
center: oSegmentInfo.center,
|
|
717
|
+
apex: oSegmentInfo.apex,
|
|
718
|
+
segmentRatio: oSegmentInfo.ratio,
|
|
719
|
+
isMultipleDirected: true
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
|
|
436
723
|
if (this.getBends().length === 0) {
|
|
437
724
|
iHolyIndex = 0;
|
|
438
725
|
} else if (sPosition === ArrowPosition.Start) {
|
|
@@ -529,8 +816,14 @@ sap.ui.define([
|
|
|
529
816
|
|
|
530
817
|
var fnCalcArrowPoint = function (oArrowVertex) {
|
|
531
818
|
var fFixedPosition = FIXED_ARROW_POSITION;
|
|
532
|
-
//
|
|
533
|
-
if (sPosition ===
|
|
819
|
+
// Handle multiple directed arrows with precise positioning along segments
|
|
820
|
+
if (sPosition === "multipleSource" || sPosition === "multipleTarget") {
|
|
821
|
+
// Use the exact position calculated to be precisely 44px away
|
|
822
|
+
oArrowCenter = oFragVector.exactPosition || {
|
|
823
|
+
x: oFragVector.center.x + (oFragVector.apex.x - oFragVector.center.x) * (oFragVector.segmentRatio || 0.5),
|
|
824
|
+
y: oFragVector.center.y + (oFragVector.apex.y - oFragVector.center.y) * (oFragVector.segmentRatio || 0.5)
|
|
825
|
+
};
|
|
826
|
+
} else if (sPosition === ArrowPosition.Middle) { // First calculate where the center of the arrow is
|
|
534
827
|
oArrowCenter = {
|
|
535
828
|
x: (oFragVector.apex.x - oFragVector.center.x) * RELATIVE_ARROW_POSITION + oFragVector.center.x,
|
|
536
829
|
y: (oFragVector.apex.y - oFragVector.center.y) * RELATIVE_ARROW_POSITION + oFragVector.center.y
|
|
@@ -584,19 +877,43 @@ sap.ui.define([
|
|
|
584
877
|
/**
|
|
585
878
|
* @private
|
|
586
879
|
*/
|
|
587
|
-
Line.prototype._getAccessibilityLabel = function () {
|
|
588
|
-
|
|
589
|
-
|
|
880
|
+
Line.prototype._getAccessibilityLabel = function (oGraph) {
|
|
881
|
+
const aSentenceParts = [];
|
|
882
|
+
const sConnectionType = this.getConnectionType();
|
|
883
|
+
const sConnectionTypeText = oGraph.getConnectionTypeMapping()[sConnectionType];
|
|
884
|
+
const sFromNodeTitle = this.getFromNode().getTitle();
|
|
885
|
+
const sFromNodeText = sFromNodeTitle ? sFromNodeTitle : this.getFromNode().getAltText();
|
|
886
|
+
const sToNodeTitle = this.getToNode().getTitle();
|
|
887
|
+
const sToNodeText = sToNodeTitle ? sToNodeTitle : this.getToNode().getAltText();
|
|
888
|
+
let sConnectionText = oResourceBundle.getText('NETWORK_GRAPH_LINE_ACCESSIBILITY_CONNECTION_FROM_TO', [sFromNodeText, sToNodeText]);
|
|
889
|
+
if (sConnectionTypeText) {
|
|
890
|
+
sConnectionText = `${sConnectionTypeText} ${sConnectionText.charAt(0).toLowerCase()}${sConnectionText.slice(1)}`;
|
|
891
|
+
}
|
|
892
|
+
aSentenceParts.push(sConnectionText);
|
|
590
893
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
894
|
+
const sStatusText = this._getStatusText(oGraph._oStatuses);
|
|
895
|
+
|
|
896
|
+
if (sStatusText) {
|
|
897
|
+
aSentenceParts.push(
|
|
898
|
+
`${oResourceBundle.getText('NETWORK_GRAPH_LINE_ACCESSIBILITY_STATUS')} ${sStatusText}`
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
if (this.getSelected()) {
|
|
903
|
+
aSentenceParts.push(oResourceBundle.getText('NETWORK_GRAPH_SELECTED_NODE'));
|
|
598
904
|
}
|
|
599
|
-
|
|
905
|
+
|
|
906
|
+
aSentenceParts.push(oResourceBundle.getText('NETWORK_GRAPH_ACCESSIBILITY_TOGGLE_STATE'));
|
|
907
|
+
|
|
908
|
+
const iIncomingConnectors = this.getFromNode().getChildLines().length;
|
|
909
|
+
if (iIncomingConnectors > 1) {
|
|
910
|
+
aSentenceParts.push(oResourceBundle.getText('NETWORK_GRAPH_LINE_NAVIGATION_ARROW_KEYS', [sFromNodeText]));
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
aSentenceParts.push(oResourceBundle.getText('NETWORK_GRAPH_LINE_NAVIGATION_TAB_TO_TARGET', [sToNodeText]));
|
|
914
|
+
aSentenceParts.push(oResourceBundle.getText('NETWORK_GRAPH_LINE_NAVIGATION_SHIFT_TAB_TO_SOURCE', [sFromNodeText]));
|
|
915
|
+
|
|
916
|
+
return aSentenceParts.join('. ');
|
|
600
917
|
};
|
|
601
918
|
|
|
602
919
|
/* =========================================================== */
|
|
@@ -794,10 +1111,39 @@ sap.ui.define([
|
|
|
794
1111
|
$line.on("mouseout", function (oEvent) {
|
|
795
1112
|
this._mouseOut();
|
|
796
1113
|
}.bind(this));
|
|
1114
|
+
|
|
1115
|
+
// Setup events for text box
|
|
1116
|
+
var oParent = this.getParent();
|
|
1117
|
+
if (oParent) {
|
|
1118
|
+
var sLineId = this._getLineId();
|
|
1119
|
+
var $textBox = oParent.$("line-texts").find('[data-line-id="' + sLineId + '"]');
|
|
1120
|
+
|
|
1121
|
+
$textBox.on("click", function (oEvent) {
|
|
1122
|
+
this._click({
|
|
1123
|
+
ctrlKey: oEvent.ctrlKey,
|
|
1124
|
+
clientX: oEvent.clientX,
|
|
1125
|
+
clientY: oEvent.clientY
|
|
1126
|
+
});
|
|
1127
|
+
}.bind(this));
|
|
1128
|
+
|
|
1129
|
+
$textBox.on("mouseover", function (oEvent) {
|
|
1130
|
+
this._mouseOver();
|
|
1131
|
+
}.bind(this));
|
|
1132
|
+
|
|
1133
|
+
$textBox.on("mouseout", function (oEvent) {
|
|
1134
|
+
this._mouseOut();
|
|
1135
|
+
}.bind(this));
|
|
1136
|
+
}
|
|
797
1137
|
};
|
|
798
1138
|
|
|
799
1139
|
Line.prototype._mouseOut = function () {
|
|
800
1140
|
this.$().removeClass(this.HIGHLIGHT_CLASS);
|
|
1141
|
+
// Remove highlight class from text box using data-line-id attribute
|
|
1142
|
+
var oParent = this.getParent();
|
|
1143
|
+
if (oParent) {
|
|
1144
|
+
var sLineId = this._getLineId();
|
|
1145
|
+
oParent.$("line-texts").find('[data-line-id="' + sLineId + '"]').removeClass(this.HIGHLIGHT_CLASS);
|
|
1146
|
+
}
|
|
801
1147
|
if (!this.getSelected()) {
|
|
802
1148
|
this._setStatusColors("");
|
|
803
1149
|
}
|
|
@@ -809,6 +1155,12 @@ sap.ui.define([
|
|
|
809
1155
|
if (!this.getSelected() && bExecuteDefault) {
|
|
810
1156
|
this._setStatusColors("Hover");
|
|
811
1157
|
this.$().addClass(this.HIGHLIGHT_CLASS);
|
|
1158
|
+
// Add highlight class to text box using data-line-id attribute
|
|
1159
|
+
var oParent = this.getParent();
|
|
1160
|
+
if (oParent) {
|
|
1161
|
+
var sLineId = this._getLineId();
|
|
1162
|
+
oParent.$("line-texts").find('[data-line-id="' + sLineId + '"]').addClass(this.HIGHLIGHT_CLASS);
|
|
1163
|
+
}
|
|
812
1164
|
}
|
|
813
1165
|
};
|
|
814
1166
|
|
|
@@ -910,10 +1262,37 @@ sap.ui.define([
|
|
|
910
1262
|
|
|
911
1263
|
oParent._aShadedNodes = [];
|
|
912
1264
|
|
|
913
|
-
|
|
1265
|
+
var sFromNodeTitle = oFrom.getTitle();
|
|
914
1266
|
var sFromNodeText = sFromNodeTitle ? sFromNodeTitle : oFrom.getAltText();
|
|
915
1267
|
|
|
916
|
-
|
|
1268
|
+
// Add connection type information (always shown in tooltip)
|
|
1269
|
+
var sConnectionTypeText = "";
|
|
1270
|
+
var sTitle = "";
|
|
1271
|
+
|
|
1272
|
+
if (this.getParent()._isNoopLayout() &&
|
|
1273
|
+
this.getArrowPosition() === ArrowPosition.Both) {
|
|
1274
|
+
|
|
1275
|
+
var sConnectionType = this.getConnectionType();
|
|
1276
|
+
var oMapping = this.getParent().getConnectionTypeMapping();
|
|
1277
|
+
|
|
1278
|
+
if (sConnectionType === ConnectionType.LeftToRight) {
|
|
1279
|
+
sConnectionTypeText = oMapping.LeftToRight || ConnectionType.LeftToRight;
|
|
1280
|
+
} else if (sConnectionType === ConnectionType.RightToLeft) {
|
|
1281
|
+
sConnectionTypeText = oMapping.RightToLeft || ConnectionType.RightToLeft;
|
|
1282
|
+
} else if (sConnectionType === ConnectionType.LeftToLeft) {
|
|
1283
|
+
sConnectionTypeText = oMapping.LeftToLeft || ConnectionType.LeftToLeft;
|
|
1284
|
+
} else if (sConnectionType === ConnectionType.RightToRight) {
|
|
1285
|
+
sConnectionTypeText = oMapping.RightToRight || ConnectionType.RightToRight;
|
|
1286
|
+
} else {
|
|
1287
|
+
sConnectionTypeText = oMapping.RightToLeft || ConnectionType.RightToLeft;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
if (sConnectionTypeText) {
|
|
1291
|
+
sTitle += "<span class=\"sapSuiteUiCommonsNetworkGraphLineTooltipText\">" + sConnectionTypeText + "</span><br/>";
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
sTitle += "<span class=\"sapSuiteUiCommonsNetworkGraphLineTooltipText\">" + sFromNodeText + "</span>";
|
|
917
1296
|
|
|
918
1297
|
if (this._isBothArrow()) {
|
|
919
1298
|
sTitle += "<span class=\"sapSuiteUiCommonsNetworkGraphLineTooltipArrow sapSuiteUiCommonsNetworkGraphLineTooltipDualArrow\"></span>"
|
|
@@ -926,7 +1305,7 @@ sap.ui.define([
|
|
|
926
1305
|
sTitle += "<span class=\"sapSuiteUiCommonsNetworkGraphLineTooltipArrow\"></span>" + "</br>";
|
|
927
1306
|
}
|
|
928
1307
|
|
|
929
|
-
|
|
1308
|
+
var sToNodeTitle = oTo.getTitle();
|
|
930
1309
|
var sToNodeText = sToNodeTitle ? sToNodeTitle : oTo.getAltText();
|
|
931
1310
|
|
|
932
1311
|
sTitle += "<span class=\"sapSuiteUiCommonsNetworkGraphLineTooltipText\">" + sToNodeText + "</span>";
|
|
@@ -1095,6 +1474,18 @@ sap.ui.define([
|
|
|
1095
1474
|
if (bFocus) {
|
|
1096
1475
|
this._renderFocusWrapper();
|
|
1097
1476
|
}
|
|
1477
|
+
|
|
1478
|
+
// Apply focus class to text box for blue border
|
|
1479
|
+
var oParent = this.getParent();
|
|
1480
|
+
if (oParent) {
|
|
1481
|
+
var sLineId = this._getLineId();
|
|
1482
|
+
var $textBox = oParent.$("line-texts").find('[data-line-id="' + sLineId + '"]');
|
|
1483
|
+
if (bFocus) {
|
|
1484
|
+
$textBox.addClass(this.FOCUS_CLASS);
|
|
1485
|
+
} else {
|
|
1486
|
+
$textBox.removeClass(this.FOCUS_CLASS);
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1098
1489
|
};
|
|
1099
1490
|
|
|
1100
1491
|
Line.prototype._isEndPosition = function () {
|
|
@@ -1111,9 +1502,29 @@ sap.ui.define([
|
|
|
1111
1502
|
if (bCollapse) {
|
|
1112
1503
|
this.$().hide();
|
|
1113
1504
|
this._bIsHidden = true;
|
|
1505
|
+
// Also hide the line text label if it exists
|
|
1506
|
+
this._hideShowLineText(true);
|
|
1114
1507
|
} else if (!this.getToNode()._bIsHidden && !this.getFromNode()._bIsHidden) {
|
|
1115
1508
|
this.$().show();
|
|
1116
1509
|
this._bIsHidden = false;
|
|
1510
|
+
// Also show the line text label if it exists
|
|
1511
|
+
this._hideShowLineText(false);
|
|
1512
|
+
}
|
|
1513
|
+
};
|
|
1514
|
+
|
|
1515
|
+
/**
|
|
1516
|
+
* Hides or shows the line text label element
|
|
1517
|
+
* @param {boolean} bHide True to hide, false to show
|
|
1518
|
+
* @private
|
|
1519
|
+
*/
|
|
1520
|
+
Line.prototype._hideShowLineText = function (bHide) {
|
|
1521
|
+
// Use the same ID pattern that was set during _renderLineText
|
|
1522
|
+
// The text box ID is: this.getId() + "-textbox-middle"
|
|
1523
|
+
var sTextBoxId = this.getId() + "-textbox-middle";
|
|
1524
|
+
var oTextBox = document.getElementById(sTextBoxId);
|
|
1525
|
+
|
|
1526
|
+
if (oTextBox) {
|
|
1527
|
+
oTextBox.style.display = bHide ? "none" : "";
|
|
1117
1528
|
}
|
|
1118
1529
|
};
|
|
1119
1530
|
|
|
@@ -1223,10 +1634,10 @@ sap.ui.define([
|
|
|
1223
1634
|
var LINE_TITLE_LENGTH = 25;
|
|
1224
1635
|
var sTitle = this.getTitle() ? (this.getTitle() + " ") : "";
|
|
1225
1636
|
|
|
1226
|
-
|
|
1637
|
+
var sFromNodeTitle = this.getFromNode().getTitle();
|
|
1227
1638
|
var sFromNodeText = sFromNodeTitle ? sFromNodeTitle : this.getFromNode().getAltText();
|
|
1228
1639
|
|
|
1229
|
-
|
|
1640
|
+
var sToNodeTitle = this.getToNode().getTitle();
|
|
1230
1641
|
var sToNodeText = sToNodeTitle ? sToNodeTitle : this.getToNode().getAltText();
|
|
1231
1642
|
|
|
1232
1643
|
return sTitle + "(" + Utils.trimText(sFromNodeText, LINE_TITLE_LENGTH) + " -> "
|
|
@@ -1261,5 +1672,299 @@ sap.ui.define([
|
|
|
1261
1672
|
return false;
|
|
1262
1673
|
};
|
|
1263
1674
|
|
|
1675
|
+
/**
|
|
1676
|
+
* Calculates the total length of the line by summing up all the segment distances.
|
|
1677
|
+
* @returns {number} Total line length in pixels
|
|
1678
|
+
* @private
|
|
1679
|
+
*/
|
|
1680
|
+
Line.prototype._calculateTotalLineLength = function () {
|
|
1681
|
+
var oCoords = this.getCoordinates();
|
|
1682
|
+
var fTotalLength = 0;
|
|
1683
|
+
|
|
1684
|
+
if (!oCoords || oCoords.length < 2) {
|
|
1685
|
+
return 0;
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
// Sum the distance of each line segment
|
|
1689
|
+
for (var i = 0; i < oCoords.length - 1; i++) {
|
|
1690
|
+
var fSegmentLength = Math.sqrt(
|
|
1691
|
+
Math.pow(oCoords[i + 1].getX() - oCoords[i].getX(), 2) +
|
|
1692
|
+
Math.pow(oCoords[i + 1].getY() - oCoords[i].getY(), 2)
|
|
1693
|
+
);
|
|
1694
|
+
fTotalLength += fSegmentLength;
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
return fTotalLength;
|
|
1698
|
+
};
|
|
1699
|
+
|
|
1700
|
+
/**
|
|
1701
|
+
* Determines if a line is long enough to accommodate two arrows at 44px from each anchor
|
|
1702
|
+
* @param {number} fLineLength - Total length of the line
|
|
1703
|
+
* @returns {boolean} True if line can fit two arrows with proper spacing
|
|
1704
|
+
* @private
|
|
1705
|
+
*/
|
|
1706
|
+
Line.prototype._canLineFitTwoArrows = function (fLineLength) {
|
|
1707
|
+
// Minimum line length calculation:
|
|
1708
|
+
// - 44px from source anchor
|
|
1709
|
+
// - 44px from target anchor
|
|
1710
|
+
// - 20px minimum spacing between arrows
|
|
1711
|
+
// Total: 108px minimum
|
|
1712
|
+
var fMinLineLength = (MULTIPLE_ARROW_DISTANCE * 2) + 20;
|
|
1713
|
+
|
|
1714
|
+
// Add some buffer for curved/bent lines where the actual path might be longer
|
|
1715
|
+
// but the arrow positions could still overlap
|
|
1716
|
+
var fSafeLineLength = fMinLineLength + 10; // 118px total
|
|
1717
|
+
|
|
1718
|
+
return fLineLength >= fSafeLineLength;
|
|
1719
|
+
};
|
|
1720
|
+
|
|
1721
|
+
Line.prototype._shouldRenderSourceArrow = function () {
|
|
1722
|
+
if (!this._hasMultipleDirectedArrows()) {
|
|
1723
|
+
return false;
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
var oSourceNode = this.getFromNode();
|
|
1727
|
+
if (!oSourceNode) {
|
|
1728
|
+
return true; // Fallback: show arrow if source node not found
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
var aOutgoingLines = oSourceNode.getChildLines();
|
|
1732
|
+
|
|
1733
|
+
// If source node has only one outgoing line, show the arrow
|
|
1734
|
+
if (!aOutgoingLines || aOutgoingLines.length <= 1) {
|
|
1735
|
+
return true;
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
// Filter valid outgoing lines first
|
|
1739
|
+
var aValidOutgoingLines = aOutgoingLines.filter(function(oLine) {
|
|
1740
|
+
return oLine && oLine.getVisible() && !oLine._isIgnored();
|
|
1741
|
+
});
|
|
1742
|
+
|
|
1743
|
+
if (aValidOutgoingLines.length <= 1) {
|
|
1744
|
+
return true;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
// Group outgoing lines by their direction/side from the source node
|
|
1748
|
+
var oLinesByDirection = this._groupLinesByDirection(aValidOutgoingLines, oSourceNode, "outgoing");
|
|
1749
|
+
|
|
1750
|
+
// Get the direction of this line
|
|
1751
|
+
var sThisLineDirection = this._getLineDirection(this, oSourceNode, "outgoing");
|
|
1752
|
+
|
|
1753
|
+
// Get lines going in the same direction as this line
|
|
1754
|
+
var aLinesInSameDirection = oLinesByDirection[sThisLineDirection] || [];
|
|
1755
|
+
|
|
1756
|
+
if (aLinesInSameDirection.length <= 1) {
|
|
1757
|
+
return true;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
// Sort lines by their ID to ensure consistent ordering
|
|
1761
|
+
aLinesInSameDirection.sort(function(a, b) {
|
|
1762
|
+
return a._getLineId().localeCompare(b._getLineId());
|
|
1763
|
+
});
|
|
1764
|
+
|
|
1765
|
+
// Show arrow only on the first line in the sorted list for this direction
|
|
1766
|
+
return aLinesInSameDirection[0] === this;
|
|
1767
|
+
};
|
|
1768
|
+
|
|
1769
|
+
/**
|
|
1770
|
+
* Checks if multiple directed arrows feature is enabled at the graph level
|
|
1771
|
+
* @returns {boolean} True if the feature is enabled
|
|
1772
|
+
* @private
|
|
1773
|
+
*/
|
|
1774
|
+
Line.prototype._hasMultipleDirectedArrows = function () {
|
|
1775
|
+
var oParent = this.getParent();
|
|
1776
|
+
return oParent && oParent._enableMultipleDirectedArrows === true;
|
|
1777
|
+
};
|
|
1778
|
+
|
|
1779
|
+
/**
|
|
1780
|
+
* Groups lines by their direction relative to a node
|
|
1781
|
+
* @param {array} aLines - Array of lines to group
|
|
1782
|
+
* @param {object} oNode - The reference node
|
|
1783
|
+
* @param {string} sType - "outgoing" or "incoming"
|
|
1784
|
+
* @returns {object} Object with direction as key and array of lines as value
|
|
1785
|
+
* @private
|
|
1786
|
+
*/
|
|
1787
|
+
Line.prototype._groupLinesByDirection = function (aLines, oNode, sType) {
|
|
1788
|
+
var oGroupedLines = {
|
|
1789
|
+
"right": [],
|
|
1790
|
+
"left": [],
|
|
1791
|
+
"up": [],
|
|
1792
|
+
"down": []
|
|
1793
|
+
};
|
|
1794
|
+
|
|
1795
|
+
aLines.forEach(function(oLine) {
|
|
1796
|
+
var sDirection = this._getLineDirection(oLine, oNode, sType);
|
|
1797
|
+
if (oGroupedLines[sDirection]) {
|
|
1798
|
+
oGroupedLines[sDirection].push(oLine);
|
|
1799
|
+
}
|
|
1800
|
+
}.bind(this));
|
|
1801
|
+
|
|
1802
|
+
return oGroupedLines;
|
|
1803
|
+
};
|
|
1804
|
+
|
|
1805
|
+
/**
|
|
1806
|
+
* Determines the direction of a line relative to a node based on connection anchors
|
|
1807
|
+
* @param {object} oLine - The line object
|
|
1808
|
+
* @param {object} oNode - The reference node
|
|
1809
|
+
* @param {string} sType - "outgoing" or "incoming"
|
|
1810
|
+
* @returns {string} Direction: "right", "left", "up", or "down"
|
|
1811
|
+
* @private
|
|
1812
|
+
*/
|
|
1813
|
+
Line.prototype._getLineDirection = function (oLine, oNode, sType) {
|
|
1814
|
+
// Validate inputs
|
|
1815
|
+
if (!oLine || !oNode) {
|
|
1816
|
+
return "right"; // Default fallback
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
var oOtherNode = sType === "outgoing" ? oLine.getToNode() : oLine.getFromNode();
|
|
1820
|
+
if (!oOtherNode) {
|
|
1821
|
+
return "right"; // Default fallback
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// Use the actual line coordinates (source/target anchors) instead of node centers
|
|
1825
|
+
var oSourceCoords = oLine.getSource();
|
|
1826
|
+
var oTargetCoords = oLine.getTarget();
|
|
1827
|
+
var oNodeCenter = oNode.getCenterPosition();
|
|
1828
|
+
|
|
1829
|
+
// Validate coordinates
|
|
1830
|
+
if (!oSourceCoords || !oTargetCoords || !oNodeCenter ||
|
|
1831
|
+
typeof oSourceCoords.getX !== 'function' || typeof oSourceCoords.getY !== 'function' ||
|
|
1832
|
+
typeof oTargetCoords.getX !== 'function' || typeof oTargetCoords.getY !== 'function' ||
|
|
1833
|
+
typeof oNodeCenter.x !== 'number' || typeof oNodeCenter.y !== 'number') {
|
|
1834
|
+
return "right"; // Default fallback
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
// Get the anchor point coordinates
|
|
1838
|
+
var fSourceX = oSourceCoords.getX();
|
|
1839
|
+
var fSourceY = oSourceCoords.getY();
|
|
1840
|
+
var fTargetX = oTargetCoords.getX();
|
|
1841
|
+
var fTargetY = oTargetCoords.getY();
|
|
1842
|
+
|
|
1843
|
+
// Determine which anchor point belongs to our reference node
|
|
1844
|
+
var fAnchorX, fAnchorY;
|
|
1845
|
+
if (sType === "outgoing") {
|
|
1846
|
+
// For outgoing lines, use the source anchor (where line starts from our node)
|
|
1847
|
+
fAnchorX = fSourceX;
|
|
1848
|
+
fAnchorY = fSourceY;
|
|
1849
|
+
} else {
|
|
1850
|
+
// For incoming lines, use the target anchor (where line ends at our node)
|
|
1851
|
+
fAnchorX = fTargetX;
|
|
1852
|
+
fAnchorY = fTargetY;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
// Calculate the direction based on anchor position relative to node center
|
|
1856
|
+
var fDeltaX = fAnchorX - oNodeCenter.x;
|
|
1857
|
+
var fDeltaY = fAnchorY - oNodeCenter.y;
|
|
1858
|
+
|
|
1859
|
+
// Use threshold to handle edge cases
|
|
1860
|
+
var fThreshold = 5.0; // 5px threshold for anchor-based detection
|
|
1861
|
+
var fAbsDeltaX = Math.abs(fDeltaX);
|
|
1862
|
+
var fAbsDeltaY = Math.abs(fDeltaY);
|
|
1863
|
+
|
|
1864
|
+
// If anchor is very close to center, fall back to target direction
|
|
1865
|
+
if (fAbsDeltaX < fThreshold && fAbsDeltaY < fThreshold) {
|
|
1866
|
+
// Use direction to other node as fallback
|
|
1867
|
+
var oOtherCenter = oOtherNode.getCenterPosition();
|
|
1868
|
+
if (oOtherCenter) {
|
|
1869
|
+
fDeltaX = oOtherCenter.x - oNodeCenter.x;
|
|
1870
|
+
fDeltaY = oOtherCenter.y - oNodeCenter.y;
|
|
1871
|
+
fAbsDeltaX = Math.abs(fDeltaX);
|
|
1872
|
+
fAbsDeltaY = Math.abs(fDeltaY);
|
|
1873
|
+
} else {
|
|
1874
|
+
return "right"; // Final fallback
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// Determine direction based on larger delta
|
|
1879
|
+
if (fAbsDeltaX > fAbsDeltaY + fThreshold) {
|
|
1880
|
+
return fDeltaX > 0 ? "right" : "left";
|
|
1881
|
+
} else if (fAbsDeltaY > fAbsDeltaX + fThreshold) {
|
|
1882
|
+
return fDeltaY > 0 ? "down" : "up";
|
|
1883
|
+
} else {
|
|
1884
|
+
// When deltas are nearly equal, prefer horizontal direction for consistency
|
|
1885
|
+
return fDeltaX >= 0 ? "right" : "left";
|
|
1886
|
+
}
|
|
1887
|
+
};
|
|
1888
|
+
|
|
1889
|
+
/**
|
|
1890
|
+
* Finds the optimal segment for placing arrows on bent lines by traveling along the path
|
|
1891
|
+
* @param {string} sDirection - "forward" from source or "backward" from target
|
|
1892
|
+
* @param {number} fDistance - Distance to travel along the line (e.g., 44px)
|
|
1893
|
+
* @returns {object} Segment information with exact position 44px away from anchor
|
|
1894
|
+
* @private
|
|
1895
|
+
*/
|
|
1896
|
+
Line.prototype._findOptimalArrowSegment = function (sDirection, fDistance) {
|
|
1897
|
+
var oCoords = this.getCoordinates(),
|
|
1898
|
+
iLastIndex = oCoords.length - 1,
|
|
1899
|
+
fTotalDistance = 0,
|
|
1900
|
+
fSegmentDistance,
|
|
1901
|
+
iSegmentIndex,
|
|
1902
|
+
fRemainingDistance,
|
|
1903
|
+
i;
|
|
1904
|
+
|
|
1905
|
+
if (sDirection === "forward") {
|
|
1906
|
+
// Travel from source towards target
|
|
1907
|
+
for (i = 0; i < iLastIndex; i++) {
|
|
1908
|
+
fSegmentDistance = Math.sqrt(
|
|
1909
|
+
Math.pow(oCoords[i + 1].getX() - oCoords[i].getX(), 2) +
|
|
1910
|
+
Math.pow(oCoords[i + 1].getY() - oCoords[i].getY(), 2)
|
|
1911
|
+
);
|
|
1912
|
+
|
|
1913
|
+
if (fTotalDistance + fSegmentDistance >= fDistance) {
|
|
1914
|
+
// Found the segment where 44px distance falls
|
|
1915
|
+
fRemainingDistance = fDistance - fTotalDistance;
|
|
1916
|
+
iSegmentIndex = i;
|
|
1917
|
+
break;
|
|
1918
|
+
}
|
|
1919
|
+
fTotalDistance += fSegmentDistance;
|
|
1920
|
+
}
|
|
1921
|
+
} else {
|
|
1922
|
+
// Travel from target towards source
|
|
1923
|
+
for (i = iLastIndex; i > 0; i--) {
|
|
1924
|
+
fSegmentDistance = Math.sqrt(
|
|
1925
|
+
Math.pow(oCoords[i].getX() - oCoords[i - 1].getX(), 2) +
|
|
1926
|
+
Math.pow(oCoords[i].getY() - oCoords[i - 1].getY(), 2)
|
|
1927
|
+
);
|
|
1928
|
+
|
|
1929
|
+
if (fTotalDistance + fSegmentDistance >= fDistance) {
|
|
1930
|
+
// Found the segment where 44px distance falls
|
|
1931
|
+
fRemainingDistance = fDistance - fTotalDistance;
|
|
1932
|
+
iSegmentIndex = i - 1; // Use the segment index format (i to i+1)
|
|
1933
|
+
break;
|
|
1934
|
+
}
|
|
1935
|
+
fTotalDistance += fSegmentDistance;
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
// Fallback to first/last segment if distance is too large
|
|
1940
|
+
if (typeof iSegmentIndex === "undefined") {
|
|
1941
|
+
iSegmentIndex = sDirection === "forward" ? 0 : Math.max(0, iLastIndex - 1);
|
|
1942
|
+
fRemainingDistance = sDirection === "forward" ? fDistance : fDistance;
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
// Calculate exact position along the segment
|
|
1946
|
+
var fSegmentLength = Math.sqrt(
|
|
1947
|
+
Math.pow(oCoords[iSegmentIndex + 1].getX() - oCoords[iSegmentIndex].getX(), 2) +
|
|
1948
|
+
Math.pow(oCoords[iSegmentIndex + 1].getY() - oCoords[iSegmentIndex].getY(), 2)
|
|
1949
|
+
);
|
|
1950
|
+
|
|
1951
|
+
var fRatio = fSegmentLength > 0 ? (fRemainingDistance / fSegmentLength) : 0;
|
|
1952
|
+
if (sDirection === "backward") {
|
|
1953
|
+
fRatio = 1 - fRatio; // Reverse the ratio for backward direction
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
// Calculate the exact coordinates that are 44px away from the anchor
|
|
1957
|
+
var fExactX = oCoords[iSegmentIndex].getX() + (oCoords[iSegmentIndex + 1].getX() - oCoords[iSegmentIndex].getX()) * fRatio;
|
|
1958
|
+
var fExactY = oCoords[iSegmentIndex].getY() + (oCoords[iSegmentIndex + 1].getY() - oCoords[iSegmentIndex].getY()) * fRatio;
|
|
1959
|
+
|
|
1960
|
+
return {
|
|
1961
|
+
segmentIndex: iSegmentIndex,
|
|
1962
|
+
ratio: fRatio,
|
|
1963
|
+
center: {x: oCoords[iSegmentIndex].getX(), y: oCoords[iSegmentIndex].getY()},
|
|
1964
|
+
apex: {x: oCoords[iSegmentIndex + 1].getX(), y: oCoords[iSegmentIndex + 1].getY()},
|
|
1965
|
+
exactPosition: {x: fExactX, y: fExactY}
|
|
1966
|
+
};
|
|
1967
|
+
};
|
|
1968
|
+
|
|
1264
1969
|
return Line;
|
|
1265
1970
|
});
|