Skip to content

Commit 0f10b6f

Browse files
hide orphans node when view options are changing
1 parent 9eb2d08 commit 0f10b6f

2 files changed

Lines changed: 70 additions & 15 deletions

File tree

packages/app-builder/src/components/Graph/GraphImpl.tsx

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
withBestHandles,
2626
} from './GraphComponents';
2727
import { layoutGraphElements } from './layout-graph';
28-
import { toFlatFlowElements } from './utils';
28+
import { reachableNodeIds, toFlatFlowElements } from './utils';
2929
import '@xyflow/react/dist/style.css';
3030

3131
export { layoutGraphElements };
@@ -89,10 +89,22 @@ function applyVisibilityFilters(
8989
nodes: GraphRfNode[],
9090
edges: GraphRfEdge[],
9191
filters: VisibilityFilters,
92+
startKey: string,
9293
): { nodes: GraphRfNode[]; edges: GraphRfEdge[] } {
93-
const visibleNodes = nodes.filter((node) => isNodeVisible(node, filters));
94+
const typeVisibleNodes = nodes.filter((node) => isNodeVisible(node, filters));
95+
const typeVisibleIds = new Set(typeVisibleNodes.map((node) => node.id));
96+
const typeVisibleEdges = edges.filter((edge) => typeVisibleIds.has(edge.source) && typeVisibleIds.has(edge.target));
97+
98+
// Drop nodes disconnected from the start after type/attribute filters
99+
// (e.g. children of a hidden company).
100+
const reachable = reachableNodeIds(
101+
typeVisibleNodes.map((node) => node.id),
102+
typeVisibleEdges,
103+
startKey,
104+
);
105+
const visibleNodes = typeVisibleNodes.filter((node) => reachable.has(node.id));
94106
const visibleIds = new Set(visibleNodes.map((node) => node.id));
95-
const visibleEdges = edges.filter((edge) => visibleIds.has(edge.source) && visibleIds.has(edge.target));
107+
const visibleEdges = typeVisibleEdges.filter((edge) => visibleIds.has(edge.source) && visibleIds.has(edge.target));
96108
return { nodes: visibleNodes, edges: visibleEdges };
97109
}
98110

@@ -108,11 +120,16 @@ export function GraphImpl({ data, dataModel, maxExplorationHops = 0 }: GraphImpl
108120
);
109121

110122
const filteredLayout = useMemo(() => {
111-
const filtered = applyVisibilityFilters(flatGraph.nodes, flatGraph.edges, {
112-
showPersons,
113-
showCompanies,
114-
attributes,
115-
});
123+
const filtered = applyVisibilityFilters(
124+
flatGraph.nodes,
125+
flatGraph.edges,
126+
{
127+
showPersons,
128+
showCompanies,
129+
attributes,
130+
},
131+
flatGraph.startKey,
132+
);
116133
return layoutGraphElements(filtered.nodes, filtered.edges, flatGraph.startKey);
117134
}, [flatGraph, showPersons, showCompanies, attributes]);
118135

packages/app-builder/src/components/Graph/utils.ts

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,11 @@ export function toFlatFlowElements(
168168
return { nodes, edges, startKey };
169169
}
170170

171-
/**
172-
* BFS spanning-tree edges from `startKey` (parent → child) for Dagre ranking.
173-
* Handles cycles by only recording the first discovery edge.
174-
*/
175-
export function bfsSpanningTreeEdges(
171+
/** Undirected adjacency for the given node ids and edges. */
172+
function buildUndirectedIdAdjacency(
176173
nodeIds: string[],
177174
edges: Array<{ source: string; target: string }>,
178-
startKey: string,
179-
): Array<{ source: string; target: string }> {
175+
): Map<string, string[]> {
180176
const idSet = new Set(nodeIds);
181177
const adj = new Map<string, string[]>();
182178
for (const id of nodeIds) {
@@ -187,10 +183,52 @@ export function bfsSpanningTreeEdges(
187183
adj.get(edge.source)!.push(edge.target);
188184
adj.get(edge.target)!.push(edge.source);
189185
}
186+
return adj;
187+
}
190188

189+
/**
190+
* Node ids reachable from `startKey` via undirected edges.
191+
* Falls back to the first node id when `startKey` is missing.
192+
*/
193+
export function reachableNodeIds(
194+
nodeIds: string[],
195+
edges: Array<{ source: string; target: string }>,
196+
startKey: string,
197+
): Set<string> {
198+
const idSet = new Set(nodeIds);
199+
const root = idSet.has(startKey) ? startKey : (nodeIds[0] ?? '');
200+
if (!root) return new Set();
201+
202+
const adj = buildUndirectedIdAdjacency(nodeIds, edges);
203+
const visited = new Set<string>([root]);
204+
const queue = [root];
205+
206+
while (queue.length > 0) {
207+
const cur = queue.shift()!;
208+
for (const nxt of adj.get(cur) ?? []) {
209+
if (visited.has(nxt)) continue;
210+
visited.add(nxt);
211+
queue.push(nxt);
212+
}
213+
}
214+
215+
return visited;
216+
}
217+
218+
/**
219+
* BFS spanning-tree edges from `startKey` (parent → child) for Dagre ranking.
220+
* Handles cycles by only recording the first discovery edge.
221+
*/
222+
export function bfsSpanningTreeEdges(
223+
nodeIds: string[],
224+
edges: Array<{ source: string; target: string }>,
225+
startKey: string,
226+
): Array<{ source: string; target: string }> {
227+
const idSet = new Set(nodeIds);
191228
const root = idSet.has(startKey) ? startKey : (nodeIds[0] ?? '');
192229
if (!root) return [];
193230

231+
const adj = buildUndirectedIdAdjacency(nodeIds, edges);
194232
const tree: Array<{ source: string; target: string }> = [];
195233
const visited = new Set<string>([root]);
196234
const queue = [root];

0 commit comments

Comments
 (0)