@falkordb/canvas 0.0.21 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +292 -86
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,22 +4,26 @@ A standalone web component for visualizing FalkorDB graphs using force-directed
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- 🎨 **Force-directed graph layout** - Automatic positioning using D3 force simulation
|
|
8
|
-
- 🎯 **Interactive** - Click, hover,
|
|
9
|
-
- 🌓 **Theme support** - Light and dark mode compatible
|
|
10
|
-
- ⚡ **Performance** - Optimized rendering with canvas
|
|
11
|
-
-
|
|
12
|
-
- 🎨 **Customizable** - Colors, sizes, and
|
|
7
|
+
- 🎨 **Force-directed graph layout** - Automatic positioning using D3 force simulation with smart collision detection
|
|
8
|
+
- 🎯 **Interactive** - Click, hover, right-click interactions on nodes, links, and background
|
|
9
|
+
- 🌓 **Theme support** - Light and dark mode compatible with customizable colors
|
|
10
|
+
- ⚡ **Performance** - Optimized rendering with HTML5 canvas
|
|
11
|
+
- 💫 **Loading states** - Built-in skeleton loading with pulse animation
|
|
12
|
+
- 🎨 **Customizable** - Colors, sizes, behaviors, and custom rendering functions
|
|
13
|
+
- 📦 **TypeScript support** - Full type definitions included
|
|
14
|
+
- 🔧 **Web Component** - Works with any framework or vanilla JavaScript
|
|
15
|
+
- 🎮 **Viewport control** - Zoom, pan, and auto-fit functionality
|
|
16
|
+
- 🔄 **Smart layout** - Adaptive force algorithm based on node connectivity
|
|
13
17
|
|
|
14
18
|
## Installation
|
|
15
19
|
|
|
16
20
|
```bash
|
|
17
|
-
npm install falkordb
|
|
21
|
+
npm install @falkordb/canvas
|
|
18
22
|
```
|
|
19
23
|
|
|
20
24
|
## Quick Start
|
|
21
25
|
|
|
22
|
-
###
|
|
26
|
+
### Vanilla JavaScript
|
|
23
27
|
|
|
24
28
|
```html
|
|
25
29
|
<!DOCTYPE html>
|
|
@@ -31,7 +35,7 @@ npm install falkordb-canvas
|
|
|
31
35
|
<falkordb-canvas id="graph" style="width: 100%; height: 600px;"></falkordb-canvas>
|
|
32
36
|
|
|
33
37
|
<script type="module">
|
|
34
|
-
import 'falkordb
|
|
38
|
+
import '@falkordb/canvas';
|
|
35
39
|
|
|
36
40
|
const canvas = document.getElementById('graph');
|
|
37
41
|
|
|
@@ -59,127 +63,323 @@ npm install falkordb-canvas
|
|
|
59
63
|
</html>
|
|
60
64
|
```
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
### Methods
|
|
66
|
+
### React / TypeScript
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
```tsx
|
|
69
|
+
import { useEffect, useRef } from 'react';
|
|
70
|
+
import '@falkordb/canvas';
|
|
71
|
+
import type { FalkorDBCanvas, Data, GraphNode } from '@falkordb/canvas';
|
|
68
72
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
nodes: [
|
|
72
|
-
{ id: 1, labels: ['Person'], color: '#FF6B6B', visible: true, data: { name: 'Alice' } }
|
|
73
|
-
],
|
|
74
|
-
links: [
|
|
75
|
-
{ id: 1, relationship: 'KNOWS', color: '#999', source: 1, target: 2, visible: true, data: {} }
|
|
76
|
-
]
|
|
77
|
-
});
|
|
78
|
-
```
|
|
73
|
+
function GraphVisualization() {
|
|
74
|
+
const canvasRef = useRef<FalkorDBCanvas>(null);
|
|
79
75
|
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const canvas = canvasRef.current;
|
|
78
|
+
if (!canvas) return;
|
|
82
79
|
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
const data: Data = {
|
|
81
|
+
nodes: [
|
|
82
|
+
{ id: 1, labels: ['Person'], color: '#FF6B6B', visible: true, data: { name: 'Alice' } },
|
|
83
|
+
{ id: 2, labels: ['Person'], color: '#4ECDC4', visible: true, data: { name: 'Bob' } }
|
|
84
|
+
],
|
|
85
|
+
links: [
|
|
86
|
+
{ id: 1, relationship: 'KNOWS', color: '#999', source: 1, target: 2, visible: true, data: {} }
|
|
87
|
+
]
|
|
88
|
+
};
|
|
85
89
|
|
|
86
|
-
|
|
87
|
-
canvas.setConfig({
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
onNodeHover: (node) => {},
|
|
102
|
-
onLinkHover: (link) => {},
|
|
103
|
-
onBackgroundClick: (event) => {},
|
|
104
|
-
onEngineStop: () => {},
|
|
105
|
-
isNodeSelected: (node) => false,
|
|
106
|
-
isLinkSelected: (link) => false
|
|
107
|
-
});
|
|
90
|
+
canvas.setData(data);
|
|
91
|
+
canvas.setConfig({
|
|
92
|
+
onNodeClick: (node: GraphNode) => {
|
|
93
|
+
console.log('Clicked node:', node);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}, []);
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<falkordb-canvas
|
|
100
|
+
ref={canvasRef}
|
|
101
|
+
style={{ width: '100%', height: '600px' }}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
108
105
|
```
|
|
109
106
|
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
## API
|
|
108
|
+
|
|
109
|
+
### Methods
|
|
110
|
+
|
|
111
|
+
| Method | Default | Description |
|
|
112
|
+
|--------|---------|-------------|
|
|
113
|
+
| **setData**(*data*) | | Set the graph data (nodes and links). Automatically triggers layout simulation and loading states. |
|
|
114
|
+
| **getData**() | | Get the current graph data in the simplified format. |
|
|
115
|
+
| **setGraphData**(*data*) | | Set graph data in the internal format (with computed properties). Use this for better performance when you already have GraphData format. |
|
|
116
|
+
| **getGraphData**() | | Get the current graph data in the internal format with all computed properties (x, y, vx, vy, etc.). |
|
|
117
|
+
| **setConfig**(*config*) | | Configure the graph visualization and behavior. Accepts a `ForceGraphConfig` object with styling, callbacks, and rendering options. |
|
|
118
|
+
| **setWidth**(*width*) | | Set canvas width in pixels. |
|
|
119
|
+
| **setHeight**(*height*) | | Set canvas height in pixels. |
|
|
120
|
+
| **setBackgroundColor**(*color*) | | Set background color (hex or CSS color). |
|
|
121
|
+
| **setForegroundColor**(*color*) | | Set foreground color for text and borders. |
|
|
122
|
+
| **setIsLoading**(*isLoading*) | | Show/hide loading skeleton. |
|
|
123
|
+
| **setCooldownTicks**(*ticks*) | | Set simulation ticks before stopping (undefined = infinite). |
|
|
124
|
+
| **getViewport**() | | Get current zoom and center position as `ViewportState`. |
|
|
125
|
+
| **setViewport**(*viewport*) | | Restore a previously saved viewport state. |
|
|
126
|
+
| **getZoom**() | | Get current zoom level. |
|
|
127
|
+
| **zoom**(*zoomLevel*) | | Set zoom level. |
|
|
128
|
+
| **zoomToFit**(*paddingMultiplier*, *filter*) | `1.0`, `undefined` | Auto-fit all visible nodes in view. Optional padding multiplier and node filter function. |
|
|
129
|
+
| **getGraph**() | | Get the underlying force-graph instance for advanced control. |
|
|
112
130
|
|
|
113
131
|
### Configuration Options
|
|
114
132
|
|
|
115
|
-
| Option |
|
|
116
|
-
|
|
117
|
-
| `width` |
|
|
118
|
-
| `height` |
|
|
119
|
-
| `backgroundColor` |
|
|
120
|
-
| `foregroundColor` |
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
+
| Option | Default | Description |
|
|
134
|
+
|--------|---------|-------------|
|
|
135
|
+
| `width` | `<window width>` | Canvas width in pixels |
|
|
136
|
+
| `height` | `<window height>` | Canvas height in pixels |
|
|
137
|
+
| `backgroundColor` | | Background color (hex or CSS color) |
|
|
138
|
+
| `foregroundColor` | | Foreground color for borders and text |
|
|
139
|
+
| `cooldownTicks` | `undefined` | Number of simulation ticks before stopping (undefined = infinite) |
|
|
140
|
+
| `cooldownTime` | `1000` | Time in ms for each simulation tick |
|
|
141
|
+
| `autoStopOnSettle` | `true` | Automatically stop simulation when settled |
|
|
142
|
+
| `isLoading` | `false` | Show/hide loading skeleton |
|
|
143
|
+
| `onNodeClick` | | Callback when a node is clicked. Signature: `(node: GraphNode, event: MouseEvent) => void` |
|
|
144
|
+
| `onNodeRightClick` | | Callback when a node is right-clicked. Signature: `(node: GraphNode, event: MouseEvent) => void` |
|
|
145
|
+
| `onLinkClick` | | Callback when a link is clicked. Signature: `(link: GraphLink, event: MouseEvent) => void` |
|
|
146
|
+
| `onLinkRightClick` | | Callback when a link is right-clicked. Signature: `(link: GraphLink, event: MouseEvent) => void` |
|
|
147
|
+
| `onNodeHover` | | Callback when hovering over a node. Signature: `(node: GraphNode \| null) => void` |
|
|
148
|
+
| `onLinkHover` | | Callback when hovering over a link. Signature: `(link: GraphLink \| null) => void` |
|
|
149
|
+
| `onBackgroundClick` | | Callback when clicking the background. Signature: `(event: MouseEvent) => void` |
|
|
150
|
+
| `onBackgroundRightClick` | | Callback when right-clicking the background. Signature: `(event: MouseEvent) => void` |
|
|
151
|
+
| `onZoom` | | Callback when zoom/pan changes. Signature: `(transform: Transform) => void` |
|
|
152
|
+
| `onEngineStop` | | Callback when the force simulation stops. Signature: `() => void` |
|
|
153
|
+
| `onLoadingChange` | | Callback when loading state changes. Signature: `(loading: boolean) => void` |
|
|
154
|
+
| `isNodeSelected` | | Function to determine if a node is selected. Signature: `(node: GraphNode) => boolean` |
|
|
155
|
+
| `isLinkSelected` | | Function to determine if a link is selected. Signature: `(link: GraphLink) => boolean` |
|
|
156
|
+
| `node` | | Custom node rendering functions (see Custom Rendering) |
|
|
157
|
+
| `link` | | Custom link rendering functions (see Custom Rendering) |
|
|
133
158
|
|
|
134
159
|
### Data Types
|
|
135
160
|
|
|
136
161
|
#### Node
|
|
162
|
+
|
|
163
|
+
| Property | Default | Description |
|
|
164
|
+
|----------|---------|-------------|
|
|
165
|
+
| `id` | *required* | Unique identifier for the node |
|
|
166
|
+
| `labels` | *required* | Array of label names for the node |
|
|
167
|
+
| `color` | *required* | Node color (hex or CSS color) |
|
|
168
|
+
| `visible` | *required* | Whether the node is visible |
|
|
169
|
+
| `size` | `6` | Node radius |
|
|
170
|
+
| `caption` | `'id'` | Property key to use from the data for display text |
|
|
171
|
+
| `data` | *required* | Node properties as key-value pairs |
|
|
172
|
+
|
|
173
|
+
#### Link
|
|
174
|
+
|
|
175
|
+
| Property | Default | Description |
|
|
176
|
+
|----------|---------|-------------|
|
|
177
|
+
| `id` | *required* | Unique identifier for the link |
|
|
178
|
+
| `relationship` | *required* | Label displayed on the link |
|
|
179
|
+
| `color` | *required* | Link color (hex or CSS color) |
|
|
180
|
+
| `source` | *required* | Source node ID |
|
|
181
|
+
| `target` | *required* | Target node ID |
|
|
182
|
+
| `visible` | *required* | Whether the link is visible |
|
|
183
|
+
| `data` | *required* | Link properties as key-value pairs |
|
|
184
|
+
|
|
185
|
+
#### GraphNode
|
|
186
|
+
Internal format with computed properties:
|
|
137
187
|
```typescript
|
|
138
188
|
{
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
189
|
+
...Node;
|
|
190
|
+
size: number; // Always present (defaults to 6)
|
|
191
|
+
displayName: [string, string]; // Computed text lines
|
|
192
|
+
x?: number; // Position from simulation
|
|
193
|
+
y?: number;
|
|
194
|
+
vx?: number; // Velocity
|
|
195
|
+
vy?: number;
|
|
196
|
+
fx?: number; // Fixed position
|
|
197
|
+
fy?: number;
|
|
144
198
|
}
|
|
145
199
|
```
|
|
146
200
|
|
|
147
|
-
####
|
|
201
|
+
#### GraphLink
|
|
202
|
+
Internal format with resolved node references:
|
|
148
203
|
```typescript
|
|
149
204
|
{
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
target: number; // Node ID
|
|
155
|
-
visible: boolean;
|
|
156
|
-
data: Record<string, any>;
|
|
205
|
+
...Link;
|
|
206
|
+
source: GraphNode; // Resolved node object
|
|
207
|
+
target: GraphNode; // Resolved node object
|
|
208
|
+
curve: number; // Computed curvature for rendering
|
|
157
209
|
}
|
|
158
210
|
```
|
|
159
211
|
|
|
212
|
+
#### ViewportState
|
|
213
|
+
```typescript
|
|
214
|
+
{
|
|
215
|
+
zoom: number;
|
|
216
|
+
centerX: number;
|
|
217
|
+
centerY: number;
|
|
218
|
+
} | undefined
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
#### Transform
|
|
222
|
+
```typescript
|
|
223
|
+
{
|
|
224
|
+
k: number; // zoom scale
|
|
225
|
+
x: number; // pan x
|
|
226
|
+
y: number; // pan y
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Custom Rendering
|
|
231
|
+
|
|
232
|
+
You can provide custom rendering functions for nodes and links:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
canvas.setConfig({
|
|
236
|
+
node: {
|
|
237
|
+
nodeCanvasObject: (node: GraphNode, ctx: CanvasRenderingContext2D) => {
|
|
238
|
+
// Custom node drawing logic
|
|
239
|
+
ctx.fillStyle = node.color;
|
|
240
|
+
ctx.fillRect(node.x! - 5, node.y! - 5, 10, 10);
|
|
241
|
+
},
|
|
242
|
+
nodePointerAreaPaint: (node: GraphNode, color: string, ctx: CanvasRenderingContext2D) => {
|
|
243
|
+
// Define clickable area
|
|
244
|
+
ctx.fillStyle = color;
|
|
245
|
+
ctx.fillRect(node.x! - 5, node.y! - 5, 10, 10);
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
link: {
|
|
249
|
+
linkCanvasObject: (link: GraphLink, ctx: CanvasRenderingContext2D) => {
|
|
250
|
+
// Custom link drawing logic
|
|
251
|
+
},
|
|
252
|
+
linkPointerAreaPaint: (link: GraphLink, color: string, ctx: CanvasRenderingContext2D) => {
|
|
253
|
+
// Define clickable area for link
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Utility Functions
|
|
260
|
+
|
|
261
|
+
The package exports utility functions for data manipulation:
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
import {
|
|
265
|
+
dataToGraphData,
|
|
266
|
+
graphDataToData,
|
|
267
|
+
getNodeDisplayText,
|
|
268
|
+
getNodeDisplayKey,
|
|
269
|
+
wrapTextForCircularNode
|
|
270
|
+
} from '@falkordb/canvas';
|
|
271
|
+
|
|
272
|
+
// Convert between formats
|
|
273
|
+
const graphData = dataToGraphData(data);
|
|
274
|
+
const data = graphDataToData(graphData);
|
|
275
|
+
|
|
276
|
+
// Get display text for a node
|
|
277
|
+
const text = getNodeDisplayText(node); // Returns node.data[caption] or defaults to id
|
|
278
|
+
|
|
279
|
+
// Wrap text for circular nodes
|
|
280
|
+
const [line1, line2] = wrapTextForCircularNode(ctx, text, radius);
|
|
281
|
+
```
|
|
282
|
+
|
|
160
283
|
## Development
|
|
161
284
|
|
|
162
285
|
```bash
|
|
163
286
|
# Install dependencies
|
|
164
287
|
npm install
|
|
165
288
|
|
|
166
|
-
# Build
|
|
289
|
+
# Build (TypeScript compilation)
|
|
167
290
|
npm run build
|
|
168
291
|
|
|
169
|
-
# Watch mode
|
|
292
|
+
# Watch mode (auto-rebuild on changes)
|
|
170
293
|
npm run dev
|
|
171
294
|
|
|
172
|
-
# Run example
|
|
295
|
+
# Run example server
|
|
173
296
|
npm run example
|
|
174
297
|
# Then open http://localhost:8080/examples/falkordb-canvas.example.html
|
|
298
|
+
|
|
299
|
+
# Lint code
|
|
300
|
+
npm run lint
|
|
301
|
+
|
|
302
|
+
# Clean build artifacts
|
|
303
|
+
npm run clean
|
|
175
304
|
```
|
|
176
305
|
|
|
306
|
+
## Web Component Attributes
|
|
307
|
+
|
|
308
|
+
The component supports HTML attributes for render modes:
|
|
309
|
+
|
|
310
|
+
```html
|
|
311
|
+
<falkordb-canvas
|
|
312
|
+
node-mode="replace" <!-- 'before' | 'after' | 'replace' -->
|
|
313
|
+
link-mode="after"> <!-- 'before' | 'after' | 'replace' -->
|
|
314
|
+
</falkordb-canvas>
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
- `replace` (default for nodes): Uses custom rendering exclusively
|
|
318
|
+
- `before`: Renders custom content before default rendering
|
|
319
|
+
- `after` (default for links): Renders custom content after default rendering
|
|
320
|
+
|
|
177
321
|
## Browser Support
|
|
178
322
|
|
|
179
323
|
- Chrome/Edge (latest)
|
|
180
324
|
- Firefox (latest)
|
|
181
325
|
- Safari (latest)
|
|
182
326
|
|
|
327
|
+
Requires support for:
|
|
328
|
+
- Web Components (Custom Elements)
|
|
329
|
+
- ES Modules
|
|
330
|
+
- Shadow DOM
|
|
331
|
+
- HTML5 Canvas
|
|
332
|
+
|
|
333
|
+
## Testing & Automation
|
|
334
|
+
|
|
335
|
+
### Engine Status Indicator
|
|
336
|
+
|
|
337
|
+
The canvas element inside the web component's shadow DOM exposes a `data-engine-status` attribute that indicates whether the force simulation is currently running or stopped. This is useful for automated testing to wait for the canvas to finish animating.
|
|
338
|
+
|
|
339
|
+
**Values:**
|
|
340
|
+
- `"running"` - Force simulation is actively running
|
|
341
|
+
- `"stopped"` - Force simulation has stopped (animation complete)
|
|
342
|
+
|
|
343
|
+
**Example usage with Playwright:**
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// Wait for canvas to be ready
|
|
347
|
+
const canvasElement = page.locator("falkordb-canvas").locator("canvas").first();
|
|
348
|
+
await canvasElement.waitFor({ state: "attached" });
|
|
349
|
+
|
|
350
|
+
// Poll until animation completes
|
|
351
|
+
while (true) {
|
|
352
|
+
const status = await canvasElement.getAttribute("data-engine-status");
|
|
353
|
+
if (status === "stopped") break;
|
|
354
|
+
await page.waitForTimeout(500);
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**Note:** The attribute is set on the `<canvas>` element within the shadow DOM, not on the `<falkordb-canvas>` web component itself.
|
|
359
|
+
|
|
360
|
+
## Performance Tips
|
|
361
|
+
|
|
362
|
+
1. **Large graphs**: Use `cooldownTicks` to limit simulation iterations
|
|
363
|
+
2. **Static graphs**: Set `cooldownTicks: 0` after initial layout
|
|
364
|
+
3. **Custom rendering**: Optimize your custom `nodeCanvasObject` and `linkCanvasObject` functions
|
|
365
|
+
4. **Viewport**: Use `getViewport()` and `setViewport()` to preserve user's view when updating data
|
|
366
|
+
|
|
367
|
+
## Examples
|
|
368
|
+
|
|
369
|
+
See the [examples directory](./examples) for complete working examples including:
|
|
370
|
+
- Basic usage
|
|
371
|
+
- Custom node/link rendering
|
|
372
|
+
- Event handling
|
|
373
|
+
- Dynamic data updates
|
|
374
|
+
- Theme switching
|
|
375
|
+
|
|
376
|
+
## Links
|
|
377
|
+
|
|
378
|
+
- [GitHub Repository](https://github.com/FalkorDB/falkordb-canvas)
|
|
379
|
+
- [FalkorDB](https://www.falkordb.com/)
|
|
380
|
+
- [Report Issues](https://github.com/FalkorDB/falkordb-canvas/issues)
|
|
381
|
+
- [npm Package](https://www.npmjs.com/package/@falkordb/canvas)
|
|
382
|
+
|
|
183
383
|
## License
|
|
184
384
|
|
|
185
385
|
MIT
|
|
@@ -187,3 +387,9 @@ MIT
|
|
|
187
387
|
## Contributing
|
|
188
388
|
|
|
189
389
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
390
|
+
|
|
391
|
+
1. Fork the repository
|
|
392
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
393
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
394
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
395
|
+
5. Open a Pull Request
|
package/package.json
CHANGED