@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.
Files changed (2) hide show
  1. package/README.md +292 -86
  2. 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, and right-click interactions
9
- - 🌓 **Theme support** - Light and dark mode compatible
10
- - ⚡ **Performance** - Optimized rendering with canvas
11
- - 💀 **Loading states** - Built-in skeleton loading with pulse animation
12
- - 🎨 **Customizable** - Colors, sizes, and behaviors
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-canvas
21
+ npm install @falkordb/canvas
18
22
  ```
19
23
 
20
24
  ## Quick Start
21
25
 
22
- ### HTML
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-canvas';
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
- ## API
63
-
64
- ### Methods
66
+ ### React / TypeScript
65
67
 
66
- #### `setData(data: Data)`
67
- Set the graph data (nodes and links).
68
+ ```tsx
69
+ import { useEffect, useRef } from 'react';
70
+ import '@falkordb/canvas';
71
+ import type { FalkorDBCanvas, Data, GraphNode } from '@falkordb/canvas';
68
72
 
69
- ```typescript
70
- canvas.setData({
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
- #### `getData(): Data`
81
- Get the current graph data.
76
+ useEffect(() => {
77
+ const canvas = canvasRef.current;
78
+ if (!canvas) return;
82
79
 
83
- #### `setConfig(config: ForceGraphConfig)`
84
- Configure the graph visualization and behavior.
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
- ```typescript
87
- canvas.setConfig({
88
- width: 800,
89
- height: 600,
90
- backgroundColor: '#FFFFFF',
91
- foregroundColor: '#1A1A1A',
92
- displayTextPriority: [
93
- { name: 'name', ignore: false },
94
- { name: 'title', ignore: false }
95
- ],
96
- cooldownTicks: 300,
97
- isLoading: false,
98
- onNodeClick: (node, event) => {},
99
- onNodeRightClick: (node, event) => {},
100
- onLinkRightClick: (link, event) => {},
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
- #### `getGraph(): ForceGraphInstance | undefined`
111
- Get the underlying force-graph instance for advanced control.
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 | Type | Description |
116
- |--------|------|-------------|
117
- | `width` | `number` | Canvas width in pixels |
118
- | `height` | `number` | Canvas height in pixels |
119
- | `backgroundColor` | `string` | Background color (hex or CSS color) |
120
- | `foregroundColor` | `string` | Foreground color for borders and text |
121
- | `displayTextPriority` | `TextPriority[]` | Priority order for displaying node text |
122
- | `cooldownTicks` | `number \| undefined` | Number of simulation ticks before stopping |
123
- | `isLoading` | `boolean` | Show/hide loading skeleton |
124
- | `onNodeClick` | `function` | Callback when a node is clicked |
125
- | `onNodeRightClick` | `function` | Callback when a node is right-clicked |
126
- | `onLinkRightClick` | `function` | Callback when a link is right-clicked |
127
- | `onNodeHover` | `function` | Callback when hovering over a node |
128
- | `onLinkHover` | `function` | Callback when hovering over a link |
129
- | `onBackgroundClick` | `function` | Callback when clicking the background |
130
- | `onEngineStop` | `function` | Callback when the force simulation stops |
131
- | `isNodeSelected` | `function` | Function to determine if a node is selected |
132
- | `isLinkSelected` | `function` | Function to determine if a link is selected |
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
- id: number;
140
- labels: string[];
141
- color: string;
142
- visible: boolean;
143
- data: Record<string, any>;
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
- #### Link
201
+ #### GraphLink
202
+ Internal format with resolved node references:
148
203
  ```typescript
149
204
  {
150
- id: number;
151
- relationship: string;
152
- color: string;
153
- source: number; // Node ID
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@falkordb/canvas",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "description": "A standalone web component for visualizing FalkorDB graphs using force-directed layouts",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",