diff --git a/src/mol-plugin/behavior/behavior.ts b/src/mol-plugin/behavior/behavior.ts index ed03b478adbda9fe13de86988d76d453dc741c57..f3fae6e80fe4442f4df5c2c7d799d8222a29cdf0 100644 --- a/src/mol-plugin/behavior/behavior.ts +++ b/src/mol-plugin/behavior/behavior.ts @@ -55,7 +55,7 @@ namespace PluginBehavior { export type CreateCategory = typeof CreateCategory export const CreateCategory = PluginStateTransform.BuiltIn({ name: 'create-behavior-category', - display: { name: 'Create Cateogry' }, + display: { name: 'Behavior Category' }, from: Root, to: Category, params: { diff --git a/src/mol-plugin/behavior/static/state.ts b/src/mol-plugin/behavior/static/state.ts index 3275404c6908cac51f2df2b1060eb47a41e4eae5..6ff152876d98c634ba8b8c76e1a453bdcca379f4 100644 --- a/src/mol-plugin/behavior/static/state.ts +++ b/src/mol-plugin/behavior/static/state.ts @@ -59,9 +59,27 @@ export function ApplyAction(ctx: PluginContext) { } export function RemoveObject(ctx: PluginContext) { - PluginCommands.State.RemoveObject.subscribe(ctx, ({ state, ref }) => { + function remove(state: State, ref: string) { const tree = state.build().delete(ref).getTree(); return ctx.runTask(state.updateTree(tree)); + } + + PluginCommands.State.RemoveObject.subscribe(ctx, ({ state, ref, removeParentGhosts }) => { + if (removeParentGhosts) { + const tree = state.tree; + let curr = tree.transforms.get(ref); + if (curr.parent === ref) return remove(state, ref); + + while (true) { + const children = tree.children.get(curr.parent); + if (curr.parent === curr.ref || children.size > 1) return remove(state, curr.ref); + const parent = tree.transforms.get(curr.parent); + if (!parent.props || !parent.props.isGhost) return remove(state, curr.ref); + curr = parent; + } + } else { + remove(state, ref); + } }); } diff --git a/src/mol-plugin/command.ts b/src/mol-plugin/command.ts index b46600218ecf730408185890bc79c9be7a50395f..c188f4cb3c8a2f005ec3576f76ad0e4c240c250e 100644 --- a/src/mol-plugin/command.ts +++ b/src/mol-plugin/command.ts @@ -18,7 +18,7 @@ export const PluginCommands = { ApplyAction: PluginCommand<{ state: State, action: StateAction.Instance, ref?: StateTransform.Ref }>(), Update: PluginCommand<{ state: State, tree: State.Tree | State.Builder, doNotLogTiming?: boolean }>(), - RemoveObject: PluginCommand<{ state: State, ref: StateTransform.Ref }>(), + RemoveObject: PluginCommand<{ state: State, ref: StateTransform.Ref, removeParentGhosts?: boolean }>(), ToggleExpanded: PluginCommand<{ state: State, ref: StateTransform.Ref }>({ isImmediate: true }), ToggleVisibility: PluginCommand<{ state: State, ref: StateTransform.Ref }>({ isImmediate: true }), diff --git a/src/mol-plugin/skin/base/components/transformer.scss b/src/mol-plugin/skin/base/components/transformer.scss index aca44d435bbafbec2138e5da13b8974509273875..f9d0105a7b49623289e0cb02f2eb5a36c6d9d51b 100644 --- a/src/mol-plugin/skin/base/components/transformer.scss +++ b/src/mol-plugin/skin/base/components/transformer.scss @@ -42,15 +42,44 @@ margin-bottom: $control-spacing; } -.msp-transform-header { - position: relative; - border-top: 1px solid $entity-color-Behaviour; // TODO: separate color +.msp-transform-update-wrapper { + border-bottom: $control-spacing solid $control-background; +} + +.msp-transform-update-wrapper-collapsed { + margin-bottom: 1px; +} - > button { +.msp-transform-update-wrapper, .msp-transform-update-wrapper-collapsed { + > .msp-transform-header > button { text-align: left; - background: color-lower-contrast($default-background, 4%); + padding-left: $row-height; + background: $control-background; // color-lower-contrast($default-background, 4%); font-weight: bold; } +} + +.msp-transform-wrapper > .msp-transform-header > button { + text-align: left; + background: color-lower-contrast($default-background, 4%); + font-weight: bold; +} + +.msp-transform-header { + position: relative; + // border-top: 1px solid $entity-color-Behaviour; // TODO: separate color + + // > button { + // text-align: left; + // padding-left: $row-height; + // background: $control-background; // color-lower-contrast($default-background, 4%); + // font-weight: bold; + // } + + > button > small { + font-weight: normal; + float: right; + } > button:hover { color: color-lower-contrast($font-color, 15%); @@ -58,9 +87,11 @@ } .msp-transform-default-params { + background: $default-background; position: absolute; - right: 0; + left: 0; top: 0; + width: $row-height; } .msp-transform-default-params:hover { @@ -74,7 +105,8 @@ } .msp-transform-refresh { - width: $control-label-width + $control-spacing; + width: $control-label-width + $control-spacing - $row-height - 1; + margin-left: $row-height + 1; background: $default-background; text-align: right; } diff --git a/src/mol-plugin/state/actions/structure.ts b/src/mol-plugin/state/actions/structure.ts index 3aadfee2de0702040851301e4d82530e857d85c8..080fc02633a0970e403b10fded502ae574764613 100644 --- a/src/mol-plugin/state/actions/structure.ts +++ b/src/mol-plugin/state/actions/structure.ts @@ -71,7 +71,7 @@ const DownloadStructure = StateAction.build({ default: throw new Error(`${(src as any).name} not supported.`); } - const data = b.toRoot().apply(StateTransforms.Data.Download, downloadParams); + const data = b.toRoot().apply(StateTransforms.Data.Download, downloadParams, { props: { isGhost: true }}); const traj = createModelTree(data, src.name === 'url' ? src.params.format : 'cif'); return state.updateTree(createStructureTree(ctx, traj, params.source.params.supportProps)); }); @@ -89,7 +89,7 @@ export const OpenStructure = StateAction.build({ function createModelTree(b: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, format: 'pdb' | 'cif' = 'cif') { const parsed = format === 'cif' - ? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif) + ? b.apply(StateTransforms.Data.ParseCif, void 0, { props: { isGhost: true }}).apply(StateTransforms.Model.TrajectoryFromMmCif) : b.apply(StateTransforms.Model.TrajectoryFromPDB); return parsed.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }); diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 4f67fc62cd221ab95984d506987628e2509057b3..0211d19fd53e9cca8a15dcb3ba0985458d05d510 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -64,7 +64,7 @@ export { TrajectoryFromPDB } type TrajectoryFromPDB = typeof TrajectoryFromPDB const TrajectoryFromPDB = PluginStateTransform.BuiltIn({ name: 'trajectory-from-pdb', - display: { name: 'Parse PDB string and create trajectory' }, + display: { name: 'Parse PDB', description: 'Parse PDB string and create trajectory.' }, from: [SO.Data.String], to: SO.Molecule.Trajectory })({ @@ -168,7 +168,6 @@ const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({ return new SO.Molecule.Structure(base, label); } - asm = model.symmetry.assemblies[0]; id = asm.id; const s = await StructureSymmetry.buildAssembly(base, id!).runInContext(ctx); const props = { label: `Assembly ${id}`, description: structureDesc(s) }; diff --git a/src/mol-plugin/ui/plugin.tsx b/src/mol-plugin/ui/plugin.tsx index 578ba4bb0c3ea49f119fb7b5a0e03dbee274a8c6..12b4855985287c87f1a55501fe9c15370234b296 100644 --- a/src/mol-plugin/ui/plugin.tsx +++ b/src/mol-plugin/ui/plugin.tsx @@ -4,23 +4,22 @@ * @author David Sehnal <david.sehnal@gmail.com> */ +import { List } from 'immutable'; +import { PluginState } from 'mol-plugin/state'; +import { formatTime } from 'mol-util'; +import { LogEntry } from 'mol-util/log-entry'; import * as React from 'react'; import { PluginContext } from '../context'; -import { StateTree } from './state-tree'; -import { Viewport, ViewportControls } from './viewport'; -import { Controls, TrajectoryControls, LociLabelControl } from './controls'; -import { PluginUIComponent, PluginReactContext } from './base'; +import { PluginReactContext, PluginUIComponent } from './base'; import { CameraSnapshots } from './camera'; +import { Controls, LociLabelControl, TrajectoryControls } from './controls'; import { StateSnapshots } from './state'; -import { List } from 'immutable'; -import { LogEntry } from 'mol-util/log-entry'; -import { formatTime } from 'mol-util'; -import { BackgroundTaskProgress } from './task'; -import { ApplyActionContol } from './state/apply-action'; -import { PluginState } from 'mol-plugin/state'; -import { UpdateTransformContol } from './state/update-transform'; -import { StateObjectCell } from 'mol-state'; +import { StateObjectActions } from './state/actions'; import { AnimationControls } from './state/animation'; +import { StateTree } from './state/tree'; +import { BackgroundTaskProgress } from './task'; +import { Viewport, ViewportControls } from './viewport'; +import { StateTransform } from 'mol-state'; export class Plugin extends React.Component<{ plugin: PluginContext }, {}> { @@ -105,8 +104,8 @@ export class State extends PluginUIComponent { const kind = this.plugin.state.behavior.kind.value; return <div className='msp-scrollable-container'> <div className='msp-btn-row-group msp-data-beh'> - <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.set('data')} style={{ fontWeight: kind === 'data' ? 'bold' : 'normal'}}>Data</button> - <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.set('behavior')} style={{ fontWeight: kind === 'behavior' ? 'bold' : 'normal'}}>Behavior</button> + <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.set('data')} style={{ fontWeight: kind === 'data' ? 'bold' : 'normal' }}>Data</button> + <button className='msp-btn msp-btn-block msp-form-control' onClick={() => this.set('behavior')} style={{ fontWeight: kind === 'behavior' ? 'bold' : 'normal' }}>Behavior</button> </div> <StateTree state={kind === 'data' ? this.plugin.state.dataState : this.plugin.state.behaviorState} /> </div> @@ -172,21 +171,21 @@ export class CurrentObject extends PluginUIComponent { const current = this.current; const ref = current.ref; const cell = current.state.cells.get(ref)!; - const parent: StateObjectCell | undefined = (cell.sourceRef && current.state.cells.get(cell.sourceRef)!) || void 0; - const transform = cell.transform; const def = transform.transformer.definition; + const display = cell.obj ? cell.obj.label : (def.display && def.display.name) || def.name; - const actions = current.state.actions.fromCell(cell, this.plugin); - return <> - <div className='msp-current-header'> - {cell.obj ? cell.obj.label : (def.display && def.display.name) || def.name} - </div> - {(parent && parent.status === 'ok') && <UpdateTransformContol state={current.state} transform={transform} />} - {cell.status === 'ok' && <> - <div className='msp-section-header'>Actions</div> - {actions.map((act, i) => <ApplyActionContol plugin={this.plugin} key={`${act.id}`} state={current.state} action={act} nodeRef={ref} />)} - </>} + let showActions = true; + if (ref === StateTransform.RootRef) { + const children = current.state.tree.children.get(ref); + showActions = children.size !== 0; + } + + if (!showActions) return null; + + return cell.status === 'ok' && <> + <div className='msp-section-header'>{`Actions (${display})`}</div> + <StateObjectActions state={current.state} nodeRef={ref} /> </>; } } \ No newline at end of file diff --git a/src/mol-plugin/ui/state/actions.tsx b/src/mol-plugin/ui/state/actions.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2d660af3b85ca7220cbd1ccc5976f5f1c5119c55 --- /dev/null +++ b/src/mol-plugin/ui/state/actions.tsx @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2018 - 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import * as React from 'react'; +import { PluginUIComponent } from '../base'; +import { ApplyActionContol } from './apply-action'; +import { State } from 'mol-state'; + +export class StateObjectActions extends PluginUIComponent<{ state: State, nodeRef: string }> { + get current() { + return this.plugin.state.behavior.currentObject.value; + } + + componentDidMount() { + this.subscribe(this.plugin.state.behavior.currentObject, o => { + this.forceUpdate(); + }); + + this.subscribe(this.plugin.events.state.object.updated, ({ ref, state }) => { + const current = this.current; + if (current.ref !== ref || current.state !== state) return; + this.forceUpdate(); + }); + } + + render() { + const { state, nodeRef: ref } = this.props; + const cell = state.cells.get(ref)!; + const actions = state.actions.fromCell(cell, this.plugin); + return actions.map((act, i) => <ApplyActionContol plugin={this.plugin} key={`${act.id}`} state={state} action={act} nodeRef={ref} />); + } +} \ No newline at end of file diff --git a/src/mol-plugin/ui/state/common.tsx b/src/mol-plugin/ui/state/common.tsx index 22b48453342b8f8ae75b780778284cae86d8ebc8..57659f7aa9d237043a57242526ea1b1dc3dafc06 100644 --- a/src/mol-plugin/ui/state/common.tsx +++ b/src/mol-plugin/ui/state/common.tsx @@ -87,7 +87,7 @@ namespace StateTransformParameters { } namespace TransformContolBase { - export interface ControlState { + export interface ComponentState { params: any, error?: string, busy: boolean, @@ -96,7 +96,7 @@ namespace TransformContolBase { } } -abstract class TransformContolBase<P, S extends TransformContolBase.ControlState> extends PurePluginUIComponent<P, S> { +abstract class TransformContolBase<P, S extends TransformContolBase.ComponentState> extends PurePluginUIComponent<P, S> { abstract applyAction(): Promise<void>; abstract getInfo(): StateTransformParameters.Props['info']; abstract getHeader(): StateTransformer.Definition['display']; @@ -166,7 +166,7 @@ abstract class TransformContolBase<P, S extends TransformContolBase.ControlState render() { const info = this.getInfo(); - if (info.isEmpty && this.isUpdate()) return null; + const isEmpty = info.isEmpty && this.isUpdate(); const display = this.getHeader(); @@ -175,17 +175,26 @@ abstract class TransformContolBase<P, S extends TransformContolBase.ControlState ? this.plugin.customParamEditors.get(tId)! : StateTransformParameters; - return <div className='msp-transform-wrapper'> + const wrapClass = this.isUpdate() + ? !isEmpty && !this.state.isCollapsed + ? 'msp-transform-update-wrapper' + : 'msp-transform-update-wrapper-collapsed' + : 'msp-transform-wrapper'; + + return <div className={wrapClass}> <div className='msp-transform-header'> - <button className='msp-btn msp-btn-block' onClick={this.toggleExpanded}>{display.name}</button> - {!this.state.isCollapsed && <button className='msp-btn msp-btn-link msp-transform-default-params' onClick={this.setDefault} disabled={this.state.busy} style={{ float: 'right'}} title='Set default params'>↻</button>} + <button className='msp-btn msp-btn-block' onClick={this.toggleExpanded} title={display.description}> + {display.name} + {!isEmpty && this.state.isCollapsed && this.isUpdate() && <small>Click to Edit</small>} + </button> </div> - {!this.state.isCollapsed && <> + {!isEmpty && !this.state.isCollapsed && <> <ParamEditor info={info} events={this.events} params={this.state.params} isDisabled={this.state.busy} /> <div className='msp-transform-apply-wrap'> + <button className='msp-btn msp-btn-block msp-transform-default-params' onClick={this.setDefault} disabled={this.state.busy} title='Set default params'>↻</button> <button className='msp-btn msp-btn-block msp-transform-refresh msp-form-control' title='Refresh params' onClick={this.refresh} disabled={this.state.busy || this.state.isInitial}> - ↶ Reset + ↶ Back </button> <div className='msp-transform-apply'> <button className={`msp-btn msp-btn-block msp-btn-commit msp-btn-commit-${this.canApply() ? 'on' : 'off'}`} onClick={this.apply} disabled={!this.canApply()}> diff --git a/src/mol-plugin/ui/state-tree.tsx b/src/mol-plugin/ui/state/tree.tsx similarity index 64% rename from src/mol-plugin/ui/state-tree.tsx rename to src/mol-plugin/ui/state/tree.tsx index d91238056a0714691a169533c87f10c86155aa65..b98a60577a573be0d405cef9ceac1c7504358403 100644 --- a/src/mol-plugin/ui/state-tree.tsx +++ b/src/mol-plugin/ui/state/tree.tsx @@ -1,23 +1,48 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018 - 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> */ import * as React from 'react'; import { PluginStateObject } from 'mol-plugin/state/objects'; -import { State, StateObject } from 'mol-state' +import { State, StateObject, StateObjectCell, StateTransform } from 'mol-state' import { PluginCommands } from 'mol-plugin/command'; -import { PluginUIComponent } from './base'; +import { PluginUIComponent } from '../base'; +import { UpdateTransformContol } from './update-transform'; +import { StateObjectActions } from './actions'; + +export class StateTree extends PluginUIComponent<{ state: State }, { showActions: boolean }> { + state = { showActions: true }; + + componentDidMount() { + this.subscribe(this.plugin.events.state.cell.created, e => { + if (e.cell.transform.parent === StateTransform.RootRef) this.forceUpdate(); + }); + + this.subscribe(this.plugin.events.state.cell.removed, e => { + if (e.parent === StateTransform.RootRef) this.forceUpdate(); + }); + } + + static getDerivedStateFromProps(props: { state: State }, state: { showActions: boolean }) { + const n = props.state.tree.root.ref; + const children = props.state.tree.children.get(n); + const showActions = children.size === 0; + if (state.showActions === showActions) return null; + return { showActions }; + } -export class StateTree extends PluginUIComponent<{ state: State }> { render() { - const n = this.props.state.tree.root.ref; - return <StateTreeNode state={this.props.state} nodeRef={n} />; + const ref = this.props.state.tree.root.ref; + if (this.state.showActions) { + return <StateObjectActions state={this.props.state} nodeRef={ref} /> + } + return <StateTreeNode state={this.props.state} nodeRef={ref} depth={0} />; } } -class StateTreeNode extends PluginUIComponent<{ nodeRef: string, state: State }, { state: State, isCollapsed: boolean }> { +class StateTreeNode extends PluginUIComponent<{ nodeRef: string, state: State, depth: number }, { state: State, isCollapsed: boolean }> { is(e: State.ObjectEvent) { return e.ref === this.props.nodeRef && e.state === this.props.state; } @@ -60,24 +85,34 @@ class StateTreeNode extends PluginUIComponent<{ nodeRef: string, state: State }, } render() { - if (this.props.state.cells.get(this.props.nodeRef)!.obj === StateObject.Null) return null; + const cell = this.props.state.cells.get(this.props.nodeRef)!; + if (cell.obj === StateObject.Null) return null; const cellState = this.cellState; - + const showLabel = cell.status !== 'ok' || !cell.transform.props || !cell.transform.props.isGhost; const children = this.props.state.tree.children.get(this.props.nodeRef); - return <div> - <StateTreeNodeLabel nodeRef={this.props.nodeRef} state={this.props.state} /> + const newDepth = showLabel ? this.props.depth + 1 : this.props.depth; + + if (!showLabel) { + if (children.size === 0) return null; + return <div style={{ display: cellState.isCollapsed ? 'none' : 'block' }}> + {children.map(c => <StateTreeNode state={this.props.state} nodeRef={c!} key={c} depth={newDepth} />)} + </div>; + } + + return <> + <StateTreeNodeLabel nodeRef={this.props.nodeRef} state={this.props.state} depth={this.props.depth} /> {children.size === 0 ? void 0 - : <div className='msp-tree-children' style={{ display: cellState.isCollapsed ? 'none' : 'block' }}> - {children.map(c => <StateTreeNode state={this.props.state} nodeRef={c!} key={c} />)} + : <div style={{ display: cellState.isCollapsed ? 'none' : 'block' }}> + {children.map(c => <StateTreeNode state={this.props.state} nodeRef={c!} key={c} depth={newDepth} />)} </div> } - </div>; + </>; } } -class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: State }, { state: State, isCurrent: boolean, isCollapsed: boolean }> { +class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: State, depth: number }, { state: State, isCurrent: boolean, isCollapsed: boolean }> { is(e: State.ObjectEvent) { return e.ref === this.props.nodeRef && e.state === this.props.state; } @@ -126,7 +161,7 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta remove = (e: React.MouseEvent<HTMLElement>) => { e.preventDefault(); - PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.props.state, ref: this.props.nodeRef }); + PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.props.state, ref: this.props.nodeRef, removeParentGhosts: true }); } toggleVisible = (e: React.MouseEvent<HTMLElement>) => { @@ -171,7 +206,11 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta } else { const obj = cell.obj as PluginStateObject.Any; const title = `${obj.label} ${obj.description ? obj.description : ''}` - label = <><a title={title} href='#' onClick={this.setCurrent}>{obj.label}</a> {obj.description ? <small>{obj.description}</small> : void 0}</>; + if (this.state.isCurrent) { + label = <><b>{obj.label}</b> {obj.description ? <small>{obj.description}</small> : void 0}</>; + } else { + label = <><a title={title} href='#' onClick={this.setCurrent}>{obj.label}</a> {obj.description ? <small>{obj.description}</small> : void 0}</>; + } } const children = this.props.state.tree.children.get(this.props.nodeRef); @@ -181,14 +220,37 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta <span className='msp-icon msp-icon-visual-visibility' /> </button>; - return <div className={`msp-tree-row${isCurrent ? ' msp-tree-row-current' : ''}`} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight}> - {isCurrent ? <b>{label}</b> : label} + const row = <div className={`msp-tree-row${isCurrent ? ' msp-tree-row-current' : ''}`} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight} + style={{ marginLeft: this.state.isCurrent ? '0px' : `${this.props.depth * 10}px`, borderRadius: this.state.isCurrent ? '0' : void 0 }}> + {label} {children.size > 0 && <button onClick={this.toggleExpanded} className='msp-btn msp-btn-link msp-tree-toggle-exp-button'> <span className={`msp-icon msp-icon-${cellState.isCollapsed ? 'expand' : 'collapse'}`} /> </button>} {!cell.transform.props.isLocked && <button onClick={this.remove} className='msp-btn msp-btn-link msp-tree-remove-button'> <span className='msp-icon msp-icon-remove' /> </button>}{visibility} - </div> + </div>; + + if (this.state.isCurrent) { + return <> + {row} + <StateTreeNodeTransform {...this.props}/> + </> + } + + return row; + } +} + +class StateTreeNodeTransform extends PluginUIComponent<{ nodeRef: string, state: State, depth: number }, { isExpanded: boolean }> { + render() { + const ref = this.props.nodeRef; + const cell = this.props.state.cells.get(ref)!; + const parent: StateObjectCell | undefined = (cell.sourceRef && this.props.state.cells.get(cell.sourceRef)!) || void 0; + + if (!parent || parent.status !== 'ok') return null; + + const transform = cell.transform; + return <UpdateTransformContol state={this.props.state} transform={transform} initiallyCollapsed={true} />; } } \ No newline at end of file diff --git a/src/mol-plugin/ui/state/update-transform.tsx b/src/mol-plugin/ui/state/update-transform.tsx index e60eae0662b930d04b7aa48b1b71d39e05a05d07..31f1a498ba9d8acbd859aae0b7b9c6370f73e3eb 100644 --- a/src/mol-plugin/ui/state/update-transform.tsx +++ b/src/mol-plugin/ui/state/update-transform.tsx @@ -13,15 +13,12 @@ export { UpdateTransformContol }; namespace UpdateTransformContol { export interface Props { transform: StateTransform, - state: State + state: State, + initiallyCollapsed?: boolean } - export interface ComponentState { - transform: StateTransform, - params: any, - error?: string, - busy: boolean, - isInitial: boolean + export interface ComponentState extends TransformContolBase.ComponentState { + transform: StateTransform } } @@ -48,10 +45,10 @@ class UpdateTransformContol extends TransformContolBase<UpdateTransformContol.Pr private _getInfo = memoizeLatest((t: StateTransform) => StateTransformParameters.infoFromTransform(this.plugin, this.props.state, this.props.transform)); - state: UpdateTransformContol.ComponentState = { transform: this.props.transform, error: void 0, isInitial: true, params: this.getInfo().initialValues, busy: false }; + state: UpdateTransformContol.ComponentState = { transform: this.props.transform, error: void 0, isInitial: true, params: this.getInfo().initialValues, busy: false, isCollapsed: this.props.initiallyCollapsed }; static getDerivedStateFromProps(props: UpdateTransformContol.Props, state: UpdateTransformContol.ComponentState) { - if (props.transform === state.transform) return null; + if (props.transform === state.transform && props.initiallyCollapsed) return null; const cell = props.state.cells.get(props.transform.ref)!; const newState: Partial<UpdateTransformContol.ComponentState> = { transform: props.transform,