init
This commit is contained in:
@@ -0,0 +1,293 @@
|
||||
# Graph Viewer — Component Reference
|
||||
|
||||
An interactive graph visualization for exploring **Cognite Data Fusion (CDF)** data model instances and their relationships. Built on [reagraph](https://github.com/reaviz/reagraph), it exposes a single hook — `useGraphViewer` — that returns a ready-to-render canvas and a full set of programmatic controls.
|
||||
|
||||
> This document is the **complete API reference** for the bundle in this folder. For the agent-facing integration workflow, see `../SKILL.md`.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Data model-aware** — automatically loads CDF data model metadata to resolve node types, icons, and colors.
|
||||
- **Progressive exploration** — starts from a seed instance and lets users expand the graph by double-clicking nodes to fetch connected instances (edges, direct relations, and configurable reverse relations).
|
||||
- **LRU node buffer** — keeps the graph performant by evicting least-recently-used nodes when `maxNodes` is exceeded.
|
||||
- **Multiple layouts** — Force-directed (2D/3D), tree (top-down / left-right), radial, and circular.
|
||||
- **Interactive legend** — color-coded node type legend with click-to-filter.
|
||||
- **Zoom controls** — built-in zoom in/out and fit-to-view buttons.
|
||||
- **Theming** — fully customizable node, edge, ring, arrow, and canvas colors via `GraphThemeConfig` and `GraphVisualConfig`.
|
||||
- **Type-aware icons** — maps CDF view types (ISA-95 assets, equipment, files, time series, etc.) to SVG icons rendered inside node circles.
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
### `useGraphViewer(config): UseGraphViewerReturn`
|
||||
|
||||
#### `UseGraphViewerConfig`
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
| ------------ | -------------------------------- | -------- | ----------------------------------------- |
|
||||
| `dataModel` | `{ space, externalId, version }` | Yes | The CDF data model to load. |
|
||||
| `instance` | `{ space, externalId }` | No | Optional seed instance to load on mount. |
|
||||
| `options` | `UseGraphViewerOptions` | No | Optional overrides (see below). |
|
||||
|
||||
#### `UseGraphViewerOptions`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
| -------------------------- | --------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||
| `maxNodes` | `number` | `1000` | Maximum nodes held in the LRU buffer. Older nodes are evicted first. |
|
||||
| `layout` | `LayoutType` | `"forceDirected2d"` | Initial graph layout algorithm. |
|
||||
| `initialConnectionLimit` | `number` | `100` | **Hard maximum** number of connected instances fetched per expansion (edges + reverse-relation nodes). |
|
||||
| `whitelistedRelationProps` | `string[]` | all | Property names to follow when extracting direct relations. Strongly recommended for large data models. |
|
||||
| `coreReverseQueries` | `ReverseRelationQuery[]` | `[]` | Reverse-relation queries to run on node expansion. See shape below. |
|
||||
| `viewPriorityConfig` | `ViewPriorityConfig` | built-in | Controls which CDF views determine node types. |
|
||||
| `visualConfig` | `Partial<GraphVisualConfig>` | defaults | Node colors, palette, icon size, path highlight. |
|
||||
| `themeConfig` | `Partial<GraphThemeConfig>` | defaults | Full reagraph theme overrides. |
|
||||
| `features` | `Partial<LiteFeatureFlags>` | all enabled | Toggle legend, zoom controls, and node expansion. |
|
||||
|
||||
##### `ReverseRelationQuery`
|
||||
|
||||
```ts
|
||||
type ReverseRelationQuery = [
|
||||
space: string, // space of the view that defines the relation
|
||||
viewExternalId: string, // external id of the view
|
||||
viewVersion: string, // view version, e.g. "v1" — required, never assumed
|
||||
propertyName: string, // direct-relation property pointing back to the expanded node
|
||||
isList: boolean, // true for list<direct>, false for direct
|
||||
];
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
const coreReverseQueries: ReverseRelationQuery[] = [
|
||||
["industrial-dm", "Cavity", "v1", "connector", false],
|
||||
["industrial-dm", "Cable", "v1", "wireGroup", true],
|
||||
];
|
||||
```
|
||||
|
||||
##### `LiteFeatureFlags`
|
||||
|
||||
| Flag | Default | Controls |
|
||||
| --------------------- | ------- | ----------------------------------------- |
|
||||
| `enableLegend` | `true` | Node-type color legend overlay |
|
||||
| `enableZoomControls` | `true` | Zoom in / out / fit buttons |
|
||||
| `enableNodeExpansion` | `true` | Double-click node to expand its neighbors |
|
||||
|
||||
#### `UseGraphViewerReturn`
|
||||
|
||||
| Property | Type | Description |
|
||||
| --------------- | -------------------------------------- | -------------------------------------------------------------- |
|
||||
| `GraphCanvas` | `React.FC<{ className? }>` | Self-contained canvas component to render. |
|
||||
| `isLoading` | `boolean` | `true` while data model, seed node, or expansion is in flight. |
|
||||
| `error` | `string \| null` | Error message, if any. |
|
||||
| `graphData` | `GraphData` | Current nodes, connections, and node type metadata. |
|
||||
| `stats` | `GraphStats \| null` | Aggregate counts by node/connection type. |
|
||||
| `layout` | `LayoutType` | Current layout. |
|
||||
| `setLayout` | `(layout) => void` | Change the layout algorithm. |
|
||||
| `selections` | `string[]` | Currently selected node/edge IDs. |
|
||||
| `setSelections` | `(ids) => void` | Programmatically select nodes/edges. |
|
||||
| `selectedNode` | `GraphNode \| null` | The selected node object. |
|
||||
| `selectedEdge` | `GraphEdge \| null` | The selected edge object. |
|
||||
| `expandNode` | `(nodeId) => Promise<void>` | Fetch and add connected instances for a node. |
|
||||
| `loadInstance` | `(space, externalId) => Promise<void>` | Load a new seed instance (replaces the graph). |
|
||||
| `fitView` | `() => void` | Fit all nodes into the viewport. |
|
||||
| `zoomIn` | `() => void` | Zoom in. |
|
||||
| `zoomOut` | `() => void` | Zoom out. |
|
||||
| `clear` | `() => void` | Remove all nodes and edges from the buffer. |
|
||||
| `graphRef` | `RefObject<GraphCanvasRef>` | Direct ref to the underlying reagraph canvas. |
|
||||
|
||||
---
|
||||
|
||||
## Layout Options
|
||||
|
||||
| ID | Label |
|
||||
| ----------------- | ----------------- |
|
||||
| `forceDirected2d` | Force 2D |
|
||||
| `forceDirected3d` | Force 3D |
|
||||
| `treeTd2d` | Tree (Top-Down) |
|
||||
| `treeLr2d` | Tree (Left-Right) |
|
||||
| `radialOut2d` | Radial |
|
||||
| `circular2d` | Circular |
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Minimal embed
|
||||
|
||||
```tsx
|
||||
function GraphPanel() {
|
||||
const { GraphCanvas } = useGraphViewer({
|
||||
dataModel: { space: "equipment", externalId: "EquipmentModel", version: "1" },
|
||||
});
|
||||
|
||||
return <GraphCanvas className="h-full w-full" />;
|
||||
}
|
||||
```
|
||||
|
||||
No seed instance — the canvas renders empty until you call `loadInstance`.
|
||||
|
||||
### Layout switcher with stats
|
||||
|
||||
```tsx
|
||||
function GraphWithControls() {
|
||||
const { GraphCanvas, stats, layout, setLayout } = useGraphViewer({
|
||||
dataModel: { space: "equipment", externalId: "EquipmentModel", version: "1" },
|
||||
instance: { space: "assets", externalId: "pump-001" },
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="flex items-center gap-4 border-b p-2">
|
||||
<select value={layout} onChange={(e) => setLayout(e.target.value as LayoutType)}>
|
||||
<option value="forceDirected2d">Force 2D</option>
|
||||
<option value="treeTd2d">Tree</option>
|
||||
<option value="radialOut2d">Radial</option>
|
||||
<option value="circular2d">Circular</option>
|
||||
</select>
|
||||
{stats && <span>{stats.totalNodes} nodes</span>}
|
||||
</div>
|
||||
<GraphCanvas className="flex-1" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Programmatic node loading
|
||||
|
||||
```tsx
|
||||
function SearchAndGraph() {
|
||||
const { GraphCanvas, loadInstance, isLoading } = useGraphViewer({
|
||||
dataModel: { space: "equipment", externalId: "EquipmentModel", version: "1" },
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<input
|
||||
placeholder="Enter node externalId…"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") loadInstance("assets", e.currentTarget.value);
|
||||
}}
|
||||
/>
|
||||
{isLoading && <p>Loading…</p>}
|
||||
<GraphCanvas className="flex-1" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Disable features
|
||||
|
||||
```tsx
|
||||
const { GraphCanvas } = useGraphViewer({
|
||||
dataModel: { space: "s", externalId: "dm", version: "1" },
|
||||
instance: { space: "s", externalId: "node-1" },
|
||||
options: {
|
||||
features: {
|
||||
enableLegend: false,
|
||||
enableZoomControls: false,
|
||||
enableNodeExpansion: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Conservative expansion for large data models
|
||||
|
||||
Whitelist relation properties and bound the per-expansion budget to keep CDF
|
||||
load predictable:
|
||||
|
||||
```tsx
|
||||
const { GraphCanvas } = useGraphViewer({
|
||||
dataModel: { space: "industrial", externalId: "EWIS", version: "1" },
|
||||
instance: { space: "instances", externalId: "connector-001" },
|
||||
options: {
|
||||
maxNodes: 500,
|
||||
initialConnectionLimit: 50,
|
||||
whitelistedRelationProps: ["parent", "child", "connectedTo"],
|
||||
coreReverseQueries: [
|
||||
["industrial-dm", "Cavity", "v1", "connector", false],
|
||||
["industrial-dm", "Cable", "v1", "wireGroup", true],
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sizing
|
||||
|
||||
`<GraphCanvas>` fills its parent. Give the parent explicit dimensions:
|
||||
|
||||
```tsx
|
||||
<GraphCanvas className="h-[600px] w-full" />
|
||||
|
||||
<div className="h-full w-full">
|
||||
<GraphCanvas className="h-full w-full" />
|
||||
</div>
|
||||
|
||||
<div className="flex h-screen flex-col">
|
||||
<header>…</header>
|
||||
<GraphCanvas className="flex-1" />
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### React to selection
|
||||
|
||||
```tsx
|
||||
const { GraphCanvas, selectedNode } = useGraphViewer({ /* … */ });
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedNode) console.log("Selected:", selectedNode.data.externalId);
|
||||
}, [selectedNode]);
|
||||
```
|
||||
|
||||
### Expand from an external trigger
|
||||
|
||||
```tsx
|
||||
const { expandNode } = useGraphViewer({ /* … */ });
|
||||
|
||||
// nodeId format is "space:externalId"
|
||||
await expandNode("my-space:pump-001");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
graph-viewer/
|
||||
├── useGraphViewer.tsx # Main hook — composes all sub-hooks, returns GraphCanvas + controls
|
||||
├── GraphViewerCanvas.tsx # Renders reagraph canvas, zoom controls, and legend
|
||||
├── GraphViewerLegend.tsx # Color-coded node type legend with click-to-filter
|
||||
├── ZoomControls.tsx # Zoom in / out / fit-view button group
|
||||
├── graph-service.ts # CDF API calls — fetchNodeDetails, fetchConnectedNodes
|
||||
├── graph-config.ts # Theme defaults, icon generation, node/edge transformers
|
||||
├── useDataModelLoader.ts # Loads data model views from CDF
|
||||
├── useSeedNode.ts # Loads the initial instance and its connections
|
||||
├── useNodeBuffer.ts # LRU buffer that caps total nodes at maxNodes
|
||||
├── useGraphDataPipeline.ts # Transforms raw CDF instances into GraphData + reagraph format
|
||||
├── useGraphSelection.ts # Tracks selected node/edge state
|
||||
├── useCanvasResize.ts # Observes container size changes and triggers reagraph resize
|
||||
├── types.ts # All shared TypeScript types, constants, and helpers
|
||||
└── index.ts # Public exports
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Purpose |
|
||||
| --------------- | ---------------------------------------------------- |
|
||||
| `react` | UI framework (peer) |
|
||||
| `@cognite/sdk` | CDF API client (instances, data models) |
|
||||
| `@cognite/dune` | Provides the authenticated SDK via `useDune()` |
|
||||
| `reagraph` | WebGL graph rendering engine |
|
||||
| `lucide-react` | Icon set used by the node-type legend |
|
||||
|
||||
Install latest compatible versions using the target app's package manager. Prefer the React version already pinned by the app rather than upgrading it.
|
||||
Reference in New Issue
Block a user