mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
Merge branch 'develop' into feature_exportfunctions
This commit is contained in:
commit
b560a5f54d
26 changed files with 1868 additions and 1144 deletions
2573
package-lock.json
generated
2573
package-lock.json
generated
File diff suppressed because it is too large
Load diff
33
package.json
33
package.json
|
@ -3,9 +3,9 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.27",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.12.1",
|
||||
"@fortawesome/react-fontawesome": "^0.1.8",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.28",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.13.0",
|
||||
"@fortawesome/react-fontawesome": "^0.1.9",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"bootstrap": "^4.4.1",
|
||||
"classnames": "^2.2.6",
|
||||
|
@ -20,33 +20,34 @@
|
|||
"file-saver": "^2.0.2",
|
||||
"flux": "^3.1.3",
|
||||
"gaugeJS": "^1.3.7",
|
||||
"handlebars": "^4.7.3",
|
||||
"jquery": "^3.4.1",
|
||||
"jszip": "^3.2.2",
|
||||
"handlebars": "^4.7.6",
|
||||
"jquery": "^3.5.0",
|
||||
"jszip": "^3.4.0",
|
||||
"libcimsvg": "git+https://git.rwth-aachen.de/acs/public/cim/pintura-npm-package.git",
|
||||
"lodash": "^4.17.15",
|
||||
"node-sass": "^4.13.1",
|
||||
"node-sass": "^4.14.0",
|
||||
"popper.js": "^1.16.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"rc-slider": "^9.2.1",
|
||||
"react": "^16.12.0",
|
||||
"react-bootstrap": "^1.0.0-beta.16",
|
||||
"rc-slider": "^9.2.4",
|
||||
"react": "^16.13.1",
|
||||
"react-bootstrap": "^1.0.1",
|
||||
"react-contexify": "^4.1.1",
|
||||
"react-d3": "^0.4.0",
|
||||
"react-dnd": "^10.0.2",
|
||||
"react-dnd-html5-backend": "^10.0.2",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-fullscreenable": "^2.5.1-0",
|
||||
"react-grid-system": "^6.2.1",
|
||||
"react-grid-system": "^6.3.1",
|
||||
"react-json-view": "^1.19.1",
|
||||
"react-notification-system": "^0.3.0",
|
||||
"react-rnd": "^10.1.6",
|
||||
"react-rnd": "^10.1.9",
|
||||
"react-router": "^5.1.2",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-scripts": "^3.4.0",
|
||||
"react-scripts": "^3.4.1",
|
||||
"react-svg-pan-zoom": "^3.8.0",
|
||||
"sass": "^1.25.0",
|
||||
"sass": "^1.26.5",
|
||||
"superagent": "^5.2.2",
|
||||
"typescript": "^3.7.5",
|
||||
"typescript": "^3.8.3",
|
||||
"validator": "^12.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -16,19 +16,19 @@
|
|||
******************************************************************************/
|
||||
|
||||
class WebsocketAPI {
|
||||
constructor(endpoint, callbacks) {
|
||||
this.endpoint = endpoint;
|
||||
constructor(host, callbacks) {
|
||||
this.host = host;
|
||||
this.callbacks = callbacks;
|
||||
|
||||
this.wasConnected = false;
|
||||
this.isClosing = false;
|
||||
|
||||
this.connect(endpoint, callbacks);
|
||||
this.connect(host, callbacks);
|
||||
}
|
||||
|
||||
connect(endpoint, callbacks) {
|
||||
connect(host, callbacks) {
|
||||
// create web socket client
|
||||
this.socket = new WebSocket(WebsocketAPI.getURL(endpoint), 'live');
|
||||
this.socket = new WebSocket(WebsocketAPI.getURL(host), 'live');
|
||||
this.socket.binaryType = 'arraybuffer';
|
||||
this.socket.onclose = this.onClose;
|
||||
this.socket.onopen = this.onOpen;
|
||||
|
@ -40,12 +40,12 @@ class WebsocketAPI {
|
|||
}
|
||||
|
||||
reconnect() {
|
||||
//console.log("Reconnecting: " + this.endpoint);
|
||||
this.connect(this.endpoint, this.callbacks);
|
||||
//console.log("Reconnecting: " + this.host);
|
||||
this.connect(this.host, this.callbacks);
|
||||
}
|
||||
|
||||
get url() {
|
||||
return WebsocketAPI.getURL(this.endpoint);
|
||||
return WebsocketAPI.getURL(this.host);
|
||||
}
|
||||
|
||||
send(data) {
|
||||
|
@ -58,7 +58,7 @@ class WebsocketAPI {
|
|||
}
|
||||
|
||||
onError = e => {
|
||||
console.error('Error on WebSocket connection to: ' + this.endpoint + ':', e);
|
||||
console.error('Error on WebSocket connection to: ' + this.host + ':', e);
|
||||
|
||||
if ('onError' in this.callbacks)
|
||||
this.callbacks.onError(e);
|
||||
|
@ -78,16 +78,16 @@ class WebsocketAPI {
|
|||
}
|
||||
else {
|
||||
if (this.wasConnected) {
|
||||
console.log("Connection to " + this.endpoint + " dropped. Attempt reconnect in 1 sec");
|
||||
console.log("Connection to " + this.host + " dropped. Attempt reconnect in 1 sec");
|
||||
window.setTimeout(() => { this.reconnect(); }, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static getURL(endpoint) {
|
||||
static getURL(host) {
|
||||
// create an anchor element (note: no need to append this element to the document)
|
||||
var link = document.createElement('a');
|
||||
link.href = endpoint;
|
||||
link.href = host;
|
||||
|
||||
if (link.protocol === 'https:')
|
||||
link.protocol = 'wss:';
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
import React from 'react';
|
||||
import {FormGroup, FormControl, FormLabel} from 'react-bootstrap';
|
||||
import _ from 'lodash';
|
||||
|
||||
import Dialog from '../common/dialogs/dialog';
|
||||
import ParametersEditor from '../common/parameters-editor';
|
||||
import SelectFile from "../file/select-file";
|
||||
|
@ -104,7 +102,7 @@ class EditConfigDialog extends React.Component {
|
|||
|
||||
render() {
|
||||
const ICOptions = this.props.ics.map(s =>
|
||||
<option key={s.id} value={s.id}>{_.get(s, 'properties.name') || _.get(s, 'rawProperties.name') || s.uuid}</option>
|
||||
<option key={s.id} value={s.id}>{s.name}</option>
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -86,7 +86,7 @@ class Dashboard extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO create list of infrastructure components in use
|
||||
|
||||
return {
|
||||
dashboard,
|
||||
|
@ -110,7 +110,6 @@ class Dashboard extends Component {
|
|||
|
||||
}
|
||||
|
||||
|
||||
static getNewWidgetKey() {
|
||||
const widgetKey = this.lastWidgetKey;
|
||||
this.lastWidgetKey++;
|
||||
|
@ -128,7 +127,9 @@ class Dashboard extends Component {
|
|||
param: '?dashboardID=' + this.state.dashboard.id
|
||||
});
|
||||
|
||||
// TODO open websockets in componentDidMount
|
||||
|
||||
// TODO close websockets in componentWillUnmount
|
||||
}
|
||||
|
||||
handleKeydown(e) {
|
||||
|
@ -185,17 +186,6 @@ class Dashboard extends Component {
|
|||
});
|
||||
|
||||
|
||||
/*let widgets = [];
|
||||
widgets = this.state.dashboard.get('widgets');
|
||||
|
||||
const widgetKey = Dashboard.getNewWidgetKey();
|
||||
widgets[widgetKey] = widget;
|
||||
|
||||
const dashboard = this.state.dashboard.set('widgets',widgets);
|
||||
|
||||
// this.increaseHeightWithWidget(widget);
|
||||
|
||||
this.setState({ dashboard });*/
|
||||
};
|
||||
|
||||
|
||||
|
@ -205,11 +195,9 @@ class Dashboard extends Component {
|
|||
}
|
||||
|
||||
widgetChange(widget, index, callback = null){
|
||||
|
||||
let tempChanges = this.state.widgetChangeData;
|
||||
tempChanges.push(widget);
|
||||
|
||||
this.setState({ widgetChangeData: tempChanges})
|
||||
let temp = this.state.widgetChangeData;
|
||||
temp.push(widget);
|
||||
this.setState({widgetChangeData: temp});
|
||||
|
||||
}
|
||||
|
||||
|
@ -264,7 +252,7 @@ class Dashboard extends Component {
|
|||
|
||||
|
||||
deleteWidget(widget, index) {
|
||||
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-remove',
|
||||
data: widget,
|
||||
|
@ -293,7 +281,7 @@ class Dashboard extends Component {
|
|||
data: widget
|
||||
});
|
||||
});
|
||||
this.setState({ editing: false });
|
||||
this.setState({ editing: false, widgetChangeData: [], widgetAddData: [] });
|
||||
};
|
||||
|
||||
saveChanges() {
|
||||
|
@ -311,21 +299,28 @@ class Dashboard extends Component {
|
|||
|
||||
cancelEditing() {
|
||||
//raw widget has no id -> cannot be deleted in its original form
|
||||
/*
|
||||
this.state.widgetAddData.forEach( widget => {
|
||||
let temp = [];
|
||||
this.state.widgetAddData.forEach(rawWidget => {
|
||||
this.state.widgets.forEach(widget => {
|
||||
if(widget.y === rawWidget.y && widget.x === rawWidget.x && widget.type === rawWidget.type){
|
||||
temp.push(widget);
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
temp.forEach( widget => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-remove',
|
||||
data: widget,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
}); */
|
||||
|
||||
});
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-load',
|
||||
token: this.state.sessionToken,
|
||||
param: '?dashboardID=1'
|
||||
});
|
||||
this.setState({ editing: false, widgetChangeData: [], widgetAddData: [] });
|
||||
this.setState({ editing: false, widgetChangeData: [], widgetAddData: []});
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -26,19 +26,19 @@ class IcDataDataManager {
|
|||
this._sockets = {};
|
||||
}
|
||||
|
||||
open(endpoint, identifier) {
|
||||
open(host, identifier) {
|
||||
// pass signals to onOpen callback
|
||||
if (this._sockets[identifier] != null)
|
||||
return; // already open?
|
||||
|
||||
this._sockets[identifier] = new WebsocketAPI(endpoint, { onOpen: (event) => this.onOpen(event, identifier, true), onClose: (event) => this.onClose(event, identifier), onMessage: (event) => this.onMessage(event, identifier) });
|
||||
this._sockets[identifier] = new WebsocketAPI(host, { onOpen: (event) => this.onOpen(event, identifier, true), onClose: (event) => this.onClose(event, identifier), onMessage: (event) => this.onMessage(event, identifier) });
|
||||
}
|
||||
|
||||
update(endpoint, identifier) {
|
||||
update(host, identifier) {
|
||||
if (this._sockets[identifier] != null) {
|
||||
if (this._sockets[identifier].endpoint !== endpoint) {
|
||||
if (this._sockets[identifier].host !== host) {
|
||||
this._sockets[identifier].close();
|
||||
this._sockets[identifier] = new WebsocketAPI(endpoint, { onOpen: (event) => this.onOpen(event, identifier, false), onClose: (event) => this.onClose(event, identifier), onMessage: (event) => this.onMessage(event, identifier), onError: (error) => this.onError(error, identifier) });
|
||||
this._sockets[identifier] = new WebsocketAPI(host, { onOpen: (event) => this.onOpen(event, identifier, false), onClose: (event) => this.onClose(event, identifier), onMessage: (event) => this.onMessage(event, identifier), onError: (error) => this.onError(error, identifier) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,11 +15,10 @@
|
|||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import ArrayStore from '../common/array-store';
|
||||
import ICsDataManager from './ics-data-manager';
|
||||
import ICDataDataManager from './ic-data-data-manager';
|
||||
import NotificationsDataManager from "../common/data-managers/notifications-data-manager";
|
||||
|
||||
class InfrastructureComponentStore extends ArrayStore {
|
||||
constructor() {
|
||||
|
@ -29,29 +28,44 @@ class InfrastructureComponentStore extends ArrayStore {
|
|||
reduce(state, action) {
|
||||
switch(action.type) {
|
||||
case 'ics/loaded':
|
||||
// connect to each infrastructure component
|
||||
for (let ic of action.data) {
|
||||
const endpoint = _.get(ic, 'properties.endpoint') || _.get(ic, 'rawProperties.endpoint');
|
||||
if (endpoint != null && endpoint !== '') {
|
||||
ICDataDataManager.open(endpoint, ic.id);
|
||||
} else {
|
||||
// console.warn('Endpoint not found for IC at ' + endpoint);
|
||||
// console.log(ic);
|
||||
}
|
||||
}
|
||||
|
||||
return super.reduce(state, action);
|
||||
|
||||
case 'ics/edited':
|
||||
// connect to each infrastructure component
|
||||
const ic = action.data;
|
||||
const endpoint = _.get(ic, 'properties.endpoint') || _.get(ic, 'rawProperties.endpoint');
|
||||
|
||||
if (endpoint != null && endpoint !== '') {
|
||||
ICDataDataManager.update(endpoint, ic.id);
|
||||
if (ic.host != null && ic.host !== '') {
|
||||
ICDataDataManager.update(ic.host, ic.id);
|
||||
}
|
||||
|
||||
return super.reduce(state, action);
|
||||
case 'ics/open-sockets':
|
||||
// open websocket for each IC contained in array action.data
|
||||
// TODO should be done when dashboard is loaded
|
||||
// TODO action.data should contain only those IC used by the scenario
|
||||
for (let ic of action.data) {
|
||||
if (ic.host != null && ic.host !== '') {
|
||||
// TODO connection should be closed again when dashboard is closed
|
||||
|
||||
ICDataDataManager.open(ic.host, ic.id);
|
||||
} else {
|
||||
|
||||
// TODO add to pool of notifications
|
||||
const IC_WEBSOCKET_HOST_ERROR = {
|
||||
title: 'Host of websocket not available',
|
||||
message: action.error.response.body.message,
|
||||
level: 'warning'
|
||||
};
|
||||
NotificationsDataManager.addNotification(IC_WEBSOCKET_HOST_ERROR);
|
||||
}
|
||||
}
|
||||
return super.reduce(state, action);
|
||||
|
||||
case 'ics/close-sockets':
|
||||
// close all websockets
|
||||
ICDataDataManager.closeAll();
|
||||
return super.reduce(state, action);
|
||||
|
||||
case 'ics/fetched':
|
||||
return this.updateElements(state, [action.data]);
|
||||
|
|
|
@ -30,7 +30,7 @@ class ImportICDialog extends React.Component {
|
|||
|
||||
this.state = {
|
||||
name: '',
|
||||
endpoint: '',
|
||||
host: '',
|
||||
uuid: ''
|
||||
};
|
||||
}
|
||||
|
@ -45,8 +45,8 @@ class ImportICDialog extends React.Component {
|
|||
uuid: this.state.uuid
|
||||
};
|
||||
|
||||
if (this.state.endpoint != null && this.state.endpoint !== "" && this.state.endpoint !== 'http://') {
|
||||
data.properties.endpoint = this.state.endpoint;
|
||||
if (this.state.host != null && this.state.host !== "" && this.state.host !== 'http://') {
|
||||
data.host = this.state.host;
|
||||
}
|
||||
|
||||
this.props.onClose(data);
|
||||
|
@ -61,7 +61,7 @@ class ImportICDialog extends React.Component {
|
|||
}
|
||||
|
||||
resetState() {
|
||||
this.setState({ name: '', endpoint: 'http://', uuid: '' });
|
||||
this.setState({ name: '', host: 'http://', uuid: '' });
|
||||
}
|
||||
|
||||
loadFile(fileList) {
|
||||
|
@ -81,7 +81,7 @@ class ImportICDialog extends React.Component {
|
|||
self.imported = true;
|
||||
self.setState({
|
||||
name: _.get(ic, 'properties.name') || _.get(ic, 'rawProperties.name'),
|
||||
endpoint: _.get(ic, 'properties.endpoint') || _.get(ic, 'rawProperties.endpoint'),
|
||||
host: _.get(ic, 'host'),
|
||||
uuid: ic.uuid
|
||||
});
|
||||
};
|
||||
|
@ -124,8 +124,8 @@ class ImportICDialog extends React.Component {
|
|||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="host">
|
||||
<FormLabel>Endpoint</FormLabel>
|
||||
<FormControl type="text" placeholder="Enter host" value={this.state.endpoint} onChange={(e) => this.handleChange(e)} />
|
||||
<FormLabel>Host</FormLabel>
|
||||
<FormControl type="text" placeholder="Enter host" value={this.state.host} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
<FormGroup controlId="uuid" validationState={this.validateForm('uuid')}>
|
||||
|
|
|
@ -70,15 +70,28 @@ class Scenario extends React.Component {
|
|||
let signals = SignalStore.getState();
|
||||
let files = FileStore.getState();
|
||||
|
||||
// apply filter to contain only ICs that are used by configs
|
||||
let icsUsed = ICStore.getState().filter(ic => {
|
||||
let ICused = false;
|
||||
for (let config of configs){
|
||||
if (ic.id === config.icID){
|
||||
ICused = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ICused;
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
scenario,
|
||||
sessionToken,
|
||||
configs: configs,
|
||||
configs,
|
||||
dashboards,
|
||||
signals,
|
||||
files,
|
||||
ics: ICStore.getState(),
|
||||
icsUsed,
|
||||
|
||||
deleteConfigModal: false,
|
||||
importConfigModal: false,
|
||||
|
@ -119,7 +132,7 @@ class Scenario extends React.Component {
|
|||
param: '?scenarioID='+this.state.scenario.id
|
||||
});
|
||||
|
||||
// load ICs to enable that component configs work with them
|
||||
// load ICs to enable that component configs and dashboards work with them
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/start-load',
|
||||
token: this.state.sessionToken
|
||||
|
|
|
@ -249,6 +249,7 @@ span.signal-unit::after {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: none;
|
||||
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
|
|
@ -28,20 +28,20 @@ const dropzoneTarget = {
|
|||
position.y -= dropzoneRect.top;
|
||||
|
||||
// Z-Index is one more the top most children
|
||||
let foundZ = props.children.reduce( (maxZ, currentChildren) => {
|
||||
if (currentChildren.props != null) {
|
||||
let foundZ = props.widgets.reduce( (maxZ, currentWidget) => {
|
||||
if (currentWidget != null) {
|
||||
// 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;
|
||||
if (currentWidget.z > maxZ) {
|
||||
return currentWidget.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return maxZ;
|
||||
}, 0);
|
||||
position.z = foundZ >= 100? foundZ : ++foundZ;
|
||||
}, 0)
|
||||
position.z = foundZ >= 100? foundZ : foundZ += 10;
|
||||
if(monitor.getItem().name === "Box"){
|
||||
position.z = 0;
|
||||
}
|
||||
|
||||
props.onDrop(monitor.getItem(), position);
|
||||
}
|
||||
|
|
|
@ -129,7 +129,8 @@ export default function CreateControls(widgetType = null, widget = null, session
|
|||
case 'Box':
|
||||
DialogControls.push(
|
||||
<EditWidgetColorControl key={0} widget={widget} controlId={'customProperties.border_color'} label={'Border color'} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetColorControl key={1} widget={widget} controlId={'customProperties.background_color'} label={'Background color'} handleChange={e => handleChange(e)} />
|
||||
<EditWidgetColorControl key={1} widget={widget} controlId={'customProperties.background_color'} label={'Background color'} handleChange={e => handleChange(e)} />,
|
||||
<EditWidgetNumberControl key={2} widget={widget} controlId={'customProperties.background_color_opacity'} label={'Background opacity'} defaultValue={0.5} handleChange={(e) => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Label':
|
||||
|
@ -155,7 +156,8 @@ export default function CreateControls(widgetType = null, widget = null, session
|
|||
case 'NumberInput':
|
||||
DialogControls.push(
|
||||
<EditWidgetTextControl key={0} widget={widget} controlId={'name'} label={'Text'} placeholder={'Enter text'} handleChange={e => handleChange(e)} />,
|
||||
<EditWidgetSignalControl key={2} widget={widget} controlId={'signalIDs'} input signals={signals} handleChange={(e) => handleChange(e)} />
|
||||
<EditWidgetSignalControl key={2} widget={widget} controlId={'signalIDs'} input signals={signals} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetCheckboxControl key={1} widget={widget} controlId={'customProperties.showUnit'} input text="Show unit" handleChange={e => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
|
||||
|
|
|
@ -25,21 +25,25 @@ class EditWidgetNumberControl extends Component {
|
|||
this.state = {
|
||||
widget: {
|
||||
customProperties:{}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
static getDerivedStateFromProps(props, state){
|
||||
return{
|
||||
widget: props.widget
|
||||
};
|
||||
}
|
||||
widget: props.widget
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
render() {
|
||||
let step = 1;
|
||||
if(this.props.controlId ==='customProperties.background_color_opacity'){
|
||||
step = 0.1;
|
||||
}
|
||||
return (
|
||||
<FormGroup controlId={this.props.controlId}>
|
||||
<FormLabel>{this.props.label}</FormLabel>
|
||||
<FormControl type="number" step="any" value={this.state.widget[this.props.controlId]} onChange={e => this.props.handleChange(e)} />
|
||||
<FormControl type="number" step={step} value={this.state.widget[this.props.controlId]} onChange={e => this.props.handleChange(e)} />
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -38,9 +38,15 @@ class EditWidgetOrientation extends Component {
|
|||
}
|
||||
|
||||
handleOrientationChange(orientation) {
|
||||
console.log("handle OrinetationChange was called. the orientation: " + orientation);
|
||||
|
||||
this.props.handleChange({ target: { id: 'customProperties.orientation', value: orientation } });
|
||||
this.handleNewDimensions(this.state.widget.width,this.state.widget.height,this.state.widget.minWidth,this.state.widget.minHeight);
|
||||
}
|
||||
|
||||
handleNewDimensions(width,height,minWidth,minHeight){
|
||||
this.props.handleChange({ target: { id: 'height', value: width } });
|
||||
this.props.handleChange({ target: { id: 'width', value: height } });
|
||||
this.props.handleChange({ target: { id: 'minHeight', value: minWidth } });
|
||||
this.props.handleChange({ target: { id: 'minWidth', value: minHeight } });
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -57,7 +63,6 @@ class EditWidgetOrientation extends Component {
|
|||
Object.keys(WidgetSlider.OrientationTypes).map( (type) => {
|
||||
let value = WidgetSlider.OrientationTypes[type].value;
|
||||
let name = WidgetSlider.OrientationTypes[type].name;
|
||||
console.log("value: " + value + " name: " + name)
|
||||
|
||||
return (
|
||||
<FormCheck inline label={name} key={value} id={value} type='radio' title='orientation' checked={ value === this.state.widget.customProperties.orientation } onChange={(e) => this.handleOrientationChange(value)}>
|
||||
|
|
|
@ -57,12 +57,36 @@ class EditWidgetDialog extends React.Component {
|
|||
const file = this.props.files.find(element => element.id === fileId);
|
||||
|
||||
// scale width to match aspect
|
||||
if(file.dimensions){
|
||||
const aspectRatio = file.dimensions.width / file.dimensions.height;
|
||||
changeObject.width = this.state.temporal.height * aspectRatio;
|
||||
}
|
||||
|
||||
return changeObject;
|
||||
}
|
||||
|
||||
setMaxWidth(changeObject){
|
||||
if(changeObject.type === 'Label'){
|
||||
changeObject.customProperties.maxWidth = (changeObject.customProperties.textSize* 0.34) * changeObject.name.length;
|
||||
}
|
||||
else if (changeObject.type === 'Value'){
|
||||
// changeObject.customProperties.maxWidth = (changeObject.customProperties.textSize* 0.5) * (changeObject.name.length+13);
|
||||
}
|
||||
return changeObject;
|
||||
}
|
||||
|
||||
setNewLockRestrictions(changeObject){
|
||||
if(changeObject.customProperties.orientation === 0){
|
||||
changeObject.customProperties.resizeTopBottomLock = true;
|
||||
changeObject.customProperties.resizeRightLeftLock = false;
|
||||
}
|
||||
else if(changeObject.customProperties.orientation === 1){
|
||||
changeObject.customProperties.resizeTopBottomLock = false;
|
||||
changeObject.customProperties.resizeRightLeftLock = true;
|
||||
}
|
||||
return changeObject;
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
|
||||
// TODO: check what we really need in this function. Can we reduce its complexity?
|
||||
|
@ -73,13 +97,13 @@ class EditWidgetDialog extends React.Component {
|
|||
// not a customProperty
|
||||
customProperty = false;
|
||||
}
|
||||
|
||||
if (e.target.id === 'lockAspect') {
|
||||
|
||||
if (parts[1] === 'lockAspect') {
|
||||
//not a customProperty
|
||||
customProperty ? changeObject[parts[0]][parts[1]] = e.target.checked : changeObject[e.target.id] = e.target.checked;
|
||||
|
||||
// correct image aspect if turned on
|
||||
if (e.target.checked) {
|
||||
if (e.target.checked && this.state.temporal.customProperties.file) {
|
||||
changeObject = this.assignAspectRatio(changeObject, this.state.temporal.customProperties.file);
|
||||
}
|
||||
} else if (e.target.id.includes('file')) {
|
||||
|
@ -91,7 +115,15 @@ class EditWidgetDialog extends React.Component {
|
|||
// TODO this if condition requires changes to work!!!
|
||||
changeObject = this.assignAspectRatio(changeObject, e.target.value);
|
||||
}
|
||||
} else if (e.target.type === 'number') {
|
||||
}else if (parts[1] === 'textSize'){
|
||||
changeObject[parts[0]][parts[1]] = Number(e.target.value);
|
||||
changeObject = this.setMaxWidth(changeObject);
|
||||
|
||||
}else if(parts[1] === 'orientation'){
|
||||
customProperty ? changeObject[parts[0]][parts[1]] = e.target.value : changeObject[e.target.id] = e.target.value ;
|
||||
changeObject = this.setNewLockRestrictions(changeObject);
|
||||
}
|
||||
else if (e.target.type === 'number') {
|
||||
customProperty ? changeObject[parts[0]][parts[1]] = Number(e.target.value) : changeObject[e.target.id] = Number(e.target.value);
|
||||
} else if(e.target.id === 'name'){
|
||||
if(customProperty ? (changeObject[parts[0]][parts[1]] != null) : (changeObject[e.target.id] != null)){
|
||||
|
@ -99,6 +131,7 @@ class EditWidgetDialog extends React.Component {
|
|||
} else{
|
||||
customProperty ? changeObject[parts[0]][parts[1]]= 'default' : changeObject[e.target.id] = 'default';
|
||||
}
|
||||
changeObject = this.setMaxWidth(changeObject);
|
||||
} else {
|
||||
customProperty ? changeObject[parts[0]][parts[1]] = e.target.value : changeObject[e.target.id] = e.target.value ;
|
||||
}
|
||||
|
|
|
@ -84,16 +84,21 @@ class EditableWidgetContainer extends React.Component {
|
|||
|
||||
render() {
|
||||
const widget = this.props.widget;
|
||||
let resizingRestricted = false;
|
||||
if(widget.customProperties.resizeRightLeftLock || widget.customProperties.resizeTopBottomLock){
|
||||
resizingRestricted = true;
|
||||
}
|
||||
|
||||
|
||||
const resizing = {
|
||||
bottom: !widget.isLocked,
|
||||
bottomLeft: !widget.isLocked,
|
||||
bottomRight: !widget.isLocked,
|
||||
left: !widget.isLocked,
|
||||
right: !widget.idLocked,
|
||||
top: !widget.isLocked,
|
||||
topLeft: !widget.isLocked,
|
||||
topRight: !widget.isLocked
|
||||
bottom: !(widget.customProperties.resizeTopBottomLock || widget.isLocked),
|
||||
bottomLeft: !(resizingRestricted|| widget.isLocked),
|
||||
bottomRight: !(resizingRestricted || widget.isLocked),
|
||||
left: !(widget.customProperties.resizeRightLeftLock || widget.isLocked),
|
||||
right: !(widget.customProperties.resizeRightLeftLock || widget.isLocked),
|
||||
top: !(widget.customProperties.resizeTopBottomLock || widget.isLocked),
|
||||
topLeft: !(resizingRestricted || widget.isLocked),
|
||||
topRight: !(resizingRestricted || widget.isLocked)
|
||||
};
|
||||
|
||||
const gridArray = [ this.props.grid, this.props.grid ];
|
||||
|
@ -108,6 +113,7 @@ class EditableWidgetContainer extends React.Component {
|
|||
default={{ x: Number(widget.x), y: Number(widget.y), width: widget.width, height: widget.height }}
|
||||
minWidth={widget.minWidth}
|
||||
minHeight={widget.minHeight}
|
||||
maxWidth ={widget.customProperties.maxWidth || '100%' }
|
||||
lockAspectRatio={Boolean(widget.isLocked)}
|
||||
bounds={'parent'}
|
||||
className={widgetClasses}
|
||||
|
|
|
@ -50,7 +50,7 @@ class WidgetArea extends React.Component {
|
|||
return absolutHeight > currentHeight ? absolutHeight : currentHeight;
|
||||
}, 0);
|
||||
|
||||
return <Dropzone height={maxHeight + 80} onDrop={this.handleDrop} editing={this.props.editing}>
|
||||
return <Dropzone height={maxHeight + 80} onDrop={this.handleDrop} editing={this.props.editing} widgets={this.props.widgets}>
|
||||
{this.props.children}
|
||||
|
||||
<Grid size={this.props.grid} disabled={this.props.grid === 1 || this.props.editing !== true} />
|
||||
|
|
|
@ -26,7 +26,7 @@ class WidgetContainer extends React.Component {
|
|||
height: Number(this.props.widget.height),
|
||||
left: Number(this.props.widget.x),
|
||||
top: Number(this.props.widget.y),
|
||||
zindex: Number(this.props.widget.z),
|
||||
zIndex: Number(this.props.widget.z),
|
||||
position: 'absolute'
|
||||
};
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class WidgetContextMenu extends React.Component {
|
|||
};
|
||||
|
||||
moveAbove = event => {
|
||||
this.props.widget.z++;
|
||||
this.props.widget.z += 10;
|
||||
if (this.props.widget.z > 100) {
|
||||
this.props.widget.z = 100;
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ class WidgetContextMenu extends React.Component {
|
|||
};
|
||||
|
||||
moveUnderneath = event => {
|
||||
this.props.widget.z--;
|
||||
this.props.widget.z -= 10;
|
||||
if (this.props.widget.z < 0) {
|
||||
this.props.widget.z = 0;
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ class WidgetContextMenu extends React.Component {
|
|||
render() {
|
||||
const isLocked = this.props.widget.locked;
|
||||
const ContextMenu = () => (
|
||||
<Menu id={'widgetMenu'+ this.props.index}>
|
||||
<Menu id={'widgetMenu'+ this.props.index} style={{zIndex: 1000}}>
|
||||
<Item disabled={isLocked} onClick={this.editWidget}>Edit</Item>
|
||||
<Item disabled={isLocked} onClick={this.deleteWidget}>Delete</Item>
|
||||
|
||||
|
|
|
@ -70,11 +70,12 @@ class WidgetFactory {
|
|||
case 'Value':
|
||||
widget.minWidth = 70;
|
||||
widget.minHeight = 20;
|
||||
widget.width = 120;
|
||||
widget.width = 150;
|
||||
widget.height = 30;
|
||||
widget.customProperties.textSize = 16;
|
||||
widget.name = 'Value';
|
||||
widget.customProperties.showUnit = false;
|
||||
widget.customProperties.resizeTopBottomLock = true;
|
||||
break;
|
||||
case 'Plot':
|
||||
widget.customProperties.ylabel = '';
|
||||
|
@ -96,11 +97,13 @@ class WidgetFactory {
|
|||
case 'Label':
|
||||
widget.minWidth = 20;
|
||||
widget.minHeight = 20;
|
||||
widget.customProperties.maxWidth = 100;
|
||||
widget.width = 100;
|
||||
widget.height = 35;
|
||||
widget.name = 'Label';
|
||||
widget.customProperties.textSize = 32;
|
||||
widget.customProperties.fontColor = 0;
|
||||
widget.customProperties.resizeTopBottomLock = true;
|
||||
break;
|
||||
case 'PlotTable':
|
||||
widget.customProperties.ylabel = '';
|
||||
|
@ -133,10 +136,12 @@ class WidgetFactory {
|
|||
widget.customProperties.toggle = false;
|
||||
break;
|
||||
case 'NumberInput':
|
||||
widget.minWidth = 200;
|
||||
widget.minWidth = 150;
|
||||
widget.minHeight = 50;
|
||||
widget.width = 200;
|
||||
widget.height = 50;
|
||||
widget.customProperties.showUnit = false;
|
||||
widget.customProperties.resizeTopBottomLock = true;
|
||||
break;
|
||||
case 'Slider':
|
||||
widget.minWidth = 380;
|
||||
|
@ -150,6 +155,8 @@ class WidgetFactory {
|
|||
widget.customProperties.showUnit = true;
|
||||
widget.customProperties.continous_update = false;
|
||||
widget.customProperties.default_value = 0;
|
||||
widget.customProperties.resizeLeftRightLock = false;
|
||||
widget.customProperties.resizeTopBottomLock = true;
|
||||
|
||||
break;
|
||||
case 'Gauge':
|
||||
|
@ -169,6 +176,7 @@ class WidgetFactory {
|
|||
widget.width = 100;
|
||||
widget.height = 100;
|
||||
widget.customProperties.border_color = 0;
|
||||
widget.customProperties.background_color_opacity = 0.5;
|
||||
widget.z = 0;
|
||||
break;
|
||||
case 'HTML':
|
||||
|
|
|
@ -27,7 +27,7 @@ class WidgetBox extends Component {
|
|||
let colorStyle = {
|
||||
borderColor: colors[this.props.widget.customProperties.border_color],
|
||||
backgroundColor: colors[this.props.widget.customProperties.background_color],
|
||||
opacity: this.props.widget.customProperties.background_color_opacity
|
||||
opacity: this.props.widget.customProperties.background_color_opacity,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -31,17 +31,20 @@ class WidgetGauge extends Component {
|
|||
unit: '',
|
||||
minValue: null,
|
||||
maxValue: null,
|
||||
useColorZones: false,
|
||||
useMinMax: false,
|
||||
useMinMaxChange: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.gauge = new Gauge(this.gaugeCanvas).setOptions(this.computeGaugeOptions(this.props.widget));
|
||||
//this.gauge.maxValue = this.state.maxValue;
|
||||
//this.gauge.setMinValue(this.state.minValue);
|
||||
this.gauge.maxValue = this.state.maxValue;
|
||||
this.gauge.setMinValue(this.state.minValue);
|
||||
this.gauge.animationSpeed = 30;
|
||||
//this.gauge.set(this.state.value);
|
||||
this.gauge.set(this.state.value);
|
||||
|
||||
//this.updateLabels(this.state.minValue, this.state.maxValue);
|
||||
this.updateLabels(this.state.minValue, this.state.maxValue);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS): void {
|
||||
|
@ -57,9 +60,19 @@ class WidgetGauge extends Component {
|
|||
this.gauge.set(this.state.value)
|
||||
}
|
||||
|
||||
if(prevState.useMinMax === true && this.state.useMinMax === false){
|
||||
this.setState({useMinMaxChange: true});
|
||||
}
|
||||
|
||||
// update labels
|
||||
if(prevState.minValue !== this.state.minValue || prevState.maxValue !== this.state.maxValue){
|
||||
this.updateLabels(this.state.minValue, this.state.maxValue)
|
||||
if(prevState.minValue !== this.state.minValue || prevState.maxValue !== this.state.maxValue || prevState.useColorZones !== this.state.useColorZones
|
||||
|| prevState.useMinMax !== this.state.useMinMax){
|
||||
this.gauge = new Gauge(this.gaugeCanvas).setOptions(this.computeGaugeOptions(this.props.widget));
|
||||
this.gauge.maxValue = this.state.maxValue;
|
||||
this.gauge.setMinValue(this.state.minValue);
|
||||
this.gauge.animationSpeed = 30;
|
||||
this.gauge.set(this.state.value);
|
||||
this.updateLabels(this.state.minValue, this.state.maxValue)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -69,9 +82,10 @@ class WidgetGauge extends Component {
|
|||
if(props.widget.signalIDs.length === 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
let returnState = {}
|
||||
|
||||
returnState["useColorZones"] = props.widget.customProperties.colorZones;
|
||||
|
||||
// Update unit (assuming there is exactly one signal for this widget)
|
||||
let signalID = props.widget.signalIDs[0];
|
||||
let widgetSignal = props.signals.find(sig => sig.id === signalID);
|
||||
|
@ -105,7 +119,8 @@ class WidgetGauge extends Component {
|
|||
const value = Math.round(signalData[signalData.length - 1].y * 1e3) / 1e3;
|
||||
let minValue = null;
|
||||
let maxValue = null;
|
||||
if (state.value !== value && value != null) {
|
||||
|
||||
if ((state.value !== value && value != null) || props.widget.customProperties.valueUseMinMax || state.useMinMaxChange) {
|
||||
//value has changed
|
||||
updateValue = true;
|
||||
|
||||
|
@ -114,17 +129,18 @@ class WidgetGauge extends Component {
|
|||
|
||||
minValue = state.minValue;
|
||||
maxValue = state.maxValue;
|
||||
|
||||
if (minValue == null) {
|
||||
|
||||
if (minValue == null || state.useMinMaxChange) {
|
||||
minValue = value - 0.5;
|
||||
updateLabels = true;
|
||||
updateMinValue = true;
|
||||
}
|
||||
|
||||
if (maxValue == null) {
|
||||
if (maxValue == null || state.useMinMaxChange) {
|
||||
maxValue = value + 0.5;
|
||||
updateLabels = true;
|
||||
updateMaxValue = true;
|
||||
returnState["useMinMaxChange"] = false;
|
||||
}
|
||||
|
||||
if (props.widget.customProperties.valueUseMinMax) {
|
||||
|
@ -141,7 +157,7 @@ class WidgetGauge extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
if (updateLabels === false) {
|
||||
if (updateLabels === false && state.gauge) {
|
||||
// check if min/max changed
|
||||
if (minValue > state.gauge.minValue) {
|
||||
minValue = state.gauge.minValue;
|
||||
|
@ -155,6 +171,13 @@ class WidgetGauge extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
if(props.widget.customProperties.valueUseMinMax !== state.useMinMax){
|
||||
returnState["useMinMax"] = props.widget.customProperties.valueUseMinMax;
|
||||
}
|
||||
if(props.widget.customProperties.colorZones !== state.useColorZones){
|
||||
returnState["useColorZones"] = props.widget.customProperties.colorZones;
|
||||
}
|
||||
|
||||
// prepare returned state
|
||||
if(updateValue === true){
|
||||
returnState["value"] = value;
|
||||
|
|
|
@ -78,15 +78,19 @@ class WidgetInput extends Component {
|
|||
<div className="number-input-widget full">
|
||||
<Form componentclass="fieldset" horizontal="true">
|
||||
<FormGroup>
|
||||
<Col as={FormLabel} xs={3}>
|
||||
<Col as={FormLabel}>
|
||||
{this.props.widget.name}
|
||||
</Col>
|
||||
<Col xs={9}>
|
||||
<Col>
|
||||
<InputGroup>
|
||||
<FormControl type="number" step="any" disabled={ this.props.editing } onKeyPress={ (e) => this.handleKeyPress(e) } onBlur={ (e) => this.valueChanged(this.state.value) } onChange={ (e) => this.valueIsChanging(e.target.value) } placeholder="Enter value" value={ this.state.value } />
|
||||
{this.props.widget.customProperties.showUnit? (
|
||||
<InputGroup.Append>
|
||||
<InputGroup.Text>{this.state.unit}</InputGroup.Text>
|
||||
</InputGroup.Append>
|
||||
):(
|
||||
<div></div>
|
||||
)}
|
||||
</InputGroup>
|
||||
</Col>
|
||||
</FormGroup>
|
||||
|
|
|
@ -57,7 +57,7 @@ class WidgetSlider extends Component {
|
|||
if (props.widget.customProperties.default_value && state.value === undefined) {
|
||||
returnState["value"] = props.widget.customProperties.default_value;
|
||||
}
|
||||
|
||||
|
||||
// Update unit (assuming there is exactly one signal for this widget)
|
||||
let signalID = props.widget.signalIDs[0];
|
||||
let signal = props.signals.find(sig => sig.id === signalID);
|
||||
|
@ -76,6 +76,7 @@ class WidgetSlider extends Component {
|
|||
|
||||
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS): void {
|
||||
// Check if the orientation changed, update the size if it did
|
||||
// this part didn't work -> dimensions and constraints are now handled by the edit orientation component
|
||||
if (this.props.widget.customProperties.orientation !== prevProps.widget.customProperties.orientation) {
|
||||
let baseWidget = this.props.widget;
|
||||
|
||||
|
@ -125,33 +126,14 @@ class WidgetSlider extends Component {
|
|||
});
|
||||
|
||||
return (
|
||||
!isVertical? (
|
||||
<div className={widgetClasses}>
|
||||
<label>{ fields.name }</label>
|
||||
<div className='slider'>{ fields.control }</div>
|
||||
{ fields.control }
|
||||
<span>{ fields.value }</span>
|
||||
{this.props.widget.customProperties.showUnit && fields.unit}
|
||||
</div>
|
||||
) : (
|
||||
<div className={widgetClasses}>
|
||||
<Slider vertical min={ this.props.widget.customProperties.rangeMin } max={ this.props.widget.customProperties.rangeMax } step={ this.props.widget.customProperties.step } value={ this.state.value } disabled={ this.props.editing } onChange={ (v) => this.valueIsChanging(v) } onAfterChange={ (v) => this.valueChanged(v) }/>,
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default WidgetSlider;
|
||||
/*!isVertical? (
|
||||
<div className={widgetClasses}>
|
||||
<label>{ fields.name }</label>
|
||||
<div className='slider'>{ fields.control }</div>
|
||||
<span>{ fields.value }</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className={widgetClasses}>
|
||||
<label>{ fields.name }</label>
|
||||
{ fields.control }
|
||||
{ fields.value }
|
||||
{ this.props.widget.customProperties.showUnit && fields.unit }
|
||||
</div>
|
||||
)*/
|
||||
|
|
|
@ -92,6 +92,14 @@ class WidgetTable extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
|
||||
let rows = this.state.rows;
|
||||
if(rows.length === 0){
|
||||
rows.push({
|
||||
name: "no entries"
|
||||
})
|
||||
}
|
||||
|
||||
var columns = [
|
||||
<TableColumn key={1} title="Signal" dataKey="name" width={120} />,
|
||||
<TableColumn key={2} title="Value" dataKey="value" modifier={format('.4s')} />
|
||||
|
@ -102,7 +110,7 @@ class WidgetTable extends Component {
|
|||
|
||||
return (
|
||||
<div className="table-widget">
|
||||
<Table data={this.state.rows}>
|
||||
<Table data={rows}>
|
||||
{ columns }
|
||||
</Table>
|
||||
</div>
|
||||
|
|
|
@ -68,14 +68,16 @@ class WidgetValue extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
var value_to_render = Number(this.state.value);
|
||||
let value_to_render = Number(this.state.value);
|
||||
let value_width = this.props.widget.customProperties.textSize*0.55* (this.state.value.length +2);
|
||||
let unit_width = this.props.widget.customProperties.textSize*2;
|
||||
return (
|
||||
<div className="single-value-widget">
|
||||
<strong style={{ fontSize: this.props.widget.customProperties.textSize + 'px' }}>{this.props.widget.name}</strong>
|
||||
<span style={{ fontSize: this.props.widget.customProperties.textSize + 'px' }}>{Number.isNaN(value_to_render) ? NaN : format('.3s')(value_to_render)}</span>
|
||||
<strong style={{ fontSize: this.props.widget.customProperties.textSize + 'px'}}>{this.props.widget.name}</strong>
|
||||
<span style={{ fontSize: this.props.widget.customProperties.textSize + 'px',width: value_width }}>{Number.isNaN(value_to_render) ? NaN : format('.3s')(value_to_render)}</span>
|
||||
{this.props.widget.customProperties.showUnit &&
|
||||
<span style={{ fontSize: this.props.widget.customProperties.textSize + 'px' }}>[{this.state.unit}]</span>
|
||||
}
|
||||
<span style={{ fontSize: this.props.widget.customProperties.textSize + 'px', width: unit_width}}>[{this.state.unit}]</span>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue