feat(smartstate): Add middleware, computed, batching, selector memoization, AbortSignal support, and Web Component Context Protocol provider
This commit is contained in:
61
ts/smartstate.contextprovider.ts
Normal file
61
ts/smartstate.contextprovider.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import type { StatePart } from './smartstate.classes.statepart.js';
|
||||
|
||||
export interface IContextProviderOptions<TPayload> {
|
||||
/** the context key (compared by strict equality) */
|
||||
context: unknown;
|
||||
/** the state part to provide */
|
||||
statePart: StatePart<any, TPayload>;
|
||||
/** optional selector to provide a derived value instead of the full state */
|
||||
selectorFn?: (state: TPayload) => any;
|
||||
}
|
||||
|
||||
/**
|
||||
* attaches a Context Protocol provider to an HTML element.
|
||||
* listens for `context-request` events and responds with the state part's value.
|
||||
* if subscribe=true, retains the callback and invokes it on every state change.
|
||||
* returns a cleanup function that removes the listener and unsubscribes.
|
||||
*/
|
||||
export function attachContextProvider<TPayload>(
|
||||
element: HTMLElement,
|
||||
options: IContextProviderOptions<TPayload>,
|
||||
): () => void {
|
||||
const { context, statePart, selectorFn } = options;
|
||||
const subscribers = new Set<(value: any, unsubscribe?: () => void) => void>();
|
||||
|
||||
const subscription = statePart.select(selectorFn).subscribe((value) => {
|
||||
for (const cb of subscribers) {
|
||||
cb(value);
|
||||
}
|
||||
});
|
||||
|
||||
const getValue = (): any => {
|
||||
const state = statePart.getState();
|
||||
if (state === undefined) return undefined;
|
||||
return selectorFn ? selectorFn(state) : state;
|
||||
};
|
||||
|
||||
const handler = (event: Event) => {
|
||||
const e = event as CustomEvent;
|
||||
const detail = e.detail;
|
||||
if (!detail || detail.context !== context) return;
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
if (detail.subscribe) {
|
||||
const cb = detail.callback;
|
||||
subscribers.add(cb);
|
||||
const unsubscribe = () => subscribers.delete(cb);
|
||||
cb(getValue(), unsubscribe);
|
||||
} else {
|
||||
detail.callback(getValue());
|
||||
}
|
||||
};
|
||||
|
||||
element.addEventListener('context-request', handler);
|
||||
|
||||
return () => {
|
||||
element.removeEventListener('context-request', handler);
|
||||
subscription.unsubscribe();
|
||||
subscribers.clear();
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user