@jeevandev/flow-canvas 0.1.2 โ 0.1.4
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/LICENSE +21 -0
- package/README.md +235 -79
- package/dist/flow-canvas.css +1 -1
- package/dist/flow-canvas.es.js +4570 -2273
- package/dist/flow-canvas.umd.js +8 -13
- package/dist/index.d.ts +877 -93
- package/package.json +36 -11
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Jeevan Jose
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,116 +1,272 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @jeevandev/flow-canvas
|
|
2
2
|
|
|
3
|
-
A powerful,
|
|
3
|
+
A powerful, production-ready, page-based canvas editor library for React. Build interactive design experiences like Figma/Canva with drag, resize, rotate, grouping, and multi-page support.
|
|
4
4
|
|
|
5
|
-
](https://www.npmjs.com/package/@jeevandev/flow-canvas)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
6
8
|
|
|
7
|
-
## Features
|
|
9
|
+
## โจ Features
|
|
8
10
|
|
|
9
|
-
- **
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
11
|
+
- ๐จ **Multi-Page Canvas** - Create and manage multiple pages with smooth scrolling
|
|
12
|
+
- ๐ฑ๏ธ **Interactive Elements** - Drag, resize, rotate with smooth interactions
|
|
13
|
+
- ๐ **Smart Snapping** - Visual snap guides with Canva-like design
|
|
14
|
+
- ๐ **Cross-Page Dragging** - Move elements between pages with ghost preview
|
|
15
|
+
- ๐ฅ **Grouping** - Group/ungroup elements with professional editing mode
|
|
16
|
+
- ๐ฏ **Multi-Selection** - Select and manipulate multiple elements
|
|
17
|
+
- โจ๏ธ **Keyboard Shortcuts** - Full keyboard support (Undo/Redo, Copy/Paste, etc.)
|
|
18
|
+
- ๐ **Zoom & Pan** - Smooth zoom and pan controls
|
|
19
|
+
- โก **High Performance** - Viewport culling and virtualization for large datasets
|
|
20
|
+
- ๐ก๏ธ **Production Ready** - Error boundaries, logging, and comprehensive error handling
|
|
21
|
+
- โฟ **Accessible** - ARIA labels and keyboard navigation support
|
|
22
|
+
- ๐ฆ **TypeScript** - Full type safety and IntelliSense support
|
|
23
|
+
- ๐จ **Customizable** - Extensive configuration options and theming
|
|
16
24
|
|
|
17
|
-
## Installation
|
|
25
|
+
## ๐ฆ Installation
|
|
18
26
|
|
|
19
27
|
```bash
|
|
20
|
-
npm
|
|
28
|
+
npm install @jeevandev/flow-canvas
|
|
21
29
|
```
|
|
22
30
|
|
|
23
|
-
##
|
|
24
|
-
|
|
25
|
-
Wrap your application in the `Editor` context and render the `Canvas`.
|
|
31
|
+
## ๐ Quick Start
|
|
26
32
|
|
|
27
33
|
```tsx
|
|
28
|
-
import
|
|
29
|
-
import
|
|
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
|
-
];
|
|
34
|
+
import { Editor, Canvas } from "@jeevandev/flow-canvas";
|
|
35
|
+
import "@jeevandev/flow-canvas/style.css";
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
function App() {
|
|
38
|
+
const initialPages = [
|
|
39
|
+
{
|
|
40
|
+
id: "page-1",
|
|
41
|
+
width: 1920,
|
|
42
|
+
height: 1080,
|
|
43
|
+
name: "Page 1",
|
|
44
|
+
backgroundColor: "#ffffff",
|
|
45
|
+
},
|
|
46
|
+
];
|
|
42
47
|
|
|
43
|
-
const App = () => {
|
|
44
48
|
return (
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
<Editor
|
|
50
|
+
initialPages={initialPages}
|
|
51
|
+
config={{
|
|
52
|
+
snapping: true,
|
|
53
|
+
snapGuide: true,
|
|
54
|
+
showGrid: true,
|
|
55
|
+
gridSize: 20,
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
<Canvas />
|
|
59
|
+
</Editor>
|
|
52
60
|
);
|
|
53
|
-
}
|
|
61
|
+
}
|
|
54
62
|
```
|
|
55
63
|
|
|
56
|
-
##
|
|
64
|
+
## ๐ Documentation
|
|
65
|
+
|
|
66
|
+
Complete documentation is available in the [`docs/`](./docs/) directory:
|
|
67
|
+
|
|
68
|
+
- **[Getting Started](./docs/getting-started.md)** - Installation and basic usage
|
|
69
|
+
- **[API Reference](./docs/api.md)** - Complete API documentation
|
|
70
|
+
- **[Configuration](./docs/configuration.md)** - Configuration options
|
|
71
|
+
- **[Advanced Usage](./docs/advanced.md)** - Advanced features and customization
|
|
72
|
+
- **[Architecture](./docs/architecture.md)** - Architecture overview
|
|
73
|
+
- **[Examples](./docs/examples/examples.md)** - Code examples and recipes
|
|
57
74
|
|
|
58
|
-
|
|
75
|
+
See [docs/README.md](./docs/README.md) for the complete documentation index.
|
|
76
|
+
|
|
77
|
+
## ๐ฏ Core Concepts
|
|
78
|
+
|
|
79
|
+
### Pages
|
|
80
|
+
|
|
81
|
+
Pages are the containers for your design elements. Each page has dimensions, background color, and can contain multiple elements.
|
|
59
82
|
|
|
60
83
|
```tsx
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
snapGuide: true, // Visual alignment guides
|
|
71
|
-
isLimited: false, // Infinite canvas if false
|
|
72
|
-
}}
|
|
73
|
-
>
|
|
74
|
-
...
|
|
75
|
-
</Editor>
|
|
84
|
+
const pages = [
|
|
85
|
+
{
|
|
86
|
+
id: "page-1",
|
|
87
|
+
width: 1920,
|
|
88
|
+
height: 1080,
|
|
89
|
+
backgroundColor: "#ffffff",
|
|
90
|
+
name: "Home Page",
|
|
91
|
+
},
|
|
92
|
+
];
|
|
76
93
|
```
|
|
77
94
|
|
|
78
|
-
|
|
95
|
+
### Nodes (Elements)
|
|
79
96
|
|
|
80
|
-
|
|
81
|
-
Any node with `resizable: true` or `rotatable: true` automatically gets the Design wrappers.
|
|
97
|
+
Nodes are the interactive elements on your canvas. They can be dragged, resized, rotated, and grouped.
|
|
82
98
|
|
|
83
99
|
```tsx
|
|
84
100
|
const nodes = [
|
|
85
101
|
{
|
|
86
|
-
id:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
width: 200,
|
|
102
|
+
id: "node-1",
|
|
103
|
+
x: 100,
|
|
104
|
+
y: 100,
|
|
105
|
+
width: 200,
|
|
106
|
+
height: 100,
|
|
107
|
+
pageId: "page-1",
|
|
108
|
+
draggable: true,
|
|
90
109
|
resizable: true,
|
|
91
110
|
rotatable: true,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
111
|
+
data: { label: "My Element" },
|
|
112
|
+
},
|
|
95
113
|
];
|
|
96
114
|
```
|
|
97
115
|
|
|
98
|
-
###
|
|
99
|
-
|
|
116
|
+
### Configuration
|
|
117
|
+
|
|
118
|
+
Customize the editor behavior with the `config` prop:
|
|
100
119
|
|
|
101
120
|
```tsx
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
121
|
+
<Editor
|
|
122
|
+
config={{
|
|
123
|
+
snapping: true, // Enable snapping
|
|
124
|
+
snapGuide: true, // Show snap guides
|
|
125
|
+
showGrid: true, // Show grid
|
|
126
|
+
gridSize: 20, // Grid size in pixels
|
|
127
|
+
pageWidth: 1920, // Default page width
|
|
128
|
+
pageHeight: 1080, // Default page height
|
|
129
|
+
minZoom: 0.1, // Minimum zoom level
|
|
130
|
+
maxZoom: 3, // Maximum zoom level
|
|
131
|
+
rotation: true, // Enable rotation
|
|
132
|
+
viewportCulling: true, // Enable viewport culling
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## ๐ง Basic Usage
|
|
138
|
+
|
|
139
|
+
### Adding Elements
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { useEditor } from "@jeevandev/flow-canvas";
|
|
143
|
+
|
|
144
|
+
function Toolbar() {
|
|
145
|
+
const addNode = useEditor((state) => state.addNode);
|
|
146
|
+
|
|
147
|
+
const handleAddElement = () => {
|
|
148
|
+
addNode({
|
|
149
|
+
id: `element-${Date.now()}`,
|
|
150
|
+
x: 100,
|
|
151
|
+
y: 100,
|
|
152
|
+
width: 200,
|
|
153
|
+
height: 100,
|
|
154
|
+
pageId: "page-1",
|
|
155
|
+
draggable: true,
|
|
156
|
+
resizable: true,
|
|
157
|
+
rotatable: true,
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return <button onClick={handleAddElement}>Add Element</button>;
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Custom Node Types
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
import { NodeComponentProps } from "@jeevandev/flow-canvas";
|
|
169
|
+
|
|
170
|
+
const CustomNode = ({ data, style, className }: NodeComponentProps) => (
|
|
171
|
+
<div style={style} className={className}>
|
|
172
|
+
<h2>{data?.title}</h2>
|
|
173
|
+
<p>{data?.description}</p>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
<Canvas nodeTypes={{ custom: CustomNode }} />
|
|
112
178
|
```
|
|
113
179
|
|
|
114
|
-
|
|
180
|
+
### Selection
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
const selectedIds = useEditor((state) => state.selectedIds);
|
|
184
|
+
const selectNode = useEditor((state) => state.selectNode);
|
|
185
|
+
const deselectAll = useEditor((state) => state.deselectAll);
|
|
186
|
+
|
|
187
|
+
// Select a node
|
|
188
|
+
selectNode("node-1");
|
|
189
|
+
|
|
190
|
+
// Deselect all
|
|
191
|
+
deselectAll();
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## ๐จ Styling
|
|
195
|
+
|
|
196
|
+
The library includes default styles that you can import:
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
import "@jeevandev/flow-canvas/style.css";
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
You can customize styles using CSS variables or by overriding the default classes. See [Configuration](./docs/CONFIGURATION.md) for details.
|
|
203
|
+
|
|
204
|
+
## โก Performance
|
|
205
|
+
|
|
206
|
+
The library is optimized for performance:
|
|
207
|
+
|
|
208
|
+
- **Viewport Culling** - Only renders visible elements (enabled by default)
|
|
209
|
+
- **Virtualization** - Advanced virtualization for 100+ elements
|
|
210
|
+
- **Memoization** - React.memo and useMemo for optimal re-renders
|
|
211
|
+
- **Bundle Size** - Optimized bundle (121KB UMD, 38KB gzipped)
|
|
212
|
+
|
|
213
|
+
## ๐ก๏ธ Production Features
|
|
214
|
+
|
|
215
|
+
- โ
**Error Boundaries** - Graceful error handling
|
|
216
|
+
- โ
**Logging** - Production-ready logger (no console.logs)
|
|
217
|
+
- โ
**Error Reporting** - Sentry-ready error reporting
|
|
218
|
+
- โ
**Performance Monitoring** - Built-in performance metrics
|
|
219
|
+
- โ
**Auto-Save** - State persistence utilities
|
|
220
|
+
- โ
**Templates** - Template system for reusable designs
|
|
221
|
+
- โ
**Accessibility** - WCAG compliant with ARIA labels
|
|
222
|
+
|
|
223
|
+
## ๐ Bundle Size
|
|
224
|
+
|
|
225
|
+
| Format | Size | Gzipped |
|
|
226
|
+
|--------|------|---------|
|
|
227
|
+
| ES Module | 165 KB | 43 KB |
|
|
228
|
+
| UMD | 121 KB | 38 KB |
|
|
229
|
+
| CSS | 13 KB | 2.8 KB |
|
|
230
|
+
|
|
231
|
+
## ๐งช Development
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
# Install dependencies
|
|
235
|
+
npm install
|
|
236
|
+
|
|
237
|
+
# Start dev server
|
|
238
|
+
npm run dev
|
|
239
|
+
|
|
240
|
+
# Build library
|
|
241
|
+
npm run build
|
|
242
|
+
|
|
243
|
+
# Run tests
|
|
244
|
+
npm test
|
|
245
|
+
|
|
246
|
+
# Type check
|
|
247
|
+
npx tsc --noEmit
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## ๐ Changelog
|
|
251
|
+
|
|
252
|
+
See [CHANGELOG.md](./CHANGELOG.md) for version history and release notes.
|
|
253
|
+
|
|
254
|
+
## ๐ค Contributing
|
|
255
|
+
|
|
256
|
+
Contributions are welcome! Please read our contributing guidelines and code of conduct.
|
|
257
|
+
|
|
258
|
+
## ๐ License
|
|
259
|
+
|
|
260
|
+
MIT ยฉ [Jeevan Jose](https://github.com/jeevanjose)
|
|
261
|
+
|
|
262
|
+
## ๐ Acknowledgments
|
|
263
|
+
|
|
264
|
+
Built with:
|
|
265
|
+
- [React](https://react.dev/)
|
|
266
|
+
- [Zustand](https://github.com/pmndrs/zustand) - State management
|
|
267
|
+
- [@use-gesture/react](https://use-gesture.netlify.app/) - Gesture handling
|
|
268
|
+
- [Vite](https://vitejs.dev/) - Build tool
|
|
269
|
+
|
|
270
|
+
---
|
|
115
271
|
|
|
116
|
-
|
|
272
|
+
**Ready for production use** โ
| **TypeScript** โ
| **Accessible** โ
| **Well-tested** โ
|
package/dist/flow-canvas.css
CHANGED
|
@@ -1 +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}}
|
|
1
|
+
.ce-editor{position:relative;width:100%;height:100%;overflow:hidden;background-color:#f3f4f6}.ce-editor__viewport{position:relative;width:100%;height:100%;overflow:auto;cursor:default}.ce-editor__viewport--panning{cursor:grab!important}.ce-editor__viewport--panning *{pointer-events:none!important}.ce-editor__scale-wrapper{width:100%;height:100%;display:flex;justify-content:center;align-items:flex-start;padding-top:40px}.ce-editor__canvas{transform-origin:top center;display:flex;flex-direction:column;align-items:center;padding:40px}.ce-editor__grid-layer{position:absolute;top:0;left:0;width:100%;height:100%;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-page{position:relative;flex-shrink:0;background-color:#fff;box-shadow:0 2px 8px #00000014,0 0 0 1px #0000000a;border-radius:4px;overflow:hidden;transition:box-shadow .2s ease,transform .2s ease}.ce-page--drag-target{box-shadow:0 4px 16px #06f3,0 0 0 2px #0066ff4d;transform:scale(1.002)}.ce-page--active{box-shadow:0 4px 12px #0000001f,0 0 0 1px #0000000f}.ce-page__ghost-preview{position:absolute;inset:0;pointer-events:none;z-index:999;background-color:#0066ff08;border:2px dashed rgba(0,102,255,.4);border-radius:4px}.ce-element{position:absolute;touch-action:none;-webkit-user-select:none;user-select:none;box-sizing:border-box;transition:box-shadow .15s ease,outline .15s ease}.ce-element--selected{outline:2px solid #0066FF;outline-width:calc(2px * var(--ce-inverse-zoom, 1));outline-offset:calc(-1px * var(--ce-inverse-zoom, 1));box-shadow:0 0 0 calc(1px * var(--ce-inverse-zoom, 1)) #ffffffe6,0 2px 8px #0066ff26;z-index:100!important}.ce-element img{pointer-events:none;-webkit-user-drag:none;-webkit-user-select:none;user-select:none}.ce-editor--editing-group .ce-element{opacity:.15;pointer-events:none!important;transition:opacity .2s ease,filter .2s ease}.ce-editor--editing-group .ce-element--editing-active{opacity:1;filter:none;pointer-events:auto;z-index:50!important;outline:2px dashed #0066FF;outline-offset:4px}.ce-editor--editing-group .ce-element--editing-active .ce-element{opacity:1;filter:none;pointer-events:auto!important}.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(8px * var(--ce-inverse-zoom, 1));height:calc(8px * var(--ce-inverse-zoom, 1));background:#fff;border:calc(1.5px * var(--ce-inverse-zoom, 1)) solid #0066FF;border-radius:2px;position:absolute;z-index:101;box-shadow:0 1px 3px #00000026;transition:transform .1s ease,box-shadow .1s ease}.ce-resize-handle:hover{transform:scale(1.2);box-shadow:0 2px 6px #0003}.ce-resize-handle--nw{top:calc(-4px * var(--ce-inverse-zoom, 1));left:calc(-4px * var(--ce-inverse-zoom, 1));cursor:nw-resize}.ce-resize-handle--ne{top:calc(-4px * var(--ce-inverse-zoom, 1));right:calc(-4px * var(--ce-inverse-zoom, 1));cursor:ne-resize}.ce-resize-handle--sw{bottom:calc(-4px * var(--ce-inverse-zoom, 1));left:calc(-4px * var(--ce-inverse-zoom, 1));cursor:sw-resize}.ce-resize-handle--se{bottom:calc(-4px * var(--ce-inverse-zoom, 1));right:calc(-4px * var(--ce-inverse-zoom, 1));cursor:se-resize}.ce-resize-handle--n{top:calc(-4px * var(--ce-inverse-zoom, 1));left:50%;transform:translate(-50%);cursor:n-resize}.ce-resize-handle--s{bottom:calc(-4px * var(--ce-inverse-zoom, 1));left:50%;transform:translate(-50%);cursor:s-resize}.ce-resize-handle--e{right:calc(-4px * var(--ce-inverse-zoom, 1));top:50%;transform:translateY(-50%);cursor:e-resize}.ce-resize-handle--w{left:calc(-4px * var(--ce-inverse-zoom, 1));top:50%;transform:translateY(-50%);cursor:w-resize}.ce-rotate-handle{width:calc(20px * var(--ce-inverse-zoom, 1));height:calc(20px * var(--ce-inverse-zoom, 1));background:#fff;border:none;box-shadow:0 2px 6px #00000026;border-radius:50%;position:absolute;bottom:auto;top:calc(-32px * var(--ce-inverse-zoom, 1));left:50%;transform:translate(-50%);margin-left:0;cursor:grab;z-index:101;display:flex;align-items:center;justify-content:center;color:#06f;pointer-events:auto;transition:transform .1s ease,box-shadow .1s ease}.ce-rotate-handle svg{width:calc(12px * var(--ce-inverse-zoom, 1));height:calc(12px * var(--ce-inverse-zoom, 1))}.ce-rotate-handle:hover{transform:translate(-50%) scale(1.1);box-shadow:0 3px 8px #0003}.ce-rotate-handle:active{cursor:grabbing;background:#f3f4f6}.ce-drag-handle-minimal{width:calc(20px * var(--ce-inverse-zoom, 1));height:calc(20px * var(--ce-inverse-zoom, 1));position:absolute;top:auto;bottom:calc(-32px * var(--ce-inverse-zoom, 1));left:50%;transform:translate(-50%);margin-left:0;background-color:#fff;border-radius:50%;box-shadow:0 2px 6px #00000026;cursor:grab;z-index:102;display:flex;align-items:center;justify-content:center;color:#06f;transition:transform .1s ease,box-shadow .1s ease}.ce-drag-handle-minimal svg{width:calc(14px * var(--ce-inverse-zoom, 1));height:calc(14px * var(--ce-inverse-zoom, 1))}.ce-drag-handle-minimal:hover{transform:translate(-50%) scale(1.1);box-shadow:0 3px 8px #0003}.ce-drag-handle-minimal:active{cursor:grabbing;background-color:#f3f4f6}.ce-selection-overlay{position:absolute;top:0;left:0;width:0;height:0;pointer-events:none;z-index:1000}.ce-selection-overlay__box{position:absolute;border:2px solid #0066FF;background-color:#0066ff0a;pointer-events:none;box-shadow:0 0 0 1px #ffffffe6,0 2px 8px #0066ff26;transition:opacity .1s ease}.ce-resize-handle{position:absolute;width:10px;height:10px;background-color:#fff;border:calc(1.5px * var(--ce-inverse-zoom, 1)) solid #3b82f6;border-radius:calc(2px * var(--ce-inverse-zoom, 1));z-index:101;pointer-events:auto;box-shadow:0 1px 4px #0000001f,0 0 0 calc(.5px * var(--ce-inverse-zoom, 1)) #ffffffe6;transition:all .15s cubic-bezier(.4,0,.2,1)}.ce-resize-handle:hover{background-color:#3b82f6;box-shadow:0 2px 8px #3b82f659,0 0 0 calc(1px * var(--ce-inverse-zoom, 1)) #fff;transform:scale(1.2)}.ce-resize-handle--nw{top:-5px;left:-5px;cursor:nw-resize}.ce-resize-handle--ne{top:-5px;right:-5px;cursor:ne-resize}.ce-resize-handle--sw{bottom:-5px;left:-5px;cursor:sw-resize}.ce-resize-handle--se{bottom:-5px;right:-5px;cursor:se-resize}.ce-resize-handle--n{top:-5px;left:50%;transform:translate(-50%);cursor:n-resize}.ce-resize-handle--s{bottom:-5px;left:50%;transform:translate(-50%);cursor:s-resize}.ce-resize-handle--e{top:50%;right:-5px;transform:translateY(-50%);cursor:e-resize}.ce-resize-handle--w{top:50%;left:-5px;transform:translateY(-50%);cursor:w-resize}.ce-drag-handle-minimal{position:absolute;top:auto;left:50%;transform:translate(-50%);cursor:grab;background-color:#fff;border-radius:50%;border:calc(1.5px * var(--ce-inverse-zoom, 1)) solid #3b82f6;box-shadow:0 2px 8px #00000026,0 0 0 calc(.5px * var(--ce-inverse-zoom, 1)) #ffffffe6;display:flex;align-items:center;justify-content:center;color:#3b82f6;-webkit-user-select:none;user-select:none;pointer-events:auto;z-index:102;transition:all .15s cubic-bezier(.4,0,.2,1)}.ce-drag-handle-minimal:hover{background-color:#3b82f6;color:#fff;box-shadow:0 4px 12px #3b82f64d,0 0 0 calc(1px * var(--ce-inverse-zoom, 1)) #fff;transform:translate(-50%) scale(1.1)}.ce-drag-handle-minimal:active{cursor:grabbing}.ce-drag-handle-minimal svg{width:calc(16px * var(--ce-inverse-zoom, 1));height:calc(16px * var(--ce-inverse-zoom, 1));fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}.ce-element-content{width:100%;height:100%}.ce-snap-guide{position:absolute;background-color:#06f;pointer-events:none;z-index:200;opacity:.85;transition:opacity .15s ease;box-shadow:0 0 0 .5px #fff9;animation:snapGuideFadeIn .15s ease-out}.ce-snap-guide--vertical{width:1px;height:100%;left:0;top:0}.ce-snap-guide--horizontal{height:1px;width:100%;left:0;top:0}.ce-snap-guide:after{content:"";position:absolute;inset:-.5px;background:#0066ff26;border-radius:1px;z-index:-1}@keyframes snapGuideFadeIn{0%{opacity:0}to{opacity:.85}}.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}.demo-node{width:100%;height:100%;background:#fff;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;-webkit-user-select:none;user-select:none;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f;display:flex;flex-direction:column}.demo-node__header{height:32px;background:#f9fafb;border-bottom:1px solid #e5e7eb;display:flex;align-items:center;padding:0 12px;font-size:12px;font-weight:500;color:#6b7280}.demo-node__body{flex:1;padding:12px;font-size:14px;color:#1f2937;display:flex;align-items:center;justify-content:center;background:#fff}.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}}.ce-group-node{position:relative;pointer-events:none;width:100%;height:100%}.ce-group-node--editing{outline:2px dashed #0066FF;outline-offset:4px;border-radius:4px}.ce-editor--editing-group .ce-element{opacity:.5;pointer-events:none!important;transition:opacity .25s ease,filter .25s ease;filter:grayscale(.15)}.ce-editor--editing-group .ce-group-node--editing{opacity:1;filter:none;pointer-events:auto;z-index:50!important;outline:2px dashed #0066FF;outline-offset:4px;border-radius:4px;background:#0066ff05;box-shadow:0 0 0 1px #0066ff1a}.ce-editor--editing-group .ce-group-node--editing .ce-element{opacity:1;filter:none;pointer-events:auto!important}.ce-editor--editing-group .ce-group-node--editing .ce-element--selected{opacity:1;filter:none;z-index:100!important}.ce-editor--editing-group .ce-group-node--editing>.ce-element{opacity:1!important;filter:none!important}.ce-element[data-node-type=group].ce-element--selected{outline:2px solid #0066FF;outline-offset:2px;box-shadow:0 0 0 1px #ffffffe6,0 2px 8px #06f3}.ce-error-boundary{padding:20px;background-color:#fee;border:1px solid #fcc;border-radius:4px;color:#c00}.ce-error-boundary__title{margin:0 0 10px;font-size:18px}.ce-error-boundary__message{margin:0 0 10px}.ce-error-boundary__details{font-size:14px}.ce-error-boundary__details summary{cursor:pointer;margin-bottom:10px}.ce-error-boundary__details pre{padding:10px;background-color:#fff;border:1px solid #ddd;border-radius:4px;overflow:auto;font-size:12px}.ce-accessibility-instructions{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.ce-group-editor{position:fixed;top:16px;left:50%;transform:translate(-50%);z-index:10000;display:flex;align-items:center;gap:8px;background-color:#fffffff2;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);padding:8px 16px;border-radius:8px;box-shadow:0 4px 12px #00000026,0 0 0 1px #0000000d;pointer-events:auto}.ce-group-editor__breadcrumbs{display:flex;align-items:center;gap:4px;font-size:13px;color:#666}.ce-group-editor__separator{color:#999;margin:0 4px}.ce-group-editor__breadcrumb{background:none;border:none;padding:4px 8px;border-radius:4px;cursor:pointer;color:#666;font-weight:400;transition:background-color .15s ease}.ce-group-editor__breadcrumb--active{color:#06f;font-weight:600}.ce-group-editor__breadcrumb:hover:not(.ce-group-editor__breadcrumb--active){background-color:#0066ff1a}.ce-group-editor__exit-button{background:none;border:1px solid #ddd;border-radius:6px;padding:6px 12px;cursor:pointer;color:#666;font-size:12px;font-weight:500;display:flex;align-items:center;gap:6px;transition:all .15s ease;margin-left:8px}.ce-group-editor__exit-button:hover{background-color:#f5f5f5;border-color:#bbb}.ce-default-node{padding:20px;background-color:#f0f0f0}.ce-virtualization-debug{position:fixed;bottom:10px;right:10px;padding:8px;background-color:#000000b3;color:#fff;font-size:12px;border-radius:4px;z-index:10000}.ce-virtualized-container{position:relative;width:100%;height:100%}.ce-global-snap-guide{position:absolute;inset:0;pointer-events:none;z-index:200}
|