mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-09 02:52:26 +03:00
improve event listeners api
This commit is contained in:
@@ -408,6 +408,7 @@ interface OverlayScrollbars {
|
||||
|
||||
elements(): Elements;
|
||||
|
||||
on(eventListeners: EventListeners): () => void;
|
||||
on<N extends keyof EventListenerMap>(name: N, listener: EventListener<N>): () => void;
|
||||
on<N extends keyof EventListenerMap>(name: N, listener: EventListener<N>[]): () => void;
|
||||
|
||||
|
||||
Generated
+2
-8
@@ -19520,10 +19520,6 @@
|
||||
"find-parent-dir": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-starter": {
|
||||
"resolved": "packages/test",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/nopt": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||
@@ -31507,7 +31503,8 @@
|
||||
},
|
||||
"packages/test": {
|
||||
"name": "node-starter",
|
||||
"version": "0.0.0"
|
||||
"version": "0.0.0",
|
||||
"extraneous": true
|
||||
},
|
||||
"website": {
|
||||
"dependencies": {
|
||||
@@ -45917,9 +45914,6 @@
|
||||
"find-parent-dir": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"node-starter": {
|
||||
"version": "file:packages/test"
|
||||
},
|
||||
"nopt": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||
|
||||
@@ -408,6 +408,7 @@ interface OverlayScrollbars {
|
||||
|
||||
elements(): Elements;
|
||||
|
||||
on(eventListeners: EventListeners): () => void;
|
||||
on<N extends keyof EventListenerMap>(name: N, listener: EventListener<N>): () => void;
|
||||
on<N extends keyof EventListenerMap>(name: N, listener: EventListener<N>[]): () => void;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { OverlayScrollbars } from '~/overlayscrollbars';
|
||||
import type { DeepPartial } from '~/typings';
|
||||
import type { Options } from '~/options';
|
||||
import type {
|
||||
InitialEventListeners as GeneralInitialEventListeners,
|
||||
EventListeners as GeneralEventListeners,
|
||||
EventListener as GeneralEventListener,
|
||||
} from '~/support/eventListeners';
|
||||
|
||||
@@ -48,13 +48,13 @@ export type EventListenerMap = {
|
||||
};
|
||||
|
||||
/**
|
||||
* An object which describes the initial event listeners.
|
||||
* An object which describes event listeners.
|
||||
* Simplified it looks like:
|
||||
* {
|
||||
* [eventName: string]: EventListener | EventListener[]
|
||||
* }
|
||||
*/
|
||||
export type InitialEventListeners = GeneralInitialEventListeners<EventListenerMap>;
|
||||
export type EventListeners = GeneralEventListeners<EventListenerMap>;
|
||||
|
||||
/** An event listener. */
|
||||
export type EventListener<N extends keyof EventListenerMap> = GeneralEventListener<
|
||||
|
||||
@@ -12,7 +12,7 @@ export type {
|
||||
export type {
|
||||
EventListenerMap,
|
||||
EventListener,
|
||||
InitialEventListeners,
|
||||
EventListeners,
|
||||
OnUpdatedEventListenerArgs,
|
||||
} from '~/eventListeners';
|
||||
export type {
|
||||
|
||||
@@ -20,7 +20,7 @@ import type { Options, ReadonlyOptions } from '~/options';
|
||||
import type { Plugin, OptionsValidationPluginInstance, PluginInstance } from '~/plugins';
|
||||
import type { InitializationTarget } from '~/initialization';
|
||||
import type { DeepPartial, OverflowStyle } from '~/typings';
|
||||
import type { EventListenerMap, EventListener, InitialEventListeners } from '~/eventListeners';
|
||||
import type { EventListenerMap, EventListener, EventListeners } from '~/eventListeners';
|
||||
import type {
|
||||
ScrollbarsSetupElement,
|
||||
ScrollbarStructure,
|
||||
@@ -48,7 +48,7 @@ export interface OverlayScrollbarsStatic {
|
||||
(
|
||||
target: InitializationTarget,
|
||||
options: DeepPartial<Options>,
|
||||
eventListeners?: InitialEventListeners
|
||||
eventListeners?: EventListeners
|
||||
): OverlayScrollbars;
|
||||
|
||||
/**
|
||||
@@ -170,6 +170,12 @@ export interface OverlayScrollbars {
|
||||
*/
|
||||
options(newOptions: DeepPartial<Options>): Options;
|
||||
|
||||
/**
|
||||
* Adds event listeners to the instance.
|
||||
* @param eventListeners An object which contains the added listeners.
|
||||
* @returns Returns a function which removes the added listeners.
|
||||
*/
|
||||
on(eventListeners: EventListeners): () => void;
|
||||
/**
|
||||
* Adds an event listener to the instance.
|
||||
* @param name The name of the event.
|
||||
@@ -227,7 +233,7 @@ const invokePluginInstance = (
|
||||
export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
||||
target: InitializationTarget,
|
||||
options?: DeepPartial<Options>,
|
||||
eventListeners?: InitialEventListeners
|
||||
eventListeners?: EventListeners
|
||||
) => {
|
||||
const { _getDefaultOptions, _getDefaultInitialization, _addListener } = getEnvironment();
|
||||
const plugins = getPlugins();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { isArray } from '~/support/utils/types';
|
||||
import { isArray, isString } from '~/support/utils/types';
|
||||
import { keys } from '~/support/utils/object';
|
||||
import { each, from, isEmptyArray } from '~/support/utils/array';
|
||||
import { each, push, from, isEmptyArray, runEachAndClear } from '~/support/utils/array';
|
||||
|
||||
export type EventListener<EventMap extends Record<string, any[]>, N extends keyof EventMap> = (
|
||||
...args: EventMap[N]
|
||||
) => void;
|
||||
|
||||
export type InitialEventListeners<EventMap extends Record<string, any[]>> = {
|
||||
export type EventListeners<EventMap extends Record<string, any[]>> = {
|
||||
[K in keyof EventMap]?: EventListener<EventMap, K> | EventListener<EventMap, K>[];
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ const manageListener = <EventMap extends Record<string, any[]>, N extends keyof
|
||||
};
|
||||
|
||||
export const createEventListenerHub = <EventMap extends Record<string, any[]>>(
|
||||
initialEventListeners?: InitialEventListeners<EventMap>
|
||||
initialEventListeners?: EventListeners<EventMap>
|
||||
) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
type EventListener<N extends keyof EventMap> = (...args: EventMap[N]) => void;
|
||||
@@ -28,11 +28,12 @@ export const createEventListenerHub = <EventMap extends Record<string, any[]>>(
|
||||
<N extends keyof EventMap>(name?: N, listener?: EventListener<N> | EventListener<N>[]): void;
|
||||
};
|
||||
type AddEvent = {
|
||||
(eventListeners: EventListeners<EventMap>): () => void;
|
||||
<N extends keyof EventMap>(name: N, listener: EventListener<N>): () => void;
|
||||
<N extends keyof EventMap>(name: N, listener: EventListener<N>[]): () => void;
|
||||
<N extends keyof EventMap>(
|
||||
name: N,
|
||||
listener: EventListener<N> | EventListener<N>[]
|
||||
nameOrEventListeners: N | EventListeners<EventMap>,
|
||||
listener?: EventListener<N> | EventListener<N>[]
|
||||
): () => void;
|
||||
};
|
||||
type TriggerEvent = {
|
||||
@@ -61,17 +62,27 @@ export const createEventListenerHub = <EventMap extends Record<string, any[]>>(
|
||||
};
|
||||
|
||||
const addEvent: AddEvent = <N extends keyof EventMap>(
|
||||
name: N,
|
||||
listener: EventListener<N> | EventListener<N>[]
|
||||
nameOrEventListeners: N | EventListeners<EventMap>,
|
||||
listener?: EventListener<N> | EventListener<N>[]
|
||||
): (() => void) => {
|
||||
const eventSet = events.get(name) || new Set();
|
||||
events.set(name, eventSet);
|
||||
if (isString(nameOrEventListeners)) {
|
||||
const eventSet = events.get(nameOrEventListeners) || new Set();
|
||||
events.set(nameOrEventListeners, eventSet);
|
||||
|
||||
manageListener((currListener) => {
|
||||
currListener && eventSet.add(currListener);
|
||||
}, listener as any);
|
||||
manageListener((currListener) => {
|
||||
currListener && eventSet.add(currListener);
|
||||
}, listener as any);
|
||||
|
||||
return removeEvent.bind(0, name as any, listener as any);
|
||||
return removeEvent.bind(0, nameOrEventListeners as any, listener as any);
|
||||
}
|
||||
|
||||
const eventListenerKeys = keys(nameOrEventListeners) as (keyof EventListeners<EventMap>)[];
|
||||
const offFns: (() => void)[] = [];
|
||||
each(eventListenerKeys, (key) => {
|
||||
push(offFns, addEvent(key, (nameOrEventListeners as EventListeners<EventMap>)[key]));
|
||||
});
|
||||
|
||||
return runEachAndClear.bind(0, offFns);
|
||||
};
|
||||
|
||||
const triggerEvent: TriggerEvent = <N extends keyof EventMap>(
|
||||
@@ -89,10 +100,7 @@ export const createEventListenerHub = <EventMap extends Record<string, any[]>>(
|
||||
});
|
||||
};
|
||||
|
||||
const initialListenerKeys = keys(initialEventListeners) as Extract<keyof EventMap, string>[];
|
||||
each(initialListenerKeys, (key) => {
|
||||
addEvent(key, initialEventListeners![key] as any);
|
||||
});
|
||||
addEvent(initialEventListeners || {});
|
||||
|
||||
return [addEvent, removeEvent, triggerEvent] as [AddEvent, RemoveEvent, TriggerEvent];
|
||||
};
|
||||
|
||||
@@ -94,6 +94,42 @@ describe('eventListeners', () => {
|
||||
expect(something).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('addEvent with event listeners', () => {
|
||||
const onBooleanA = jest.fn((a: boolean, b: string) => a + b);
|
||||
const onBooleanB = jest.fn();
|
||||
const onString = jest.fn();
|
||||
const onUndefined = jest.fn();
|
||||
const [addEvent, , triggerEvent] = createEventListenerHub<EventMap>();
|
||||
const removeEvents = addEvent({
|
||||
onBoolean: [onBooleanA, onBooleanB],
|
||||
onString: [onString, onString], // multiple equal listeners are treated as one
|
||||
onUndefined,
|
||||
});
|
||||
|
||||
triggerEvent('onBoolean', [true, 'hi']);
|
||||
triggerEvent('onString', ['hi']);
|
||||
triggerEvent('onUndefined');
|
||||
|
||||
removeEvents();
|
||||
|
||||
triggerEvent('onBoolean', [true, 'hi']);
|
||||
triggerEvent('onString', ['hi']);
|
||||
triggerEvent('onUndefined');
|
||||
|
||||
expect(onBooleanA).toHaveBeenCalledTimes(1);
|
||||
expect(onBooleanA).toHaveBeenLastCalledWith(true, 'hi');
|
||||
|
||||
expect(onBooleanB).toHaveBeenCalledTimes(1);
|
||||
expect(onBooleanB).toHaveBeenLastCalledWith(true, 'hi');
|
||||
|
||||
// even though onString was registered twice, its only called once
|
||||
expect(onString).toHaveBeenCalledTimes(1);
|
||||
expect(onString).toHaveBeenLastCalledWith('hi');
|
||||
|
||||
expect(onUndefined).toHaveBeenCalledTimes(1);
|
||||
expect(onUndefined).toHaveBeenLastCalledWith();
|
||||
});
|
||||
|
||||
test('removeEvent', () => {
|
||||
const onBooleanA = jest.fn();
|
||||
const onBooleanB = jest.fn();
|
||||
|
||||
Reference in New Issue
Block a user