theatre/packages/dataverse/src/utils/Tappable.ts
2021-06-18 13:05:06 +02:00

91 lines
2.1 KiB
TypeScript

type Untap = () => void
type UntapFromSource = () => void
interface IProps<V> {
tapToSource: (cb: (payload: V) => void) => UntapFromSource
}
type Listener<V> = ((v: V) => void) | (() => void)
export default class Tappable<V> {
private _props: IProps<V>
private _tappers: Map<number, {bivarianceHack(v: V): void}['bivarianceHack']>
private _untapFromSource: null | UntapFromSource
private _lastTapperId: number
private _untapFromSourceTimeout: null | NodeJS.Timer = null
constructor(props: IProps<V>) {
this._lastTapperId = 0
this._untapFromSource = null
this._props = props
this._tappers = new Map()
}
private _check() {
if (this._untapFromSource) {
if (this._tappers.size === 0) {
this._scheduleToUntapFromSource()
/*
* this._untapFromSource()
* this._untapFromSource = null
*/
}
} else {
if (this._tappers.size !== 0) {
this._untapFromSource = this._props.tapToSource(this._cb)
}
}
}
private _scheduleToUntapFromSource() {
if (this._untapFromSourceTimeout !== null) return
this._untapFromSourceTimeout = setTimeout(() => {
this._untapFromSourceTimeout = null
if (this._tappers.size === 0) {
this._untapFromSource!()
this._untapFromSource = null
}
}, 0)
}
private _cb: any = (arg: any): void => {
this._tappers.forEach((cb) => {
cb(arg)
})
}
tap(cb: Listener<V>): Untap {
const tapperId = this._lastTapperId++
this._tappers.set(tapperId, cb)
this._check()
return () => {
this._removeTapperById(tapperId)
}
}
/*
* tapImmediate(cb: Listener<V>): Untap {
* const ret = this.tap(cb)
* return ret
* }
*/
private _removeTapperById(id: number) {
this._tappers.delete(id)
this._check()
}
// /**
// * @deprecated
// */
// map<T>(transform: {bivarianceHack(v: V): T}['bivarianceHack']): Tappable<T> {
// return new Tappable({
// tapToSource: (cb: (v: T) => void) => {
// return this.tap((v: $IntentionalAny) => {
// return cb(transform(v))
// })
// },
// })
// }
}