Skip to content

Commit

Permalink
feat: 开发 react-node-registry 包用于用户自定义 react 节点
Browse files Browse the repository at this point in the history
 - 通过监听 properties 变化的事件,触发节点的更新
 - 通过 Portal 的方式,可以获取到宿主系统的数据(比如状态),保持组件渲染和系统同步
  • Loading branch information
boyongjiong committed Jun 7, 2024
1 parent 74a0882 commit 9afcda9
Show file tree
Hide file tree
Showing 20 changed files with 788 additions and 19 deletions.
20 changes: 20 additions & 0 deletions examples/feature-examples/.umirc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,26 @@ export default defineConfig({
},
],
},
{
path: '/react',
name: 'react node registry',
routes: [
{
path: 'react',
redirect: 'react/custom',
},
{
path: '/react/custom',
name: '自定义 React 节点',
component: './react',
},
{
path: '/react/portal',
name: 'React Portal节点',
component: './react/Portal',
},
],
},
{
name: 'official extensions',
path: '/extension',
Expand Down
1 change: 1 addition & 0 deletions examples/feature-examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@logicflow/core": "workspace:latest",
"@logicflow/engine": "workspace:latest",
"@logicflow/extension": "workspace:latest",
"@logicflow/react-node-registry": "workspace:latest",
"antd": "^5.4.0",
"umi": "^4.2.1"
},
Expand Down
Binary file added examples/feature-examples/src/assets/didi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
123 changes: 123 additions & 0 deletions examples/feature-examples/src/pages/react/Portal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import React, { FC, useContext } from 'react'
import LogicFlow from '@logicflow/core'
import {
register,
ReactNodeProps,
Portal,
} from '@logicflow/react-node-registry'
import { Button, Card } from 'antd'

import styles from './index.less'

const LFReactPortalProvider = Portal.getProvider() // 注意,一个 LogicFlow 实例只能生命一个 portal provider
const ThemeContext = React.createContext('light')

const NodeComponent: FC<ReactNodeProps> = ({ node }) => {
const theme = useContext(ThemeContext)
const data = node.getData()
if (!data.properties) data.properties = {}

return (
<div className={`react-algo-node ${theme === 'light' ? 'light' : 'dark'}`}>
<img src={require('@/assets/didi.png')} alt="滴滴出行" />
<span>{data.properties.name as string}</span>
</div>
)
}

export default class Example extends React.Component {
private container!: HTMLDivElement
private count = 0
private timer?: ReturnType<typeof setTimeout>

state = {
theme: 'light',
}

componentDidMount() {
const lf = new LogicFlow({
container: this.container,
// width: 800,
// height: 600,
})

lf.render({
nodes: [
{
type: 'rect',
x: 400,
y: 100,
text: '???',
properties: {
name: '矩形',
},
},
],
})

register(
{
type: 'custom-react-node',
component: NodeComponent,
},
lf,
)

const node = lf.addNode({
id: 'react-node-1',
type: 'custom-react-node',
x: 80,
y: 80,
properties: {
name: '今日出行',
width: 120,
height: 28,
},
})
console.log('node --->>>', node)

const update = () => {
// lf.setProperties('react-node-1', { name: `逻辑回归 ${(this.count += 1)}` })
node.setProperty('name', `今日出行 ${(this.count += 1)}`)
this.timer = setTimeout(update, 1000)
}

update()
}

componentWillUnmount() {
console.log('0-0-0 componentWillUnmount 0-0-0')
if (this.timer) {
clearTimeout(this.timer)
}
}

changeTheme = () => {
this.setState({
theme: this.state.theme === 'light' ? 'dark' : 'light',
})
}

refContainer = (container: HTMLDivElement) => {
this.container = container
}

render() {
return (
<Card title="React 自定义节点">
<ThemeContext.Provider value={this.state.theme}>
<LFReactPortalProvider />
</ThemeContext.Provider>

<Button onClick={this.changeTheme}>
{this.state.theme === 'light' ? '切换到暗色' : '切换到亮色'}
</Button>
<div
ref={this.refContainer}
id="graph"
className={styles.viewport}
></div>
</Card>
)
}
}
35 changes: 35 additions & 0 deletions examples/feature-examples/src/pages/react/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.viewport {
position: relative;
height: 80vh;
overflow: hidden;
}

:global {
.react-algo-node {
display: flex;
align-items: center;
width: 100%;
height: 100%;
border: 1px solid #e59b68;
border-radius: 14px;

img {
width: 24px;
height: 24px;
}

span {
margin-left: 4px;
color: #000000a6;
font-size: 12px;
}

&.dark {
background-color: #141414;

span {
color: #fff;
}
}
}
}
98 changes: 98 additions & 0 deletions examples/feature-examples/src/pages/react/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { FC } from 'react'
import LogicFlow from '@logicflow/core'
import { register, ReactNodeProps } from '@logicflow/react-node-registry'
import { Card } from 'antd'

import styles from './index.less'

const NodeComponent: FC<ReactNodeProps> = ({ node }) => {
const data = node.getData()
if (!data.properties) data.properties = {}

return (
<div className="react-algo-node">
<img src={require('@/assets/didi.png')} alt="滴滴出行" />
<span>{data.properties.name as string}</span>
</div>
)
}

export default class Example extends React.Component {
private container!: HTMLDivElement
private count = 0
private timer?: ReturnType<typeof setTimeout>

componentDidMount() {
const lf = new LogicFlow({
container: this.container,
// width: 800,
// height: 600,
})

lf.render({
nodes: [
{
type: 'rect',
x: 400,
y: 100,
text: '???',
properties: {
name: '矩形',
},
},
],
})

register(
{
type: 'custom-react-node',
component: NodeComponent,
},
lf,
)

const node = lf.addNode({
id: 'react-node-1',
type: 'custom-react-node',
x: 80,
y: 80,
properties: {
name: '今日出行',
width: 120,
height: 28,
},
})
console.log('node --->>>', node)

const update = () => {
// lf.setProperties('react-node-1', { name: `逻辑回归 ${(this.count += 1)}` })
node.setProperty('name', `今日出行 ${(this.count += 1)}`)
this.timer = setTimeout(update, 1000)
}

update()
}

componentWillUnmount() {
console.log('0-0-0 componentWillUnmount 0-0-0')
if (this.timer) {
clearTimeout(this.timer)
}
}

refContainer = (container: HTMLDivElement) => {
this.container = container
}

render() {
return (
<Card title="React 自定义节点">
<div
ref={this.refContainer}
id="graph"
className={styles.viewport}
></div>
</Card>
)
}
}
5 changes: 5 additions & 0 deletions packages/core/src/constant/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export enum ModelType {
ELLIPSE_NODE = 'ellipse-node',
DIAMOND_NODE = 'diamond-node',
HTML_NODE = 'html-node',
CUSTOM_HTML_NODE = 'custom-html-node',
EDGE = 'edge',
LINE_EDGE = 'line-edge',
POLYLINE_EDGE = 'polyline-edge',
Expand Down Expand Up @@ -53,6 +54,10 @@ export enum EventType {
NODE_CONTEXTMENU = 'node:contextmenu',
NODE_ROTATE = 'node:rotate',

// 节点 properties 变化事件
NODE_PROPERTIES_CHANGE = 'node:properties-change',
NODE_PROPERTIES_DELETE = 'node:properties-delete',

// Edge events
EDGE_ADD = 'edge:add',
EDGE_DELETE = 'edge:delete',
Expand Down
Loading

0 comments on commit 9afcda9

Please sign in to comment.