Browse Source

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

David Sehnal 5 years ago
parent
commit
10f7f15f70

+ 12 - 61
src/mol-plugin-ui/controls/common.tsx

@@ -64,7 +64,8 @@ export interface TextInputProps<T> {
     blurOnEnter?: boolean,
     blurOnEscape?: boolean,
     isDisabled?: boolean,
-    placeholder?: string
+    placeholder?: string,
+    numeric?: boolean
 }
 
 interface TextInputState {
@@ -116,15 +117,21 @@ export class TextInput<T = string> extends React.PureComponent<TextInputProps<T>
     onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         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.setState({ value });
             return;
         }
 
-        const converted = (this.props.toValue || _id)(value);
-        const formatted = (this.props.fromValue || _id)(converted);
-        this.setState({ value: formatted }, () => this.triggerChanged(formatted, converted));
+        if (this.props.numeric) {
+            this.setState({ value }, () => this.triggerChanged(value, +value as any));
+        } else {
+            const converted = (this.props.toValue || _id)(value);
+            const formatted = (this.props.fromValue || _id)(converted);
+            this.setState({ value: formatted }, () => this.triggerChanged(formatted, converted));
+        }
+
     }
 
     onKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
@@ -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<{
     label: string,
     colorStripe?: Color,

+ 24 - 19
src/mol-plugin-ui/controls/parameters.tsx

@@ -19,7 +19,7 @@ import { camelCaseToWords } from '../../mol-util/string';
 import { PluginUIComponent, _Props, _State } from '../base';
 import { ActionMenu } from './action-menu';
 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 { legendFor } from './legend';
 import LineGraphComponent from './line-graph/line-graph-component';
@@ -353,7 +353,7 @@ export class NumberInputControl extends React.PureComponent<ParamProps<PD.Numeri
         return <ControlRow
             title={this.props.param.description}
             label={label}
-            control={<NumericInput
+            control={<TextInput numeric
                 value={parseFloat(this.props.value.toFixed(p))} onEnter={this.props.onEnter} placeholder={placeholder}
                 isDisabled={this.props.isDisabled} onChange={this.update} />} />;
     }
@@ -724,22 +724,6 @@ export class Mat4Control extends React.PureComponent<ParamProps<PD.Mat4>, { isEx
     state = { isExpanded: false }
 
     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' })
     }
 
@@ -763,15 +747,36 @@ export class Mat4Control extends React.PureComponent<ParamProps<PD.Mat4>, { isEx
         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() {
         const v = {
-            ...this.props.value,
             json: JSON.stringify(this.props.value)
         };
         const label = this.props.param.label || camelCaseToWords(this.props.name);
         return <>
             <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.grid}
                 <ParameterControls params={this.components} values={v} onChange={this.componentChange} onEnter={this.props.onEnter} />
             </div>}
         </>;

+ 4 - 4
src/mol-plugin-ui/controls/slider.tsx

@@ -6,7 +6,7 @@
  */
 
 import * as React from 'react';
-import { NumericInput } from './common';
+import { TextInput } from './common';
 import { noop } from '../../mol-util';
 
 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} />
             </div>
             <div>
-                <NumericInput
+                <TextInput numeric delayMs={50}
                     value={this.state.current} blurOnEnter={true} onBlur={this.onManualBlur}
                     isDisabled={this.props.disabled} onChange={this.updateManually} />
             </div>
@@ -126,7 +126,7 @@ export class Slider2 extends React.Component<{
         if (step === void 0) step = 1;
         return <div className='msp-slider2'>
             <div>
-                <NumericInput
+                <TextInput numeric delayMs={50}
                     value={this.state.current[0]} onEnter={this.props.onEnter} blurOnEnter={true}
                     isDisabled={this.props.disabled} onChange={this.updateMin} />
             </div>
@@ -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} />
             </div>
             <div>
-                <NumericInput
+                <TextInput numeric delayMs={50}
                     value={this.state.current[1]} onEnter={this.props.onEnter} blurOnEnter={true}
                     isDisabled={this.props.disabled} onChange={this.updateMax} />
             </div>

+ 6 - 0
src/mol-plugin-ui/skin/base/components/misc.scss

@@ -531,6 +531,12 @@
     }
 }
 
+.msp-parameter-matrix {
+    input {
+        flex: 1 1 auto;
+    }
+}
+
 @mixin type-class-border($name, $color) {
     .msp-type-class-#{$name} {
         border-left-color: $color;