diff --git a/core/signal/src/context-consumer.ts b/core/signal/src/context-consumer.ts new file mode 100644 index 000000000..8e8d91cdb --- /dev/null +++ b/core/signal/src/context-consumer.ts @@ -0,0 +1,135 @@ +import {Stringifyable, OmitFirstParam} from '@alwatr/type'; + +import {signalManager} from './signal-manager.js'; + +/** + * Context consumer interface. + */ +export const contextConsumer = { + /** + * Get context value. + * + * Return undefined if context not set before or expired. + * + * Example: + * + * ```ts + * const currentProductList = contextConsumer.getValue<ProductListType>('product-list'); + * if (currentProductList === undefined) { + * // productList not set before or expired. + * } + * ``` + */ + getValue: signalManager.getDetail, + + /** + * Waits until the context value changes. + * + * Example: + * + * ```ts + * const newProductList = await contextConsumer.untilChange<ProductListType>('product-list'); + * ``` + */ + untilChange: signalManager.untilNext, + + /** + * Subscribe to context changes, work like addEventListener. + * + * Example: + * + * ```ts + * const listener = contextConsumer.subscribe<ProductListType>('product-list', (productList) => { + * console.log(productList); + * }); + * // ... + * contextConsumer.unsubscribe(listener); + * ``` + */ + subscribe: signalManager.subscribe, + + /** + * Unsubscribe from context changes, work like removeEventListener. + * + * Example: + * + * ```ts + * const listener = contextConsumer.subscribe<ProductListType>('product-list', (productList) => { + * console.log(productList); + * }); + * // ... + * contextConsumer.unsubscribe(listener); + * ``` + */ + unsubscribe: signalManager.unsubscribe, + + /** + * Bind this interface to special context. + * + * Example: + * + * ```ts + * const productListConsumer = contextConsumer.bind<ProductListType>('product-list'); + * ``` + */ + bind: <T extends Stringifyable>(contextId: string) =>({ + /** + * Event signal Id. + */ + id: contextId, + + /** + * Get context value. + * + * Return undefined if context not set before or expired. + * + * Example: + * + * ```ts + * const currentProductList = productListConsumer.getValue(); + * if (currentProductList === undefined) { + * // productList not set before or expired. + * } + * ``` + */ + getValue: signalManager.getDetail.bind(null, contextId) as OmitFirstParam<typeof signalManager.getDetail<T>>, + + /** + * Waits until the context value changes. + * + * Example: + * + * ```ts + * const newProductList = await productListConsumer.untilChange(); + * ``` + */ + untilChange: signalManager.untilNext.bind(null, contextId) as OmitFirstParam<typeof signalManager.untilNext<T>>, + + /** + * Subscribe to context changes, work like addEventListener. + * + * Example: + * + * ```ts + * const listener = productListConsumer.subscribe((productList) => console.log(productList)); + * // ... + * productListConsumer.unsubscribe(listener); + * ``` + */ + subscribe: signalManager.subscribe.bind(null, contextId) as unknown as + OmitFirstParam<typeof signalManager.subscribe<T>>, + + /** + * Unsubscribe from context changes, work like removeEventListener. + * + * Example: + * + * ```ts + * const listener = productListConsumer.subscribe((productList) => console.log(productList)); + * // ... + * productListConsumer.unsubscribe(listener); + * ``` + */ + unsubscribe: signalManager.unsubscribe, + }), +} as const;