@jeevandev/flow-canvas 0.0.1
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 +116 -0
- package/dist/flow-canvas.css +1 -0
- package/dist/flow-canvas.es.js +3034 -0
- package/dist/flow-canvas.umd.js +12 -0
- package/dist/vite.svg +12 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Flow Canvas
|
|
2
|
+
|
|
3
|
+
A powerful, dual-mode React diagramming library that combines free-form design tools (like Canva) with structured node-based workflows
|
|
4
|
+
|
|
5
|
+
 <!-- Replace with actual demo image if available -->
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Dual-Mode Engine**:
|
|
10
|
+
- **Design Mode**: Free-form resizing, rotating, and snapping for design elements.
|
|
11
|
+
- **Workflow Mode**: Structured nodes with handles and strict connection logic.
|
|
12
|
+
- **Infinite Canvas**: Zoomable, pannable, and virtually infinite workspace.
|
|
13
|
+
- **Performance Optimized**: Viewport culling (virtualization) for thousands of nodes and edges.
|
|
14
|
+
- **Customizable**: Fully stylable nodes and edges using standard CSS/SCSS.
|
|
15
|
+
- **Interactive**: Drag, drop, resize, rotate, and connect interactions built-in.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install flow-canvas
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Basic Usage
|
|
24
|
+
|
|
25
|
+
Wrap your application in the `Editor` context and render the `Canvas`.
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import React from 'react';
|
|
29
|
+
import { Editor, Canvas, NodeLayer } from 'flow-canvas';
|
|
30
|
+
import 'flow-canvas/style.css'; // Import default styles
|
|
31
|
+
|
|
32
|
+
// 1. Define Nodes
|
|
33
|
+
const initialNodes = [
|
|
34
|
+
{ id: '1', type: 'default', x: 100, y: 100, data: { label: 'Node A' } },
|
|
35
|
+
{ id: '2', type: 'input', x: 400, y: 200, data: { label: 'Node B' } }
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
// 2. Define Edges
|
|
39
|
+
const initialEdges = [
|
|
40
|
+
{ id: 'e1', source: '1', target: '2' }
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const App = () => {
|
|
44
|
+
return (
|
|
45
|
+
<div style={{ width: '100vw', height: '100vh' }}>
|
|
46
|
+
<Editor initialNodes={initialNodes} initialEdges={initialEdges}>
|
|
47
|
+
<Canvas>
|
|
48
|
+
<NodeLayer />
|
|
49
|
+
</Canvas>
|
|
50
|
+
</Editor>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Configuration
|
|
57
|
+
|
|
58
|
+
You can customize the editor behavior via the `config` prop:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
<Editor
|
|
62
|
+
config={{
|
|
63
|
+
viewOnly: false,
|
|
64
|
+
pan: true,
|
|
65
|
+
zoom: true,
|
|
66
|
+
snapping: true,
|
|
67
|
+
showGrid: true,
|
|
68
|
+
gridSize: 20,
|
|
69
|
+
gridColor: '#e5e7eb',
|
|
70
|
+
snapGuide: true, // Visual alignment guides
|
|
71
|
+
isLimited: false, // Infinite canvas if false
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
...
|
|
75
|
+
</Editor>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Creating Custom Nodes
|
|
79
|
+
|
|
80
|
+
### Design Node (Resizable & Rotatable)
|
|
81
|
+
Any node with `resizable: true` or `rotatable: true` automatically gets the Design wrappers.
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
const nodes = [
|
|
85
|
+
{
|
|
86
|
+
id: 'design-1',
|
|
87
|
+
type: 'custom',
|
|
88
|
+
x: 100, y: 100,
|
|
89
|
+
width: 200, height: 200,
|
|
90
|
+
resizable: true,
|
|
91
|
+
rotatable: true,
|
|
92
|
+
doubleClickToEdit: true, // Prevents interaction until double-click
|
|
93
|
+
data: { ... }
|
|
94
|
+
}
|
|
95
|
+
];
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Workflow Node (Logic & Connections)
|
|
99
|
+
Standard nodes usually just need handles.
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
// Custom Component
|
|
103
|
+
const MyNode = ({ data }) => {
|
|
104
|
+
return (
|
|
105
|
+
<div className="my-node">
|
|
106
|
+
<Handle type="target" position="left" />
|
|
107
|
+
{data.label}
|
|
108
|
+
<Handle type="source" position="right" />
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.ce-editor{position:relative;width:100%;height:100%;overflow:hidden;background-color:#f3f4f6}.ce-editor__canvas{position:absolute;top:0;left:0;width:100%;height:100%;touch-action:none;transform-origin:0 0}.ce-editor__viewport{width:100%;height:100%;overflow:hidden}.ce-editor__viewport--panning{cursor:grab!important}.ce-editor__viewport--panning *{pointer-events:none!important}.ce-editor__grid-layer{position:absolute;top:-50000px;left:-50000px;width:100000px;height:100000px;opacity:.5;pointer-events:none;z-index:0}.ce-editor__content-area{position:absolute;left:0;top:0;background-color:#fff;overflow:hidden;z-index:1;box-shadow:0 0 20px #0000001a}.ce-editor__infinite-area{position:relative;z-index:1}.ce-element{position:absolute;touch-action:none;-webkit-user-select:none;user-select:none;box-sizing:border-box;transition:box-shadow .1s}.ce-element--selected{outline:2px solid #3b82f6;outline-width:calc(2px * var(--ce-inverse-zoom, 1));box-shadow:0 0 0 calc(4px * var(--ce-inverse-zoom, 1)) #3b82f633;z-index:100!important}.ce-element img{pointer-events:none;-webkit-user-drag:none;-webkit-user-select:none;user-select:none}.ce-editor--connecting{cursor:crosshair!important}.ce-editor--connecting .ce-element:hover{outline:2px dashed #3b82f6;outline-width:calc(2px * var(--ce-inverse-zoom, 1));background-color:#3b82f60d;cursor:copy}.ce-editor--connecting .ce-handle:hover{transform:scale(1.5);background-color:#3b82f6!important;border-color:#fff!important;transition:transform .1s}.ce-resize-handle{width:calc(10px * var(--ce-inverse-zoom, 1));height:calc(10px * var(--ce-inverse-zoom, 1));background:#fff;border:calc(1px * var(--ce-inverse-zoom, 1)) solid #3b82f6;position:absolute;z-index:101}.ce-resize-handle--nw{top:calc(-6px * var(--ce-inverse-zoom, 1));left:calc(-6px * var(--ce-inverse-zoom, 1));cursor:nw-resize}.ce-resize-handle--ne{top:calc(-6px * var(--ce-inverse-zoom, 1));right:calc(-6px * var(--ce-inverse-zoom, 1));cursor:ne-resize}.ce-resize-handle--sw{bottom:calc(-6px * var(--ce-inverse-zoom, 1));left:calc(-6px * var(--ce-inverse-zoom, 1));cursor:sw-resize}.ce-resize-handle--se{bottom:calc(-6px * var(--ce-inverse-zoom, 1));right:calc(-6px * var(--ce-inverse-zoom, 1));cursor:se-resize}.ce-resize-handle--n{top:calc(-6px * var(--ce-inverse-zoom, 1));left:50%;transform:translate(-50%);cursor:n-resize}.ce-resize-handle--s{bottom:calc(-6px * var(--ce-inverse-zoom, 1));left:50%;transform:translate(-50%);cursor:s-resize}.ce-resize-handle--e{right:calc(-6px * var(--ce-inverse-zoom, 1));top:50%;transform:translateY(-50%);cursor:e-resize}.ce-resize-handle--w{left:calc(-6px * var(--ce-inverse-zoom, 1));top:50%;transform:translateY(-50%);cursor:w-resize}.ce-rotate-handle{width:calc(24px * var(--ce-inverse-zoom, 1));height:calc(24px * var(--ce-inverse-zoom, 1));background:#fff;border:none;box-shadow:0 2px 4px #0003;border-radius:50%;position:absolute;top:auto;bottom:calc(-35px * var(--ce-inverse-zoom, 1));left:50%;transform:translate(-50%);cursor:grab;z-index:101;display:flex;align-items:center;justify-content:center;color:#555}.ce-rotate-handle:after{display:none}.ce-rotate-handle:active{cursor:grabbing;background:#f3f4f6;color:#000}.ce-drag-handle-minimal{width:calc(24px * var(--ce-inverse-zoom, 1));height:calc(24px * var(--ce-inverse-zoom, 1));position:absolute;top:auto;bottom:calc(-35px * var(--ce-inverse-zoom, 1));left:calc(50% + calc(30px * var(--ce-inverse-zoom, 1)));transform:translate(-50%);background-color:#fff;border-radius:50%;box-shadow:0 2px 4px #0003;cursor:grab;z-index:102;display:flex;align-items:center;justify-content:center;color:#555;background-image:none}.ce-drag-handle-minimal:active{cursor:grabbing;background-color:#f3f4f6;color:#000}.ce-snap-guide{position:absolute;background-color:#f0f;pointer-events:none;z-index:200}.ce-snap-guide--vertical{width:1px;height:100vh}.ce-snap-guide--horizontal{height:1px;width:100vw}.ce-node-default,.ce-node-input,.ce-node-output{background:#fff;border:1px solid #777;border-radius:4px;padding:10px;min-width:180px;text-align:center;font-size:14px;color:#222;box-shadow:0 1px 4px #0000001a;position:relative}.ce-node-input{border-color:#3b82f6}.ce-node-output{border-color:#10b981}.ce-node-content{pointer-events:none}.ce-handle{width:10px;height:10px;background:#fff;border-radius:50%;position:absolute;border:3px solid #777;z-index:10;cursor:crosshair;transition:border-color .2s}.ce-handle--left{left:0;transform:translate(-50%)}.ce-handle--right{right:0;transform:translate(50%)}.ce-handle--top{top:0;transform:translateY(-50%)}.ce-handle--bottom{bottom:0;transform:translateY(50%)}.ce-workflow-node:hover .ce-delete-node-btn{opacity:1;pointer-events:auto}.ce-delete-node-btn{position:absolute;top:-8px;right:-8px;width:18px;height:18px;background:#ff4d4f;color:#fff;border:none;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:14px;line-height:1;padding:0;z-index:20;opacity:0;pointer-events:none;transition:opacity .2s,transform .1s;box-shadow:0 2px 4px #0000001a}.ce-delete-node-btn:hover{transform:scale(1.1);background:#ff7875}.ce-edge-group .ce-edge-delete-fo{opacity:0;pointer-events:none;transition:opacity .2s}.ce-edge-group:hover .ce-edge-delete-fo{opacity:1;pointer-events:auto}.ce-connection-line{animation:dashdraw .5s linear infinite}@keyframes dashdraw{0%{stroke-dashoffset:10}to{stroke-dashoffset:0}}
|