Skip to content

Commit a89ac0f

Browse files
D3 Force Graph Component (#19)
* Added D3 force graph component * Added D3 force graph component
1 parent 41c87f2 commit a89ac0f

6 files changed

Lines changed: 2154 additions & 0 deletions

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# ForceGraphComponent
2+
3+
An interactive D3 force-directed graph custom component for Retool. Visualises node-link data with dynamic grouping, multiple themes, zoom controls, and a detail panel.
4+
5+
---
6+
7+
## Features
8+
9+
- `Force-directed layout` via D3 with drag, zoom, and pan
10+
- `Auto node grouping` — detects a model, category, or any suitable key and colour-codes nodes accordingly
11+
- `4 preset themes` — Arctic, Midnight, Rose Gold, Slate Pro
12+
- `Custom theme editor` — live colour pickers for background, grid, edges, labels, node strokes, per-group node colours, and tooltip
13+
- `Info panel` — click any node to inspect all its fields; shows a graph summary when nothing is selected
14+
- `Hover tooltip` — displays node name, group value, and a prompt to click
15+
- `Zoom controls` — zoom in, zoom out, reset
16+
- `Stable rendering` — data identity is tracked by content, not object reference, so the simulation only rebuilds when data actually changes
17+
- `Persisted preferences` — active theme and custom colour overrides survive page refresh via localStorage
18+
19+
---
20+
21+
## Input
22+
The component accepts a single Retool state object named `graphData`.
23+
24+
Schema
25+
```json
26+
{
27+
"nodes": [
28+
{
29+
"id": "string | number",
30+
"name": "string",
31+
"model": "string (optional)",
32+
"category": "string (optional)",
33+
"avgLatency": "number (optional)",
34+
"avgCost": "number (optional)",
35+
"numCalls": "number (optional)",
36+
"performanceScore": "number (optional)"
37+
}
38+
],
39+
"links": [
40+
{
41+
"source": "string | number (node id)",
42+
"target": "string | number (node id)",
43+
"weight": "number (optional, default 1)"
44+
}
45+
]
46+
}
47+
```
48+
49+
---
50+
51+
52+
Any additional fields on a node are displayed in the `All Fields` section of the info panel.
53+
54+
Example
55+
```json
56+
{
57+
"nodes": [
58+
{ "id": "1", "name": "Classifier", "model": "GPT-4", "numCalls": 120, "avgLatency": 340 },
59+
{ "id": "2", "name": "Summariser", "model": "Claude", "numCalls": 80, "avgCost": 0.002 },
60+
{ "id": "3", "name": "Embedder", "model": "GPT-3.5", "numCalls": 200 }
61+
],
62+
"links": [
63+
{ "source": "1", "target": "2", "weight": 2 },
64+
{ "source": "2", "target": "3" }
65+
]
66+
}
67+
```
68+
69+
70+
---
71+
72+
73+
## Automatic grouping
74+
75+
The component scans all node keys in order — model, category, then any other key — and picks the first one that has between 2 and 20 distinct values. That key becomes the group key, used to colour nodes and populate the legend in the info panel.
76+
To disable grouping, ensure no key on your nodes produces 2–20 distinct values
77+
78+
---
79+
80+
# Theming
81+
82+
## Preset themes
83+
84+
Select from the theme bar at the bottom of the component: `Arctic, Midnight, Rose Gold, Slate Pro`.
85+
86+
## Custom theme
87+
88+
Click ✏️ `Custom` to open the colour editor. Changes to background, grid, edges, labels, node stroke, and tooltip colours apply instantly. Edits to per-group node colours also apply immediately and automatically activate the custom theme — no need to click Apply Custom Theme first.
89+
Click `↺ Reset` to restore the last selected preset and clear all custom colour overrides.
287 KB
Loading
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"id": "d3-force-graph",
3+
"title": "D3 Force Graph",
4+
"author": "@widlestudiollp",
5+
"shortDescription": "An interactive D3 force-directed graph with zoom, themes, node grouping, and detailed insights panel.",
6+
"tags": [
7+
"D3",
8+
"Graph",
9+
"Visualization",
10+
"Force Layout",
11+
"Analytics",
12+
"Interactive",
13+
"Network",
14+
"React"
15+
]
16+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "my-react-app",
3+
"version": "0.1.0",
4+
"private": true,
5+
"dependencies": {
6+
"@tryretool/custom-component-support": "latest",
7+
"d3": "^7.9.0",
8+
"react": "^18.2.0",
9+
"react-dom": "^18.2.0"
10+
},
11+
"engines": {
12+
"node": ">=20.0.0"
13+
},
14+
"scripts": {
15+
"dev": "npx retool-ccl dev",
16+
"deploy": "npx retool-ccl deploy",
17+
"test": "vitest"
18+
},
19+
"browserslist": {
20+
"production": [
21+
">0.2%",
22+
"not dead",
23+
"not op_mini all"
24+
],
25+
"development": [
26+
"last 1 chrome version",
27+
"last 1 firefox version",
28+
"last 1 safari version"
29+
]
30+
},
31+
"devDependencies": {
32+
"@types/react": "^18.2.55",
33+
"@typescript-eslint/eslint-plugin": "^7.3.1",
34+
"@typescript-eslint/parser": "^7.3.1",
35+
"eslint": "^8.57.0",
36+
"eslint-plugin-react": "^7.34.1",
37+
"postcss-modules": "^6.0.0",
38+
"prettier": "^3.0.3",
39+
"vitest": "^4.0.17"
40+
},
41+
"retoolCustomComponentLibraryConfig": {
42+
"name": "D3Graph",
43+
"label": "D3 Graph",
44+
"description": "D3 Conversation Flow Graph",
45+
"entryPoint": "src/index.tsx",
46+
"outputPath": "dist"
47+
}
48+
}

0 commit comments

Comments
 (0)