editor.js/src/components/utils/events.ts
Peter Savchenko ff91466b14
feat(toolbar): toolbar refactored and ui improved (#1815)
* chore(block-tune-toggler): toggler moved to the left (draft)

* toolbox ui updated

* fixd caret jumpling, improved some styles

* toolbar moving by block-hover

- UI module triggers 'block-hovered' event
- Toolbar uses 'block-hovered' for appearing
- `currentBlock` setter added to the BlockManager
- (reactangle-selection): the throttling added to the mousemove and scroll handlers
- `getBlockIndex` method added to the Api
- (api-blocks): toolbar moving logic removed from `blocks.move()` and `blocks.swap()` methods. Instead, MoveUp and MoveDown tunes uses Toolbar API

* the dark-theme to the example-dev.html

* positioning improved

* fix(rectangle-selection): first click after RS does not clears selection state

* toolbox position fixed

* the toolbox module became a standalone class

- Toolbox became a standalone class from the editor module. It can be accessed only via the owner (the Toolbar module)
- (api.blocks) the insert() method now has the `replace` param. Also, it returns inserted Block API now.

* new(api.listeners): `on()` now returns the listener id. The new `offById()` method added

* fix bug with Tab pressing on hovered but not focused block

* mobile version improved

* upd example dev

* small updaets

* add nested-list

* linting

* (api.toolbar): `toggleBlockSettings` now fires toggling event with the same state

* EventDispatcher used instead of callbacks for the Toolbox

* UIApi added

* fix ci

* git submodules removed from the ci flow

* add paragraph submodule to the ci flow

* Update CHANGELOG.md

* Update package.json

* use ubuntu-latest for chrome ci
2021-11-24 21:14:24 +03:00

107 lines
2.9 KiB
TypeScript

import { isEmpty } from '../utils';
/**
* @class EventDispatcher
*
* Has two important methods:
* - {Function} on - appends subscriber to the event. If event doesn't exist - creates new one
* - {Function} emit - fires all subscribers with data
* - {Function off - unsubscribes callback
*
* @version 1.0.0
*
* @typedef {Events} Events
* @property {object} subscribers - all subscribers grouped by event name
*/
export default class EventsDispatcher<Events extends string = string> {
/**
* Object with events` names as key and array of callback functions as value
*
* @type {{}}
*/
private subscribers: {[name: string]: Array<(data?: object) => unknown>} = {};
/**
* Subscribe any event on callback
*
* @param {string} eventName - event name
* @param {Function} callback - subscriber
*/
public on(eventName: Events, callback: (data: object) => unknown): void {
if (!(eventName in this.subscribers)) {
this.subscribers[eventName] = [];
}
// group by events
this.subscribers[eventName].push(callback);
}
/**
* Subscribe any event on callback. Callback will be called once and be removed from subscribers array after call.
*
* @param {string} eventName - event name
* @param {Function} callback - subscriber
*/
public once(eventName: Events, callback: (data: object) => unknown): void {
if (!(eventName in this.subscribers)) {
this.subscribers[eventName] = [];
}
const wrappedCallback = (data: object): unknown => {
const result = callback(data);
const indexOfHandler = this.subscribers[eventName].indexOf(wrappedCallback);
if (indexOfHandler !== -1) {
this.subscribers[eventName].splice(indexOfHandler, 1);
}
return result;
};
// group by events
this.subscribers[eventName].push(wrappedCallback);
}
/**
* Emit callbacks with passed data
*
* @param {string} eventName - event name
* @param {object} data - subscribers get this data when they were fired
*/
public emit(eventName: Events, data?: object): void {
if (isEmpty(this.subscribers) || !this.subscribers[eventName]) {
return;
}
this.subscribers[eventName].reduce((previousData, currentHandler) => {
const newData = currentHandler(previousData);
return newData || previousData;
}, data);
}
/**
* Unsubscribe callback from event
*
* @param {string} eventName - event name
* @param {Function} callback - event handler
*/
public off(eventName: Events, callback: (data: object) => unknown): void {
for (let i = 0; i < this.subscribers[eventName].length; i++) {
if (this.subscribers[eventName][i] === callback) {
delete this.subscribers[eventName][i];
break;
}
}
}
/**
* Destroyer
* clears subscribers list
*/
public destroy(): void {
this.subscribers = null;
}
}