@connectv/core
Version:
agent-based reactive programming library for typescript/javascript
146 lines • 5.97 kB
JavaScript
import { distinctUntilKeyChanged } from "rxjs/operators";
import { emission } from "../shared/emission";
import { diff } from "../util/keyed-array-diff";
import { map } from "../pin/map";
import { sink } from "../pin/sink";
import { group } from "../pin/group";
import { value } from "../pin/value";
import { pipe } from "../pin/pipe";
import { SimpleDeep } from "./simple-deep";
/**
*
* Represents a [keyed deep state](https://connective.dev/docs/deep#keyed-deep).
*
*/
export class KeyedDeep extends SimpleDeep {
/**
*
* @param stateOrAccessor underlying state of this deep state or a state tree accessor (for sub-states)
* @param keyfunc key function to be used to track entities within the state's value
* @param compare equality function used to detect changes. If state is passed as first argument this is ignored.
*
*/
constructor(stateOrAccessor, keyfunc, compare) {
super(stateOrAccessor, compare, {
inputs: ['value'],
outputs: ['value', 'changes']
});
this.keyfunc = keyfunc;
this._keyMap = {};
}
/**
*
* Creates a sub-state bound to entity identified by given key. Entity `x` is
* said to be identified by key `k` if `state.keyfunc(x) === k`.
*
* @param key the identifier of the entity to track
* @param factory the factory function to be used to construct the sub-state
*
*/
key(key, factory) {
let initialized = false;
let _this = this;
let _factory = factory || ((accessor, compare) => new SimpleDeep(accessor, compare));
return _factory({
initial: (_this._keyMap[key] || { item: undefined }).item
|| (Object.values(this.value) || []).find((i) => this.keyfunc(i) == key),
get: group(_this.changes, _this.reemit).to(map(() => (_this._keyMap[key] || { item: undefined }).item)),
set: sink((v, context) => {
try {
let _entry = _this._keyMap[key];
if (_entry) {
_entry.item = v;
if (_this.accessor) {
_this.value = (Array.isArray(_this.value)) ?
Object.assign([], _this.value, { [_entry.index]: v }) :
Object.assign({}, _this.value, { [_entry.index]: v });
}
else {
_this.value[_entry.index] = v;
if (initialized)
this.reemit.emit(emission(v, context));
else
initialized = true;
}
}
}
catch (err) { }
}),
bind(track) { return track(this.set.subscribe()); },
}, this.state.compare);
}
/**
*
* Returns a [pin](https://connective.dev/docs/pin) that reflects the reactive value of
* the index of entity identified by given key in the state's value. Entity `x` is said
* to be identified by key `k` if `state.keyfunc(x) === k`.
*
* @param key the key to identify target entity with
*
*/
index(key) {
let initial;
if (this._keyMap[key])
initial = this._keyMap[key].index;
else
initial = ((Object.entries(this.value) || []).find(([index, item]) => this.keyfunc(item) == key)
|| [-1, undefined])[0];
return group(value(initial), group(this.changes, this.reemit).to(map(() => (this._keyMap[key] || { index: -1 }).index))).to(pipe(distinctUntilKeyChanged('value')));
}
/**
*
* Will bind the underlying state, and cause deep change-detection to happen upon
* changes of the state value. [Read this](https://connective.dev/docs/deep#change-detection)
* for more information on deep change-detection.
*
* If this is a sub-state, also enables up-propagation
* of state value, causing the parent state to pick up changes made to the value of this
* sub-state. [Read this](https://connective.dev/docs/deep#two-way-keyed) for more details
* and examples.
*
*/
bind() {
super.bind();
this.track(this.changes.subscribe());
return this;
}
/**
*
* Keys that entities within the value of the state are identified with. Entity
* `x` is said to be indetified with key `k` if `state.keyfunc(x) === k`.
*
* **WARNING** the keys will not be calculcated unless deep change-detection is active.
* You can ensure deep change-detection is active by subscribing on `.changes` or
* calling `.bind()`. [Read this](https://connective.dev/docs/deep#change-detection)
* for more information on deep change-detection.
*
*/
get keys() {
return Object.keys(this._keyMap);
}
/**
*
* A [pin](https://connective.dev/docs/pin) that emits changes to this deep state's list value.
* These changes include entities being added to the list, removed from it or moved around in it.
* [Read this](https://connective.dev/docs/deep#change-detection) for more information on
* deep change-detection.
*
*/
get changes() { return this.out('changes'); }
createOutput(label) {
if (label === 'changes') {
this.output; // --> wire output before hand
let initial = true;
return this.state.to(map((value, done) => {
const result = diff(value, this._keyMap, this.keyfunc);
this._keyMap = result.newKeyMap;
let changes = Object.assign({}, result.changes, { initial });
initial = false;
done(changes);
}));
}
else
return super.createOutput(label);
}
}
//# sourceMappingURL=keyed-deep.js.map