From 04e7ea218401678d9d568671347e1541306fc047 Mon Sep 17 00:00:00 2001 From: Markus Grigull Date: Tue, 7 Mar 2017 11:12:22 +0100 Subject: [PATCH] Add simulation-model route and dialogs Add inline edit to table --- .../dialog/edit-simulation-model.js | 140 ++++++++++++++++++ src/components/dialog/edit-widget-value.js | 92 ++++++++++++ src/components/dialog/new-simulation-model.js | 136 +++++++++++++++++ src/components/table-column.js | 4 +- src/components/table.js | 38 ++++- src/containers/simulation.js | 31 ++-- src/styles/app.css | 25 +--- 7 files changed, 422 insertions(+), 44 deletions(-) create mode 100644 src/components/dialog/edit-simulation-model.js create mode 100644 src/components/dialog/edit-widget-value.js create mode 100644 src/components/dialog/new-simulation-model.js diff --git a/src/components/dialog/edit-simulation-model.js b/src/components/dialog/edit-simulation-model.js new file mode 100644 index 0000000..e51380c --- /dev/null +++ b/src/components/dialog/edit-simulation-model.js @@ -0,0 +1,140 @@ +/** + * File: edit-simulation-model.js + * Author: Markus Grigull + * Date: 04.03.2017 + * Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC + * This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential. + * Unauthorized copying of this file, via any medium is strictly prohibited. + **********************************************************************************/ + +import React, { Component, PropTypes } from 'react'; +import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap'; + +import Table from '../table'; +import TableColumn from '../table-column'; +import Dialog from './dialog'; + +class EditSimulationModelDialog extends Component { + static propTypes = { + show: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + data: PropTypes.object.isRequired, + simulators: PropTypes.array.isRequired + }; + + valid: false; + + constructor(props) { + super(props); + + this.state = { + name: '', + simulator: '', + length: 1 + } + } + + onClose(canceled) { + if (canceled === false) { + this.props.onClose(this.state); + } else { + this.props.onClose(); + } + } + + handleChange(e) { + if (e.target.id === 'length') { + // change mapping size + if (e.target.value > this.state.mapping.length) { + // add missing signals + while (this.state.mapping.length < e.target.value) { + this.state.mapping.push({ name: 'Signal', type: 'Type' }); + } + } else { + // remove signals + this.state.mapping.splice(e.target.value, this.state.mapping.length - e.target.value); + } + } + + this.setState({ [e.target.id]: e.target.value }); + } + + handleMappingChange(event, row, column) { + var mapping = this.state.mapping; + + if (column === 1) { + mapping[row].name = event.target.value; + } else if (column === 2) { + mapping[row].type = event.target.value; + } + + this.setState({ mapping: mapping }); + } + + resetState() { + this.setState({ + name: this.props.data.name, + simulator: this.props.data.simulator, + length: this.props.data.length, + mapping: this.props.data.mapping + }); + } + + validateForm(target) { + // check all controls + var name = true; + var length = true; + + if (this.state.name === '') { + name = false; + } + + // test if simulatorid is a number (in a string, not type of number) + if (!/^\d+$/.test(this.state.length)) { + length = false; + } + + this.valid = name && length; + + // return state to control + if (target === 'name') return name ? "success" : "error"; + else if (target === 'length') return length ? "success" : "error"; + } + + render() { + return ( + this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}> +
+ + Name + this.handleChange(e)} /> + + + + Simulator + this.handleChange(e)}> + {this.props.simulators.map(simulator => ( + + ))} + + + + Length + this.handleChange(e)} /> + + + + Mapping + + + this.handleMappingChange(event, row, column)} /> + this.handleMappingChange(event, row, column)} /> +
+
+
+
+ ); + } +} + +export default EditSimulationModelDialog; diff --git a/src/components/dialog/edit-widget-value.js b/src/components/dialog/edit-widget-value.js new file mode 100644 index 0000000..1fa5de8 --- /dev/null +++ b/src/components/dialog/edit-widget-value.js @@ -0,0 +1,92 @@ +/** + * File: edit-widget-value.js + * Author: Markus Grigull + * Date: 04.03.2017 + * Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC + * This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential. + * Unauthorized copying of this file, via any medium is strictly prohibited. + **********************************************************************************/ + +import React, { Component, PropTypes } from 'react'; +import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap'; + +import Dialog from './dialog'; + +class EditWidgetValueDialog extends Component { + static propTypes = { + show: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired + }; + + valid: false; + + constructor(props) { + super(props); + + this.state = { + name: '', + simulator: '', + signal: 0 + } + } + + onClose(canceled) { + if (canceled === false) { + this.props.onClose(this.state); + } else { + this.props.onClose(); + } + } + + handleChange(e) { + this.setState({ [e.target.id]: e.target.value }); + } + + resetState() { + this.setState({ name: '' }); + } + + validateForm(target) { + // check all controls + var name = true; + + if (this.state.name === '') { + name = false; + } + + this.valid = name; + + // return state to control + if (target === 'name') return name ? "success" : "error"; + + return "success"; + } + + render() { + return ( + this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}> +
+ + Name + this.handleChange(e)} /> + + + + Simulator + + + + + + + Signal + this.handleChange(e)} /> + + +
+
+ ); + } +} + +export default EditWidgetValueDialog; diff --git a/src/components/dialog/new-simulation-model.js b/src/components/dialog/new-simulation-model.js new file mode 100644 index 0000000..005c928 --- /dev/null +++ b/src/components/dialog/new-simulation-model.js @@ -0,0 +1,136 @@ +/** + * File: new-simulation-model.js + * Author: Markus Grigull + * Date: 04.03.2017 + * Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC + * This file is part of VILLASweb. All Rights Reserved. Proprietary and confidential. + * Unauthorized copying of this file, via any medium is strictly prohibited. + **********************************************************************************/ + +import React, { Component, PropTypes } from 'react'; +import { FormGroup, FormControl, ControlLabel, HelpBlock } from 'react-bootstrap'; + +import Table from '../table'; +import TableColumn from '../table-column'; +import Dialog from './dialog'; + +class NewSimulationModelDialog extends Component { + static propTypes = { + show: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + simulators: PropTypes.array.isRequired + }; + + valid: false; + + constructor(props) { + super(props); + + this.state = { + name: '', + simulator: '', + length: '1', + mapping: [ { name: 'Signal', type: 'Type' } ] + }; + } + + onClose(canceled) { + if (canceled === false) { + this.props.onClose(this.state); + } else { + this.props.onClose(); + } + } + + handleChange(e) { + if (e.target.id === 'length') { + // change mapping size + if (e.target.value > this.state.mapping.length) { + // add missing signals + while (this.state.mapping.length < e.target.value) { + this.state.mapping.push({ name: 'Signal', type: 'Type' }); + } + } else { + // remove signals + this.state.mapping.splice(e.target.value, this.state.mapping.length - e.target.value); + } + } + + this.setState({ [e.target.id]: e.target.value }); + } + + handleMappingChange(event, row, column) { + var mapping = this.state.mapping; + + if (column === 1) { + mapping[row].name = event.target.value; + } else if (column === 2) { + mapping[row].type = event.target.value; + } + + this.setState({ mapping: mapping }); + } + + resetState() { + this.setState({ name: '', simulator: '', length: '1', mapping: [ { name: 'Signal', type: 'Type' } ] }); + } + + validateForm(target) { + // check all controls + var name = true; + var length = true; + + if (this.state.name === '') { + name = false; + } + + // test if simulatorid is a number (in a string, not type of number) + if (!/^\d+$/.test(this.state.length)) { + length = false; + } + + this.valid = name && length; + + // return state to control + if (target === 'name') return name ? "success" : "error"; + else if (target === 'length') return length ? "success" : "error"; + } + + render() { + return ( + this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}> +
+ + Name + this.handleChange(e)} /> + + + + Simulator + this.handleChange(e)}> + {this.props.simulators.map(simulator => ( + + ))} + + + + Length + this.handleChange(e)} /> + + + + Mapping + Click Name or Type cell to edit + + + this.handleMappingChange(event, row, column)} /> + this.handleMappingChange(event, row, column)} /> +
+
+
+
+ ); + } +} + +export default NewSimulationModelDialog; diff --git a/src/components/table-column.js b/src/components/table-column.js index d657859..905ca2a 100644 --- a/src/components/table-column.js +++ b/src/components/table-column.js @@ -17,7 +17,9 @@ class TableColumn extends Component { editButton: false, deleteButton: false, link: '/', - linkKey: '' + linkKey: '', + dataIndex: false, + inlineEditable: false }; render() { diff --git a/src/components/table.js b/src/components/table.js index 898e65f..a40eff8 100644 --- a/src/components/table.js +++ b/src/components/table.js @@ -8,16 +8,28 @@ **********************************************************************************/ import React, { Component } from 'react'; -import { Table, Button, Glyphicon } from 'react-bootstrap'; +import { Table, Button, Glyphicon, FormControl } from 'react-bootstrap'; import { Link } from 'react-router'; import TableColumn from './table-column'; class CustomTable extends Component { + constructor(props) { + super(props); + + this.state = { + editCell: [ -1, -1 ] + }; + } + static defaultProps = { width: null }; + onClick(event, row, column) { + this.setState({ editCell: [ column, row ]}); // x, y + } + render() { // create sorted data for rows var rows = []; @@ -53,6 +65,10 @@ class CustomTable extends Component { } } + if (this.props.children[i].props.dataIndex) { + cell.push(index); + } + // add buttons if (this.props.children[i].props.editButton) { const onEdit = this.props.children[i].props.onEdit; @@ -80,13 +96,19 @@ class CustomTable extends Component { - {rows.map((row, index) => ( - - {row.map((cell, index) => ( - - {cell.map((element, index) => ( - {element} - ))} + {rows.map((row, rowIndex) => ( + + {row.map((cell, cellIndex) => ( + this.onClick(event, rowIndex, cellIndex) : () => {}}> + {(this.state.editCell[0] === cellIndex && this.state.editCell[1] === rowIndex ) ? ( + this.props.children[cellIndex].props.onInlineChange(event, rowIndex, cellIndex)} /> + ) : ( + + {cell.map((element, elementIndex) => ( + {element} + ))} + + )} ))} diff --git a/src/containers/simulation.js b/src/containers/simulation.js index a4b59b7..b6f6c97 100644 --- a/src/containers/simulation.js +++ b/src/containers/simulation.js @@ -34,6 +34,7 @@ class Simulation extends Component { deleteModal: false, editModal: false, modalData: {}, + modalIndex: null, simulation: {} } @@ -79,24 +80,30 @@ class Simulation extends Component { } confirmDeleteModal() { - this.setState({ deleteModal: false }); + // remove model from simulation + var simulation = this.state.simulation; + simulation.models.splice(this.state.modalIndex, 1); + this.setState({ deleteModal: false, simulation: simulation }); - - /*AppDispatcher.dispatch({ - type: 'visualizations/start-remove', - data: this.state.modalVisualization - });*/ + AppDispatcher.dispatch({ + type: 'simulations/start-edit', + data: simulation + }); } closeEditModal(data) { this.setState({ editModal : false }); if (data) { - /*AppDispatcher.dispatch({ - type: 'visualizations/start-edit', - data: data - });*/ + var simulation = this.state.simulation; + simulation.models[this.state.modalIndex] = data; + this.setState({ simulation: simulation }); + + AppDispatcher.dispatch({ + type: 'simulations/start-edit', + data: simulation + }); } } @@ -119,14 +126,14 @@ class Simulation extends Component { this.getSimulatorName(id)} /> - this.setState({ editModal: true, modalData: this.state.simulation.models[index] })} onDelete={(index) => this.setState({ deleteModal: true, modalData: this.state.simulation.models[index] })} /> + this.setState({ editModal: true, modalData: this.state.simulation.models[index], modalIndex: index })} onDelete={(index) => this.setState({ deleteModal: true, modalData: this.state.simulation.models[index], modalIndex: index })} /> this.closeNewModal(data)} simulators={this.state.simulators} /> - this.closeEditModal(data)} data={this.state.modalData} /> + this.closeEditModal(data)} data={this.state.modalData} simulators={this.state.simulators} /> diff --git a/src/styles/app.css b/src/styles/app.css index aa73469..c1aad96 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -28,7 +28,7 @@ body { padding: 10px 0 0 0; - color: #103B7D; + color: #527984; background-color: #fff; } @@ -51,7 +51,7 @@ body { } .app-content { - min-height: 200px; + min-height: 400px; margin: 20px 20px 20px 200px; padding: 15px 20px; @@ -94,32 +94,11 @@ body { /** * Tables */ -.table { - margin-top: 20px; - - border: 0; - border-collapse: collapse; - - background-color: #f6f6f6; -} - -.table th, td { - padding: 5px; - - text-align: left; - - border: none; -} - .table th { background-color: #527984; color: #fff; } -.table tr:nth-child(even) { - background-color: #ddd; -} - /** * Buttons */