From d240d5553bea918128471868ef0510790e0d52db Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Thu, 13 Apr 2017 10:03:30 +0200 Subject: [PATCH 01/27] Deal with undefined sim models and show notification --- src/components/widget-plot-table.js | 37 ++++++++++++--------- src/components/widget-plot.js | 38 ++++++++++++---------- src/containers/visualization.js | 20 +++++++++--- src/data-managers/notifications-factory.js | 24 ++++++++++++++ 4 files changed, 81 insertions(+), 38 deletions(-) create mode 100644 src/data-managers/notifications-factory.js diff --git a/src/components/widget-plot-table.js b/src/components/widget-plot-table.js index cbdee75..65de31e 100644 --- a/src/components/widget-plot-table.js +++ b/src/components/widget-plot-table.js @@ -58,22 +58,27 @@ class WidgetPlotTable extends Component { return (model.simulator === simulator); }); - // Create checkboxes using the signal indices from simulation model - const preselectedSignals = simulationModel.mapping.reduce( - // Loop through simulation model signals - (accum, model_signal, signal_index) => { - // Append them if they belong to the current selected type - if (nextProps.widget.preselectedSignals.indexOf(signal_index) > -1) { - accum.push( - { - index: signal_index, - name: model_signal.name - } - ) - } - return accum; - }, []); - this.setState({ preselectedSignals: preselectedSignals }); + let preselectedSignals = []; + // Proceed if a simulation model is available + if (simulationModel) { + // Create checkboxes using the signal indices from simulation model + preselectedSignals = simulationModel.mapping.reduce( + // Loop through simulation model signals + (accum, model_signal, signal_index) => { + // Append them if they belong to the current selected type + if (nextProps.widget.preselectedSignals.indexOf(signal_index) > -1) { + accum.push( + { + index: signal_index, + name: model_signal.name + } + ) + } + return accum; + }, []); + } + + this.setState({ preselectedSignals: preselectedSignals }); } updateSignalSelection(signal_index, checked) { diff --git a/src/components/widget-plot.js b/src/components/widget-plot.js index df901c9..e0c2662 100644 --- a/src/components/widget-plot.js +++ b/src/components/widget-plot.js @@ -15,25 +15,29 @@ import PlotLegend from './widget-plot/plot-legend'; class WidgetPlot extends Component { render() { - if (this.props.simulation == null) { - return (
Empty
); + + const simulator = this.props.widget.simulator; + const simulation = this.props.simulation; + let legendSignals = []; + let simulatorData = []; + + // Proceed if a simulation with models and a simulator are available + if (simulator && simulation && simulation.models.length > 0) { + + const model = simulation.models.find( (model) => model.simulator === simulator ); + const chosenSignals = this.props.widget.signals; + + simulatorData = this.props.data[simulator]; + + // Query the signals that will be displayed in the legend + legendSignals = model.mapping.reduce( (accum, model_signal, signal_index) => { + if (chosenSignals.includes(signal_index)) { + accum.push({ index: signal_index, name: model_signal.name }); + } + return accum; + }, []); } - let simulator = this.props.widget.simulator; - let simulation = this.props.simulation; - let model = simulation.models.find( (model) => model.simulator === simulator ); - let chosenSignals = this.props.widget.signals; - - let simulatorData = this.props.data[simulator]; - - // Query the signals that will be displayed in the legend - let legendSignals = model.mapping.reduce( (accum, model_signal, signal_index) => { - if (chosenSignals.includes(signal_index)) { - accum.push({ index: signal_index, name: model_signal.name }); - } - return accum; - }, []); - return (

{this.props.widget.name}

