From bdb2a61529c1cf8d986c678139b77a4c3d400ea0 Mon Sep 17 00:00:00 2001 From: Valery Zinchenko Date: Thu, 30 Nov 2023 00:44:41 +0300 Subject: [PATCH 1/3] add local `EventEmitter` --- src/EventEmitter.ts | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/EventEmitter.ts diff --git a/src/EventEmitter.ts b/src/EventEmitter.ts new file mode 100644 index 0000000..3c49ea0 --- /dev/null +++ b/src/EventEmitter.ts @@ -0,0 +1,39 @@ +/* + +MIT License + +Copyright (c) 2023 Valery Zinchenko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +*/ + +type EventEmitterListener = (...args: never[]) => void + +class EventEmitter, EventName extends keyof Events = keyof Events> { + private callbacks: Partial>> = {} + + public on(event: Event, callback: Events[Event]) { + this.callbacks[event] ??= new Set + this.callbacks[event]?.add(callback as EventEmitterListener) + } + public off(event: Event, callback: Events[Event]) { + this.callbacks[event]?.delete(callback as EventEmitterListener) + } + public emit(event: Event, ...args: Parameters) { + const callbacks = this.callbacks[event] + if (callbacks == null) return + + for (const callback of callbacks) callback(...args) + } +} + +export default EventEmitter From 98a12b4f53b64833b16bffb0cb78bf937fcbb4db Mon Sep 17 00:00:00 2001 From: Valery Zinchenko Date: Thu, 30 Nov 2023 00:46:54 +0300 Subject: [PATCH 2/3] remove `eventemitter3` --- package-lock.json | 126 ---------------------------------------------- package.json | 1 - 2 files changed, 127 deletions(-) diff --git a/package-lock.json b/package-lock.json index 401d44b..dde2482 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "2.3.2", "license": "MIT", "dependencies": { - "eventemitter3": "^5.0.0", "type-fest": "^4.0.0" }, "devDependencies": { @@ -998,23 +997,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz", - "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/type-utils": { "version": "6.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz", @@ -1099,46 +1081,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/types": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz", - "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz", - "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/@typescript-eslint/utils": { "version": "6.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", @@ -1238,23 +1180,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz", - "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.9.1", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -2233,11 +2158,6 @@ "node": ">=0.10.0" } }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" - }, "node_modules/expect": { "version": "29.4.1", "resolved": "https://registry.npmjs.org/expect/-/expect-29.4.1.tgz", @@ -5552,16 +5472,6 @@ } } }, - "@typescript-eslint/scope-manager": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz", - "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1" - } - }, "@typescript-eslint/type-utils": { "version": "6.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz", @@ -5607,27 +5517,6 @@ } } }, - "@typescript-eslint/types": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz", - "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz", - "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - } - }, "@typescript-eslint/utils": { "version": "6.13.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", @@ -5686,16 +5575,6 @@ } } }, - "@typescript-eslint/visitor-keys": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz", - "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.9.1", - "eslint-visitor-keys": "^3.4.1" - } - }, "@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -6443,11 +6322,6 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" - }, "expect": { "version": "29.4.1", "resolved": "https://registry.npmjs.org/expect/-/expect-29.4.1.tgz", diff --git a/package.json b/package.json index c43921b..2012942 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ } }, "dependencies": { - "eventemitter3": "^5.0.0", "type-fest": "^4.0.0" }, "devDependencies": { From ca56d91d101ce4cbf362b83b0125cac44289ea94 Mon Sep 17 00:00:00 2001 From: Valery Zinchenko Date: Thu, 30 Nov 2023 00:47:52 +0300 Subject: [PATCH 3/3] Refactor with local `EventEmitter` --- src/ModalController.ts | 23 +++++++++++------------ src/ModalWindow.ts | 29 ++++++++++++++--------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/ModalController.ts b/src/ModalController.ts index 75e24b9..257d49d 100644 --- a/src/ModalController.ts +++ b/src/ModalController.ts @@ -16,16 +16,15 @@ copies or substantial portions of the Software. */ -import EventEmitter from "eventemitter3" - +import EventEmitter from "./EventEmitter" import { ModalWindow, ModalWindowAny } from "./ModalWindow" import { ExternalStore, MODAL_WINDOW_PARAMS_EXPLANATION, ModalComponent, ModalComponentProps, ModalNamedComponents, ModalParams, ModalSnapshot, ModalWindowParams } from "./types" interface ModalControllerEvents { - add: [ModalWindowAny] - remove: [ModalWindowAny] - update: [] + add(modalWindow: ModalWindowAny): void + remove(modalWindow: ModalWindowAny): void + update(): void } interface ModalControllerConfig { @@ -86,11 +85,11 @@ class ModalController = ModalContr * @example * await Modal.open(MyModal, { id: 1 }) * console.log("Modal was closed") - * + * * @example * const modal = Modal.open(MyModal, { id: 1 }) * modal.then(() => console.log("Modal was closed")) - * + * * @example * const modal = Modal.open(MyModal, { id: 1 }) * modal.on("close", () => console.log("Modal was closed")) @@ -229,13 +228,13 @@ class ModalController = ModalContr /** * Subscribes to `event` with `listener`. - * + * * @example * Modal.on("close", () => { }) - * + * * @returns `unsubscribe` method */ - public on(event: T, listener: (...args: ModalControllerEvents[T]) => void) { + public on(event: T, listener: ModalControllerEvents[T]) { this.events.on(event, listener) return () => { @@ -245,9 +244,9 @@ class ModalController = ModalContr /** * Used for container component to get the current state. - * + * * Observes the state and calls the callback on any changes. - * + * * @returns `unsubscribe` method to stop observing. */ public subscribe(callback: () => void) { diff --git a/src/ModalWindow.ts b/src/ModalWindow.ts index 53edf97..c769aa7 100644 --- a/src/ModalWindow.ts +++ b/src/ModalWindow.ts @@ -16,17 +16,16 @@ copies or substantial portions of the Software. */ -import EventEmitter from "eventemitter3" - import Deffered from "./Deffered" +import EventEmitter from "./EventEmitter" import { ModalComponent, ModalParams } from "./types" import { cyrb53, serialize } from "./utils" const MODAL_SEED = Date.now() interface ModalWindowEvents { - open: [] - close: [] + open(): void + close(): void } const DEFAULT_PARAMS: ModalParams = { @@ -43,12 +42,12 @@ const DEFAULT_PARAMS: ModalParams = { class ModalWindow { /** * Hash of `serialized` property. - * + * * Unique id of the modal window. * If two modals have the same id, they will be treated as the same modal. - * + * * This is usually used in `key` prop for React components. - * + * * @note * This is not the same as `params.id` because `id` is unique for each modal window. */ @@ -62,7 +61,7 @@ class ModalWindow { public params: ModalParams & CustomParams /** * Indicates that the `close` method has been called and the modal window is going to be removed. - * + * * @default * false */ @@ -88,10 +87,10 @@ class ModalWindow { /** * Closes the modal window. - * + * * @note * This is an arrow function, which prevents `this` from being lost - You can use it without `bind`. - * + * * @example * const modal = Modal.open(PopupHello, { title: "Hello" }) * modal.close() @@ -108,7 +107,7 @@ class ModalWindow { /** * Can be used to wait for the modal to be closed before performing an action. - * + * * @example * await Modal.open(PopupHello, { title: "Hello" }) * doAnyAction() @@ -121,12 +120,12 @@ class ModalWindow { * @example * const modal = Modal.open(PopupHello, { title: "Hello" }) * modal.on("close", () => { }) - * + * * @note If you want to do something on close, you can use await directly on this instance. For details see `then` method in `ModalWindow`. - * + * * @returns `unsubscribe` method */ - on(event: K, listener: (...args: ModalWindowEvents[K]) => void) { + on(event: K, listener: ModalWindowEvents[K]) { this.events.on(event, listener) return () => { @@ -137,7 +136,7 @@ class ModalWindow { /** * This is a workaround for TypeScript. - * + * * Used if it doesn't matter what type of `CustomParams` is used. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any