UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

127 lines (126 loc) 3.29 kB
import { FanOut } from 'thingies/lib/fanout'; /** * Merges multiple fanouts into a single fanout. The merged fanout emits the * same data as the source fanouts. */ export class MergeFanOut extends FanOut { fanouts; unsubs = []; constructor(fanouts) { super(); this.fanouts = fanouts; } listen(listener) { if (!this.listeners.size) this.unsubs = this.fanouts.map((fanout) => fanout.listen((data) => this.emit(data))); const unsub = super.listen(listener); return () => { unsub(); if (!this.listeners.size) { for (const unsub of this.unsubs) unsub(); this.unsubs = []; } }; } } /** * Buffers data from a fanout and emits the buffered data once per microtask. */ export class MicrotaskBufferFanOut extends FanOut { source; buffer = []; unsub = undefined; constructor(source) { super(); this.source = source; } listen(listener) { if (!this.unsub) { this.unsub = this.source.listen((data) => { const buffer = this.buffer; if (!buffer.length) { queueMicrotask(() => { this.emit(buffer); this.buffer = []; }); } buffer.push(data); }); } const unsub = super.listen(listener); return () => { unsub(); if (!this.listeners.size) this.clear(); }; } clear() { this.listeners.clear(); this.buffer = []; this.unsub?.(); this.unsub = undefined; } } /** * Maps the data from a fanout using a mapper function. */ export class MapFanOut extends FanOut { source; mapper; constructor(source, mapper) { super(); this.source = source; this.mapper = mapper; } unsub = undefined; listen(listener) { if (!this.unsub) this.unsub = this.source.listen((data) => this.emit(this.mapper(data))); const unsub = super.listen(listener); return () => { unsub(); if (!this.listeners.size) this.clear(); }; } clear() { this.listeners.clear(); this.unsub?.(); this.unsub = undefined; } } /** * Emits only when the source fanout emits a new value. The first value is * emitted immediately. */ export class OnNewFanOut extends FanOut { source; last; unsub = undefined; constructor(source, last = undefined) { super(); this.source = source; this.last = last; } listen(listener) { if (!this.unsub) { this.unsub = this.source.listen((data) => { if (this.last !== data) this.emit((this.last = data)); }); } const unsub = super.listen(listener); return () => { unsub(); if (!this.listeners.size) this.clear(); }; } clear() { this.listeners.clear(); this.last = undefined; this.unsub?.(); this.unsub = undefined; } }