diff --git a/local-namespace-config.js b/local-namespace-config.js index 2ccf2c3..dcc2c3e 100644 --- a/local-namespace-config.js +++ b/local-namespace-config.js @@ -6,4 +6,5 @@ module.exports = { '@utils/math': './src/utils/math/index', '@utils/debounce': './src/utils/debounce/index', '@utils/scrolling': './src/utils/scrolling/index', + '@utils/nanoevents': './src/utils/nanoevents/index', }; diff --git a/package.json b/package.json index a811b86..52ce781 100644 --- a/package.json +++ b/package.json @@ -68,8 +68,7 @@ }, "dependencies": { "@rozhkov/react-useful-hooks": "^1.0.10", - "date-fns": "^4.1.0", - "nanoevents": "^9.1.0" + "date-fns": "^4.1.0" }, "devDependencies": { "@babel/cli": "^7.21.5", diff --git a/src/picker-control/create-control.ts b/src/picker-control/create-control.ts index 8cad652..c4c70ff 100644 --- a/src/picker-control/create-control.ts +++ b/src/picker-control/create-control.ts @@ -3,7 +3,7 @@ import type { ValueChangedEvent, ValueChangingEvent, } from '@implementation/base'; -import {createNanoEvents, type Unsubscribe} from 'nanoevents'; +import {createNanoEvents, type Unsubscribe} from '@utils/nanoevents'; type PickerName = string; export type BaseControlConfig = Record}>; diff --git a/src/utils/nanoevents/index.ts b/src/utils/nanoevents/index.ts new file mode 100644 index 0000000..adaf511 --- /dev/null +++ b/src/utils/nanoevents/index.ts @@ -0,0 +1,8 @@ +export { + createNanoEvents, + type DefaultEvents, + type EventsMap, + type Emitter, + type EmitterMixin, + type Unsubscribe, +} from './nanoevents'; diff --git a/src/utils/nanoevents/nanoevents.d.ts b/src/utils/nanoevents/nanoevents.d.ts new file mode 100644 index 0000000..857f0e2 --- /dev/null +++ b/src/utils/nanoevents/nanoevents.d.ts @@ -0,0 +1,105 @@ +interface EventsMap { + [event: string]: any; +} + +interface DefaultEvents extends EventsMap { + [event: string]: (...args: any) => void; +} + +export interface Unsubscribe { + (): void; +} + +export interface Emitter { + /** + * Calls each of the listeners registered for a given event. + * + * ```js + * ee.emit('tick', tickType, tickDuration) + * ``` + * + * @param event The event name. + * @param args The arguments for listeners. + */ + emit( + this: this, + event: K, + ...args: Parameters + ): void; + + /** + * Event names in keys and arrays with listeners in values. + * + * ```js + * emitter1.events = emitter2.events + * emitter2.events = { } + * ``` + */ + events: Partial<{[E in keyof Events]: Events[E][]}>; + + /** + * Add a listener for a given event. + * + * ```js + * const unbind = ee.on('tick', (tickType, tickDuration) => { + * count += 1 + * }) + * + * disable () { + * unbind() + * } + * ``` + * + * @param event The event name. + * @param cb The listener function. + * @returns Unbind listener from event. + */ + on(this: this, event: K, cb: Events[K]): Unsubscribe; +} + +/** + * Create event emitter. + * + * ```js + * import { createNanoEvents } from 'nanoevents' + * + * class Ticker { + * constructor() { + * this.emitter = createNanoEvents() + * } + * on(...args) { + * return this.emitter.on(...args) + * } + * tick() { + * this.emitter.emit('tick') + * } + * } + * ``` + */ +export function createNanoEvents< + Events extends EventsMap = DefaultEvents, +>(): Emitter; + +/** + * An interface for mixins that expose the `on` function (without the emitter + * bound to `this`) + * + * ```js + * import { createNanoEvents } from 'nanoevents' + * + * class Ticker implements EmitterMixin { + * constructor() { + * this.emitter = createNanoEvents() + * } + * on(...args) { + * return this.emitter.on(...args) + * } + * tick() { + * this.emitter.emit('tick') + * } + * } + * ``` + */ +export interface EmitterMixin { + on(event: K, cb: Events[K]): Unsubscribe; +} diff --git a/src/utils/nanoevents/nanoevents.js b/src/utils/nanoevents/nanoevents.js new file mode 100644 index 0000000..2240ee3 --- /dev/null +++ b/src/utils/nanoevents/nanoevents.js @@ -0,0 +1,20 @@ +export let createNanoEvents = () => ({ + emit(event, ...args) { + for ( + let callbacks = this.events[event] || [], + i = 0, + length = callbacks.length; + i < length; + i++ + ) { + callbacks[i](...args); + } + }, + events: {}, + on(event, cb) { + (this.events[event] ||= []).push(cb); + return () => { + this.events[event] = this.events[event]?.filter((i) => cb !== i); + }; + }, +}); diff --git a/tsconfig.json b/tsconfig.json index bbf8058..83f882a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "@utils/math": ["./src/utils/math/index"], "@utils/debounce": ["./src/utils/debounce/index"], "@utils/scrolling": ["./src/utils/scrolling/index"], + "@utils/nanoevents": ["./src/utils/nanoevents/index"], }, "allowUnreachableCode": false, "allowUnusedLabels": false, diff --git a/yarn.lock b/yarn.lock index 620b1a0..1253709 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3422,7 +3422,6 @@ __metadata: eslint-plugin-prettier: "npm:^4.0.0" jest: "npm:^28.1.1" metro-react-native-babel-preset: "npm:^0.77.0" - nanoevents: "npm:^9.1.0" pod-install: "npm:^0.1.0" prettier: "npm:^2.0.5" react: "npm:19.0.0" @@ -10357,13 +10356,6 @@ __metadata: languageName: node linkType: hard -"nanoevents@npm:^9.1.0": - version: 9.1.0 - resolution: "nanoevents@npm:9.1.0" - checksum: 10/77abbc15c4efc15518a0075b604e5db045e5d6184f4d08892c771928a84f53b1dd46138902213f66e8a9989e6c930168ca6441b80ff9098aec83c01a782bd42b - languageName: node - linkType: hard - "natural-compare-lite@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare-lite@npm:1.4.0"