diff --git a/src/containers/visualization.js b/src/containers/visualization.js index 6085b06..c82d56d 100644 --- a/src/containers/visualization.js +++ b/src/containers/visualization.js @@ -22,6 +22,8 @@ import ProjectStore from '../stores/project-store'; import SimulationStore from '../stores/simulation-store'; import FileStore from '../stores/file-store'; import AppDispatcher from '../app-dispatcher'; +import NotificationsDataManager from '../data-managers/notifications-data-manager'; +import NotificationsFactory from '../data-managers/notifications-factory'; import WidgetSlider from '../components/widget-slider'; @@ -138,16 +140,24 @@ class Visualization extends Component { z: 0 }; + let defaultSimulator = null; + + if (this.state.simulation.models && this.state.simulation.models.length === 0) { + NotificationsDataManager.addNotification(NotificationsFactory.NO_SIM_MODEL_AVAILABLE); + } else { + defaultSimulator = this.state.simulation.models[0].simulator; + } + // set type specific properties if (item.name === 'Value') { - widget.simulator = this.state.simulation.models[0].simulator; + widget.simulator = defaultSimulator; widget.signal = 0; widget.minWidth = 70; widget.minHeight = 20; widget.width = 120; widget.height = 70; } else if (item.name === 'Plot') { - widget.simulator = this.state.simulation.models[0].simulator; + widget.simulator = defaultSimulator; widget.signals = [ 0 ]; widget.time = 60; widget.minWidth = 400; @@ -155,7 +165,7 @@ class Visualization extends Component { widget.width = 400; widget.height = 200; } else if (item.name === 'Table') { - widget.simulator = this.state.simulation.models[0].simulator; + widget.simulator = defaultSimulator; widget.minWidth = 300; widget.minHeight = 200; widget.width = 400; @@ -164,7 +174,7 @@ class Visualization extends Component { widget.minWidth = 70; widget.minHeight = 20; } else if (item.name === 'PlotTable') { - widget.simulator = this.state.simulation.models[0].simulator; + widget.simulator = defaultSimulator; widget.preselectedSignals = []; widget.signals = []; // initialize selected signals widget.minWidth = 400; @@ -194,7 +204,7 @@ class Visualization extends Component { widget.height = 50; widget.orientation = WidgetSlider.OrientationTypes.HORIZONTAL.value; // Assign default orientation } else if (item.name === 'Gauge') { - widget.simulator = this.state.simulation.models[0].simulator; + widget.simulator = defaultSimulator; widget.signal = 0; widget.minWidth = 200; widget.minHeight = 150; diff --git a/src/data-managers/notifications-factory.js b/src/data-managers/notifications-factory.js new file mode 100644 index 0000000..56c095c --- /dev/null +++ b/src/data-managers/notifications-factory.js @@ -0,0 +1,24 @@ +/** + * File: notifications-factory.js + * Description: An unique source of pre-defined notifications that are displayed + * throughout the application. + * Author: Ricardo Hernandez-Montoya + * Date: 13.04.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. + **********************************************************************************/ + +class NotificationsFactory { + + static get NO_SIM_MODEL_AVAILABLE() { + return { + title: 'No simulation model available', + message: 'Consider defining a simulation model in the simulators section.', + level: 'warning' + }; + } + +} + +export default NotificationsFactory; \ No newline at end of file From fe543dee931b8d2131d3b8386932b1772e62dae5 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Thu, 13 Apr 2017 10:05:57 +0200 Subject: [PATCH 02/27] Extracted controls in Widget Edit model and included proper message when no sim model is available --- ...-image.js => edit-widget-image-control.js} | 6 +- src/components/dialog/edit-widget-plot.js | 85 ------------------- .../dialog/edit-widget-signals-control.js | 31 ++++--- .../dialog/edit-widget-simulator-control.js | 11 ++- .../dialog/edit-widget-time-control.js | 40 +++++++++ src/components/dialog/edit-widget-value.js | 64 -------------- src/components/dialog/edit-widget.js | 32 ++++--- 7 files changed, 87 insertions(+), 182 deletions(-) rename src/components/dialog/{edit-widget-image.js => edit-widget-image-control.js} (94%) delete mode 100644 src/components/dialog/edit-widget-plot.js create mode 100644 src/components/dialog/edit-widget-time-control.js delete mode 100644 src/components/dialog/edit-widget-value.js diff --git a/src/components/dialog/edit-widget-image.js b/src/components/dialog/edit-widget-image-control.js similarity index 94% rename from src/components/dialog/edit-widget-image.js rename to src/components/dialog/edit-widget-image-control.js index f7b06e4..d484bee 100644 --- a/src/components/dialog/edit-widget-image.js +++ b/src/components/dialog/edit-widget-image-control.js @@ -1,5 +1,5 @@ /** - * File: edit-widget-value.js + * File: edit-widget-image-control.js * Author: Markus Grigull * Date: 04.03.2017 * Copyright: 2017, Institute for Automation of Complex Power Systems, EONERC @@ -12,7 +12,7 @@ import { FormGroup, FormControl, ControlLabel, Button } from 'react-bootstrap'; import AppDispatcher from '../../app-dispatcher'; -class EditImageWidget extends Component { +class EditImageWidgetControl extends Component { constructor(props) { super(props); @@ -68,4 +68,4 @@ class EditImageWidget extends Component { } } -export default EditImageWidget; +export default EditImageWidgetControl; diff --git a/src/components/dialog/edit-widget-plot.js b/src/components/dialog/edit-widget-plot.js deleted file mode 100644 index a2371e3..0000000 --- a/src/components/dialog/edit-widget-plot.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * File: edit-widget-plot.js - * Author: Markus Grigull - * Date: 13.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 { FormGroup, FormControl, ControlLabel, Checkbox, HelpBlock } from 'react-bootstrap'; - -class EditPlotWidget extends Component { - constructor(props) { - super(props); - - this.state = { - widget: { - simulator: '', - signals: [], - time: 0 - } - }; - } - - componentWillReceiveProps(nextProps) { - this.setState({ widget: nextProps.widget }); - } - - handleSignalChange(e, index) { - var signals = this.state.widget.signals; - - if (e.target.checked) { - // add signal - signals.push(index); - } else { - // remove signal - const pos = signals.indexOf(index); - if (pos > -1) { - signals.splice(pos, 1); - } - } - - this.props.handleChange({ target: { id: 'signals', value: signals } }); - } - - render() { - // get selected simulation model - var simulationModel = {}; - - if (this.props.simulation) { - this.props.simulation.models.forEach((model) => { - if (model.simulation === this.state.widget.simulation) { - simulationModel = model; - } - }); - } - - return ( -
- - Time - this.props.handleChange(e)} /> - Time in seconds - - - Simulator - this.props.handleChange(e)}> - {this.props.simulation.models.map((model, index) => ( - - ))} - - - - Signals - {simulationModel.mapping.map((signal, index) => ( - this.handleSignalChange(e, index)}>{signal.name} - ))} - -
- ); - } -} - -export default EditPlotWidget; diff --git a/src/components/dialog/edit-widget-signals-control.js b/src/components/dialog/edit-widget-signals-control.js index 2842618..a7fcd07 100644 --- a/src/components/dialog/edit-widget-signals-control.js +++ b/src/components/dialog/edit-widget-signals-control.js @@ -8,7 +8,7 @@ **********************************************************************************/ import React, { Component } from 'react'; -import { FormGroup, Checkbox, ControlLabel } from 'react-bootstrap'; +import { FormGroup, Checkbox, ControlLabel, FormControl } from 'react-bootstrap'; class EditWidgetSignalsControl extends Component { constructor(props) { @@ -39,27 +39,32 @@ class EditWidgetSignalsControl extends Component { new_signals = signals.filter( (idx) => idx !== index ); } - this.props.handleChange({ target: { id: 'preselectedSignals', value: new_signals } }); + this.props.handleChange({ target: { id: this.props.controlId, value: new_signals } }); } render() { - // get selected simulation model - var simulationModel = {}; + let signalsToRender = []; if (this.props.simulation) { - this.props.simulation.models.forEach((model) => { - if (model.simulation === this.state.widget.simulation) { - simulationModel = model; - } - }); - } + // get selected simulation model + const simulationModel = this.props.simulation.models.find( model => model.simulation === this.state.widget.simulation ); + // If simulation model update the signals to render + signalsToRender = simulationModel? simulationModel.mapping : []; + } + return ( Signals - {simulationModel.mapping.map((signal, index) => ( - this.handleSignalChange(e.target.checked, index)}>{signal.name} - ))} + { + signalsToRender.length === 0 ? ( + No signals available. + ) : ( + signalsToRender.map((signal, index) => ( + this.handleSignalChange(e.target.checked, index)}>{signal.name} + )) + ) + } ); } diff --git a/src/components/dialog/edit-widget-simulator-control.js b/src/components/dialog/edit-widget-simulator-control.js index baa9679..23de386 100644 --- a/src/components/dialog/edit-widget-simulator-control.js +++ b/src/components/dialog/edit-widget-simulator-control.js @@ -31,10 +31,15 @@ class EditWidgetSimulatorControl extends Component { return ( Simulator - this.props.handleChange(e)}> - {this.props.simulation.models.map((model, index) => ( + this.props.handleChange(e)}> + { + this.props.simulation.models.length === 0? ( + + ) : ( + this.props.simulation.models.map((model, index) => ( - ))} + ))) + } ); diff --git a/src/components/dialog/edit-widget-time-control.js b/src/components/dialog/edit-widget-time-control.js new file mode 100644 index 0000000..5b8e234 --- /dev/null +++ b/src/components/dialog/edit-widget-time-control.js @@ -0,0 +1,40 @@ +/** + * File: edit-widget-time-control.js + * Author: Ricardo Hernandez-Montoya + * Date: 13.04.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 { FormGroup, FormControl, ControlLabel, HelpBlock } from 'react-bootstrap'; + +class EditWidgetTimeControl extends Component { + constructor(props) { + super(props); + + this.state = { + widget: { + time: 0 + } + }; + } + + componentWillReceiveProps(nextProps) { + this.setState({ widget: nextProps.widget }); + } + + render() { + + return ( + + Time + this.props.handleChange(e)} /> + Time in seconds + + ); + } +} + +export default EditWidgetTimeControl; diff --git a/src/components/dialog/edit-widget-value.js b/src/components/dialog/edit-widget-value.js deleted file mode 100644 index 7f16a03..0000000 --- a/src/components/dialog/edit-widget-value.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * 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 } from 'react'; -import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap'; - -class EditValueWidget extends Component { - constructor(props) { - super(props); - - this.state = { - widget: { - simulator: '', - signal: 0 - } - }; - } - - componentWillReceiveProps(nextProps) { - this.setState({ widget: nextProps.widget }); - } - - render() { - // get selected simulation model - var simulationModel = {}; - - if (this.props.simulation) { - this.props.simulation.models.forEach((model) => { - if (model.simulation === this.state.widget.simulation) { - simulationModel = model; - } - }); - } - - return ( -
- - Simulator - this.props.handleChange(e)}> - {this.props.simulation.models.map((model, index) => ( - - ))} - - - - Signal - this.props.handleChange(e)}> - {simulationModel.mapping.map((signal, index) => ( - - ))} - - -
- ); - } -} - -export default EditValueWidget; diff --git a/src/components/dialog/edit-widget.js b/src/components/dialog/edit-widget.js index a5a2daa..b90e75c 100644 --- a/src/components/dialog/edit-widget.js +++ b/src/components/dialog/edit-widget.js @@ -12,10 +12,8 @@ import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap'; import Dialog from './dialog'; -import EditValueWidget from './edit-widget-value'; -import EditPlotWidget from './edit-widget-plot'; -import EditTableWidget from './edit-widget-table'; -import EditImageWidget from './edit-widget-image'; +import EditWidgetTimeControl from './edit-widget-time-control'; +import EditImageWidgetControl from './edit-widget-image-control'; import EditWidgetSimulatorControl from './edit-widget-simulator-control'; import EditWidgetSignalControl from './edit-widget-signal-control'; import EditWidgetSignalsControl from './edit-widget-signals-control'; @@ -53,8 +51,6 @@ class EditWidgetDialog extends Component { var update = this.state.temporal; update[e.target.id] = e.target.value; this.setState({ temporal: update }); - - //console.log(this.state.widget); } resetState() { @@ -77,20 +73,29 @@ class EditWidgetDialog extends Component { } render() { - // get widget part - var widgetDialog = null; // Use a list to concatenate the controls according to the widget type var dialogControls = []; if (this.props.widget) { if (this.props.widget.type === 'Value') { - widgetDialog = this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} />; + dialogControls.push( + this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} />, + this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} /> + ) } else if (this.props.widget.type === 'Plot') { - widgetDialog = this.validateForm(id)} simulation={this.props.simulation} handleChange={(e, index) => this.handleChange(e, index)} />; + dialogControls.push( + this.validateForm(id)} simulation={this.props.simulation} handleChange={(e, index) => this.handleChange(e, index)} />, + this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} />, + this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} /> + ) } else if (this.props.widget.type === 'Table') { - widgetDialog = this.validateForm(id)} simulation={this.props.simulation} handleChange={(e, index) => this.handleChange(e, index)} />; + dialogControls.push( + this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} /> + ) } else if (this.props.widget.type === 'Image') { - widgetDialog = this.validateForm(id)} simulation={this.props.simulation} handleChange={(e, index) => this.handleChange(e, index)} />; + dialogControls.push( + this.validateForm(id)} simulation={this.props.simulation} handleChange={(e, index) => this.handleChange(e, index)} /> + ) } else if (this.props.widget.type === 'Gauge') { dialogControls.push( this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} />, @@ -99,7 +104,7 @@ class EditWidgetDialog extends Component { } else if (this.props.widget.type === 'PlotTable') { dialogControls.push( this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} />, - this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} /> + this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} /> ) } else if (this.props.widget.type === 'Slider') { dialogControls.push( @@ -117,7 +122,6 @@ class EditWidgetDialog extends Component { { dialogControls } - {widgetDialog} ); From 279dc7b0f2e6690565b409590a87b25876d18836 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Thu, 13 Apr 2017 10:24:01 +0200 Subject: [PATCH 03/27] Extracted widget creation to separate factory --- src/components/widget-factory.js | 108 +++++++++++++++++++++++++++++++ src/containers/visualization.js | 79 ++-------------------- 2 files changed, 112 insertions(+), 75 deletions(-) create mode 100644 src/components/widget-factory.js diff --git a/src/components/widget-factory.js b/src/components/widget-factory.js new file mode 100644 index 0000000..0f45052 --- /dev/null +++ b/src/components/widget-factory.js @@ -0,0 +1,108 @@ +/** + * File: widget-factory.js + * Description: A factory to create and pre-configure widgets + * Author: Ricardo Hernandez-Montoya + * Date: 02.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 WidgetSlider from './widget-slider'; + +class WidgetFactory { + + static createWidgetOfType(type, position, defaultSimulator = null) { + + let widget = { + name: 'Name', + type: type, + width: 100, + height: 100, + x: position.x, + y: position.y, + z: 0 + }; + + // set type specific properties + switch(type) { + case 'Value': + widget.simulator = defaultSimulator; + widget.signal = 0; + widget.minWidth = 70; + widget.minHeight = 20; + widget.width = 120; + widget.height = 70; + break; + case 'Plot': + widget.simulator = defaultSimulator; + widget.signals = [ 0 ]; + widget.time = 60; + widget.minWidth = 400; + widget.minHeight = 200; + widget.width = 400; + widget.height = 200; + break; + case 'Table': + widget.simulator = defaultSimulator; + widget.minWidth = 300; + widget.minHeight = 200; + widget.width = 400; + widget.height = 200; + break; + case 'Label': + widget.minWidth = 70; + widget.minHeight = 20; + break; + case 'PlotTable': + widget.simulator = defaultSimulator; + widget.preselectedSignals = []; + widget.signals = []; // initialize selected signals + widget.minWidth = 400; + widget.minHeight = 300; + widget.width = 500; + widget.height = 500; + widget.time = 60; + break; + case 'Image': + widget.minWidth = 100; + widget.minHeight = 100; + widget.width = 200; + widget.height = 200; + break; + case 'Button': + widget.minWidth = 100; + widget.minHeight = 50; + widget.width = 100; + widget.height = 100; + break; + case 'NumberInput': + widget.minWidth = 200; + widget.minHeight = 50; + widget.width = 200; + widget.height = 50; + break; + case 'Slider': + widget.minWidth = 380; + widget.minHeight = 30; + widget.width = 400; + widget.height = 50; + widget.orientation = WidgetSlider.OrientationTypes.HORIZONTAL.value; // Assign default orientation + break; + case 'Gauge': + widget.simulator = defaultSimulator; + widget.signal = 0; + widget.minWidth = 200; + widget.minHeight = 150; + widget.width = 200; + widget.height = 150; + break; + default: + widget.width = 100; + widget.height = 100; + } + return widget; + } +} + +export default WidgetFactory; \ No newline at end of file diff --git a/src/containers/visualization.js b/src/containers/visualization.js index c82d56d..7328205 100644 --- a/src/containers/visualization.js +++ b/src/containers/visualization.js @@ -12,6 +12,7 @@ import { Container } from 'flux/utils'; import { Button } from 'react-bootstrap'; import { ContextMenu, MenuItem } from 'react-contextmenu'; +import WidgetFactory from '../components/widget-factory'; import ToolboxItem from '../components/toolbox-item'; import Dropzone from '../components/dropzone'; import Widget from './widget'; @@ -25,8 +26,6 @@ import AppDispatcher from '../app-dispatcher'; import NotificationsDataManager from '../data-managers/notifications-data-manager'; import NotificationsFactory from '../data-managers/notifications-factory'; -import WidgetSlider from '../components/widget-slider'; - class Visualization extends Component { static getStores() { return [ VisualizationStore, ProjectStore, SimulationStore, FileStore ]; @@ -129,17 +128,8 @@ class Visualization extends Component { } handleDrop(item, position) { - // add new widget - var widget = { - name: 'Name', - type: item.name, - width: 100, - height: 100, - x: position.x, - y: position.y, - z: 0 - }; + let widget = null; let defaultSimulator = null; if (this.state.simulation.models && this.state.simulation.models.length === 0) { @@ -148,69 +138,8 @@ class Visualization extends Component { defaultSimulator = this.state.simulation.models[0].simulator; } - // set type specific properties - if (item.name === 'Value') { - widget.simulator = defaultSimulator; - widget.signal = 0; - widget.minWidth = 70; - widget.minHeight = 20; - widget.width = 120; - widget.height = 70; - } else if (item.name === 'Plot') { - widget.simulator = defaultSimulator; - widget.signals = [ 0 ]; - widget.time = 60; - widget.minWidth = 400; - widget.minHeight = 200; - widget.width = 400; - widget.height = 200; - } else if (item.name === 'Table') { - widget.simulator = defaultSimulator; - widget.minWidth = 300; - widget.minHeight = 200; - widget.width = 400; - widget.height = 200; - } else if (item.name === 'Label') { - widget.minWidth = 70; - widget.minHeight = 20; - } else if (item.name === 'PlotTable') { - widget.simulator = defaultSimulator; - widget.preselectedSignals = []; - widget.signals = []; // initialize selected signals - widget.minWidth = 400; - widget.minHeight = 300; - widget.width = 500; - widget.height = 500; - widget.time = 60 - } else if (item.name === 'Image') { - widget.minWidth = 100; - widget.minHeight = 100; - widget.width = 200; - widget.height = 200; - } else if (item.name === 'Button') { - widget.minWidth = 100; - widget.minHeight = 50; - widget.width = 100; - widget.height = 100; - } else if (item.name === 'NumberInput') { - widget.minWidth = 200; - widget.minHeight = 50; - widget.width = 200; - widget.height = 50; - } else if (item.name === 'Slider') { - widget.minWidth = 380; - widget.minHeight = 30; - widget.width = 400; - widget.height = 50; - widget.orientation = WidgetSlider.OrientationTypes.HORIZONTAL.value; // Assign default orientation - } else if (item.name === 'Gauge') { - widget.simulator = defaultSimulator; - widget.signal = 0; - widget.minWidth = 200; - widget.minHeight = 150; - widget.width = 200; - widget.height = 150; - } + // create new widget + widget = WidgetFactory.createWidgetOfType(item.name, position, defaultSimulator); var new_widgets = this.state.visualization.widgets; From 00f6739241e950dbffa63b8ea551d209c59ee31b Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Thu, 13 Apr 2017 10:39:31 +0200 Subject: [PATCH 04/27] Missing sim model handling in signal edit control --- .../dialog/edit-widget-signal-control.js | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/components/dialog/edit-widget-signal-control.js b/src/components/dialog/edit-widget-signal-control.js index fd2cd5e..879052b 100644 --- a/src/components/dialog/edit-widget-signal-control.js +++ b/src/components/dialog/edit-widget-signal-control.js @@ -27,24 +27,29 @@ class EditWidgetSignalControl extends Component { } render() { - // get selected simulation model - var simulationModel = {}; + let signalsToRender = []; if (this.props.simulation) { - this.props.simulation.models.forEach((model) => { - if (model.simulation === this.state.widget.simulation) { - simulationModel = model; - } - }); + // get selected simulation model + const simulationModel = this.props.simulation.models.find( model => model.simulation === this.state.widget.simulation ); + + // If simulation model update the signals to render + signalsToRender = simulationModel? simulationModel.mapping : []; } return ( Signal this.props.handleChange(e)}> - {simulationModel.mapping.map((signal, index) => ( - - ))} + { + signalsToRender.length === 0 ? ( + + ) : ( + signalsToRender.map((signal, index) => ( + + )) + ) + } ); From 6c28a1a77b43a23cdfbaa56fa4bf3d82c052503e Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Thu, 13 Apr 2017 13:42:26 +0200 Subject: [PATCH 05/27] outline-offset as Firefox-specific --- src/styles/widgets.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/styles/widgets.css b/src/styles/widgets.css index c8b5678..df4619e 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -19,9 +19,15 @@ background-color: #fff; } +/* Firefox-specific rules */ +@-moz-document url-prefix() { + .editing-widget:not(.border):hover { + outline-offset: -10px; + } +} + .editing-widget:not(.border):hover { outline: 1px solid lightgray; - outline-offset: -10px; } /* Area to trigger the context menu */ From ff77a43dedfec8477dd2f614bd3156199bd4b2d9 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Thu, 13 Apr 2017 13:45:43 +0200 Subject: [PATCH 06/27] Missing classes --- src/components/widget-label.js | 4 +++- src/components/widget-value.js | 5 +++-- src/styles/widgets.css | 9 --------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/components/widget-label.js b/src/components/widget-label.js index 2c29650..0347778 100644 --- a/src/components/widget-label.js +++ b/src/components/widget-label.js @@ -12,7 +12,9 @@ import React, { Component } from 'react'; class WidgetLabel extends Component { render() { return ( -

