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

mol-plugin-ui: Mat4 UI & removed NumericInput in favor of TextInput.numeric

parent d40ff293
No related branches found
No related tags found
No related merge requests found
...@@ -64,7 +64,8 @@ export interface TextInputProps<T> { ...@@ -64,7 +64,8 @@ export interface TextInputProps<T> {
blurOnEnter?: boolean, blurOnEnter?: boolean,
blurOnEscape?: boolean, blurOnEscape?: boolean,
isDisabled?: boolean, isDisabled?: boolean,
placeholder?: string placeholder?: string,
numeric?: boolean
} }
interface TextInputState { interface TextInputState {
...@@ -116,17 +117,23 @@ export class TextInput<T = string> extends React.PureComponent<TextInputProps<T> ...@@ -116,17 +117,23 @@ export class TextInput<T = string> extends React.PureComponent<TextInputProps<T>
onChange = (e: React.ChangeEvent<HTMLInputElement>) => { onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value; const value = e.target.value;
if (this.props.isValid && !this.props.isValid(value)) { let isInvalid = (this.props.isValid && !this.props.isValid(value)) || (this.props.numeric && Number.isNaN(+value));
if (isInvalid) {
this.clearTimeout(); this.clearTimeout();
this.setState({ value }); this.setState({ value });
return; return;
} }
if (this.props.numeric) {
this.setState({ value }, () => this.triggerChanged(value, +value as any));
} else {
const converted = (this.props.toValue || _id)(value); const converted = (this.props.toValue || _id)(value);
const formatted = (this.props.fromValue || _id)(converted); const formatted = (this.props.fromValue || _id)(converted);
this.setState({ value: formatted }, () => this.triggerChanged(formatted, converted)); this.setState({ value: formatted }, () => this.triggerChanged(formatted, converted));
} }
}
onKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => { onKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.charCode === 27 || e.keyCode === 27 /* esc */) { if (e.charCode === 27 || e.keyCode === 27 /* esc */) {
if (this.props.blurOnEscape && this.input.current) { if (this.props.blurOnEscape && this.input.current) {
...@@ -171,62 +178,6 @@ export class TextInput<T = string> extends React.PureComponent<TextInputProps<T> ...@@ -171,62 +178,6 @@ export class TextInput<T = string> extends React.PureComponent<TextInputProps<T>
} }
} }
// TODO: replace this with parametrized TextInput
export class NumericInput extends React.PureComponent<{
value: number,
onChange: (v: number) => void,
onEnter?: () => void,
onBlur?: () => void,
blurOnEnter?: boolean,
isDisabled?: boolean,
placeholder?: string
}, { value: string }> {
state = { value: '0' };
input = React.createRef<HTMLInputElement>();
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = +e.target.value;
this.setState({ value: e.target.value }, () => {
if (!Number.isNaN(value) && value !== this.props.value) {
this.props.onChange(value);
}
});
}
onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if ((e.keyCode === 13 || e.charCode === 13)) {
if (this.props.blurOnEnter && this.input.current) {
this.input.current.blur();
}
if (this.props.onEnter) this.props.onEnter();
}
e.stopPropagation();
}
onBlur = () => {
this.setState({ value: '' + this.props.value });
if (this.props.onBlur) this.props.onBlur();
}
static getDerivedStateFromProps(props: { value: number }, state: { value: string }) {
const value = +state.value;
if (Number.isNaN(value) || value === props.value) return null;
return { value: '' + props.value };
}
render() {
return <input type='text'
ref={this.input}
onBlur={this.onBlur}
value={this.state.value}
placeholder={this.props.placeholder}
onChange={this.onChange}
onKeyPress={this.props.onEnter || this.props.blurOnEnter ? this.onKeyPress : void 0}
disabled={!!this.props.isDisabled}
/>;
}
}
export class ExpandableControlRow extends React.Component<{ export class ExpandableControlRow extends React.Component<{
label: string, label: string,
colorStripe?: Color, colorStripe?: Color,
......
...@@ -19,7 +19,7 @@ import { camelCaseToWords } from '../../mol-util/string'; ...@@ -19,7 +19,7 @@ import { camelCaseToWords } from '../../mol-util/string';
import { PluginUIComponent, _Props, _State } from '../base'; import { PluginUIComponent, _Props, _State } from '../base';
import { ActionMenu } from './action-menu'; import { ActionMenu } from './action-menu';
import { ColorOptions, ColorValueOption, CombinedColorControl } from './color'; import { ColorOptions, ColorValueOption, CombinedColorControl } from './color';
import { ControlGroup, ExpandGroup, IconButton, NumericInput, ToggleButton, Button, ControlRow } from './common'; import { ControlGroup, ExpandGroup, IconButton, TextInput, ToggleButton, Button, ControlRow } from './common';
import { Icon } from './icons'; import { Icon } from './icons';
import { legendFor } from './legend'; import { legendFor } from './legend';
import LineGraphComponent from './line-graph/line-graph-component'; import LineGraphComponent from './line-graph/line-graph-component';
...@@ -353,7 +353,7 @@ export class NumberInputControl extends React.PureComponent<ParamProps<PD.Numeri ...@@ -353,7 +353,7 @@ export class NumberInputControl extends React.PureComponent<ParamProps<PD.Numeri
return <ControlRow return <ControlRow
title={this.props.param.description} title={this.props.param.description}
label={label} label={label}
control={<NumericInput control={<TextInput numeric
value={parseFloat(this.props.value.toFixed(p))} onEnter={this.props.onEnter} placeholder={placeholder} value={parseFloat(this.props.value.toFixed(p))} onEnter={this.props.onEnter} placeholder={placeholder}
isDisabled={this.props.isDisabled} onChange={this.update} />} />; isDisabled={this.props.isDisabled} onChange={this.update} />} />;
} }
...@@ -724,22 +724,6 @@ export class Mat4Control extends React.PureComponent<ParamProps<PD.Mat4>, { isEx ...@@ -724,22 +724,6 @@ export class Mat4Control extends React.PureComponent<ParamProps<PD.Mat4>, { isEx
state = { isExpanded: false } state = { isExpanded: false }
components = { components = {
0: PD.Numeric(0, undefined, { label: 'Col 0, Row 0' }),
1: PD.Numeric(0, undefined, { label: 'Col 0, Row 1' }),
2: PD.Numeric(0, undefined, { label: 'Col 0, Row 2' }),
3: PD.Numeric(0, undefined, { label: 'Col 0, Row 3' }),
4: PD.Numeric(0, undefined, { label: 'Col 1, Row 0' }),
5: PD.Numeric(0, undefined, { label: 'Col 1, Row 1' }),
6: PD.Numeric(0, undefined, { label: 'Col 1, Row 2' }),
7: PD.Numeric(0, undefined, { label: 'Col 1, Row 3' }),
8: PD.Numeric(0, undefined, { label: 'Col 2, Row 0' }),
9: PD.Numeric(0, undefined, { label: 'Col 2, Row 1' }),
10: PD.Numeric(0, undefined, { label: 'Col 2, Row 2' }),
11: PD.Numeric(0, undefined, { label: 'Col 2, Row 3' }),
12: PD.Numeric(0, undefined, { label: 'Col 3, Row 0' }),
13: PD.Numeric(0, undefined, { label: 'Col 3, Row 1' }),
14: PD.Numeric(0, undefined, { label: 'Col 3, Row 2' }),
15: PD.Numeric(0, undefined, { label: 'Col 3, Row 3' }),
json: PD.Text(JSON.stringify(Mat4()), { description: 'JSON array with 4x4 matrix in a column major (j * 4 + i indexing) format' }) json: PD.Text(JSON.stringify(Mat4()), { description: 'JSON array with 4x4 matrix in a column major (j * 4 + i indexing) format' })
} }
...@@ -763,15 +747,36 @@ export class Mat4Control extends React.PureComponent<ParamProps<PD.Mat4>, { isEx ...@@ -763,15 +747,36 @@ export class Mat4Control extends React.PureComponent<ParamProps<PD.Mat4>, { isEx
e.currentTarget.blur(); e.currentTarget.blur();
} }
changeValue(idx: number) {
return (v: number) => {
const m = Mat4.copy(Mat4(), this.props.value);
m[idx] = v;
this.change(m);
};
}
get grid() {
const v = this.props.value;
const rows: React.ReactNode[] = [];
for (let i = 0; i < 4; i++) {
const row: React.ReactNode[] = [];
for (let j = 0; j < 4; j++) {
row.push(<TextInput key={j} numeric delayMs={50} value={Mat4.getValue(v, i, j)} onChange={this.changeValue(4 * j + i)} className='msp-form-control' blurOnEnter={true} isDisabled={this.props.isDisabled} />);
}
rows.push(<div className='msp-flex-row' key={i}>{row}</div>);
}
return <div className='msp-parameter-matrix'>{rows}</div>;
}
render() { render() {
const v = { const v = {
...this.props.value,
json: JSON.stringify(this.props.value) json: JSON.stringify(this.props.value)
}; };
const label = this.props.param.label || camelCaseToWords(this.props.name); const label = this.props.param.label || camelCaseToWords(this.props.name);
return <> return <>
<ControlRow label={label} control={<button onClick={this.toggleExpanded} disabled={this.props.isDisabled}>{'4\u00D74 Matrix'}</button>} /> <ControlRow label={label} control={<button onClick={this.toggleExpanded} disabled={this.props.isDisabled}>{'4\u00D74 Matrix'}</button>} />
{this.state.isExpanded && <div className='msp-control-offset'> {this.state.isExpanded && <div className='msp-control-offset'>
{this.grid}
<ParameterControls params={this.components} values={v} onChange={this.componentChange} onEnter={this.props.onEnter} /> <ParameterControls params={this.components} values={v} onChange={this.componentChange} onEnter={this.props.onEnter} />
</div>} </div>}
</>; </>;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
*/ */
import * as React from 'react'; import * as React from 'react';
import { NumericInput } from './common'; import { TextInput } from './common';
import { noop } from '../../mol-util'; import { noop } from '../../mol-util';
export class Slider extends React.Component<{ export class Slider extends React.Component<{
...@@ -65,7 +65,7 @@ export class Slider extends React.Component<{ ...@@ -65,7 +65,7 @@ export class Slider extends React.Component<{
onChange={this.updateCurrent as any} onAfterChange={this.end as any} /> onChange={this.updateCurrent as any} onAfterChange={this.end as any} />
</div> </div>
<div> <div>
<NumericInput <TextInput numeric delayMs={50}
value={this.state.current} blurOnEnter={true} onBlur={this.onManualBlur} value={this.state.current} blurOnEnter={true} onBlur={this.onManualBlur}
isDisabled={this.props.disabled} onChange={this.updateManually} /> isDisabled={this.props.disabled} onChange={this.updateManually} />
</div> </div>
...@@ -126,7 +126,7 @@ export class Slider2 extends React.Component<{ ...@@ -126,7 +126,7 @@ export class Slider2 extends React.Component<{
if (step === void 0) step = 1; if (step === void 0) step = 1;
return <div className='msp-slider2'> return <div className='msp-slider2'>
<div> <div>
<NumericInput <TextInput numeric delayMs={50}
value={this.state.current[0]} onEnter={this.props.onEnter} blurOnEnter={true} value={this.state.current[0]} onEnter={this.props.onEnter} blurOnEnter={true}
isDisabled={this.props.disabled} onChange={this.updateMin} /> isDisabled={this.props.disabled} onChange={this.updateMin} />
</div> </div>
...@@ -135,7 +135,7 @@ export class Slider2 extends React.Component<{ ...@@ -135,7 +135,7 @@ export class Slider2 extends React.Component<{
onBeforeChange={this.begin} onChange={this.updateCurrent as any} onAfterChange={this.end as any} range={true} pushable={true} /> onBeforeChange={this.begin} onChange={this.updateCurrent as any} onAfterChange={this.end as any} range={true} pushable={true} />
</div> </div>
<div> <div>
<NumericInput <TextInput numeric delayMs={50}
value={this.state.current[1]} onEnter={this.props.onEnter} blurOnEnter={true} value={this.state.current[1]} onEnter={this.props.onEnter} blurOnEnter={true}
isDisabled={this.props.disabled} onChange={this.updateMax} /> isDisabled={this.props.disabled} onChange={this.updateMax} />
</div> </div>
......
...@@ -531,6 +531,12 @@ ...@@ -531,6 +531,12 @@
} }
} }
.msp-parameter-matrix {
input {
flex: 1 1 auto;
}
}
@mixin type-class-border($name, $color) { @mixin type-class-border($name, $color) {
.msp-type-class-#{$name} { .msp-type-class-#{$name} {
border-left-color: $color; border-left-color: $color;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment