mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
few components
Signed-off-by: iripiri <ikoester@eonerc.rwth-aachen.de>
This commit is contained in:
parent
5247019c56
commit
6ff1d4983f
26 changed files with 1066 additions and 1127 deletions
|
@ -1,74 +0,0 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { DragSource } from 'react-dnd';
|
||||
import classNames from 'classnames';
|
||||
import Icon from '../common/icon';
|
||||
|
||||
const toolboxItemSource = {
|
||||
beginDrag(props) {
|
||||
return {
|
||||
name: props.name
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function collect(connect, monitor) {
|
||||
return {
|
||||
connectDragSource: connect.dragSource(),
|
||||
isDragging: monitor.isDragging()
|
||||
}
|
||||
}
|
||||
|
||||
class ToolboxItem extends React.Component {
|
||||
static defaultProps = {
|
||||
disabled: false
|
||||
};
|
||||
|
||||
render() {
|
||||
var itemClass = classNames({
|
||||
'toolbox-item': true,
|
||||
'toolbox-item-dragging': this.props.isDragging,
|
||||
'toolbox-item-disabled': this.props.disabled
|
||||
});
|
||||
var dropEffect = 'copy';
|
||||
|
||||
if (this.props.disabled === false) {
|
||||
return this.props.connectDragSource(
|
||||
<div className={itemClass}>
|
||||
<span className="btn " style={{marginTop: '5px'}}>
|
||||
{this.props.icon && <Icon style={{marginRight: '5px'}} icon={this.props.icon} /> }
|
||||
{this.props.name}
|
||||
</span>
|
||||
</div>
|
||||
, {dropEffect});
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<div className={itemClass}>
|
||||
<span className="btn btn-info" style={{marginTop: '5px'}}>
|
||||
{this.props.icon && <Icon style={{marginRight: '5px'}} icon={this.props.icon} /> }
|
||||
{this.props.name}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DragSource('widget', toolboxItemSource, collect)(ToolboxItem);
|
74
src/widget/toolbox-item.jsx
Normal file
74
src/widget/toolbox-item.jsx
Normal file
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from "react";
|
||||
import { DragSource } from "react-dnd";
|
||||
import classNames from "classnames";
|
||||
import Icon from "../common/icon";
|
||||
|
||||
// Drag source specification
|
||||
const toolboxItemSource = {
|
||||
beginDrag(props) {
|
||||
return {
|
||||
name: props.name,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// The collect function gathers props for the component related to dragging
|
||||
function collect(connect, monitor) {
|
||||
return {
|
||||
connectDragSource: connect.dragSource(),
|
||||
isDragging: monitor.isDragging(),
|
||||
};
|
||||
}
|
||||
|
||||
// The functional component for ToolboxItem
|
||||
const ToolboxItem = ({
|
||||
connectDragSource,
|
||||
isDragging,
|
||||
disabled,
|
||||
name,
|
||||
icon,
|
||||
}) => {
|
||||
const itemClass = classNames({
|
||||
"toolbox-item": true,
|
||||
"toolbox-item-dragging": isDragging,
|
||||
"toolbox-item-disabled": disabled,
|
||||
});
|
||||
|
||||
const content = (
|
||||
<span className="btn " style={{ marginTop: "5px" }}>
|
||||
{icon && <Icon style={{ marginRight: "5px" }} icon={icon} />}
|
||||
{name}
|
||||
</span>
|
||||
);
|
||||
|
||||
return disabled ? (
|
||||
<div className={itemClass}>{content}</div>
|
||||
) : (
|
||||
connectDragSource(<div className={itemClass}>{content}</div>, {
|
||||
dropEffect: "copy",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
ToolboxItem.defaultProps = {
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
export default DragSource("widget", toolboxItemSource, collect)(ToolboxItem);
|
|
@ -1,56 +0,0 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { scaleOrdinal} from 'd3-scale';
|
||||
import { schemeCategory10 } from 'd3-scale-chromatic'
|
||||
|
||||
function Legend(props){
|
||||
|
||||
const signal = props.sig;
|
||||
const hasScalingFactor = (signal.scalingFactor !== 1);
|
||||
|
||||
let color = typeof props.lineColor === "undefined" ? schemeCategory10[props.index % 10] : props.lineColor;
|
||||
|
||||
return (
|
||||
<li key={signal.id} className="signal-legend" style={{ color: color }}>
|
||||
<span className="signal-legend-name">{signal.name}</span>
|
||||
{props.showUnit ? <span style={{marginLeft: '0.3em'}} className="signal-unit">{signal.unit}</span> : <></> }
|
||||
{hasScalingFactor ? <span style={{ marginLeft: '0.3em' }} className="signal-scale">{signal.scalingFactor}</span> : <></>}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
class PlotLegend extends React.Component {
|
||||
render() {
|
||||
|
||||
return <div className="plot-legend">
|
||||
<ul>
|
||||
{ this.props.lineColors !== undefined && this.props.lineColors != null ? (
|
||||
this.props.signals.map( (signal, idx) =>
|
||||
<Legend key={signal.id} sig={signal} showUnit={this.props.showUnit} index={idx} lineColor={this.props.lineColors[idx]}/>
|
||||
)) : (
|
||||
this.props.signals.map( (signal, idx) =>
|
||||
<Legend key={signal.id} sig={signal} showUnit={this.props.showUnit} index={idx} lineColor={"undefined"}/>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default PlotLegend;
|
62
src/widget/widget-plot/plot-legend.jsx
Normal file
62
src/widget/widget-plot/plot-legend.jsx
Normal file
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from "react";
|
||||
import { schemeCategory10 } from "d3-scale-chromatic";
|
||||
|
||||
function Legend(props) {
|
||||
const { sig: signal, lineColor, index, showUnit } = props;
|
||||
const hasScalingFactor = signal.scalingFactor !== 1;
|
||||
const color =
|
||||
typeof lineColor === "undefined" ? schemeCategory10[index % 10] : lineColor;
|
||||
|
||||
return (
|
||||
<li key={signal.id} className="signal-legend" style={{ color: color }}>
|
||||
<span className="signal-legend-name">{signal.name}</span>
|
||||
{showUnit && (
|
||||
<span style={{ marginLeft: "0.3em" }} className="signal-unit">
|
||||
{signal.unit}
|
||||
</span>
|
||||
)}
|
||||
{hasScalingFactor && (
|
||||
<span style={{ marginLeft: "0.3em" }} className="signal-scale">
|
||||
{signal.scalingFactor}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
const PlotLegend = ({ signals, lineColors, showUnit }) => {
|
||||
return (
|
||||
<div className="plot-legend">
|
||||
<ul>
|
||||
{signals.map((signal, idx) => (
|
||||
<Legend
|
||||
key={signal.id}
|
||||
sig={signal}
|
||||
showUnit={showUnit}
|
||||
index={idx}
|
||||
lineColor={lineColors ? lineColors[idx] : undefined}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlotLegend;
|
|
@ -15,29 +15,28 @@
|
|||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Button, ButtonGroup } from 'react-bootstrap';
|
||||
import Icon from '../../common/icon';
|
||||
import React from "react";
|
||||
import { Button, ButtonGroup } from "react-bootstrap";
|
||||
import Icon from "../../common/icon";
|
||||
|
||||
class WidgetAction extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const WidgetAction = (props) => {
|
||||
const onClick = (e) => {
|
||||
// Put your onClick logic here
|
||||
};
|
||||
|
||||
this.state = {
|
||||
};
|
||||
}
|
||||
|
||||
onClick(e) {
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
return <ButtonGroup>
|
||||
<Button onClick={this.onClick} ><Icon icon="play" /> Start</Button>
|
||||
<Button onClick={this.onClick} ><Icon icon="pause" /> Pause</Button>
|
||||
<Button onClick={this.onClick} ><Icon icon="stop" /> Stop</Button>
|
||||
</ButtonGroup>;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<Button onClick={onClick}>
|
||||
<Icon icon="play" /> Start
|
||||
</Button>
|
||||
<Button onClick={onClick}>
|
||||
<Icon icon="pause" /> Pause
|
||||
</Button>
|
||||
<Button onClick={onClick}>
|
||||
<Icon icon="stop" /> Stop
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetAction;
|
|
@ -15,25 +15,21 @@
|
|||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import React from "react";
|
||||
|
||||
const WidgetBox = (props) => {
|
||||
let colorStyle = {
|
||||
borderColor: props.widget.customProperties.border_color,
|
||||
backgroundColor: props.widget.customProperties.background_color,
|
||||
opacity: props.widget.customProperties.background_color_opacity,
|
||||
borderWidth: props.widget.customProperties.border_width + "px",
|
||||
};
|
||||
|
||||
class WidgetBox extends Component {
|
||||
render() {
|
||||
|
||||
let colorStyle = {
|
||||
borderColor: this.props.widget.customProperties.border_color,
|
||||
backgroundColor: this.props.widget.customProperties.background_color,
|
||||
opacity: this.props.widget.customProperties.background_color_opacity,
|
||||
borderWidth: this.props.widget.customProperties.border_width + 'px'
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="box-widget full" style={colorStyle}>
|
||||
{ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="box-widget full" style={colorStyle}>
|
||||
{}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetBox;
|
|
@ -1,115 +0,0 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import AppDispatcher from '../../common/app-dispatcher';
|
||||
|
||||
class WidgetButton extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
pressed: props.widget.customProperties.pressed
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let widget = this.props.widget
|
||||
widget.customProperties.simStartedSendValue = false
|
||||
widget.customProperties.pressed = false
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-edit',
|
||||
token: this.props.token,
|
||||
data: widget
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
// a simulaton was started, make an update
|
||||
if (this.props.widget.customProperties.simStartedSendValue) {
|
||||
// update widget, 'unpress' button at each simulation start
|
||||
let widget = this.props.widget
|
||||
widget.customProperties.simStartedSendValue = false
|
||||
widget.customProperties.pressed = false
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-edit',
|
||||
token: this.props.token,
|
||||
data: widget
|
||||
});
|
||||
|
||||
// send value without changing widget
|
||||
this.props.onInputChanged(widget.customProperties.off_value, '', false, false);
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state){
|
||||
return {
|
||||
pressed: props.widget.customProperties.pressed
|
||||
}
|
||||
}
|
||||
|
||||
onPress(e) {
|
||||
if (e.button === 0 && !this.props.widget.customProperties.toggle) {
|
||||
this.valueChanged(this.props.widget.customProperties.on_value, true);
|
||||
}
|
||||
}
|
||||
|
||||
onRelease(e) {
|
||||
if (e.button === 0) {
|
||||
let nextState = false;
|
||||
if (this.props.widget.customProperties.toggle) {
|
||||
nextState = !this.state.pressed;
|
||||
}
|
||||
this.valueChanged(nextState ? this.props.widget.customProperties.on_value : this.props.widget.customProperties.off_value, nextState);
|
||||
}
|
||||
}
|
||||
|
||||
valueChanged(newValue, pressed) {
|
||||
if (this.props.onInputChanged) {
|
||||
this.props.onInputChanged(newValue, 'pressed', pressed, true);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let opacity = this.props.widget.customProperties.background_color_opacity
|
||||
const buttonStyle = {
|
||||
backgroundColor: this.props.widget.customProperties.background_color,
|
||||
borderColor: this.props.widget.customProperties.border_color,
|
||||
color: this.props.widget.customProperties.font_color,
|
||||
opacity: this.state.pressed ? (opacity + 1)/4 : opacity,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="button-widget full">
|
||||
<Button
|
||||
className="full"
|
||||
style={buttonStyle}
|
||||
active={ this.state.pressed }
|
||||
disabled={ this.props.editing }
|
||||
onMouseDown={ (e) => this.onPress(e) }
|
||||
onMouseUp={ (e) => this.onRelease(e) }>
|
||||
{this.props.widget.name}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default WidgetButton;
|
108
src/widget/widgets/button.jsx
Normal file
108
src/widget/widgets/button.jsx
Normal file
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import AppDispatcher from '../../common/app-dispatcher';
|
||||
|
||||
const WidgetButton = (props) => {
|
||||
const [pressed, setPressed] = useState(props.widget.customProperties.pressed);
|
||||
|
||||
useEffect(() => {
|
||||
let widget = props.widget;
|
||||
widget.customProperties.simStartedSendValue = false;
|
||||
widget.customProperties.pressed = false;
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-edit',
|
||||
token: props.token,
|
||||
data: widget
|
||||
});
|
||||
|
||||
// Effect cleanup
|
||||
return () => {
|
||||
// Clean up if needed
|
||||
};
|
||||
}, [props.token, props.widget]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.widget.customProperties.simStartedSendValue) {
|
||||
let widget = props.widget;
|
||||
widget.customProperties.simStartedSendValue = false;
|
||||
widget.customProperties.pressed = false;
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-edit',
|
||||
token: props.token,
|
||||
data: widget
|
||||
});
|
||||
|
||||
props.onInputChanged(widget.customProperties.off_value, '', false, false);
|
||||
}
|
||||
}, [props, setPressed]);
|
||||
|
||||
useEffect(() => {
|
||||
setPressed(props.widget.customProperties.pressed);
|
||||
}, [props.widget.customProperties.pressed]);
|
||||
|
||||
const onPress = (e) => {
|
||||
if (e.button === 0 && !props.widget.customProperties.toggle) {
|
||||
valueChanged(props.widget.customProperties.on_value, true);
|
||||
}
|
||||
};
|
||||
|
||||
const onRelease = (e) => {
|
||||
if (e.button === 0) {
|
||||
let nextState = false;
|
||||
if (props.widget.customProperties.toggle) {
|
||||
nextState = !pressed;
|
||||
}
|
||||
valueChanged(nextState ? props.widget.customProperties.on_value : props.widget.customProperties.off_value, nextState);
|
||||
}
|
||||
};
|
||||
|
||||
const valueChanged = (newValue, newPressed) => {
|
||||
if (props.onInputChanged) {
|
||||
props.onInputChanged(newValue, 'pressed', newPressed, true);
|
||||
}
|
||||
setPressed(newPressed);
|
||||
};
|
||||
|
||||
let opacity = props.widget.customProperties.background_color_opacity;
|
||||
const buttonStyle = {
|
||||
backgroundColor: props.widget.customProperties.background_color,
|
||||
borderColor: props.widget.customProperties.border_color,
|
||||
color: props.widget.customProperties.font_color,
|
||||
opacity: pressed ? (opacity + 1) / 4 : opacity,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="button-widget full">
|
||||
<Button
|
||||
className="full"
|
||||
style={buttonStyle}
|
||||
active={pressed}
|
||||
disabled={props.editing}
|
||||
onMouseDown={(e) => onPress(e)}
|
||||
onMouseUp={(e) => onRelease(e)}
|
||||
>
|
||||
{props.widget.name}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetButton;
|
|
@ -1,66 +0,0 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import Icon from '../../common/icon';
|
||||
import ICStore from '../../ic/ic-store';
|
||||
import AppDispatcher from '../../common/app-dispatcher';
|
||||
|
||||
class WidgetCustomAction extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
ic: null
|
||||
};
|
||||
}
|
||||
|
||||
static getStores() {
|
||||
return [ ICStore ];
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state){
|
||||
if(props.widget.signalIDs.length === 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
return{
|
||||
ic: ICStore.getState().find(s => s.id === props.icIDs[0]),
|
||||
sessionToken: localStorage.getItem("token")
|
||||
};
|
||||
}
|
||||
|
||||
onClick() {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/start-action',
|
||||
ic: this.state.ic,
|
||||
data: this.props.widget.customProperties.actions,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="widget-custom-action full">
|
||||
<Button className="full" disabled={this.state.ic === null} onClick={(e) => this.onClick()}>
|
||||
<Icon icon={this.props.widget.customProperties.icon} /> <span dangerouslySetInnerHTML={{ __html: this.props.widget.name }} />
|
||||
</Button>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default WidgetCustomAction;
|
118
src/widget/widgets/custom-action.jsx
Normal file
118
src/widget/widgets/custom-action.jsx
Normal file
|
@ -0,0 +1,118 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Button } from "react-bootstrap";
|
||||
import Icon from "../../common/icon";
|
||||
import ICStore from "../../ic/ic-store";
|
||||
import AppDispatcher from "../../common/app-dispatcher";
|
||||
|
||||
const WidgetCustomAction = (props) => {
|
||||
const [ic, setIC] = useState(null);
|
||||
const [sessionToken, setSessionToken] = useState(
|
||||
localStorage.getItem("token")
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleStoresChanged = () => {
|
||||
if (props.widget.signalIDs.length === 0) {
|
||||
setIC(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const newIC = ICStore.getState().find((s) => s.id === props.icIDs[0]);
|
||||
setIC(newIC);
|
||||
};
|
||||
|
||||
// Subscribe to store changes
|
||||
const unsubscribe = ICStore.subscribe(handleStoresChanged);
|
||||
handleStoresChanged(); // Also call it to set initial state
|
||||
|
||||
return () => {
|
||||
unsubscribe(); // Clean up the subscription when the component is unmounted
|
||||
};
|
||||
}, [props.widget.signalIDs, props.icIDs]);
|
||||
|
||||
const onClick = () => {
|
||||
AppDispatcher.dispatch({
|
||||
type: "ics/start-action",
|
||||
ic: ic,
|
||||
data: props.widget.customProperties.actions,
|
||||
token: sessionToken,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="widget-custom-action full">
|
||||
<Button className="full" disabled={ic === null} onClick={onClick}>
|
||||
<Icon icon={props.widget.customProperties.icon} />
|
||||
<span dangerouslySetInnerHTML={{ __html: props.widget.name }} />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetCustomAction;
|
||||
|
||||
// import React, { Component } from 'react';
|
||||
// import { Button } from 'react-bootstrap';
|
||||
// import Icon from '../../common/icon';
|
||||
// import ICStore from '../../ic/ic-store';
|
||||
// import AppDispatcher from '../../common/app-dispatcher';
|
||||
|
||||
// class WidgetCustomAction extends Component {
|
||||
// constructor(props) {
|
||||
// super(props);
|
||||
|
||||
// this.state = {
|
||||
// ic: null
|
||||
// };
|
||||
// }
|
||||
|
||||
// static getStores() {
|
||||
// return [ ICStore ];
|
||||
// }
|
||||
|
||||
// static getDerivedStateFromProps(props, state){
|
||||
// if(props.widget.signalIDs.length === 0){
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// return{
|
||||
// ic: ICStore.getState().find(s => s.id === props.icIDs[0]),
|
||||
// sessionToken: localStorage.getItem("token")
|
||||
// };
|
||||
// }
|
||||
|
||||
// onClick() {
|
||||
// AppDispatcher.dispatch({
|
||||
// type: 'ics/start-action',
|
||||
// ic: this.state.ic,
|
||||
// data: this.props.widget.customProperties.actions,
|
||||
// token: this.state.sessionToken
|
||||
// });
|
||||
// }
|
||||
|
||||
// render() {
|
||||
// return <div className="widget-custom-action full">
|
||||
// <Button className="full" disabled={this.state.ic === null} onClick={(e) => this.onClick()}>
|
||||
// <Icon icon={this.props.widget.customProperties.icon} /> <span dangerouslySetInnerHTML={{ __html: this.props.widget.name }} />
|
||||
// </Button>
|
||||
// </div>;
|
||||
// }
|
||||
// }
|
||||
|
||||
// export default WidgetCustomAction;
|
|
@ -15,12 +15,16 @@
|
|||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
class WidgetHTML extends React.Component {
|
||||
render() {
|
||||
return <div dangerouslySetInnerHTML={{__html: this.props.widget.customProperties.content }} />
|
||||
}
|
||||
}
|
||||
const WidgetHTML = (props) => {
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: props.widget.customProperties.content,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetHTML;
|
|
@ -1,79 +0,0 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { Badge } from 'react-bootstrap';
|
||||
import { stateLabelStyle } from "../../ic/ics";
|
||||
import AppDispatcher from '../../common/app-dispatcher';
|
||||
|
||||
|
||||
class WidgetICstatus extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
sessionToken: localStorage.getItem("token")
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Start timer for periodic refresh
|
||||
this.timer = window.setInterval(() => this.refresh(), 3000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.clearInterval(this.timer);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
if (this.props.ics) {
|
||||
this.props.ics.forEach(ic => {
|
||||
let icID = parseInt(ic.id, 10);
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/start-load',
|
||||
data: icID,
|
||||
token: this.state.sessionToken,
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let badges = []
|
||||
let checkedICs = []
|
||||
if (this.props.widget) {
|
||||
checkedICs = this.props.widget.customProperties.checkedIDs
|
||||
}
|
||||
if (this.props.ics && checkedICs) {
|
||||
this.props.ics.forEach(ic => {
|
||||
if (!checkedICs.includes(ic.id)) {
|
||||
return
|
||||
}
|
||||
let badgeStyle = stateLabelStyle(ic.state, ic)
|
||||
badges.push(<Badge
|
||||
key={ic.id}
|
||||
bg={badgeStyle[0]}
|
||||
className={badgeStyle[1]}>
|
||||
{ic.name + ": " + ic.state}</Badge>)
|
||||
})
|
||||
}
|
||||
|
||||
return (<div>{badges}</div>);
|
||||
}
|
||||
}
|
||||
|
||||
export default WidgetICstatus;
|
71
src/widget/widgets/icstatus.jsx
Normal file
71
src/widget/widgets/icstatus.jsx
Normal file
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Badge } from "react-bootstrap";
|
||||
import { stateLabelStyle } from "../../ic/ics";
|
||||
import AppDispatcher from "../../common/app-dispatcher";
|
||||
|
||||
const WidgetICstatus = (props) => {
|
||||
const [sessionToken, setSessionToken] = useState(
|
||||
localStorage.getItem("token")
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Function to refresh data
|
||||
const refresh = () => {
|
||||
if (props.ics) {
|
||||
props.ics.forEach((ic) => {
|
||||
let icID = parseInt(ic.id, 10);
|
||||
AppDispatcher.dispatch({
|
||||
type: "ics/start-load",
|
||||
data: icID,
|
||||
token: sessionToken,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Start timer for periodic refresh
|
||||
const timer = window.setInterval(() => refresh(), 3000);
|
||||
|
||||
// Cleanup function equivalent to componentWillUnmount
|
||||
return () => {
|
||||
window.clearInterval(timer);
|
||||
};
|
||||
}, [props.ics, sessionToken]);
|
||||
|
||||
let badges = [];
|
||||
let checkedICs = props.widget ? props.widget.customProperties.checkedIDs : [];
|
||||
|
||||
if (props.ics && checkedICs) {
|
||||
badges = props.ics
|
||||
.filter((ic) => checkedICs.includes(ic.id))
|
||||
.map((ic) => {
|
||||
let badgeStyle = stateLabelStyle(ic.state, ic);
|
||||
return (
|
||||
<Badge key={ic.id} bg={badgeStyle[0]} className={badgeStyle[1]}>
|
||||
{ic.name + ": " + ic.state}
|
||||
</Badge>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return <div>{badges}</div>;
|
||||
};
|
||||
|
||||
export default WidgetICstatus;
|
|
@ -1,89 +0,0 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import AppDispatcher from '../../common/app-dispatcher';
|
||||
|
||||
class WidgetImage extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
file: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Query the image referenced by the widget
|
||||
let widgetFile = this.props.widget.customProperties.file;
|
||||
if (widgetFile !== -1 && this.state.file === undefined) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/start-download',
|
||||
data: widgetFile,
|
||||
token: this.props.token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS) {
|
||||
|
||||
if(this.props.widget.customProperties.file === -1){
|
||||
this.props.widget.customProperties.update = false;
|
||||
if(this.state.file !== undefined) this.setState({ file: undefined })
|
||||
}
|
||||
|
||||
let file = this.props.files.find(file => file.id === parseInt(this.props.widget.customProperties.file, 10));
|
||||
|
||||
if (file !== undefined) {
|
||||
if (this.props.widget.customProperties.update) {
|
||||
this.props.widget.customProperties.update = false;
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/start-download',
|
||||
data: file.id,
|
||||
token: this.props.token
|
||||
});
|
||||
this.setState({ file: file })
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
imageError(e){
|
||||
console.error("Image ", this.state.file.name, "cannot be displayed.");
|
||||
}
|
||||
|
||||
render() {
|
||||
let objectURL=''
|
||||
if(this.state.file !== undefined && this.state.file.objectURL !== undefined) {
|
||||
objectURL = this.state.file.objectURL
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="full">
|
||||
{objectURL !== '' ? (
|
||||
<img onError={(e) => this.imageError(e)} className="full" alt={this.state.file.name} src={objectURL} onDragStart={e => e.preventDefault()} />
|
||||
) : (
|
||||
<img className="full" alt="No file selected." />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default WidgetImage;
|
78
src/widget/widgets/image.jsx
Normal file
78
src/widget/widgets/image.jsx
Normal file
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import AppDispatcher from "../../common/app-dispatcher";
|
||||
|
||||
const WidgetImage = (props) => {
|
||||
const [file, setFile] = useState(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
let widgetFile = props.widget.customProperties.file;
|
||||
if (widgetFile !== -1 && file === undefined) {
|
||||
AppDispatcher.dispatch({
|
||||
type: "files/start-download",
|
||||
data: widgetFile,
|
||||
token: props.token,
|
||||
});
|
||||
}
|
||||
}, [file, props.token, props.widget.customProperties.file]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.widget.customProperties.file === -1) {
|
||||
props.widget.customProperties.update = false;
|
||||
if (file !== undefined) setFile(undefined);
|
||||
} else {
|
||||
let foundFile = props.files.find(
|
||||
(f) => f.id === parseInt(props.widget.customProperties.file, 10)
|
||||
);
|
||||
if (foundFile && props.widget.customProperties.update) {
|
||||
props.widget.customProperties.update = false;
|
||||
AppDispatcher.dispatch({
|
||||
type: "files/start-download",
|
||||
data: foundFile.id,
|
||||
token: props.token,
|
||||
});
|
||||
setFile(foundFile);
|
||||
}
|
||||
}
|
||||
}, [props.widget.customProperties, props.files, props.token, file]);
|
||||
|
||||
const imageError = (e) => {
|
||||
console.error("Image error:", e);
|
||||
};
|
||||
|
||||
let objectURL = file && file.objectURL ? file.objectURL : "";
|
||||
|
||||
return (
|
||||
<div className="full">
|
||||
{objectURL ? (
|
||||
<img
|
||||
onError={imageError}
|
||||
className="full"
|
||||
alt={file.name}
|
||||
src={objectURL}
|
||||
onDragStart={(e) => e.preventDefault()}
|
||||
/>
|
||||
) : (
|
||||
<img className="full" alt="No file selected." />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetImage;
|
|
@ -15,23 +15,20 @@
|
|||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import React from "react";
|
||||
|
||||
const WidgetLabel = (props) => {
|
||||
const style = {
|
||||
fontSize: props.widget.customProperties.textSize + "px",
|
||||
color: props.widget.customProperties.fontColor,
|
||||
opacity: props.widget.customProperties.fontColor_opacity,
|
||||
};
|
||||
|
||||
class WidgetLabel extends Component {
|
||||
render() {
|
||||
const style = {
|
||||
fontSize: this.props.widget.customProperties.textSize + 'px',
|
||||
color: this.props.widget.customProperties.fontColor,
|
||||
opacity: this.props.widget.customProperties.fontColor_opacity,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="label-widget">
|
||||
<h4 style={style}>{this.props.widget.name}</h4>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="label-widget">
|
||||
<h4 style={style}>{props.widget.name}</h4>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetLabel;
|
|
@ -1,86 +0,0 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
|
||||
class WidgetLamp extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
value: '',
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state){
|
||||
if(props.widget.signalIDs.length === 0){
|
||||
return{ value: ''};
|
||||
}
|
||||
|
||||
// get the signal with the selected signal ID
|
||||
let signalID = props.widget.signalIDs[0];
|
||||
let signal = props.signals.filter(s => s.id === signalID)
|
||||
|
||||
if(signal.length>0) {
|
||||
// determine ID of infrastructure component related to signal[0] (there is only one signal for a lamp widget)
|
||||
let icID = props.icIDs[signal[0].id];
|
||||
|
||||
// check if data available
|
||||
if (props.data == null
|
||||
|| props.data[icID] == null
|
||||
|| props.data[icID].output == null
|
||||
|| props.data[icID].output.values == null) {
|
||||
return {value: ''};
|
||||
}
|
||||
|
||||
// check if value has changed
|
||||
const data = props.data[icID].output.values[signal[0].index];
|
||||
if (data != null && Number(state.value) !== signal[0].scalingFactor * data[data.length - 1].y) {
|
||||
return {value: signal[0].scalingFactor * data[data.length - 1].y};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
let color;
|
||||
let opacity;
|
||||
|
||||
if (Number(this.state.value) > Number(this.props.widget.customProperties.threshold)){
|
||||
color = this.props.widget.customProperties.on_color;
|
||||
opacity = this.props.widget.customProperties.on_color_opacity;
|
||||
}
|
||||
else{
|
||||
color = this.props.widget.customProperties.off_color;
|
||||
opacity = this.props.widget.customProperties.off_color_opacity;
|
||||
}
|
||||
|
||||
let style = {
|
||||
backgroundColor: color,
|
||||
opacity: opacity
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="lamp-widget" style={style} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default WidgetLamp;
|
72
src/widget/widgets/lamp.jsx
Normal file
72
src/widget/widgets/lamp.jsx
Normal file
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
const WidgetLamp = (props) => {
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if(props.widget.signalIDs.length === 0){
|
||||
setValue('');
|
||||
return;
|
||||
}
|
||||
|
||||
let signalID = props.widget.signalIDs[0];
|
||||
let signal = props.signals.find(s => s.id === signalID);
|
||||
|
||||
if(signal) {
|
||||
let icID = props.icIDs[signal.id];
|
||||
|
||||
if (!(props.data &&
|
||||
props.data[icID] &&
|
||||
props.data[icID].output &&
|
||||
props.data[icID].output.values)) {
|
||||
setValue('');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = props.data[icID].output.values[signal.index];
|
||||
if(data) {
|
||||
const newValue = String(signal.scalingFactor * data[data.length - 1].y);
|
||||
if (value !== newValue) {
|
||||
setValue(newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [props.widget.signalIDs, props.signals, props.icIDs, props.data, value]);
|
||||
|
||||
let color, opacity;
|
||||
if (Number(value) > Number(props.widget.customProperties.threshold)) {
|
||||
color = props.widget.customProperties.on_color;
|
||||
opacity = props.widget.customProperties.on_color_opacity;
|
||||
} else {
|
||||
color = props.widget.customProperties.off_color;
|
||||
opacity = props.widget.customProperties.off_color_opacity;
|
||||
}
|
||||
|
||||
let style = {
|
||||
backgroundColor: color,
|
||||
opacity: opacity
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="lamp-widget" style={style} />
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetLamp;
|
|
@ -1,83 +0,0 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
class WidgetLine extends Component {
|
||||
// WidgetLine is newly created when widget props are changed and saved
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.illustrateDuringEdit = this.illustrateDuringEdit.bind(this);
|
||||
|
||||
this.state = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
editing: false
|
||||
}
|
||||
}
|
||||
|
||||
// needed to update the looks of the line in edit mode
|
||||
illustrateDuringEdit(newwidth, newheight) {
|
||||
this.setState({width: newwidth, height: newheight, editing: true});
|
||||
}
|
||||
|
||||
render() {
|
||||
let rotation = this.props.widget.customProperties.rotation;
|
||||
let rad = rotation * (Math.PI / 180);
|
||||
|
||||
// get the dimensions either from props (saved widget)
|
||||
// or from the state (widget in edit mode)
|
||||
let width = this.props.widget.width;
|
||||
let height = this.props.widget.height;
|
||||
|
||||
if (this.state.editing) {
|
||||
width = this.state.width;
|
||||
height = this.state.height;
|
||||
}
|
||||
|
||||
let length = width;
|
||||
rotation = Math.abs(parseInt(rotation,10));
|
||||
if(rotation % 90 === 0 && (rotation/90) % 2 === 1){
|
||||
length = height;
|
||||
}
|
||||
|
||||
// calculate line coordinates (in percent)
|
||||
const x1 = width * 0.5 - 0.5 * Math.cos(rad) * length;
|
||||
const x1p = '' + Math.round(100 * x1 / width) + '%';
|
||||
|
||||
const x2 = width * 0.5 + 0.5 * Math.cos(rad) * length;
|
||||
const x2p = '' + Math.round(100 * x2/width) + '%';
|
||||
|
||||
const y1 = height * 0.5 + 0.5 * Math.sin(rad) * length;
|
||||
const y1p = '' + Math.round(100 * y1/height) + '%';
|
||||
|
||||
const y2 = height * 0.5 - 0.5 * Math.sin(rad) * length;
|
||||
const y2p = '' + Math.round(100 * y2/height) + '%';
|
||||
|
||||
|
||||
const lineStyle = {
|
||||
stroke: '' + this.props.widget.customProperties.border_color,
|
||||
strokeWidth: '' + this.props.widget.customProperties.border_width + 'px'
|
||||
};
|
||||
|
||||
return <svg height="100%" width="100%">
|
||||
<line x1={x1p} x2={x2p} y1={y1p} y2={y2p} style={lineStyle}/>
|
||||
</svg>;
|
||||
}
|
||||
}
|
||||
|
||||
export default WidgetLine;
|
70
src/widget/widgets/line.jsx
Normal file
70
src/widget/widgets/line.jsx
Normal file
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { useState } from "react";
|
||||
|
||||
const WidgetLine = (props) => {
|
||||
const [dimensions, setDimensions] = useState({
|
||||
width: props.widget.width || 0,
|
||||
height: props.widget.height || 0,
|
||||
editing: false,
|
||||
});
|
||||
|
||||
const illustrateDuringEdit = (newWidth, newHeight) => {
|
||||
setDimensions({ width: newWidth, height: newHeight, editing: true });
|
||||
};
|
||||
|
||||
// Assuming illustrateDuringEdit may be called from outside to update the dimensions.
|
||||
// If not, you can remove this line.
|
||||
props.illustrateDuringEditRef &&
|
||||
props.illustrateDuringEditRef(illustrateDuringEdit);
|
||||
|
||||
let { rotation } = props.widget.customProperties;
|
||||
let rad = rotation * (Math.PI / 180);
|
||||
let length = dimensions.editing ? dimensions.width : props.widget.width;
|
||||
|
||||
rotation = Math.abs(parseInt(rotation, 10));
|
||||
if (rotation % 90 === 0 && (rotation / 90) % 2 === 1) {
|
||||
length = dimensions.editing ? dimensions.height : props.widget.height;
|
||||
}
|
||||
|
||||
// calculate line coordinates (in percent)
|
||||
const x1 = length * 0.5 - 0.5 * Math.cos(rad) * length;
|
||||
const x1p = `${Math.round((100 * x1) / length)}%`;
|
||||
|
||||
const x2 = length * 0.5 + 0.5 * Math.cos(rad) * length;
|
||||
const x2p = `${Math.round((100 * x2) / length)}%`;
|
||||
|
||||
const y1 = length * 0.5 + 0.5 * Math.sin(rad) * length;
|
||||
const y1p = `${Math.round((100 * y1) / length)}%`;
|
||||
|
||||
const y2 = length * 0.5 - 0.5 * Math.sin(rad) * length;
|
||||
const y2p = `${Math.round((100 * y2) / length)}%`;
|
||||
|
||||
const lineStyle = {
|
||||
stroke: props.widget.customProperties.border_color,
|
||||
strokeWidth: `${props.widget.customProperties.border_width}px`,
|
||||
};
|
||||
|
||||
return (
|
||||
<svg height="100%" width="100%">
|
||||
<line x1={x1p} x2={x2p} y1={y1p} y2={y2p} style={lineStyle} />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetLine;
|
|
@ -1,120 +0,0 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Plot from '../widget-plot/plot';
|
||||
import PlotLegend from '../widget-plot/plot-legend';
|
||||
|
||||
class WidgetPlot extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
data: [],
|
||||
signals: []
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
static getDerivedStateFromProps(props, state){
|
||||
|
||||
let intersection = []
|
||||
let data = [];
|
||||
let signalID, sig;
|
||||
for (signalID of props.widget.signalIDs) {
|
||||
for (sig of props.signals) {
|
||||
if (signalID === sig.id) {
|
||||
intersection.push(sig);
|
||||
|
||||
// sig is a selected signal, get data
|
||||
// determine ID of infrastructure component related to signal (via config)
|
||||
let icID = props.icIDs[sig.id]
|
||||
|
||||
// distinguish between input and output signals
|
||||
if (sig.direction === "out") {
|
||||
if (props.data[icID] != null && props.data[icID].output != null && props.data[icID].output.values != null) {
|
||||
if (props.data[icID].output.values[sig.index] !== undefined) {
|
||||
let values = props.data[icID].output.values[sig.index];
|
||||
if(sig.scalingFactor !== 1) {
|
||||
let scaledValues = JSON.parse(JSON.stringify(values));
|
||||
for (let i=0; i< scaledValues.length; i++){
|
||||
scaledValues[i].y = scaledValues[i].y * sig.scalingFactor;
|
||||
}
|
||||
data.push(scaledValues);
|
||||
} else {
|
||||
data.push(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (sig.direction === "in") {
|
||||
if (props.data[icID] != null && props.data[icID].input != null && props.data[icID].input.values != null) {
|
||||
if (props.data[icID].input.values[sig.index] !== undefined) {
|
||||
let values = props.data[icID].output.values[sig.index];
|
||||
if(sig.scalingFactor !== 1) {
|
||||
let scaledValues = JSON.parse(JSON.stringify(values));
|
||||
for (let i=0; i< scaledValues.length; i++){
|
||||
scaledValues[i].y = scaledValues[i].y * sig.scalingFactor;
|
||||
}
|
||||
data.push(scaledValues);
|
||||
} else {
|
||||
data.push(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // sig is selected signal
|
||||
} // loop over props.signals
|
||||
} // loop over selected signals
|
||||
|
||||
return {signals: intersection, data: data}
|
||||
|
||||
}
|
||||
|
||||
//do we need this function?
|
||||
scaleData(data, scaleFactor){
|
||||
// data is an array of value pairs x,y
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="plot-widget" ref="wrapper">
|
||||
<div className="widget-plot">
|
||||
<Plot
|
||||
data={this.state.data}
|
||||
mode={this.props.widget.customProperties.mode || "auto time-scrolling"}
|
||||
height={this.props.widget.height - 55}
|
||||
width={this.props.widget.width - 20}
|
||||
time={this.props.widget.customProperties.time}
|
||||
samples={this.props.widget.customProperties.nbrSamples || 100}
|
||||
yMin={this.props.widget.customProperties.yMin}
|
||||
yMax={this.props.widget.customProperties.yMax}
|
||||
yUseMinMax={this.props.widget.customProperties.yUseMinMax}
|
||||
paused={this.props.paused}
|
||||
yLabel={this.props.widget.customProperties.ylabel}
|
||||
lineColors={this.props.widget.customProperties.lineColors}
|
||||
signalIDs={this.props.widget.signalIDs}
|
||||
/>
|
||||
</div>
|
||||
<PlotLegend
|
||||
signals={this.state.signals}
|
||||
lineColors={this.props.widget.customProperties.lineColors}
|
||||
showUnit={this.props.widget.customProperties.showUnit} />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default WidgetPlot;
|
96
src/widget/widgets/plot.jsx
Normal file
96
src/widget/widgets/plot.jsx
Normal file
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Plot from "../widget-plot/plot";
|
||||
import PlotLegend from "../widget-plot/plot-legend";
|
||||
|
||||
const WidgetPlot = (props) => {
|
||||
const [data, setData] = useState([]);
|
||||
const [signals, setSignals] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const intersection = [];
|
||||
const plotData = [];
|
||||
let signalID, sig;
|
||||
for (signalID of props.widget.signalIDs) {
|
||||
for (sig of props.signals) {
|
||||
if (signalID === sig.id) {
|
||||
intersection.push(sig);
|
||||
|
||||
// Signal is a selected signal, get data
|
||||
let icID = props.icIDs[sig.id];
|
||||
let values = null;
|
||||
|
||||
if (
|
||||
sig.direction === "out" &&
|
||||
props.data[icID]?.output?.values?.[sig.index] !== undefined
|
||||
) {
|
||||
values = props.data[icID].output.values[sig.index];
|
||||
} else if (
|
||||
sig.direction === "in" &&
|
||||
props.data[icID]?.input?.values?.[sig.index] !== undefined
|
||||
) {
|
||||
values = props.data[icID].input.values[sig.index];
|
||||
}
|
||||
|
||||
if (values) {
|
||||
if (sig.scalingFactor !== 1) {
|
||||
values = values.map((v) => ({
|
||||
...v,
|
||||
y: v.y * sig.scalingFactor,
|
||||
}));
|
||||
}
|
||||
plotData.push(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setData(plotData);
|
||||
setSignals(intersection);
|
||||
}, [props.widget.signalIDs, props.signals, props.icIDs, props.data]);
|
||||
|
||||
return (
|
||||
<div className="plot-widget">
|
||||
<div className="widget-plot">
|
||||
<Plot
|
||||
data={data}
|
||||
mode={props.widget.customProperties.mode || "auto time-scrolling"}
|
||||
height={props.widget.height - 55}
|
||||
width={props.widget.width - 20}
|
||||
time={props.widget.customProperties.time}
|
||||
samples={props.widget.customProperties.nbrSamples || 100}
|
||||
yMin={props.widget.customProperties.yMin}
|
||||
yMax={props.widget.customProperties.yMax}
|
||||
yUseMinMax={props.widget.customProperties.yUseMinMax}
|
||||
paused={props.paused}
|
||||
yLabel={props.widget.customProperties.ylabel}
|
||||
lineColors={props.widget.customProperties.lineColors}
|
||||
signalIDs={props.widget.signalIDs}
|
||||
/>
|
||||
</div>
|
||||
<PlotLegend
|
||||
signals={signals}
|
||||
lineColors={props.widget.customProperties.lineColors}
|
||||
showUnit={props.widget.customProperties.showUnit}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetPlot;
|
|
@ -1,161 +0,0 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { format } from 'd3';
|
||||
import classNames from 'classnames';
|
||||
import Slider from 'rc-slider';
|
||||
import 'rc-slider/assets/index.css';
|
||||
import AppDispatcher from '../../common/app-dispatcher';
|
||||
|
||||
|
||||
class WidgetSlider extends Component {
|
||||
|
||||
static get OrientationTypes() {
|
||||
return ({
|
||||
HORIZONTAL: {value: 0, name: 'Horizontal'},
|
||||
VERTICAL: {value: 1, name: 'Vertical'}
|
||||
})
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
unit: '',
|
||||
value: '',
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let widget = this.props.widget
|
||||
widget.customProperties.simStartedSendValue = false
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-edit',
|
||||
token: this.props.token,
|
||||
data: widget
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
// a simulaton was started, make an update
|
||||
if (this.props.widget.customProperties.simStartedSendValue) {
|
||||
let widget = this.props.widget
|
||||
widget.customProperties.simStartedSendValue = false
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-edit',
|
||||
token: this.props.token,
|
||||
data: widget
|
||||
});
|
||||
|
||||
// send value without changing widget
|
||||
this.props.onInputChanged(widget.customProperties.value , '', '', false);
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state){
|
||||
|
||||
let value = ''
|
||||
let unit = ''
|
||||
|
||||
if(props.widget.customProperties.hasOwnProperty('value') && props.widget.customProperties.value !== state.value){
|
||||
// set value to customProperties.value if this property exists and the value is different from current state
|
||||
value = Number(props.widget.customProperties.value);
|
||||
} else if (state.value === '') {
|
||||
value = 0.0;
|
||||
}
|
||||
|
||||
// Update unit (assuming there is exactly one signal for this widget)
|
||||
if (props.widget.signalIDs.length > 0) {
|
||||
let signalID = props.widget.signalIDs[0];
|
||||
let signal = props.signals.find(sig => sig.id === signalID);
|
||||
if (signal !== undefined) {
|
||||
unit = signal.unit;
|
||||
}
|
||||
}
|
||||
|
||||
if (unit !== '' && value !== ''){
|
||||
// unit and value have changed
|
||||
return {unit: unit, value: value};
|
||||
} else if (unit !== ''){
|
||||
// only unit has changed
|
||||
return {unit: unit}
|
||||
} else if (value !== ''){
|
||||
// only value has changed
|
||||
return {value: value}
|
||||
} else {
|
||||
// nothing has changed
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
valueIsChanging(newValue) {
|
||||
this.props.widget.customProperties.value = newValue;
|
||||
if (this.props.widget.customProperties.continous_update)
|
||||
this.valueChanged(newValue, false);
|
||||
|
||||
this.setState({ value: newValue });
|
||||
}
|
||||
|
||||
valueChanged(newValue, isFinalChange) {
|
||||
if (this.props.onInputChanged) {
|
||||
this.props.onInputChanged(newValue, 'value', newValue, isFinalChange);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
let isVertical = this.props.widget.customProperties.orientation === WidgetSlider.OrientationTypes.VERTICAL.value;
|
||||
let fields = {
|
||||
name: this.props.widget.name,
|
||||
control: <Slider
|
||||
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 }
|
||||
vertical={ isVertical }
|
||||
onChange={ (v) => this.valueIsChanging(v) }
|
||||
onAfterChange={ (v) => this.valueChanged(v, true)
|
||||
}/>,
|
||||
value: <span className="signal-value">{ format('.2f')(Number.parseFloat(this.state.value)) }</span>,
|
||||
unit: <span className="signal-unit">{ this.state.unit }</span>
|
||||
}
|
||||
|
||||
let widgetClasses = classNames({
|
||||
'slider-widget': true,
|
||||
'full': true,
|
||||
'vertical': isVertical,
|
||||
'horizontal': !isVertical
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div >
|
||||
{ fields.name }
|
||||
{ fields.value }
|
||||
{this.props.widget.customProperties.showUnit && fields.unit}
|
||||
</div>
|
||||
<div className={widgetClasses}>
|
||||
{ fields.control }
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default WidgetSlider;
|
148
src/widget/widgets/slider.jsx
Normal file
148
src/widget/widgets/slider.jsx
Normal file
|
@ -0,0 +1,148 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { format } from "d3";
|
||||
import classNames from "classnames";
|
||||
import Slider from "rc-slider";
|
||||
import "rc-slider/assets/index.css";
|
||||
import AppDispatcher from "../../common/app-dispatcher";
|
||||
|
||||
const WidgetSlider = (props) => {
|
||||
const [value, setValue] = useState("");
|
||||
const [unit, setUnit] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
let widget = { ...props.widget };
|
||||
widget.customProperties.simStartedSendValue = false;
|
||||
AppDispatcher.dispatch({
|
||||
type: "widgets/start-edit",
|
||||
token: props.token,
|
||||
data: widget,
|
||||
});
|
||||
}, [props.token, props.widget]);
|
||||
|
||||
useEffect(() => {
|
||||
// A simulation was started, make an update
|
||||
if (props.widget.customProperties.simStartedSendValue) {
|
||||
let widget = { ...props.widget };
|
||||
widget.customProperties.simStartedSendValue = false;
|
||||
AppDispatcher.dispatch({
|
||||
type: "widgets/start-edit",
|
||||
token: props.token,
|
||||
data: widget,
|
||||
});
|
||||
|
||||
// Send value without changing widget
|
||||
props.onInputChanged(widget.customProperties.value, "", "", false);
|
||||
}
|
||||
}, [props.token, props.widget, props.onInputChanged]);
|
||||
|
||||
useEffect(() => {
|
||||
let newValue = "";
|
||||
let newUnit = "";
|
||||
|
||||
if (
|
||||
props.widget.customProperties.hasOwnProperty("value") &&
|
||||
props.widget.customProperties.value !== value
|
||||
) {
|
||||
newValue = Number(props.widget.customProperties.value);
|
||||
} else if (value === "") {
|
||||
newValue = 0.0;
|
||||
}
|
||||
|
||||
if (props.widget.signalIDs.length > 0) {
|
||||
let signalID = props.widget.signalIDs[0];
|
||||
let signal = props.signals.find((sig) => sig.id === signalID);
|
||||
if (signal !== undefined) {
|
||||
newUnit = signal.unit;
|
||||
}
|
||||
}
|
||||
|
||||
if (newUnit) {
|
||||
setUnit(newUnit);
|
||||
}
|
||||
if (newValue !== "") {
|
||||
setValue(newValue);
|
||||
}
|
||||
}, [props.signals, props.widget, value]);
|
||||
|
||||
const valueIsChanging = (newValue) => {
|
||||
props.widget.customProperties.value = newValue;
|
||||
if (props.widget.customProperties.continous_update) {
|
||||
valueChanged(newValue, false);
|
||||
}
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
const valueChanged = (newValue, isFinalChange) => {
|
||||
if (props.onInputChanged) {
|
||||
props.onInputChanged(newValue, "value", newValue, isFinalChange);
|
||||
}
|
||||
};
|
||||
|
||||
let isVertical =
|
||||
props.widget.customProperties.orientation ===
|
||||
WidgetSlider.OrientationTypes.VERTICAL.value;
|
||||
|
||||
const fields = {
|
||||
name: props.widget.name,
|
||||
control: (
|
||||
<Slider
|
||||
min={props.widget.customProperties.rangeMin}
|
||||
max={props.widget.customProperties.rangeMax}
|
||||
step={props.widget.customProperties.step}
|
||||
value={value}
|
||||
disabled={props.editing}
|
||||
vertical={isVertical}
|
||||
onChange={valueIsChanging}
|
||||
onAfterChange={(v) => valueChanged(v, true)}
|
||||
/>
|
||||
),
|
||||
value: (
|
||||
<span className="signal-value">
|
||||
{format(".2f")(Number.parseFloat(value))}
|
||||
</span>
|
||||
),
|
||||
unit: <span className="signal-unit">{unit}</span>,
|
||||
};
|
||||
|
||||
const widgetClasses = classNames({
|
||||
"slider-widget": true,
|
||||
full: true,
|
||||
vertical: isVertical,
|
||||
horizontal: !isVertical,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{fields.name}
|
||||
{fields.value}
|
||||
{props.widget.customProperties.showUnit && fields.unit}
|
||||
</div>
|
||||
<div className={widgetClasses}>{fields.control}</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
WidgetSlider.OrientationTypes = {
|
||||
HORIZONTAL: { value: 0, name: "Horizontal" },
|
||||
VERTICAL: { value: 1, name: "Vertical" },
|
||||
};
|
||||
|
||||
export default WidgetSlider;
|
|
@ -1,136 +0,0 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { format } from 'd3';
|
||||
|
||||
import { Table, DataColumn } from '../../common/table';
|
||||
|
||||
class WidgetTable extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
rows: [],
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state){
|
||||
|
||||
let rows = [];
|
||||
let signalID, sig;
|
||||
for (signalID of props.widget.signalIDs) {
|
||||
for (sig of props.signals) {
|
||||
if (signalID === sig.id) {
|
||||
// sig is a selected signal, get data
|
||||
// determine ID of infrastructure component related to signal (via config)
|
||||
let icID = props.icIDs[sig.id]
|
||||
|
||||
// distinguish between input and output signals
|
||||
if (sig.direction === "out") {
|
||||
if (props.data[icID] != null && props.data[icID].output != null && props.data[icID].output.values != null) {
|
||||
if (props.data[icID].output.values[sig.index] !== undefined) {
|
||||
let data = props.data[icID].output.values[sig.index];
|
||||
rows.push({
|
||||
name: sig.name,
|
||||
unit: sig.unit,
|
||||
value: data[data.length - 1].y * sig.scalingFactor,
|
||||
scalingFactor: sig.scalingFactor
|
||||
});
|
||||
|
||||
} else {
|
||||
// no data available
|
||||
rows.push({
|
||||
name: sig.name,
|
||||
unit: sig.unit,
|
||||
value: NaN,
|
||||
scalingFactor: sig.scalingFactor
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if (sig.direction === "in") {
|
||||
if (props.data[icID] != null && props.data[icID].input != null && props.data[icID].input.values != null) {
|
||||
if (props.data[icID].input.values[sig.index] !== undefined) {
|
||||
let data = props.data[icID].input.values[sig.index];
|
||||
rows.push({
|
||||
name: sig.name,
|
||||
unit: sig.unit,
|
||||
value: data[data.length - 1].y * sig.scalingFactor,
|
||||
scalingFactor: sig.scalingFactor
|
||||
});
|
||||
} else {
|
||||
// no data available
|
||||
rows.push({
|
||||
name: sig.name,
|
||||
unit: sig.unit,
|
||||
value: NaN,
|
||||
scalingFactor: sig.scalingFactor
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} // sig is selected signal
|
||||
} // loop over props.signals
|
||||
} // loop over selected signals
|
||||
|
||||
return {rows: rows}
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
|
||||
let showScalingFactor;
|
||||
if (this.props.widget.customProperties.showScalingFactor !== undefined){ // this line ensures backwards compatibility with older versions of VILLASweb
|
||||
showScalingFactor = this.props.widget.customProperties.showScalingFactor;
|
||||
} else {
|
||||
showScalingFactor = true;
|
||||
}
|
||||
|
||||
let rows = this.state.rows;
|
||||
|
||||
if(rows.length === 0){
|
||||
rows.push({
|
||||
name: "no entries"
|
||||
})
|
||||
}
|
||||
|
||||
let columns = [
|
||||
<DataColumn key={1} title="Signal" dataKey="name" width={120} />,
|
||||
<DataColumn key={2} title="Value" dataKey="value" modifier={format('.4f')} />,
|
||||
];
|
||||
|
||||
let nextKey = 3;
|
||||
if (showScalingFactor) {
|
||||
columns.push(<DataColumn key={nextKey} title="Scale" dataKey="scalingFactor" modifier={format('.2f')}/>);
|
||||
nextKey++;
|
||||
}
|
||||
if (this.props.widget.customProperties.showUnit) {
|
||||
columns.push(<DataColumn key={nextKey} title="Unit" dataKey="unit"/>);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="table-widget" style={{width: this.props.widget.width, height: this.props.widget.height, overflowY: 'auto'}}>
|
||||
<Table data={rows}>
|
||||
{ columns }
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default WidgetTable;
|
111
src/widget/widgets/table.jsx
Normal file
111
src/widget/widgets/table.jsx
Normal file
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { format } from "d3";
|
||||
import { Table, DataColumn } from "../../common/table";
|
||||
|
||||
const WidgetTable = (props) => {
|
||||
const [rows, setRows] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
let newRows = [];
|
||||
let signalID, sig;
|
||||
for (signalID of props.widget.signalIDs) {
|
||||
for (sig of props.signals) {
|
||||
if (signalID === sig.id) {
|
||||
let icID = props.icIDs[sig.id];
|
||||
|
||||
let direction = sig.direction === "out" ? "output" : "input";
|
||||
if (
|
||||
props.data[icID] &&
|
||||
props.data[icID][direction] &&
|
||||
props.data[icID][direction].values
|
||||
) {
|
||||
if (props.data[icID][direction].values[sig.index] !== undefined) {
|
||||
let data = props.data[icID][direction].values[sig.index];
|
||||
newRows.push({
|
||||
name: sig.name,
|
||||
unit: sig.unit,
|
||||
value: data[data.length - 1].y * sig.scalingFactor,
|
||||
scalingFactor: sig.scalingFactor,
|
||||
});
|
||||
} else {
|
||||
newRows.push({
|
||||
name: sig.name,
|
||||
unit: sig.unit,
|
||||
value: NaN,
|
||||
scalingFactor: sig.scalingFactor,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newRows.length === 0) {
|
||||
newRows.push({ name: "no entries" });
|
||||
}
|
||||
|
||||
setRows(newRows);
|
||||
}, [props.widget.signalIDs, props.signals, props.icIDs, props.data]);
|
||||
|
||||
let showScalingFactor =
|
||||
props.widget.customProperties.showScalingFactor !== undefined
|
||||
? props.widget.customProperties.showScalingFactor
|
||||
: true;
|
||||
|
||||
let columns = [
|
||||
<DataColumn key={1} title="Signal" dataKey="name" width={120} />,
|
||||
<DataColumn
|
||||
key={2}
|
||||
title="Value"
|
||||
dataKey="value"
|
||||
modifier={format(".4f")}
|
||||
/>,
|
||||
];
|
||||
|
||||
let nextKey = 3;
|
||||
if (showScalingFactor) {
|
||||
columns.push(
|
||||
<DataColumn
|
||||
key={nextKey}
|
||||
title="Scale"
|
||||
dataKey="scalingFactor"
|
||||
modifier={format(".2f")}
|
||||
/>
|
||||
);
|
||||
nextKey++;
|
||||
}
|
||||
if (props.widget.customProperties.showUnit) {
|
||||
columns.push(<DataColumn key={nextKey} title="Unit" dataKey="unit" />);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="table-widget"
|
||||
style={{
|
||||
width: props.widget.width,
|
||||
height: props.widget.height,
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
<Table data={rows}>{columns}</Table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetTable;
|
Loading…
Add table
Reference in a new issue