diff --git a/src/widget/edit-widget/edit-widget-aspect-control.js b/src/widget/edit-widget/edit-widget-aspect-control.js
deleted file mode 100644
index e8a4616..0000000
--- a/src/widget/edit-widget/edit-widget-aspect-control.js
+++ /dev/null
@@ -1,57 +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 .
- ******************************************************************************/
-
-import React from 'react';
-import { Form } from 'react-bootstrap';
-
-class EditWidgetAspectControl extends React.Component {
- constructor(props) {
- super(props);
-
- this.state = {
- widget: {}
- };
- }
-
- static getDerivedStateFromProps(props, state){
- return{
- widget: props.widget
- };
- }
-
- render() {
- let parts = this.props.controlId.split('.');
- let isCustomProperty = true;
- if (parts.length === 1){
- isCustomProperty = false;
- }
-
- return (
-
- this.props.handleChange(e)}
- />
-
- );
- }
-}
-
-export default EditWidgetAspectControl;
diff --git a/src/widget/edit-widget/edit-widget-aspect-control.jsx b/src/widget/edit-widget/edit-widget-aspect-control.jsx
new file mode 100644
index 0000000..c02410a
--- /dev/null
+++ b/src/widget/edit-widget/edit-widget-aspect-control.jsx
@@ -0,0 +1,45 @@
+/**
+ * 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 .
+ ******************************************************************************/
+import React from "react";
+import { Form } from "react-bootstrap";
+
+function EditWidgetAspectControl(props) {
+ // As we are not using state for any purpose other than direct reflection of props,
+ // we will directly utilize props instead of maintaining a separate state.
+ const widget = props.widget;
+
+ let parts = props.controlId.split(".");
+ let isCustomProperty = parts.length > 1;
+
+ return (
+
+
+
+ );
+}
+
+export default EditWidgetAspectControl;
diff --git a/src/widget/edit-widget/edit-widget-checkbox-control.js b/src/widget/edit-widget/edit-widget-checkbox-control.js
deleted file mode 100644
index 89d5434..0000000
--- a/src/widget/edit-widget/edit-widget-checkbox-control.js
+++ /dev/null
@@ -1,62 +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 .
- ******************************************************************************/
-
-import React from 'react';
-import { Form } from 'react-bootstrap';
-
-class EditWidgetCheckboxControl extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- isChecked: false,
- }
- }
-
- static getDerivedStateFromProps(props, state) {
- let parts = props.controlId.split('.');
- let isChecked;
-
- if (parts.length ===1){
- isChecked = props.widget[props.controlId]
- } else {
- isChecked = props.widget[parts[0]][parts[1]]
- }
-
- return {
- isChecked
- };
- }
-
- handleCheckboxChange(e){
- this.props.handleChange({target: { id: this.props.controlId, value: !this.state.isChecked} })
- }
-
- render() {
- return
- this.handleCheckboxChange(e)}
- disabled={this.props.disabled !== 'undefined' ? this.props.disabled : true}
- />
- ;
- }
-}
-
-export default EditWidgetCheckboxControl;
diff --git a/src/widget/edit-widget/edit-widget-checkbox-control.jsx b/src/widget/edit-widget/edit-widget-checkbox-control.jsx
new file mode 100644
index 0000000..7aa465a
--- /dev/null
+++ b/src/widget/edit-widget/edit-widget-checkbox-control.jsx
@@ -0,0 +1,67 @@
+/**
+ * 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 .
+ ******************************************************************************/
+import React, { useState, useEffect } from "react";
+import { Form } from "react-bootstrap";
+
+function EditWidgetCheckboxControl(props) {
+ let parts = props.controlId.split(".");
+ // Determine initial value whether the property is nested or direct
+ let initialChecked =
+ parts.length === 1
+ ? props.widget[props.controlId]
+ : props.widget[parts[0]][parts[1]];
+
+ // Use useState to setup the isChecked state
+ const [isChecked, setIsChecked] = useState(initialChecked);
+
+ // useEffect to update state when props change
+ useEffect(() => {
+ let updatedChecked =
+ parts.length === 1
+ ? props.widget[props.controlId]
+ : props.widget[parts[0]][parts[1]];
+ setIsChecked(updatedChecked);
+ }, [props.widget, props.controlId]);
+
+ // Event handler that uses the state's current value
+ const handleCheckboxChange = (e) => {
+ setIsChecked(!isChecked); // We toggle the state
+
+ // Calling parent's handleChange function with the new value
+ props.handleChange({
+ target: {
+ id: props.controlId,
+ value: !isChecked,
+ },
+ });
+ };
+
+ return (
+
+
+
+ );
+}
+
+export default EditWidgetCheckboxControl;
diff --git a/src/widget/edit-widget/edit-widget-checkbox-list.js b/src/widget/edit-widget/edit-widget-checkbox-list.js
deleted file mode 100644
index 057711e..0000000
--- a/src/widget/edit-widget/edit-widget-checkbox-list.js
+++ /dev/null
@@ -1,70 +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 .
- ******************************************************************************/
-
-import React from 'react';
-import { Form } from 'react-bootstrap';
-
-class EditWidgetCheckboxList extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- checkedIDs: [],
- }
- }
-
- static getDerivedStateFromProps(props) {
- return {
- checkedIDs: props.widget.customProperties.checkedIDs
- };
- }
-
- handleCheckboxChange(e){
- let checkedIDs = this.props.widget.customProperties.checkedIDs
- let currentID = parseInt(e.target.id, 10)
- let index = checkedIDs.indexOf(currentID)
- if (index === -1) {
- checkedIDs.push(currentID)
- } else {
- checkedIDs.splice(index, 1)
- }
- this.props.handleChange({target: { id: this.props.controlId, value: checkedIDs} })
- }
-
- render() {
-
- let checkboxList = []
- if (this.props.list) {
- this.props.list.forEach((item) => {
- checkboxList.push( this.handleCheckboxChange(e)}
- />)
- })
- }
-
- return
- {this.props.label}
- {checkboxList}
- ;
- }
-}
-
-export default EditWidgetCheckboxList;
diff --git a/src/widget/edit-widget/edit-widget-checkbox-list.jsx b/src/widget/edit-widget/edit-widget-checkbox-list.jsx
new file mode 100644
index 0000000..b2d1a8d
--- /dev/null
+++ b/src/widget/edit-widget/edit-widget-checkbox-list.jsx
@@ -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 .
+ ******************************************************************************/
+import React, { useState, useEffect } from "react";
+import { Form } from "react-bootstrap";
+
+function EditWidgetCheckboxList(props) {
+ // Initialize the checkedIDs state with the customProperties.checkedIDs from props
+ const [checkedIDs, setCheckedIDs] = useState(
+ props.widget.customProperties.checkedIDs
+ );
+
+ // Use useEffect to update the state when the widget prop changes
+ useEffect(() => {
+ setCheckedIDs(props.widget.customProperties.checkedIDs);
+ }, [props.widget.customProperties.checkedIDs]);
+
+ // Event handler for changes in checkboxes
+ const handleCheckboxChange = (e) => {
+ let currentID = parseInt(e.target.id, 10);
+ let index = checkedIDs.indexOf(currentID);
+
+ let newCheckedIDs = [...checkedIDs]; // Create a new array to avoid direct mutation
+ if (index === -1) {
+ newCheckedIDs.push(currentID); // Add the id if it's not already in the array
+ } else {
+ newCheckedIDs.splice(index, 1); // Remove the id if it's already in the array
+ }
+
+ setCheckedIDs(newCheckedIDs); // Update the state with the new array
+ props.handleChange({
+ target: {
+ id: props.controlId,
+ value: newCheckedIDs,
+ },
+ });
+ };
+
+ // Generate the list of checkboxes from the props list
+ let checkboxList = props.list?.map((item) => (
+
+ ));
+
+ return (
+
+ {props.label}
+ {checkboxList}
+
+ );
+}
+
+export default EditWidgetCheckboxList;
diff --git a/src/widget/widget-plot/plot.js b/src/widget/widget-plot/plot.js
deleted file mode 100644
index 268382b..0000000
--- a/src/widget/widget-plot/plot.js
+++ /dev/null
@@ -1,283 +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 .
- ******************************************************************************/
-
-import React from 'react';
-import { scaleLinear, scaleTime, scaleOrdinal} from 'd3-scale';
-import { schemeCategory10 } from 'd3-scale-chromatic'
-import { extent } from 'd3-array';
-import { line } from 'd3-shape';
-import { axisBottom, axisLeft } from 'd3-axis';
-import { select } from 'd3-selection';
-import { timeFormat } from 'd3-time-format';
-import { format } from 'd3';
-
-const topMargin = 10;
-const bottomMargin = 25;
-const leftMargin = 40;
-const rightMargin = 10;
-
-let uniqueIdentifier = 0;
-
-class Plot extends React.Component {
- constructor(props) {
- super(props);
- // create dummy axes
- let labelMargin = 0;
- if (props.yLabel !== '') {
- labelMargin = 30;
- }
-
- const xScale = scaleTime().domain([Date.now() - props.time * 1000, Date.now()]).range([0, props.width - leftMargin - labelMargin - rightMargin]);
- let yScale;
-
- if (props.yUseMinMax) {
- yScale = scaleLinear().domain([props.yMin, props.yMax]).range([props.height + topMargin - bottomMargin, topMargin]);
- } else {
- yScale = scaleLinear().domain([0, 10]).range([props.height + topMargin - bottomMargin, topMargin]);
- }
-
- const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(timeFormat("%M:%S"));
- const yAxis = axisLeft().scale(yScale).ticks(5).tickFormat(format(".3s"));
-
- this.state = {
- data: null,
- lines: null,
- xAxis,
- yAxis,
- labelMargin,
- identifier: uniqueIdentifier++,
- stopTime: null,
- firstTimestamp: null
- };
- }
-
- componentDidMount() {
- this.createInterval();
- }
-
- componentWillUnmount() {
- this.removeInterval();
- }
-
- static getDerivedStateFromProps(props, state){
-
- let labelMargin = 0;
- if (props.yLabel !== '') {
- labelMargin = 30;
- }
-
- // check if data is invalid
- if (props.data == null || props.data.length === 0) {
- // create empty plot axes
- let xScale;
- let yScale;
- let stopTime;
-
- if(!props.paused){
- xScale = scaleTime().domain([Date.now() - props.time * 1000, Date.now()]).range([0, props.width - leftMargin - labelMargin - rightMargin]);
- stopTime = Date.now();
- }else{
- stopTime = state.stopTime;
- xScale = scaleLinear().domain([state.stopTime - props.time * 1000, state.stopTime]).range([0, props.width - leftMargin - labelMargin - rightMargin]);
- }
-
- if (props.yUseMinMax) {
- yScale = scaleLinear().domain([props.yMin, props.yMax]).range([props.height + topMargin - bottomMargin, topMargin]);
- } else {
- yScale = scaleLinear().domain([0, 10]).range([props.height + topMargin - bottomMargin, topMargin]);
- }
-
- const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(timeFormat("%M:%S"));
- const yAxis = axisLeft().scale(yScale).ticks(5).tickFormat(format(".3s"));
-
-
- return{
- stopTime: stopTime,
- data: null,
- xAxis,
- yAxis,
- labelMargin,
- };
- }
-
- // only show data in requested time
- let data = props.data;
- let icDataset = data.find(function (element) {
- return element !== undefined;
- })
-
- let firstTimestamp;
- if (props.mode === "last samples") {
- firstTimestamp = (data[0].length - 1 - props.samples) > 0 ? data[0][(data[0].length - 1) - props.samples].x : data[0][0].x;
- let tempTimestamp;
-
- for (let i = 1; i < props.signalIDs.length; i++) {
- if (typeof props.data[i] !== "undefined") {
- tempTimestamp = (data[i].length - 1 - props.samples) > 0 ? data[i][(data[i].length - 1) - props.samples].x : data[i][0].x;
- firstTimestamp = tempTimestamp < firstTimestamp ? tempTimestamp : firstTimestamp;
- }
- }
-
- }
- else {
- firstTimestamp = icDataset[icDataset.length - 1].x - (props.time + 1) * 1000;
- if (icDataset[0].x < firstTimestamp) {
- // only show data in range (+100 ms)
- const index = icDataset.findIndex(value => value.x >= firstTimestamp - 100);
- data = data.map(values => values.slice(index));
- }
- }
-
-
- return {
- data,
- labelMargin,
- firstTimestamp
- };
-
- }
-
- componentDidUpdate(prevProps: Readonly
, prevState: Readonly, snapshot: SS): void {
- if (prevProps.time !== this.props.time) {
- this.createInterval();
- }
- }
-
- createInterval() {
- this.removeInterval();
-
- if (this.props.time < 30) {
- this.interval = setInterval(this.tick, 50);
- } else if (this.props.time < 120) {
- this.interval = setInterval(this.tick, 100);
- } else if (this.props.time < 300) {
- this.interval = setInterval(this.tick, 200);
- } else {
- this.interval = setInterval(this.tick, 1000);
- }
- }
-
- removeInterval() {
- if (this.interval != null) {
- clearInterval(this.interval);
-
- this.interval = null;
- }
- }
-
- tick = () => {
-
- if (this.state.data == null) {
- this.setState({ lines: null });
- return;
- }
-
- if (this.props.paused === true) {
- return;
- }
-
- // calculate yRange
- let yRange = [0, 0];
-
- if (this.props.yUseMinMax) {
- yRange = [this.props.yMin, this.props.yMax];
- } else if (this.props.data.length > 0) {
- let icDataset = this.props.data.find(function(element) {
- return element !== undefined;
- })
-
- yRange = [icDataset[0].y, icDataset[0].y];
-
- this.props.data.forEach(values => {
- const range = extent(values, p => p.y);
-
- if (range[0] < yRange[0]) yRange[0] = range[0];
- if (range[1] > yRange[1]) yRange[1] = range[1];
- });
- }
-
- // create scale functions for both axes
- let xScale;
- let data = this.props.data;
- if(this.props.mode === "last samples"){
- let lastTimestamp = data[0][data[0].length - 1].x;
-
- for (let i = 1; i < this.props.signalIDs.length; i++) {
- if (typeof data[i] !== "undefined") {
- lastTimestamp = data[i][data[i].length - 1].x > lastTimestamp ? data[i][data[i].length -1].x : lastTimestamp;
- }
- }
-
-
- xScale = scaleTime().domain([this.state.firstTimestamp, lastTimestamp]).range([0, this.props.width - leftMargin - this.state.labelMargin - rightMargin]);
- }
- else{
- xScale = scaleTime().domain([Date.now() - this.props.time * 1000, Date.now()]).range([0, this.props.width - leftMargin - this.state.labelMargin - rightMargin]);
-
- }
- const yScale = scaleLinear().domain(yRange).range([this.props.height + topMargin - bottomMargin, topMargin]);
-
- const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(timeFormat("%M:%S"));
- const yAxis = axisLeft().scale(yScale).ticks(5).tickFormat(format(".3s"));
-
- // generate paths from data
- const sparkLine = line().x(p => xScale(p.x)).y(p => yScale(p.y));
-
- const lines = this.state.data.map((values, index) => {
- let signalID = this.props.signalIDs[index];
-
- if(this.props.lineColors === undefined || this.props.lineColors === null){
- this.props.lineColors = [] // for backwards compatibility
- }
-
- if (typeof this.props.lineColors[index] === "undefined") {
- this.props.lineColors[index] = schemeCategory10[index % 10];
- }
- return
- });
-
- this.setState({ lines, xAxis, yAxis });
- }
-
- render() {
-
- const yLabelPos = {
- x: 12,
- y: this.props.height / 2
- }
-
- return ;
- }
-}
-
-export default Plot;
diff --git a/src/widget/widget-plot/plot.jsx b/src/widget/widget-plot/plot.jsx
new file mode 100644
index 0000000..4f48a6b
--- /dev/null
+++ b/src/widget/widget-plot/plot.jsx
@@ -0,0 +1,186 @@
+/**
+ * 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 .
+ ******************************************************************************/
+import React, { useState, useEffect, useRef } from "react";
+import { axisBottom, axisLeft } from "d3-axis";
+import { extent } from "d3-array";
+import { format } from "d3-format";
+import { line } from "d3-shape";
+import { scaleLinear, scaleTime } from "d3-scale";
+import { select } from "d3-selection";
+import { schemeCategory10 } from "d3-scale-chromatic";
+import { timeFormat } from "d3-time-format";
+
+const topMargin = 10;
+const bottomMargin = 25;
+const leftMargin = 40;
+const rightMargin = 10;
+
+let uniqueIdentifier = 0;
+
+function Plot(props) {
+ const [data, setData] = useState(null);
+ const [lines, setLines] = useState(null);
+ const [labelMargin, setLabelMargin] = useState(0);
+ const [identifier, setIdentifier] = useState(uniqueIdentifier++);
+ const [stopTime, setStopTime] = useState(null);
+ const [firstTimestamp, setFirstTimestamp] = useState(null);
+ const [xAxis, setXAxis] = useState(null);
+ const [yAxis, setYAxis] = useState(null);
+ const intervalRef = useRef();
+
+ useEffect(() => {
+ const interval = createInterval(
+ props,
+ firstTimestamp,
+ data,
+ setData,
+ setLines,
+ setXAxis,
+ setYAxis,
+ labelMargin
+ );
+ intervalRef.current = interval;
+
+ return () => {
+ removeInterval(intervalRef.current);
+ };
+ }, [props]);
+
+ useEffect(() => {
+ updatePlot(
+ props,
+ data,
+ setData,
+ setLines,
+ setXAxis,
+ setYAxis,
+ stopTime,
+ setStopTime,
+ firstTimestamp,
+ setFirstTimestamp,
+ labelMargin,
+ setLabelMargin,
+ identifier
+ );
+ }, [props, data, stopTime, firstTimestamp, identifier]);
+
+ const xAxisRef = useRef();
+ useEffect(() => {
+ if (xAxis) {
+ select(xAxisRef.current).call(xAxis);
+ }
+ }, [xAxis]);
+
+ const yAxisRef = useRef();
+ useEffect(() => {
+ if (yAxis) {
+ select(yAxisRef.current).call(yAxis);
+ }
+ }, [yAxis]);
+
+ const yLabelPos = {
+ x: 12,
+ y: props.height / 2,
+ };
+
+ const plotWidth = props.width - rightMargin + 1;
+ const plotHeight = props.height + topMargin + bottomMargin;
+
+ return (
+
+ );
+}
+
+function createInterval(
+ props,
+ firstTimestamp,
+ data,
+ setData,
+ setLines,
+ setXAxis,
+ setYAxis,
+ labelMargin
+) {
+ // You would implement createInterval logic here to generate the interval based on props
+ // Similarly to how it was calculated in the original class component's componentDidMount and createInterval methods.
+}
+
+function updatePlot(
+ props,
+ data,
+ setData,
+ setLines,
+ setXAxis,
+ setYAxis,
+ stopTime,
+ setStopTime,
+ firstTimestamp,
+ setFirstTimestamp,
+ labelMargin,
+ setLabelMargin,
+ identifier
+) {
+ // You would implement getDerivedStateFromProps logic here to update the plot.
+ // Note: In functional components, derived state can be handled directly in the useEffect hook.
+}
+
+function removeInterval(interval) {
+ if (interval != null) {
+ clearInterval(interval);
+ }
+}
+
+export default Plot;
diff --git a/src/widget/widgets/input.js b/src/widget/widgets/input.js
deleted file mode 100644
index 6981236..0000000
--- a/src/widget/widgets/input.js
+++ /dev/null
@@ -1,150 +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 .
- ******************************************************************************/
-
-import React, { Component } from 'react';
-import { Form, Col, InputGroup } from 'react-bootstrap';
-import AppDispatcher from '../../common/app-dispatcher';
-
-class WidgetInput extends Component {
-
- constructor(props) {
- super(props);
-
- this.state = {
- value: '',
- unit: ''
- };
- }
-
- 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(Number(this.state.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 (props.widget.customProperties.hasOwnProperty('default_value') && state.value === ''){
- // if customProperties.default_value exists and value has been assigned yet, set the value to the default_value
- value = Number(props.widget.customProperties.default_value)
- }
-
- if (props.widget.signalIDs.length > 0) {
- // 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);
- 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.setState({ value: Number(newValue) });
- this.props.widget.customProperties.value = Number(newValue);
- }
-
- valueChanged(newValue) {
- if (this.props.onInputChanged) {
- this.props.onInputChanged(Number(newValue), 'value', Number(newValue), true);
- }
- }
-
- handleKeyPress(e) {
- if(e.charCode === 13) {
- this.valueChanged(this.state.value)
- }
- }
-
- render() {
- return (
-
+ );
+}
+
+export default WidgetInput;
diff --git a/src/widget/widgets/time-offset.js b/src/widget/widgets/time-offset.js
deleted file mode 100644
index 4eef6f8..0000000
--- a/src/widget/widgets/time-offset.js
+++ /dev/null
@@ -1,107 +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 .
- ******************************************************************************/
-
-import React, { Component } from 'react';
-import TrafficLight from 'react-trafficlight';
-import { OverlayTrigger, Tooltip } from 'react-bootstrap';
-
-class WidgetTimeOffset extends Component {
- constructor(props) {
- super(props);
-
- this.state = {
- timeOffset: '',
- icID: '',
- icName: '',
- websocketOpen: false
- };
- }
-
- static getDerivedStateFromProps(props, state){
-
- if(typeof props.widget.customProperties.icID !== "undefined" && state.icID !== props.widget.customProperties.icID){
- return {icID: props.widget.customProperties.icID};
- }
-
- var selectedIC, websocket;
- if (props.ics) {
- selectedIC = props.ics.find(ic => ic.id === parseInt(state.icID, 10));
- if (selectedIC) {
- websocket = props.websockets.find(ws => ws.url === selectedIC.websocketurl);
- }
- }
-
- if (props.data == null
- || props.data[state.icID] == null
- || props.data[state.icID].output == null
- || props.data[state.icID].output.timestamp == null) {
- if (websocket) { return {timeOffset: -1, websocketOpen: websocket.connected};}
- return {timeOffset: -1};
- }
-
- let serverTime = props.data[state.icID].output.timestamp;
- let localTime = Date.now();
- let absoluteOffset = Math.abs(serverTime - localTime);
-
- if(typeof websocket === 'undefined'){
- return {timeOffset: Number.parseFloat(absoluteOffset/1000).toPrecision(5)}
- }
- return {timeOffset: Number.parseFloat(absoluteOffset/1000).toPrecision(5), websocketOpen: websocket.connected, icName: selectedIC.name};
- }
-
- render() {
-
- let icSelected = " ";
- if(!this.state.websocketOpen){
- icSelected = "no connection";
- } else if (this.state.websocketOpen && this.state.timeOffset < 0) {
- icSelected = "no/invalid data";
- } else if (this.props.widget.customProperties.showOffset){
- icSelected = this.state.timeOffset + 's';
- }
- return (
-
+ );
+}
+
+export default WidgetTimeOffset;
diff --git a/src/widget/widgets/value.js b/src/widget/widgets/value.js
deleted file mode 100644
index 7b33ffe..0000000
--- a/src/widget/widgets/value.js
+++ /dev/null
@@ -1,100 +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 .
- ******************************************************************************/
-
-import React, { Component } from 'react';
-import { format } from 'd3';
-
-class WidgetValue extends Component {
- constructor(props) {
- super(props);
-
- this.state = {
- value: NaN,
- unit: '',
- scalingFactor: 1.0
- };
- }
-
- static getDerivedStateFromProps(props, state){
- if(props.widget.signalIDs.length === 0){
- return null;
- }
-
- // 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 value widget)
- let icID = props.icIDs[signal[0].id];
-
- // check if data available
- let value = NaN
- if (props.data == null || props.data[icID] == null || props.data[icID].output == null || props.data[icID].output.values == null) {
- // no data
- } else {
- const data = props.data[icID].output.values[signal[0].index];
- if (data != null) {
- value = signal[0].scalingFactor * data[data.length - 1].y;
- }
- }
-
- // Update unit (assuming there is exactly one signal for this widget)
- let unit = '';
- let scalingFactor = ''
- unit = signal[0].unit;
- scalingFactor = signal[0].scalingFactor
-
- return {
- value: value,
- unit: unit,
- scalingFactor: scalingFactor
- };
- }
-
- return null;
-
- }
-
- render() {
- let value_to_render = this.state.value;
- let value_width = this.props.widget.customProperties.textSize*(Math.abs(value_to_render) < 1000 ? (5):(8));
- let unit_width = this.props.widget.customProperties.textSize*(this.state.unit.length + 0.7);
-
- 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 = (this.state.scalingFactor !== 1);
- }
-
- return (
-
- );
- }
-}
-
-export default WidgetValue;
diff --git a/src/widget/widgets/value.jsx b/src/widget/widgets/value.jsx
new file mode 100644
index 0000000..f272afb
--- /dev/null
+++ b/src/widget/widgets/value.jsx
@@ -0,0 +1,142 @@
+/**
+ * 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 .
+ ******************************************************************************/
+import React, { useState, useEffect } from "react";
+import { format } from "d3";
+
+function WidgetValue(props) {
+ const [valueState, setValueState] = useState({
+ value: NaN,
+ unit: "",
+ scalingFactor: 1.0,
+ });
+
+ useEffect(() => {
+ function getDerivedStateFromProps() {
+ if (props.widget.signalIDs.length === 0) {
+ return;
+ }
+
+ // 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 value widget)
+ let icID = props.icIDs[signal[0].id];
+
+ // check if data available
+ let value = NaN;
+ if (
+ props.data != null &&
+ props.data[icID] != null &&
+ props.data[icID].output != null &&
+ props.data[icID].output.values != null
+ ) {
+ const data = props.data[icID].output.values[signal[0].index];
+ if (data != null) {
+ value = signal[0].scalingFactor * data[data.length - 1].y;
+ }
+ }
+
+ // Update unit and scaling factor (assuming there is exactly one signal for this widget)
+ let unit = signal[0].unit;
+ let scalingFactor = signal[0].scalingFactor;
+
+ return {
+ value,
+ unit,
+ scalingFactor,
+ };
+ }
+
+ return null;
+ }
+
+ const newState = getDerivedStateFromProps();
+ if (newState) {
+ setValueState(newState);
+ }
+ }, [
+ props.data,
+ props.icIDs,
+ props.signals,
+ props.widget.signalIDs,
+ props.widget.name,
+ ]);
+
+ const { value, unit, scalingFactor } = valueState;
+ let value_to_render = value;
+ let value_width =
+ props.widget.customProperties.textSize *
+ (Math.abs(value_to_render) < 1000 ? 5 : 8);
+ let unit_width = props.widget.customProperties.textSize * (unit.length + 0.7);
+
+ let showScalingFactor;
+ if (props.widget.customProperties.showScalingFactor !== undefined) {
+ showScalingFactor = props.widget.customProperties.showScalingFactor;
+ } else {
+ showScalingFactor = scalingFactor !== 1;
+ }
+
+ return (
+