Browse Source

mol-plugin: tree UI

David Sehnal 6 years ago
parent
commit
9cb4b3390f

+ 24 - 2
src/mol-plugin/skin/base/components/temp.scss

@@ -78,10 +78,31 @@
     margin-bottom: 1px;
     padding-left: $row-height;
     padding-right: 2 * $row-height + $control-spacing;
-    border-bottom-left-radius: $control-spacing;
+    border-left: 1px dotted $entity-color-Group; // TODO custom color
+    // border-bottom-left-radius: $control-spacing;
 
     &-current {
-        background: $control-background
+        // background: $control-background
+        a {
+            color: $font-color;
+        }
+
+        a:hover, a:hover > small {
+            color: color-lower-contrast($font-color, 24%);
+        }
+    }
+
+    a {
+        display: block;
+    }
+
+    a > small {
+        color: $font-color;
+    }
+
+    a:hover {
+        font-weight: bold;
+        text-decoration: none;
     }
 }
 
@@ -99,6 +120,7 @@
     left: 0;
     top: 0;
     width: $row-height;
+    padding: 0;
     color: color-lower-contrast($font-color, 24%);
 }
 

+ 6 - 4
src/mol-plugin/skin/base/components/transformer.scss

@@ -10,7 +10,7 @@
     }
 }
 
-.msp-layout-right {
+.msp-layout-right, .msp-layout-left {
     background: $control-background;
 }
 
@@ -43,7 +43,8 @@
 }
 
 .msp-transform-update-wrapper {
-    border-bottom: $control-spacing solid $control-background;
+    //border-bottom: $control-spacing solid $control-background;
+    margin-bottom: 1px;
 }
 
 .msp-transform-update-wrapper-collapsed {
@@ -54,8 +55,9 @@
     > .msp-transform-header > button {
         text-align: left;
         padding-left: $row-height;
-        background: $control-background; // color-lower-contrast($default-background, 4%);
-        font-weight: bold;
+        line-height: 24px;
+        background: color-lower-contrast($control-background, 4%); // $control-background; // color-lower-contrast($default-background, 4%);
+        // font-weight: bold;
     }
 }
 

+ 2 - 2
src/mol-plugin/state/actions/structure.ts

@@ -89,8 +89,8 @@ 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, void 0, { props: { isGhost: true }}).apply(StateTransforms.Model.TrajectoryFromMmCif)
-        : b.apply(StateTransforms.Model.TrajectoryFromPDB);
+        ? b.apply(StateTransforms.Data.ParseCif, void 0, { props: { isGhost: true }}).apply(StateTransforms.Model.TrajectoryFromMmCif, void 0, { props: { isGhost: true }})
+        : b.apply(StateTransforms.Model.TrajectoryFromPDB, void 0, { props: { isGhost: true }});
 
     return parsed.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 });
 }

+ 4 - 2
src/mol-plugin/state/transforms/model.ts

