diff --git a/ui/src/pages/case.graph.js b/ui/src/pages/case.graph.js index 3b907a734..b12b96a9e 100644 --- a/ui/src/pages/case.graph.js +++ b/ui/src/pages/case.graph.js @@ -13,6 +13,126 @@ function get_case_graph() { } var network; +var graphNodes; +var graphEdges; +var graphNodeDefaults; +var NODE_DEFAULT_SIZE = 25; +var NODE_HIGHLIGHT_SIZE = 40; +var EDGE_DEFAULT_WIDTH = 1; +var EDGE_HIGHLIGHT_WIDTH = 2; + +function getNodeFontColor(baseFont) { + if (typeof baseFont === 'string') { + const lower = baseFont.toLowerCase(); + if (lower.includes('white')) { + return '#ffffff'; + } + if (lower.includes('black')) { + return '#111111'; + } + } + + if (baseFont && typeof baseFont === 'object' && baseFont.color) { + return baseFont.color; + } + + return '#111111'; +} + +function getNodeFontFace(baseFont) { + if (baseFont && typeof baseFont === 'object' && baseFont.face) { + return baseFont.face; + } + + return 'verdana'; +} + +function buildHighlightFont(baseFont) { + const baseColor = getNodeFontColor(baseFont); + const isLightColor = baseColor.toLowerCase() === '#ffffff' || baseColor.toLowerCase() === 'white'; + + return { + color: baseColor, + face: getNodeFontFace(baseFont), + size: 18, + strokeWidth: isLightColor ? 4 : 3, + strokeColor: isLightColor ? '#000000' : '#ffffff' + }; +} + +function selectLinkedNodes(nodeId) { + const connectedNodes = network.getConnectedNodes(nodeId); + const connectedEdges = network.getConnectedEdges(nodeId); + + applyGraphHighlight([...new Set([nodeId, ...connectedNodes])], connectedEdges); +} + +function selectEdgeEndpoints(edgeIds) { + const connectedNodes = []; + + edgeIds.forEach((edgeId) => { + const edge = graphEdges.get(edgeId); + if (!edge) { + return; + } + + connectedNodes.push(edge.from, edge.to); + }); + + applyGraphHighlight([...new Set(connectedNodes)], edgeIds); +} + +function applyGraphHighlight(nodeIds, edgeIds) { + const nodeSet = new Set(nodeIds); + const edgeSet = new Set(edgeIds); + + const nodeUpdates = []; + graphNodes.forEach((node) => { + const isHighlighted = nodeSet.has(node.id); + const defaults = graphNodeDefaults[node.id] || {}; + nodeUpdates.push({ + id: node.id, + size: isHighlighted ? NODE_HIGHLIGHT_SIZE : NODE_DEFAULT_SIZE, + borderWidth: 0, + shadow: false, + font: isHighlighted ? buildHighlightFont(defaults.font) : defaults.font + }); + }); + graphNodes.update(nodeUpdates); + + const edgeUpdates = []; + graphEdges.forEach((edge) => { + edgeUpdates.push({ + id: edge.id, + width: edgeSet.has(edge.id) ? EDGE_HIGHLIGHT_WIDTH : EDGE_DEFAULT_WIDTH + }); + }); + graphEdges.update(edgeUpdates); +} + +function clearGraphHighlight() { + const nodeUpdates = []; + graphNodes.forEach((node) => { + const defaults = graphNodeDefaults[node.id] || {}; + nodeUpdates.push({ + id: node.id, + size: NODE_DEFAULT_SIZE, + borderWidth: 0, + shadow: false, + font: defaults.font + }); + }); + graphNodes.update(nodeUpdates); + + const edgeUpdates = []; + graphEdges.forEach((edge) => { + edgeUpdates.push({ + id: edge.id, + width: EDGE_DEFAULT_WIDTH + }); + }); + graphEdges.update(edgeUpdates); +} function redrawAll(data) { if (data.nodes.length == 0) { @@ -28,7 +148,8 @@ function redrawAll(data) { enabled: true, type: 'continuous', roundness: 0.5 - } + }, + selectionWidth: 3 }, layout: { randomSeed: 2, @@ -53,15 +174,42 @@ function redrawAll(data) { } }; - nodes = data.nodes; - edges = data.edges; + var preparedNodes = data.nodes.map((node) => ({ + ...node, + size: NODE_DEFAULT_SIZE + })); + graphNodeDefaults = {}; + preparedNodes.forEach((node) => { + graphNodeDefaults[node.id] = { + font: node.font + }; + }); + graphNodes = new vis.DataSet(preparedNodes); + graphEdges = new vis.DataSet(data.edges); - network = new vis.Network(container, data, options); + network = new vis.Network(container, { + nodes: graphNodes, + edges: graphEdges + }, options); network.on("stabilizationIterationsDone", function () { network.setOptions( { physics: false } ); }); + network.on("click", function (params) { + if (params.nodes.length > 0) { + selectLinkedNodes(params.nodes[0]); + return; + } + + if (params.edges.length > 0) { + selectEdgeEndpoints(params.edges); + return; + } + + clearGraphHighlight(); + }); + } @@ -69,4 +217,3 @@ function redrawAll(data) { $(document).ready(function(){ get_case_graph(); }); -