# Resource Graph (Sigma.js)
This is a self-contained HTML demo that renders a directed resource graph using Sigma.js + Graphology and vanilla JavaScript only.
## Features
- Directed graph with labeled edges.
- Click a resource to highlight incoming/outgoing edges.
- Click event emitted for resource selection so you can render details elsewhere.
- Incremental updates (add nodes/edges).
- Reset the graph to start fresh.
## APIs
The page exposes a global `ResourceGraph` object:
```js
ResourceGraph.setGraph({ nodes: [...], edges: [...] });
ResourceGraph.resetGraph();
ResourceGraph.addResources([{ id: 200 }, { id: 201 }]);
ResourceGraph.addEdges([{ from: 200, to: 201, label: "author" }]);
ResourceGraph.applyIncrementalUpdate({
nodes: [{ id: 300 }],
edges: [{ from: 201, to: 300, label: "haspart" }]
});
ResourceGraph.enqueueBatch({ nodes: [...], edges: [...], runLayout: true });
ResourceGraph.stepBack();
ResourceGraph.runLayout({ iterations: 400 });
ResourceGraph.fitCamera();
ResourceGraph.refresh();
```
### API Details
- `setGraph({ nodes, edges, startNodeId, useWorker, setActiveToStart })`
- Clears the graph, inserts data in batches, and runs the layout. Use `useWorker: true` to attempt worker layout.
- If `setActiveToStart` is true, sets the active node to `startNodeId` after the graph draws.
- `resetGraph()`
- Clears all nodes/edges and resets selection.
- `addResources(nodes, createdNodes?, nearResourceId?)`
- Adds nodes immediately (no batching). Optionally seed positions near a resource id.
- `addEdges(edges)`
- Adds edges immediately (no batching).
- `applyIncrementalUpdate({ nodes, edges, nearResourceId })`
- Adds nodes/edges in batches and runs the layout. Optionally seed positions near a resource id.
- `enqueueBatch({ nodes, edges, runLayout, resetHistory, nearResourceId })`
- Low-level batched insert; `runLayout` triggers layout after the batch.
- `stepBack()`
- Removes nodes/edges added by the last completed batch (stops at initial graph).
- `runLayout(options)`
- Runs the ForceAtlas2-like layout; accepts tuning options. Use `useWorker: true` to run in a web worker if available.
- `setDefaultUseWorker(enabled)`
- Sets the default worker usage for future incremental batches.
- `fitCamera()`
- Resets the camera to fit the graph.
- `refresh()`
- Forces a Sigma render.
- `setActiveResource(resourceId)`
- Activates a node by resource id and triggers click/edge-load logic.
- `setStartNode(resourceId | null)`
- Sets the optional start node for path highlighting.
- `setPathOnlyMode(enabled)`
- When `true`, hide everything except the focus path.
- `setPathWeights({ outgoing, incoming })`
- Configure pathfinding costs (default outgoing `1`, incoming `10`).
- `getNodeInfo(resourceId)`
- Returns node attributes and in/out edges.
- `getEdgeInfo(edgeId)`
- Returns edge attributes and endpoints.
- `markEdgesLoaded(resourceIds, loaded = true)`
- Marks edges for resources as loaded and clears loading flags.
- `markEdgesLoading(resourceIds, loading = true)`
- Sets a loading flag (auto-timeout applies).
- `setEdgesLoadingTimeout(ms)`
- Sets edge-loading timeout in milliseconds.
- `setCategoryVisibility(categoryId, visible)`
- Show/hide nodes by category id.
- `hideCategory(categoryId)` / `showCategory(categoryId)`
- Convenience wrappers.
- `getHiddenCategories()`
- Returns hidden category ids.
- `setHiddenCategories(categoryIds)`
- Replace the hidden category list with the provided ids.
- `setPredicateVisibility(predicateId, visible)`
- Show/hide edges by predicate id.
- `hidePredicate(predicateId)` / `showPredicate(predicateId)`
- Convenience wrappers.
- `getHiddenPredicates()`
- Returns hidden predicate ids.
- `setHiddenPredicates(predicateIds)`
- Replace the hidden predicate list with the provided ids.
## UI Button IDs
If present in the DOM, the following button IDs are wired up:
- `zoom-in` → zoom in
- `zoom-out` → zoom out
- `step-back` → remove the last inserted batch
- `reset-graph` → clear the graph
- `toggle-path-only` → toggle path-only visibility
## Click Event
The graph dispatches a `resource:click` event on `window` when a node is clicked:
```js
window.addEventListener("resource:click", (e) => {
console.log("Clicked resource", e.detail.id, e.detail);
});
```
You can also provide a callback:
```js
window.onResourceClick = (resourceId, attrs) => {
console.log("Clicked resource", resourceId, attrs);
};
```
## Active Resource Event
When the active resource changes, a `resource:active-change` event is dispatched:
```js
window.addEventListener("resource:active-change", (e) => {
console.log("Active resource changed", e.detail.id, e.detail.path, e.detail.pathNodes, e.detail);
});
```
Or use a callback:
```js
window.onResourceActiveChange = (resourceId, attrs, path) => {
console.log("Active resource changed", resourceId, path, attrs);
};
```
## Data Shape
Nodes and edges follow this shape:
```js
{ id: 101 }
{ id: 102, label: "Alice", category: "person", category_id: 2, edgesLoaded: true }
{ id: 1, from: 101, to: 102, label: "author" }
```
Node `id` must be an integer. Optional node fields:
- `label` string (defaults to `id` if empty)
- `category` string (affects node color/size; e.g. `article`, `person`, `keyword`, `collection`, `image`, `video`)
- `category_id` integer (used for visibility filtering)
- `edgesLoaded` boolean (whether all edges for this resource have been loaded)
- `edgesLoading` boolean (whether edge loading is currently in progress)
Edge fields:
- `id` integer (required, used to dedupe edges)
- `from` integer
- `to` integer
- `label` string
- `predicate_id` integer (used for visibility filtering)
## Edge Loading Events
When a resource is clicked and `edgesLoaded` is falsey, a `resource:needs-edges` event is dispatched:
```js
window.addEventListener("resource:needs-edges", (e) => {
console.log("Need edges for resource", e.detail.id, e.detail);
});
```
You can also set a callback:
```js
window.onResourceNeedsEdges = (resourceId, attrs) => {
console.log("Need edges for resource", resourceId, attrs);
};
```
When you start loading, you can set a loading flag (also set automatically on click):
```js
ResourceGraph.markEdgesLoading(101, true);
```
Configure the loading timeout (ms). After the timeout, `edgesLoading` is cleared:
```js
ResourceGraph.setEdgesLoadingTimeout(20000);
```
When a timeout happens, a `resource:edges-timeout` event is dispatched:
```js
window.addEventListener("resource:edges-timeout", (e) => {
console.log("Edge load timed out", e.detail.id, e.detail);
});
```
Or use a callback:
```js
window.onResourceEdgesTimeout = (resourceId, attrs) => {
console.log("Edge load timed out", resourceId, attrs);
};
```
After you fetch edges, mark the resource as loaded:
```js
ResourceGraph.markEdgesLoaded([101, 102], true);
```
## Category Visibility
You can toggle resource categories, and edges will hide if either endpoint is hidden:
```js
ResourceGraph.setCategoryVisibility("person", false);
ResourceGraph.setCategoryVisibility("person", true);
```
Convenience helpers:
```js
ResourceGraph.hideCategory(2);
ResourceGraph.showCategory(2);
ResourceGraph.getHiddenCategories(); // -> [2, 5]
```
## Predicate Visibility
You can toggle predicate IDs, and nodes will hide if all their edges are hidden:
```js
ResourceGraph.setPredicateVisibility(42, false);
ResourceGraph.setPredicateVisibility(42, true);
```
Convenience helpers:
```js
ResourceGraph.hidePredicate(42);
ResourceGraph.showPredicate(42);
ResourceGraph.getHiddenPredicates(); // -> [42, 91]
```
## Dependencies
- Sigma.js
- Graphology