@@ -85,7 +85,7 @@ const plus1 = (v: number) => v + 1, minus1 = (v: number) => v - 1;
 type ModelFromTrajectory = typeof ModelFromTrajectory
 const ModelFromTrajectory = PluginStateTransform.BuiltIn({
     name: 'model-from-trajectory',
-    display: { name: 'Model from Trajectory', description: 'Create a molecular structure from the specified model.' },
+    display: { name: 'Molecular Model', description: 'Create a molecular model from specified index in a trajectory.' },
     from: SO.Molecule.Trajectory,
     to: SO.Molecule.Model,
     params: a => {
@@ -99,7 +99,9 @@ const ModelFromTrajectory = PluginStateTransform.BuiltIn({
     apply({ a, params }) {
         if (params.modelIndex < 0 || params.modelIndex >= a.data.length) throw new Error(`Invalid modelIndex ${params.modelIndex}`);
         const model = a.data[params.modelIndex];
-        const props = { label: `Model ${model.modelNum}` };
+        const props = a.data.length === 1
+            ? { label: `${model.label}` }
+            : { label: `${model.label}:${model.modelNum}`, description: `Model ${model.modelNum} of ${a.data.length}` };
         return new SO.Molecule.Model(model, props);
     }
 });

+ 2 - 2
src/mol-plugin/ui/controls/parameters.tsx

@@ -31,7 +31,7 @@ export class ParameterControls<P extends PD.Params> extends React.PureComponent<
         const values = this.props.values;
         const keys = Object.keys(params);
         if (keys.length === 0) return null;
-        return <div style={{ width: '100%' }}>
+        return <>
             {keys.map(key => {
                 const param = params[key];
                 if (param.isHidden) return null;
@@ -39,7 +39,7 @@ export class ParameterControls<P extends PD.Params> extends React.PureComponent<
                 if (!Control) return null;
                 return <Control param={param} key={key} onChange={this.props.onChange} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} name={key} value={values[key]} />
             })}
-        </div>;
+        </>;
     }
 }
 

+ 23 - 9
src/mol-plugin/ui/state/tree.tsx

@@ -11,6 +11,7 @@ import { PluginCommands } from 'mol-plugin/command';
 import { PluginUIComponent } from '../base';
 import { UpdateTransformContol } from './update-transform';
 import { StateObjectActions } from './actions';
+import { Observable, Subject } from 'rxjs';
 
 export class StateTree extends PluginUIComponent<{ state: State }, { showActions: boolean }> {
     state = { showActions: true };
@@ -112,7 +113,10 @@ class StateTreeNode extends PluginUIComponent<{ nodeRef: string, state: State, d
     }
 }
 
-class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: State, depth: number }, { state: State, isCurrent: boolean, isCollapsed: boolean }> {
+class StateTreeNodeLabel extends PluginUIComponent<
+    { nodeRef: string, state: State, depth: number },
+    { state: State, isCurrent: boolean, isCollapsed: boolean, updaterCollapsed: boolean }> {
+
     is(e: State.ObjectEvent) {
         return e.ref === this.props.nodeRef && e.state === this.props.state;
     }
@@ -142,7 +146,8 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
     state = {
         isCurrent: this.props.state.current === this.props.nodeRef,
         isCollapsed: this.props.state.cellStates.get(this.props.nodeRef).isCollapsed,
-        state: this.props.state
+        state: this.props.state,
+        updaterCollapsed: true
     }
 
     static getDerivedStateFromProps(props: { nodeRef: string, state: State }, state: { state: State, isCurrent: boolean, isCollapsed: boolean }) {
@@ -150,12 +155,14 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
         return {
             isCurrent: props.state.current === props.nodeRef,
             isCollapsed: props.state.cellStates.get(props.nodeRef).isCollapsed,
-            state: props.state
+            state: props.state,
+            updaterCollapsed: true
         };
     }
 
     setCurrent = (e: React.MouseEvent<HTMLElement>) => {
         e.preventDefault();
+        e.currentTarget.blur();
         PluginCommands.State.SetCurrentObject.dispatch(this.plugin, { state: this.props.state, ref: this.props.nodeRef });
     }
 
@@ -188,6 +195,13 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
         e.currentTarget.blur();
     }
 
+    private toggleUpdaterObs = new Subject();
+    toggleUpdater = (e: React.MouseEvent<HTMLAnchorElement>) => {
+        e.preventDefault();
+        e.currentTarget.blur();
+        this.toggleUpdaterObs.next();
+    }
+
     render() {
         const n = this.props.state.transforms.get(this.props.nodeRef)!;
         const cell = this.props.state.cells.get(this.props.nodeRef)!;
@@ -207,9 +221,9 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
             const obj = cell.obj as PluginStateObject.Any;
             const title = `${obj.label} ${obj.description ? obj.description : ''}`
             if (this.state.isCurrent) {
-                label = <><b>{obj.label}</b> {obj.description ? <small>{obj.description}</small> : void 0}</>;
+                label = <><a title={title} href='#' onClick={this.toggleUpdater}><b>{obj.label}</b> {obj.description ? <small>{obj.description}</small> : void 0}</a></>;
             } else {
-                label = <><a title={title} href='#' onClick={this.setCurrent}>{obj.label}</a> {obj.description ? <small>{obj.description}</small> : void 0}</>;
+                label = <><a title={title} href='#' onClick={this.setCurrent}>{obj.label} {obj.description ? <small>{obj.description}</small> : void 0}</a></>;
             }
         }
 
@@ -221,7 +235,7 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
         </button>;
 
         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 }}>
