import type { StatePart } from './smartstate.classes.statepart.js'; export interface IContextProviderOptions { /** the context key (compared by strict equality) */ context: unknown; /** the state part to provide */ statePart: StatePart; /** 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( element: HTMLElement, options: IContextProviderOptions, ): () => 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(); }; }