Skip to content

Commit eb2b7d8

Browse files
committed
fix download
1 parent bc27faf commit eb2b7d8

File tree

1 file changed

+109
-5
lines changed

1 file changed

+109
-5
lines changed

src/views/GraphControls.tsx

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ const GraphSearch: FC = () => {
174174
const GraphControls: FC = () => {
175175
const sigma = useSigma();
176176
const graph = sigma.getGraph();
177-
const { graphFile } = useContext(GraphContext);
177+
const { graphFile, navState, hovered, computedData } = useContext(GraphContext);
178178

179179
const zoom = useCallback(
180180
(ratio?: number): void => {
@@ -201,17 +201,121 @@ const GraphControls: FC = () => {
201201

202202
const downloadGraph = useCallback(() => {
203203
if (graphFile) {
204-
const blob = new Blob([graphFile.textContent], { type: "application/xml" });
204+
// Parse the existing GEXF content
205+
const parser = new DOMParser();
206+
const xmlDoc = parser.parseFromString(graphFile.textContent, "text/xml");
207+
208+
// Get the graph element
209+
const graphElement = xmlDoc.querySelector('graph');
210+
if (!graphElement) return;
211+
212+
// Create sets for highlighted nodes and edges
213+
const highlightedNodesSet = new Set<string>();
214+
const highlightedEdgesSet = new Set<string>();
215+
216+
// Add selected node
217+
if (navState.selectedNode) {
218+
highlightedNodesSet.add(navState.selectedNode);
219+
}
220+
221+
// Add hovered nodes
222+
if (typeof hovered === "string") {
223+
highlightedNodesSet.add(hovered);
224+
} else if (hovered instanceof Set) {
225+
hovered.forEach(node => highlightedNodesSet.add(node));
226+
}
227+
228+
// If no nodes are highlighted from context, use filtered nodes (exclude faded ones)
229+
if (highlightedNodesSet.size === 0 && computedData.filteredNodes) {
230+
// Use the filtered nodes as the base - these are the active/visible nodes
231+
computedData.filteredNodes.forEach(node => {
232+
highlightedNodesSet.add(node);
233+
});
234+
}
235+
236+
// Only include the specifically highlighted nodes (no neighbors)
237+
const nodesToAdd = new Set<string>();
238+
highlightedNodesSet.forEach(node => {
239+
if (graph.hasNode(node)) {
240+
nodesToAdd.add(node);
241+
}
242+
});
243+
244+
// Add edges where BOTH source AND target nodes are visible (to avoid orphaned edges)
245+
graph.forEachEdge((edge, attributes, source, target) => {
246+
if (nodesToAdd.has(source) && nodesToAdd.has(target)) {
247+
// Additional check: only include edges that are actually visible in the current filtered view
248+
// This excludes edges that were created but are now hidden due to filtering
249+
if (computedData.filteredEdges && computedData.filteredEdges.has(edge)) {
250+
highlightedEdgesSet.add(edge);
251+
} else if (!computedData.filteredEdges) {
252+
// If no filtered edges exist, include all edges between visible nodes
253+
highlightedEdgesSet.add(edge);
254+
}
255+
}
256+
});
257+
258+
259+
260+
// If no nodes are highlighted, download the entire graph
261+
if (nodesToAdd.size === 0) {
262+
const blob = new Blob([graphFile.textContent], { type: "application/xml" });
263+
const url = URL.createObjectURL(blob);
264+
const a = document.createElement("a");
265+
a.href = url;
266+
a.download = graphFile.name || "graph.gexf";
267+
document.body.appendChild(a);
268+
a.click();
269+
document.body.removeChild(a);
270+
URL.revokeObjectURL(url);
271+
return;
272+
}
273+
274+
// Create a new XML document for the filtered graph
275+
const newXmlDoc = parser.parseFromString(graphFile.textContent, "text/xml");
276+
const newGraphElement = newXmlDoc.querySelector('graph');
277+
if (!newGraphElement) return;
278+
279+
// Remove all existing nodes and edges
280+
const existingNodes = newXmlDoc.querySelectorAll('node');
281+
const existingEdges = newXmlDoc.querySelectorAll('edge');
282+
283+
existingNodes.forEach(node => {
284+
const nodeId = node.getAttribute('id');
285+
if (nodeId && !nodesToAdd.has(nodeId)) {
286+
node.remove();
287+
}
288+
});
289+
290+
existingEdges.forEach(edge => {
291+
const edgeId = edge.getAttribute('id');
292+
if (edgeId && !highlightedEdgesSet.has(edgeId)) {
293+
edge.remove();
294+
}
295+
});
296+
297+
// Serialize the filtered XML
298+
const serializer = new XMLSerializer();
299+
const filteredGexfContent = serializer.serializeToString(newXmlDoc);
300+
301+
// Create and download the filtered file
302+
const blob = new Blob([filteredGexfContent], { type: "application/xml" });
205303
const url = URL.createObjectURL(blob);
206304
const a = document.createElement("a");
207305
a.href = url;
208-
a.download = graphFile.name || "graph.gexf";
306+
307+
// Create filename with indication that it's filtered
308+
const baseName = graphFile.name || "graph";
309+
const extension = graphFile.extension || "gexf";
310+
const filteredName = `${baseName}_highlighted.${extension}`;
311+
312+
a.download = filteredName;
209313
document.body.appendChild(a);
210314
a.click();
211315
document.body.removeChild(a);
212316
URL.revokeObjectURL(url);
213317
}
214-
}, [graphFile]);
318+
}, [graphFile, graph, navState.selectedNode, hovered]);
215319

216320
return (
217321
<>
@@ -233,7 +337,7 @@ const GraphControls: FC = () => {
233337
<FaFileImage />
234338
</button>
235339

236-
<button className="btn btn-outline-dark graph-button mt-3" onClick={downloadGraph} title="Download graph file (.gexf)">
340+
<button className="btn btn-outline-dark graph-button mt-3" onClick={downloadGraph} title="Download highlighted graph file (.gexf)">
237341
<FaDownload />
238342
</button>
239343
</>

0 commit comments

Comments
 (0)