diff --git a/package.json b/package.json index 6a3b1b1..454bcc5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-policy-topology", - "version": "0.1.5", + "version": "0.1.6", "type": "module", "main": "src/main.js", "module": "src/main.js", diff --git a/src/App.js b/src/App.js index d65e32b..afe893c 100644 --- a/src/App.js +++ b/src/App.js @@ -1,10 +1,7 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect } from 'react'; import PolicyTopology from './PolicyTopology.js'; import PickResource from './PickResource.js'; -import ResetPolicyTopology from './ResetPolicyTopology.js'; -import DotStringEditor from './DotStringEditor.js'; -import graphlib from 'graphlib'; -import * as dot from 'graphlib-dot'; +import * as dot from 'graphlib-dot'; // still needed for parsing dotString import './App.css'; function App() { @@ -125,67 +122,14 @@ function App() { setGraph(g); }, [dotString]); - const handleNodeSelection = useCallback((nodeId) => { - if (!graph) return; - - if (nodeId === null) { - setDotString(initialDotString); - return; - } - - const filteredGraph = new graphlib.Graph(); - const nodesToInclude = new Set(); - - const addPredecessors = (node) => { - if (!nodesToInclude.has(node)) { - nodesToInclude.add(node); - const predecessors = graph.predecessors(node) || []; - predecessors.forEach(addPredecessors); - } - }; - - const addSuccessors = (node) => { - const successors = graph.successors(node) || []; - successors.forEach(successor => { - nodesToInclude.add(successor); - }); - }; - - addPredecessors(nodeId); - addSuccessors(nodeId); - - nodesToInclude.forEach(node => { - filteredGraph.setNode(node, graph.node(node)); - }); - - graph.edges().forEach(edge => { - if (nodesToInclude.has(edge.v) && nodesToInclude.has(edge.w)) { - filteredGraph.setEdge(edge.v, edge.w, graph.edge(edge.v, edge.w)); - } - }); - - const filteredDotString = dot.write(filteredGraph); - setDotString(filteredDotString); - }, [graph, initialDotString]); - - const resetGraph = () => { - setDotString(initialDotString); - }; - - const handleDotStringChange = (newDotString) => { - setDotString(newDotString); - }; - return (

Policy Topology Example

- - + {}} />
- - +
); diff --git a/src/PolicyTopology.js b/src/PolicyTopology.js index 0430091..1d663b7 100644 --- a/src/PolicyTopology.js +++ b/src/PolicyTopology.js @@ -1,13 +1,66 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useRef, useState, useCallback } from 'react'; import * as d3 from 'd3'; import { graphviz } from 'd3-graphviz'; // eslint-disable-line no-unused-vars +import { Button } from '@patternfly/react-core'; +import graphlib from 'graphlib'; +import * as dot from 'graphlib-dot'; import './PolicyTopology.css'; -const PolicyTopology = ({ dotString, onNodeClick }) => { +const PolicyTopology = ({ initialDotString }) => { const containerRef = useRef(null); + const [dotString, setDotString] = useState(initialDotString); // State for the dot string + const [graph, setGraph] = useState(null); + // Parse the DOT string into a graph object useEffect(() => { - if (containerRef.current && dotString) { + if (dotString) { + const g = dot.read(dotString); + setGraph(g); + } + }, [dotString]); + + // Function to handle node selection and update the graph + const handleNodeSelection = useCallback((nodeId) => { + if (!graph) return; + + const filteredGraph = new graphlib.Graph(); + const nodesToInclude = new Set(); + + const addPredecessors = (node) => { + if (!nodesToInclude.has(node)) { + nodesToInclude.add(node); + const predecessors = graph.predecessors(node) || []; + predecessors.forEach(addPredecessors); + } + }; + + const addSuccessors = (node) => { + const successors = graph.successors(node) || []; + successors.forEach(successor => { + nodesToInclude.add(successor); + }); + }; + + addPredecessors(nodeId); + addSuccessors(nodeId); + + nodesToInclude.forEach(node => { + filteredGraph.setNode(node, graph.node(node)); + }); + + graph.edges().forEach(edge => { + if (nodesToInclude.has(edge.v) && nodesToInclude.has(edge.w)) { + filteredGraph.setEdge(edge.v, edge.w, graph.edge(edge.v, edge.w)); + } + }); + + const filteredDotString = dot.write(filteredGraph); + setDotString(filteredDotString); // Update the dotString state + }, [graph]); + + // Render the graph when the component mounts or the dotString changes + useEffect(() => { + if (containerRef.current && graph) { const renderGraph = () => { d3.select(containerRef.current).graphviz() .zoom(false) @@ -19,7 +72,7 @@ const PolicyTopology = ({ dotString, onNodeClick }) => { node.addEventListener('click', (event) => { const nodeElement = event.target.closest('g.node'); const nodeId = nodeElement.querySelector('title').textContent; - onNodeClick(nodeId); + handleNodeSelection(nodeId); }); }); }); @@ -27,9 +80,21 @@ const PolicyTopology = ({ dotString, onNodeClick }) => { renderGraph(); } - }, [dotString, onNodeClick]); + }, [graph, dotString, handleNodeSelection]); + + // Function to reset the graph to its initial state + const resetGraph = useCallback(() => { + setDotString(initialDotString); // Reset the dotString state + }, [initialDotString]); - return
; + return ( +
+
+ +
+ ); }; export default PolicyTopology;