11// React component code
2- import { FC , RefObject , useContext , useEffect , useRef , useState } from 'react' ;
3- import { graphviz } from 'd3-graphviz' ;
2+ import React , { FC , RefObject , useContext , useEffect , useRef , useState } from 'react' ;
3+ import { graphviz } from 'd3-graphviz' ;
44import {
55 generateDotString ,
66 normalizeThicknesses ,
@@ -11,8 +11,8 @@ import {
1111} from './GraphvizProcessing' ;
1212import ErrorBoundary from "@/components/errorBoundary.tsx" ;
1313import '../GraphvizContainer.css' ;
14- import { Context } from "@/Context.tsx" ;
15- import { Button } from './ui/button' ;
14+ import { Context } from "@/Context.tsx" ;
15+ import { Button } from './ui/button' ;
1616
1717interface GraphvizParentProps {
1818 csvData : string ;
@@ -21,11 +21,16 @@ interface GraphvizParentProps {
2121 minVisits : number ;
2222}
2323
24- const GraphvizParent : FC < GraphvizParentProps > = ( { csvData, filter, selfLoops, minVisits} ) => {
24+ const GraphvizParent : React . FC < GraphvizParentProps > = ( {
25+ csvData,
26+ filter,
27+ selfLoops,
28+ minVisits,
29+ } ) => {
2530 const [ dotString , setDotString ] = useState < string | null > ( null ) ;
2631 const [ filteredDotString , setFilteredDotString ] = useState < string | null > ( null ) ;
2732 const [ topDotString , setTopDotString ] = useState < string | null > ( null ) ;
28- const { selectedSequence, setSelectedSequence, top5Sequences, setTop5Sequences} = useContext ( Context ) ;
33+ const { selectedSequence, setSelectedSequence, top5Sequences, setTop5Sequences } = useContext ( Context ) ;
2934
3035 // Refs for rendering the Graphviz graphs
3136 const graphRefMain = useRef < HTMLDivElement > ( null ) ;
@@ -121,33 +126,8 @@ const GraphvizParent: FC<GraphvizParentProps> = ({csvData, filter, selfLoops, mi
121126 }
122127 } , [ csvData , filter , selfLoops , minVisits , selectedSequence ] ) ;
123128
124- // Render Graphviz graphs using d3-graphviz
125- const renderGraph = (
126- dot : string | null ,
127- ref : React . RefObject < HTMLDivElement > ,
128- filename : string ,
129- numberOfGraphs : number
130- ) => {
131- if ( dot && ref . current ) {
132- // Dynamically adjust width based on the number of graphs
133- const width = numberOfGraphs === 3 ? 325 : 425 ; // Adjust the width for 3 graphs or 2 graphs
134- const height = 530 ; // Fixed height (or adjust dynamically if needed)
135-
136- graphviz ( ref . current )
137- . width ( width )
138- . height ( height )
139- . renderDot ( dot )
140- . on ( 'end' , ( ) => {
141- const svgElement = ref . current ?. querySelector ( 'svg' ) ;
142- if ( svgElement ) {
143- exportGraphAsPNG ( ref , filename ) ;
144- }
145- } ) ;
146- }
147- } ;
148-
149129 // Export a graph as high-quality PNG
150- const exportGraphAsPNG = ( graphRef : RefObject < HTMLDivElement > , filename : string ) => {
130+ const exportGraphAsPNG = ( graphRef : React . RefObject < HTMLDivElement > , filename : string ) => {
151131 if ( ! graphRef . current ) return ;
152132
153133 const svgElement = graphRef . current . querySelector ( 'svg' ) ;
@@ -197,17 +177,47 @@ const GraphvizParent: FC<GraphvizParentProps> = ({csvData, filter, selfLoops, mi
197177
198178 const numberOfGraphs = [ topDotString , dotString , filteredDotString ] . filter ( Boolean ) . length ;
199179
180+ // Render Graphviz graphs using d3-graphviz
181+ const renderGraph = (
182+ dot : string | null ,
183+ ref : React . RefObject < HTMLDivElement > ,
184+ filename : string ,
185+ numberOfGraphs : number
186+ ) => {
187+ if ( dot && ref . current ) {
188+ // Dynamically adjust width based on the number of graphs
189+ const width = numberOfGraphs === 3 ? 325 : 425 ;
190+ const height = 530 ;
191+
192+ try {
193+ graphviz ( ref . current )
194+ . width ( width )
195+ . height ( height )
196+ . renderDot ( dot )
197+ } catch ( error ) {
198+ console . error ( "Error rendering graph:" , error ) ;
199+ }
200+ }
201+ } ;
202+
203+
200204 useEffect ( ( ) => {
201- renderGraph ( filteredDotString , graphRefFiltered , 'filtered_graph' , numberOfGraphs ) ;
202- } , [ topDotString ] ) ;
205+ if ( topDotString && graphRefTop . current ) {
206+ renderGraph ( topDotString , graphRefTop , 'selected_sequence' , numberOfGraphs ) ;
207+ }
208+ } , [ topDotString , numberOfGraphs ] ) ;
203209
204210 useEffect ( ( ) => {
205- renderGraph ( topDotString , graphRefTop , 'selected_sequence' , numberOfGraphs ) ;
206- } , [ dotString ] ) ;
211+ if ( dotString && graphRefMain . current ) {
212+ renderGraph ( dotString , graphRefMain , 'all_students' , numberOfGraphs ) ;
213+ }
214+ } , [ dotString , numberOfGraphs ] ) ;
207215
208216 useEffect ( ( ) => {
209- renderGraph ( dotString , graphRefMain , 'all_students' , numberOfGraphs ) ;
210- } , [ filteredDotString ] ) ;
217+ if ( filteredDotString && graphRefFiltered . current ) {
218+ renderGraph ( filteredDotString , graphRefFiltered , 'filtered_graph' , numberOfGraphs ) ;
219+ }
220+ } , [ filteredDotString , numberOfGraphs ] ) ;
211221
212222
213223 return (
@@ -219,25 +229,25 @@ const GraphvizParent: FC<GraphvizParentProps> = ({csvData, filter, selfLoops, mi
219229 className = { `graph-item flex flex-col items-center ${ topDotString && dotString && filteredDotString ? 'w-[400px]' : 'w-[500px]' } border-2 border-gray-700 rounded-lg p-4 bg-gray-100` } >
220230 < h2 className = "text-lg font-semibold text-center mb-2" > Selected Sequence</ h2 >
221231 < div ref = { graphRefTop }
222- className = "w-full h-[575px] border-2 border-gray-700 rounded-lg p-4 bg-white items-center" > </ div >
223- < ExportButton onClick = { ( ) => exportGraphAsPNG ( graphRefTop , 'selected_sequence' ) } />
232+ className = "w-full h-[575px] border-2 border-gray-700 rounded-lg p-4 bg-white items-center" > </ div >
233+ < ExportButton onClick = { ( ) => exportGraphAsPNG ( graphRefTop , 'selected_sequence' ) } />
224234 </ div >
225235 ) }
226236 { dotString && (
227237 < div
228238 className = { `graph-item flex flex-col items-center ${ topDotString && dotString && filteredDotString ? 'w-[400px]' : 'w-[500px]' } border-2 border-gray-700 rounded-lg p-4 bg-gray-100` } >
229239 < h2 className = "text-lg font-semibold text-center mb-2" > All Students, All Paths</ h2 >
230240 < div ref = { graphRefMain }
231- className = "w-full h-[575px] border-2 border-gray-700 rounded-lg p-4 bg-white" > </ div >
232- < ExportButton onClick = { ( ) => exportGraphAsPNG ( graphRefMain , 'all_students' ) } /> </ div >
241+ className = "w-full h-[575px] border-2 border-gray-700 rounded-lg p-4 bg-white" > </ div >
242+ < ExportButton onClick = { ( ) => exportGraphAsPNG ( graphRefMain , 'all_students' ) } /> </ div >
233243 ) }
234244 { filteredDotString && (
235245 < div
236246 className = { `graph-item flex flex-col items-center ${ topDotString && dotString && filteredDotString ? 'w-[400px]' : 'w-[500px]' } border-2 border-gray-700 rounded-lg p-4 bg-gray-100` } >
237247 < h2 className = "text-lg font-semibold text-center mb-4" > Filtered Graph</ h2 >
238248 < div ref = { graphRefFiltered }
239- className = "w-full h-[575px] border-2 border-gray-700 rounded-lg p-4 bg-white" > </ div >
240- < ExportButton onClick = { ( ) => exportGraphAsPNG ( graphRefFiltered , 'filtered_graph' ) } />
249+ className = "w-full h-[575px] border-2 border-gray-700 rounded-lg p-4 bg-white" > </ div >
250+ < ExportButton onClick = { ( ) => exportGraphAsPNG ( graphRefFiltered , 'filtered_graph' ) } />
241251 </ div >
242252 ) }
243253 </ div >
@@ -254,7 +264,7 @@ interface ExportButtonProps {
254264 label ?: string ;
255265}
256266
257- function ExportButton ( { onClick, label = "Export Image" } : ExportButtonProps ) {
267+ function ExportButton ( { onClick, label = "Export Image" } : ExportButtonProps ) {
258268 return (
259269 < Button
260270 variant = { 'secondary' }
@@ -263,4 +273,4 @@ function ExportButton({onClick, label = "Export Image"}: ExportButtonProps) {
263273 { label }
264274 </ Button >
265275 ) ;
266- } ;
276+ }
0 commit comments