diff --git a/src/components/Graph/Graph.tsx b/src/components/Graph/Graph.tsx index 4e308564..7b701009 100644 --- a/src/components/Graph/Graph.tsx +++ b/src/components/Graph/Graph.tsx @@ -19,6 +19,7 @@ import ReactFlow, { useNodesState, useOnSelectionChange, useUpdateNodeInternals, + Controls, } from "reactflow"; import "reactflow/dist/style.css"; @@ -160,11 +161,33 @@ const GraphProvided: FC = ({ // Regularly update the node internals to make sure edges are consistent const updateNodeInternals = useUpdateNodeInternals(); + const prevNodeWidths = useRef>( + new Map(), + ); useEffect(() => { nodes.forEach((node) => { - updateNodeInternals(node.id); + if (prevNodeWidths.current.get(node.id) !== node.width) { + prevNodeWidths.current.set(node.id, node.width); + updateNodeInternals(node.id); + } }); - }, [nodes.length]); + }, [nodes]); + + // For the first 3 seconds after mounting, we want to updateNodeInternals for all nodes every 100ms + const [firstUpdate, setFirstUpdate] = useState(true); + useEffect(() => { + if (firstUpdate) { + const interval = setInterval(() => { + nodes.forEach((node) => { + updateNodeInternals(node.id); + }); + }, 100); + setTimeout(() => { + clearInterval(interval); + setFirstUpdate(false); + }, 10000); + } + }, [firstUpdate]); // Record Optimization ------------------------------------------------------- @@ -498,13 +521,19 @@ const GraphProvided: FC = ({ selectionMode={SelectionMode.Partial} zoomOnDoubleClick={true} className="h-full w-full" + maxZoom={1.5} + minZoom={0.25} > - + diff --git a/src/components/Graph/Legend/Legend.tsx b/src/components/Graph/Legend/Legend.tsx index 3069e3ca..4fd62686 100644 --- a/src/components/Graph/Legend/Legend.tsx +++ b/src/components/Graph/Legend/Legend.tsx @@ -1,46 +1,79 @@ import { FC } from "react"; +import { InformationCircleIcon } from "@heroicons/react/16/solid"; import "./LegendPath.css"; +const RightTip: FC = () => { + return ( + + + + + +

Right

+
+ ); +}; + +const LeftTip: FC = () => { + return ( + + + + + +

Left

+
+ ); +}; + +const RiskTip: FC = () => { + return ( + +

+ 8.2 +

+

Risk

+
+ ); +}; + const Legend: FC = () => { return ( -
- - - - - -

Right

-
- - - - - -

Left

-
+
+

+ + LEGEND +

+
+ + +
+
+ +
); }; diff --git a/src/components/Graph/custom_elements/edges/TransfershipEdge/CustomEdgePath.css b/src/components/Graph/custom_elements/edges/TransfershipEdge/CustomEdgePath.css index 164b6338..18b56985 100644 --- a/src/components/Graph/custom_elements/edges/TransfershipEdge/CustomEdgePath.css +++ b/src/components/Graph/custom_elements/edges/TransfershipEdge/CustomEdgePath.css @@ -7,6 +7,6 @@ .animated-dotted-line { stroke-dasharray: 15, 15; stroke-dashoffset: 30; - /* animation: dash 0.6s linear infinite; */ + animation: dash 0.6s linear infinite; stroke-linecap: round; /* Add this line */ } diff --git a/src/components/Graph/custom_elements/edges/TransfershipEdge/CustomEdgePath.tsx b/src/components/Graph/custom_elements/edges/TransfershipEdge/CustomEdgePath.tsx index 73295c37..c8ae49c5 100644 --- a/src/components/Graph/custom_elements/edges/TransfershipEdge/CustomEdgePath.tsx +++ b/src/components/Graph/custom_elements/edges/TransfershipEdge/CustomEdgePath.tsx @@ -87,6 +87,9 @@ const CustomEdgePath = ({ strokeWidth={strokeWidth} markerEnd={`url(#${triangleMarkerID})`} opacity={opacity} + style={{ + transition: "stroke-width 1s ease-in-out", + }} /> {isClickable && ( diff --git a/src/components/Graph/custom_elements/nodes/AddressNode/AddressNode/AddressNode.tsx b/src/components/Graph/custom_elements/nodes/AddressNode/AddressNode/AddressNode.tsx index 434258a5..bdc9f061 100644 --- a/src/components/Graph/custom_elements/nodes/AddressNode/AddressNode/AddressNode.tsx +++ b/src/components/Graph/custom_elements/nodes/AddressNode/AddressNode/AddressNode.tsx @@ -15,6 +15,7 @@ import { createTransfershipEdge, TransfershipEdgeStates, } from "../../../edges/TransfershipEdge"; +import { Transition } from "@headlessui/react"; /** Context data for the AddressNode */ @@ -146,39 +147,47 @@ const AddressNode: FC = ({ data: { address } }) => { isConnectable={false} /> - { - if (analysisData) { - setFocusedAddressData(analysisData); - } - }} + - {/* Address Risk inside a badge */} - - - {/* Address information */} -
-

- {`${address.slice(0, 5)}...${address.slice(-5)}`} - {analysisData && analysisData.labels.length > 0 && ( - - )} -

- {analysisData && } -
-
+ { + if (analysisData) { + setFocusedAddressData(analysisData); + } + }} + > + {/* Address Risk inside a badge */} + + + {/* Address information */} +
+

+ {`${address.slice(0, 5)}...${address.slice(-5)}`} + {analysisData && analysisData.labels.length > 0 && ( + + )} +

+ {analysisData && } +
+
+ ); }; diff --git a/src/components/Graph/graph_calculations.tsx b/src/components/Graph/graph_calculations.tsx index 9665dd3d..f38847ac 100644 --- a/src/components/Graph/graph_calculations.tsx +++ b/src/components/Graph/graph_calculations.tsx @@ -301,7 +301,6 @@ export function calculateLayoutedElements( Dagre.layout(dagreGraph); const newNodes: Node[] = [...nodes]; - dagreGraph.nodes().forEach((nodeId) => { const node = newNodes.find((n) => n.id === nodeId); if (node) {