Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- **[Viewer]**: The graph view now honours the active filters
- It drew every record and every edge regardless of the facets, contradicting the "N of M records" count above it
- A placeholder node stays while the record referencing it survives the filter
- An empty result now says "No ADRs match the current filters" instead of "No relationships to display"

### Added

- **[Docs]**: Add comprehensive dependencies reference documentation
Expand Down
63 changes: 57 additions & 6 deletions templates/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,49 @@
});
}

// The graph is built once over every record, while the facets narrow the list. Without this
// the graph keeps drawing the whole estate after a filter, contradicting the record count.
function filteredGraph() {
var visible = {};
state.filteredRecords.forEach(function(adr) {
visible[adr.id] = true;
});

var placeholder = {};
state.graph.nodes.forEach(function(node) {
if (node.title === undefined) {
placeholder[node.id] = true;
}
});

// A placeholder stands for a reference to a record outside the collection, so it has no
// facets of its own. Keep it while the record pointing at it survives the filter.
var edges = state.graph.edges.filter(function(edge) {
var source = visible[edge.source] === true;
var target = visible[edge.target] === true;

return (source && target)
|| (source && placeholder[edge.target] === true)
|| (target && placeholder[edge.source] === true);
});

var keep = {};
Object.keys(visible).forEach(function(id) {
keep[id] = true;
});
edges.forEach(function(edge) {
keep[edge.source] = true;
keep[edge.target] = true;
});

return {
nodes: state.graph.nodes.filter(function(node) {
return keep[node.id] === true;
}),
edges: edges
};
}

function renderGraphView() {
var canvas = elements.graphCanvas;
var ctx = canvas.getContext('2d');
Expand All @@ -746,21 +789,29 @@

ctx.clearRect(0, 0, canvas.width, canvas.height);

if (state.graph.nodes.length === 0) {
var graph = filteredGraph();

if (graph.nodes.length === 0) {
ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--color-text-muted');
ctx.font = '14px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('No relationships to display', canvas.width / 2, canvas.height / 2);
ctx.fillText(
state.filteredRecords.length === 0
? 'No ADRs match the current filters'
: 'No relationships to display',
canvas.width / 2,
canvas.height / 2
);
return;
}

// Simple force-directed layout simulation
var nodes = state.graph.nodes.map(function(n, i) {
var nodes = graph.nodes.map(function(n, i) {
return {
id: n.id,
status: n.status,
x: canvas.width / 2 + Math.cos(i * 2 * Math.PI / state.graph.nodes.length) * 200,
y: canvas.height / 2 + Math.sin(i * 2 * Math.PI / state.graph.nodes.length) * 200,
x: canvas.width / 2 + Math.cos(i * 2 * Math.PI / graph.nodes.length) * 200,
y: canvas.height / 2 + Math.sin(i * 2 * Math.PI / graph.nodes.length) * 200,
radius: 20
};
});
Expand All @@ -772,7 +823,7 @@
ctx.strokeStyle = getComputedStyle(document.documentElement).getPropertyValue('--color-border');
ctx.lineWidth = 1;

state.graph.edges.forEach(function(edge) {
graph.edges.forEach(function(edge) {
var source = nodeMap[edge.source];
var target = nodeMap[edge.target];
if (source && target) {
Expand Down