UNPKG

@connectv/core

Version:

agent-based reactive programming library for typescript/javascript

140 lines 4.95 kB
import createRandomTag from "../util/random-tag"; import { emission } from "../shared/emission"; import { map } from "../pin/map"; import { sink } from "../pin/sink"; import { group } from "../pin/group"; import { filter } from "../pin/filter"; import { Source } from "../pin/source"; import { Agent } from "./agent"; import { State } from "./state"; /** * * Represents non-keyed (simple) [deep states](https://connective.dev/docs/deep). * */ export class SimpleDeep extends Agent { /** * * @param stateOrAccessor underlying state of this deep state or a state tree accessor (for sub-states) * @param compare equality function used to detect changes. If state is passed as first argument this is ignored. * @param signature the signature of the state, to be overriden by child classes. * */ constructor(stateOrAccessor, compare, signature) { super(signature || { inputs: ['value'], outputs: ['value'] }); this.bound = false; this.reemit = new Source(); this.downPropageteKey = createRandomTag(); if (stateOrAccessor instanceof State) this.state = stateOrAccessor; else { this.accessor = stateOrAccessor; this.state = new State(this.accessor.initial, compare); this.accessor.get .to(map((_, done, __, context) => { context[this.downPropageteKey] = true; done(_); })) .to(this) .to(filter((_, done, __, context) => { const downPropagated = context[this.downPropageteKey]; delete context[this.downPropageteKey]; done(!downPropagated); })) .to(this.accessor.set); } } /** * * Creates a sub-state for given index/property. * [Read this](https://connective.dev/docs/deep) for more details * * @param index * @param factory the factory function to be used to construct the sub-state * */ sub(index, factory) { let initialized = false; let _this = this; let _factory = factory || ((accessor, compare) => new SimpleDeep(accessor, compare)); return _factory({ initial: (_this.value || [])[index], get: _this.output.to(map((v) => (v || [])[index])), set: sink((v, context) => { try { if (!_this.value) _this.value = []; if (this.accessor) { _this.value = (Array.isArray(_this.value)) ? Object.assign([], _this.value, { [index]: v }) : Object.assign({}, _this.value, { [index]: v }); } else { _this.value[index] = v; if (initialized) this.reemit.emit(emission(v, context)); else initialized = true; } } catch (_) { } }), bind(track) { return track(this.set.subscribe()); }, }, this.state.compare); } /** * * Allows reading or updating state's value directly. * */ get value() { return this.state.value; } set value(v) { this.state.value = v; } /** * * The equality function used by this deep state. Is used for change detection. * */ get compare() { return this.state.compare; } /** * * Shortcut for `.in('value')`, on which the state receives new values. * [Read this](https://connective.dev/docs/state#signature) for more details. * */ get input() { return this.in('value'); } /** * * Shortcut for `.out('value')`, on which the state emits new values. * [Read this](https://connective.dev/docs/state#signature) for more details. * */ get output() { return this.out('value'); } /** * * Binds the underlying state. If this is a sub-state, it will also * allow 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-data) * for more details and examples. * */ bind() { if (!this.bound) { if (this.accessor) this.accessor.bind(sub => this.track(sub)); else this.track(this.output.subscribe()); this.bound = true; } return this; } createOutput(_) { this.checkOutput(_); return group(this.input.to(this.state), this.reemit.to(map(() => this.value))); } createEntries() { return [this.input]; } createExits() { return [this.output]; } } //# sourceMappingURL=simple-deep.js.map