From ebf74e6580aa211e2fa384c5bd8b2998f5e72e5d Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Sat, 9 Mar 2019 21:16:13 +0100 Subject: [PATCH] mol-state: better typing for cells and selections --- src/apps/basic-wrapper/index.ts | 6 +- .../structure-representation-interaction.ts | 2 +- .../skin/base/components/transformer.scss | 1 + src/mol-plugin/skin/base/icons.scss | 6 +- src/mol-plugin/state/actions/structure.ts | 19 ++--- src/mol-plugin/state/animation/built-in.ts | 76 ++++++++----------- src/mol-plugin/ui/controls.tsx | 3 +- src/mol-plugin/ui/state.tsx | 2 +- src/mol-state/action.ts | 2 +- src/mol-state/object.ts | 9 ++- src/mol-state/state.ts | 2 +- src/mol-state/state/builder.ts | 17 +++-- src/mol-state/state/selection.ts | 29 ++++++- src/mol-state/transform.ts | 10 +-- src/mol-state/transformer.ts | 6 +- 15 files changed, 106 insertions(+), 84 deletions(-) diff --git a/src/apps/basic-wrapper/index.ts b/src/apps/basic-wrapper/index.ts index 383b8736c..aaa87f3d9 100644 --- a/src/apps/basic-wrapper/index.ts +++ b/src/apps/basic-wrapper/index.ts @@ -11,7 +11,7 @@ import { PluginCommands } from 'mol-plugin/command'; import { StateTransforms } from 'mol-plugin/state/transforms'; import { StructureRepresentation3DHelpers } from 'mol-plugin/state/transforms/representation'; import { Color } from 'mol-util/color'; -import { PluginStateObject as PSO, PluginStateObject } from 'mol-plugin/state/objects'; +import { PluginStateObject as PSO } from 'mol-plugin/state/objects'; import { AnimateModelIndex } from 'mol-plugin/state/animation/built-in'; import { StateBuilder } from 'mol-state'; import { StripedResidues } from './coloring'; @@ -123,12 +123,12 @@ class BasicWrapper { applyStripes: async () => { const state = this.plugin.state.dataState; - const visuals = state.selectQ(q => q.ofType(PluginStateObject.Molecule.Structure.Representation3D).filter(c => c.transform.transformer === StateTransforms.Representation.StructureRepresentation3D)); + const visuals = state.selectQ(q => q.ofTransformer(StateTransforms.Representation.StructureRepresentation3D)); const tree = state.build(); const colorTheme = { name: StripedResidues.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(StripedResidues.Descriptor.name).defaultValues }; for (const v of visuals) { - tree.to(v.transform.ref).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme })); + tree.to(v).update(old => ({ ...old, colorTheme })); } await PluginCommands.State.Update.dispatch(this.plugin, { state, tree }); diff --git a/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts b/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts index 959baa33b..497d14205 100644 --- a/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts +++ b/src/mol-plugin/behavior/dynamic/selection/structure-representation-interaction.ts @@ -55,7 +55,7 @@ export class StructureRepresentationInteractionBehavior extends PluginBehavior.W const refs = StateSelection.findUniqueTagsInSubtree(tree, cell.transform.ref, TagSet); if (!refs['structure-interaction-group']) { - refs['structure-interaction-group'] = builder.to(cell.transform.ref).group(StateTransforms.Misc.CreateGroup, + refs['structure-interaction-group'] = builder.to(cell).group(StateTransforms.Misc.CreateGroup, { label: 'Current Interaction' }, { props: { tag: Tags.Group } }).ref; } diff --git a/src/mol-plugin/skin/base/components/transformer.scss b/src/mol-plugin/skin/base/components/transformer.scss index f5316f080..8fe043e01 100644 --- a/src/mol-plugin/skin/base/components/transformer.scss +++ b/src/mol-plugin/skin/base/components/transformer.scss @@ -97,6 +97,7 @@ left: 0; top: 0; width: $row-height; + padding: 0; } .msp-transform-default-params:hover { diff --git a/src/mol-plugin/skin/base/icons.scss b/src/mol-plugin/skin/base/icons.scss index c0d183795..2c4c0590c 100644 --- a/src/mol-plugin/skin/base/icons.scss +++ b/src/mol-plugin/skin/base/icons.scss @@ -208,4 +208,8 @@ .msp-icon-code:before { content: "\e834"; -} \ No newline at end of file +} + +.msp-icon-floppy:before { + content: "\e8d0"; +} diff --git a/src/mol-plugin/state/actions/structure.ts b/src/mol-plugin/state/actions/structure.ts index 52284f8b2..76b4b0d9f 100644 --- a/src/mol-plugin/state/actions/structure.ts +++ b/src/mol-plugin/state/actions/structure.ts @@ -245,27 +245,24 @@ export const UpdateTrajectory = StateAction.build({ by: PD.asOptional(PD.Numeric(1, { min: -1, max: 1, step: 1 })) } })(({ params, state }) => { - const models = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Model) - .filter(c => c.transform.transformer === StateTransforms.Model.ModelFromTrajectory)); + const models = state.selectQ(q => q.ofTransformer(StateTransforms.Model.ModelFromTrajectory)); const update = state.build(); if (params.action === 'reset') { for (const m of models) { - update.to(m.transform.ref).update(StateTransforms.Model.ModelFromTrajectory, - () => ({ modelIndex: 0 })); + update.to(m).update({ modelIndex: 0 }); } } else { for (const m of models) { const parent = StateSelection.findAncestorOfType(state.tree, state.cells, m.transform.ref, [PluginStateObject.Molecule.Trajectory]); if (!parent || !parent.obj) continue; - const traj = parent.obj as PluginStateObject.Molecule.Trajectory; - update.to(m.transform.ref).update(StateTransforms.Model.ModelFromTrajectory, - old => { - let modelIndex = (old.modelIndex + params.by!) % traj.data.length; - if (modelIndex < 0) modelIndex += traj.data.length; - return { modelIndex }; - }); + const traj = parent.obj; + update.to(m).update(old => { + let modelIndex = (old.modelIndex + params.by!) % traj.data.length; + if (modelIndex < 0) modelIndex += traj.data.length; + return { modelIndex }; + }); } } diff --git a/src/mol-plugin/state/animation/built-in.ts b/src/mol-plugin/state/animation/built-in.ts index 123fcbfc1..f8211ea44 100644 --- a/src/mol-plugin/state/animation/built-in.ts +++ b/src/mol-plugin/state/animation/built-in.ts @@ -30,8 +30,7 @@ export const AnimateModelIndex = PluginStateAnimation.create({ } const state = ctx.plugin.state.dataState; - const models = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Model) - .filter(c => c.transform.transformer === StateTransforms.Model.ModelFromTrajectory)); + const models = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Model.ModelFromTrajectory)); if (models.length === 0) { // nothing more to do here @@ -47,37 +46,36 @@ export const AnimateModelIndex = PluginStateAnimation.create({ for (const m of models) { const parent = StateSelection.findAncestorOfType(state.tree, state.cells, m.transform.ref, [PluginStateObject.Molecule.Trajectory]); if (!parent || !parent.obj) continue; - const traj = parent.obj as PluginStateObject.Molecule.Trajectory; - update.to(m.transform.ref).update(StateTransforms.Model.ModelFromTrajectory, - old => { - const len = traj.data.length; - if (len !== 1) { - allSingles = false; - } else { + const traj = parent.obj; + update.to(m).update(old => { + const len = traj.data.length; + if (len !== 1) { + allSingles = false; + } else { + return old; + } + let dir: -1 | 1 = 1; + if (params.mode.name === 'once') { + dir = params.mode.params.direction === 'backward' ? -1 : 1; + // if we are at start or end already, do nothing. + if ((dir === -1 && old.modelIndex === 0) || (dir === 1 && old.modelIndex === len - 1)) { + isEnd = true; return old; } - let dir: -1 | 1 = 1; - if (params.mode.name === 'once') { - dir = params.mode.params.direction === 'backward' ? -1 : 1; - // if we are at start or end already, do nothing. - if ((dir === -1 && old.modelIndex === 0) || (dir === 1 && old.modelIndex === len - 1)) { - isEnd = true; - return old; - } - } else if (params.mode.name === 'palindrome') { - if (old.modelIndex === 0) dir = 1; - else if (old.modelIndex === len - 1) dir = -1; - else dir = palindromeDirections[m.transform.ref] || 1; - } - palindromeDirections[m.transform.ref] = dir; + } else if (params.mode.name === 'palindrome') { + if (old.modelIndex === 0) dir = 1; + else if (old.modelIndex === len - 1) dir = -1; + else dir = palindromeDirections[m.transform.ref] || 1; + } + palindromeDirections[m.transform.ref] = dir; - let modelIndex = (old.modelIndex + dir) % len; - if (modelIndex < 0) modelIndex += len; + let modelIndex = (old.modelIndex + dir) % len; + if (modelIndex < 0) modelIndex += len; - isEnd = isEnd || (dir === -1 && modelIndex === 0) || (dir === 1 && modelIndex === len - 1); + isEnd = isEnd || (dir === -1 && modelIndex === 0) || (dir === 1 && modelIndex === len - 1); - return { modelIndex }; - }); + return { modelIndex }; + }); } await PluginCommands.State.Update.dispatch(ctx.plugin, { state, tree: update, options: { doNotLogTiming: true } }); @@ -102,13 +100,11 @@ export const AnimateAssemblyUnwind = PluginStateAnimation.create({ const update = state.build(); let changed = false; for (const r of reprs) { - const unwinds = state.select(StateSelection.Generators.byValue(r) - .children() - .filter(c => c.transform.transformer === StateTransforms.Representation.UnwindStructureAssemblyRepresentation3D)); + const unwinds = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.UnwindStructureAssemblyRepresentation3D, r.transform.ref)); if (unwinds.length > 0) continue; changed = true; - update.to(r.transform.ref) + update.to(r) .apply(StateTransforms.Representation.UnwindStructureAssemblyRepresentation3D, { t: 0 }, { props: { tag: 'animate-assembly-unwind' } }); } @@ -128,11 +124,9 @@ export const AnimateAssemblyUnwind = PluginStateAnimation.create({ }, async apply(animState, t, ctx) { const state = ctx.plugin.state.dataState; - const anims = state.selectQ(q => q.ofType(PluginStateObject.Molecule.Structure.Representation3DState) - .filter(c => c.transform.transformer === StateTransforms.Representation.UnwindStructureAssemblyRepresentation3D)); + const anims = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.UnwindStructureAssemblyRepresentation3D)); if (anims.length === 0) { - // nothing more to do here return { kind: 'finished' }; } @@ -142,7 +136,7 @@ export const AnimateAssemblyUnwind = PluginStateAnimation.create({ const newTime = (animState.t + d) % 1; for (const m of anims) { - update.to(m.transform.ref).update(StateTransforms.Representation.UnwindStructureAssemblyRepresentation3D, _ => ({ t: newTime })); + update.to(m).update({ t: newTime }); } await PluginCommands.State.Update.dispatch(ctx.plugin, { state, tree: update, options: { doNotLogTiming: true } }); @@ -165,9 +159,7 @@ export const AnimateUnitsExplode = PluginStateAnimation.create({ const update = state.build(); let changed = false; for (const r of reprs) { - const explodes = state.select(StateSelection.Generators.byValue(r) - .children() - .filter(c => c.transform.transformer === StateTransforms.Representation.ExplodeStructureRepresentation3D)); + const explodes = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.ExplodeStructureRepresentation3D, r.transform.ref)); if (explodes.length > 0) continue; changed = true; @@ -191,11 +183,9 @@ export const AnimateUnitsExplode = PluginStateAnimation.create({ }, async apply(animState, t, ctx) { const state = ctx.plugin.state.dataState; - const anims = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Structure.Representation3DState) - .filter(c => c.transform.transformer === StateTransforms.Representation.ExplodeStructureRepresentation3D)); + const anims = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Representation.ExplodeStructureRepresentation3D)); if (anims.length === 0) { - // nothing more to do here return { kind: 'finished' }; } @@ -205,7 +195,7 @@ export const AnimateUnitsExplode = PluginStateAnimation.create({ const newTime = (animState.t + d) % 1; for (const m of anims) { - update.to(m.transform.ref).update(StateTransforms.Representation.ExplodeStructureRepresentation3D, _ => ({ t: newTime })); + update.to(m).update({ t: newTime }); } await PluginCommands.State.Update.dispatch(ctx.plugin, { state, tree: update, options: { doNotLogTiming: true } }); diff --git a/src/mol-plugin/ui/controls.tsx b/src/mol-plugin/ui/controls.tsx index 0bc8ee24e..7985f1754 100644 --- a/src/mol-plugin/ui/controls.tsx +++ b/src/mol-plugin/ui/controls.tsx @@ -22,8 +22,7 @@ export class TrajectoryViewportControls extends PluginUIComponent<{}, { show: bo private update = () => { const state = this.plugin.state.dataState; - const models = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Model) - .filter(c => c.transform.transformer === StateTransforms.Model.ModelFromTrajectory)); + const models = state.selectQ(q => q.ofTransformer(StateTransforms.Model.ModelFromTrajectory)); if (models.length === 0) { this.setState({ show: false }); diff --git a/src/mol-plugin/ui/state.tsx b/src/mol-plugin/ui/state.tsx index eeba3b574..31265bb4d 100644 --- a/src/mol-plugin/ui/state.tsx +++ b/src/mol-plugin/ui/state.tsx @@ -93,7 +93,7 @@ class LocalStateSnapshots extends PluginUIComponent< }}/> <div className='msp-btn-row-group'> - <button className='msp-btn msp-btn-block msp-form-control' onClick={this.add}><Icon name='record' /> Save</button> + <button className='msp-btn msp-btn-block msp-form-control' onClick={this.add}><Icon name='floppy' /> Save</button> {/* <button className='msp-btn msp-btn-block msp-form-control' onClick={this.upload} disabled={this.state.isUploading}>Upload</button> */} <button className='msp-btn msp-btn-block msp-form-control' onClick={this.clear}>Clear</button> </div> diff --git a/src/mol-state/action.ts b/src/mol-state/action.ts index 31b597fc2..81baf5fc5 100644 --- a/src/mol-state/action.ts +++ b/src/mol-state/action.ts @@ -46,7 +46,7 @@ namespace StateAction { run(params: ApplyParams<A, P>, globalCtx: unknown): T | Task<T>, /** Test if the transform can be applied to a given node */ - isApplicable?(a: A, aTransform: StateTransform<any, A, any>, globalCtx: unknown): boolean + isApplicable?(a: A, aTransform: StateTransform<StateTransformer<any, A, any>>, globalCtx: unknown): boolean } export interface Definition<A extends StateObject = StateObject, T = any, P extends {} = {}> extends DefinitionBase<A, T, P> { diff --git a/src/mol-state/object.ts b/src/mol-state/object.ts index 612964518..b74efcf5e 100644 --- a/src/mol-state/object.ts +++ b/src/mol-state/object.ts @@ -8,7 +8,7 @@ import { UUID } from 'mol-util'; import { StateTransform } from './transform'; import { ParamDefinition } from 'mol-util/param-definition'; import { State } from './state'; -import { StateSelection } from 'mol-state'; +import { StateSelection, StateTransformer } from 'mol-state'; export { StateObject, StateObjectCell } @@ -55,8 +55,8 @@ namespace StateObject { }; } -interface StateObjectCell<T = StateObject> { - transform: StateTransform, +interface StateObjectCell<T extends StateObject = StateObject, F extends StateTransform<StateTransformer<any, T, any>> = StateTransform<StateTransformer<any, T, any>>> { + transform: F, // Which object was used as a parent to create data in this cell sourceRef: StateTransform.Ref | undefined, @@ -77,6 +77,9 @@ interface StateObjectCell<T = StateObject> { namespace StateObjectCell { export type Status = 'ok' | 'error' | 'pending' | 'processing' + export type Obj<C extends StateObjectCell> = C extends StateObjectCell<infer T> ? T : never + export type Transform<C extends StateObjectCell> = C extends StateObjectCell<any, infer T> ? T : never + export interface State { isHidden: boolean, isCollapsed: boolean diff --git a/src/mol-state/state.ts b/src/mol-state/state.ts index c343b2aba..6c70a4071 100644 --- a/src/mol-state/state.ts +++ b/src/mol-state/state.ts @@ -103,7 +103,7 @@ class State { * Select Cells by building a query generated on the fly. * @example state.select(q => q.byRef('test').subtree()) */ - selectQ(selector: (q: typeof StateSelection.Generators) => StateSelection.Selector) { + selectQ<C extends StateObjectCell>(selector: (q: typeof StateSelection.Generators) => StateSelection.Selector<C>) { if (typeof selector === 'string') return StateSelection.select(selector, this); return StateSelection.select(selector(StateSelection.Generators), this) } diff --git a/src/mol-state/state/builder.ts b/src/mol-state/state/builder.ts index d1e3f9d63..46ea7bdaf 100644 --- a/src/mol-state/state/builder.ts +++ b/src/mol-state/state/builder.ts @@ -42,7 +42,12 @@ namespace StateBuilder { private state: BuildState; get editInfo() { return this.state.editInfo; } - to<A extends StateObject>(ref: StateTransform.Ref) { return new To<A>(this.state, ref, this); } + to<A extends StateObject>(ref: StateTransform.Ref): To<A> + to<C extends StateObjectCell>(cell: C): To<StateObjectCell.Obj<C>, StateTransform.Transformer<StateObjectCell.Transform<C>>> + to(refOrCell: StateTransform.Ref | StateObjectCell) { + const ref = typeof refOrCell === 'string' ? refOrCell : refOrCell.transform.ref; + return new To<StateObject, StateTransformer>(this.state, ref, this); + } toRoot<A extends StateObject>() { return new To<A>(this.state, this.state.tree.root.ref, this); } delete(ref: StateTransform.Ref) { this.editInfo.count++; @@ -53,7 +58,7 @@ namespace StateBuilder { constructor(tree: StateTree) { this.state = { tree: tree.asTransient(), editInfo: { sourceTree: tree, count: 0, lastUpdate: void 0 } } } } - export class To<A extends StateObject> implements StateBuilder { + export class To<A extends StateObject, T extends StateTransformer = StateTransformer> implements StateBuilder { get editInfo() { return this.state.editInfo; } readonly ref: StateTransform.Ref; @@ -121,14 +126,16 @@ namespace StateBuilder { } update<T extends StateTransformer<any, A, any>>(transformer: T, params: (old: StateTransformer.Params<T>) => StateTransformer.Params<T>): Root - update<T extends StateTransformer<any, A, any> = StateTransformer<any, A, any>>(params: StateTransformer.Params<T>): Root - update<T extends StateTransformer<any, A, any>>(paramsOrTransformer: T, provider?: (old: StateTransformer.Params<T>) => StateTransformer.Params<T>) { + update(params: StateTransformer.Params<T> | ((old: StateTransformer.Params<T>) => StateTransformer.Params<T>)): Root + update<T extends StateTransformer<any, A, any>>(paramsOrTransformer: T | any, provider?: (old: StateTransformer.Params<T>) => StateTransformer.Params<T>) { let params: any; if (provider) { const old = this.state.tree.transforms.get(this.ref)!; params = provider(old.params as any); } else { - params = paramsOrTransformer; + params = typeof paramsOrTransformer === 'function' + ? paramsOrTransformer(this.state.tree.transforms.get(this.ref)!.params) + : paramsOrTransformer; } if (this.state.tree.setParams(this.ref, params)) { diff --git a/src/mol-state/state/selection.ts b/src/mol-state/state/selection.ts index 3bea867d5..12a319073 100644 --- a/src/mol-state/state/selection.ts +++ b/src/mol-state/state/selection.ts @@ -8,6 +8,7 @@ import { StateObject, StateObjectCell } from '../object'; import { State } from '../state'; import { StateTree } from '../tree'; import { StateTransform } from '../transform'; +import { StateTransformer } from '../transformer'; namespace StateSelection { export type Selector<C extends StateObjectCell = StateObjectCell> = Query<C> | Builder<C> | string | C; @@ -48,6 +49,7 @@ namespace StateSelection { parent(): Builder<C>; first(): Builder<C>; filter(p: (n: C) => boolean): Builder<C>; + withTransformer<T extends StateTransformer<any, StateObjectCell.Obj<C>, any>>(t: T): Builder<StateObjectCell<StateObjectCell.Obj<C>, StateTransform<T>>>; withStatus(s: StateObjectCell.Status): Builder<C>; subtree(): Builder; children(): Builder; @@ -89,18 +91,26 @@ namespace StateSelection { export function byValue<T extends StateObjectCell>(...objects: T[]) { return build(() => (state: State) => objects); } - export function rootsOfType<T extends StateObject.Ctor>(type: T) { + export function rootsOfType<T extends StateObject.Ctor>(type: T, root: StateTransform.Ref = StateTransform.RootRef) { return build(() => state => { const ctx = { roots: [] as StateObjectCell<StateObject.From<T>>[], cells: state.cells, type: type.type }; - StateTree.doPreOrder(state.tree, state.tree.root, ctx, _findRootsOfType); + StateTree.doPreOrder(state.tree, state.tree.transforms.get(root), ctx, _findRootsOfType); return ctx.roots; }); } - export function ofType<T extends StateObject.Ctor>(type: T) { + export function ofType<T extends StateObject.Ctor>(type: T, root: StateTransform.Ref = StateTransform.RootRef) { return build(() => state => { const ctx = { ret: [] as StateObjectCell<StateObject.From<T>>[], cells: state.cells, type: type.type }; - StateTree.doPreOrder(state.tree, state.tree.root, ctx, _findOfType); + StateTree.doPreOrder(state.tree, state.tree.transforms.get(root), ctx, _findOfType); + return ctx.ret; + }); + } + + export function ofTransformer<T extends StateTransformer<any, A, any>, A extends StateObject>(t: T, root: StateTransform.Ref = StateTransform.RootRef) { + return build(() => state => { + const ctx = { ret: [] as StateObjectCell<A, StateTransform<T>>[], cells: state.cells, t }; + StateTree.doPreOrder(state.tree, state.tree.transforms.get(root), ctx, _findOfTransformer); return ctx.ret; }); } @@ -121,6 +131,14 @@ namespace StateSelection { } return true; } + + function _findOfTransformer(n: StateTransform, _: any, s: { t: StateTransformer, ret: StateObjectCell[], cells: State.Cells }) { + const cell = s.cells.get(n.ref); + if (cell && cell.obj && cell.transform.transformer === s.t) { + s.ret.push(cell); + } + return true; + } } registerModifier('flatMap', flatMap); @@ -206,6 +224,9 @@ namespace StateSelection { registerModifier('ancestorOfType', ancestorOfType); export function ancestorOfType(b: Selector, types: StateObject.Ctor[]) { return unique(mapObject(b, (n, s) => findAncestorOfType(s.tree, s.cells, n.transform.ref, types))); } + registerModifier('withTransformer', withTransformer); + export function withTransformer(b: Selector, t: StateTransformer) { return filter(b, o => o.transform.transformer === t); } + registerModifier('rootOfType', rootOfType); export function rootOfType(b: Selector, types: StateObject.Ctor[]) { return unique(mapObject(b, (n, s) => findRootOfType(s.tree, s.cells, n.transform.ref, types))); } diff --git a/src/mol-state/transform.ts b/src/mol-state/transform.ts index 12de75d1c..9703a2f53 100644 --- a/src/mol-state/transform.ts +++ b/src/mol-state/transform.ts @@ -4,23 +4,23 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { StateObject } from './object'; import { StateTransformer } from './transformer'; import { UUID } from 'mol-util'; export { Transform as StateTransform } -interface Transform<A extends StateObject = StateObject, B extends StateObject = StateObject, P extends {} = {}> { +interface Transform<T extends StateTransformer = StateTransformer> { readonly parent: Transform.Ref, - readonly transformer: StateTransformer<A, B, P>, + readonly transformer: T, readonly props: Transform.Props, readonly ref: Transform.Ref, - readonly params?: P, + readonly params?: StateTransformer.Params<T>, readonly version: string } namespace Transform { export type Ref = string + export type Transformer<T extends Transform> = T extends Transform<infer S> ? S : never export const RootRef = '-=root=-' as Ref; @@ -36,7 +36,7 @@ namespace Transform { props?: Props } - export function create<A extends StateObject, B extends StateObject, P extends {} = {}>(parent: Ref, transformer: StateTransformer<A, B, P>, params?: P, options?: Options): Transform<A, B, P> { + export function create<T extends StateTransformer>(parent: Ref, transformer: T, params?: StateTransformer.Params<T>, options?: Options): Transform<T> { const ref = options && options.ref ? options.ref : UUID.create22() as string as Ref; return { parent, diff --git a/src/mol-state/transformer.ts b/src/mol-state/transformer.ts index a7ba452d4..01759e738 100644 --- a/src/mol-state/transformer.ts +++ b/src/mol-state/transformer.ts @@ -14,8 +14,8 @@ import { StateTreeSpine } from './tree/spine'; export { Transformer as StateTransformer } -interface Transformer<A extends StateObject = StateObject, B extends StateObject = StateObject, P extends {} = {}> { - apply(parent: StateTransform.Ref, params?: P, props?: Partial<StateTransform.Options>): StateTransform<A, B, P>, +interface Transformer<A extends StateObject = StateObject, B extends StateObject = StateObject, P extends {} = any> { + apply(parent: StateTransform.Ref, params?: P, props?: Partial<StateTransform.Options>): StateTransform<this>, toAction(): StateAction<A, void, P>, readonly namespace: string, readonly id: Transformer.Id, @@ -136,7 +136,7 @@ namespace Transformer { } const t: Transformer<A, B, P> = { - apply(parent, params, props) { return StateTransform.create<A, B, P>(parent, t, params, props); }, + apply(parent, params, props) { return StateTransform.create<Transformer<A, B, P>>(parent, t, params, props); }, toAction() { return StateAction.fromTransformer(t); }, namespace, id, -- GitLab