From 5bf82b3f05abc89102634e9b864d81b5b5af527e Mon Sep 17 00:00:00 2001 From: Ali Mihandoost Date: Sat, 26 Feb 2022 03:35:39 +0330 Subject: [PATCH] feat(signal): make new package for manage signals --- package/signal/README.md | 10 +++ package/signal/package.json | 25 +++++++ package/signal/src/signal.ts | 134 +++++++++++++++++++++++++++++++++++ package/signal/src/type.ts | 68 ++++++++++++++++++ package/signal/tsconfig.json | 17 +++++ 5 files changed, 254 insertions(+) create mode 100644 package/signal/README.md create mode 100644 package/signal/package.json create mode 100644 package/signal/src/signal.ts create mode 100644 package/signal/src/type.ts create mode 100644 package/signal/tsconfig.json diff --git a/package/signal/README.md b/package/signal/README.md new file mode 100644 index 000000000..d8dbbf436 --- /dev/null +++ b/package/signal/README.md @@ -0,0 +1,10 @@ +# @vatr/signal + +Manage and control all signals in your project. + +## Example usage + +```js +import { ... } from 'https://esm.run/@vatr/signal'; +... +``` diff --git a/package/signal/package.json b/package/signal/package.json new file mode 100644 index 000000000..e2a334e81 --- /dev/null +++ b/package/signal/package.json @@ -0,0 +1,25 @@ +{ + "name": "@vatr/signal", + "version": "0.0.0", + "description": "Manage and control all signals in your project.", + "main": "signal.js", + "type": "module", + "types": "signal.d.ts", + "author": "S. Ali Mihandoost ", + "license": "MIT", + "files": [ + "**/*.js", + "**/*.d.ts", + "**/*.map", + "**/*.html", + "**/*.md" + ], + "repository": { + "type": "git", + "url": "https://github.com/AliMD/vatr", + "directory": "package/signal" + }, + "dependencies": { + "tslib": "^2.3.1" + } +} diff --git a/package/signal/src/signal.ts b/package/signal/src/signal.ts new file mode 100644 index 000000000..479951fa0 --- /dev/null +++ b/package/signal/src/signal.ts @@ -0,0 +1,134 @@ +import {createLogger} from '@vatr/logger'; +import type {ListenerOptions, DispatchOptions, ListenerCallback, RequestSignalOptions} from './type'; + +const log = createLogger('vatr/signal'); + +/** + * Add new listener to specific signal. + * + * @example + * const listener = addSignalListener('route-change', money => console.log(money)); + */ +export function addSignalListener( + signalName: SignalName, + signalCallback: ListenerCallback, + options?: Partial, +): symbol { + log('addSignalListener: %o', {signalName, options}); + + // return _addSignalListener(signalName, signalCallback as ListenerCallback, { + // once: false, + // capture: false, + // disabled: false, + // ...options, + // }); +} + +/** + * Remove listener from specific signal. + * + * @example + * const listener = addSignalListener('content-change', ...); + * removeSignalListener('content-change', listener); + */ +export function removeSignalListener( + signalName: SignalName, + listenerId: symbol, +): void { + log('removeSignalListener: %s', signalName); +} + +/** + * Dispatch signal to all listeners. + * + * @example + * dispatchSignal('content-change', content); + */ +export function dispatchSignal( + signalName: SignalName, + value: VatrSignals[SignalName], + options?: Partial, +): void { + log('dispatchSignal: %s => %o', signalName, value); +} + +/** + * Resolved with signal value when signal is ready base on requested options. + * By default, dispatch request signal and wait for answer (wait new signal dispatched). + * + * @example + * // dispatch request signal and wait for answer (wait for NEW signal). + * const newContent = await requestSignal('content-change', {foo: 'bar'}); + */ +export async function requestSignal( + signalName: SignalName, + requestParam: VatrRequestSignals[SignalName], +): Promise { + log('requestSignal: %s', signalName); + + return Promise.resolve(undefined); +} + +/** + * Define signal provider, which will be called when signal requested. + * + * @example + * defineSignalProvider('content-change', async (requestParam) => { + * const content = await fetchNewContent(requestParam); + * if (content != null) { + * return content; // dispatchSignal('content-change', content); + * } + * else { + * dispatchSignal('content-not-found'); + * } + * } + */ +export function defineSignalProvider( + signalName: SignalName, + signalCallback: ListenerCallback, +): symbol { + log('addSignalListener: %o', {signalName, options}); + +// return _addSignalListener(signalName, signalCallback as ListenerCallback, { +// once: false, +// capture: false, +// disabled: false, +// ...options, +// }); +} + + +/** + * Resolved with signal value when signal is ready base on requested options. + * By default, wait new signal received. + * + * @param receivePrevious If true, get signal value from last dispatched signal (if any) or wait new signal received. + * + * @example + * // Wait for NEW signal received. + * const newContent = await waitForSignal('content-change'); + * // get signal value from last dispatched signal (if any) or wait new signal received. + * const route = await waitForSignal('route-change', {receivePrevious: true}); + */ +export async function waitForSignal( + signalName: SignalName, + receivePrevious?: boolean, +): Promise { + log('requestSignal: %s', signalName); + + return Promise.resolve(undefined); +} + +/** + * Check signal dispatched before or not! + * + * @example + * if(hasSignalDispatchedBefore('easter-egg')) { ... } + */ +export function hasSignalDispatchedBefore(signalName: SignalName): boolean { + const dispatched = true; + log('hasSignalDispatchedBefore: %s => %s', signalName, dispatched); + return dispatched; +} + + diff --git a/package/signal/src/type.ts b/package/signal/src/type.ts new file mode 100644 index 000000000..609412bc0 --- /dev/null +++ b/package/signal/src/type.ts @@ -0,0 +1,68 @@ +declare global +{ + /** + * Global signal registry. + */ + interface VatrSignals + { + readonly 'easter-egg': void; + } + + interface VatrRequestSignals + { + readonly 'easter-egg': number; + } +} + +export interface ListenerOptions +{ + /** + * If true, the listener will be called only once. + * @default false + */ + once: boolean; + + /** + * If true, the listener will be called before other. + * @default false + * @deprecated @TODO Please implement this feature. + */ + priority: boolean; + + /** + * If true, the listener will be defined disabled by default. + * + * @default false + */ + disabled: boolean; + + /** + * If true, the listener will be called in animation frame debounce. + * If false, every signal is matter and count. + * + * @default true or dispatch signal config. + * @deprecated @TODO Please implement this feature. + */ + debounce: boolean; + + /** + * Calling this listener (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'; +} + +export interface DispatchOptions +{ + /** + * If true, the dispatch will be send after animation frame debounce. + * If false, every signal is matter and count. + * + * @default true + */ + debounce: boolean; +} + +export type ListenerCallback = (detail: T) => void | Promise; diff --git a/package/signal/tsconfig.json b/package/signal/tsconfig.json new file mode 100644 index 000000000..094ca6531 --- /dev/null +++ b/package/signal/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": ".tsbuildinfo", + "rootDir": "src", + "outDir": ".", + }, + // files, include and exclude from the inheriting config are always overwritten. + "include": [ + "src/**/*.ts" + ], + "exclude": [], + "references": [ + { "path": "../logger" }, + ] +}