Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(EventEmitter): 新增事件链式注册,统一清除功能 #1968

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions examples/feature-examples/.umirc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,5 +212,16 @@ export default defineConfig({
},
],
},
{
name: 'event',
path: '/event',
routes: [
{
path: '/event/batch-use',
name: '批量使用示例',
component: './event/batch-use',
},
],
},
],
})
5 changes: 5 additions & 0 deletions examples/feature-examples/src/pages/event/batch-use.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.viewport {
position: relative;
height: 80vh;
overflow: hidden;
}
96 changes: 96 additions & 0 deletions examples/feature-examples/src/pages/event/batch-use.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { FC, useEffect, useRef, useState } from 'react'
import { Card, Button, Divider, message } from 'antd'
import '@logicflow/core/es/index.css'
import LogicFlow from '@logicflow/core'
import styles from './batch-use.less'

const Example: FC = () => {
const [haRegist, setHaRegist] = useState(false)
const refContainer = useRef<HTMLDivElement>(null)
const lf = useRef<LogicFlow>()
const [messageApi, contextHolder] = message.useMessage()

useEffect(() => {
const container = refContainer.current
if (!container) {
return
}

const instance = new LogicFlow({
container,
})

instance.render({
nodes: [
{
type: 'rect',
x: 100,
y: 100,
text: '节点1',
properties: {
name: '矩形',
},
},
{
type: 'rect',
x: 300,
y: 100,
text: '节点2',
properties: {
name: '矩形',
},
},
],
})

lf.current = instance
return () => {
instance.destroy()
}
}, [])

const off = useRef(() => {})

const registerEvent = () => {
if (!lf.current) {
return
}
off.current = lf.current.graphModel.eventCenter
.on('node:click', () => {
messageApi.info('node click')
})
.on('node:mouseenter', () => {
messageApi.success('node mouseenter')
})
.on('node:mouseleave', () => {
messageApi.warning('node mouseleave')
})
}

const offEvent = () => {
off.current()
}

const toggleEvent = () => {
setHaRegist(!haRegist)
if (haRegist) {
offEvent()
} else {
registerEvent()
}
}
return (
<Card title="批量注册事件">
{contextHolder}
<div>
<Button onClick={toggleEvent}>
{haRegist ? '批量清除' : '批量注册'}
</Button>
</div>
<Divider />
<div ref={refContainer} className={styles.viewport}></div>
</Card>
)
}

export default Example
21 changes: 11 additions & 10 deletions packages/core/src/LogicFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ import { Dnd, snapline } from './view/behavior'
import Tool from './tool'
import History from './history'
import Keyboard, { initDefaultShortcut } from './keyboard'
import { EventCallback, CallbackArgs, EventArgs } from './event/eventEmitter'
import {
OnEvent,
OnceEvent,
EventCallback,
EventArgs,
CallbackArgs,
} from './event/eventEmitter'
import { ElementType, EventType, SegmentDirection } from './constant'

