json-joy
Version:
Collection of libraries for building collaborative editing apps.
127 lines (126 loc) • 3.29 kB
JavaScript
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;
}
}