diff --git a/package/signal/src/signal.ts b/package/signal/src/signal.ts index afc35b242..e5d832c1a 100644 --- a/package/signal/src/signal.ts +++ b/package/signal/src/signal.ts @@ -1,6 +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. @@ -11,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, @@ -80,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); @@ -139,18 +150,36 @@ export function requestSignal( * } * } */ -export function addSignalProvider( +export function setSignalProvider( signalName: SignalName, - signalCallback: (detail: VatrRequestSignals[SignalName]) => void | Promise, + signalProvider: SignalProvider, + options?: SignalProviderOptions, ): symbol { - log('addSignalProvider(%s)', signalName); + log('setSignalProvider(%s)', signalName); + // @TODO: refactor with removeSignalProvider + const signal = _getSignalObject(`request-${signalName}` as unknown as SignalName); + if (signal.listenerList.length>0) { + log('setSignalProvider(%s): WARNING! another provider defined and will removed!', + 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}, ); } +// @TODO: removeSignalProvider(signalName): void + /** * Resolved with signal value when signal is ready base on requested options. * By default, wait new signal received. diff --git a/package/signal/src/type.ts b/package/signal/src/type.ts index a14290936..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,14 +76,21 @@ export interface DispatchOptions * * @default true */ - debounce: boolean; + debounce?: boolean; } /** * Signal listeners callback function type. */ export type ListenerCallback = - (detail: VatrSignals[SignalName]) => void | Promise; + (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.