mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-23 21:30:37 +03:00
improve event listeners api
This commit is contained in:
@@ -408,6 +408,7 @@ interface OverlayScrollbars {
|
|||||||
|
|
||||||
elements(): Elements;
|
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;
|
||||||
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"
|
"find-parent-dir": "^0.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-starter": {
|
|
||||||
"resolved": "packages/test",
|
|
||||||
"link": true
|
|
||||||
},
|
|
||||||
"node_modules/nopt": {
|
"node_modules/nopt": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||||
@@ -31507,7 +31503,8 @@
|
|||||||
},
|
},
|
||||||
"packages/test": {
|
"packages/test": {
|
||||||
"name": "node-starter",
|
"name": "node-starter",
|
||||||
"version": "0.0.0"
|
"version": "0.0.0",
|
||||||
|
"extraneous": true
|
||||||
},
|
},
|
||||||
"website": {
|
"website": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -45917,9 +45914,6 @@
|
|||||||
"find-parent-dir": "^0.3.0"
|
"find-parent-dir": "^0.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node-starter": {
|
|
||||||
"version": "file:packages/test"
|
|
||||||
},
|
|
||||||
"nopt": {
|
"nopt": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||||
|
|||||||
@@ -408,6 +408,7 @@ interface OverlayScrollbars {
|
|||||||
|
|
||||||
elements(): Elements;
|
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;
|
||||||
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 { DeepPartial } from '~/typings';
|
||||||
import type { Options } from '~/options';
|
import type { Options } from '~/options';
|
||||||
import type {
|
import type {
|
||||||
InitialEventListeners as GeneralInitialEventListeners,
|
EventListeners as GeneralEventListeners,
|
||||||
EventListener as GeneralEventListener,
|
EventListener as GeneralEventListener,
|
||||||
} from '~/support/eventListeners';
|
} 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:
|
* Simplified it looks like:
|
||||||
* {
|
* {
|
||||||
* [eventName: string]: EventListener | EventListener[]
|
* [eventName: string]: EventListener | EventListener[]
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
export type InitialEventListeners = GeneralInitialEventListeners<EventListenerMap>;
|
export type EventListeners = GeneralEventListeners<EventListenerMap>;
|
||||||
|
|
||||||
/** An event listener. */
|
/** An event listener. */
|
||||||
export type EventListener<N extends keyof EventListenerMap> = GeneralEventListener<
|
export type EventListener<N extends keyof EventListenerMap> = GeneralEventListener<
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export type {
|
|||||||
export type {
|
export type {
|
||||||
EventListenerMap,
|
EventListenerMap,
|
||||||
EventListener,
|
EventListener,
|
||||||
InitialEventListeners,
|
EventListeners,
|
||||||
OnUpdatedEventListenerArgs,
|
OnUpdatedEventListenerArgs,
|
||||||
} from '~/eventListeners';
|
} from '~/eventListeners';
|
||||||
export type {
|
export type {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import type { Options, ReadonlyOptions } from '~/options';
|
|||||||
import type { Plugin, OptionsValidationPluginInstance, PluginInstance } from '~/plugins';
|
import type { Plugin, OptionsValidationPluginInstance, PluginInstance } from '~/plugins';
|
||||||
import type { InitializationTarget } from '~/initialization';
|
import type { InitializationTarget } from '~/initialization';
|
||||||
import type { DeepPartial, OverflowStyle } from '~/typings';
|
import type { DeepPartial, OverflowStyle } from '~/typings';
|
||||||
import type { EventListenerMap, EventListener, InitialEventListeners } from '~/eventListeners';
|
import type { EventListenerMap, EventListener, EventListeners } from '~/eventListeners';
|
||||||
import type {
|
import type {
|
||||||
ScrollbarsSetupElement,
|
ScrollbarsSetupElement,
|
||||||
ScrollbarStructure,
|
ScrollbarStructure,
|
||||||
@@ -48,7 +48,7 @@ export interface OverlayScrollbarsStatic {
|
|||||||
(
|
(
|
||||||
target: InitializationTarget,
|
target: InitializationTarget,
|
||||||
options: DeepPartial<Options>,
|
options: DeepPartial<Options>,
|
||||||
eventListeners?: InitialEventListeners
|
eventListeners?: EventListeners
|
||||||
): OverlayScrollbars;
|
): OverlayScrollbars;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -170,6 +170,12 @@ export interface OverlayScrollbars {
|
|||||||
*/
|
*/
|
||||||
options(newOptions: DeepPartial<Options>): Options;
|
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.
|
* Adds an event listener to the instance.
|
||||||
* @param name The name of the event.
|
* @param name The name of the event.
|
||||||
@@ -227,7 +233,7 @@ const invokePluginInstance = (
|
|||||||
export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
||||||
target: InitializationTarget,
|
target: InitializationTarget,
|
||||||
options?: DeepPartial<Options>,
|
options?: DeepPartial<Options>,
|
||||||
eventListeners?: InitialEventListeners
|
eventListeners?: EventListeners
|
||||||
) => {
|
) => {
|
||||||
const { _getDefaultOptions, _getDefaultInitialization, _addListener } = getEnvironment();
|
const { _getDefaultOptions, _getDefaultInitialization, _addListener } = getEnvironment();
|
||||||
const plugins = getPlugins();
|
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 { 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> = (
|
export type EventListener<EventMap extends Record<string, any[]>, N extends keyof EventMap> = (
|
||||||
...args: EventMap[N]
|
...args: EventMap[N]
|
||||||
) => void;
|
) => 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>[];
|
[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[]>>(
|
export const createEventListenerHub = <EventMap extends Record<string, any[]>>(
|
||||||
initialEventListeners?: InitialEventListeners<EventMap>
|
initialEventListeners?: EventListeners<EventMap>
|
||||||
) => {
|
) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
type EventListener<N extends keyof EventMap> = (...args: EventMap[N]) => void;
|
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;
|
<N extends keyof EventMap>(name?: N, listener?: EventListener<N> | EventListener<N>[]): void;
|
||||||
};
|
};
|
||||||
type AddEvent = {
|
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>[]): () => void;
|
<N extends keyof EventMap>(name: N, listener: EventListener<N>[]): () => void;
|
||||||
<N extends keyof EventMap>(
|
<N extends keyof EventMap>(
|
||||||
name: N,
|
nameOrEventListeners: N | EventListeners<EventMap>,
|
||||||
listener: EventListener<N> | EventListener<N>[]
|
listener?: EventListener<N> | EventListener<N>[]
|
||||||
): () => void;
|
): () => void;
|
||||||
};
|
};
|
||||||
type TriggerEvent = {
|
type TriggerEvent = {
|
||||||
@@ -61,17 +62,27 @@ export const createEventListenerHub = <EventMap extends Record<string, any[]>>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const addEvent: AddEvent = <N extends keyof EventMap>(
|
const addEvent: AddEvent = <N extends keyof EventMap>(
|
||||||
name: N,
|
nameOrEventListeners: N | EventListeners<EventMap>,
|
||||||
listener: EventListener<N> | EventListener<N>[]
|
listener?: EventListener<N> | EventListener<N>[]
|
||||||
): (() => void) => {
|
): (() => void) => {
|
||||||
const eventSet = events.get(name) || new Set();
|
if (isString(nameOrEventListeners)) {
|
||||||
events.set(name, eventSet);
|
const eventSet = events.get(nameOrEventListeners) || new Set();
|
||||||
|
events.set(nameOrEventListeners, eventSet);
|
||||||
|
|
||||||
manageListener((currListener) => {
|
manageListener((currListener) => {
|
||||||
currListener && eventSet.add(currListener);
|
currListener && eventSet.add(currListener);
|
||||||
}, listener as any);
|
}, 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>(
|
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>[];
|
addEvent(initialEventListeners || {});
|
||||||
each(initialListenerKeys, (key) => {
|
|
||||||
addEvent(key, initialEventListeners![key] as any);
|
|
||||||
});
|
|
||||||
|
|
||||||
return [addEvent, removeEvent, triggerEvent] as [AddEvent, RemoveEvent, TriggerEvent];
|
return [addEvent, removeEvent, triggerEvent] as [AddEvent, RemoveEvent, TriggerEvent];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -94,6 +94,42 @@ describe('eventListeners', () => {
|
|||||||
expect(something).toHaveBeenCalledTimes(1);
|
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', () => {
|
test('removeEvent', () => {
|
||||||
const onBooleanA = jest.fn();
|
const onBooleanA = jest.fn();
|
||||||
const onBooleanB = jest.fn();
|
const onBooleanB = jest.fn();
|
||||||
|
|||||||
Reference in New Issue
Block a user