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

plugin: wip styling

parent 22a2c3c7
No related branches found
No related tags found
No related merge requests found
...@@ -21,6 +21,7 @@ import { PluginCommand, PluginCommands } from './command'; ...@@ -21,6 +21,7 @@ import { PluginCommand, PluginCommands } from './command';
import { PluginSpec } from './spec'; import { PluginSpec } from './spec';
import { PluginState } from './state'; import { PluginState } from './state';
import { TaskManager } from './util/task-manager'; import { TaskManager } from './util/task-manager';
import { Color } from 'mol-util/color';
export class PluginContext { export class PluginContext {
private disposed = false; private disposed = false;
...@@ -68,6 +69,7 @@ export class PluginContext { ...@@ -68,6 +69,7 @@ export class PluginContext {
initViewer(canvas: HTMLCanvasElement, container: HTMLDivElement) { initViewer(canvas: HTMLCanvasElement, container: HTMLDivElement) {
try { try {
(this.canvas3d as Canvas3D) = Canvas3D.create(canvas, container); (this.canvas3d as Canvas3D) = Canvas3D.create(canvas, container);
this.canvas3d.setProps({ backgroundColor: Color(0xFCFBF9) });
this.canvas3d.animate(); this.canvas3d.animate();
return true; return true;
} catch (e) { } catch (e) {
......
.msp-row {
position: relative;
height: $row-height;
background: $default-background;
margin-top: 1px;
select, button, input[type=text] {
@extend .msp-form-control;
}
button {
@extend .msp-btn;
@extend .msp-btn-block;
}
}
.msp-control-row { .msp-control-row {
position: relative; position: relative;
height: $row-height; height: $row-height;
...@@ -54,41 +70,41 @@ ...@@ -54,41 +70,41 @@
} }
.msp-slider { .msp-slider {
> div { > div:first-child {
> div:first-child { position: absolute;
position: absolute; top: 0;
top: 0; left: 0;
left: 0; bottom: 0;
bottom: 0; right: 50px;
right: 0; width: 100%;
width: 100%; padding-right: 50px;
padding-right: 50px; display: table;
display: table;
> div {
> div {
height: $row-height;
display: table-cell;
vertical-align: middle;
padding: 0 ($control-spacing + 4px);
}
}
> div:last-child {
position: absolute;
height: $row-height; height: $row-height;
right: 0; display: table-cell;
width: 50px; vertical-align: middle;
top: 0; padding: 0 ($control-spacing + 4px);
bottom: 0;
} }
} }
> div:last-child {
input[type=text] { position: absolute;
text-align: right; height: $row-height;
line-height: $row-height;
text-align: center;
right: 0;
width: 50px;
top: 0;
bottom: 0;
} }
input[type=range] { // input[type=text] {
width: 100%; // text-align: right;
} // }
// input[type=range] {
// width: 100%;
// }
} }
.msp-toggle-color-picker { .msp-toggle-color-picker {
...@@ -135,6 +151,42 @@ ...@@ -135,6 +151,42 @@
} }
} }
.msp-control-offset {
// border-left-width: $control-spacing / 2;
// border-left-style: solid;
// border-left-color: color-increase-contrast($default-background, 10%);
// padding-left: 1px;
padding-left: $control-spacing;
}
.msp-control-group-wrapper {
//border-left-width: $control-spacing / 2;
//border-left-style: solid;
//border-left-color: color-increase-contrast($default-background, 10%);
margin-bottom: 1px;
padding-top: 1px;
}
// TODO : get rid of the important
.msp-control-group-header {
> button {
padding-left: $control-spacing / 2 !important;
text-align: left !important;
height: 2 * $row-height / 3 !important;
line-height: 2 * $row-height / 3 !important;
font-size: 70% !important;
background: $default-background !important;
color: color-lower-contrast($font-color, 15%) !important;
}
}
.msp-control-group-footer {
background: color-increase-contrast($default-background, 5%);
height: $control-spacing / 2;
font-size: 1px;
margin-top: 1px;
}
.msp-control-subgroup { .msp-control-subgroup {
margin-top: 1px; margin-top: 1px;
......
...@@ -16,7 +16,16 @@ ...@@ -16,7 +16,16 @@
position: absolute; position: absolute;
} }
.msp-layout-scrollable { .msp-scrollable {
overflow-y: auto;
}
.msp-scrollable-container {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
overflow-y: auto; overflow-y: auto;
} }
...@@ -33,13 +42,12 @@ ...@@ -33,13 +42,12 @@
} }
} }
.msp-layout-right { .msp-layout-right {
.msp-layout-static { .msp-layout-static {
left: 0; left: 0;
right: 0; right: 0;
top: 0; top: 0;
height: $row-height + $control-spacing; bottom: 0; // height: $row-height + $control-spacing;
} }
.msp-layout-scrollable { .msp-layout-scrollable {
......
...@@ -11,8 +11,8 @@ $slider-border-radius-base: 6px; ...@@ -11,8 +11,8 @@ $slider-border-radius-base: 6px;
// layout // layout
$expanded-top-height: 100px; $expanded-top-height: 100px;
$expanded-bottom-height: 3 * $row-height + 2; $expanded-bottom-height: 3 * $row-height + 2;
$expanded-right-width: 290px; $expanded-right-width: 300px;
$expanded-left-width: 290px; $expanded-left-width: 300px;
$expanded-portrait-bottom-height: 10 * ($row-height + 1) + 3 * $control-spacing + 1; $expanded-portrait-bottom-height: 10 * ($row-height + 1) + 3 * $control-spacing + 1;
$expanded-portrait-top-height: 2 * $row-height + 1; $expanded-portrait-top-height: 2 * $row-height + 1;
......
...@@ -19,18 +19,14 @@ namespace CreateStructureRepresentation { ...@@ -19,18 +19,14 @@ namespace CreateStructureRepresentation {
} }
const CreateStructureRepresentation = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Representation3D, CreateStructureRepresentation.Params>({ const CreateStructureRepresentation = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Representation3D, CreateStructureRepresentation.Params>({
name: 'create-structure-representation', name: 'create-structure-representation',
display: { name: 'Create 3D Representation' }, display: { name: '3D Representation' },
from: [SO.Molecule.Structure], from: [SO.Molecule.Structure],
to: [SO.Molecule.Representation3D], to: [SO.Molecule.Representation3D],
params: (a, ctx: PluginContext) => ({ params: (a, ctx: PluginContext) => ({
type: PD.Mapped( type: PD.Mapped(
ctx.structureReprensentation.registry.default.name, ctx.structureReprensentation.registry.default.name,
ctx.structureReprensentation.registry.types, ctx.structureReprensentation.registry.types,
name => PD.Group<any>( name => PD.Group<any>(ctx.structureReprensentation.registry.get(name).getParams(ctx.structureReprensentation.themeCtx, a.data)))
ctx.structureReprensentation.registry.get(name).getParams(ctx.structureReprensentation.themeCtx, a.data),
{ label: 'Type Parameters' }
),
{ label: 'Type' })
}), }),
apply({ a, params }, plugin: PluginContext) { apply({ a, params }, plugin: PluginContext) {
return Task.create('Structure Representation', async ctx => { return Task.create('Structure Representation', async ctx => {
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
// export const ToggleButton = (props: {
// onChange: (v: boolean) => void,
// value: boolean,
// label: string,
// title?: string
// }) => <div className='lm-control-row lm-toggle-button' title={props.title}>
// <span>{props.label}</span>
// <div>
// <button onClick={e => { props.onChange.call(null, !props.value); (e.target as HTMLElement).blur(); }}>
// <span className={ `lm-icon lm-icon-${props.value ? 'ok' : 'off'}` }></span> {props.value ? 'On' : 'Off'}
// </button>
// </div>
// </div>
\ No newline at end of file
...@@ -10,6 +10,7 @@ import { ParamDefinition as PD } from 'mol-util/param-definition'; ...@@ -10,6 +10,7 @@ import { ParamDefinition as PD } from 'mol-util/param-definition';
import { camelCaseToWords } from 'mol-util/string'; import { camelCaseToWords } from 'mol-util/string';
import { ColorNames } from 'mol-util/color/tables'; import { ColorNames } from 'mol-util/color/tables';
import { Color } from 'mol-util/color'; import { Color } from 'mol-util/color';
import { Slider } from './slider';
export interface ParameterControlsProps<P extends PD.Params = PD.Params> { export interface ParameterControlsProps<P extends PD.Params = PD.Params> {
params: P, params: P,
...@@ -38,7 +39,8 @@ function controlFor(param: PD.Any): ParamControl | undefined { ...@@ -38,7 +39,8 @@ function controlFor(param: PD.Any): ParamControl | undefined {
switch (param.type) { switch (param.type) {
case 'value': return void 0; case 'value': return void 0;
case 'boolean': return BoolControl; case 'boolean': return BoolControl;
case 'number': return NumberControl; case 'number': return typeof param.min !== 'undefined' && typeof param.max !== 'undefined'
? NumberRangeControl : NumberInputControl;
case 'converted': return ConvertedControl; case 'converted': return ConvertedControl;
case 'multi-select': return MultiSelectControl; case 'multi-select': return MultiSelectControl;
case 'color': return ColorControl; case 'color': return ColorControl;
...@@ -67,33 +69,42 @@ export abstract class SimpleParam<P extends PD.Any> extends React.PureComponent< ...@@ -67,33 +69,42 @@ export abstract class SimpleParam<P extends PD.Any> extends React.PureComponent<
render() { render() {
const label = this.props.param.label || camelCaseToWords(this.props.name); const label = this.props.param.label || camelCaseToWords(this.props.name);
return <div style={{ padding: '0 3px', borderBottom: '1px solid #ccc' }}> return <div className='msp-control-row'>
<div style={{ lineHeight: '20px', float: 'left' }} title={this.props.param.description}>{label}</div> <span title={this.props.param.description}>{label}</span>
<div style={{ float: 'left', marginLeft: '5px' }}> <div>
{this.renderControl()} {this.renderControl()}
</div> </div>
<div style={{ clear: 'both' }} />
</div>; </div>;
} }
} }
export class BoolControl extends SimpleParam<PD.Boolean> { export class BoolControl extends SimpleParam<PD.Boolean> {
onClick = () => { this.update(!this.props.value); } onClick = (e: React.MouseEvent<HTMLButtonElement>) => { this.update(!this.props.value); e.currentTarget.blur(); }
renderControl() { renderControl() {
return <button onClick={this.onClick} disabled={this.props.isDisabled}>{this.props.value ? '✓ On' : '✗ Off'}</button>; return <button onClick={this.onClick} disabled={this.props.isDisabled}>
<span className={`msp-icon msp-icon-${this.props.value ? 'ok' : 'off'}`} />
{this.props.value ? 'On' : 'Off'}
</button>;
} }
} }
export class NumberControl extends SimpleParam<PD.Numeric> { export class NumberInputControl extends SimpleParam<PD.Numeric> {
onChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.update(+e.target.value); } onChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.update(+e.target.value); }
renderControl() { renderControl() {
return <span> return <span>
<input type='range' value={'' + this.props.value} min={this.props.param.min} max={this.props.param.max} step={this.props.param.step} onChange={this.onChange} disabled={this.props.isDisabled} /> number input TODO
<br />{this.props.value}
</span> </span>
} }
} }
export class NumberRangeControl extends SimpleParam<PD.Numeric> {
onChange = (v: number) => { this.update(v); }
renderControl() {
return <Slider value={this.props.value} min={this.props.param.min!} max={this.props.param.max!}
step={this.props.param.step} onChange={this.onChange} disabled={this.props.isDisabled} />
}
}
export class TextControl extends SimpleParam<PD.Text> { export class TextControl extends SimpleParam<PD.Text> {
onChange = (e: React.ChangeEvent<HTMLInputElement>) => { onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value; const value = e.target.value;
...@@ -153,34 +164,53 @@ export class ColorControl extends SimpleParam<PD.Color> { ...@@ -153,34 +164,53 @@ export class ColorControl extends SimpleParam<PD.Color> {
} }
} }
export class MultiSelectControl extends React.PureComponent<ParamProps<PD.MultiSelect<any>>> { export class MultiSelectControl extends React.PureComponent<ParamProps<PD.MultiSelect<any>>, { isExpanded: boolean }> {
state = { isExpanded: false }
change(value: PD.MultiSelect<any>['defaultValue'] ) { change(value: PD.MultiSelect<any>['defaultValue'] ) {
this.props.onChange({ name: this.props.name, param: this.props.param, value }); this.props.onChange({ name: this.props.name, param: this.props.param, value });
} }
toggle(key: string) { toggle(key: string) {
return () => { return (e: React.MouseEvent<HTMLButtonElement>) => {
if (this.props.value.indexOf(key) < 0) this.change(this.props.value.concat(key)); if (this.props.value.indexOf(key) < 0) this.change(this.props.value.concat(key));
else this.change(this.props.value.filter(v => v !== key)) else this.change(this.props.value.filter(v => v !== key));
e.currentTarget.blur();
} }
} }
toggleExpanded = (e: React.MouseEvent<HTMLButtonElement>) => {
this.setState({ isExpanded: !this.state.isExpanded });
e.currentTarget.blur();
}
render() { render() {
const current = this.props.value; const current = this.props.value;
const label = this.props.param.label || camelCaseToWords(this.props.name); const label = this.props.param.label || camelCaseToWords(this.props.name);
return <div> return <>
<div>{label} <small>{`${current.length} of ${this.props.param.options.length}`}</small></div> <div className='msp-control-row'>
<div style={{ paddingLeft: '7px' }}> <span>{label}</span>
<div>
<button onClick={this.toggleExpanded}>
{`${current.length} of ${this.props.param.options.length}`}
</button>
</div>
</div>
<div className='msp-control-offset' style={{ display: this.state.isExpanded ? 'block' : 'none' }}>
{this.props.param.options.map(([value, label]) => {this.props.param.options.map(([value, label]) =>
<button key={value} onClick={this.toggle(value)} disabled={this.props.isDisabled}> <div key={value} className='msp-row'>
{current.indexOf(value) >= 0 ? `✓ ${label}` : `✗ ${label}`} <button onClick={this.toggle(value)} disabled={this.props.isDisabled}>
</button>)} {current.indexOf(value) >= 0 ? `✓ ${label}` : `✗ ${label}`}
</button>
</div>)}
</div> </div>
</div>; </>;
} }
} }
export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>> { export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>, { isExpanded: boolean }> {
state = { isExpanded: false }
change(value: PD.Mapped<any>['defaultValue'] ) { change(value: PD.Mapped<any>['defaultValue'] ) {
this.props.onChange({ name: this.props.name, param: this.props.param, value }); this.props.onChange({ name: this.props.name, param: this.props.param, value });
} }
...@@ -190,15 +220,25 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>> ...@@ -190,15 +220,25 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>>
this.change({ ...value.params, [e.name]: e.value }); this.change({ ...value.params, [e.name]: e.value });
} }
toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded });
render() { render() {
const value: PD.Mapped<any>['defaultValue'] = this.props.value; const value: PD.Mapped<any>['defaultValue'] = this.props.value;
const params = this.props.param.params; const params = this.props.param.params;
const label = this.props.param.label || camelCaseToWords(this.props.name); const label = this.props.param.label || camelCaseToWords(this.props.name);
// TODO toggle panel // TODO toggle panel
return <div> return <div className='msp-control-group-wrapper'>
<div>{label}</div> <div className='msp-control-group-header'>
<ParameterControls params={params} onChange={this.onChangeParam} values={value.params} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} /> <button className='msp-btn msp-btn-block' onClick={this.toggleExpanded}>
<span className={`msp-icon msp-icon-${this.state.isExpanded ? 'collapse' : 'expand'}`} />
{label}
</button>
</div>
{this.state.isExpanded && <div className='msp-control-offset' style={{ display: this.state.isExpanded ? 'block' : 'none' }}>
<ParameterControls params={params} onChange={this.onChangeParam} values={value.params} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} />
</div>
}
</div> </div>
} }
} }
...@@ -234,9 +274,7 @@ export class MappedControl extends React.PureComponent<ParamProps<PD.Mapped<any> ...@@ -234,9 +274,7 @@ export class MappedControl extends React.PureComponent<ParamProps<PD.Mapped<any>
return <div> return <div>
{select} {select}
<div style={{ borderLeft: '5px solid #777', paddingLeft: '5px' }}> <Mapped param={param} value={value} name={`${label} Properties`} onChange={this.onChangeParam} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} />
<Mapped param={param} value={value} name='' onChange={this.onChangeParam} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} />
</div>
</div> </div>
} }
} }
......
This diff is collapsed.
...@@ -21,39 +21,51 @@ import { PluginState } from 'mol-plugin/state'; ...@@ -21,39 +21,51 @@ import { PluginState } from 'mol-plugin/state';
import { UpdateTransformContol } from './state/update-transform'; import { UpdateTransformContol } from './state/update-transform';
export class Plugin extends React.Component<{ plugin: PluginContext }, {}> { export class Plugin extends React.Component<{ plugin: PluginContext }, {}> {
region(kind: 'left' | 'right' | 'bottom' | 'main', element: JSX.Element) {
return <div className={`msp-layout-region msp-layout-${kind}`}>
<div className='msp-layout-static'>
{element}
</div>
</div>
}
render() { render() {
return <PluginReactContext.Provider value={this.props.plugin}> return <PluginReactContext.Provider value={this.props.plugin}>
<div style={{ position: 'absolute', width: '100%', height: '100%', fontFamily: 'monospace' }}> <div className='msp-plugin'>
<div style={{ position: 'absolute', width: '350px', height: '100%', overflowY: 'scroll', padding: '10px' }}> <div className='msp-plugin-content msp-layout-expanded'>
<State /> <div className='msp-layout-hide-top'>
</div> {this.region('main', <ViewportWrapper />)}
<div style={{ position: 'absolute', left: '350px', right: '300px', top: '0', bottom: '100px' }}> {this.region('left', <State />)}
<Viewport /> {this.region('right', <div className='msp-scrollable-container'>
<div style={{ position: 'absolute', left: '10px', top: '10px', height: '100%', color: 'white' }}> <CurrentObject />
<TrajectoryControls /> <Controls />
</div> <CameraSnapshots />
<ViewportControls /> <StateSnapshots />
<div style={{ position: 'absolute', left: '10px', bottom: '10px', color: 'white' }}> </div>)}
<BackgroundTaskProgress /> {this.region('bottom', <Log />)}
</div> </div>
</div> </div>
<div style={{ position: 'absolute', width: '300px', right: '0', top: '0', bottom: '0', padding: '10px', overflowY: 'scroll' }}>
<CurrentObject />
<hr />
<Controls />
<hr />
<CameraSnapshots />
<hr />
<StateSnapshots />
</div>
<div style={{ position: 'absolute', right: '300px', left: '350px', bottom: '0', height: '100px', overflow: 'hidden' }}>
<Log />
</div>
</div> </div>
</PluginReactContext.Provider>; </PluginReactContext.Provider>;
} }
} }
export class ViewportWrapper extends PluginComponent {
render() {
return <>
<Viewport />
<div style={{ position: 'absolute', left: '10px', top: '10px', height: '100%', color: 'white' }}>
<TrajectoryControls />
</div>
<ViewportControls />
<div style={{ position: 'absolute', left: '10px', bottom: '10px', color: 'white' }}>
<BackgroundTaskProgress />
</div>
</>;
}
}
export class State extends PluginComponent { export class State extends PluginComponent {
componentDidMount() { componentDidMount() {
this.subscribe(this.plugin.state.behavior.kind, () => this.forceUpdate()); this.subscribe(this.plugin.state.behavior.kind, () => this.forceUpdate());
......
...@@ -83,8 +83,8 @@ export class Viewport extends PluginComponent<{ }, ViewportState> { ...@@ -83,8 +83,8 @@ export class Viewport extends PluginComponent<{ }, ViewportState> {
render() { render() {
if (this.state.noWebGl) return this.renderMissing(); if (this.state.noWebGl) return this.renderMissing();
return <div style={{ backgroundColor: 'rgb(0, 0, 0)', width: '100%', height: '100%'}}> return <div className='msp-viewport'>
<div ref={elm => this.container = elm} style={{width: '100%', height: '100%'}}> <div className='msp-viewport-host3d' ref={elm => this.container = elm}>
<canvas ref={elm => this.canvas = elm}></canvas> <canvas ref={elm => this.canvas = elm}></canvas>
</div> </div>
</div>; </div>;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment