Skip to content

Commit 537706f

Browse files
authored
feat: export EventEmitter class (#2467)
1 parent 9bd862d commit 537706f

File tree

6 files changed

+64
-17
lines changed

6 files changed

+64
-17
lines changed

src/components/Toaster/Toaster.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {EventEmitter} from '../../utils/EventEmitter/EventEmitter';
1+
import {EventEmitter} from '../../utils/EventEmitter';
22

33
import type {InternalToastProps, ToastProps} from './types';
44
import {getToastIndex} from './utilities/getToastIndex';
@@ -9,7 +9,8 @@ export class Toaster {
99
/** We were tried to notify about toaster changes, but no one were listened */
1010
private hasUndelivered = false;
1111
private toasts: InternalToastProps[] = [];
12-
private eventEmitter: EventEmitter<InternalToastProps[]> = new EventEmitter();
12+
private eventEmitter: EventEmitter<{'toasts-change': [InternalToastProps[]]}> =
13+
new EventEmitter();
1314

1415
destroy() {
1516
this.removeAll();
@@ -71,7 +72,7 @@ export class Toaster {
7172
}
7273

7374
subscribe(listener: (toasts: InternalToastProps[]) => void) {
74-
const unsubscribe = this.eventEmitter.subscribe(listener);
75+
const unsubscribe = this.eventEmitter.subscribe('toasts-change', listener);
7576

7677
if (this.hasUndelivered) {
7778
this.notify();
@@ -81,7 +82,7 @@ export class Toaster {
8182
}
8283

8384
private notify() {
84-
const isDelivered = this.eventEmitter.notify(this.toasts);
85+
const isDelivered = this.eventEmitter.notify('toasts-change', [this.toasts]);
8586

8687
this.hasUndelivered = !isDelivered;
8788
}

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export {Lang, configure, getConfig} from './utils/configure';
22
export type {StringWithSuggest} from './utils/types';
3+
export {EventEmitter} from './utils/EventEmitter';
4+
export type {EventListeners} from './utils/EventEmitter';
35
export * from './components';
46
export * from './hooks';
57
export * from './utils/dom';

src/utils/EventEmitter/EventEmitter.ts

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,52 @@
1-
type EventListener<T> = (data: T) => void;
2-
3-
export class EventEmitter<T> {
4-
private listeners: EventListener<T>[];
1+
import type {EventListeners} from './types';
2+
3+
/**
4+
* Class for emitting events
5+
* @example
6+
* import {EventEmitter} from '@gravity-ui/uikit';
7+
*
8+
* const emitter = new EventEmitter<{
9+
* foobar: [string, number]
10+
* }>();
11+
*
12+
* emitter.notify('foobar', ['foo', 0]); // returns "false", because there is no attached listeners yet
13+
*
14+
* const unsubscribe = emitter.subscribe('foobar', (a, b) => {
15+
* console.log(a, b);
16+
* });
17+
*
18+
* emitter.notify('foobar', ['foo', 1]); // returns "true", because listener is appeared
19+
*/
20+
export class EventEmitter<T extends Record<string, unknown[]>> {
21+
private listeners: EventListeners<T>;
522

623
constructor() {
7-
this.listeners = [];
24+
this.listeners = {};
825
}
926

1027
destroy() {
11-
this.listeners = [];
28+
this.listeners = {};
1229
}
1330

14-
subscribe(listener: EventListener<T>) {
31+
subscribe<Event extends keyof T>(event: Event, listener: (...args: T[Event]) => void) {
1532
if (typeof listener === 'function') {
16-
this.listeners.push(listener);
33+
this.listeners[event] = (this.listeners[event] || []).concat(listener);
1734
}
1835

1936
return () => {
20-
this.listeners = this.listeners.filter(
37+
this.listeners[event] = this.listeners[event]?.filter(
2138
(currentListener) => listener !== currentListener,
2239
);
2340
};
2441
}
2542

26-
notify(data: T) {
27-
if (this.listeners.length === 0) {
43+
notify<Event extends keyof T>(event: Event, data: T[Event]) {
44+
if (!this.listeners[event]?.length) {
2845
return false;
2946
}
3047

31-
for (const listener of this.listeners) {
32-
listener(data);
48+
for (const listener of this.listeners[event]) {
49+
listener(...data);
3350
}
3451

3552
return true;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import {EventEmitter} from '../EventEmitter';
2+
3+
test('should subscribe for events', (done) => {
4+
const emitter = new EventEmitter<{foo: [string, string, string]}>();
5+
6+
expect(emitter.notify('foo', ['foo', 'bar', '1qux'])).toBe(false);
7+
8+
const unsubscribe1 = emitter.subscribe('foo', (...data) => {
9+
expect(data).toEqual(['foo', 'bar', 'qux']);
10+
});
11+
12+
const unsubscribe2 = emitter.subscribe('foo', (...data) => {
13+
expect(data).toEqual(['foo', 'bar', 'qux']);
14+
15+
done();
16+
});
17+
18+
expect(emitter.notify('foo', ['foo', 'bar', 'qux'])).toBe(true);
19+
20+
unsubscribe1();
21+
unsubscribe2();
22+
});

src/utils/EventEmitter/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export {EventEmitter} from './EventEmitter';
2+
export type {EventListeners} from './types';

src/utils/EventEmitter/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export type EventListeners<T extends Record<string, unknown[]>> = {
2+
[Event in keyof T]?: ((...args: T[Event]) => void)[];
3+
};

0 commit comments

Comments
 (0)