@statelyai/layout 0.0.2 → 0.0.4

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.
@@ -30,6 +30,37 @@ function getOrientedEndpoints(
30
30
  : [edge.sourceId, edge.targetId];
31
31
  }
32
32
 
33
+ export function getOrientedPortDirection(
34
+ input: LayeredPhaseInput,
35
+ orientation: AcyclicOrientation,
36
+ node: GraphNode,
37
+ port: GraphPort,
38
+ ): GraphPort["direction"] {
39
+ const configuredSide = input.portSettings?.(port, node)?.["port.side"];
40
+ if (
41
+ configuredSide !== "NORTH" &&
42
+ configuredSide !== "SOUTH" &&
43
+ configuredSide !== "WEST" &&
44
+ configuredSide !== "EAST"
45
+ ) {
46
+ return port.direction;
47
+ }
48
+ let incoming = false;
49
+ let outgoing = false;
50
+ for (const edge of input.graph.edges) {
51
+ const reversed = orientation.reversedEdgeIds.has(edge.id);
52
+ if (edge.sourceId === node.id && edge.sourcePort === port.name) {
53
+ if (reversed) incoming = true;
54
+ else outgoing = true;
55
+ }
56
+ if (edge.targetId === node.id && edge.targetPort === port.name) {
57
+ if (reversed) outgoing = true;
58
+ else incoming = true;
59
+ }
60
+ }
61
+ return incoming && outgoing ? "inout" : outgoing ? "out" : incoming ? "in" : port.direction;
62
+ }
63
+
33
64
  function cycleModelOrder(input: LayeredPhaseInput): Map<string, number> {
34
65
  const enforced =
35
66
  input.settings["considerModelOrder.groupModelOrder.cbGroupOrderStrategy"] === "ENFORCED";
@@ -4312,10 +4343,15 @@ function routeEdges(style: "ORTHOGONAL" | "POLYLINE" | "SPLINES"): EdgeRouter {
4312
4343
  source.id === target.id &&
4313
4344
  feedbackSourcePortSide !== undefined &&
4314
4345
  feedbackSourcePortSide === feedbackTargetPortSide;
4346
+ const hasFixedPortSide = (node: GraphNode): boolean => {
4347
+ const constraints = String(input.nodeSettings?.(node)?.portConstraints ?? "UNDEFINED");
4348
+ return constraints !== "UNDEFINED" && constraints !== "FREE";
4349
+ };
4315
4350
  const fixedSideFeedback =
4316
4351
  style === "ORTHOGONAL" &&
4317
4352
  !sameSideSelfLoop &&
4318
- (feedbackSourcePortSide === sourceAwaySide || feedbackTargetPortSide === targetAwaySide);
4353
+ ((hasFixedPortSide(source) && feedbackSourcePortSide === sourceAwaySide) ||
4354
+ (hasFixedPortSide(target) && feedbackTargetPortSide === targetAwaySide));
4319
4355
  if ((input.settings.feedbackEdges === true && reversedEdge) || fixedSideFeedback) {
4320
4356
  if (fixedSideFeedback) outsideFeedbackEdgeIds.add(edge.id);
4321
4357
  const crossSpacing = Number(input.settings["spacing.edgeNode"] ?? 10);
@@ -4769,12 +4805,32 @@ function routeEdges(style: "ORTHOGONAL" | "POLYLINE" | "SPLINES"): EdgeRouter {
4769
4805
  (candidate) =>
4770
4806
  candidate.targetId === edge.targetId && candidate.targetPort === edge.targetPort,
4771
4807
  ).length === 1;
4772
- const start = sourceFixedSide
4808
+ const flexibleFeedback =
4809
+ reversedEdge && !hasFixedPortSide(source) && !hasFixedPortSide(target);
4810
+ const start = flexibleFeedback
4773
4811
  ? sourceFallback
4774
- : getPortPoint(source, edge.sourcePort, sourceRect, sourceFallback, input.direction, input);
4775
- const end = targetFixedSide
4812
+ : sourceFixedSide
4813
+ ? sourceFallback
4814
+ : getPortPoint(
4815
+ source,
4816
+ edge.sourcePort,
4817
+ sourceRect,
4818
+ sourceFallback,
4819
+ input.direction,
4820
+ input,
4821
+ );
4822
+ const end = flexibleFeedback
4776
4823
  ? targetFallback
4777
- : getPortPoint(target, edge.targetPort, targetRect, targetFallback, input.direction, input);
4824
+ : targetFixedSide
4825
+ ? targetFallback
4826
+ : getPortPoint(
4827
+ target,
4828
+ edge.targetPort,
4829
+ targetRect,
4830
+ targetFallback,
4831
+ input.direction,
4832
+ input,
4833
+ );
4778
4834
  const sourceLayer = flowLayerByNodeId.get(edge.sourceId) ?? 0;
4779
4835
  const targetLayer = flowLayerByNodeId.get(edge.targetId) ?? 0;
4780
4836
  const earlierLayer = Math.min(sourceLayer, targetLayer);
@@ -4955,6 +5011,7 @@ export function placePorts<P>(
4955
5011
  direction: LayeredPhaseInput["direction"],
4956
5012
  portSettings?: (port: GraphPort<P>) => ElkLayeredOptionValueByName | undefined,
4957
5013
  nodeSettings?: ElkLayeredOptionValueByName,
5014
+ portDirection?: (port: GraphPort<P>) => GraphPort<P>["direction"],
4958
5015
  ): GraphPort<P>[] | undefined {
4959
5016
  if (!ports) return undefined;
4960
5017
  const horizontal = direction === "left" || direction === "right";
@@ -4981,7 +5038,7 @@ export function placePorts<P>(
4981
5038
  sideByPort.set(port, configured);
4982
5039
  continue;
4983
5040
  }
4984
- const outgoing = port.direction === "out";
5041
+ const outgoing = (portDirection?.(port) ?? port.direction) === "out";
4985
5042
  const normalFarSide = reverse ? !outgoing : outgoing;
4986
5043
  const farSide = sideFixed && configured === "UNDEFINED" ? !normalFarSide : normalFarSide;
4987
5044
  sideByPort.set(port, horizontal ? (farSide ? "EAST" : "WEST") : farSide ? "SOUTH" : "NORTH");
@@ -5221,6 +5278,7 @@ export function normalizePlacementForPortExtents(
5221
5278
  input: LayeredPhaseInput,
5222
5279
  placement: NodePlacement,
5223
5280
  order: LayerOrder,
5281
+ orientation: AcyclicOrientation,
5224
5282
  ): void {
5225
5283
  const horizontal = input.direction === "left" || input.direction === "right";
5226
5284
  const physicalLayers = order.layers
@@ -5267,6 +5325,7 @@ export function normalizePlacementForPortExtents(
5267
5325
  input.direction,
5268
5326
  (port) => input.portSettings?.(port, node),
5269
5327
  { ...input.settings, ...input.nodeSettings?.(node) },
5328
+ (port) => getOrientedPortDirection(input, orientation, node, port),
5270
5329
  );
5271
5330
  for (const port of ports ?? []) {
5272
5331
  const extent = horizontal
@@ -5297,6 +5356,7 @@ export function normalizePlacementForPortExtents(
5297
5356
  input.direction,
5298
5357
  (port) => input.portSettings?.(port, node),
5299
5358
  { ...input.settings, ...input.nodeSettings?.(node) },
5359
+ (port) => getOrientedPortDirection(input, orientation, node, port),
5300
5360
  );
5301
5361
  for (const port of ports ?? []) {
5302
5362
  minimumX = Math.min(minimumX, rect.x + (port.x ?? 0));