diff --git a/package/signal/src/signal.ts b/package/signal/src/signal.ts index 3d14be1d9..e5d832c1a 100644 --- a/package/signal/src/signal.ts +++ b/package/signal/src/signal.ts @@ -1,5 +1,17 @@ -import {log, _getSignalObject, _callListeners, _removeSignalListener} from './core'; -import type {ListenerOptions, DispatchOptions, ListenerCallback, ListenerObject} from './type'; +import {log, + _getSignalObject, + _callListeners, + _removeSignalListener, +} from './core'; + +import type { + ListenerOptions, + DispatchOptions, + ListenerCallback, + ListenerObject, + SignalProvider, + SignalProviderOptions, +} from './type'; /** * Add new listener to specific signal. @@ -10,13 +22,13 @@ import type {ListenerOptions, DispatchOptions, ListenerCallback, ListenerObject} export function addSignalListener( signalName: SignalName, signalCallback: ListenerCallback, - options?: Partial, + options?: ListenerOptions, ): symbol { log('addSignalListener(%s, %o)', signalName, options); const signal = _getSignalObject(signalName); const listener: ListenerObject = { - id: Symbol('Vatr Signal Listener'), + id: Symbol('Vatr signal listener for ' + signalName), once: options?.once ?? false, disabled: options?.disabled ?? false, callback: signalCallback, @@ -79,8 +91,8 @@ export function removeSignalListener( */ export function dispatchSignal( signalName: SignalName, - value?: VatrSignals[SignalName], - options?: Partial, + value: VatrSignals[SignalName], + options?: DispatchOptions, ): void { log('dispatchSignal(%s, %o, %o)', signalName, value, options); @@ -140,7 +152,8 @@ export function requestSignal( */ export function setSignalProvider( signalName: SignalName, - signalCallback: (requestParam: VatrRequestSignals[SignalName]) => void | Promise, + signalProvider: SignalProvider, + options?: SignalProviderOptions, ): symbol { log('setSignalProvider(%s)', signalName); // @TODO: refactor with removeSignalProvider @@ -150,10 +163,18 @@ export function setSignalProvider( signalName, signal.listenerList.length); signal.listenerList = []; } + + const _callback = async (requestParam: VatrRequestSignals[SignalName]) => { + const signalValue = await signalProvider(requestParam); + if (signalValue !== undefined) { // null can be a valid value. + dispatchSignal(signalName, signalValue, {debounce: options?.debounce ?? true}); + } + }; + return addSignalListener( `request-${signalName}` as unknown as SignalName, - signalCallback as unknown as ListenerCallback, - {receivePrevious: true}, + _callback as unknown as ListenerCallback, + {receivePrevious: options?.receivePrevious ?? true}, ); } diff --git a/package/signal/src/type.ts b/package/signal/src/type.ts index 5ec0e444c..abebdeb47 100644 --- a/package/signal/src/type.ts +++ b/package/signal/src/type.ts @@ -17,26 +17,25 @@ declare global { /** * addSignalListener options type */ -export interface ListenerOptions -{ +export interface ListenerOptions { /** * If true, the listener will be called only once. * @default false */ - once: boolean; + once?: boolean; /** * If true, the listener will be called before other. * @default false */ - priority: boolean; + priority?: boolean; /** * If true, the listener will be defined disabled by default. * * @default false */ - disabled: boolean; + disabled?: boolean; /** * Calling this listener (callback) with preview signal value (if dispatched before). @@ -44,14 +43,32 @@ export interface ListenerOptions * * @default true */ - receivePrevious: boolean | 'Immediate'; + receivePrevious?: boolean | 'Immediate'; } /** * dispatchSignal options type */ -export interface DispatchOptions -{ +export interface DispatchOptions { + /** + * If true, the dispatch will be send after animation frame debounce. + * If false, every signal is matter and count. + * tips: debounce work like throttle this means listeners call with last dispatch value. + * + * @default true + */ + debounce?: boolean; +} + +export interface SignalProviderOptions { + /** + * Calling signal provider (request signal callback) with preview signal value (if dispatched before). + * If Immediate, the listener will be called immediately without any debounce for preview signal. + * + * @default true + */ + receivePrevious?: boolean | 'Immediate'; + /** * If true, the dispatch will be send after animation frame debounce. * If false, every signal is matter and count. @@ -59,7 +76,7 @@ export interface DispatchOptions * * @default true */ - debounce: boolean; + debounce?: boolean; } /** @@ -68,6 +85,13 @@ export interface DispatchOptions export type ListenerCallback = (signalValue: VatrSignals[SignalName]) => void | Promise; +/** + * Signal provider function type used to setSignalProvider. + */ +export type SignalProvider = + (requestParam: VatrRequestSignals[SignalName]) => + VatrSignals[SignalName] | Promise | void | Promise + /** * Signal listeners object in database. */