Skip to content

Commit

Permalink
Add Redux in React Flow component logic
Browse files Browse the repository at this point in the history
  • Loading branch information
AbbyB97 committed Jul 10, 2024
1 parent 1bc8b44 commit c33767a
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 25 deletions.
2 changes: 1 addition & 1 deletion src/components/FlowChart/CustomEdge/edgeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CustomNode, nodeHeight, nodeWidth } from '..';
function getNodeIntersection(intersectionNode: CustomNode, targetNode: CustomNode) {
const { positionAbsolute: intersectionNodePosition } = intersectionNode;
const targetPosition = targetNode.positionAbsolute;
const currentIntersectionNodeWidth = intersectionNode?.data?.group ? 242.5 : nodeWidth;
const currentIntersectionNodeWidth = intersectionNode?.data?.group || intersectionNode.hidden !== undefined ? 242.5 : nodeWidth;
const w = currentIntersectionNodeWidth / 2;
const currentIntersectionNodeNodeHeight = intersectionNode?.data?.description ? nodeHeight + 35 : nodeHeight;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,8 @@
}
}

.groupNode {
.groupNode,
.hiddenStatus {
width: 242.5px;
}

Expand Down
125 changes: 116 additions & 9 deletions src/components/FlowChart/CustomFlowNode/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import cx from 'classnames';
import { useState } from 'react';
import { useDispatch } from 'react-redux';
import { actions as userInterfaceActions, selectors as userInterfaceSelectors } from 'ducks/user-interface';
import { useCallback, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import { Handle, Position } from 'reactflow';
import { Button, Collapse } from 'reactstrap';
Expand All @@ -9,16 +10,97 @@ import { CertificateValidationStatus } from 'types/openapi';
import { useCopyToClipboard } from 'utils/common-hooks';
import style from './customFlowNode.module.scss';

export default function CustomFlowNode({ data, dragging, selected, xPos, yPos }: EntityNodeProps) {
export default function CustomFlowNode({ data, dragging, selected, xPos, yPos, id }: EntityNodeProps) {
const [collapse, setCollapse] = useState(data.expandedByDefault ?? false);
const [addNodeContentCollapse, setAddNodeContentCollapse] = useState(false);
// const [status, setStatus] = useState('+');
// TODO: Use this during dynamic flowchart updates
// const onEntering = () => setStatus("Opening...");
// const onExiting = () => setStatus("Closing...");
const reactFlowUI = useSelector(userInterfaceSelectors.selectReactFlowUI);
const [showHiddenNodes, setShowHiddenNodes] = useState(false);

const currentnodes = reactFlowUI?.flowChartNodes;
const thisNodeState = currentnodes?.find((node) => node?.id === id);

const hasHiddenChildren = useMemo(() => {
return currentnodes?.some((node) => node?.parentId === id && node.hidden !== undefined);
}, [currentnodes, id]);
const dispatch = useDispatch();
// const onEntered = () => setStatus('-');
// const onExited = () => setStatus('+');

const toggleHiddenNodes = useCallback(
(showHiddenNodes: boolean) => {
// const
if (showHiddenNodes) {
const updatedNodes = currentnodes?.map((node, i) => {
const totalNodes = currentnodes?.filter((node) => node.parentId === id).length || 1; // Total child nodes

const angleIncrement = (2 * Math.PI) / totalNodes; // Divide the circle based on the number of groups
const multiplier = totalNodes < 3 ? 300 : 180;
const nodeRadius = multiplier * (totalNodes * 0.3); // Smaller radius for nodes within a group
const nodeAngle = ((2 * Math.PI) / totalNodes) * i;
const onlyTwoNodes = totalNodes === 2;
let yOffset = 0;
if (onlyTwoNodes && i === 1) {
yOffset = 105;
}

if (node?.parentId === id && node.hidden !== undefined) {
const angle = angleIncrement * i; // Calculate angle for this node
const position = {
// x: xPos + Math.cos(angle) * radius, // Calculate x position
// y: yPos + Math.sin(angle) * radius, // Calculate y position
// x: xPos + nodeRadius * 1.75 * Math.cos(nodeAngle),
// y: yPos + nodeRadius * Math.sin(nodeAngle) + yOffset,
x: nodeRadius * 1.75 * Math.cos(nodeAngle),
y: nodeRadius * Math.sin(nodeAngle) + yOffset,
};

return {
...node,
position: position,
// position: {
// x: xPos + 100,
// y: yPos + 100,
// },
hidden: false,
};
}
return node;
});

dispatch(
userInterfaceActions.setReactFlowUI({
flowChartNodes: updatedNodes || [],
flowChartEdges: reactFlowUI?.flowChartEdges || [],
flowDirection: reactFlowUI?.flowDirection,
legends: reactFlowUI?.legends,
}),
);
} else {
const updatedNodes = currentnodes?.map((node) => {
if (node?.parentId === id && node.hidden !== undefined) {
return {
...node,
// position: {
// x: xPos + 100,
// y: yPos + 100,
// },
hidden: true,
};
}
return node;
});

dispatch(
userInterfaceActions.setReactFlowUI({
flowChartNodes: updatedNodes || [],
flowChartEdges: reactFlowUI?.flowChartEdges || [],
flowDirection: reactFlowUI?.flowDirection,
legends: reactFlowUI?.legends,
}),
);
}
},
[currentnodes, reactFlowUI, dispatch, id],
);

const toggle = () => setCollapse(!collapse);
const copyToClipboard = useCopyToClipboard();

Expand Down Expand Up @@ -50,6 +132,15 @@ export default function CustomFlowNode({ data, dragging, selected, xPos, yPos }:
return style.actionGroupNodeStatus;
}

// switch(data)

// switch (thisNodeState?.hidden) {
// case true:
// return style.hiddenStatus;
// case false:
// return style.hiddenStatus;
// }

return style.unknownStatus;
};

Expand Down Expand Up @@ -98,6 +189,7 @@ export default function CustomFlowNode({ data, dragging, selected, xPos, yPos }:
{
[style.mainNodeBody]: data.isMainNode,
[style.groupNode]: data.group,
[style.hiddenStatus]: thisNodeState?.hidden !== undefined,
},
getStatusClasses(),
)}
Expand All @@ -115,6 +207,21 @@ export default function CustomFlowNode({ data, dragging, selected, xPos, yPos }:
<i className={cx('fa ', { 'fa-chevron-down': !collapse, 'fa-chevron-up': collapse })} />
</Button>
)}

{hasHiddenChildren ? (
<Button
color="primary"
onClick={() => {
setShowHiddenNodes(!showHiddenNodes);
toggleHiddenNodes(!showHiddenNodes);
}}
className={cx('mt-1', style.nodeButton, getExpandButtonStatusClasses())}
>
{/* <span className="mx-auto">{status}</span> */}
<i className={cx('fa ', { 'fa-eye': !showHiddenNodes, 'fa-eye-slash': showHiddenNodes })} />
</Button>
) : null}
{/*
{/* TODO: Make this button to be collapsible and expandable to the right side, show attachable items to that
specific node */}
{data.addButtonContent && (
Expand Down
61 changes: 50 additions & 11 deletions src/components/FlowChart/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import Widget from 'components/Widget';
import dagre from 'dagre';
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect } from 'react';

import cx from 'classnames';
import { actions as userInterfaceActions, selectors as userInterfaceSelectors } from 'ducks/user-interface';
import { useDispatch, useSelector } from 'react-redux';
import {
Background,
BackgroundVariant,
Expand Down Expand Up @@ -170,29 +172,66 @@ const getLayoutedElements = (nodes: CustomNode[], edges: Edge[], direction = 'TB
};

const FlowChart = ({ flowChartTitle, flowChartEdges, flowChartNodes, defaultViewport, busy, flowDirection, legends }: FlowChartProps) => {
const [nodes, setNodes] = useState(flowChartNodes);
const [edges, setEdges] = useState(flowChartEdges);
const defaultEdgeOptions = { animated: true };
const reactFlowUI = useSelector(userInterfaceSelectors.selectReactFlowUI);
const dispatch = useDispatch();

const onNodesChange = useCallback((changes: NodeChange[]) => setNodes((nds) => applyNodeChanges(changes, nds)), [setNodes]);
const onEdgesChange = useCallback((changes: EdgeChange[]) => setEdges((eds) => applyEdgeChanges(changes, eds)), [setEdges]);
// TODO: Implement onConnect in future if needed
const onNodesChange = useCallback(
(changes: NodeChange[]) => {
const newNodes = applyNodeChanges(changes, reactFlowUI?.flowChartNodes ?? []);
dispatch(
userInterfaceActions.setReactFlowUI({
flowChartNodes: newNodes,
flowChartEdges: reactFlowUI?.flowChartEdges || [],
flowDirection: reactFlowUI?.flowDirection,
legends: reactFlowUI?.legends,
}),
);
},
[dispatch, reactFlowUI],
);

const onEdgesChange = useCallback(
(changes: EdgeChange[]) => {
const newEdges = applyEdgeChanges(changes, reactFlowUI?.flowChartEdges ?? []);
dispatch(
userInterfaceActions.setReactFlowUI({
flowChartNodes: reactFlowUI?.flowChartNodes || [],
flowChartEdges: newEdges,
flowDirection: reactFlowUI?.flowDirection,
legends: reactFlowUI?.legends,
}),
);
},
[dispatch, reactFlowUI],
);

// // TODO: Implement onConnect in future if needed
// const onConnect = useCallback((connection: Edge | Connection) => setEdges((eds) => addEdge(connection, eds)), [setEdges]);

useEffect(() => {
// initial placement of nodes and edges
const { nodes, edges } = getLayoutedElements(flowChartNodes, flowChartEdges, flowDirection);
setNodes(nodes);
setEdges(edges);
}, [flowChartEdges, flowChartNodes, flowDirection]);
// setNodes(nodes);
// setEdges(edges);

dispatch(
userInterfaceActions.setReactFlowUI({
flowChartNodes: nodes,
flowChartEdges: edges,
flowDirection,
}),
);
}, [flowChartEdges, flowChartNodes, flowDirection, dispatch]);

return (
<Widget className={style.flowWidget} busy={busy}>
{flowChartTitle && <h5 className="text-muted">{flowChartTitle}</h5>}
<div className={cx(style.flowChartContainer, style.floatingedges)}>
<ReactFlow
nodes={nodes}
nodes={reactFlowUI?.flowChartNodes}
proOptions={{ hideAttribution: true }}
edges={edges}
edges={reactFlowUI?.flowChartEdges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
nodeTypes={nodeTypes}
Expand Down
62 changes: 60 additions & 2 deletions src/ducks/transform/rules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -603,17 +603,75 @@ export function useTransformTriggerObjectToNodesAndEdges(
},
],
},
{
uuid: 'a5c73bb1-b0de-4057-b598-de11b6bf5aba',
name: 'test-execution-3',
description: 'test-execution-3',
type: 'setField',
resource: 'certificates',
items: [
{
fieldSource: 'property',
fieldIdentifier: 'GROUP_NAME',
data: '["b9f33da1-595d-4747-a30b-b381700046ce"]',
},
],
},
{
uuid: 'bf1140f6-5a1c-48a7-ab8d-92303b198a64',
name: 'test-execution-4',
description: 'test-execution-4',
type: 'setField',
resource: 'certificates',
items: [
{
fieldSource: 'property',
fieldIdentifier: 'GROUP_NAME',
data: '["b9f33da1-595d-4747-a30b-b381700046ce"]',
},
],
},
{
uuid: 'bf1140f6-5a1c-48a7-ab8d-92303b198a65',
name: 'test-execution-5',
description: 'test-execution-5',
type: 'setField',
resource: 'certificates',
items: [
{
fieldSource: 'property',
fieldIdentifier: 'GROUP_NAME',
data: '["b9f33da1-595d-4747-a30b-b381700046ce"]',
},
],
},
{
uuid: 'bf1140f6-5a1c-48a7-ab8d-92303b198a66',
name: 'test-execution-6',
description: 'test-execution-6',
type: 'setField',
resource: 'certificates',
items: [
{
fieldSource: 'property',
fieldIdentifier: 'GROUP_NAME',
data: '["b9f33da1-595d-4747-a30b-b381700046ce"]',
},
],
},
];
let tempExec: CustomNode[] = [];
if (index === 0) {
tempExec = staticExec.map((execution, index) => ({
id: `execution-${execution.uuid}`,
type: 'customFlowNode',
hidden: true,
parentId: `action-${action.uuid}`,
position: { x: 0, y: 0 },
// width: nodeWidth,
data: {
// hidden: true,
// group: 'executions',
customNodeCardTitle: `Execution ${index + 1}`,
entityLabel: execution.name,
icon: 'fa fa-cogs',
Expand All @@ -639,8 +697,8 @@ export function useTransformTriggerObjectToNodesAndEdges(
edges.push(
...tempExec.map((execution) => ({
id: `e1-execution-${execution.id}`,
source: `action-${action.uuid}`,
target: execution.id,
target: `action-${action.uuid}`,
source: execution.id,
type: 'floating',
markerEnd: { type: MarkerType.Arrow },
})),
Expand Down
Loading

0 comments on commit c33767a

Please sign in to comment.