diff --git a/src/components/table-column.js b/src/components/table-column.js index 905ca2a..7be0160 100644 --- a/src/components/table-column.js +++ b/src/components/table-column.js @@ -19,7 +19,8 @@ class TableColumn extends Component { link: '/', linkKey: '', dataIndex: false, - inlineEditable: false + inlineEditable: false, + clickable: false }; render() { diff --git a/src/components/table.js b/src/components/table.js index a40eff8..3b64cd8 100644 --- a/src/components/table.js +++ b/src/components/table.js @@ -11,13 +11,14 @@ import React, { Component } from 'react'; import { Table, Button, Glyphicon, FormControl } from 'react-bootstrap'; import { Link } from 'react-router'; -import TableColumn from './table-column'; +//import TableColumn from './table-column'; class CustomTable extends Component { constructor(props) { super(props); this.state = { + rows: [], editCell: [ -1, -1 ] }; } @@ -30,78 +31,97 @@ class CustomTable extends Component { this.setState({ editCell: [ column, row ]}); // x, y } + addCell(data, index, child) { + // add data to cell + const dataKey = child.props.dataKey; + var cell = []; + + if (dataKey && data[dataKey] != null) { + // get content + var content; + const modifier = child.props.modifier; + + if (modifier) { + content = modifier(data[dataKey]); + } else { + content = data[dataKey].toString(); + } + + // check if cell should be a link + const linkKey = child.props.linkKey; + if (linkKey && data[linkKey] != null) { + cell.push({content}); + } else if (child.props.clickable) { + cell.push( child.props.onClick(index)}>{content}); + } else { + cell.push(content); + } + } + + if (child.props.dataIndex) { + cell.push(index); + } + + // add buttons + if (child.props.editButton) { + cell.push(); + } + + if (child.props.deleteButton) { + cell.push(); + } + + return cell; + } + + componentWillReceiveProps(nextProps) { + // check if data exists + if (nextProps.data == null) { + this.setState({ rows: [] }); + return; + } + + // create row data + var rows = nextProps.data.map((data, index) => { + // check if multiple columns + if (Array.isArray(nextProps.children)) { + var row = []; + + nextProps.children.forEach(child => { + row.push(this.addCell(data, index, child)); + }); + + return row; + } else { + // table only has a single column + return [ this.addCell(data, index, nextProps.children) ]; + } + }); + + this.setState({ rows: rows }); + } + render() { - // create sorted data for rows - var rows = []; - if (this.props.data) { - rows = this.props.data.map((row, index) => { - var array = []; - - for (var i = 0; i < this.props.children.length; i++) { - // only handle table-column children - if (this.props.children[i].type === TableColumn) { - // add content to cell - var cell = []; - - // add data to cell - const dataKey = this.props.children[i].props.dataKey; - if (dataKey && row[dataKey] != null) { - // get content - var content; - const modifier = this.props.children[i].props.modifier; - - if (modifier) { - content = modifier(row[dataKey]); - } else { - content = row[dataKey].toString(); - } - - // check if cell should be a link - const linkKey = this.props.children[i].props.linkKey; - if (linkKey && row[linkKey] != null) { - cell.push({content}); - } else { - cell.push(content); - } - } - - 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; - cell.push(); - } - - if (this.props.children[i].props.deleteButton) { - const onDelete = this.props.children[i].props.onDelete; - cell.push(); - } - - array.push(cell); - } - } - - return array; - }); + // get children + var children = this.props.children; + if (Array.isArray(this.props.children) === false) { + children = [ children ]; } return ( - +
{this.props.children} - {rows.map((row, rowIndex) => ( + {this.state.rows.map((row, rowIndex) => ( {row.map((cell, cellIndex) => ( - +
this.onClick(event, rowIndex, 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)} /> + children[cellIndex].props.onInlineChange(event, rowIndex, cellIndex)} /> ) : ( {cell.map((element, elementIndex) => ( diff --git a/src/components/widget-plot-table.js b/src/components/widget-plot-table.js new file mode 100644 index 0000000..ddca1c0 --- /dev/null +++ b/src/components/widget-plot-table.js @@ -0,0 +1,112 @@ +/** + * File: widget-plot-table.js + * Author: Markus Grigull + * Date: 15.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 } from 'react'; +import { LineChart } from 'rd3'; +import { Col } from 'react-bootstrap'; + +import Table from './table'; +import TableColumn from './table-column'; + +class WidgetPlotTable extends Component { + constructor(props) { + super(props); + + this.state = { + signal: 0, + firstTimestamp: 0, + latestTimestamp: 0, + sequence: null, + rows: [], + values: [] + }; + } + + componentWillReceiveProps(nextProps) { + // check data + const simulator = nextProps.widget.simulator; + + if (nextProps.simulation == null || nextProps.data == null || nextProps.data[simulator] == null || nextProps.data[simulator].length === 0 || nextProps.data[simulator].values[0].length === 0) { + // clear values + this.setState({ values: [], sequence: null, rows: [] }); + return; + } + + // check if new data, otherwise skip + if (this.state.sequence >= nextProps.data[simulator].sequence) { + return; + } + + // get simulation model + const simulationModel = nextProps.simulation.models.find((model) => { + return (model.simulator === simulator); + }); + + // get rows + var rows = []; + + simulationModel.mapping.forEach((signal) => { + rows.push({ name: signal.name }) + }); + + // get timestamps + var latestTimestamp = nextProps.data[simulator].values[0][nextProps.data[simulator].values[0].length - 1].x; + var firstTimestamp = latestTimestamp - nextProps.widget.time * 1000; + var firstIndex; + + if (nextProps.data[simulator].values[0][0].x < firstTimestamp) { + // find element index representing firstTimestamp + firstIndex = nextProps.data[simulator].values[0].findIndex((value) => { + return value.x >= firstTimestamp; + }); + } else { + firstIndex = 0; + firstTimestamp = nextProps.data[simulator].values[0][0].x; + latestTimestamp = firstTimestamp + nextProps.widget.time * 1000; + } + + // copy all values for each signal in time region + var values = [{ + values: nextProps.data[simulator].values[this.state.signal].slice(firstIndex, nextProps.data[simulator].values[this.state.signal].length - 1) + }]; + + this.setState({ values: values, firstTimestamp: firstTimestamp, latestTimestamp: latestTimestamp, sequence: nextProps.data[simulator].sequence, rows: rows }); + } + + render() { + return ( +
+

{this.props.widget.name}

+ +
+ this.setState({ signal: index }) } /> +
+ + + + {this.state.sequence && + { if (d != null) { return new Date(d.x); } }} + hoverAnimation={false} + circleRadius={0} + domain={{ x: [this.state.firstTimestamp, this.state.latestTimestamp] }} + /> + } + + + ); + } +} + +export default WidgetPlotTable; diff --git a/src/containers/visualization.js b/src/containers/visualization.js index 14cbc96..ac4ff03 100644 --- a/src/containers/visualization.js +++ b/src/containers/visualization.js @@ -118,7 +118,7 @@ class Visualization extends Component { } else if (item.name === 'Plot') { widget.simulator = this.state.simulation.models[0].simulator; widget.signals = [ 0 ]; - widget.time = 300; + widget.time = 60; widget.width = 400; widget.height = 200; } else if (item.name === 'Table') { @@ -127,6 +127,11 @@ class Visualization extends Component { widget.height = 200; } else if (item.name === 'Label') { + } else if (item.name === 'PlotTable') { + widget.simulator = this.state.simulation.models[0].simulator; + widget.width = 500; + widget.height = 400; + widget.time = 60 } var visualization = this.state.visualization; @@ -276,6 +281,7 @@ class Visualization extends Component { + } diff --git a/src/containers/widget.js b/src/containers/widget.js index 6cdcdb8..dd4dc74 100644 --- a/src/containers/widget.js +++ b/src/containers/widget.js @@ -18,6 +18,7 @@ import WidgetValue from '../components/widget-value'; import WidgetPlot from '../components/widget-plot'; import WidgetTable from '../components/widget-table'; import WidgetLabel from '../components/widget-label'; +import WidgetPlotTable from '../components/widget-plot-table'; import '../styles/widgets.css'; @@ -83,6 +84,7 @@ class Widget extends Component { const widget = this.props.data; var element = null; + // dummy is passed to widgets to keep updating them while in edit mode if (widget.type === 'Value') { element = } else if (widget.type === 'Plot') { @@ -91,6 +93,8 @@ class Widget extends Component { element = } else if (widget.type === 'Label') { element = + } else if (widget.type === 'PlotTable') { + element = } if (this.props.editing) {