UNPKG

@platform/state

Version:

A small, simple, strongly typed, [rx/observable] state-machine.

76 lines (75 loc) 2.38 kB
import produce from 'immer'; import { Subject } from 'rxjs'; import { filter, map, share, takeUntil } from 'rxjs/operators'; export class Store { constructor(args) { this._dispose$ = new Subject(); this._changing$ = new Subject(); this._changed$ = new Subject(); this._ = { state: undefined, }; this.dispose$ = this._dispose$.pipe(share()); this.changing$ = this._changing$.pipe(share()); this.changed$ = this._changed$.pipe(share()); this._.state = Object.assign({}, args.initial); this._event$ = args.event$ || new Subject(); this.event$ = this._event$.pipe(takeUntil(this.dispose$), map((e) => this.toDispatchEvent(e)), share()); } static create(args) { return new Store(args); } dispose() { this._dispose$.next(); this._dispose$.complete(); } get isDisposed() { return this._dispose$.isStopped; } get state() { return Object.assign({}, this._.state); } dispatch(event) { this._event$.next(event); return this; } on(type) { return this.event$.pipe(filter((e) => e.type === type), map((e) => e)); } toDispatchEvent(event) { const { type, payload } = event; const from = this.state; const result = { type, payload, get state() { return Object.assign({}, from); }, change: (next) => { const to = typeof next !== 'function' ? Object.assign({}, next) : produce(from, (draft) => { next(draft); return undefined; }); let isCancelled = false; const change = { type, event, from, to }; this._changing$.next({ change, isCancelled, cancel: () => (isCancelled = true), }); if (isCancelled) { return result; } this._.state = to; this._changed$.next(change); return result; }, dispatch: (event) => { this.dispatch(event); return result; }, }; return result; } }