@connectv/core
Version:
agent-based reactive programming library for typescript/javascript
147 lines • 4.31 kB
JavaScript
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