@knotx/react
v0.5.10
Published
React for Knotx
Downloads
480
Readme
@knotx/react
🚀 一个强大的 React 节点编辑器组件,支持可视化流程图、组织架构图、脑图等多种场景
📦 安装
npm install @knotx/reactyarn add @knotx/reactpnpm add @knotx/react对等依赖
该包需要以下对等依赖:
{
"react": ">=17.0.0",
"react-dom": ">=17.0.0"
}🎯 基本概述
@knotx/react 是 Knotx 生态系统中的 React 适配器,提供了一个功能强大且灵活的节点编辑器组件。它支持:
- 🎨 可视化节点编辑 - 拖拽、缩放、连接节点
- 🔧 插件系统 - 高度可扩展的插件架构
- 🎭 自定义渲染 - 完全自定义的节点和边的渲染
- 📱 响应式设计 - 适配不同屏幕尺寸
- ⚡ 高性能 - 基于 RxJS 的响应式状态管理
- 🎪 丰富交互 - 支持选择、拖拽、缩放、小地图等
🚀 快速开始
基础用法
import type { Edge, Node } from '@knotx/core'
import { Knotx } from '@knotx/react'
import React from 'react'
function App() {
const initialNodes: Node[] = [
{
id: '1',
type: 'basic',
position: { x: 300, y: 300 },
measured: { width: 160, height: 60 },
data: { label: '节点 1' }
},
{
id: '2',
type: 'basic',
position: { x: 600, y: 300 },
measured: { width: 160, height: 60 },
data: { label: '节点 2' }
}
]
const initialEdges: Edge[] = [
{
id: '1-2',
type: 'bezier',
source: '1',
target: '2',
data: {}
}
]
return (
<div style={{ width: '100vw', height: '100vh' }}>
<Knotx
initialNodes={initialNodes}
initialEdges={initialEdges}
plugins={[]} // 插件列表
/>
</div>
)
}
export default App自定义节点渲染
import type { Node } from '@knotx/core'
import { BasePlugin } from '@knotx/core'
import { nodeType } from '@knotx/decorators'
import { Knotx } from '@knotx/react'
import React from 'react'
// 创建自定义节点插件
class CustomNodePlugin extends BasePlugin<'customNode'> {
name = 'customNode' as const
@nodeType('custom')
renderCustomNode({ node }: { node: Node }) {
return (
<div
style={{
width: '100%',
height: '100%',
background: '#f0f0f0',
border: '2px solid #ccc',
borderRadius: '8px',
padding: '12px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '14px',
fontWeight: 'bold'
}}
>
{node.data?.label || node.id}
</div>
)
}
}
function App() {
const nodes: Node[] = [
{
id: '1',
type: 'custom',
position: { x: 300, y: 300 },
measured: { width: 200, height: 80 },
data: { label: '自定义节点' }
}
]
return (
<div style={{ width: '100vw', height: '100vh' }}>
<Knotx
initialNodes={nodes}
plugins={[CustomNodePlugin]}
/>
</div>
)
}受控组件模式
import type { Edge, Node, ReactEngine } from '@knotx/react'
import { Knotx } from '@knotx/react'
import React, { useState } from 'react'
function App() {
const [nodes, setNodes] = useState<Node[]>([
{
id: '1',
type: 'basic',
position: { x: 300, y: 300 },
measured: { width: 160, height: 60 },
data: { label: '节点 1' }
}
])
const [edges, setEdges] = useState<Edge[]>([])
const handleInit = (engine: ReactEngine) => {
console.log('引擎初始化完成:', engine)
// 监听节点变化
engine.nodesManager.dataMap$.subscribe((nodesMap) => {
setNodes(Array.from(nodesMap.values()))
})
// 监听边变化
engine.edgesManager.dataMap$.subscribe((edgesMap) => {
setEdges(Array.from(edgesMap.values()))
})
}
return (
<div style={{ width: '100vw', height: '100vh' }}>
<Knotx
nodes={nodes}
edges={edges}
onInit={handleInit}
plugins={[]}
/>
</div>
)
}🔧 API 参考
Knotx 组件
Knotx 是主要的 React 组件,用于渲染节点编辑器。
Props
| 属性 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| className | string | - | 容器的 CSS 类名 |
| style | CSSProperties | - | 容器的内联样式 |
| getContainerStyle | (engine: ReactEngine) => CSSProperties | - | 获取容器样式的函数 |
| initialNodes | Node[] | [] | 初始节点数据 |
| initialEdges | Edge[] | [] | 初始边数据 |
| nodes | Node[] | - | 受控的节点数据 |
| edges | Edge[] | - | 受控的边数据 |
| plugins | Plugin[] | [] | 插件列表 |
| pluginConfig | PluginConfig | {} | 插件配置 |
| disablePresetPlugins | boolean | false | 禁用预设插件 |
| onInit | (engine: ReactEngine) => void | - | 引擎初始化回调 |
| direction | 'horizontal' \| 'vertical' | 'horizontal' | 布局方向 |
| ref | Ref<KnotxInstance> | - | 组件引用 |
示例
import type { KnotxInstance, ReactEngine } from '@knotx/react'
import { Knotx } from '@knotx/react'
import React, { useRef } from 'react'
function App() {
const knotxRef = useRef<KnotxInstance>(null)
const handleInit = (engine: ReactEngine) => {
console.log('引擎初始化完成')
}
const handleRerender = () => {
knotxRef.current?.rerender()
}
return (
<div>
<button onClick={handleRerender}>重新渲染</button>
<Knotx
ref={knotxRef}
className="my-knotx"
style={{ border: '1px solid #ccc' }}
initialNodes={[]}
initialEdges={[]}
plugins={[]}
direction="horizontal"
onInit={handleInit}
getContainerStyle={engine => ({
backgroundColor: '#f5f5f5'
})}
/>
</div>
)
}KnotxInstance 接口
通过 ref 获取的组件实例接口。
属性
| 属性 | 类型 | 描述 |
|------|------|------|
| engineRef | MutableRefObject<ReactEngine \| null> | 引擎实例引用 |
| rerender | () => void | 强制重新渲染组件 |
示例
import type { KnotxInstance } from '@knotx/react'
import { Knotx } from '@knotx/react'
import React, { useEffect, useRef } from 'react'
function App() {
const knotxRef = useRef<KnotxInstance>(null)
useEffect(() => {
if (knotxRef.current) {
const engine = knotxRef.current.engineRef.current
if (engine) {
// 使用引擎 API
console.log('当前节点数量:', engine.nodesManager.dataMap$.value.size)
}
}
}, [])
return (
<Knotx
ref={knotxRef}
initialNodes={[]}
initialEdges={[]}
plugins={[]}
/>
)
}ReactEngine 类型
扩展自 @knotx/core 的 Engine 类型,专门用于 React 环境。
主要方法
| 方法 | 描述 |
|------|------|
| dispatchNodeOperation(operation: NodeOperation) | 分发节点操作 |
| dispatchEdgeOperation(operation: EdgeOperation) | 分发边操作 |
| getLayerComponents(layer: number) | 获取层级组件 |
| destroy() | 销毁引擎实例 |
插件配置
插件配置类型
interface PluginConfig {
[pluginName: string]: any
}示例
import { Canvas } from '@knotx/plugins-canvas'
import { Drag } from '@knotx/plugins-drag'
import { Knotx } from '@knotx/react'
import React from 'react'
function App() {
const pluginConfig = {
canvas: {
minScale: 0.1,
maxScale: 2.0,
wheel: {
step: 0.1,
wheelDisabled: false
}
},
drag: {
allowDrag: true,
dragThreshold: 5
}
}
return (
<Knotx
initialNodes={[]}
initialEdges={[]}
plugins={[Canvas, Drag]}
pluginConfig={pluginConfig}
/>
)
}🧩 插件系统
常用插件
以下是一些常用的插件:
@knotx/plugins-canvas- 画布功能(缩放、平移)@knotx/plugins-drag- 拖拽功能@knotx/plugins-selection- 选择功能@knotx/plugins-minimap- 小地图@knotx/plugins-background- 背景@knotx/plugins-bounding- 边界调整@knotx/plugins-connection-line- 连接线@knotx/plugins-group- 分组@knotx/plugins-history- 历史记录
创建自定义插件
import type { EdgeProps, Node } from '@knotx/core'
import { BasePlugin } from '@knotx/core'
import { edgeType, nodeType } from '@knotx/decorators'
class MyPlugin extends BasePlugin<'myPlugin'> {
name = 'myPlugin' as const
@nodeType('myNode')
renderMyNode({ node }: { node: Node }) {
return (
<div style={{ padding: '10px', border: '1px solid #ccc' }}>
自定义节点:
{' '}
{node.id}
</div>
)
}
@edgeType('myEdge')
renderMyEdge(props: EdgeProps) {
return (
<path
d={props.path}
stroke="#ff0000"
strokeWidth={2}
fill="none"
/>
)
}
}
// 使用插件
function App() {
return (
<Knotx
initialNodes={[
{
id: '1',
type: 'myNode',
position: { x: 100, y: 100 },
measured: { width: 120, height: 60 },
data: {}
}
]}
initialEdges={[]}
plugins={[MyPlugin]}
/>
)
}📂 文件目录结构
packages/react/
├── src/
│ ├── components/ # React 组件
│ │ ├── content.tsx # 层级内容组件
│ │ └── index.ts # 组件导出
│ ├── hooks/ # React Hooks
│ │ ├── container.ts # 容器引用 Hook
│ │ ├── data.ts # 数据更新 Hook
│ │ ├── engine.ts # 引擎管理 Hook
│ │ ├── plugin.ts # 插件管理 Hook
│ │ └── index.ts # Hooks 导出
│ ├── definition.ts # 类型定义
│ ├── engine.ts # React 引擎初始化
│ ├── index.ts # 主入口文件
│ ├── knotx.tsx # 主要 Knotx 组件
│ └── layer.tsx # 层级渲染组件
├── dist/ # 构建输出目录
├── CHANGELOG.md # 变更日志
├── package.json # 包配置
├── README.md # 中文文档
├── README.en.md # 英文文档
└── tsconfig.json # TypeScript 配置核心文件说明
| 文件 | 描述 |
|------|------|
| knotx.tsx | 主要的 Knotx React 组件实现 |
| definition.ts | TypeScript 类型定义和接口 |
| engine.ts | React 引擎初始化和运行时配置 |
| layer.tsx | 层级渲染逻辑 |
| components/content.tsx | 层级内容渲染组件 |
| hooks/container.ts | 容器大小监听 Hook |
| hooks/data.ts | 数据差异更新 Hook |
| hooks/engine.ts | 引擎生命周期管理 Hook |
| hooks/plugin.ts | 插件合并和配置更新 Hook |
🔗 类型导出
该包重新导出了所有来自 @knotx/core 的类型,方便使用:
import type {
// 其他
Direction,
Edge,
EdgeOperation,
EdgeProps,
Engine,
EngineOptions,
IData,
IPlugin,
KnotxInstance,
KnotxProps,
Layer,
// 核心类型
Node,
// 操作类型
NodeOperation,
NodePosition,
// 属性类型
NodeProps,
Plugin,
// 配置类型
PluginConfigs,
// 位置类型
Position,
// React 特定类型
ReactEngine
} from '@knotx/react'🎨 最佳实践
1. 性能优化
import { Knotx } from '@knotx/react'
import React, { memo, useMemo } from 'react'
const MyKnotx = memo(({ data }: { data: any }) => {
// 使用 useMemo 缓存节点和边数据
const nodes = useMemo(() => {
return data.nodes || []
}, [data.nodes])
const edges = useMemo(() => {
return data.edges || []
}, [data.edges])
return (
<Knotx
nodes={nodes}
edges={edges}
plugins={[]}
/>
)
})2. 错误处理
import { Knotx } from '@knotx/react'
import React, { ErrorBoundary } from 'react'
class KnotxErrorBoundary extends React.Component {
constructor(props: any) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error: Error) {
// 处理错误:记录错误并返回错误状态
console.error('React Error Boundary:', error)
return { hasError: true }
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
// 处理错误:记录错误信息
console.error('Knotx Error:', error, errorInfo)
// 可以在这里添加错误上报逻辑
// 例如:this.reportError(error, errorInfo)
}
render() {
if (this.state.hasError) {
return <div>Something went wrong with Knotx.</div>
}
return this.props.children
}
}
function App() {
return (
<KnotxErrorBoundary>
<Knotx
initialNodes={[]}
initialEdges={[]}
plugins={[]}
/>
</KnotxErrorBoundary>
)
}3. 主题定制
import { BasePlugin } from '@knotx/core'
import { layer } from '@knotx/decorators'
import { Knotx } from '@knotx/react'
import React from 'react'
class ThemePlugin extends BasePlugin<'theme'> {
name = 'theme' as const
@layer(100)
renderTheme() {
return (
<style>
{`
.knotx-container {
--node-bg: #ffffff;
--node-border: #e1e4e8;
--edge-stroke: #586069;
--selection-bg: rgba(0, 123, 255, 0.1);
--selection-border: #007bff;
}
.dark-theme .knotx-container {
--node-bg: #2d3748;
--node-border: #4a5568;
--edge-stroke: #a0aec0;
--selection-bg: rgba(66, 153, 225, 0.1);
--selection-border: #4299e1;
}
`}
</style>
)
}
}
function App() {
return (
<div className="dark-theme">
<Knotx
initialNodes={[]}
initialEdges={[]}
plugins={[ThemePlugin]}
/>
</div>
)
}📝 常见问题
Q: 如何处理大量节点的性能问题?
A: 可以使用虚拟化技术,只渲染可见区域的节点:
import { BasePlugin } from '@knotx/core'
import { Knotx } from '@knotx/react'
import React from 'react'
// 实现虚拟化插件
class VirtualizationPlugin extends BasePlugin<'virtualization'> {
name = 'virtualization' as const
// 实现虚拟化逻辑
// ...
}Q: 如何实现节点的自定义连接点?
A: 使用 @knotx/plugins-connection-line 插件:
import { ConnectionLine } from '@knotx/plugins-connection-line'
// 在节点中添加连接点
function CustomNode({ node }) {
return (
<div>
<div
className="connection-handle"
data-handle-position="top"
/>
节点内容
</div>
)
}Q: 如何保存和恢复画布状态?
A: 通过引擎的数据管理器:
function saveState(engine: ReactEngine) {
const state = {
nodes: Array.from(engine.nodesManager.dataMap$.value.values()),
edges: Array.from(engine.edgesManager.dataMap$.value.values()),
viewport: engine.container
}
localStorage.setItem('knotx-state', JSON.stringify(state))
}
function loadState(engine: ReactEngine) {
const state = JSON.parse(localStorage.getItem('knotx-state') || '{}')
// 恢复状态逻辑
}📄 许可证
MIT License - 详见 LICENSE 文件
🤝 贡献
欢迎提交 Pull Request 和 Issue!