+            style={{ marginLeft: this.state.isCurrent ? '0px' : `${this.props.depth * 10}px`, borderLeft: isCurrent || this.props.depth === 0 ? 'none' : 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'}`} />
@@ -234,7 +248,7 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
         if (this.state.isCurrent) {
             return <>
                 {row}
-                <StateTreeNodeTransform {...this.props}/>
+                <StateTreeNodeTransform {...this.props} toggleCollapsed={this.toggleUpdaterObs} />
             </>
         }
 
@@ -242,7 +256,7 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
     }
 }
 
-class StateTreeNodeTransform extends PluginUIComponent<{ nodeRef: string, state: State, depth: number }, { isExpanded: boolean }> {
+class StateTreeNodeTransform extends PluginUIComponent<{ nodeRef: string, state: State, depth: number, toggleCollapsed?: Observable<any> }> {
     render() {
         const ref = this.props.nodeRef;
         const cell = this.props.state.cells.get(ref)!;
@@ -251,6 +265,6 @@ class StateTreeNodeTransform extends PluginUIComponent<{ nodeRef: string, state:
         if (!parent || parent.status !== 'ok') return null;
 
         const transform = cell.transform;
-        return <UpdateTransformContol state={this.props.state} transform={transform} initiallyCollapsed={true} />;
+        return <UpdateTransformContol state={this.props.state} transform={transform} initiallyCollapsed={true} toggleCollapsed={this.props.toggleCollapsed} />;
     }
 }

+ 9 - 1
src/mol-plugin/ui/state/update-transform.tsx

@@ -7,6 +7,7 @@
 import { State, StateTransform } from 'mol-state';
 import { memoizeLatest } from 'mol-util/memoize';
 import { StateTransformParameters, TransformContolBase } from './common';
+import { Observable } from 'rxjs';
 
 export { UpdateTransformContol };
 
@@ -14,6 +15,7 @@ namespace UpdateTransformContol {
     export interface Props {
         transform: StateTransform,
         state: State,
+        toggleCollapsed?: Observable<any>,
         initiallyCollapsed?: boolean
     }
 
@@ -43,12 +45,18 @@ class UpdateTransformContol extends TransformContolBase<UpdateTransformContol.Pr
         return autoUpdate({ a: cell.obj!, b: parentCell.obj!, oldParams: this.getInfo().initialValues, newParams }, this.plugin);
     }
 
+    componentDidMount() {
+        if (super.componentDidMount) super.componentDidMount();
+
+        if (this.props.toggleCollapsed) this.subscribe(this.props.toggleCollapsed, () => this.setState({ isCollapsed: !this.state.isCollapsed }));
+    }
+
     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, isCollapsed: this.props.initiallyCollapsed };
 
     static getDerivedStateFromProps(props: UpdateTransformContol.Props, state: UpdateTransformContol.ComponentState) {
-        if (props.transform === state.transform && props.initiallyCollapsed) return null;
+        if (props.transform === state.transform) return null;
         const cell = props.state.cells.get(props.transform.ref)!;
         const newState: Partial<UpdateTransformContol.ComponentState> = {
             transform: props.transform,