Files
fusion/plugins/fusion-plugin-dependency-graph/src/hooks/useDependencyChain.ts
Fusion 6f63ee3fcd feat(FN-3088): add dependency graph highlighting with hover and selection s
The merge adds visual highlighting to the dependency graph plugin. A new `useDependencyChain` hook tracks dependency relationships, while `GraphTaskNode` and `DependencyGraph` gained hover, selection, and highlight visual states backed by new CSS. Tests cover the hook logic, highlighting behavior, a

Fusion-Task-Id: FN-3088
2026-05-07 04:48:19 -07:00

61 lines
1.7 KiB
TypeScript

import { useCallback, useMemo } from "react";
import type { Task } from "@fusion/core";
export function useDependencyChain(tasks: Task[]) {
const { upstreamMap, downstreamMap } = useMemo(() => {
const upstream = new Map<string, Set<string>>();
const downstream = new Map<string, Set<string>>();
for (const task of tasks) {
upstream.set(task.id, new Set(task.dependencies ?? []));
if (!downstream.has(task.id)) downstream.set(task.id, new Set());
}
for (const task of tasks) {
for (const dependencyId of task.dependencies ?? []) {
if (!downstream.has(dependencyId)) downstream.set(dependencyId, new Set());
downstream.get(dependencyId)?.add(task.id);
}
}
return { upstreamMap: upstream, downstreamMap: downstream };
}, [tasks]);
const getChain = useCallback(
(taskId: string): Set<string> => {
if (!upstreamMap.has(taskId) && !downstreamMap.has(taskId)) {
return new Set();
}
const chain = new Set<string>([taskId]);
const visit = (origin: string, adjacency: Map<string, Set<string>>) => {
const queue = [origin];
const visited = new Set<string>([origin]);
while (queue.length > 0) {
const current = queue.shift();
if (!current) continue;
const neighbors = adjacency.get(current);
if (!neighbors) continue;
for (const neighbor of neighbors) {
if (visited.has(neighbor)) continue;
visited.add(neighbor);
chain.add(neighbor);
queue.push(neighbor);
}
}
};
visit(taskId, upstreamMap);
visit(taskId, downstreamMap);
return chain;
},
[downstreamMap, upstreamMap],
);
return { getChain };
}