@knotx/react 0.4.11 → 0.4.13
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.en.md +722 -0
- package/README.md +722 -0
- package/dist/index.cjs +9 -3
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +9 -3
- package/package.json +8 -8
package/README.md
ADDED
|
@@ -0,0 +1,722 @@
|
|
|
1
|
+
# @knotx/react
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@knotx/react)
|
|
4
|
+
[](https://github.com/boenfu/knotx/blob/main/LICENSE)
|
|
5
|
+
|
|
6
|
+
> 🚀 一个强大的 React 节点编辑器组件,支持可视化流程图、组织架构图、脑图等多种场景
|
|
7
|
+
|
|
8
|
+
## 📦 安装
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @knotx/react
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
yarn add @knotx/react
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @knotx/react
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 对等依赖
|
|
23
|
+
|
|
24
|
+
该包需要以下对等依赖:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"react": ">=17.0.0",
|
|
29
|
+
"react-dom": ">=17.0.0"
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 🎯 基本概述
|
|
34
|
+
|
|
35
|
+
`@knotx/react` 是 Knotx 生态系统中的 React 适配器,提供了一个功能强大且灵活的节点编辑器组件。它支持:
|
|
36
|
+
|
|
37
|
+
- 🎨 **可视化节点编辑** - 拖拽、缩放、连接节点
|
|
38
|
+
- 🔧 **插件系统** - 高度可扩展的插件架构
|
|
39
|
+
- 🎭 **自定义渲染** - 完全自定义的节点和边的渲染
|
|
40
|
+
- 📱 **响应式设计** - 适配不同屏幕尺寸
|
|
41
|
+
- ⚡ **高性能** - 基于 RxJS 的响应式状态管理
|
|
42
|
+
- 🎪 **丰富交互** - 支持选择、拖拽、缩放、小地图等
|
|
43
|
+
|
|
44
|
+
## 🚀 快速开始
|
|
45
|
+
|
|
46
|
+
### 基础用法
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import type { Edge, Node } from '@knotx/core'
|
|
50
|
+
|
|
51
|
+
import { Knotx } from '@knotx/react'
|
|
52
|
+
import React from 'react'
|
|
53
|
+
|
|
54
|
+
function App() {
|
|
55
|
+
const initialNodes: Node[] = [
|
|
56
|
+
{
|
|
57
|
+
id: '1',
|
|
58
|
+
type: 'basic',
|
|
59
|
+
position: { x: 300, y: 300 },
|
|
60
|
+
measured: { width: 160, height: 60 },
|
|
61
|
+
data: { label: '节点 1' }
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: '2',
|
|
65
|
+
type: 'basic',
|
|
66
|
+
position: { x: 600, y: 300 },
|
|
67
|
+
measured: { width: 160, height: 60 },
|
|
68
|
+
data: { label: '节点 2' }
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
const initialEdges: Edge[] = [
|
|
73
|
+
{
|
|
74
|
+
id: '1-2',
|
|
75
|
+
type: 'bezier',
|
|
76
|
+
source: '1',
|
|
77
|
+
target: '2',
|
|
78
|
+
data: {}
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div style={{ width: '100vw', height: '100vh' }}>
|
|
84
|
+
<Knotx
|
|
85
|
+
initialNodes={initialNodes}
|
|
86
|
+
initialEdges={initialEdges}
|
|
87
|
+
plugins={[]} // 插件列表
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export default App
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 自定义节点渲染
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import type { Node } from '@knotx/core'
|
|
100
|
+
|
|
101
|
+
import { BasePlugin } from '@knotx/core'
|
|
102
|
+
import { nodeType } from '@knotx/decorators'
|
|
103
|
+
import { Knotx } from '@knotx/react'
|
|
104
|
+
import React from 'react'
|
|
105
|
+
|
|
106
|
+
// 创建自定义节点插件
|
|
107
|
+
class CustomNodePlugin extends BasePlugin<'customNode'> {
|
|
108
|
+
name = 'customNode' as const
|
|
109
|
+
|
|
110
|
+
@nodeType('custom')
|
|
111
|
+
renderCustomNode({ node }: { node: Node }) {
|
|
112
|
+
return (
|
|
113
|
+
<div
|
|
114
|
+
style={{
|
|
115
|
+
width: '100%',
|
|
116
|
+
height: '100%',
|
|
117
|
+
background: '#f0f0f0',
|
|
118
|
+
border: '2px solid #ccc',
|
|
119
|
+
borderRadius: '8px',
|
|
120
|
+
padding: '12px',
|
|
121
|
+
display: 'flex',
|
|
122
|
+
alignItems: 'center',
|
|
123
|
+
justifyContent: 'center',
|
|
124
|
+
fontSize: '14px',
|
|
125
|
+
fontWeight: 'bold'
|
|
126
|
+
}}
|
|
127
|
+
>
|
|
128
|
+
{node.data?.label || node.id}
|
|
129
|
+
</div>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function App() {
|
|
135
|
+
const nodes: Node[] = [
|
|
136
|
+
{
|
|
137
|
+
id: '1',
|
|
138
|
+
type: 'custom',
|
|
139
|
+
position: { x: 300, y: 300 },
|
|
140
|
+
measured: { width: 200, height: 80 },
|
|
141
|
+
data: { label: '自定义节点' }
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<div style={{ width: '100vw', height: '100vh' }}>
|
|
147
|
+
<Knotx
|
|
148
|
+
initialNodes={nodes}
|
|
149
|
+
plugins={[CustomNodePlugin]}
|
|
150
|
+
/>
|
|
151
|
+
</div>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 受控组件模式
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import type { Edge, Node, ReactEngine } from '@knotx/react'
|
|
160
|
+
|
|
161
|
+
import { Knotx } from '@knotx/react'
|
|
162
|
+
import React, { useState } from 'react'
|
|
163
|
+
|
|
164
|
+
function App() {
|
|
165
|
+
const [nodes, setNodes] = useState<Node[]>([
|
|
166
|
+
{
|
|
167
|
+
id: '1',
|
|
168
|
+
type: 'basic',
|
|
169
|
+
position: { x: 300, y: 300 },
|
|
170
|
+
measured: { width: 160, height: 60 },
|
|
171
|
+
data: { label: '节点 1' }
|
|
172
|
+
}
|
|
173
|
+
])
|
|
174
|
+
|
|
175
|
+
const [edges, setEdges] = useState<Edge[]>([])
|
|
176
|
+
|
|
177
|
+
const handleInit = (engine: ReactEngine) => {
|
|
178
|
+
console.log('引擎初始化完成:', engine)
|
|
179
|
+
|
|
180
|
+
// 监听节点变化
|
|
181
|
+
engine.nodesManager.dataMap$.subscribe((nodesMap) => {
|
|
182
|
+
setNodes(Array.from(nodesMap.values()))
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
// 监听边变化
|
|
186
|
+
engine.edgesManager.dataMap$.subscribe((edgesMap) => {
|
|
187
|
+
setEdges(Array.from(edgesMap.values()))
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<div style={{ width: '100vw', height: '100vh' }}>
|
|
193
|
+
<Knotx
|
|
194
|
+
nodes={nodes}
|
|
195
|
+
edges={edges}
|
|
196
|
+
onInit={handleInit}
|
|
197
|
+
plugins={[]}
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## 🔧 API 参考
|
|
205
|
+
|
|
206
|
+
### Knotx 组件
|
|
207
|
+
|
|
208
|
+
`Knotx` 是主要的 React 组件,用于渲染节点编辑器。
|
|
209
|
+
|
|
210
|
+
#### Props
|
|
211
|
+
|
|
212
|
+
| 属性 | 类型 | 默认值 | 描述 |
|
|
213
|
+
|------|------|--------|------|
|
|
214
|
+
| `className` | `string` | - | 容器的 CSS 类名 |
|
|
215
|
+
| `style` | `CSSProperties` | - | 容器的内联样式 |
|
|
216
|
+
| `getContainerStyle` | `(engine: ReactEngine) => CSSProperties` | - | 获取容器样式的函数 |
|
|
217
|
+
| `initialNodes` | `Node[]` | `[]` | 初始节点数据 |
|
|
218
|
+
| `initialEdges` | `Edge[]` | `[]` | 初始边数据 |
|
|
219
|
+
| `nodes` | `Node[]` | - | 受控的节点数据 |
|
|
220
|
+
| `edges` | `Edge[]` | - | 受控的边数据 |
|
|
221
|
+
| `plugins` | `Plugin[]` | `[]` | 插件列表 |
|
|
222
|
+
| `pluginConfig` | `PluginConfig` | `{}` | 插件配置 |
|
|
223
|
+
| `disablePresetPlugins` | `boolean` | `false` | 禁用预设插件 |
|
|
224
|
+
| `onInit` | `(engine: ReactEngine) => void` | - | 引擎初始化回调 |
|
|
225
|
+
| `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` | 布局方向 |
|
|
226
|
+
| `ref` | `Ref<KnotxInstance>` | - | 组件引用 |
|
|
227
|
+
|
|
228
|
+
#### 示例
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import type { KnotxInstance, ReactEngine } from '@knotx/react'
|
|
232
|
+
|
|
233
|
+
import { Knotx } from '@knotx/react'
|
|
234
|
+
import React, { useRef } from 'react'
|
|
235
|
+
|
|
236
|
+
function App() {
|
|
237
|
+
const knotxRef = useRef<KnotxInstance>(null)
|
|
238
|
+
|
|
239
|
+
const handleInit = (engine: ReactEngine) => {
|
|
240
|
+
console.log('引擎初始化完成')
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const handleRerender = () => {
|
|
244
|
+
knotxRef.current?.rerender()
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<div>
|
|
249
|
+
<button onClick={handleRerender}>重新渲染</button>
|
|
250
|
+
<Knotx
|
|
251
|
+
ref={knotxRef}
|
|
252
|
+
className="my-knotx"
|
|
253
|
+
style={{ border: '1px solid #ccc' }}
|
|
254
|
+
initialNodes={[]}
|
|
255
|
+
initialEdges={[]}
|
|
256
|
+
plugins={[]}
|
|
257
|
+
direction="horizontal"
|
|
258
|
+
onInit={handleInit}
|
|
259
|
+
getContainerStyle={engine => ({
|
|
260
|
+
backgroundColor: '#f5f5f5'
|
|
261
|
+
})}
|
|
262
|
+
/>
|
|
263
|
+
</div>
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### KnotxInstance 接口
|
|
269
|
+
|
|
270
|
+
通过 `ref` 获取的组件实例接口。
|
|
271
|
+
|
|
272
|
+
#### 属性
|
|
273
|
+
|
|
274
|
+
| 属性 | 类型 | 描述 |
|
|
275
|
+
|------|------|------|
|
|
276
|
+
| `engineRef` | `MutableRefObject<ReactEngine \| null>` | 引擎实例引用 |
|
|
277
|
+
| `rerender` | `() => void` | 强制重新渲染组件 |
|
|
278
|
+
|
|
279
|
+
#### 示例
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
import type { KnotxInstance } from '@knotx/react'
|
|
283
|
+
|
|
284
|
+
import { Knotx } from '@knotx/react'
|
|
285
|
+
import React, { useEffect, useRef } from 'react'
|
|
286
|
+
|
|
287
|
+
function App() {
|
|
288
|
+
const knotxRef = useRef<KnotxInstance>(null)
|
|
289
|
+
|
|
290
|
+
useEffect(() => {
|
|
291
|
+
if (knotxRef.current) {
|
|
292
|
+
const engine = knotxRef.current.engineRef.current
|
|
293
|
+
if (engine) {
|
|
294
|
+
// 使用引擎 API
|
|
295
|
+
console.log('当前节点数量:', engine.nodesManager.dataMap$.value.size)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}, [])
|
|
299
|
+
|
|
300
|
+
return (
|
|
301
|
+
<Knotx
|
|
302
|
+
ref={knotxRef}
|
|
303
|
+
initialNodes={[]}
|
|
304
|
+
initialEdges={[]}
|
|
305
|
+
plugins={[]}
|
|
306
|
+
/>
|
|
307
|
+
)
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### ReactEngine 类型
|
|
312
|
+
|
|
313
|
+
扩展自 `@knotx/core` 的 `Engine` 类型,专门用于 React 环境。
|
|
314
|
+
|
|
315
|
+
#### 主要方法
|
|
316
|
+
|
|
317
|
+
| 方法 | 描述 |
|
|
318
|
+
|------|------|
|
|
319
|
+
| `dispatchNodeOperation(operation: NodeOperation)` | 分发节点操作 |
|
|
320
|
+
| `dispatchEdgeOperation(operation: EdgeOperation)` | 分发边操作 |
|
|
321
|
+
| `getLayerComponents(layer: number)` | 获取层级组件 |
|
|
322
|
+
| `destroy()` | 销毁引擎实例 |
|
|
323
|
+
|
|
324
|
+
### 插件配置
|
|
325
|
+
|
|
326
|
+
#### 插件配置类型
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
interface PluginConfig {
|
|
330
|
+
[pluginName: string]: any
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
#### 示例
|
|
335
|
+
|
|
336
|
+
```tsx
|
|
337
|
+
import { Canvas } from '@knotx/plugins-canvas'
|
|
338
|
+
import { Drag } from '@knotx/plugins-drag'
|
|
339
|
+
import { Knotx } from '@knotx/react'
|
|
340
|
+
import React from 'react'
|
|
341
|
+
|
|
342
|
+
function App() {
|
|
343
|
+
const pluginConfig = {
|
|
344
|
+
canvas: {
|
|
345
|
+
minScale: 0.1,
|
|
346
|
+
maxScale: 2.0,
|
|
347
|
+
wheel: {
|
|
348
|
+
step: 0.1,
|
|
349
|
+
wheelDisabled: false
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
drag: {
|
|
353
|
+
allowDrag: true,
|
|
354
|
+
dragThreshold: 5
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return (
|
|
359
|
+
<Knotx
|
|
360
|
+
initialNodes={[]}
|
|
361
|
+
initialEdges={[]}
|
|
362
|
+
plugins={[Canvas, Drag]}
|
|
363
|
+
pluginConfig={pluginConfig}
|
|
364
|
+
/>
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## 🧩 插件系统
|
|
370
|
+
|
|
371
|
+
### 常用插件
|
|
372
|
+
|
|
373
|
+
以下是一些常用的插件:
|
|
374
|
+
|
|
375
|
+
- `@knotx/plugins-canvas` - 画布功能(缩放、平移)
|
|
376
|
+
- `@knotx/plugins-drag` - 拖拽功能
|
|
377
|
+
- `@knotx/plugins-selection` - 选择功能
|
|
378
|
+
- `@knotx/plugins-minimap` - 小地图
|
|
379
|
+
- `@knotx/plugins-background` - 背景
|
|
380
|
+
- `@knotx/plugins-bounding` - 边界调整
|
|
381
|
+
- `@knotx/plugins-connection-line` - 连接线
|
|
382
|
+
- `@knotx/plugins-group` - 分组
|
|
383
|
+
- `@knotx/plugins-history` - 历史记录
|
|
384
|
+
|
|
385
|
+
### 创建自定义插件
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
import type { EdgeProps, Node } from '@knotx/core'
|
|
389
|
+
|
|
390
|
+
import { BasePlugin } from '@knotx/core'
|
|
391
|
+
import { edgeType, nodeType } from '@knotx/decorators'
|
|
392
|
+
|
|
393
|
+
class MyPlugin extends BasePlugin<'myPlugin'> {
|
|
394
|
+
name = 'myPlugin' as const
|
|
395
|
+
|
|
396
|
+
@nodeType('myNode')
|
|
397
|
+
renderMyNode({ node }: { node: Node }) {
|
|
398
|
+
return (
|
|
399
|
+
<div style={{ padding: '10px', border: '1px solid #ccc' }}>
|
|
400
|
+
自定义节点:
|
|
401
|
+
{' '}
|
|
402
|
+
{node.id}
|
|
403
|
+
</div>
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
@edgeType('myEdge')
|
|
408
|
+
renderMyEdge(props: EdgeProps) {
|
|
409
|
+
return (
|
|
410
|
+
<path
|
|
411
|
+
d={props.path}
|
|
412
|
+
stroke="#ff0000"
|
|
413
|
+
strokeWidth={2}
|
|
414
|
+
fill="none"
|
|
415
|
+
/>
|
|
416
|
+
)
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// 使用插件
|
|
421
|
+
function App() {
|
|
422
|
+
return (
|
|
423
|
+
<Knotx
|
|
424
|
+
initialNodes={[
|
|
425
|
+
{
|
|
426
|
+
id: '1',
|
|
427
|
+
type: 'myNode',
|
|
428
|
+
position: { x: 100, y: 100 },
|
|
429
|
+
measured: { width: 120, height: 60 },
|
|
430
|
+
data: {}
|
|
431
|
+
}
|
|
432
|
+
]}
|
|
433
|
+
initialEdges={[]}
|
|
434
|
+
plugins={[MyPlugin]}
|
|
435
|
+
/>
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## 📂 文件目录结构
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
packages/react/
|
|
444
|
+
├── src/
|
|
445
|
+
│ ├── components/ # React 组件
|
|
446
|
+
│ │ ├── content.tsx # 层级内容组件
|
|
447
|
+
│ │ └── index.ts # 组件导出
|
|
448
|
+
│ ├── hooks/ # React Hooks
|
|
449
|
+
│ │ ├── container.ts # 容器引用 Hook
|
|
450
|
+
│ │ ├── data.ts # 数据更新 Hook
|
|
451
|
+
│ │ ├── engine.ts # 引擎管理 Hook
|
|
452
|
+
│ │ ├── plugin.ts # 插件管理 Hook
|
|
453
|
+
│ │ └── index.ts # Hooks 导出
|
|
454
|
+
│ ├── definition.ts # 类型定义
|
|
455
|
+
│ ├── engine.ts # React 引擎初始化
|
|
456
|
+
│ ├── index.ts # 主入口文件
|
|
457
|
+
│ ├── knotx.tsx # 主要 Knotx 组件
|
|
458
|
+
│ └── layer.tsx # 层级渲染组件
|
|
459
|
+
├── dist/ # 构建输出目录
|
|
460
|
+
├── CHANGELOG.md # 变更日志
|
|
461
|
+
├── package.json # 包配置
|
|
462
|
+
├── README.md # 中文文档
|
|
463
|
+
├── README.en.md # 英文文档
|
|
464
|
+
└── tsconfig.json # TypeScript 配置
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### 核心文件说明
|
|
468
|
+
|
|
469
|
+
| 文件 | 描述 |
|
|
470
|
+
|------|------|
|
|
471
|
+
| `knotx.tsx` | 主要的 Knotx React 组件实现 |
|
|
472
|
+
| `definition.ts` | TypeScript 类型定义和接口 |
|
|
473
|
+
| `engine.ts` | React 引擎初始化和运行时配置 |
|
|
474
|
+
| `layer.tsx` | 层级渲染逻辑 |
|
|
475
|
+
| `components/content.tsx` | 层级内容渲染组件 |
|
|
476
|
+
| `hooks/container.ts` | 容器大小监听 Hook |
|
|
477
|
+
| `hooks/data.ts` | 数据差异更新 Hook |
|
|
478
|
+
| `hooks/engine.ts` | 引擎生命周期管理 Hook |
|
|
479
|
+
| `hooks/plugin.ts` | 插件合并和配置更新 Hook |
|
|
480
|
+
|
|
481
|
+
## 🔗 类型导出
|
|
482
|
+
|
|
483
|
+
该包重新导出了所有来自 `@knotx/core` 的类型,方便使用:
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
import type {
|
|
487
|
+
// 其他
|
|
488
|
+
Direction,
|
|
489
|
+
Edge,
|
|
490
|
+
EdgeOperation,
|
|
491
|
+
EdgeProps,
|
|
492
|
+
|
|
493
|
+
Engine,
|
|
494
|
+
EngineOptions,
|
|
495
|
+
|
|
496
|
+
IData,
|
|
497
|
+
IPlugin,
|
|
498
|
+
|
|
499
|
+
KnotxInstance,
|
|
500
|
+
KnotxProps,
|
|
501
|
+
|
|
502
|
+
Layer,
|
|
503
|
+
// 核心类型
|
|
504
|
+
Node,
|
|
505
|
+
|
|
506
|
+
// 操作类型
|
|
507
|
+
NodeOperation,
|
|
508
|
+
NodePosition,
|
|
509
|
+
// 属性类型
|
|
510
|
+
NodeProps,
|
|
511
|
+
Plugin,
|
|
512
|
+
|
|
513
|
+
// 配置类型
|
|
514
|
+
PluginConfigs,
|
|
515
|
+
// 位置类型
|
|
516
|
+
Position,
|
|
517
|
+
// React 特定类型
|
|
518
|
+
ReactEngine
|
|
519
|
+
} from '@knotx/react'
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
## 🎨 最佳实践
|
|
523
|
+
|
|
524
|
+
### 1. 性能优化
|
|
525
|
+
|
|
526
|
+
```tsx
|
|
527
|
+
import { Knotx } from '@knotx/react'
|
|
528
|
+
import React, { memo, useMemo } from 'react'
|
|
529
|
+
|
|
530
|
+
const MyKnotx = memo(({ data }: { data: any }) => {
|
|
531
|
+
// 使用 useMemo 缓存节点和边数据
|
|
532
|
+
const nodes = useMemo(() => {
|
|
533
|
+
return data.nodes || []
|
|
534
|
+
}, [data.nodes])
|
|
535
|
+
|
|
536
|
+
const edges = useMemo(() => {
|
|
537
|
+
return data.edges || []
|
|
538
|
+
}, [data.edges])
|
|
539
|
+
|
|
540
|
+
return (
|
|
541
|
+
<Knotx
|
|
542
|
+
nodes={nodes}
|
|
543
|
+
edges={edges}
|
|
544
|
+
plugins={[]}
|
|
545
|
+
/>
|
|
546
|
+
)
|
|
547
|
+
})
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### 2. 错误处理
|
|
551
|
+
|
|
552
|
+
```tsx
|
|
553
|
+
import { Knotx } from '@knotx/react'
|
|
554
|
+
import React, { ErrorBoundary } from 'react'
|
|
555
|
+
|
|
556
|
+
class KnotxErrorBoundary extends React.Component {
|
|
557
|
+
constructor(props: any) {
|
|
558
|
+
super(props)
|
|
559
|
+
this.state = { hasError: false }
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
static getDerivedStateFromError(error: Error) {
|
|
563
|
+
// 处理错误:记录错误并返回错误状态
|
|
564
|
+
console.error('React Error Boundary:', error)
|
|
565
|
+
return { hasError: true }
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
|
|
569
|
+
// 处理错误:记录错误信息
|
|
570
|
+
console.error('Knotx Error:', error, errorInfo)
|
|
571
|
+
|
|
572
|
+
// 可以在这里添加错误上报逻辑
|
|
573
|
+
// 例如:this.reportError(error, errorInfo)
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
render() {
|
|
577
|
+
if (this.state.hasError) {
|
|
578
|
+
return <div>Something went wrong with Knotx.</div>
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return this.props.children
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function App() {
|
|
586
|
+
return (
|
|
587
|
+
<KnotxErrorBoundary>
|
|
588
|
+
<Knotx
|
|
589
|
+
initialNodes={[]}
|
|
590
|
+
initialEdges={[]}
|
|
591
|
+
plugins={[]}
|
|
592
|
+
/>
|
|
593
|
+
</KnotxErrorBoundary>
|
|
594
|
+
)
|
|
595
|
+
}
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### 3. 主题定制
|
|
599
|
+
|
|
600
|
+
```tsx
|
|
601
|
+
import { BasePlugin } from '@knotx/core'
|
|
602
|
+
import { layer } from '@knotx/decorators'
|
|
603
|
+
import { Knotx } from '@knotx/react'
|
|
604
|
+
import React from 'react'
|
|
605
|
+
|
|
606
|
+
class ThemePlugin extends BasePlugin<'theme'> {
|
|
607
|
+
name = 'theme' as const
|
|
608
|
+
|
|
609
|
+
@layer(100)
|
|
610
|
+
renderTheme() {
|
|
611
|
+
return (
|
|
612
|
+
<style>
|
|
613
|
+
{`
|
|
614
|
+
.knotx-container {
|
|
615
|
+
--node-bg: #ffffff;
|
|
616
|
+
--node-border: #e1e4e8;
|
|
617
|
+
--edge-stroke: #586069;
|
|
618
|
+
--selection-bg: rgba(0, 123, 255, 0.1);
|
|
619
|
+
--selection-border: #007bff;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
.dark-theme .knotx-container {
|
|
623
|
+
--node-bg: #2d3748;
|
|
624
|
+
--node-border: #4a5568;
|
|
625
|
+
--edge-stroke: #a0aec0;
|
|
626
|
+
--selection-bg: rgba(66, 153, 225, 0.1);
|
|
627
|
+
--selection-border: #4299e1;
|
|
628
|
+
}
|
|
629
|
+
`}
|
|
630
|
+
</style>
|
|
631
|
+
)
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function App() {
|
|
636
|
+
return (
|
|
637
|
+
<div className="dark-theme">
|
|
638
|
+
<Knotx
|
|
639
|
+
initialNodes={[]}
|
|
640
|
+
initialEdges={[]}
|
|
641
|
+
plugins={[ThemePlugin]}
|
|
642
|
+
/>
|
|
643
|
+
</div>
|
|
644
|
+
)
|
|
645
|
+
}
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
## 📝 常见问题
|
|
649
|
+
|
|
650
|
+
### Q: 如何处理大量节点的性能问题?
|
|
651
|
+
|
|
652
|
+
A: 可以使用虚拟化技术,只渲染可见区域的节点:
|
|
653
|
+
|
|
654
|
+
```tsx
|
|
655
|
+
import { BasePlugin } from '@knotx/core'
|
|
656
|
+
import { Knotx } from '@knotx/react'
|
|
657
|
+
import React from 'react'
|
|
658
|
+
|
|
659
|
+
// 实现虚拟化插件
|
|
660
|
+
class VirtualizationPlugin extends BasePlugin<'virtualization'> {
|
|
661
|
+
name = 'virtualization' as const
|
|
662
|
+
|
|
663
|
+
// 实现虚拟化逻辑
|
|
664
|
+
// ...
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
### Q: 如何实现节点的自定义连接点?
|
|
669
|
+
|
|
670
|
+
A: 使用 `@knotx/plugins-connection-line` 插件:
|
|
671
|
+
|
|
672
|
+
```tsx
|
|
673
|
+
import { ConnectionLine } from '@knotx/plugins-connection-line'
|
|
674
|
+
|
|
675
|
+
// 在节点中添加连接点
|
|
676
|
+
function CustomNode({ node }) {
|
|
677
|
+
return (
|
|
678
|
+
<div>
|
|
679
|
+
<div
|
|
680
|
+
className="connection-handle"
|
|
681
|
+
data-handle-position="top"
|
|
682
|
+
/>
|
|
683
|
+
节点内容
|
|
684
|
+
</div>
|
|
685
|
+
)
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Q: 如何保存和恢复画布状态?
|
|
690
|
+
|
|
691
|
+
A: 通过引擎的数据管理器:
|
|
692
|
+
|
|
693
|
+
```tsx
|
|
694
|
+
function saveState(engine: ReactEngine) {
|
|
695
|
+
const state = {
|
|
696
|
+
nodes: Array.from(engine.nodesManager.dataMap$.value.values()),
|
|
697
|
+
edges: Array.from(engine.edgesManager.dataMap$.value.values()),
|
|
698
|
+
viewport: engine.container
|
|
699
|
+
}
|
|
700
|
+
localStorage.setItem('knotx-state', JSON.stringify(state))
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function loadState(engine: ReactEngine) {
|
|
704
|
+
const state = JSON.parse(localStorage.getItem('knotx-state') || '{}')
|
|
705
|
+
// 恢复状态逻辑
|
|
706
|
+
}
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
## 📄 许可证
|
|
710
|
+
|
|
711
|
+
MIT License - 详见 [LICENSE](https://github.com/boenfu/knotx/blob/main/LICENSE) 文件
|
|
712
|
+
|
|
713
|
+
## 🤝 贡献
|
|
714
|
+
|
|
715
|
+
欢迎提交 Pull Request 和 Issue!
|
|
716
|
+
|
|
717
|
+
## 🔗 相关链接
|
|
718
|
+
|
|
719
|
+
- [GitHub 仓库](https://github.com/boenfu/knotx)
|
|
720
|
+
- [在线示例](https://knotx.dev)
|
|
721
|
+
- [API 文档](https://knotx.dev/docs)
|
|
722
|
+
- [更新日志](./CHANGELOG.md)
|