Commit 02b0c85c authored by Jaden DIEFENBAUGH's avatar Jaden DIEFENBAUGH
Browse files

add plotter editor, #82

parent 40cec9fb
......@@ -33,6 +33,7 @@ import LibraryEditorContainer from './library';
import DatabaseEditorContainer from './database';
import ExperimentEditorContainer from './experiment';
import ToolchainEditorContainer from './toolchain';
import PlotterEditorContainer from './plotter';
type Props = {
match: any,
......@@ -155,6 +156,13 @@ export class EntityDetail extends React.Component<Props, State> {
saveFunc={this.saveChanges}
/>
}
{
this.props.match.params.entity === 'plotter' &&
<PlotterEditorContainer
data={this.props.getEntityObject()}
saveFunc={this.saveChanges}
/>
}
</TabPane>
<TabPane tabId='1'>
<Row>
......
// @flow
import * as React from 'react';
import {
Row,
Col,
Button,
Form, FormGroup,
Label, Input,
Card, CardHeader, CardBody,
TabContent, TabPane, Nav, NavItem, NavLink,
Alert,
UncontrolledDropdown, DropdownToggle, DropdownMenu,
InputGroup, InputGroupAddon,
} from 'reactstrap';
import { connect } from 'react-redux';
import cn from 'classnames';
import { changeObjFieldName, generateNewKey, jsonClone } from '@helpers';
import { BUILTIN_TYPES, ANALYZER_RESULT_TYPES, getValidPlotterObj as getValidObj } from '@helpers/beat';
import type { BeatObject } from '@helpers/beat';
import * as Selectors from '@store/selectors.js';
import CacheInput from '../CacheInput.jsx';
import ValidSchemaBadge from '../ValidSchemaBadge.jsx';
import DeleteInputBtn from '../DeleteInputBtn.jsx';
import TypedField from '../TypedField.jsx';
import TemplateButton from '../EntityTemplateGenerationButton.jsx';
import InfoTooltip from '../InfoTooltip.jsx';
type Props = {
data: BeatObject,
libraries: BeatObject[],
plotDfNames: string[],
saveFunc: (BeatObject) => any,
};
type State = {
cache: any,
choiceCache: string,
};
export class PlotterEditor extends React.Component<Props, State> {
constructor(props: Props) {
//console.log(`Creating AlgDetail`);
super(props);
}
state = {
cache: getValidObj(this.props.data),
choiceCache: '',
}
componentWillReceiveProps (nextProps: Props) {
this.setState({
cache: getValidObj(nextProps.data),
});
}
setContents = (newContents: any) => {
this.setState({
cache: {
...this.state.cache,
contents: {
...this.state.cache.contents,
...newContents,
}
}
});
}
updateDescription = (desc: string) => {
this.setContents({ description: desc });
}
updateDataformat = (df: string) => {
this.setContents({ dataformat: df });
}
// 4 different functions:
// creates a parameter (provide name & value)
// updates a parameter (provide name & value)
// renames a parameter (provide name & value & the old name)
// deletes a parameter (provide null as name, value is optional, the name)
updateParameter = (name: null | string, value: any, oldName: ?string) => {
const params = jsonClone(this.state.cache.contents.parameters);
if(oldName)
delete params[oldName];
if(name !== null)
params[name] = value;
this.setContents({ parameters: params });
}
render = () => {
return (
<div>
<div className='d-flex'>
<Button
className='mx-auto'
outline
color='secondary'
onClick={() => this.props.saveFunc(this.state.cache)}
>
Save Changes (Changes are <ValidSchemaBadge entity='plotter' obj={this.state.cache} />)
</Button>
{/*
<TemplateButton
data={this.state.cache}
entity={'plotter'}
/>
*/}
</div>
<Form>
<FormGroup tag='fieldset'>
<FormGroup>
<Label>Short Description</Label>
<Input
type='text'
id='plDesc'
placeholder='Plotter description...'
value={this.state.cache.contents.description}
onChange={(e) => this.updateDescription(e.target.value)}
/>
</FormGroup>
<FormGroup>
<Label>Input Dataformat</Label>
<Input
id='plDf'
type='select'
className='custom-select'
value={this.state.cache.contents.dataformat}
onChange={e => this.updateDataformat(e.target.value)}
>
{
this.props.plotDfNames.map((df, i) =>
<option key={i} value={df}>{ df }</option>
)
}
</Input>
</FormGroup>
<FormGroup>
<h5>Parameters</h5>
{
(Object.entries(this.state.cache.contents.parameters): [string, any][]).map(([name, param], i, params) => (
<Row key={i} className='mb-2'>
<Col sm='12'>
<TypedField
placeholder='Parameter name...'
name={name}
type={param.type}
types={BUILTIN_TYPES.filter(d => !d.startsWith('complex'))}
existingFields={params.map(([n, v]) => n)}
nameUpdateFunc={str => this.updateParameter(str, param, name)}
typeUpdateFunc={(str) => {
const newParam = {
...param,
type: str
};
switch(newParam.type){
case 'string':
delete newParam['range'];
break;
case 'bool':
delete newParam['range'];
delete newParam['choice'];
break;
default:
break;
}
this.updateParameter(name, newParam);
}}
deleteFunc={() => {
this.updateParameter(null, null, name);
}}
>
<Col>
<Input
type='text'
placeholder='Description'
value={param[name] || param.description || ''}
onChange={(e) => this.updateParameter(name, {
...param,
description: e.target.value
})}
/>
</Col>
</TypedField>
<FormGroup row className='ml-3'>
{
param.type !== 'bool' &&
<Col sm='4'>
<Label>Restrict to...</Label>
<FormGroup check>
<Row>
<Col>
<Label check>
<Input
type='checkbox'
checked={Array.isArray(param.choice)}
onChange={e => {
const b = e.target.checked;
const newParam = {...param};
if(b){
delete newParam['range'];
newParam['choice'] = [];
} else {
delete newParam['choice'];
}
this.updateParameter(name, newParam);
}}
/>{' '}
Choices
</Label>
</Col>
{
param.type !== 'string' &&
<Col>
<Label check className='ml-3'>
<Input
type='checkbox'
checked={Array.isArray(param.range)}
onChange={e => {
const b = e.target.checked;
const newParam = {...param};
if(b){
delete newParam['choice'];
newParam['range'] = [0, 1];
} else {
delete newParam['range'];
}
this.updateParameter(name, newParam);
}}
/>{' '}
Range
</Label>
</Col>
}
</Row>
</FormGroup>
</Col>
}
{
Array.isArray(param.choice) &&
<Col sm='auto'>
<Label>
Choices
</Label>
<UncontrolledDropdown>
<DropdownToggle caret>
Choices List
</DropdownToggle>
<DropdownMenu className='p-0'>
{
param.choice
.map((c, j) =>
<InputGroup key={j}>
<Input
defaultValue={c}
className='border-top-0 border-left-0 border-right-0 rounded-0'
onSubmit={e => {
e.preventDefault();
}}
onChange={e => {
this.updateParameter(
name,
{
...param,
choice: param.choice.map((c, k) => k === j ? e.target.value : c)
});
}}
/>
<InputGroupAddon addonType='append'>
<Button
className='border-top-0 border-left-0 border-right-0 rounded-0'
color='danger'
onClick={e => {
this.updateParameter(name,
{
...param,
choice: param.choice.filter((c, k) => k !== j)
}
);
}}
>
x
</Button>
</InputGroupAddon>
</InputGroup>
)
}
<Input
placeholder='New Choice...'
className='border-top-0 border-left-0 border-right-0 rounded-0'
value={this.state.choiceCache}
onSubmit={e => {
e.preventDefault();
}}
onBlur={e => {
if(this.state.choiceCache.length > 0)
this.updateParameter(name,
{
...param,
choice: [
...param.choice,
this.state.choiceCache
]
});
this.setState({ choiceCache: '' });
}}
onChange={e => {
this.setState({ choiceCache: e.target.value });
}}
onKeyPress={e => {
if(e.key === 'Enter'){
if(this.state.choiceCache.length > 0)
this.updateParameter(name,
{
...param,
choice: [
...param.choice,
this.state.choiceCache
]
}
);
this.setState({ choiceCache: '' });
}
}}
/>
</DropdownMenu>
</UncontrolledDropdown>
</Col>
}
{
Array.isArray(param.range) &&
<Col sm='4'>
<Label>
Range
</Label>
<FormGroup row>
<Col>
<InputGroup>
<InputGroupAddon>Min</InputGroupAddon>
<Input
type='number'
step={param.type.startsWith('float') ? 'any' : '1'}
value={param.range[0]}
onChange={e => {
const num = e.target.value;
const newParam = {
...param,
range: [
num,
param.range[1],
]
};
this.updateParameter(
name,
newParam
);
}}
required
/>
</InputGroup>
</Col>
<Col>
<InputGroup>
<InputGroupAddon>Max</InputGroupAddon>
<Input
type='number'
step={param.type.startsWith('float') ? 'any' : '1'}
value={param.range[1]}
required
onChange={e => {
const num = e.target.value;
const newParam = {
...param,
range: [
param.range[0],
num,
]
};
this.updateParameter(
name,
newParam
);
}}
/>
</InputGroup>
</Col>
</FormGroup>
</Col>
}
{ param.type !== 'bool' &&
<Col>
<Label for={`default${ i }`}>
Default
</Label>
<Input
name={`default${ i }`}
type='text'
placeholder='Default'
value={param.default}
onChange={(e) => this.updateParameter(name, {
...param,
default: e.target.value
})}
/>
</Col>
||
<Col sm='auto'>
<Label>
Default
</Label>
<FormGroup check>
<Row>
<Col>
<Label check>
<Input
name={`default${ i }`}
type='radio'
/>
True
</Label>
</Col>
<Col>
<Label check className='ml-1'>
<Input
name={`default${ i }`}
type='radio'
/>
False
</Label>
</Col>
</Row>
</FormGroup>
</Col>
}
</FormGroup>
</Col>
</Row>
))
}
<Button outline block
id='newParameterBtn'
onClick={() => {
const newKey = generateNewKey('parameter', Object.keys(this.state.cache.contents.parameters || {}));
this.updateParameter(
newKey,
{
type: '',
default: '',
description: '',
}
);
}}
>
New Parameter
</Button>
</FormGroup>
</FormGroup>
</Form>
</div>
);
}
}
const mapStateToProps = (state, ownProps) => {
const obj = {
plotters: Selectors.plotterGet(state),
libraries: Selectors.libraryGet(state),
plotDfNames: Selectors.dataformatGet(state).map(df => df.name).filter(df => df.startsWith('plot/')),
};
return obj;
};
export default connect(mapStateToProps)(PlotterEditor);
// @flow
import ConnectedPlotterEditor, {PlotterEditor} from './PlotterEditor.jsx';
export {
PlotterEditor,
};
export default ConnectedPlotterEditor;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment