export class UIEventSource{ public data: T; private _callbacks = []; constructor(data: T) { this.data = data; } public addCallback(callback: ((latestData : T) => void)) { this._callbacks.push(callback); return this; } public setData(t: T): void { if (this.data === t) { return; } this.data = t; this.ping(); } public ping(): void { for (const callback of this._callbacks) { callback(this.data); } } public static flatten(source: UIEventSource>, possibleSources: UIEventSource[]): UIEventSource { const sink = new UIEventSource(source.data?.data); source.addCallback((latestData) => { sink.setData(latestData?.data); }); for (const possibleSource of possibleSources) { possibleSource.addCallback(() => { sink.setData(source.data?.data); }) } return sink; } public map(f: ((T) => J), extraSources: UIEventSource[] = []): UIEventSource { const self = this; const update = function () { newSource.setData(f(self.data)); newSource.ping(); } this.addCallback(update); for (const extraSource of extraSources) { extraSource.addCallback(update); } const newSource = new UIEventSource( f(this.data) ); return newSource; } public syncWith(otherSource: UIEventSource) : UIEventSource{ this.addCallback((latest) => otherSource.setData(latest)); const self = this; otherSource.addCallback((latest) => self.setData(latest)); if(this.data === undefined){ this.setData(otherSource.data); }else{ otherSource.setData(this.data); } return this; } }