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

mol-plugin: tree UI

parent 467463db
No related branches found
No related tags found
No related merge requests found
...@@ -78,10 +78,31 @@ ...@@ -78,10 +78,31 @@
margin-bottom: 1px; margin-bottom: 1px;
padding-left: $row-height; padding-left: $row-height;
padding-right: 2 * $row-height + $control-spacing; 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 { &-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 @@ ...@@ -99,6 +120,7 @@
left: 0; left: 0;
top: 0; top: 0;
width: $row-height; width: $row-height;
padding: 0;
color: color-lower-contrast($font-color, 24%); color: color-lower-contrast($font-color, 24%);
} }
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
} }
} }
.msp-layout-right { .msp-layout-right, .msp-layout-left {
background: $control-background; background: $control-background;
} }
...@@ -43,7 +43,8 @@ ...@@ -43,7 +43,8 @@
} }
.msp-transform-update-wrapper { .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 { .msp-transform-update-wrapper-collapsed {
...@@ -54,8 +55,9 @@ ...@@ -54,8 +55,9 @@
> .msp-transform-header > button { > .msp-transform-header > button {
text-align: left; text-align: left;
padding-left: $row-height; padding-left: $row-height;
background: $control-background; // color-lower-contrast($default-background, 4%); line-height: 24px;
font-weight: bold; background: color-lower-contrast($control-background, 4%); // $control-background; // color-lower-contrast($default-background, 4%);
// font-weight: bold;
} }
} }
......
...@@ -89,8 +89,8 @@ export const OpenStructure = StateAction.build({ ...@@ -89,8 +89,8 @@ export const OpenStructure = StateAction.build({
function createModelTree(b: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, format: 'pdb' | 'cif' = 'cif') { function createModelTree(b: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, format: 'pdb' | 'cif' = 'cif') {
const parsed = format === 'cif' const parsed = format === 'cif'
? b.apply(StateTransforms.Data.ParseCif, void 0, { props: { isGhost: true }}).apply(StateTransforms.Model.TrajectoryFromMmCif) ? b.apply(StateTransforms.Data.ParseCif, void 0, { props: { isGhost: true }}).apply(StateTransforms.Model.TrajectoryFromMmCif, void 0, { props: { isGhost: true }})
: b.apply(StateTransforms.Model.TrajectoryFromPDB); : b.apply(StateTransforms.Model.TrajectoryFromPDB, void 0, { props: { isGhost: true }});
return parsed.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }); return parsed.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 });
} }
......
...@@ -85,7 +85,7 @@ const plus1 = (v: number) => v + 1, minus1 = (v: number) => v - 1; ...@@ -85,7 +85,7 @@ const plus1 = (v: number) => v + 1, minus1 = (v: number) => v - 1;
type ModelFromTrajectory = typeof ModelFromTrajectory type ModelFromTrajectory = typeof ModelFromTrajectory
const ModelFromTrajectory = PluginStateTransform.BuiltIn({ const ModelFromTrajectory = PluginStateTransform.BuiltIn({
name: 'model-from-trajectory', 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, from: SO.Molecule.Trajectory,
to: SO.Molecule.Model, to: SO.Molecule.Model,
params: a => { params: a => {
...@@ -99,7 +99,9 @@ const ModelFromTrajectory = PluginStateTransform.BuiltIn({ ...@@ -99,7 +99,9 @@ const ModelFromTrajectory = PluginStateTransform.BuiltIn({
apply({ a, params }) { apply({ a, params }) {
if (params.modelIndex < 0 || params.modelIndex >= a.data.length) throw new Error(`Invalid modelIndex ${params.modelIndex}`); if (params.modelIndex < 0 || params.modelIndex >= a.data.length) throw new Error(`Invalid modelIndex ${params.modelIndex}`);
const model = a.data[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); return new SO.Molecule.Model(model, props);
} }
}); });
......
...@@ -31,7 +31,7 @@ export class ParameterControls<P extends PD.Params> extends React.PureComponent< ...@@ -31,7 +31,7 @@ export class ParameterControls<P extends PD.Params> extends React.PureComponent<
const values = this.props.values; const values = this.props.values;
const keys = Object.keys(params); const keys = Object.keys(params);
if (keys.length === 0) return null; if (keys.length === 0) return null;
return <div style={{ width: '100%' }}> return <>
{keys.map(key => { {keys.map(key => {
const param = params[key]; const param = params[key];
if (param.isHidden) return null; if (param.isHidden) return null;
...@@ -39,7 +39,7 @@ export class ParameterControls<P extends PD.Params> extends React.PureComponent< ...@@ -39,7 +39,7 @@ export class ParameterControls<P extends PD.Params> extends React.PureComponent<
if (!Control) return null; 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]} /> return <Control param={param} key={key} onChange={this.props.onChange} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} name={key} value={values[key]} />
})} })}
</div>; </>;
} }
} }
......
...@@ -11,6 +11,7 @@ import { PluginCommands } from 'mol-plugin/command'; ...@@ -11,6 +11,7 @@ import { PluginCommands } from 'mol-plugin/command';
import { PluginUIComponent } from '../base'; import { PluginUIComponent } from '../base';
import { UpdateTransformContol } from './update-transform'; import { UpdateTransformContol } from './update-transform';
import { StateObjectActions } from './actions'; import { StateObjectActions } from './actions';
import { Observable, Subject } from 'rxjs';
export class StateTree extends PluginUIComponent<{ state: State }, { showActions: boolean }> { export class StateTree extends PluginUIComponent<{ state: State }, { showActions: boolean }> {
state = { showActions: true }; state = { showActions: true };
...@@ -112,7 +113,10 @@ class StateTreeNode extends PluginUIComponent<{ nodeRef: string, state: State, d ...@@ -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) { is(e: State.ObjectEvent) {
return e.ref === this.props.nodeRef && e.state === this.props.state; return e.ref === this.props.nodeRef && e.state === this.props.state;
} }
...@@ -142,7 +146,8 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta ...@@ -142,7 +146,8 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
state = { state = {
isCurrent: this.props.state.current === this.props.nodeRef, isCurrent: this.props.state.current === this.props.nodeRef,
isCollapsed: this.props.state.cellStates.get(this.props.nodeRef).isCollapsed, 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 }) { 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 ...@@ -150,12 +155,14 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
return { return {
isCurrent: props.state.current === props.nodeRef, isCurrent: props.state.current === props.nodeRef,
isCollapsed: props.state.cellStates.get(props.nodeRef).isCollapsed, isCollapsed: props.state.cellStates.get(props.nodeRef).isCollapsed,
state: props.state state: props.state,
updaterCollapsed: true
}; };
} }
setCurrent = (e: React.MouseEvent<HTMLElement>) => { setCurrent = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault(); e.preventDefault();
e.currentTarget.blur();
PluginCommands.State.SetCurrentObject.dispatch(this.plugin, { state: this.props.state, ref: this.props.nodeRef }); 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 ...@@ -188,6 +195,13 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
e.currentTarget.blur(); e.currentTarget.blur();
} }
private toggleUpdaterObs = new Subject();
toggleUpdater = (e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault();
e.currentTarget.blur();
this.toggleUpdaterObs.next();
}
render() { render() {
const n = this.props.state.transforms.get(this.props.nodeRef)!; const n = this.props.state.transforms.get(this.props.nodeRef)!;
const cell = this.props.state.cells.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 ...@@ -207,9 +221,9 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
const obj = cell.obj as PluginStateObject.Any; const obj = cell.obj as PluginStateObject.Any;
const title = `${obj.label} ${obj.description ? obj.description : ''}` const title = `${obj.label} ${obj.description ? obj.description : ''}`
if (this.state.isCurrent) { 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 { } 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 ...@@ -221,7 +235,7 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
</button>; </button>;
const row = <div className={`msp-tree-row${isCurrent ? ' msp-tree-row-current' : ''}`} onMouseEnter={this.highlight} onMouseLeave={this.clearHighlight} 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} {label}
{children.size > 0 && <button onClick={this.toggleExpanded} className='msp-btn msp-btn-link msp-tree-toggle-exp-button'> {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'}`} /> <span className={`msp-icon msp-icon-${cellState.isCollapsed ? 'expand' : 'collapse'}`} />
...@@ -234,7 +248,7 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta ...@@ -234,7 +248,7 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta
if (this.state.isCurrent) { if (this.state.isCurrent) {
return <> return <>
{row} {row}
<StateTreeNodeTransform {...this.props}/> <StateTreeNodeTransform {...this.props} toggleCollapsed={this.toggleUpdaterObs} />
</> </>
} }
...@@ -242,7 +256,7 @@ class StateTreeNodeLabel extends PluginUIComponent<{ nodeRef: string, state: Sta ...@@ -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() { render() {
const ref = this.props.nodeRef; const ref = this.props.nodeRef;
const cell = this.props.state.cells.get(ref)!; const cell = this.props.state.cells.get(ref)!;
...@@ -251,6 +265,6 @@ class StateTreeNodeTransform extends PluginUIComponent<{ nodeRef: string, state: ...@@ -251,6 +265,6 @@ class StateTreeNodeTransform extends PluginUIComponent<{ nodeRef: string, state:
if (!parent || parent.status !== 'ok') return null; if (!parent || parent.status !== 'ok') return null;
const transform = cell.transform; 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} />;
} }
} }
\ No newline at end of file
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
import { State, StateTransform } from 'mol-state'; import { State, StateTransform } from 'mol-state';
import { memoizeLatest } from 'mol-util/memoize'; import { memoizeLatest } from 'mol-util/memoize';
import { StateTransformParameters, TransformContolBase } from './common'; import { StateTransformParameters, TransformContolBase } from './common';
import { Observable } from 'rxjs';
export { UpdateTransformContol }; export { UpdateTransformContol };
...@@ -14,6 +15,7 @@ namespace UpdateTransformContol { ...@@ -14,6 +15,7 @@ namespace UpdateTransformContol {
export interface Props { export interface Props {
transform: StateTransform, transform: StateTransform,
state: State, state: State,
toggleCollapsed?: Observable<any>,
initiallyCollapsed?: boolean initiallyCollapsed?: boolean
} }
...@@ -43,12 +45,18 @@ class UpdateTransformContol extends TransformContolBase<UpdateTransformContol.Pr ...@@ -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); 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)); 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 }; 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) { 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 cell = props.state.cells.get(props.transform.ref)!;
const newState: Partial<UpdateTransformContol.ComponentState> = { const newState: Partial<UpdateTransformContol.ComponentState> = {
transform: props.transform, transform: props.transform,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment