fix: canvas resize issues in graph visualization (#1167)

## Description
This PR adds **responsive resizing** to the graph container. The
`ForceGraph` component now dynamically adjusts its width and height when
the browser window is resized, improving the user experience by removing
the need to manually click the "Fit Into View" button. solver issue
#1164

## DCO Affirmation
I affirm that all code in every commit of this pull request conforms to
the terms of the Topoteretes Developer Certificate of Origin.

---

##  Changes Made (Minimal, Additive Only)

### 1. **Added necessary imports**
```typescript
import { MutableRefObject, useEffect, useImperativeHandle, useRef, useState, useCallback } from "react";
// Added useCallback to existing imports
```

### 2. **Added responsive sizing state and refs**
```typescript
// State for tracking container dimensions
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const containerRef = useRef<HTMLDivElement>(null);
```

### 3. **Added resize handling logic**
```typescript
// Handle window resize
const handleResize = useCallback(() => {
  if (containerRef.current) {
    const { clientWidth, clientHeight } = containerRef.current;
    setDimensions({ width: clientWidth, height: clientHeight });

    // Trigger graph refresh after resize
    if (graphRef.current) {
      // Small delay to ensure DOM has updated
      setTimeout(() => {
        graphRef.current?.refresh();
      }, 100);
    }
  }
}, []);

// Set up resize observer 
useEffect(() => {
  // Initial size calculation
  handleResize();

  // ResizeObserver for more precise container size tracking
  const resizeObserver = new ResizeObserver(() => {
    handleResize();
  });

  if (containerRef.current) {
    resizeObserver.observe(containerRef.current);
  }

  return () => {
    resizeObserver.disconnect();
  };
}, [handleResize]);
```

### 4. **Added container ref to wrapping div**
```tsx
<div ref={containerRef} className="w-full h-full" id="graph-container">
  {/* Graph component rendered here */}
</div>
```

### 5. **Passed dynamic width/height to ForceGraph**
```tsx
<ForceGraph
  ref={graphRef}
  width={dimensions.width}
  height={dimensions.height}
  dagMode={graphShape as unknown as undefined}
  // ... rest of props unchanged
/>
```

---

you can check this video out:


https://github.com/user-attachments/assets/e8e42c99-23e9-4acd-a51b-c59e8bee7094
This commit is contained in:
Boris 2025-07-30 11:16:38 +02:00 committed by GitHub
commit 2182f619df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,6 +1,6 @@
"use client";
import { MutableRefObject, useEffect, useImperativeHandle, useRef, useState } from "react";
import { MutableRefObject, useEffect, useImperativeHandle, useRef, useState, useCallback } from "react";
import { forceCollide, forceManyBody } from "d3-force-3d";
import ForceGraph, { ForceGraphMethods, GraphData, LinkObject, NodeObject } from "react-force-graph-2d";
import { GraphControlsAPI } from "./GraphControls";
@ -22,6 +22,45 @@ export default function GraphVisualization({ ref, data, graphControls }: GraphVi
const nodeSize = 15;
// const addNodeDistanceFromSourceNode = 15;
// State for tracking container dimensions
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const containerRef = useRef<HTMLDivElement>(null);
// Handle resize
const handleResize = useCallback(() => {
if (containerRef.current) {
const { clientWidth, clientHeight } = containerRef.current;
setDimensions({ width: clientWidth, height: clientHeight });
// Trigger graph refresh after resize
if (graphRef.current) {
// Small delay to ensure DOM has updated
setTimeout(() => {
graphRef.current?.zoomToFit(1000,50);
}, 100);
}
}
}, []);
// Set up resize observer
useEffect(() => {
// Initial size calculation
handleResize();
// ResizeObserver
const resizeObserver = new ResizeObserver(() => {
handleResize();
});
if (containerRef.current) {
resizeObserver.observe(containerRef.current);
}
return () => {
resizeObserver.disconnect();
};
}, [handleResize]);
const handleNodeClick = (node: NodeObject) => {
graphControls.current?.setSelectedNode(node);
// ref.current?.d3ReheatSimulation()
@ -174,10 +213,12 @@ export default function GraphVisualization({ ref, data, graphControls }: GraphVi
}));
return (
<div className="w-full h-full" id="graph-container">
<div ref={containerRef} className="w-full h-full" id="graph-container">
{(data && typeof window !== "undefined") ? (
<ForceGraph
ref={graphRef}
width={dimensions.width}
height={dimensions.height}
dagMode={graphShape as unknown as undefined}
dagLevelDistance={300}
onDagError={handleDagError}
@ -201,6 +242,8 @@ export default function GraphVisualization({ ref, data, graphControls }: GraphVi
) : (
<ForceGraph
ref={graphRef}
width={dimensions.width}
height={dimensions.height}
dagMode={graphShape as unknown as undefined}
dagLevelDistance={100}
graphData={{