Politur: Robustes ID-Parsing bei D3-Objektmutationen fuer hover/degree in GraphView behoben
This commit is contained in:
+7
-7
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -7,7 +7,7 @@
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<title>Mission Control 2.0</title>
|
||||
<script type="module" crossorigin src="/assets/index-C6kUDd9F.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-Bz6q5kVA.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index--2fz5jZz.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -61,11 +61,16 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
}
|
||||
}, [data])
|
||||
|
||||
// Grad (Degree) berechnen – fängt D3-Objektmutationen robust ab!
|
||||
const degree = useMemo(() => {
|
||||
const d: Record<string, number> = {}
|
||||
data.edges.forEach((e) => {
|
||||
d[e.source] = (d[e.source] || 0) + 1
|
||||
d[e.target] = (d[e.target] || 0) + 1
|
||||
const sId = typeof e.source === "object" ? (e.source as any).id : e.source
|
||||
const tId = typeof e.target === "object" ? (e.target as any).id : e.target
|
||||
if (sId && tId) {
|
||||
d[sId] = (d[sId] || 0) + 1
|
||||
d[tId] = (d[tId] || 0) + 1
|
||||
}
|
||||
})
|
||||
return d
|
||||
}, [data])
|
||||
@@ -98,24 +103,20 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
fg.d3Force("center", null)
|
||||
|
||||
// 2. Clustering-Kräfte (Knoten zu ihren jeweiligen Sektions-Foci ziehen)
|
||||
// Reduzierte Stärke (0.07 statt 0.18), damit sie sich nicht zu einem dichten Brei zusammenballen.
|
||||
fg.d3Force("x", forceX().x((node: any) => getClusterX(node.category)).strength(0.07))
|
||||
fg.d3Force("y", forceY().y((node: any) => getClusterY(node.category)).strength(0.07))
|
||||
|
||||
// 3. Starke Abstoßung (Charge) für ein weit gefächertes, luftiges Netz
|
||||
// Erhöht auf -380, damit sich die Knoten kräftiger abstoßen.
|
||||
const charge = fg.d3Force("charge")
|
||||
if (charge) charge.strength(-380)
|
||||
|
||||
// 4. Kollisionskraft (Sicherer Radius, verhindert Überlappung)
|
||||
// Wenn der Knoten hovered oder selected ist, geben wir ihm mehr Platz
|
||||
fg.d3Force("collide", forceCollide().radius((node: any) => {
|
||||
const isLarge = node.id === hovered || node.id === selectedNodeId
|
||||
return isLarge ? 50 : 25
|
||||
}).iterations(2))
|
||||
|
||||
// 5. Federkraft (Links)
|
||||
// Längere Verbindungsstriche (85 statt 60), damit das Netz übersichtlicher und "verbindungsreicher" wirkt.
|
||||
const link = fg.d3Force("link")
|
||||
if (link) link.distance(85)
|
||||
|
||||
@@ -131,13 +132,15 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
fgRef.current.zoomToFit(400, 40)
|
||||
}
|
||||
|
||||
// Nachbarmenge für extrem schnellen Lookup im Render-Loop
|
||||
// Nachbarmenge für extrem schnellen Lookup – fängt D3-Objektmutationen robust ab!
|
||||
const connectedNodes = useMemo(() => {
|
||||
if (!activeNode) return new Set<string>()
|
||||
const set = new Set<string>()
|
||||
data.edges.forEach((e) => {
|
||||
if (e.source === activeNode) set.add(e.target)
|
||||
if (e.target === activeNode) set.add(e.source)
|
||||
const sId = typeof e.source === "object" ? (e.source as any).id : e.source
|
||||
const tId = typeof e.target === "object" ? (e.target as any).id : e.target
|
||||
if (sId === activeNode) set.add(tId)
|
||||
if (tId === activeNode) set.add(sId)
|
||||
})
|
||||
return set
|
||||
}, [activeNode, data])
|
||||
@@ -154,11 +157,12 @@ export function GraphView({ data, selectedNodeId, onNodeSelect }: {
|
||||
backgroundColor="rgba(0,0,0,0)"
|
||||
cooldownTicks={120}
|
||||
|
||||
// Exakte Hit-Map auf unsichtbarem Canvas zeichnen für 100% zuverlässiges Hovern und Klicken
|
||||
// Exakte Hit-Map auf unsichtbarem Canvas zeichnen – fängt D3-Objektmutationen robust ab!
|
||||
nodePointerAreaPaint={(node: any, color: string, ctx: CanvasRenderingContext2D) => {
|
||||
const x = node.x ?? 0
|
||||
const y = node.y ?? 0
|
||||
const size = 6 + Math.min(degree[node.id] || 0, 15) * 0.8
|
||||
const nId = typeof node === "object" ? node.id : node
|
||||
const size = 6 + Math.min(degree[nId] || 0, 15) * 0.8
|
||||
ctx.fillStyle = color
|
||||
ctx.beginPath()
|
||||
ctx.arc(x, y, size + 5, 0, 2 * Math.PI, false) // 5px Puffer für einfaches Treffen
|
||||
|
||||
Reference in New Issue
Block a user