Skip to content

Commit

Permalink
feat(signal): make new package for manage signals
Browse files Browse the repository at this point in the history
  • Loading branch information
alimd committed Feb 26, 2022
1 parent 53e032e commit 5bf82b3
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 0 deletions.
10 changes: 10 additions & 0 deletions package/signal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# @vatr/signal

Manage and control all signals in your project.

## Example usage

```js
import { ... } from 'https://esm.run/@vatr/signal';
...
```
25 changes: 25 additions & 0 deletions package/signal/package.json
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>",
"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"
}
}
134 changes: 134 additions & 0 deletions package/signal/src/signal.ts
Original file line number Diff line number Diff line change
@@ -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 extends keyof VatrSignals>(
signalName: SignalName,
signalCallback: ListenerCallback<VatrSignals[SignalName]>,
options?: Partial<ListenerOptions>,
): 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 extends keyof VatrSignals>(
signalName: SignalName,
listenerId: symbol,
): void {
log('removeSignalListener: %s', signalName);
}

/**
* Dispatch signal to all listeners.
*
* @example
* dispatchSignal('content-change', content);
*/
export function dispatchSignal<SignalName extends keyof VatrSignals>(
signalName: SignalName,
value: VatrSignals[SignalName],
options?: Partial<DispatchOptions>,
): 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 extends keyof VatrRequestSignals>(
signalName: SignalName,
requestParam: VatrRequestSignals[SignalName],
): Promise<VatrSignals[SignalName]> {
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 extends keyof VatrSignals>(
signalName: SignalName,
signalCallback: ListenerCallback<VatrSignals[SignalName]>,
): 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 extends keyof VatrSignals>(
signalName: SignalName,
receivePrevious?: boolean,
): Promise<VatrSignals[SignalName]> {
log('requestSignal: %s', signalName);

return Promise.resolve(undefined);
}

/**
* Check signal dispatched before or not!
*
* @example
* if(hasSignalDispatchedBefore('easter-egg')) { ... }
*/
export function hasSignalDispatchedBefore<SignalName extends keyof VatrSignals>(signalName: SignalName): boolean {
const dispatched = true;
log('hasSignalDispatchedBefore: %s => %s', signalName, dispatched);
return dispatched;
}


68 changes: 68 additions & 0 deletions package/signal/src/type.ts
Original file line number Diff line number Diff line change
@@ -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<T = unknown> = (detail: T) => void | Promise<void>;
17 changes: 17 additions & 0 deletions package/signal/tsconfig.json
Original file line number Diff line number Diff line change
@@ -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" },
]
}

0 comments on commit 5bf82b3

Please sign in to comment.