UNPKG

@connectv/core

Version:

agent-based reactive programming library for typescript/javascript

147 lines 4.31 kB
import { isBindable } from '../shared/bindable'; import { Agent } from './agent'; import { ChildNotDefined } from './errors/child-not-defined.error'; import { ChildIsNotPin, ChildIsNotAgent } from './errors/child-type-mismatch.error'; /** * * Represents [compositions](https://connective.dev/docs/composition). This class * is to be used directly if you want to create class-based compositions, otherwise * utilize [`composition()`](https://connective.dev/docs/composition) method. * */ export class Composition extends Agent { /** * * @param signature the signature of the composition * */ constructor(signature) { super(signature); this.init(); } /** * * Override this to modify the initialization process of a composition. * This function is called by parent's constructor, so if you want to * invoke `.build()` and `.wire()` after some child-class properties have been * initialized as well, you would need to override this function. This is a typical * scenario in case of parametric class-based compositions. * */ init() { this.build(); this.wire(); } /** * * Adds a child (pin or agent) to the composition. You can provide a name. If not, * the child will be named numerically based on the length of the children already added: * * ```typescript * build() { * this.add('myState', state()); * this.add(expr((x, y) => x * y)); * } * * wire() { * this.agent('myState'); // --> this is the defined state * this.agent(1); // --> this is the defined expr * } * ``` * * @param nameOrChild * @param child * */ add(nameOrChild, child) { if (!this._children) this._children = {}; if (!child) return this.add(`${Object.keys(this._children).length}`, nameOrChild); let _name = nameOrChild; this._children[_name] = child; if (isBindable(child)) this.toBind(child); return child; } /** * * @param name * @returns the child with given name. * @throws an error if no child with given name is defined. * */ child(name) { if (typeof name !== 'string') return this.child(name.toString()); if (this._children && name in this._children) return this._children[name]; throw new ChildNotDefined(name); } /** * * @param name * @returns the pin child with given name. * @throws an error if no child with given name is defined or if it is not a pin. * */ pin(name) { let _child = this.child(name); if (_child instanceof Agent) throw new ChildIsNotPin(name.toString()); return _child; } /** * * @param name * @returns the child agent with given name. * @throws an error if no child with given name is defined or if it is not an agent. * */ agent(name) { let _child = this.child(name); if (!(_child instanceof Agent)) throw new ChildIsNotAgent(name.toString()); return _child; } /** * * Registers a `Bindable` that will be bound when `.bind()` is called on this composition. * * @param bindable * */ toBind(bindable) { if (!this._bindables) this._bindables = []; this._bindables.push(bindable); return this; } /** * * Binds all registered `Bindable`s, including bindable children like * [states](https://connective.dev/docs/state) and * [sinks](https://connective.dev/docs/sink). * */ bind() { if (this._bindables) this._bindables.forEach(bindable => bindable.bind()); return this; } /** * * @note `.clear()` on `Composition` also clears all registered children. * */ clear() { if (this._children) { Object.values(this._children).forEach(child => child.clear()); this._children = undefined; } if (this._bindables) this._bindables = undefined; return super.clear(); } } //# sourceMappingURL=composition.js.map