rxdeep
Version:
RxJS deep state management
108 lines • 4.44 kB
JavaScript
import { Observable, Subject } from 'rxjs';
import { map, filter, multicast, refCount, startWith, tap, distinctUntilChanged } from 'rxjs/operators';
import { State } from './state';
import { isLeaf } from './types/changes';
import { Watcher } from './util/watcher';
import { trace } from './trace';
export class KeyedState extends Observable {
constructor(state, keyfunc) {
super((observer) => {
return this._changes.pipe(map(([change, _]) => change.value || []), startWith(this.value)).subscribe(observer);
});
this.state = state;
this.keyfunc = keyfunc;
this._value = [];
this._watcher = new Watcher(state.value, keyfunc);
this._value = state.value || [];
this._changesub = new Subject();
this._changes = this.state.downstream.pipe(map(change => [change, this._watcher.changes(change.value)]), map(([change, listChanges]) => {
if (listChanges.moves.length > 0 && !isLeaf(change.trace)) {
const mapping = listChanges.moves.reduce((total, move) => {
total[move.oldIndex] = move.newIndex;
return total;
}, {});
const _tr = { subs: Object.assign({}, change.trace.subs) };
Object.entries(mapping).forEach(([src, dest]) => {
const subtrace = trace(this._value[src], change.value[dest]);
if (subtrace) {
_tr.subs[dest] = subtrace;
}
else {
delete _tr.subs[dest];
}
});
return [{
value: change.value,
trace: _tr
}, listChanges];
}
return [change, listChanges];
}), tap(([change]) => {
this._value = change.value || [];
}), multicast(() => this._changesub), refCount());
}
next(t) {
this.state.upstream.next({ value: t, trace: { from: this.value, to: t } });
}
error(err) { this.state.upstream.error(err); }
complete() { this._changesub.complete(); }
get value() { return this._value; }
set value(t) { this.next(t); }
key(key) {
var _a;
const sub = new State((_a = this._watcher.keymap[key]) === null || _a === void 0 ? void 0 : _a.item, this.keyDownstream(key, () => sub.value), this.keyUpstream(key));
return sub;
}
keyDownstream(key, current) {
return this._changes.pipe(map(([change, _]) => ({
trace: change.trace,
entry: this._watcher.keymap[key],
})), filter(change => {
/* istanbul ignore next */
if (isLeaf(change.trace)) {
return current() !== change.entry.item;
}
else {
return (!change.entry && !!current())
|| (change.entry && change.entry.index in change.trace.subs);
}
}), map(change => {
var _a;
return ({
value: (_a = change.entry) === null || _a === void 0 ? void 0 : _a.item,
trace: isLeaf(change.trace) || !change.entry ?
undefined :
change.trace.subs[change.entry.index]
});
}));
}
keyUpstream(key) {
return {
next: change => {
const entry = this._watcher.keymap[key];
this._value[entry.index] = change.value;
this.state.upstream.next({
value: this._value,
trace: {
subs: {
[entry.index]: change.trace
}
}
});
},
error: err => this.state.upstream.error(err),
complete: () => { },
};
}
index(key) {
var _a;
return this._changes.pipe(map(() => { var _a; return (_a = this._watcher.keymap[key]) === null || _a === void 0 ? void 0 : _a.index; }), startWith((_a = this._watcher.keymap[key]) === null || _a === void 0 ? void 0 : _a.index), distinctUntilChanged());
}
changes() {
return this._changes.pipe(map(([_, listChanges]) => listChanges));
}
}
export function keyed(state, keyfunc) {
return new KeyedState(state, keyfunc);
}
//# sourceMappingURL=keyed.js.map