Context
Since menu
type plugins are siblings in the DOMTree, it is not possible for menu
type plugins to communicate with each other. Next to the menu
type plugin, it is not possible to communicate from an editor
type plugin to another editor
type plugin or a menu
type plugin.
Example use-case
An end-user selects 2 IED's in 1 plug-in. In order to make a connection between IED's, the user want to open the selection in an another plug.
Also, since addons are not extending from a HTMLElement, addons cannot subscribe to events themselves.
HTML events are only going upwards in the DOMTree, not downwards and/or to siblings.
Decision
If we want plug-ins for OpenSCD to be asynchronous accessible to each other, OpenSCD-Core should make use of a central event-bus.
Instead of dispatching an event on the plug-in itself, the plug-in can dispatch an event on the central event-bus. This will take care of the current problems we're facing:
Addons
Addons can listen to CustomEvents being caught by the OpenSCD HTML Element. This will solve the communication problem from a plug-in or addon to a different addon.
Plug-ins communicating between themselves are not solved by this solution.
```ts
export class EventBus implements EventTarget {
private _eventListeners: Map<string, EventListenerOrEventListenerObject[]>;
constructor() {
this._eventListeners = new Map<string, EventListenerOrEventListenerObject[]>();
}
addEventListener(type: string, callback: EventListenerOrEventListenerObject): void {
if (!this._eventListeners.has(type)) {
this._eventListeners.set(type, []);
}
this._eventListeners.get(type)!.push(callback);
}
dispatchEvent(event: Event): boolean {
if (this._eventListeners.has(event.type)) {
this._eventListeners
.get(event.type)
!.forEach((cb) =>
typeof cb === 'function' ? cb(event) : cb.handleEvent(event)
);
return true;
}
return false;
}
removeEventListener(type: string, callback: EventListenerOrEventListenerObject): void {
if (this._eventListeners.has(type)) {
this._eventListeners.set(type, this._eventListeners.get(type)!.filter((cb) => cb !== callback));
}
}
}
```
Alternatives
Instead of the central EventBus, we can let plugins dispatch events on the plugin itself. If a plugin needs an event from another plugin, It can listen to an event being dispatched to Open-SCD Core
.
Example
export default class MyPlugin extends LitElement { fistUpdated() { this.getRootNode().addEventListener('oscd-event', () => { ... }); } }
In the code example above, the plugin listens to an OscdEvent dispatched by (for example) another plugin and is listening to this event via the common parent; Open-SCD Core.
- Body
- OpenSCD
- Menu Plugin 1
- Menu Plugin 2
- Active Editor Plugin
- OpenSCD
Due to WebApi restrictions, Events that are emitted by one of the plugins are only bubbling up directly (https://www.freecodecamp.org/news/event-bubbling-in-javascript/)
Pro's:
No new Code needs to be introduced
Con's:
This is only working for HTMLElements (Plugins).
Consequences
Implementing a central event-bus means that current OSCD plugins need some refactoring to dispatch events to the event-bus instead of dispatching events on itself.