From 0bacc113cdf8eec3dd01e4dc89937f2538f65e19 Mon Sep 17 00:00:00 2001 From: Hufe921 Date: Mon, 24 Jul 2023 21:17:36 +0800 Subject: [PATCH] feat: add event bus --- .vscode/settings.json | 2 + docs/.vitepress/config.ts | 3 +- docs/guide/eventbus.md | 101 +++++++++++++++++++++ docs/guide/listener.md | 4 +- src/editor/core/draw/Draw.ts | 32 ++++++- src/editor/core/draw/control/Control.ts | 47 +++++++--- src/editor/core/event/eventbus/EventBus.ts | 46 ++++++++++ src/editor/core/event/handlers/keydown.ts | 4 + src/editor/core/range/RangeManager.ts | 35 +++++-- src/editor/core/zone/Zone.ts | 4 + src/editor/index.ts | 8 +- src/editor/interface/EventBus.ts | 25 +++++ 12 files changed, 285 insertions(+), 26 deletions(-) create mode 100644 docs/guide/eventbus.md create mode 100644 src/editor/core/event/eventbus/EventBus.ts create mode 100644 src/editor/interface/EventBus.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 4ede554..b19fe45 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,7 +11,9 @@ "deletable", "dppx", "esbenp", + "eventbus", "inputarea", + "keyof", "linebreak", "noopener", "Parens", diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index ab8eabd..8598149 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -39,7 +39,8 @@ export default defineConfig({ { text: '监听', items: [ - { text: '事件监听', link: '/guide/listener' } + { text: '事件监听(listener)', link: '/guide/listener' }, + { text: '事件监听(eventBus)', link: '/guide/eventbus' } ] }, { diff --git a/docs/guide/eventbus.md b/docs/guide/eventbus.md new file mode 100644 index 0000000..c6456ed --- /dev/null +++ b/docs/guide/eventbus.md @@ -0,0 +1,101 @@ +# 事件监听(eventBus) + +## 使用方式 + +```javascript +import Editor from "@hufe921/canvas-editor" + +const instance = new Editor(container, data, options) + +// 注册 +instance.eventBus.on( + eventName: K, + callback: EventMap[K] +) + +// 移除 +instance.eventBus.off( + eventName: K, + callback: EventMap[K] +) +``` + +## rangeStyleChange +功能:选区样式发生改变 + +用法: +```javascript +instance.eventBus.on('rangeStyleChange', (payload: IRangeStyle) => void) +``` + +## visiblePageNoListChange +功能:可见页发生改变 + +用法: +```javascript +instance.eventBus.on('visiblePageNoListChange', (payload: number[]) => void) +``` + +## intersectionPageNoChange +功能:当前页发生改变 + +用法: +```javascript +instance.eventBus.on('intersectionPageNoChange', (payload: number) => void) +``` + +## pageSizeChange +功能:当前页数发生改变 + +用法: +```javascript +instance.eventBus.on('pageSizeChange', (payload: number) => void) +``` + +## pageScaleChange +功能:当前页面缩放比例发生改变 + +用法: +```javascript +instance.eventBus.on('pageScaleChange', (payload: number) => void) +``` + +## contentChange +功能:当前内容发生改变 + +用法: +```javascript +instance.eventBus.on('contentChange', () => void) +``` + +## controlChange +功能:当前光标所在控件发生改变 + +用法: +```javascript +instance.eventBus.on('controlChange', (payload: IControl | null) => void) +``` + +## pageModeChange +功能:页面模式发生改变 + +用法: +```javascript +instance.eventBus.on('pageModeChange', (payload: PageMode) => void) +``` + +## saved +功能:文档执行保存 + +用法: +```javascript +instance.eventBus.on('saved', (payload: IEditorResult) => void) +``` + +## zoneChange +功能:区域发生改变 + +用法: +```javascript +instance.eventBus.on('zoneChange', (payload: EditorZone) => void) +``` \ No newline at end of file diff --git a/docs/guide/listener.md b/docs/guide/listener.md index 3757355..c679fcd 100644 --- a/docs/guide/listener.md +++ b/docs/guide/listener.md @@ -1,4 +1,6 @@ -# 事件监听 +# 事件监听(listener) + +> listener只能响应一个方法,推荐使用eventBus进行事件监听 ## 使用方式 diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index aa47b8b..d97da99 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -79,6 +79,8 @@ import { INLINE_ELEMENT_TYPE } from '../../dataset/constant/Element' import { ListParticle } from './particle/ListParticle' import { Placeholder } from './frame/Placeholder' import { WORD_LIKE_REG } from '../../dataset/constant/Regular' +import { EventBus } from '../event/eventbus/EventBus' +import { EventBusMap } from '../../interface/EventBus' export class Draw { private container: HTMLDivElement @@ -95,6 +97,7 @@ export class Draw { private elementList: IElement[] private footerElementList: IElement[] private listener: Listener + private eventBus: EventBus private i18n: I18n private canvasEvent: CanvasEvent @@ -146,7 +149,8 @@ export class Draw { rootContainer: HTMLElement, options: DeepRequired, data: IEditorData, - listener: Listener + listener: Listener, + eventBus: EventBus ) { this.container = this._wrapContainer(rootContainer) this.pageList = [] @@ -159,6 +163,7 @@ export class Draw { this.elementList = data.main this.footerElementList = data.footer || [] this.listener = listener + this.eventBus = eventBus this._formatContainer() this.pageContainer = this._createPageContainer() @@ -336,6 +341,9 @@ export class Draw { if (this.listener.visiblePageNoListChange) { this.listener.visiblePageNoListChange(this.visiblePageNoList) } + if (this.eventBus.isSubscribe('visiblePageNoListChange')) { + this.eventBus.emit('visiblePageNoListChange', this.visiblePageNoList) + } } public getIntersectionPageNo(): number { @@ -347,6 +355,9 @@ export class Draw { if (this.listener.intersectionPageNoChange) { this.listener.intersectionPageNoChange(this.intersectionPageNo) } + if (this.eventBus.isSubscribe('intersectionPageNoChange')) { + this.eventBus.emit('intersectionPageNoChange', this.intersectionPageNo) + } } public getPageNo(): number { @@ -569,6 +580,10 @@ export class Draw { return this.listener } + public getEventBus(): EventBus { + return this.eventBus + } + public getCursor(): Cursor { return this.cursor } @@ -709,6 +724,9 @@ export class Draw { if (this.listener.pageModeChange) { this.listener.pageModeChange(payload) } + if (this.eventBus.isSubscribe('pageModeChange')) { + this.eventBus.emit('pageModeChange', payload) + } }) } @@ -1801,9 +1819,17 @@ export class Draw { if (this.listener.pageSizeChange) { this.listener.pageSizeChange(this.pageRowList.length) } + if (this.eventBus.isSubscribe('pageSizeChange')) { + this.eventBus.emit('pageSizeChange', this.pageRowList.length) + } // 文档内容改变 - if (this.listener.contentChange && isSubmitHistory) { - this.listener.contentChange() + if (isSubmitHistory) { + if (this.listener.contentChange) { + this.listener.contentChange() + } + if (this.eventBus.isSubscribe('contentChange')) { + this.eventBus.emit('contentChange') + } } }) } diff --git a/src/editor/core/draw/control/Control.ts b/src/editor/core/draw/control/Control.ts index 788eb39..f959449 100644 --- a/src/editor/core/draw/control/Control.ts +++ b/src/editor/core/draw/control/Control.ts @@ -7,13 +7,15 @@ import { IControlOption } from '../../../interface/Control' import { IElement, IElementPosition } from '../../../interface/Element' +import { EventBusMap } from '../../../interface/EventBus' import { IRange } from '../../../interface/Range' -import { deepClone, splitText } from '../../../utils' +import { deepClone, nextTick, splitText } from '../../../utils' import { formatElementContext, pickElementAttr, zipElementList } from '../../../utils/element' +import { EventBus } from '../../event/eventbus/EventBus' import { Listener } from '../../listener/Listener' import { RangeManager } from '../../range/RangeManager' import { Draw } from '../Draw' @@ -29,6 +31,7 @@ export class Control { private draw: Draw private range: RangeManager private listener: Listener + private eventBus: EventBus private options: IControlOption private activeControl: IControlInstance | null @@ -36,6 +39,8 @@ export class Control { this.draw = draw this.range = draw.getRange() this.listener = draw.getListener() + this.eventBus = draw.getEventBus() + this.options = draw.getOptions().control this.activeControl = null } @@ -132,16 +137,23 @@ export class Control { this.activeControl = new CheckboxControl(element, this) } // 激活控件回调 - setTimeout(() => { - if (this.listener.controlChange) { - let payload: IControl - const value = this.activeControl?.getValue() - if (value && value.length) { - payload = zipElementList(value)[0].control! - } else { - payload = pickElementAttr(deepClone(element)).control! - } - this.listener.controlChange(payload) + nextTick(() => { + const controlChangeListener = this.listener.controlChange + const isSubscribeControlChange = + this.eventBus.isSubscribe('controlChange') + if (!controlChangeListener && !isSubscribeControlChange) return + let payload: IControl + const value = this.activeControl?.getValue() + if (value && value.length) { + payload = zipElementList(value)[0].control! + } else { + payload = pickElementAttr(deepClone(element)).control! + } + if (controlChangeListener) { + controlChangeListener(payload) + } + if (isSubscribeControlChange) { + this.eventBus.emit('controlChange', payload) } }) } @@ -153,9 +165,16 @@ export class Control { } this.activeControl = null // 销毁控件回调 - setTimeout(() => { - if (this.listener.controlChange) { - this.listener.controlChange(null) + nextTick(() => { + const controlChangeListener = this.listener.controlChange + const isSubscribeControlChange = + this.eventBus.isSubscribe('controlChange') + if (!controlChangeListener && !isSubscribeControlChange) return + if (controlChangeListener) { + controlChangeListener(null) + } + if (isSubscribeControlChange) { + this.eventBus.emit('controlChange', null) } }) } diff --git a/src/editor/core/event/eventbus/EventBus.ts b/src/editor/core/event/eventbus/EventBus.ts new file mode 100644 index 0000000..8209462 --- /dev/null +++ b/src/editor/core/event/eventbus/EventBus.ts @@ -0,0 +1,46 @@ +export class EventBus { + private eventHub: Map> + + constructor() { + this.eventHub = new Map() + } + + public on( + eventName: K, + callback: EventMap[K] + ) { + if (!eventName || typeof callback !== 'function') return + const eventSet = this.eventHub.get(eventName) || new Set() + eventSet.add(callback) + this.eventHub.set(eventName, eventSet) + } + + public emit( + eventName: K, + payload?: EventMap[K] extends (payload: infer P) => void ? P : never + ) { + if (!eventName) return + const callBackSet = this.eventHub.get(eventName) + if (!callBackSet) return + if (callBackSet.size === 1) { + const callBack = [...callBackSet] + return callBack[0](payload) + } + callBackSet.forEach(callBack => callBack(payload)) + } + + public off( + eventName: K, + callback: EventMap[K] + ) { + if (!eventName || typeof callback !== 'function') return + const callBackSet = this.eventHub.get(eventName) + if (!callBackSet) return + callBackSet.delete(callback) + } + + public isSubscribe(eventName: K): boolean { + const eventSet = this.eventHub.get(eventName) + return !!eventSet && eventSet.size > 0 + } +} diff --git a/src/editor/core/event/handlers/keydown.ts b/src/editor/core/event/handlers/keydown.ts index 4ad909a..b6460aa 100644 --- a/src/editor/core/event/handlers/keydown.ts +++ b/src/editor/core/event/handlers/keydown.ts @@ -308,6 +308,10 @@ export function keydown(evt: KeyboardEvent, host: CanvasEvent) { if (listener.saved) { listener.saved(draw.getValue()) } + const eventBus = draw.getEventBus() + if (eventBus.isSubscribe('saved')) { + eventBus.emit('saved', draw.getValue()) + } evt.preventDefault() } else if (evt.key === KeyMap.ESC) { // 退出格式刷 diff --git a/src/editor/core/range/RangeManager.ts b/src/editor/core/range/RangeManager.ts index 5efee14..0fc747c 100644 --- a/src/editor/core/range/RangeManager.ts +++ b/src/editor/core/range/RangeManager.ts @@ -4,9 +4,12 @@ import { TEXTLIKE_ELEMENT_TYPE } from '../../dataset/constant/Element' import { ControlComponent } from '../../dataset/enum/Control' import { IEditorOption } from '../../interface/Editor' import { IElement } from '../../interface/Element' +import { EventBusMap } from '../../interface/EventBus' +import { IRangeStyle } from '../../interface/Listener' import { IRange, RangeRowArray, RangeRowMap } from '../../interface/Range' import { getAnchorElement } from '../../utils/element' import { Draw } from '../draw/Draw' +import { EventBus } from '../event/eventbus/EventBus' import { HistoryManager } from '../history/HistoryManager' import { Listener } from '../listener/Listener' import { Position } from '../position/Position' @@ -16,6 +19,7 @@ export class RangeManager { private options: Required private range: IRange private listener: Listener + private eventBus: EventBus private position: Position private historyManager: HistoryManager @@ -23,6 +27,7 @@ export class RangeManager { this.draw = draw this.options = draw.getOptions() this.listener = draw.getListener() + this.eventBus = draw.getEventBus() this.position = draw.getPosition() this.historyManager = draw.getHistoryManager() this.range = { @@ -241,7 +246,10 @@ export class RangeManager { } public setRangeStyle() { - if (!this.listener.rangeStyleChange) return + const rangeStyleChangeListener = this.listener.rangeStyleChange + const isSubscribeRangeStyleChange = + this.eventBus.isSubscribe('rangeStyleChange') + if (!rangeStyleChangeListener && !isSubscribeRangeStyleChange) return // 结束光标位置 const { startIndex, endIndex, isCrossRowCol } = this.range if (!~startIndex && !~endIndex) return @@ -281,7 +289,7 @@ export class RangeManager { const painter = !!this.draw.getPainterStyle() const undo = this.historyManager.isCanUndo() const redo = this.historyManager.isCanRedo() - this.listener.rangeStyleChange({ + const rangeStyle: IRangeStyle = { type, undo, redo, @@ -300,18 +308,27 @@ export class RangeManager { level, listType, listStyle - }) + } + if (rangeStyleChangeListener) { + rangeStyleChangeListener(rangeStyle) + } + if (isSubscribeRangeStyleChange) { + this.eventBus.emit('rangeStyleChange', rangeStyle) + } } public recoveryRangeStyle() { - if (!this.listener.rangeStyleChange) return + const rangeStyleChangeListener = this.listener.rangeStyleChange + const isSubscribeRangeStyleChange = + this.eventBus.isSubscribe('rangeStyleChange') + if (!rangeStyleChangeListener && !isSubscribeRangeStyleChange) return const font = this.options.defaultFont const size = this.options.defaultSize const rowMargin = this.options.defaultRowMargin const painter = !!this.draw.getPainterStyle() const undo = this.historyManager.isCanUndo() const redo = this.historyManager.isCanRedo() - this.listener.rangeStyleChange({ + const rangeStyle: IRangeStyle = { type: null, undo, redo, @@ -330,7 +347,13 @@ export class RangeManager { level: null, listType: null, listStyle: null - }) + } + if (rangeStyleChangeListener) { + rangeStyleChangeListener(rangeStyle) + } + if (isSubscribeRangeStyleChange) { + this.eventBus.emit('rangeStyleChange', rangeStyle) + } } public shrinkBoundary() { diff --git a/src/editor/core/zone/Zone.ts b/src/editor/core/zone/Zone.ts index 0cea548..ad8cc05 100644 --- a/src/editor/core/zone/Zone.ts +++ b/src/editor/core/zone/Zone.ts @@ -59,6 +59,10 @@ export class Zone { if (listener.zoneChange) { listener.zoneChange(payload) } + const eventBus = this.draw.getEventBus() + if (eventBus.isSubscribe('zoneChange')) { + eventBus.emit('zoneChange', payload) + } }) } diff --git a/src/editor/index.ts b/src/editor/index.ts index afc2629..c05e6ce 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -57,10 +57,13 @@ import { IPlaceholder } from './interface/Placeholder' import { defaultPlaceholderOption } from './dataset/constant/Placeholder' import { Plugin } from './core/plugin/Plugin' import { UsePlugin } from './interface/Plugin' +import { EventBus } from './core/event/eventbus/EventBus' +import { EventBusMap } from './interface/EventBus' export default class Editor { public command: Command public listener: Listener + public eventBus: EventBus public register: Register public destroy: () => void public use: UsePlugin @@ -179,6 +182,8 @@ export default class Editor { }) // 监听 this.listener = new Listener() + // 事件 + this.eventBus = new EventBus() // 启动 const draw = new Draw( container, @@ -188,7 +193,8 @@ export default class Editor { main: mainElementList, footer: footerElementList }, - this.listener + this.listener, + this.eventBus ) // 命令 this.command = new Command(new CommandAdapt(draw)) diff --git a/src/editor/interface/EventBus.ts b/src/editor/interface/EventBus.ts new file mode 100644 index 0000000..0c047cf --- /dev/null +++ b/src/editor/interface/EventBus.ts @@ -0,0 +1,25 @@ +import { + IContentChange, + IControlChange, + IIntersectionPageNoChange, + IPageModeChange, + IPageScaleChange, + IPageSizeChange, + IRangeStyleChange, + ISaved, + IVisiblePageNoListChange, + IZoneChange +} from './Listener' + +export interface EventBusMap { + rangeStyleChange: IRangeStyleChange + visiblePageNoListChange: IVisiblePageNoListChange + intersectionPageNoChange: IIntersectionPageNoChange + pageSizeChange: IPageSizeChange + pageScaleChange: IPageScaleChange + saved: ISaved + contentChange: IContentChange + controlChange: IControlChange + pageModeChange: IPageModeChange + zoneChange: IZoneChange +}