{this.props.widget.name}

+
+

{this.props.widget.name}

+
); } } diff --git a/src/components/widget-value.js b/src/components/widget-value.js index c096695..0eb7a9d 100644 --- a/src/components/widget-value.js +++ b/src/components/widget-value.js @@ -35,9 +35,10 @@ class WidgetValue extends Component { } render() { + var value_to_render = Number(this.state.value); return ( -
- {this.props.widget.name}: {this.state.value} +
+ {this.props.widget.name} { Number.isNaN(value_to_render)? NaN : value_to_render.toFixed(3) }
); } diff --git a/src/styles/widgets.css b/src/styles/widgets.css index df4619e..e69fae9 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -248,15 +248,6 @@ div[class*="-widget"] .btn[disabled], .btn.disabled, div[class*="-widget"] input /* End Plots */ -/*.single-value-widget { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 95%; - height: 95%; -}*/ - .single-value-widget { width: 100%; height: 100%; From be0fa587ab6f3fa3523f5e20050d2fdaa37234bd Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Thu, 13 Apr 2017 14:39:01 +0200 Subject: [PATCH 07/27] Meaningful hint --- src/components/widget-plot-table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/widget-plot-table.js b/src/components/widget-plot-table.js index cbdee75..a985221 100644 --- a/src/components/widget-plot-table.js +++ b/src/components/widget-plot-table.js @@ -126,7 +126,7 @@ class WidgetPlotTable extends Component { { checkBoxes } - ) : ( No signal found, select a different signal type. ) + ) : ( No signal has been pre-selected. ) }
From e980245be59fd923e24acf396f1d9b90666ccadd Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Thu, 13 Apr 2017 14:39:32 +0200 Subject: [PATCH 08/27] No overflow --- src/styles/widgets.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/widgets.css b/src/styles/widgets.css index e69fae9..0a4f286 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -237,7 +237,7 @@ div[class*="-widget"] .btn[disabled], .btn.disabled, div[class*="-widget"] input .signal-legend { font-size: 0.8em; font-weight: 700; - overflow-x: hidden; + overflow: hidden; } .legend-color { From fed600d2e90db083fe354951cdc54f2f9e4afc4b Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Thu, 13 Apr 2017 15:46:54 +0200 Subject: [PATCH 09/27] Dropped use of height percentage in column-oriented flex child (not supported by Chrome) --- src/styles/widgets.css | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/styles/widgets.css b/src/styles/widgets.css index 0a4f286..c449bf6 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -200,11 +200,6 @@ div[class*="-widget"] .btn[disabled], .btn.disabled, div[class*="-widget"] input .plot-table-widget input[type="checkbox"] { display: none; } - -.plot-table-widget .widget-plot { - -webkit-flex: 1 1 auto; - flex: 1 1 auto; -} /* End PlotTable Widget */ /* Plot Widget */ @@ -213,16 +208,17 @@ div[class*="-widget"] .btn[disabled], .btn.disabled, div[class*="-widget"] input display: flex; flex-direction: column; } - -.plot-widget .widget-plot { - -webkit-flex: 1 1 auto; - flex: 1 1 auto; -} /* End Plot Widget */ /* Plots */ +/* The plot container, in order to avoid 100% height/width */ +.widget-plot { + display: flex; + -webkit-flex: 1 1 auto; + flex: 1 1 auto; +} + .chart-wrapper { - height: 100%; width: 100%; } From 7eed1cdc54c537ee08a12c00d4d355c14a1c1f54 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Tue, 18 Apr 2017 14:03:33 +0200 Subject: [PATCH 10/27] let visualization area expand and shrink with new widgets --- src/containers/visualization.js | 68 ++++++++++++++++++++++++--------- src/styles/app.css | 18 ++++----- 2 files changed, 57 insertions(+), 29 deletions(-) diff --git a/src/containers/visualization.js b/src/containers/visualization.js index 6085b06..813dae5 100644 --- a/src/containers/visualization.js +++ b/src/containers/visualization.js @@ -50,11 +50,13 @@ class Visualization extends Component { editModal: prevState.editModal || false, modalData: prevState.modalData || null, modalIndex: prevState.modalIndex || null, - + + maxWidgetHeight: prevState.maxWidgetHeight || 0, + dropZoneHeight: prevState.dropZoneHeight || 0, last_widget_key: prevState.last_widget_key || 0 }; } - + componentWillMount() { AppDispatcher.dispatch({ type: 'visualizations/start-load' @@ -116,6 +118,8 @@ class Visualization extends Component { widgets: tempVisualization.widgets? this.transformToWidgetsDict(tempVisualization.widgets) : {} }); + this.computeHeightWithWidgets(visualization.widgets); + this.setState({ visualization: visualization, project: null }); AppDispatcher.dispatch({ @@ -210,6 +214,8 @@ class Visualization extends Component { var visualization = Object.assign({}, this.state.visualization, { widgets: new_widgets }); + + this.increaseHeightWithWidget(widget); this.setState({ visualization: visualization }); } @@ -227,9 +233,48 @@ class Visualization extends Component { var visualization = Object.assign({}, this.state.visualization, { widgets: new_widgets }); + + // Check if the height needs to be increased, the section may have shrunk if not + if (!this.increaseHeightWithWidget(updated_widget)) { + this.computeHeightWithWidgets(visualization.widgets); + } this.setState({ visualization: visualization }, callback); } + /* + * Set the initial height state based on the existing widgets + */ + computeHeightWithWidgets(widgets) { + // Compute max height from widgets + let maxHeight = Object.keys(widgets).reduce( (maxHeightSoFar, widgetKey) => { + let thisWidget = widgets[widgetKey]; + let thisWidgetHeight = thisWidget.y + thisWidget.height; + + return thisWidgetHeight > maxHeightSoFar? thisWidgetHeight : maxHeightSoFar; + }, 0); + + this.setState({ + maxWidgetHeight: maxHeight, + dropZoneHeight: maxHeight + 40 + }); + } + /* + * Adapt the area's height with the position of the new widget. + * Return true if the height increased, otherwise false. + */ + increaseHeightWithWidget(widget) { + let increased = false; + let thisWidgetHeight = widget.y + widget.height; + if (thisWidgetHeight > this.state.maxWidgetHeight) { + increased = true; + this.setState({ + maxWidgetHeight: thisWidgetHeight, + dropZoneHeight: thisWidgetHeight + 40 + }); + } + return increased; + } + editWidget(e, data) { this.setState({ editModal: true, modalData: this.state.visualization.widgets[data.key], modalIndex: data.key }); } @@ -324,25 +369,10 @@ class Visualization extends Component { } render() { - // calculate widget area height - var height = 0; - var current_widgets = this.state.visualization.widgets; - if (current_widgets) { - Object.keys(current_widgets).forEach( (widget_key) => { - var widget = current_widgets[widget_key]; - if (widget.y + widget.height > height) { - height = widget.y + widget.height; - } - }); - - // add padding - height += 40; - } - return ( -
+
@@ -383,7 +413,7 @@ class Visualization extends Component {
} - this.handleDrop(item, position)} editing={this.state.editing}> + this.handleDrop(item, position)} editing={this.state.editing}> {current_widgets != null && Object.keys(current_widgets).map( (widget_key) => ( this.widgetChange(w, k)} onWidgetStatusChange={(w, k) => this.widgetStatusChange(w, k)} editing={this.state.editing} index={widget_key} grid={this.state.grid} /> diff --git a/src/styles/app.css b/src/styles/app.css index f1972a0..a22bcaa 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -47,8 +47,7 @@ body { .app-footer { width: 100%; height: 60px; - position: absolute; - bottom: 0px; + float: right; padding-top: 20px; text-align: center; @@ -57,14 +56,12 @@ body { } .app-content { - position: absolute; - bottom: 0px; - top: 60px; - right: 0px; - left: 200px; - min-height: 400px; + display: flex; + float: right; + min-height: calc(100vh - 140px); + width: calc(100% - 220px); - margin: 20px 20px 60px 20px; + margin: 20px 20px 0px 20px; padding: 15px 20px; background-color: #fff; @@ -207,7 +204,8 @@ body { } .section { - height: 100%; + min-height: 100%; + width: 100%; } .section-header div { From 8e08dcf4e781496adea45687bdc8b8bc9d2abca4 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Tue, 18 Apr 2017 14:04:47 +0200 Subject: [PATCH 11/27] Update sections with css layout --- src/containers/project.js | 2 +- src/containers/projects.js | 2 +- src/containers/simulation.js | 2 +- src/containers/simulations.js | 2 +- src/containers/simulators.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/containers/project.js b/src/containers/project.js index 910eebe..48c42ba 100644 --- a/src/containers/project.js +++ b/src/containers/project.js @@ -136,7 +136,7 @@ class Visualizations extends Component { } return ( -
+

{this.state.project.name}

diff --git a/src/containers/projects.js b/src/containers/projects.js index 4e6a4b6..84931f0 100644 --- a/src/containers/projects.js +++ b/src/containers/projects.js @@ -90,7 +90,7 @@ class Projects extends Component { render() { return ( -
+

Projects

diff --git a/src/containers/simulation.js b/src/containers/simulation.js index b6f6c97..75c5877 100644 --- a/src/containers/simulation.js +++ b/src/containers/simulation.js @@ -119,7 +119,7 @@ class Simulation extends Component { render() { return ( -
+

{this.state.simulation.name}

diff --git a/src/containers/simulations.js b/src/containers/simulations.js index dd80a71..f21c3cd 100644 --- a/src/containers/simulations.js +++ b/src/containers/simulations.js @@ -100,7 +100,7 @@ class Simulations extends Component { render() { return ( -
+

Simulations

diff --git a/src/containers/simulators.js b/src/containers/simulators.js index a913f73..620ecc1 100644 --- a/src/containers/simulators.js +++ b/src/containers/simulators.js @@ -84,7 +84,7 @@ class Simulators extends Component { render() { return ( -
+

Simulators

From 94bb086d5674de15b5a4971ff7062994c25b36d2 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Tue, 18 Apr 2017 16:20:01 +0200 Subject: [PATCH 12/27] more consisting widget sizes (handle overflow) --- src/styles/widgets.css | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/styles/widgets.css b/src/styles/widgets.css index c449bf6..6c8987a 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -9,6 +9,7 @@ .widget { background-color: #fff; + overflow: auto; } .border { @@ -37,7 +38,7 @@ position: absolute; top: 0px; left: 0px; - padding: 3px 6px; + overflow: auto; } .react-contextmenu { @@ -281,6 +282,10 @@ div[class*="-widget"] .btn[disabled], .btn.disabled, div[class*="-widget"] input div[class*="-widget"] label { cursor: inherit; } + +.number-input-widget .form-horizontal .form-group { + margin: 0px; +} /* End number input widget */ /* Slider widget */ @@ -339,7 +344,7 @@ input[type=range]::-ms-thumb { .gauge-widget canvas { width: 100%; - height: 90%; + height: 87%; } .gauge-name { From 3c60a4800c4fb68bda620b5c1e6627ac4084e3b4 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Wed, 19 Apr 2017 09:12:16 +0200 Subject: [PATCH 13/27] Allow locking aspect ratio (gauge) --- src/containers/visualization.js | 5 +++-- src/containers/widget.js | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/containers/visualization.js b/src/containers/visualization.js index 813dae5..ec9dc1e 100644 --- a/src/containers/visualization.js +++ b/src/containers/visualization.js @@ -200,10 +200,11 @@ class Visualization extends Component { } else if (item.name === 'Gauge') { widget.simulator = this.state.simulation.models[0].simulator; widget.signal = 0; - widget.minWidth = 200; + widget.minWidth = 150; widget.minHeight = 150; widget.width = 200; - widget.height = 150; + widget.height = 200; + widget.lockedAspectRatio = true; } var new_widgets = this.state.visualization.widgets; diff --git a/src/containers/widget.js b/src/containers/widget.js index 8f17940..d8c9e00 100644 --- a/src/containers/widget.js +++ b/src/containers/widget.js @@ -161,6 +161,7 @@ class Widget extends Component { initial={{ x: Number(widget.x), y: Number(widget.y), width: widget.width, height: widget.height }} minWidth={ widget.minWidth } minHeight={ widget.minHeight } + lockAspectRatio={ widget.lockedAspectRatio } bounds={'parent'} className={ widgetClasses } onResizeStart={ (direction, styleSize, clientSize, event) => this.borderWasClicked(event) } From 02011d6067389a372412e574371658eecfabb803 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Wed, 19 Apr 2017 15:46:23 +0200 Subject: [PATCH 14/27] Included slider library. Slider adapts to orientation changes --- package.json | 3 +- src/components/widget-slider.js | 63 +++++++++++++++------- src/containers/visualization.js | 7 ++- src/containers/widget.js | 2 +- src/styles/widgets.css | 92 ++++++++++++++++++++------------- 5 files changed, 108 insertions(+), 59 deletions(-) diff --git a/package.json b/package.json index 7d9df16..1e6f8b1 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "react-router": "^3.0.2", "superagent": "^3.5.0", "gaugeJS": "^1.3.2", - "d3-scale": "^1.0.5" + "d3-scale": "^1.0.5", + "rc-slider": "^7.0.1" }, "devDependencies": { "react-scripts": "0.9.3" diff --git a/src/components/widget-slider.js b/src/components/widget-slider.js index 40f890f..2ea720c 100644 --- a/src/components/widget-slider.js +++ b/src/components/widget-slider.js @@ -9,6 +9,8 @@ import React, { Component } from 'react'; import classNames from 'classnames'; +import Slider from 'rc-slider'; +import 'rc-slider/assets/index.css'; class WidgetSlider extends Component { @@ -27,43 +29,68 @@ class WidgetSlider extends Component { }; } - valueChanged(e) { - this.setState({ value: e.target.value }); + componentWillReceiveProps(nextProps) { + // Update value + if (nextProps.widget.value && this.state.value !== nextProps.widget.value) { + this.setState({ value: nextProps.widget.value }) + } + // Check if the orientation changed, update the size if it did + if (this.props.widget.orientation !== nextProps.widget.orientation) { + let baseWidget = nextProps.widget; + // Exchange dimensions and constraints + let newWidget = Object.assign({}, baseWidget, { + width: baseWidget.height, + height: baseWidget.width, + minWidth: baseWidget.minHeight, + minHeight: baseWidget.minWidth, + maxWidth: baseWidget.maxHeight, + maxHeight: baseWidget.maxWidth + }); + nextProps.onWidgetChange(newWidget); + } + } + + valueIsChanging(newValue) { + this.setState({ value: newValue }); + } + + valueChanged(newValue) { + // Enable to propagate action + // let newWidget = Object.assign({}, this.props.widget, { + // value: newValue + // }); + // this.props.onWidgetChange(newWidget); } render() { + + let isVertical = this.props.widget.orientation === WidgetSlider.OrientationTypes.VERTICAL.value; + let fields = { 'name': this.props.widget.name, - 'control': this.valueChanged(e) } defaultValue={ this.state.value }/>, + 'control': this.valueIsChanging(v) } onAfterChange={ (v) => this.valueChanged(v) }/>, 'value': this.state.value } - let vertical = this.props.widget.orientation === WidgetSlider.OrientationTypes.VERTICAL.value; var widgetClasses = classNames({ 'slider-widget': true, 'full': true, - 'vertical': vertical, - 'horizontal': !vertical + 'vertical': isVertical, + 'horizontal': !isVertical }); return ( this.props.widget.orientation === WidgetSlider.OrientationTypes.HORIZONTAL.value? (
-
- -
-
- { fields.control } - { fields.value } -
+ +
{ fields.control }
+ { fields.value }
) : (
-
- - { fields.value } -
-
{ fields.control }
+ + { fields.control } + { fields.value }
) ); diff --git a/src/containers/visualization.js b/src/containers/visualization.js index ec9dc1e..84be870 100644 --- a/src/containers/visualization.js +++ b/src/containers/visualization.js @@ -192,8 +192,11 @@ class Visualization extends Component { widget.width = 200; widget.height = 50; } else if (item.name === 'Slider') { - widget.minWidth = 380; - widget.minHeight = 30; + // Set dimensions and constraints as Horizontal + widget.minWidth = 150; + widget.minHeight = 50; + widget.maxHeight = 51; + widget.maxWidth = 1000; widget.width = 400; widget.height = 50; widget.orientation = WidgetSlider.OrientationTypes.HORIZONTAL.value; // Assign default orientation diff --git a/src/containers/widget.js b/src/containers/widget.js index d8c9e00..1b55f94 100644 --- a/src/containers/widget.js +++ b/src/containers/widget.js @@ -143,7 +143,7 @@ class Widget extends Component { } else if (widget.type === 'NumberInput') { element = } else if (widget.type === 'Slider') { - element = + element = this.props.onWidgetStatusChange(w, this.props.index) } /> } else if (widget.type === 'Gauge') { element = } diff --git a/src/styles/widgets.css b/src/styles/widgets.css index 6c8987a..aa57d12 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -288,52 +288,70 @@ div[class*="-widget"] label { } /* End number input widget */ -/* Slider widget */ -.slider-widget.vertical input[type="range"] { - position: absolute; - top: 40%; - left: 50%; - transform: rotate(270deg); - /*margin-left: 20px;*/ - width: 150px; -} - -input[type=range]::-moz-range-thumb { - background: #ffffff; -} - -input[type=range]::-webkit-slider-thumb { - background: #ffffff; -} - -input[type=range]::-ms-thumb { - background: #ffffff; -} - -.slider-widget.horizontal div { - width: 50%; - display: inline-block; - text-align: center; - vertical-align: top; -} - -.slider-widget.horizontal span { - display: block; - margin: 5px; -} - -.slider-widget.vertical div { - width: 50%; +/* Begin Slider widget */ +.slider-widget { display: flex; - flex-direction: column; align-items: center; justify-content: space-around; } +.slider-widget label { + flex: 0 0 auto; + text-align: center; +} + .slider-widget span { + text-align: center; font-size: 1.5em; font-weight: 600; } + +.slider-widget.horizontal .slider { + flex: 1 1 auto; +} + +.slider-widget.horizontal span { + flex: 0 0 50pt; +} + +.slider-widget.horizontal label { + padding-right: 10px; +} + +.slider-widget.vertical { + flex-direction: column; +} + +.slider-widget.vertical span { + padding-top: 10px; +} + +.slider-widget.vertical label { + padding-bottom: 10px; +} + +/* Begin Slider customization */ +.rc-slider-track { + background-color: #6EA2B0; +} + +.rc-slider-handle, .rc-slider-handle:hover { + border-color: #6EA2B0; +} + +.rc-slider-disabled { + background-color: inherit; +} + +.rc-slider-disabled .rc-slider-handle { + cursor: inherit; +} + +.rc-slider-disabled .rc-slider-handle:hover { + border-color:#ccc; +} +/* End Slider customization */ + /* End slider widget */ /* Gauge widget */ From 3859aa887bff2bb98262e427bee977709c93431a Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Wed, 19 Apr 2017 16:04:28 +0200 Subject: [PATCH 15/27] Center label widget content --- src/styles/widgets.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/styles/widgets.css b/src/styles/widgets.css index aa57d12..04dd7f3 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -388,4 +388,10 @@ div[class*="-widget"] label { bottom: 10%; text-align: center; } -/* End gauge widget */ \ No newline at end of file +/* End gauge widget */ + +/* Begin label widget */ +.label-widget { + text-align: center; +} +/* End label widget */ \ No newline at end of file From 67d0e159bf1323e88116b790511ff9f83443fa39 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Wed, 19 Apr 2017 16:14:42 +0200 Subject: [PATCH 16/27] Center content in table widget --- src/components/widget-table.js | 2 +- src/styles/widgets.css | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/widget-table.js b/src/components/widget-table.js index 91e59de..e8862f3 100644 --- a/src/components/widget-table.js +++ b/src/components/widget-table.js @@ -57,7 +57,7 @@ class WidgetTable extends Component { render() { return ( -
+

{this.props.widget.name}

diff --git a/src/styles/widgets.css b/src/styles/widgets.css index 04dd7f3..8752db5 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -394,4 +394,10 @@ div[class*="-widget"] label { .label-widget { text-align: center; } -/* End label widget */ \ No newline at end of file +/* End label widget */ + +/* Begin table widget */ +.table-widget td, .table-widget th { + text-align: center; +} +/* End table widget*/ \ No newline at end of file From d163d93cd05fd9a7669eb3b185f12006f1d36ff9 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Thu, 20 Apr 2017 15:06:17 +0200 Subject: [PATCH 17/27] Gauge: restrict updates to value changes, gauge updated directly. --- src/components/widget-gauge.js | 29 +++++++++++++++++++---------- src/containers/widget.js | 2 +- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/components/widget-gauge.js b/src/components/widget-gauge.js index a61c3e8..b4365fe 100644 --- a/src/components/widget-gauge.js +++ b/src/components/widget-gauge.js @@ -23,7 +23,7 @@ class WidgetGauge extends Component { } staticLabels(widget_height) { - var label_font_size = widget_height * 0.055; // font scaling factor + let label_font_size = Math.floor(widget_height * 0.055); // font scaling factor, integer for performance return { font: label_font_size + 'px "Helvetica Neue"', labels: [0.0, 0.1, 0.5, 0.9, 1.0], @@ -58,17 +58,18 @@ class WidgetGauge extends Component { this.gauge.set(this.state.value); } - componentWillUpdate() { - // Update labels after possible resize - this.gauge.setOptions({ staticLabels: this.staticLabels(this.props.widget.height) }); + shouldComponentUpdate(nextProps, nextState) { + + // Check if size changed, resize labels if it did (the canvas itself is scaled with css) + if (this.props.widget.height !== nextProps.widget.height) { + this.updateAfterResize(nextProps.widget.height); + } + + // signal component update only if the value changed + return this.state.value !== nextState.value; } - componentDidUpdate() { - // update gauge's value - this.gauge.set(this.state.value); - } - - componentWillReceiveProps(nextProps) { + componentWillReceiveProps(nextProps) { // update value const simulator = nextProps.widget.simulator; @@ -84,9 +85,17 @@ class WidgetGauge extends Component { const new_value = Math.round( signal[signal.length - 1].y * 1e3 ) / 1e3; if (this.state.value !== new_value) { this.setState({ value: new_value }); + + // update gauge's value + this.gauge.set(new_value); } } + updateAfterResize(newHeight) { + // Update labels after resize + this.gauge.setOptions({ staticLabels: this.staticLabels(newHeight) }); + } + render() { var componentClass = this.props.editing ? "gauge-widget editing" : "gauge-widget"; var signalType = null; diff --git a/src/containers/widget.js b/src/containers/widget.js index 1b55f94..89323b1 100644 --- a/src/containers/widget.js +++ b/src/containers/widget.js @@ -77,7 +77,7 @@ class Widget extends Component { resizeStop(direction, styleSize, clientSize, delta) { // update widget - var widget = this.props.data; + let widget = Object.assign({}, this.props.data); // resize depends on direction if (direction === 'left' || direction === 'topLeft' || direction === 'bottomLeft') { From fe1c9248367acec218da535c4dad5b18010af3b5 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Thu, 20 Apr 2017 16:30:12 +0200 Subject: [PATCH 18/27] correct highlight in selected plot table widget buttons --- src/styles/widgets.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/widgets.css b/src/styles/widgets.css index 8752db5..b327871 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -115,7 +115,7 @@ div[class*="-widget"] .btn[disabled], .btn.disabled, div[class*="-widget"] input pointer-events: none; } -.editing-widget .btn-default.disabled { +.editing-widget .btn-default.active { background-color: #abcfd8; border-color: #ccc; } From 3be7fdcd35a829fb2d331b5952a840c374904166 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Fri, 21 Apr 2017 11:54:41 +0200 Subject: [PATCH 19/27] Initial blank plot and allocate fixed initial height to legend (avoid resizing) --- src/components/widget-plot/plot.js | 59 +++++++++++++++++++++--------- src/styles/widgets.css | 5 +++ 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/components/widget-plot/plot.js b/src/components/widget-plot/plot.js index bb5ff01..cb9aba8 100644 --- a/src/components/widget-plot/plot.js +++ b/src/components/widget-plot/plot.js @@ -17,13 +17,26 @@ class Plot extends Component { this.chartWrapper = null; - this.state = { - size: { w: 0, h: 0 }, - firstTimestamp: 0, - latestTimestamp: 0, - sequence: null, - values: [] - }; + // Initialize plot size and data + this.state = Object.assign( + { size: { w: 0, h: 0 } }, + this.getPlotInitData(true) + ); + } + + // Get an object with 'invisible' init data for the last minute. + // Include start/end timestamps if required. + getPlotInitData(withRangeTimestamps = false) { + + const initSecondTime = Date.now(); + const initFirstTime = initSecondTime - 1000 * 60; // Decrease 1 min + const values = [{ values: [{x: initFirstTime, y: 0}], strokeWidth: 0 }]; + + let output = withRangeTimestamps? + { sequence: 0, values: values, firstTimestamp: initFirstTime, latestTimestamp: initSecondTime, } : + { sequence: 0, values: values }; + + return output; } componentWillReceiveProps(nextProps) { @@ -37,22 +50,34 @@ class Plot extends Component { this.setState({size: { w, h } }); } + // If signals were cleared, clear the plot (triggers a new state) + if (this.signalsWereJustCleared(nextProps)) { this.clearPlot(); return; } + + // If no signals have been selected, just leave + if (nextProps.signals == null || nextProps.signals.length === 0) { return; } + // Identify simulation reset - if (nextData == null || nextData.length === 0 || nextData.values[0].length === 0) { - // clear values - this.setState({ values: [], sequence: null }); - return; - } + if (nextData == null || nextData.length === 0 || nextData.values[0].length === 0) { this.clearPlot(); return; } // check if new data, otherwise skip - if (this.state.sequence >= nextData.sequence) { - return; - } - + if (this.state.sequence >= nextData.sequence) { return; } + this.updatePlotData(nextProps); } + signalsWereJustCleared(nextProps) { + + return this.props.signals && + nextProps.signals && + this.props.signals.length > 0 && + nextProps.signals.length === 0; + } + + clearPlot() { + this.setState( this.getPlotInitData(false) ); + } + updatePlotData(nextProps) { let nextData = nextProps.simulatorData; @@ -91,7 +116,7 @@ class Plot extends Component { return (
this.chartWrapper = domNode }> - {this.state.sequence && + {this.state.sequence != null && Date: Fri, 21 Apr 2017 15:10:36 +0200 Subject: [PATCH 20/27] fixed signals control generic controlId --- src/components/dialog/edit-widget-signals-control.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/dialog/edit-widget-signals-control.js b/src/components/dialog/edit-widget-signals-control.js index a7fcd07..b84d930 100644 --- a/src/components/dialog/edit-widget-signals-control.js +++ b/src/components/dialog/edit-widget-signals-control.js @@ -16,8 +16,7 @@ class EditWidgetSignalsControl extends Component { this.state = { widget: { - simulator: '', - preselectedSignals: [] + simulator: '' } }; } @@ -28,7 +27,7 @@ class EditWidgetSignalsControl extends Component { } handleSignalChange(checked, index) { - var signals = this.state.widget.preselectedSignals; + var signals = this.state.widget[this.props.controlId]; var new_signals; if (checked) { @@ -57,11 +56,11 @@ class EditWidgetSignalsControl extends Component { Signals { - signalsToRender.length === 0 ? ( + signalsToRender.length === 0 || !this.state.widget.hasOwnProperty(this.props.controlId)? ( No signals available. ) : ( signalsToRender.map((signal, index) => ( - this.handleSignalChange(e.target.checked, index)}>{signal.name} + this.handleSignalChange(e.target.checked, index)}>{signal.name} )) ) } From dd068b919d08d89a68f29b33803360d0d6541b2e Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Fri, 21 Apr 2017 16:13:07 +0200 Subject: [PATCH 21/27] Plots' Y-axis label can be edited --- .../dialog/edit-widget-text-control.js | 39 +++++++++++++++++++ src/components/dialog/edit-widget.js | 7 +++- src/components/widget-factory.js | 2 + src/components/widget-plot-table.js | 2 +- src/components/widget-plot.js | 2 +- src/components/widget-plot/plot.js | 1 + src/styles/widgets.css | 1 + 7 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 src/components/dialog/edit-widget-text-control.js diff --git a/src/components/dialog/edit-widget-text-control.js b/src/components/dialog/edit-widget-text-control.js new file mode 100644 index 0000000..d4196b4 --- /dev/null +++ b/src/components/dialog/edit-widget-text-control.js @@ -0,0 +1,39 @@ +/** + * File: edit-widget-text-control.js + * Author: Ricardo Hernandez-Montoya + * Date: 21.04.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 { FormGroup, FormControl, ControlLabel } from 'react-bootstrap'; + +class EditWidgetTextControl extends Component { + constructor(props) { + super(props); + + this.state = { + widget: {} + }; + } + + componentWillReceiveProps(nextProps) { + // Update state's widget with props + this.setState({ widget: nextProps.widget }); + } + + render() { + + return ( + + { this.props.label } + this.props.handleChange(e)} /> + + + ); + } +} + +export default EditWidgetTextControl; \ No newline at end of file diff --git a/src/components/dialog/edit-widget.js b/src/components/dialog/edit-widget.js index b90e75c..a7fc542 100644 --- a/src/components/dialog/edit-widget.js +++ b/src/components/dialog/edit-widget.js @@ -12,6 +12,7 @@ import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap'; import Dialog from './dialog'; +import EditWidgetTextControl from './edit-widget-text-control'; import EditWidgetTimeControl from './edit-widget-time-control'; import EditImageWidgetControl from './edit-widget-image-control'; import EditWidgetSimulatorControl from './edit-widget-simulator-control'; @@ -86,7 +87,8 @@ class EditWidgetDialog extends Component { dialogControls.push( this.validateForm(id)} simulation={this.props.simulation} handleChange={(e, index) => this.handleChange(e, index)} />, this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} />, - this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} /> + this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} />, + this.handleChange(e)} /> ) } else if (this.props.widget.type === 'Table') { dialogControls.push( @@ -104,7 +106,8 @@ class EditWidgetDialog extends Component { } else if (this.props.widget.type === 'PlotTable') { dialogControls.push( this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} />, - this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} /> + this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} />, + this.handleChange(e)} /> ) } else if (this.props.widget.type === 'Slider') { dialogControls.push( diff --git a/src/components/widget-factory.js b/src/components/widget-factory.js index 0f45052..b22faa3 100644 --- a/src/components/widget-factory.js +++ b/src/components/widget-factory.js @@ -37,6 +37,7 @@ class WidgetFactory { case 'Plot': widget.simulator = defaultSimulator; widget.signals = [ 0 ]; + widget.ylabel = ''; widget.time = 60; widget.minWidth = 400; widget.minHeight = 200; @@ -58,6 +59,7 @@ class WidgetFactory { widget.simulator = defaultSimulator; widget.preselectedSignals = []; widget.signals = []; // initialize selected signals + widget.ylabel = ''; widget.minWidth = 400; widget.minHeight = 300; widget.width = 500; diff --git a/src/components/widget-plot-table.js b/src/components/widget-plot-table.js index 4041c8f..43eaf03 100644 --- a/src/components/widget-plot-table.js +++ b/src/components/widget-plot-table.js @@ -136,7 +136,7 @@ class WidgetPlotTable extends Component {
- +
diff --git a/src/components/widget-plot.js b/src/components/widget-plot.js index e0c2662..1c05d58 100644 --- a/src/components/widget-plot.js +++ b/src/components/widget-plot.js @@ -43,7 +43,7 @@ class WidgetPlot extends Component {

{this.props.widget.name}

- +
diff --git a/src/components/widget-plot/plot.js b/src/components/widget-plot/plot.js index cb9aba8..6bae9d0 100644 --- a/src/components/widget-plot/plot.js +++ b/src/components/widget-plot/plot.js @@ -126,6 +126,7 @@ class Plot extends Component { gridHorizontal={true} xAccessor={(d) => { if (d != null) { return new Date(d.x); } }} xAxisTickCount={ tickCount } + yAxisLabel={ this.props.yAxisLabel } hoverAnimation={false} circleRadius={0} domain={{ x: [this.state.firstTimestamp, this.state.latestTimestamp] }} diff --git a/src/styles/widgets.css b/src/styles/widgets.css index b031497..93c40e4 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -221,6 +221,7 @@ div[class*="-widget"] .btn[disabled], .btn.disabled, div[class*="-widget"] input .chart-wrapper { width: 100%; + padding-left: 10px; } .plot-legend { From 9d229a828ce48314f81a475d7fd82472fbf9ec80 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Mon, 24 Apr 2017 14:41:23 +0200 Subject: [PATCH 22/27] Button color selection --- .../dialog/edit-widget-color-control.js | 70 +++++++++++++++++++ src/components/dialog/edit-widget.js | 6 ++ src/components/widget-button.js | 15 +++- src/components/widget-factory.js | 2 + src/styles/widgets.css | 37 +++++++++- 5 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 src/components/dialog/edit-widget-color-control.js diff --git a/src/components/dialog/edit-widget-color-control.js b/src/components/dialog/edit-widget-color-control.js new file mode 100644 index 0000000..1b50f44 --- /dev/null +++ b/src/components/dialog/edit-widget-color-control.js @@ -0,0 +1,70 @@ +/** + * File: edit-widget-color-control.js + * Author: Ricardo Hernandez-Montoya + * Date: 24.04.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 { FormGroup, Col, Row, Radio, ControlLabel } from 'react-bootstrap'; +import classNames from 'classnames'; +import { scaleOrdinal, schemeCategory20 } from 'd3-scale'; + +class EditWidgetColorControl extends Component { + + static get ColorPalette() { + let colorCount = 0; + const colors = []; + const colorScale = scaleOrdinal(schemeCategory20); + while (colorCount < 20) { colors.push(colorScale(colorCount)); colorCount++; } + colors.unshift('#000', '#FFF'); // include black and white + + return colors; + } + + constructor(props) { + super(props); + + this.state = { + widget: {} + }; + } + + componentWillReceiveProps(nextProps) { + // Update state's widget with props + this.setState({ widget: nextProps.widget }); + } + + render() { + + return ( + + +
+ { this.props.label } + + + { + EditWidgetColorControl.ColorPalette.map( (color, idx ) => { + let colorStyle = { + background: color, + borderColor: color + }; + + let checkedClass = classNames({ + 'checked': idx === this.state.widget[this.props.controlId] + }); + + return ( this.props.handleChange({target: { id: this.props.controlId, value: idx}})} />) + } + ) + } + + + ) + } +} + +export default EditWidgetColorControl; \ No newline at end of file diff --git a/src/components/dialog/edit-widget.js b/src/components/dialog/edit-widget.js index a7fc542..d23c3a5 100644 --- a/src/components/dialog/edit-widget.js +++ b/src/components/dialog/edit-widget.js @@ -13,6 +13,7 @@ import { FormGroup, FormControl, ControlLabel } from 'react-bootstrap'; import Dialog from './dialog'; import EditWidgetTextControl from './edit-widget-text-control'; +import EditWidgetColorControl from './edit-widget-color-control'; import EditWidgetTimeControl from './edit-widget-time-control'; import EditImageWidgetControl from './edit-widget-image-control'; import EditWidgetSimulatorControl from './edit-widget-simulator-control'; @@ -113,6 +114,11 @@ class EditWidgetDialog extends Component { dialogControls.push( this.validateForm(id)} simulation={this.props.simulation} handleChange={(e) => this.handleChange(e)} />, ) + } else if (this.props.widget.type === 'Button') { + dialogControls.push( + this.validateForm(id)} handleChange={(e, index) => this.handleChange(e, index)} />, + this.validateForm(id)} handleChange={(e, index) => this.handleChange(e, index)} /> + ) } } diff --git a/src/components/widget-button.js b/src/components/widget-button.js index 72f33fa..98ced0d 100644 --- a/src/components/widget-button.js +++ b/src/components/widget-button.js @@ -9,6 +9,8 @@ import React, { Component } from 'react'; +import EditWidgetColorControl from './dialog/edit-widget-color-control'; + class WidgetButton extends Component { action(e) { @@ -17,12 +19,21 @@ class WidgetButton extends Component { } render() { + + let colors = EditWidgetColorControl.ColorPalette; + + let colorStyle = { + background: colors[this.props.widget.background_color], + color: colors[this.props.widget.font_color], + borderColor: colors[this.props.widget.font_color] + } + return (
{ this.props.editing ? ( - + ) : ( - + ) }
diff --git a/src/components/widget-factory.js b/src/components/widget-factory.js index b22faa3..6414180 100644 --- a/src/components/widget-factory.js +++ b/src/components/widget-factory.js @@ -77,6 +77,8 @@ class WidgetFactory { widget.minHeight = 50; widget.width = 100; widget.height = 100; + widget.background_color = 1; + widget.font_color = 0; break; case 'NumberInput': widget.minWidth = 200; diff --git a/src/styles/widgets.css b/src/styles/widgets.css index 93c40e4..394b3d0 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -145,6 +145,39 @@ div[class*="-widget"] .btn[disabled], .btn.disabled, div[class*="-widget"] input } /* End match */ +/* Begin edit menu: Colors */ +.color-control input[type="radio"] { + display: none; +} + +.color-control .radio-inline.checked { + border-color: #000 !important; +} + +.color-control .radio-inline { + height: 24px; + flex: 1 1 auto; + border: 2px solid; + /* Reset bootstrap padding */ + padding-left: 0px; +} + +.color-control .radio-inline + .radio-inline { + /* Reset bootstrap margin */ + margin-left: 0px; +} + +.color-control .radio-inline:hover { + border-color: #444 !important; +} + +.color-control div[class*="colors-column-"] { + display: flex; + padding: 2px 20px; +} + +/* End edit menu: Colors */ + /* PlotTable widget */ .plot-table-widget, .plot-widget, .value-widget, .image-widget, .label-widget { width: 100%; @@ -270,8 +303,10 @@ div[class*="-widget"] .btn[disabled], .btn.disabled, div[class*="-widget"] input .button-widget button { border-radius: 25px; border-style: double; - border-width: 5px; + border-width: 4px; overflow-x: hidden; + font-weight: 500; + font-size: 1.2em; } .button-widget button:hover { From 1ded5755d8c4a8799d38180e8300db74c51aaac2 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Tue, 25 Apr 2017 14:31:28 +0200 Subject: [PATCH 23/27] Box to group widgets --- src/components/dialog/edit-widget.js | 4 ++++ src/components/widget-box.js | 33 ++++++++++++++++++++++++++++ src/components/widget-factory.js | 8 +++++++ src/containers/visualization.js | 1 + src/containers/widget.js | 3 +++ src/styles/widgets.css | 10 ++++++++- 6 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/components/widget-box.js diff --git a/src/components/dialog/edit-widget.js b/src/components/dialog/edit-widget.js index d23c3a5..696675b 100644 --- a/src/components/dialog/edit-widget.js +++ b/src/components/dialog/edit-widget.js @@ -119,6 +119,10 @@ class EditWidgetDialog extends Component { this.validateForm(id)} handleChange={(e, index) => this.handleChange(e, index)} />, this.validateForm(id)} handleChange={(e, index) => this.handleChange(e, index)} /> ) + } else if (this.props.widget.type === 'Box') { + dialogControls.push( + this.validateForm(id)} handleChange={(e, index) => this.handleChange(e, index)} /> + ) } } diff --git a/src/components/widget-box.js b/src/components/widget-box.js new file mode 100644 index 0000000..b917b65 --- /dev/null +++ b/src/components/widget-box.js @@ -0,0 +1,33 @@ +/** + * File: widget-box.js + * Author: Ricardo Hernandez-Montoya + * Date: 25.04.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 EditWidgetColorControl from './dialog/edit-widget-color-control'; + +class WidgetBox extends Component { + render() { + + let colors = EditWidgetColorControl.ColorPalette; + + let colorStyle = { + borderColor: colors[this.props.widget.border_color] + } + + return ( +
+
+ { } +
+
+ ); + } +} + +export default WidgetBox; diff --git a/src/components/widget-factory.js b/src/components/widget-factory.js index 6414180..59c49d6 100644 --- a/src/components/widget-factory.js +++ b/src/components/widget-factory.js @@ -101,6 +101,14 @@ class WidgetFactory { widget.width = 200; widget.height = 150; break; + case 'Box': + widget.minWidth = 50; + widget.minHeight = 50; + widget.width = 100; + widget.height = 100; + widget.border_color = 0; + widget.z = 0; + break; default: widget.width = 100; widget.height = 100; diff --git a/src/containers/visualization.js b/src/containers/visualization.js index 7766c05..8490f95 100644 --- a/src/containers/visualization.js +++ b/src/containers/visualization.js @@ -349,6 +349,7 @@ class Visualization extends Component { + } diff --git a/src/containers/widget.js b/src/containers/widget.js index 89323b1..b72311c 100644 --- a/src/containers/widget.js +++ b/src/containers/widget.js @@ -27,6 +27,7 @@ import WidgetButton from '../components/widget-button'; import WidgetNumberInput from '../components/widget-number-input'; import WidgetSlider from '../components/widget-slider'; import WidgetGauge from '../components/widget-gauge'; +import WidgetBox from '../components/widget-box'; import '../styles/widgets.css'; @@ -146,6 +147,8 @@ class Widget extends Component { element = this.props.onWidgetStatusChange(w, this.props.index) } /> } else if (widget.type === 'Gauge') { element = + } else if (widget.type === 'Box') { + element = } let widgetClasses = classNames({ diff --git a/src/styles/widgets.css b/src/styles/widgets.css index 394b3d0..19ce5f4 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -441,4 +441,12 @@ div[class*="-widget"] label { .table-widget td, .table-widget th { text-align: center; } -/* End table widget*/ \ No newline at end of file +/* End table widget*/ + +/* Begin box widget */ +.box-widget .border { + width: 100%; + height: 100%; + border: 2px solid; +} +/* End box widget */ \ No newline at end of file From e7660ceace55c5d3c209f3963f4c965b54bec59e Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Tue, 25 Apr 2017 15:41:20 +0200 Subject: [PATCH 24/27] Place new widgets always on top of the existing ones --- src/components/dropzone.js | 13 +++++++++++++ src/components/widget-factory.js | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/dropzone.js b/src/components/dropzone.js index 18815e2..dea4398 100644 --- a/src/components/dropzone.js +++ b/src/components/dropzone.js @@ -19,6 +19,19 @@ const dropzoneTarget = { position.x -= dropzoneRect.left; position.y -= dropzoneRect.top; + // Z-Index is one more the top most children + let foundZ = props.children.reduce( (maxZ, currentChildren) => { + // Is there a simpler way? Is not easy to expose a getter in a Container.create(Component) + let widget = currentChildren.props.data; + if (widget && widget.z) { + if (widget.z > maxZ) { + return widget.z; + } + } + return maxZ; + }, 0); + position.z = foundZ >= 100? foundZ : ++foundZ; + props.onDrop(monitor.getItem(), position); } }; diff --git a/src/components/widget-factory.js b/src/components/widget-factory.js index 59c49d6..364562a 100644 --- a/src/components/widget-factory.js +++ b/src/components/widget-factory.js @@ -21,7 +21,7 @@ class WidgetFactory { height: 100, x: position.x, y: position.y, - z: 0 + z: position.z }; // set type specific properties From 6e130accc15aecb2ae9e456d0ae4b55c52f907b0 Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Tue, 25 Apr 2017 15:47:30 +0200 Subject: [PATCH 25/27] center number input widget vertically --- src/styles/widgets.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/styles/widgets.css b/src/styles/widgets.css index 19ce5f4..8745a2c 100644 --- a/src/styles/widgets.css +++ b/src/styles/widgets.css @@ -324,6 +324,12 @@ div[class*="-widget"] label { cursor: inherit; } +.number-input-widget { + display: flex; + flex-direction: column; + justify-content: center; +} + .number-input-widget .form-horizontal .form-group { margin: 0px; } From 2340850b40667a21c3f7a474457627bcc6ecbdfc Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Fri, 5 May 2017 09:07:22 +0200 Subject: [PATCH 26/27] adapt layout to previous changes --- src/styles/app.css | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/styles/app.css b/src/styles/app.css index 4029992..41b2bbb 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -57,6 +57,7 @@ body { .app-footer { width: 100%; height: 60px; + /* Float below body */ float: right; padding-top: 20px; @@ -68,24 +69,21 @@ body { .app-body { /* Let sidebar grow and content occupy rest of the space */ display: flex; - position: absolute; - top: 60px; - bottom: 60px; - right: 0px; - left: 0px; + float: right; + width: 100%; + /* Fit between header and footer */ + min-height: calc(100vh - 140px); padding: 15px 5px 0px 5px; } -.app-body div { +.app-body > div { margin-left: 7px; margin-right: 7px; } .app-content { flex: 1 1 auto; - min-height: 400px; - height: 100%; padding: 15px 20px; background-color: #fff; From fa09f9eef75547fe09630ca092b7c79bed5e1d4d Mon Sep 17 00:00:00 2001 From: Ricardo Hernandez-Montoya Date: Fri, 5 May 2017 09:08:43 +0200 Subject: [PATCH 27/27] removed commented rules --- src/styles/app.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/app.css b/src/styles/app.css index 41b2bbb..33b3280 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -114,7 +114,6 @@ body { .active { font-weight: bold; - /*text-decoration:none;*/ } .menu-sidebar ul {