feat(FN-4549): complete Step 4 — align edges and bounds to measured heights

Fusion-Task-Id: FN-4549
Fusion-Task-Lineage: 7c6b974d-cad9-418c-bf02-9d8490725c6e
This commit is contained in:
Fusion
2026-05-14 20:21:51 -07:00
committed by gsxdsm
parent d3dd9bd38c
commit 7b35a5b426
3 changed files with 29 additions and 12 deletions

View File

@@ -227,9 +227,13 @@ export function DependencyGraph({
const viewport = viewportRef.current;
if (!viewport) return;
fitToGraph(positions, viewport.clientWidth, viewport.clientHeight, { nodeWidth: NODE_WIDTH, nodeHeight: NODE_HEIGHT });
fitToGraph(positions, viewport.clientWidth, viewport.clientHeight, {
nodeWidth: NODE_WIDTH,
nodeHeight: NODE_HEIGHT,
measuredHeights,
});
initialFitDoneRef.current = true;
}, [filteredTasks.length, fitToGraph, positions, savedPositions]);
}, [filteredTasks.length, fitToGraph, measuredHeights, positions, savedPositions]);
const bounds = useMemo(() => {
const values = Array.from(positions.values());
@@ -240,7 +244,7 @@ export function DependencyGraph({
const minX = Math.min(...values.map((pos) => pos.x));
const minY = Math.min(...values.map((pos) => pos.y));
const maxX = Math.max(...values.map((pos) => pos.x + NODE_WIDTH));
const maxY = Math.max(...values.map((pos) => pos.y + NODE_HEIGHT));
const maxY = Math.max(...Array.from(positions.entries()).map(([taskId, pos]) => pos.y + (measuredHeights.get(taskId) ?? NODE_HEIGHT)));
return {
minX,
@@ -250,7 +254,7 @@ export function DependencyGraph({
width: Math.max(0, maxX - minX),
height: Math.max(0, maxY - minY),
};
}, [positions]);
}, [measuredHeights, positions]);
const normalizedPositions = useMemo(() => {
if (positions.size === 0) return positions;
@@ -333,7 +337,11 @@ export function DependencyGraph({
onKeyDown={(event) => {
const viewport = viewportRef.current;
if (!viewport) return;
handleKeyDown(event, viewport.clientWidth, viewport.clientHeight, normalizedPositions, { nodeWidth: NODE_WIDTH, nodeHeight: NODE_HEIGHT });
handleKeyDown(event, viewport.clientWidth, viewport.clientHeight, normalizedPositions, {
nodeWidth: NODE_WIDTH,
nodeHeight: NODE_HEIGHT,
measuredHeights,
});
}}
tabIndex={0}
onClick={() => {
@@ -350,6 +358,7 @@ export function DependencyGraph({
positions={normalizedPositions}
nodeWidth={NODE_WIDTH}
nodeHeight={NODE_HEIGHT}
nodeHeights={measuredHeights}
highlightedEdgeIds={
highlightedTaskIds.size > 0
? new Set(
@@ -441,7 +450,11 @@ export function DependencyGraph({
for (const [taskId, position] of freshLayout.entries()) {
normalizedFreshLayout.set(taskId, { x: position.x - minX, y: position.y - minY });
}
fitToGraph(normalizedFreshLayout, viewport.clientWidth, viewport.clientHeight, { nodeWidth: NODE_WIDTH, nodeHeight: NODE_HEIGHT });
fitToGraph(normalizedFreshLayout, viewport.clientWidth, viewport.clientHeight, {
nodeWidth: NODE_WIDTH,
nodeHeight: NODE_HEIGHT,
measuredHeights,
});
}}
onResetView={() => {
handleResetLayout();

View File

@@ -6,6 +6,7 @@ interface GraphEdgesProps {
positions: Map<string, GraphPosition>;
nodeWidth?: number;
nodeHeight?: number;
nodeHeights?: ReadonlyMap<string, number>;
highlightedEdgeIds?: Set<string>;
}
@@ -17,6 +18,7 @@ export function GraphEdges({
positions,
nodeWidth = DEFAULT_NODE_WIDTH,
nodeHeight = DEFAULT_NODE_HEIGHT,
nodeHeights,
highlightedEdgeIds,
}: GraphEdgesProps) {
const hasHighlights = Boolean(highlightedEdgeIds && highlightedEdgeIds.size > 0);
@@ -44,7 +46,7 @@ export function GraphEdges({
const edgeId = `${edge.source}->${edge.target}`;
const isActiveHighlight = hasHighlights && (highlightedEdgeIds?.has(edgeId) ?? false);
const x1 = source.x + nodeWidth / 2;
const y1 = source.y + nodeHeight;
const y1 = source.y + (nodeHeights?.get(edge.source) ?? nodeHeight);
const x2 = target.x + nodeWidth / 2;
const y2 = target.y;
const controlY = y1 + (y2 - y1) / 2;

View File

@@ -172,11 +172,13 @@ export function useGraphInteraction() {
const nodeWidth = layoutOptions?.nodeWidth ?? 280;
const nodeHeight = layoutOptions?.nodeHeight ?? 100;
const entries = Array.from(positions.values());
const minX = Math.min(...entries.map((p) => p.x));
const minY = Math.min(...entries.map((p) => p.y));
const maxX = Math.max(...entries.map((p) => p.x + nodeWidth));
const maxY = Math.max(...entries.map((p) => p.y + nodeHeight));
const entries = Array.from(positions.entries());
const minX = Math.min(...entries.map(([, p]) => p.x));
const minY = Math.min(...entries.map(([, p]) => p.y));
const maxX = Math.max(...entries.map(([, p]) => p.x + nodeWidth));
const maxY = Math.max(
...entries.map(([taskId, p]) => p.y + (layoutOptions?.measuredHeights?.get(taskId) ?? nodeHeight)),
);
const graphWidth = Math.max(1, maxX - minX);
const graphHeight = Math.max(1, maxY - minY);