import Extension = LogicFlow.Extension
Expand Down Expand Up @@ -1251,12 +1257,9 @@ export class LogicFlow {
* lf.on('node:click,node:contextmenu', (data) => {
* });
*/
on<T extends keyof EventArgs>(evt: T, callback: EventCallback<T>): void
on<T extends string>(evt: T, callback: EventCallback<T>): void
on(evt: string, callback: EventCallback) {
this.graphModel.eventCenter.on(evt, callback)
on: OnEvent = (evt: string, callback: EventCallback, once?: boolean) => {
return this.graphModel.eventCenter.on(evt, callback, once)
}

/**
* 撤销监听事件
*/
Expand All @@ -1269,10 +1272,8 @@ export class LogicFlow {
/**
* 监听事件,只监听一次
*/
once<T extends keyof EventArgs>(evt: T, callback: EventCallback<T>): void
once<T extends string>(evt: T, callback: EventCallback<T>): void
once(evt: string, callback: EventCallback) {
this.graphModel.eventCenter.once(evt, callback)
once: OnceEvent = (evt: string, callback: EventCallback) => {
return this.graphModel.eventCenter.once(evt, callback)
}

/**
Expand Down
96 changes: 84 additions & 12 deletions packages/core/src/event/eventEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,30 @@ export type EventCallback<T extends string = string> = (

const WILDCARD = '*'

export interface OnEvent {
<T extends keyof EventArgs>(
evt: T,
callback: EventCallback<T>,
once?: boolean,
): ClearCallback
<T extends string>(
evt: T,
callback: EventCallback<T>,
once?: boolean,
): ClearCallback
}

export interface OnceEvent {
<T extends keyof EventArgs>(evt: T, callback: EventCallback<T>): ClearCallback
<T extends string>(evt: T, callback: EventCallback<T>): ClearCallback
}

interface ClearCallback {
(): void
on: OnEvent
once: OnceEvent
}

/* event-emitter */
export default class EventEmitter {
private _events: EventsType = {}
Expand All @@ -30,14 +54,21 @@ export default class EventEmitter {
* @param evt 事件名称
* @param callback 回调函数
* @param once 是否只监听一次
* @returns { ClearCallback } 返回支持链式调用的清除函数
* @example
* const bus = new EventEmitter();
* const off = bus
* .on("e1", () => {})
* .once("e2", () => {})
* .on("e3", () => {});
*
* off() // 清除这一次注册的 e1、e2、e3 事件
*/
on<T extends keyof EventArgs>(
evt: T,
callback: EventCallback<T>,
on: OnEvent = (
evt: string,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这块儿有点问题,evt 不能直接写成 string,上面说的类型提示失效好像是这块儿的问题,得再确认一下

callback: EventCallback,
once?: boolean,
): void
on<T extends string>(evt: T, callback: EventCallback<T>, once?: boolean): void
on(evt: string, callback: EventCallback, once?: boolean) {
): ClearCallback => {
evt?.split(',').forEach((evKey) => {
evKey = evKey.trim()
if (!this._events[evKey]) {
Expand All @@ -48,23 +79,39 @@ export default class EventEmitter {
once: !!once,
})
})

return createClearCallback(this, () => {
if (evt) {
this.off(evt, callback)
}
})
}

/**
* 监听一个事件一次
* @param evt 事件名称
* @param callback 回调函数
* @returns { ClearCallback } 返回支持链式调用的清除函数
* @example
* const bus = new EventEmitter();
* const off = bus
* .on("e4", () => {})
* .once("e5", () => {})
* .on("e6", () => {});
*
* off() // 清除这一次注册的 e4、e5、e6 事件
*/
once<T extends keyof EventArgs>(
evt: T,
callback: (args: EventArgs[T]) => void,
): void
once<T extends string>(evt: T, callback: EventCallback<T>): void
once(evt: string, callback: EventCallback) {
once: OnceEvent = (evt: string, callback: EventCallback): ClearCallback => {
evt?.split(',').forEach((evKey) => {
evKey = evKey.trim()
this.on(evKey, callback, true)
})

return createClearCallback(this, () => {
if (evt) {
this.off(evt, callback)
}
})
}

/**
Expand Down Expand Up @@ -149,4 +196,29 @@ export default class EventEmitter {
}
}

const createClearCallback = (
ctx: EventEmitter,
clear: () => void,
): ClearCallback => {
const preClear = clear as ClearCallback

preClear.on = (evt: string, callback: EventCallback, once?: boolean) => {
const clear = ctx.on(evt, callback, once)
return createClearCallback(ctx, () => {
preClear()
clear()
})
}

preClear.once = (evt: string, callback: EventCallback) => {
const clear = ctx.once(evt, callback)
return createClearCallback(ctx, () => {
preClear()
clear()
})
}

return preClear
}

export { EventEmitter, EventArgs }
42 changes: 29 additions & 13 deletions sites/docs/docs/api/detail/index.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -1182,9 +1182,14 @@ closeEdgeAnimation: (edgeId: string): void => {}
Event listener for the graph, see [event](../eventCenter.en.md).

```ts | pure
import { EventCallback } from './EventEmitter'
import { EventCallback, ClearCallback } from './EventEmitter'

export interface OnEvent {
<T extends keyof EventArgs>(evt: T,callback: EventCallback<T>,once?: boolean): ClearCallback
<T extends string>(evt: T, callback: EventCallback<T>, once?: boolean): ClearCallback
}

on: (evt: string, callback: EventCallback<T>): void => {}
on: OnEvent
```

Parameters:
Expand All @@ -1193,16 +1198,18 @@ Parameters:
|:---------|:-------|:---------|:--------|:------------------|
| evt | string | ✅ | - | Event name |
| callback | `EventCallback<T>` | ✅ | - | Callback function |
| once | boolean | | false | only once |

Example:

```ts | pure
lf.on("node:click", (args) => {
console.log("node:click", args.position);
});
lf.on("element:click", (args) => {
console.log("element:click", args.e.target);
});
const clear = lf
.on("node:click", (args) => {
console.log("node:click", args.position);
}, false)
.on("element:click", (args) => {
console.log("element:click", args.e.target);
});
```

### off
Expand Down Expand Up @@ -1238,9 +1245,14 @@ lf.off("element:click", () => {
Event listener that triggers only once.

```ts | pure
import { EventCallback } from './EventEmitter'
import { EventCallback, ClearCallback } from './EventEmitter'

export interface OnceEvent {
<T extends keyof EventArgs>(evt: T, callback: EventCallback<T>): ClearCallback
<T extends string>(evt: T, callback: EventCallback<T>): ClearCallback
}

once: (evt: string, callback: EventCallback<T>): void => {}
once: OnceEvent
```

Parameters:
Expand All @@ -1253,9 +1265,13 @@ Parameters:
Example:

```ts | pure
lf.once("node:click", () => {
console.log("node:click");
});
const clear = lf
.once("node:click", (args) => {
console.log("node:click", args.position);
})
.once("element:click", (args) => {
console.log("element:click", args.e.target);
});
```

### emit
Expand Down
Loading