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';
import { PluginSpec } from './spec';
import { PluginState } from './state';
import { TaskManager } from './util/task-manager';
import { Color } from 'mol-util/color';
export class PluginContext {
private disposed = false;
......@@ -68,6 +69,7 @@ export class PluginContext {
initViewer(canvas: HTMLCanvasElement, container: HTMLDivElement) {
try {
(this.canvas3d as Canvas3D) = Canvas3D.create(canvas, container);
this.canvas3d.setProps({ backgroundColor: Color(0xFCFBF9) });
this.canvas3d.animate();
return true;
} 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 {
position: relative;
height: $row-height;
......@@ -54,13 +70,12 @@
}
.msp-slider {
> div {
> div:first-child {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
right: 50px;
width: 100%;
padding-right: 50px;
display: table;
......@@ -75,20 +90,21 @@
> div:last-child {
position: absolute;
height: $row-height;
line-height: $row-height;
text-align: center;
right: 0;
width: 50px;
top: 0;
bottom: 0;
}
}
input[type=text] {
text-align: right;
}
// input[type=text] {
// text-align: right;
// }
input[type=range] {
width: 100%;
}
// input[type=range] {
// width: 100%;
// }
}
.msp-toggle-color-picker {
......@@ -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 {
margin-top: 1px;
......
......@@ -16,7 +16,16 @@
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;
}
......@@ -34,12 +43,11 @@
}
.msp-layout-right {
.msp-layout-static {
left: 0;
right: 0;
top: 0;
height: $row-height + $control-spacing;
bottom: 0; // height: $row-height + $control-spacing;
}
.msp-layout-scrollable {
......
......@@ -11,8 +11,8 @@ $slider-border-radius-base: 6px;
// layout
$expanded-top-height: 100px;
$expanded-bottom-height: 3 * $row-height + 2;
$expanded-right-width: 290px;
$expanded-left-width: 290px;
$expanded-right-width: 300px;
$expanded-left-width: 300px;
$expanded-portrait-bottom-height: 10 * ($row-height + 1) + 3 * $control-spacing + 1;
$expanded-portrait-top-height: 2 * $row-height + 1;
......
......@@ -19,18 +19,14 @@ namespace CreateStructureRepresentation {
}
const CreateStructureRepresentation = PluginStateTransform.Create<SO.Molecule.Structure, SO.Molecule.Representation3D, CreateStructureRepresentation.Params>({
name: 'create-structure-representation',
display: { name: 'Create 3D Representation' },
display: { name: '3D Representation' },
from: [SO.Molecule.Structure],
to: [SO.Molecule.Representation3D],
params: (a, ctx: PluginContext) => ({
type: PD.Mapped(
ctx.structureReprensentation.registry.default.name,
ctx.structureReprensentation.registry.types,
name => PD.Group<any>(
ctx.structureReprensentation.registry.get(name).getParams(ctx.structureReprensentation.themeCtx, a.data),
{ label: 'Type Parameters' }
),
{ label: 'Type' })
name => PD.Group<any>(ctx.structureReprensentation.registry.get(name).getParams(ctx.structureReprensentation.themeCtx, a.data)))
}),
apply({ a, params }, plugin: PluginContext) {
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';
import { camelCaseToWords } from 'mol-util/string';
import { ColorNames } from 'mol-util/color/tables';
import { Color } from 'mol-util/color';
import { Slider } from './slider';
export interface ParameterControlsProps<P extends PD.Params = PD.Params> {
params: P,
......@@ -38,7 +39,8 @@ function controlFor(param: PD.Any): ParamControl | undefined {
switch (param.type) {
case 'value': return void 0;
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 'multi-select': return MultiSelectControl;
case 'color': return ColorControl;
......@@ -67,33 +69,42 @@ export abstract class SimpleParam<P extends PD.Any> extends React.PureComponent<
render() {
const label = this.props.param.label || camelCaseToWords(this.props.name);
return <div style={{ padding: '0 3px', borderBottom: '1px solid #ccc' }}>
<div style={{ lineHeight: '20px', float: 'left' }} title={this.props.param.description}>{label}</div>
<div style={{ float: 'left', marginLeft: '5px' }}>
return <div className='msp-control-row'>
<span title={this.props.param.description}>{label}</span>
<div>
{this.renderControl()}
</div>
<div style={{ clear: 'both' }} />
</div>;
}
}
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() {
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); }
renderControl() {
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} />
<br />{this.props.value}
number input TODO
</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> {
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
......@@ -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'] ) {
this.props.onChange({ name: this.props.name, param: this.props.param, value });
}
toggle(key: string) {
return () => {
return (e: React.MouseEvent<HTMLButtonElement>) => {
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() {
const current = this.props.value;
const label = this.props.param.label || camelCaseToWords(this.props.name);
return <div>
<div>{label} <small>{`${current.length} of ${this.props.param.options.length}`}</small></div>
<div style={{ paddingLeft: '7px' }}>
return <>
<div className='msp-control-row'>
<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]) =>
<button key={value} onClick={this.toggle(value)} disabled={this.props.isDisabled}>
<div key={value} className='msp-row'>
<button onClick={this.toggle(value)} disabled={this.props.isDisabled}>
{current.indexOf(value) >= 0 ? `✓ ${label}` : `✗ ${label}`}
</button>)}
</button>
</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'] ) {
this.props.onChange({ name: this.props.name, param: this.props.param, value });
}
......@@ -190,17 +220,27 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>>
this.change({ ...value.params, [e.name]: e.value });
}
toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded });
render() {
const value: PD.Mapped<any>['defaultValue'] = this.props.value;
const params = this.props.param.params;
const label = this.props.param.label || camelCaseToWords(this.props.name);
// TODO toggle panel
return <div>
<div>{label}</div>
return <div className='msp-control-group-wrapper'>
<div className='msp-control-group-header'>
<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>
}
}
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>
{select}
<div style={{ borderLeft: '5px solid #777', paddingLeft: '5px' }}>
<Mapped param={param} value={value} name='' onChange={this.onChangeParam} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} />
</div>
<Mapped param={param} value={value} name={`${label} Properties`} onChange={this.onChangeParam} onEnter={this.props.onEnter} isDisabled={this.props.isDisabled} />
</div>
}
}
......
This diff is collapsed.
......@@ -21,13 +21,39 @@ import { PluginState } from 'mol-plugin/state';
import { UpdateTransformContol } from './state/update-transform';
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() {
return <PluginReactContext.Provider value={this.props.plugin}>
<div style={{ position: 'absolute', width: '100%', height: '100%', fontFamily: 'monospace' }}>
<div style={{ position: 'absolute', width: '350px', height: '100%', overflowY: 'scroll', padding: '10px' }}>
<State />
<div className='msp-plugin'>
<div className='msp-plugin-content msp-layout-expanded'>
<div className='msp-layout-hide-top'>
{this.region('main', <ViewportWrapper />)}
{this.region('left', <State />)}
{this.region('right', <div className='msp-scrollable-container'>
<CurrentObject />
<Controls />
<CameraSnapshots />
<StateSnapshots />
</div>)}
{this.region('bottom', <Log />)}
</div>
</div>
<div style={{ position: 'absolute', left: '350px', right: '300px', top: '0', bottom: '100px' }}>
</div>
</PluginReactContext.Provider>;
}
}
export class ViewportWrapper extends PluginComponent {
render() {
return <>
<Viewport />
<div style={{ position: 'absolute', left: '10px', top: '10px', height: '100%', color: 'white' }}>
<TrajectoryControls />
......@@ -36,21 +62,7 @@ export class Plugin extends React.Component<{ plugin: PluginContext }, {}> {
<div style={{ position: 'absolute', left: '10px', bottom: '10px', color: 'white' }}>
<BackgroundTaskProgress />
</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>
</PluginReactContext.Provider>;
</>;
}
}
......
......@@ -83,8 +83,8 @@ export class Viewport extends PluginComponent<{ }, ViewportState> {
render() {
if (this.state.noWebGl) return this.renderMissing();
return <div style={{ backgroundColor: 'rgb(0, 0, 0)', width: '100%', height: '100%'}}>
<div ref={elm => this.container = elm} style={{width: '100%', height: '100%'}}>
return <div className='msp-viewport'>
<div className='msp-viewport-host3d' ref={elm => this.container = elm}>
<canvas ref={elm => this.canvas = elm}></canvas>
</div>
</div>;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment