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

mol-plugin: added TextInput wrapper and RGB color input

parent 6f36a3c7
No related branches found
No related tags found
No related merge requests found
...@@ -11,6 +11,7 @@ import { camelCaseToWords } from '../../../mol-util/string'; ...@@ -11,6 +11,7 @@ import { camelCaseToWords } from '../../../mol-util/string';
import * as React from 'react'; import * as React from 'react';
import { _Props, _State } from '../base'; import { _Props, _State } from '../base';
import { ParamProps } from './parameters'; import { ParamProps } from './parameters';
import { TextInput } from './common';
export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Color>, { isExpanded: boolean }> { export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Color>, { isExpanded: boolean }> {
state = { isExpanded: false } state = { isExpanded: false }
...@@ -38,6 +39,12 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo ...@@ -38,6 +39,12 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo
} }
} }
onChangeText = (value: Color) => {
if (value !== this.props.value) {
this.update(value);
}
}
swatch() { swatch() {
// const def = this.props.param.defaultValue; // const def = this.props.param.defaultValue;
return <div className='msp-combined-color-swatch'> return <div className='msp-combined-color-swatch'>
...@@ -46,25 +53,6 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo ...@@ -46,25 +53,6 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo
</div>; </div>;
} }
// TODO: include text options as well?
// onChangeText = () => {};
// text() {
// const [r, g, b] = Color.toRgb(this.props.value);
// return <input type='text'
// value={`${r} ${g} ${b}`}
// placeholder={'Red Green Blue'}
// onChange={this.onChangeText}
// onKeyPress={this.props.onEnter ? this.onKeyPress : void 0}
// disabled={this.props.isDisabled}
// />;
// }
// onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
// if (!this.props.onEnter) return;
// if ((e.keyCode === 13 || e.charCode === 13)) {
// this.props.onEnter();
// }
// }
stripStyle(): React.CSSProperties { stripStyle(): React.CSSProperties {
return { return {
background: Color.toStyle(this.props.value), background: Color.toStyle(this.props.value),
...@@ -76,7 +64,6 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo ...@@ -76,7 +64,6 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo
}; };
} }
render() { render() {
const label = this.props.param.label || camelCaseToWords(this.props.name); const label = this.props.param.label || camelCaseToWords(this.props.name);
return <> return <>
...@@ -89,7 +76,17 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo ...@@ -89,7 +76,17 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo
{this.state.isExpanded && <div className='msp-control-offset'> {this.state.isExpanded && <div className='msp-control-offset'>
{this.swatch()} {this.swatch()}
<div className='msp-control-row'> <div className='msp-control-row'>
<div style={{ position: 'relative' }}> <span>RGB</span>
<div>
<TextInput onChange={this.onChangeText} value={this.props.value}
fromValue={formatColorRGB} toValue={getColorFromString} isValid={isValidColorString}
className='msp-form-control' onEnter={this.props.onEnter} blurOnEnter={true} blurOnEscape={true}
placeholder='e.g. 127 127 127' delayMs={250} />
</div>
</div>
<div className='msp-control-row'>
<span>Color List</span>
<div>
<select value={this.props.value} onChange={this.onChangeSelect}> <select value={this.props.value} onChange={this.onChangeSelect}>
{ColorValueOption(this.props.value)} {ColorValueOption(this.props.value)}
{ColorOptions()} {ColorOptions()}
...@@ -102,6 +99,28 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo ...@@ -102,6 +99,28 @@ export class CombinedColorControl extends React.PureComponent<ParamProps<PD.Colo
} }
} }
function formatColorRGB(c: Color) {
const [r, g, b] = Color.toRgb(c);
return `${r} ${g} ${b}`;
}
function getColorFromString(s: string) {
const cs = s.split(/\s+/g);
return Color.fromRgb(+cs[0], +cs[1], +cs[2]);
}
function isValidColorString(s: string) {
const cs = s.split(/\s+/g);
if (cs.length !== 3 && !(cs.length === 4 && cs[3] === '')) return false;
for (const c of cs) {
if (c === '') continue;
const n = +c;
if ('' + n !== c) return false;
if (n < 0 || n > 255) return false;
}
return true;
}
// the 1st color is the default value. // the 1st color is the default value.
const SwatchColors = [ const SwatchColors = [
0x000000, 0x808080, 0xFFFFFF, 0xD33115, 0xE27300, 0xFCC400, 0x000000, 0x808080, 0xFFFFFF, 0xD33115, 0xE27300, 0xFCC400,
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
import * as React from 'react'; import * as React from 'react';
import { Color } from '../../../mol-util/color'; import { Color } from '../../../mol-util/color';
import { PurePluginUIComponent } from '../base';
export class ControlGroup extends React.Component<{ header: string, initialExpanded?: boolean }, { isExpanded: boolean }> { export class ControlGroup extends React.Component<{ header: string, initialExpanded?: boolean }, { isExpanded: boolean }> {
state = { isExpanded: !!this.props.initialExpanded } state = { isExpanded: !!this.props.initialExpanded }
...@@ -28,6 +29,128 @@ export class ControlGroup extends React.Component<{ header: string, initialExpan ...@@ -28,6 +29,128 @@ export class ControlGroup extends React.Component<{ header: string, initialExpan
} }
} }
export interface TextInputProps<T> {
className?: string,
style?: React.CSSProperties,
value: T,
fromValue?(v: T): string,
toValue?(s: string): T,
// TODO: add error/help messages here?
isValid?(s: string): boolean,
onChange(value: T): void,
onEnter?(): void,
onBlur?(): void,
delayMs?: number,
blurOnEnter?: boolean,
blurOnEscape?: boolean,
isDisabled?: boolean,
placeholder?: string
}
interface TextInputState {
originalValue: string,
value: string
}
function _id(x: any) { return x; }
export class TextInput<T = string> extends PurePluginUIComponent<TextInputProps<T>, TextInputState> {
private input = React.createRef<HTMLInputElement>();
private delayHandle: any = void 0;
private pendingValue: T | undefined = void 0;
state = { originalValue: '', value: '' }
onBlur = () => {
this.setState({ value: '' + this.state.originalValue });
if (this.props.onBlur) this.props.onBlur();
}
get isPending() { return typeof this.delayHandle !== 'undefined'; }
clearTimeout() {
if (this.isPending) {
clearTimeout(this.delayHandle);
this.delayHandle = void 0;
}
}
raiseOnChange = () => {
this.props.onChange(this.pendingValue!);
this.pendingValue = void 0;
}
triggerChanged(formatted: string, converted: T) {
this.clearTimeout();
if (formatted === this.state.originalValue) return;
if (this.props.delayMs) {
this.pendingValue = converted;
this.delayHandle = setTimeout(this.raiseOnChange, this.props.delayMs);
} else {
this.props.onChange(converted);
}
}
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
if (this.props.isValid && !this.props.isValid(value)) {
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));
}
onKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.charCode === 27 || e.keyCode === 27 /* esc */) {
if (this.props.blurOnEscape && this.input.current) {
this.input.current.blur();
}
}
}
onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.keyCode === 13 || e.charCode === 13 /* enter */) {
if (this.isPending) {
this.clearTimeout();
this.raiseOnChange();
}
if (this.props.blurOnEnter && this.input.current) {
this.input.current.blur();
}
if (this.props.onEnter) this.props.onEnter();
}
}
static getDerivedStateFromProps(props: TextInputProps<any>, state: TextInputState) {
const value = props.fromValue ? props.fromValue(props.value) : props.value;
if (value === state.originalValue) return null;
return { originalValue: value, value };
}
render() {
return <input type='text'
className={this.props.className}
style={this.props.style}
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.props.blurOnEscape ? this.onKeyPress : void 0}
onKeyDown={this.props.blurOnEscape ? this.onKeyUp : void 0}
disabled={!!this.props.isDisabled}
/>;
}
}
// TODO: replace this with parametrized TextInput
export class NumericInput extends React.PureComponent<{ export class NumericInput extends React.PureComponent<{
value: number, value: number,
onChange: (v: number) => void, onChange: (v: number) => void,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment