Skip to content
Snippets Groups Projects
Commit 248c6c07 authored by David Sehnal's avatar David Sehnal
Browse files

wip plugin

parent ce331e48
Branches
Tags
No related merge requests found
Showing with 281 additions and 79 deletions
......@@ -6,7 +6,9 @@
export * from './behavior/behavior'
import * as Data from './behavior/data'
import * as Representation from './behavior/representation'
export const PluginBehaviors = {
Data
Data,
Representation
}
\ No newline at end of file
......@@ -10,6 +10,7 @@ import { Transformer } from 'mol-state';
import { Task } from 'mol-task';
import { PluginContext } from 'mol-plugin/context';
import { PluginCommand } from '../command';
import { Observable } from 'rxjs';
export { PluginBehavior }
......@@ -29,14 +30,14 @@ namespace PluginBehavior {
ctor: Ctor<P>,
label?: (params: P) => { label: string, description?: string },
display: { name: string, description?: string },
params?: Transformer.Definition<SO.Root, SO.Behavior, P>['params']
params?: Transformer.Definition<SO.BehaviorRoot, SO.Behavior, P>['params']
}
export function create<P>(params: CreateParams<P>) {
return PluginStateTransform.Create<SO.Root, SO.Behavior, P>({
return PluginStateTransform.Create<SO.BehaviorRoot, SO.Behavior, P>({
name: params.name,
display: params.display,
from: [SO.Root],
from: [SO.BehaviorRoot],
to: [SO.Behavior],
params: params.params,
apply({ params: p }, ctx: PluginContext) {
......@@ -53,7 +54,7 @@ namespace PluginBehavior {
});
}
export function commandHandler<T>(cmd: PluginCommand<T>, action: (data: T, ctx: PluginContext) => void | Promise<void>) {
export function simpleCommandHandler<T>(cmd: PluginCommand<T>, action: (data: T, ctx: PluginContext) => void | Promise<void>) {
return class implements PluginBehavior<undefined> {
private sub: PluginCommand.Subscription | undefined = void 0;
register(): void {
......@@ -66,4 +67,25 @@ namespace PluginBehavior {
constructor(private ctx: PluginContext) { }
}
}
export abstract class Handler implements PluginBehavior<undefined> {
private subs: PluginCommand.Subscription[] = [];
protected subscribeCommand<T>(cmd: PluginCommand<T>, action: PluginCommand.Action<T>) {
this.subs.push(cmd.subscribe(this.ctx, action));
}
protected subscribeObservable<T>(o: Observable<T>, action: (v: T) => void) {
this.subs.push(o.subscribe(action));
}
protected track<T>(sub: PluginCommand.Subscription) {
this.subs.push(sub);
}
abstract register(): void;
unregister() {
for (const s of this.subs) s.unsubscribe();
this.subs = [];
}
constructor(protected ctx: PluginContext) {
}
}
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
......@@ -8,28 +7,14 @@
import { PluginBehavior } from './behavior';
import { PluginCommands } from 'mol-plugin/command';
// export class SetCurrentObject implements PluginBehavior<undefined> {
// private sub: PluginCommand.Subscription | undefined = void 0;
// register(): void {
// this.sub = PluginCommands.Data.SetCurrentObject.subscribe(this.ctx, ({ ref }) => this.ctx.state.data.setCurrent(ref));
// }
// unregister(): void {
// if (this.sub) this.sub.unsubscribe();
// this.sub = void 0;
// }
// constructor(private ctx: PluginContext) { }
// }
export const SetCurrentObject = PluginBehavior.create({
name: 'set-current-data-object-behavior',
ctor: PluginBehavior.commandHandler(PluginCommands.Data.SetCurrentObject, ({ ref }, ctx) => ctx.state.data.setCurrent(ref)),
ctor: PluginBehavior.simpleCommandHandler(PluginCommands.Data.SetCurrentObject, ({ ref }, ctx) => ctx.state.data.setCurrent(ref)),
display: { name: 'Set Current Handler' }
});
export const Update = PluginBehavior.create({
name: 'update-data-behavior',
ctor: PluginBehavior.commandHandler(PluginCommands.Data.Update, ({ tree }, ctx) => ctx.runTask(ctx.state.data.update(tree))),
ctor: PluginBehavior.simpleCommandHandler(PluginCommands.Data.Update, ({ tree }, ctx) => ctx.runTask(ctx.state.data.update(tree))),
display: { name: 'Update Data Handler' }
});
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PluginBehavior } from './behavior';
import { PluginStateObjects as SO } from '../state/objects';
class _AddRepresentationToCanvas extends PluginBehavior.Handler {
register(): void {
this.subscribeObservable(this.ctx.events.state.data.object.created, o => {
if (!SO.StructureRepresentation3D.is(o.obj)) return;
this.ctx.canvas3d.add(o.obj.data.repr);
this.ctx.canvas3d.requestDraw(true);
});
this.subscribeObservable(this.ctx.events.state.data.object.updated, o => {
const oo = o.obj;
if (!SO.StructureRepresentation3D.is(oo)) return;
this.ctx.canvas3d.add(oo.data.repr);
this.ctx.canvas3d.requestDraw(true);
});
this.subscribeObservable(this.ctx.events.state.data.object.removed, o => {
const oo = o.obj;
console.log('removed', o.ref, oo && oo.type);
if (!SO.StructureRepresentation3D.is(oo)) return;
this.ctx.canvas3d.remove(oo.data.repr);
console.log('removed from canvas', o.ref);
this.ctx.canvas3d.requestDraw(true);
oo.data.repr.destroy();
});
this.subscribeObservable(this.ctx.events.state.data.object.replaced, o => {
if (o.oldObj && SO.StructureRepresentation3D.is(o.oldObj)) {
this.ctx.canvas3d.remove(o.oldObj.data.repr);
this.ctx.canvas3d.requestDraw(true);
o.oldObj.data.repr.destroy();
}
if (o.newObj && SO.StructureRepresentation3D.is(o.newObj)) {
this.ctx.canvas3d.add(o.newObj.data.repr);
this.ctx.canvas3d.requestDraw(true);
}
});
}
}
export const AddRepresentationToCanvas = PluginBehavior.create({
name: 'add-representation-to-canvas',
ctor: _AddRepresentationToCanvas,
display: { name: 'Add Representation To Canvas' }
});
\ No newline at end of file
......@@ -87,7 +87,7 @@ namespace PluginCommand {
/** Resolves after all actions have completed */
dispatch<T>(cmd: PluginCommand<T> | Id, params: T) {
return new Promise<void>((resolve, reject) => {
if (!this.disposing) {
if (this.disposing) {
reject('disposed');
return;
}
......
......@@ -4,15 +4,17 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { StateTree, StateSelection, Transformer } from 'mol-state';
import { StateTree, StateSelection, Transformer, Transform } from 'mol-state';
import Canvas3D from 'mol-canvas3d/canvas3d';
import { StateTransforms } from './state/transforms';
import { PluginStateObjects as SO } from './state/objects';
import { RxEventHelper } from 'mol-util/rx-event-helper';
import { PluginState } from './state';
import { MolScriptBuilder } from 'mol-script/language/builder';
import { PluginCommand } from './command';
import { PluginCommand, PluginCommands } from './command';
import { Task } from 'mol-task';
import { merge } from 'rxjs';
import { PluginBehaviors } from './behavior';
export class PluginContext {
private disposed = false;
......@@ -22,10 +24,17 @@ export class PluginContext {
readonly commands = new PluginCommand.Manager();
readonly events = {
data: this.state.data.context.events
state: {
data: this.state.data.context.events,
behavior: this.state.behavior.context.events
}
};
readonly behaviors = {
state: {
data: this.state.data.context.behaviors,
behavior: this.state.behavior.context.behaviors
},
command: this.commands.behaviour
};
......@@ -66,6 +75,21 @@ export class PluginContext {
this.disposed = true;
}
async _test_initBehaviours() {
const tree = StateTree.build(this.state.behavior.tree)
.toRoot().apply(PluginBehaviors.Data.SetCurrentObject)
.and().toRoot().apply(PluginBehaviors.Data.Update)
.and().toRoot().apply(PluginBehaviors.Representation.AddRepresentationToCanvas)
.getTree();
await this.state.updateBehaviour(tree);
}
_test_applyTransform(a: Transform.Ref, transformer: Transformer, params: any) {
const tree = StateTree.build(this.state.data.tree).to(a).apply(transformer, params).getTree();
PluginCommands.Data.Update.dispatch(this, { tree });
}
_test_createState(url: string) {
const b = StateTree.build(this.state.data.tree);
......@@ -94,23 +118,27 @@ export class PluginContext {
}
private initEvents() {
this.state.data.context.events.object.created.subscribe(o => {
if (!SO.StructureRepresentation3D.is(o.obj)) return;
console.log('adding repr', o.obj.data.repr);
this.canvas3d.add(o.obj.data.repr);
this.canvas3d.requestDraw(true);
merge(this.events.state.data.object.created, this.events.state.behavior.object.created).subscribe(o => {
console.log('creating', o.obj.type);
if (!SO.Behavior.is(o.obj)) return;
o.obj.data.register();
});
this.state.data.context.events.object.updated.subscribe(o => {
const oo = o.obj;
if (!SO.StructureRepresentation3D.is(oo)) return;
console.log('updating repr', oo.data.repr);
this.canvas3d.add(oo.data.repr);
this.canvas3d.requestDraw(true);
merge(this.events.state.data.object.removed, this.events.state.behavior.object.removed).subscribe(o => {
if (!SO.Behavior.is(o.obj)) return;
o.obj.data.unregister();
});
merge(this.events.state.data.object.replaced, this.events.state.behavior.object.replaced).subscribe(o => {
if (o.oldObj && SO.Behavior.is(o.oldObj)) o.oldObj.data.unregister();
if (o.newObj && SO.Behavior.is(o.newObj)) o.newObj.data.register();
});
}
_test_centerView() {
const sel = StateSelection.select('structure', this.state.data);
const sel = StateSelection.select(StateSelection.root().subtree().ofType(SO.Structure.type), this.state.data);
if (!sel.length) return;
const center = (sel[0].obj! as SO.Structure).data.boundary.sphere.center;
console.log({ sel, center, rc: this.canvas3d.reprCount });
this.canvas3d.center(center);
......@@ -135,6 +163,8 @@ export class PluginContext {
constructor() {
this.initEvents();
this._test_initBehaviours();
}
// logger = ;
......
......@@ -12,41 +12,47 @@ export { PluginState }
class PluginState {
readonly data: State;
readonly behaviour: State;
readonly canvas = {
camera: CombinedCamera.create()
};
readonly behavior: State;
getSnapshot(): PluginState.Snapshot {
return {
data: this.data.getSnapshot(),
behaviour: this.behaviour.getSnapshot(),
canvas: {
camera: { ...this.canvas.camera }
behaviour: this.behavior.getSnapshot(),
canvas3d: {
camera: { ...this.plugin.canvas3d.camera }
}
};
}
setSnapshot(snapshot: PluginState.Snapshot) {
// TODO events
this.behaviour.setSnapshot(snapshot.behaviour);
this.behavior.setSnapshot(snapshot.behaviour);
this.data.setSnapshot(snapshot.data);
this.canvas.camera = { ...snapshot.canvas.camera };
// TODO: handle camera
// console.log({ old: { ...this.plugin.canvas3d.camera }, new: snapshot.canvas3d.camera });
// CombinedCamera.copy(snapshot.canvas3d.camera, this.plugin.canvas3d.camera);
// CombinedCamera.update(this.plugin.canvas3d.camera);
// this.plugin.canvas3d.center
// console.log({ copied: { ...this.plugin.canvas3d.camera } });
// this.plugin.canvas3d.requestDraw(true);
// console.log('updated camera');
}
async updateData(tree: StateTree) {
// TODO: "task observer"
await this.data.update(tree).run(p => console.log(p), 250);
updateData(tree: StateTree) {
return this.plugin.runTask(this.data.update(tree));
}
updateBehaviour(tree: StateTree) {
return this.plugin.runTask(this.behavior.update(tree));
}
dispose() {
this.data.dispose();
}
constructor(globalContext: unknown) {
this.data = State.create(new SO.Root({ label: 'Root' }, { }), { globalContext });
this.behaviour = State.create(new SO.Root({ label: 'Root' }, { }), { globalContext });
constructor(private plugin: import('./context').PluginContext) {
this.data = State.create(new SO.DataRoot({ label: 'Root' }, { }), { globalContext: plugin });
this.behavior = State.create(new SO.BehaviorRoot({ label: 'Root' }, { }), { globalContext: plugin });
}
}
......@@ -54,6 +60,8 @@ namespace PluginState {
export interface Snapshot {
data: State.Snapshot,
behaviour: State.Snapshot,
canvas: PluginState['canvas']
canvas3d: {
camera: CombinedCamera
}
}
}
......@@ -12,7 +12,9 @@ import { StructureRepresentation } from 'mol-repr/structure/index';
const _create = PluginStateObject.Create
namespace PluginStateObjects {
export class Root extends _create({ name: 'Root', shortName: 'R', typeClass: 'Root', description: 'Where everything begins.' }) { }
export class DataRoot extends _create({ name: 'Root', shortName: 'R', typeClass: 'Root', description: 'Where everything begins.' }) { }
export class BehaviorRoot extends _create({ name: 'Root', shortName: 'R', typeClass: 'Root', description: 'Where everything begins.' }) { }
export class Group extends _create({ name: 'Group', shortName: 'G', typeClass: 'Group', description: 'A group on entities.' }) { }
export class Behavior extends _create<import('../behavior').PluginBehavior>({ name: 'Behavior', shortName: 'B', typeClass: 'Behavior', description: 'Modifies plugin functionality.' }) { }
......
......@@ -13,15 +13,18 @@ import { ParamDefinition as PD } from 'mol-util/param-definition';
export { Download }
namespace Download { export interface Params { url: string, isBinary?: boolean, label?: string } }
const Download = PluginStateTransform.Create<SO.Root, SO.Data.String | SO.Data.Binary, Download.Params>({
const Download = PluginStateTransform.Create<SO.DataRoot, SO.Data.String | SO.Data.Binary, Download.Params>({
name: 'download',
display: {
name: 'Download',
description: 'Download string or binary data from the specified URL'
},
from: [SO.Root],
from: [SO.DataRoot],
to: [SO.Data.String, SO.Data.Binary],
params: {
default: () => ({
url: 'https://www.ebi.ac.uk/pdbe/static/entry/1cbs_updated.cif'
}),
controls: () => ({
url: PD.Text('URL', 'Resource URL. Must be the same domain or support CORS.', ''),
isBinary: PD.Boolean('Binary', 'If true, download data as binary (string otherwise)', false)
......
......@@ -15,6 +15,7 @@ export { CreateStructureRepresentation }
namespace CreateStructureRepresentation { export interface Params { } }
const CreateStructureRepresentation = PluginStateTransform.Create<SO.Structure, SO.StructureRepresentation3D, CreateStructureRepresentation.Params>({
name: 'create-structure-representation',
display: { name: 'Create 3D Representation' },
from: [SO.Structure],
to: [SO.StructureRepresentation3D],
apply({ a, params }) {
......
......@@ -6,6 +6,8 @@
import * as React from 'react';
import { PluginContext } from '../context';
import { Transform, Transformer, StateObject } from 'mol-state';
import { ParametersComponent } from 'mol-app/component/parameters';
export class Controls extends React.Component<{ plugin: PluginContext }, { id: string }> {
state = { id: '1grm' };
......@@ -16,6 +18,16 @@ export class Controls extends React.Component<{ plugin: PluginContext }, { id: s
this.props.plugin._test_createState(url);
}
private _snap:any = void 0;
private getSnapshot = () => {
this._snap = this.props.plugin.state.getSnapshot();
console.log(btoa(JSON.stringify(this._snap)));
}
private setSnapshot = () => {
if (!this._snap) return;
this.props.plugin.state.setSnapshot(this._snap);
}
render() {
return <div>
<input type='text' defaultValue={this.state.id} onChange={e => this.setState({ id: e.currentTarget.value })} />
......@@ -23,6 +35,55 @@ export class Controls extends React.Component<{ plugin: PluginContext }, { id: s
<button onClick={() => this.props.plugin._test_centerView()}>Center View</button><br/>
<button onClick={() => this.props.plugin._test_nextModel()}>Next Model</button><br/>
<button onClick={() => this.props.plugin._test_playModels()}>Play Models</button><br/>
<hr />
<button onClick={this.getSnapshot}>Get Snapshot</button>
<button onClick={this.setSnapshot}>Set Snapshot</button>
</div>;
}
}
export class _test_CreateTransform extends React.Component<{ plugin: PluginContext, nodeRef: Transform.Ref, transformer: Transformer }, { params: any }> {
private getObj() {
const obj = this.props.plugin.state.data.objects.get(this.props.nodeRef)!;
return obj;
}
private getDefaultParams() {
const p = this.props.transformer.definition.params;
if (!p || !p.default) return { };
const obj = this.getObj();
if (!obj.obj) return { };
return p.default(obj.obj, this.props.plugin);
}
private getParamDef() {
const p = this.props.transformer.definition.params;
if (!p || !p.controls) return { };
const obj = this.getObj();
if (!obj.obj) return { };
return p.controls(obj.obj, this.props.plugin);
}
private create() {
console.log(this.props.transformer.definition.name, this.state.params);
this.props.plugin._test_applyTransform(this.props.nodeRef, this.props.transformer, this.state.params);
}
state = { params: this.getDefaultParams() }
render() {
const obj = this.getObj();
if (obj.state !== StateObject.StateType.Ok) {
// TODO filter this elsewhere
return <div />;
}
const t = this.props.transformer;
return <div key={`${this.props.nodeRef} ${this.props.transformer.id}`}>
<div style={{ borderBottom: '1px solid #999'}}>{(t.definition.display && t.definition.display.name) || t.definition.name}</div>
<ParametersComponent params={this.getParamDef()} values={this.state.params as any} onChange={params => this.setState({ params })} />
<button onClick={() => this.create()} style={{ width: '100%' }}>Create</button>
</div>
}
}
\ No newline at end of file
......@@ -6,17 +6,22 @@
import * as React from 'react';
import { PluginContext } from '../context';
import { Tree } from './tree';
import { StateTree } from './state-tree';
import { Viewport } from './viewport';
import { Controls } from './controls';
import { Controls, _test_CreateTransform } from './controls';
import { Transformer } from 'mol-state';
// TODO: base object with subscribe helpers
export class Plugin extends React.Component<{ plugin: PluginContext }, { }> {
render() {
return <div style={{ position: 'absolute', width: '100%', height: '100%' }}>
<div style={{ position: 'absolute', width: '250px', height: '100%' }}>
<Tree plugin={this.props.plugin} />
<div style={{ position: 'absolute', width: '350px', height: '100%', overflowY: 'scroll' }}>
<StateTree plugin={this.props.plugin} />
<hr />
<_test_CurrentObject plugin={this.props.plugin} />
</div>
<div style={{ position: 'absolute', left: '250px', right: '250px', height: '100%' }}>
<div style={{ position: 'absolute', left: '350px', right: '250px', height: '100%' }}>
<Viewport plugin={this.props.plugin} />
</div>
<div style={{ position: 'absolute', width: '250px', right: '0', height: '100%' }}>
......@@ -25,3 +30,27 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, { }> {
</div>;
}
}
export class _test_CurrentObject extends React.Component<{ plugin: PluginContext }, { }> {
componentWillMount() {
this.props.plugin.behaviors.state.data.currentObject.subscribe(() => this.forceUpdate());
}
render() {
const ref = this.props.plugin.behaviors.state.data.currentObject.value.ref;
// const n = this.props.plugin.state.data.tree.nodes.get(ref)!;
const obj = this.props.plugin.state.data.objects.get(ref)!;
const type = obj && obj.obj ? obj.obj.type : void 0;
const transforms = type
? Transformer.fromType(type)
: []
return <div>
Current Ref: {this.props.plugin.behaviors.state.data.currentObject.value.ref}
<hr />
{
transforms.map((t, i) => <_test_CreateTransform key={`${t.id} ${ref} ${i}`} plugin={this.props.plugin} transformer={t} nodeRef={ref} />)
}
</div>;
}
}
\ No newline at end of file
......@@ -8,21 +8,23 @@ import * as React from 'react';
import { PluginContext } from '../context';
import { PluginStateObject } from 'mol-plugin/state/base';
import { StateObject } from 'mol-state'
import { PluginCommands } from 'mol-plugin/command';
export class Tree extends React.Component<{ plugin: PluginContext }, { }> {
export class StateTree extends React.Component<{ plugin: PluginContext }, { }> {
componentWillMount() {
this.props.plugin.events.data.updated.subscribe(() => this.forceUpdate());
this.props.plugin.events.state.data.updated.subscribe(() => this.forceUpdate());
}
render() {
const n = this.props.plugin.state.data.tree.nodes.get(this.props.plugin.state.data.tree.rootRef)!;
// const n = this.props.plugin.state.data.tree.nodes.get(this.props.plugin.state.data.tree.rootRef)!;
const n = this.props.plugin.state.data.tree.rootRef;
return <div>
{n.children.map(c => <TreeNode plugin={this.props.plugin} nodeRef={c!} key={c} />)}
<StateTreeNode plugin={this.props.plugin} nodeRef={n} key={n} />
{ /* n.children.map(c => <StateTreeNode plugin={this.props.plugin} nodeRef={c!} key={c} />) */}
</div>;
}
}
export class TreeNode extends React.Component<{ plugin: PluginContext, nodeRef: string }, { }> {
export class StateTreeNode extends React.Component<{ plugin: PluginContext, nodeRef: string }, { }> {
render() {
const n = this.props.plugin.state.data.tree.nodes.get(this.props.nodeRef)!;
const obj = this.props.plugin.state.data.objects.get(this.props.nodeRef)!;
......@@ -33,10 +35,14 @@ export class TreeNode extends React.Component<{ plugin: PluginContext, nodeRef:
}
const props = obj.obj!.props as PluginStateObject.Props;
return <div style={{ borderLeft: '1px solid black', paddingLeft: '5px' }}>
{props.label} {props.description ? <small>{props.description}</small> : void 0}
<a href='#' onClick={e => {
e.preventDefault();
PluginCommands.Data.SetCurrentObject.dispatch(this.props.plugin, { ref: this.props.nodeRef });
}}>{props.label}</a>
{props.description ? <small>{props.description}</small> : void 0}
{n.children.size === 0
? void 0
: <div style={{ marginLeft: '10px' }}>{n.children.map(c => <TreeNode plugin={this.props.plugin} nodeRef={c!} key={c} />)}</div>
: <div style={{ marginLeft: '10px' }}>{n.children.map(c => <StateTreeNode plugin={this.props.plugin} nodeRef={c!} key={c} />)}</div>
}
</div>;
}
......
......@@ -43,8 +43,8 @@ class State {
setSnapshot(snapshot: State.Snapshot): void {
const tree = StateTree.fromJSON(snapshot.tree);
// TODO: support props
this.update(tree);
// TODO: support props and async
this.update(tree).run();
}
setCurrent(ref: Transform.Ref) {
......@@ -128,9 +128,10 @@ namespace State {
const roots = findUpdateRoots(ctx.objects, ctx.tree);
const deletes = findDeletes(ctx);
for (const d of deletes) {
const obj = ctx.objects.has(d) ? ctx.objects.get(d)!.obj : void 0;
ctx.objects.delete(d);
ctx.transformCache.delete(d);
ctx.stateCtx.events.object.removed.next({ ref: d });
ctx.stateCtx.events.object.removed.next({ ref: d, obj });
// TODO: handle current object change
}
......
......@@ -52,8 +52,8 @@ namespace StateTree {
export class Root implements Builder {
private state: State;
to<A extends StateObject>(ref: Transform.Ref) { return new To<A>(this.state, ref); }
toRoot<A extends StateObject>() { return new To<A>(this.state, this.state.tree.rootRef as any); }
to<A extends StateObject>(ref: Transform.Ref) { return new To<A>(this.state, ref, this); }
toRoot<A extends StateObject>() { return new To<A>(this.state, this.state.tree.rootRef as any, this); }
delete(ref: Transform.Ref) { this.state.tree.remove(ref); return this; }
getTree(): StateTree { return this.state.tree.asImmutable(); }
constructor(tree: StateTree) { this.state = { tree: ImmutableTree.asTransient(tree) } }
......@@ -63,12 +63,14 @@ namespace StateTree {
apply<T extends Transformer<A, any, any>>(tr: T, params?: Transformer.Params<T>, props?: Partial<Transform.Options>): To<Transformer.To<T>> {
const t = tr.apply(params, props);
this.state.tree.add(this.ref, t);
return new To(this.state, t.ref);
return new To(this.state, t.ref, this.root);
}
and() { return this.root; }
getTree(): StateTree { return this.state.tree.asImmutable(); }
constructor(private state: State, private ref: Transform.Ref) {
constructor(private state: State, private ref: Transform.Ref, private root: Root) {
if (!this.state.tree.nodes.has(ref)) {
throw new Error(`Could not find node '${ref}'.`);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment