Skip to content

Commit 9c9d9a0

Browse files
committed
fix search node
1 parent 9cbae93 commit 9c9d9a0

3 files changed

Lines changed: 94 additions & 13 deletions

File tree

app/components/code-graph.tsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import dynamic from 'next/dynamic';
1515
import { Position } from "./graphView";
1616
import { prepareArg } from '../utils';
1717
import { NodeObject, ForceGraphMethods } from "react-force-graph-2d";
18+
import { handleZoomToFit } from "@/lib/utils";
1819

1920
const GraphView = dynamic(() => import('./graphView'));
2021

@@ -68,6 +69,7 @@ export function CodeGraph({
6869
const [cooldownTicks, setCooldownTicks] = useState<number | undefined>(0)
6970
const [cooldownTime, setCooldownTime] = useState<number>(0)
7071
const containerRef = useRef<HTMLDivElement>(null);
72+
const [zoomedNodes, setZoomedNodes] = useState<Node[]>([]);
7173

7274
useEffect(() => {
7375
setData({ ...graph.Elements })
@@ -167,17 +169,17 @@ export function CodeGraph({
167169
}
168170

169171
const deleteNeighbors = (nodes: Node[]) => {
170-
172+
171173
if (nodes.length === 0) return;
172-
174+
173175
const expandedNodes: Node[] = []
174-
176+
175177
graph.Elements = {
176178
nodes: graph.Elements.nodes.filter(node => {
177179
if (!node.collapsed) return true
178-
180+
179181
const isTarget = graph.Elements.links.some(link => link.target.id === node.id && nodes.some(n => n.id === link.source.id));
180-
182+
181183
if (!isTarget) return true
182184

183185
const deleted = graph.NodesMap.delete(Number(node.id))
@@ -190,7 +192,7 @@ export function CodeGraph({
190192
}),
191193
links: graph.Elements.links
192194
}
193-
195+
194196
deleteNeighbors(expandedNodes)
195197

196198
graph.removeLinks()
@@ -239,12 +241,12 @@ export function CodeGraph({
239241
}
240242
graph.visibleLinks(true, [chartNode!.id])
241243
setData({ ...graph.Elements })
244+
setZoomedNodes([node])
245+
return
242246
}
243-
247+
248+
handleZoomToFit(chartRef, 4, (n: NodeObject<Node>) => n.id === node.id)
244249
setSearchNode(chartNode)
245-
setTimeout(() => {
246-
chart.zoomToFit(1000, 150, (n: NodeObject<Node>) => n.id === chartNode!.id);
247-
}, 0)
248250
}
249251
}
250252

@@ -307,7 +309,9 @@ export function CodeGraph({
307309
graph={graph}
308310
onValueChange={(node) => setSearchNode(node)}
309311
icon={<Search />}
310-
handleSubmit={handleSearchSubmit}
312+
handleSubmit={(node) => {
313+
handleSearchSubmit(node)
314+
}}
311315
node={searchNode}
312316
/>
313317
<Labels categories={graph.Categories} onClick={onCategoryClick} />
@@ -421,6 +425,8 @@ export function CodeGraph({
421425
setCooldownTicks={setCooldownTicks}
422426
cooldownTime={cooldownTime}
423427
setCooldownTime={setCooldownTime}
428+
setZoomedNodes={setZoomedNodes}
429+
zoomedNodes={zoomedNodes}
424430
/>
425431
</div>
426432
: <div className="flex flex-col items-center justify-center h-full text-gray-400">

app/components/graphView.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11

2-
import ForceGraph2D, { ForceGraphMethods } from 'react-force-graph-2d';
2+
import ForceGraph2D, { ForceGraphMethods, NodeObject } from 'react-force-graph-2d';
33
import { Graph, GraphData, Link, Node } from './model';
44
import { Dispatch, RefObject, SetStateAction, useEffect, useRef, useState } from 'react';
55
import { Path } from '../page';
6+
import { handleZoomToFit } from '@/lib/utils';
67

78
export interface Position {
89
x: number,
@@ -30,6 +31,8 @@ interface Props {
3031
setCooldownTicks: Dispatch<SetStateAction<number | undefined>>
3132
cooldownTime: number | undefined
3233
setCooldownTime: Dispatch<SetStateAction<number>>
34+
setZoomedNodes: Dispatch<SetStateAction<Node[]>>
35+
zoomedNodes: Node[]
3336
}
3437

3538
const PATH_COLOR = "#ffde21"
@@ -56,7 +59,9 @@ export default function GraphView({
5659
cooldownTicks,
5760
cooldownTime,
5861
setCooldownTicks,
59-
setCooldownTime
62+
setCooldownTime,
63+
zoomedNodes,
64+
setZoomedNodes
6065
}: Props) {
6166

6267
const parentRef = useRef<HTMLDivElement>(null)
@@ -295,6 +300,8 @@ export default function GraphView({
295300
onEngineStop={() => {
296301
setCooldownTicks(0)
297302
setCooldownTime(0)
303+
handleZoomToFit(chartRef, zoomedNodes.length === 1 ? 4 : 1, (n: NodeObject<Node>) => zoomedNodes.some(node => node.id === n.id))
304+
setZoomedNodes([])
298305
}}
299306
cooldownTicks={cooldownTicks}
300307
cooldownTime={cooldownTime}

lib/utils.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,74 @@
11
import { type ClassValue, clsx } from "clsx"
22
import { twMerge } from "tailwind-merge"
3+
import { ForceGraphMethods, NodeObject } from "react-force-graph-2d"
4+
import { Link, Node } from "@/app/components/model"
5+
import { MutableRefObject } from "react"
6+
7+
export type PathData = {
8+
nodes: any[]
9+
links: any[]
10+
}
11+
12+
export type PathNode = {
13+
id?: number
14+
name?: string
15+
}
16+
17+
export type Path = {
18+
start?: PathNode,
19+
end?: PathNode
20+
}
21+
22+
export enum MessageTypes {
23+
Query,
24+
Response,
25+
Path,
26+
PathResponse,
27+
Pending,
28+
Text,
29+
}
30+
31+
export interface Message {
32+
type: MessageTypes;
33+
text?: string;
34+
paths?: { nodes: any[], links: any[] }[];
35+
graphName?: string;
36+
}
37+
38+
export type GraphRef = MutableRefObject<ForceGraphMethods<Node, Link> | undefined>
339

440
export function cn(...inputs: ClassValue[]) {
541
return twMerge(clsx(inputs))
642
}
43+
44+
export function handleZoomToFit(chartRef: GraphRef, paddingMultiplier = 1, filter?: (node: NodeObject<Node>) => boolean) {
45+
const chart = chartRef.current
46+
if (chart) {
47+
// Find the currently visible canvas by checking display property
48+
const canvases = document.querySelectorAll('.force-graph-container canvas') as NodeListOf<HTMLCanvasElement>;
49+
const container = Array.from(canvases).find(canvas => {
50+
const container = canvas.parentElement;
51+
52+
if (!container) return false;
53+
54+
// Check if element is actually in viewport
55+
const rect = container.getBoundingClientRect();
56+
const isInViewport = rect.width > 0 &&
57+
rect.height > 0 &&
58+
rect.top >= 0 &&
59+
rect.left >= 0 &&
60+
rect.bottom <= window.innerHeight &&
61+
rect.right <= window.innerWidth;
62+
63+
return isInViewport;
64+
})?.parentElement;
65+
66+
if (!container) return;
67+
68+
// Calculate padding as 10% of the smallest canvas dimension
69+
const minDimension = Math.min(container.clientWidth, container.clientHeight);
70+
const padding = minDimension * 0.1 * paddingMultiplier;
71+
72+
chart.zoomToFit(1000, padding, filter);
73+
}
74+
}

0 commit comments

Comments
 (0)