11import { Dispatch , RefObject , SetStateAction , useContext , useEffect , useRef , useState } from "react" ;
2- import { GraphData , Node } from "./model" ;
2+ import { GraphData , Link , Node } from "./model" ;
33import { GraphContext } from "./provider" ;
44import { Toolbar } from "./toolbar" ;
55import { Labels } from "./labels" ;
6- import { GitFork , Search , X } from "lucide-react" ;
6+ import { Download , GitFork , Search , X } from "lucide-react" ;
77import ElementMenu from "./elementMenu" ;
88import Combobox from "./combobox" ;
99import { toast } from '@/components/ui/use-toast' ;
@@ -21,7 +21,7 @@ const GraphView = dynamic(() => import('./graphView'));
2121interface Props {
2222 data : GraphData ,
2323 setData : Dispatch < SetStateAction < GraphData > > ,
24- onFetchGraph : ( graphName : string ) => void ,
24+ onFetchGraph : ( graphName : string ) => Promise < void > ,
2525 onFetchNode : ( nodeIds : number [ ] ) => Promise < GraphData > ,
2626 options : string [ ]
2727 setOptions : Dispatch < SetStateAction < string [ ] > >
@@ -55,7 +55,7 @@ export function CodeGraph({
5555 let graph = useContext ( GraphContext )
5656
5757 const [ url , setURL ] = useState ( "" ) ;
58- const [ selectedObj , setSelectedObj ] = useState < Node > ( ) ;
58+ const [ selectedObj , setSelectedObj ] = useState < Node | Link > ( ) ;
5959 const [ selectedObjects , setSelectedObjects ] = useState < Node [ ] > ( [ ] ) ;
6060 const [ position , setPosition ] = useState < Position > ( ) ;
6161 const [ graphName , setGraphName ] = useState < string > ( "" ) ;
@@ -145,9 +145,10 @@ export function CodeGraph({
145145 }
146146
147147 run ( )
148+
148149 } , [ graphName ] )
149150
150- function handleSelectedValue ( value : string ) {
151+ async function handleSelectedValue ( value : string ) {
151152 setGraphName ( value )
152153 onFetchGraph ( value )
153154 }
@@ -166,29 +167,38 @@ export function CodeGraph({
166167 }
167168
168169 const deleteNeighbors = ( nodes : Node [ ] ) => {
170+
169171 if ( nodes . length === 0 ) return ;
170-
172+
173+ const expandedNodes : Node [ ] = [ ]
174+
171175 graph . Elements = {
172- nodes : graph . Elements . nodes . map ( node => {
176+ nodes : graph . Elements . nodes . filter ( node => {
177+ if ( ! node . collapsed ) return true
178+
173179 const isTarget = graph . Elements . links . some ( link => link . target . id === node . id && nodes . some ( n => n . id === link . source . id ) ) ;
180+
181+ debugger
174182
175- if ( ! isTarget || ! node . collapsed ) return node
183+ if ( ! isTarget ) return true
176184
177- if ( node . expand ) {
178- node . expand = false
179- deleteNeighbors ( [ node ] )
185+ const deleted = graph . NodesMap . delete ( Number ( node . id ) )
186+
187+ if ( deleted && node . expand ) {
188+ expandedNodes . push ( node )
180189 }
181190
182- graph . NodesMap . delete ( Number ( node . id ) )
183- } ) . filter ( node => node !== undefined ) ,
191+ return false
192+ } ) ,
184193 links : graph . Elements . links
185194 }
195+
196+ deleteNeighbors ( expandedNodes )
186197
187198 graph . removeLinks ( )
188199 }
189200
190201 const handleExpand = async ( nodes : Node [ ] , expand : boolean ) => {
191-
192202 if ( expand ) {
193203 const elements = await onFetchNode ( nodes . map ( n => n . id ) )
194204
@@ -216,12 +226,11 @@ export function CodeGraph({
216226
217227 const handleSearchSubmit = ( node : any ) => {
218228 const chart = chartRef . current
219-
229+
220230 if ( chart ) {
221- const n = { name : node . properties . name , id : node . id }
222231
223232 let chartNode = graph . Elements . nodes . find ( n => n . id == node . id )
224-
233+
225234 if ( ! chartNode ?. visible ) {
226235 if ( ! chartNode ) {
227236 chartNode = graph . extend ( { nodes : [ node ] , edges : [ ] } ) . nodes [ 0 ]
@@ -233,8 +242,8 @@ export function CodeGraph({
233242 graph . visibleLinks ( true , [ chartNode ! . id ] )
234243 setData ( { ...graph . Elements } )
235244 }
236-
237- setSearchNode ( n )
245+
246+ setSearchNode ( chartNode )
238247 setTimeout ( ( ) => {
239248 chart . zoomToFit ( 1000 , 150 , ( n : NodeObject < Node > ) => n . id === chartNode ! . id ) ;
240249 } , 0 )
@@ -252,6 +261,33 @@ export function CodeGraph({
252261 setData ( { ...graph . Elements } )
253262 }
254263
264+ const handleDownloadImage = async ( ) => {
265+ try {
266+ const canvas = document . querySelector ( '.force-graph-container canvas' ) as HTMLCanvasElement ;
267+ if ( ! canvas ) {
268+ toast ( {
269+ title : "Error" ,
270+ description : "Canvas not found" ,
271+ variant : "destructive" ,
272+ } ) ;
273+ return ;
274+ }
275+
276+ const dataURL = canvas . toDataURL ( 'image/webp' ) ;
277+ const link = document . createElement ( 'a' ) ;
278+ link . href = dataURL ;
279+ link . download = `${ graphName } .webp` ;
280+ link . click ( ) ;
281+ } catch ( error ) {
282+ console . error ( 'Error downloading graph image:' , error ) ;
283+ toast ( {
284+ title : "Error" ,
285+ description : "Failed to download image. Please try again." ,
286+ variant : "destructive" ,
287+ } ) ;
288+ }
289+ } ;
290+
255291 return (
256292 < div className = "h-full w-full flex flex-col gap-4 p-8 bg-gray-100" >
257293 < header className = "flex flex-col gap-4" >
@@ -271,8 +307,7 @@ export function CodeGraph({
271307 < div className = 'flex gap-4' >
272308 < Input
273309 graph = { graph }
274- value = { searchNode . name }
275- onValueChange = { ( { name } ) => setSearchNode ( { name } ) }
310+ onValueChange = { ( node ) => setSearchNode ( node ) }
276311 icon = { < Search /> }
277312 handleSubmit = { handleSearchSubmit }
278313 node = { searchNode }
@@ -345,6 +380,12 @@ export function CodeGraph({
345380 className = "pointer-events-auto"
346381 chartRef = { chartRef }
347382 />
383+ < button
384+ className = "pointer-events-auto bg-white p-2 rounded-md"
385+ onClick = { handleDownloadImage }
386+ >
387+ < Download />
388+ </ button >
348389 </ div >
349390 </ div >
350391 < ElementMenu
@@ -371,7 +412,7 @@ export function CodeGraph({
371412 setSelectedObjects = { setSelectedObjects }
372413 setPosition = { setPosition }
373414 onFetchNode = { onFetchNode }
374- deleteNeighbors = { deleteNeighbors }
415+ handleExpand = { handleExpand }
375416 isShowPath = { isShowPath }
376417 setPath = { setPath }
377418 isPathResponse = { isPathResponse }
0 commit comments