Update flow diagram to support branching edges for conditionals (#28481)
- If there is a conditional subflow, instead of creating nodes for the start and end of the subflow, there will now be branching edges for the condition nodes, representing the false and true cases. - Adds an optional label attribute to the edges to support having true and false labels for condition node edges. - Modifies auto layout to reduce the amount of overlap that occurs with the branching condition edges and labels. - Also removed the startSubFlow check in the createNode method since the start and end subflow nodes are always created by the renderSubFlowNodes method. Closes #28453 Signed-off-by: Greg Baroni <greg.baroni@appfolio.com>
This commit is contained in:
parent
b2c88e9876
commit
8140c76147
3 changed files with 87 additions and 19 deletions
|
@ -31,11 +31,16 @@ type FlowDiagramProps = {
|
|||
executionList: ExecutionList;
|
||||
};
|
||||
|
||||
const createEdge = (fromNode: string, toNode: string): Edge => ({
|
||||
const createEdge = (
|
||||
fromNode: string,
|
||||
toNode: string,
|
||||
label?: string,
|
||||
): Edge => ({
|
||||
id: `edge-${fromNode}-to-${toNode}`,
|
||||
type: "buttonEdge",
|
||||
source: fromNode,
|
||||
target: toNode,
|
||||
label: label,
|
||||
data: {
|
||||
onEdgeClick: (
|
||||
evt: ReactMouseEvent<HTMLButtonElement, MouseEvent>,
|
||||
|
@ -49,9 +54,6 @@ const createEdge = (fromNode: string, toNode: string): Edge => ({
|
|||
|
||||
const createNode = (ex: ExpandableExecution): Node => {
|
||||
let nodeType: string | undefined = undefined;
|
||||
if (ex.executionList) {
|
||||
nodeType = "startSubFlow";
|
||||
}
|
||||
if (providerConditionFilter(ex)) {
|
||||
nodeType = "conditional";
|
||||
}
|
||||
|
@ -73,10 +75,13 @@ const renderParallelEdges = (
|
|||
start: AuthenticationExecutionInfoRepresentation,
|
||||
execution: ExpandableExecution,
|
||||
end: AuthenticationExecutionInfoRepresentation,
|
||||
): Edge[] => [
|
||||
): Edge[] => {
|
||||
const falseConditionLabel = providerConditionFilter(execution) ? "false" : "";
|
||||
return [
|
||||
createEdge(start.id!, execution.id!),
|
||||
createEdge(execution.id!, end.id!),
|
||||
];
|
||||
createEdge(execution.id!, end.id!, falseConditionLabel),
|
||||
];
|
||||
};
|
||||
|
||||
const renderSequentialNodes = (execution: ExpandableExecution): Node[] => [
|
||||
createNode(execution),
|
||||
|
@ -95,16 +100,46 @@ const renderSequentialEdges = (
|
|||
if (isFirst) {
|
||||
edges.push(createEdge(start.id!, execution.id!));
|
||||
} else {
|
||||
edges.push(createEdge(prefExecution.id!, execution.id!));
|
||||
const trueConditionLabel = providerConditionFilter(prefExecution)
|
||||
? "true"
|
||||
: "";
|
||||
edges.push(
|
||||
createEdge(prefExecution.id!, execution.id!, trueConditionLabel),
|
||||
);
|
||||
}
|
||||
|
||||
if (isLast) {
|
||||
edges.push(createEdge(execution.id!, end.id!));
|
||||
if (isLast || providerConditionFilter(execution)) {
|
||||
const falseConditionLabel = providerConditionFilter(execution)
|
||||
? "false"
|
||||
: "";
|
||||
edges.push(createEdge(execution.id!, end.id!, falseConditionLabel));
|
||||
}
|
||||
|
||||
return edges;
|
||||
};
|
||||
|
||||
const renderConditionalSubFlowNodes = (
|
||||
execution: ExpandableExecution,
|
||||
): Node[] => renderFlowNodes(execution.executionList || []);
|
||||
|
||||
const renderConditionalSubFlowEdges = (
|
||||
execution: ExpandableExecution,
|
||||
start: AuthenticationExecutionInfoRepresentation,
|
||||
end: AuthenticationExecutionInfoRepresentation,
|
||||
prefExecution?: ExpandableExecution,
|
||||
): Edge[] => {
|
||||
const conditionalSubFlowStart =
|
||||
prefExecution && prefExecution.requirement !== "ALTERNATIVE"
|
||||
? prefExecution
|
||||
: start!;
|
||||
|
||||
return renderFlowEdges(
|
||||
conditionalSubFlowStart,
|
||||
execution.executionList || [],
|
||||
end,
|
||||
);
|
||||
};
|
||||
|
||||
const renderSubFlowNodes = (execution: ExpandableExecution): Node[] => {
|
||||
const nodes: Node[] = [];
|
||||
|
||||
|
@ -165,7 +200,11 @@ const renderFlowNodes = (executionList: ExpandableExecution[]): Node[] => {
|
|||
for (let index = 0; index < executionList.length; index++) {
|
||||
const execution = executionList[index];
|
||||
if (execution.executionList) {
|
||||
if (execution.requirement === "CONDITIONAL") {
|
||||
elements = elements.concat(renderConditionalSubFlowNodes(execution));
|
||||
} else {
|
||||
elements = elements.concat(renderSubFlowNodes(execution));
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
execution.requirement === "ALTERNATIVE" ||
|
||||
|
@ -191,9 +230,20 @@ const renderFlowEdges = (
|
|||
for (let index = 0; index < executionList.length; index++) {
|
||||
const execution = executionList[index];
|
||||
if (execution.executionList) {
|
||||
if (execution.requirement === "CONDITIONAL") {
|
||||
elements = elements.concat(
|
||||
renderConditionalSubFlowEdges(
|
||||
execution,
|
||||
start,
|
||||
end,
|
||||
executionList[index - 1],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
elements = elements.concat(
|
||||
renderSubFlowEdges(execution, start, end, executionList[index - 1]),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
execution.requirement === "ALTERNATIVE" ||
|
||||
|
|
|
@ -27,6 +27,7 @@ export const ButtonEdge = ({
|
|||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
label,
|
||||
style = {},
|
||||
markerType,
|
||||
markerEndId,
|
||||
|
@ -52,6 +53,18 @@ export const ButtonEdge = ({
|
|||
d={edgePath}
|
||||
markerEnd={markerEnd}
|
||||
/>
|
||||
{!selected && (
|
||||
<text>
|
||||
<textPath
|
||||
href={`#${id}`}
|
||||
style={{ fontSize: "11px" }}
|
||||
startOffset="50%"
|
||||
textAnchor="middle"
|
||||
>
|
||||
{label}
|
||||
</textPath>
|
||||
</text>
|
||||
)}
|
||||
{selected && (
|
||||
<foreignObject
|
||||
width={foreignObjectSize}
|
||||
|
|
|
@ -5,16 +5,21 @@ const dagreGraph = new graphlib.Graph();
|
|||
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
||||
|
||||
const nodeWidth = 130;
|
||||
const nodeHeight = 28;
|
||||
const nodeHeight = 40;
|
||||
const nodeAfterConditionalHeight = 130;
|
||||
|
||||
export const getLayoutedNodes = (nodes: Node[], direction = "LR"): Node[] => {
|
||||
const isHorizontal = direction === "LR";
|
||||
dagreGraph.setGraph({ rankdir: direction });
|
||||
|
||||
nodes.forEach((element) => {
|
||||
nodes.forEach((element, index) => {
|
||||
const prevNode = index > 0 ? nodes[index - 1] : undefined;
|
||||
dagreGraph.setNode(element.id, {
|
||||
width: nodeWidth,
|
||||
height: nodeHeight,
|
||||
height:
|
||||
prevNode?.type === "conditional"
|
||||
? nodeAfterConditionalHeight
|
||||
: nodeHeight,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -26,7 +31,7 @@ export const getLayoutedNodes = (nodes: Node[], direction = "LR"): Node[] => {
|
|||
node.sourcePosition = isHorizontal ? Position.Right : Position.Bottom;
|
||||
|
||||
node.position = {
|
||||
x: nodeWithPosition.x - nodeWidth / 2 + Math.random() / 1000,
|
||||
x: nodeWithPosition.x - nodeWidth / 2,
|
||||
y: nodeWithPosition.y - nodeHeight / 2,
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue