62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
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();
|
|
};
|
|
}
|