frontend: adding SliderComponent

This commit is contained in:
Mose Müller 2023-08-02 12:06:20 +02:00
parent 44639222bf
commit fa0d69feb8
4 changed files with 186 additions and 5 deletions

View File

@ -14,6 +14,7 @@
"bootstrap": "^5.3.0",
"react": "^18.2.0",
"react-bootstrap": "^2.8.0",
"react-bootstrap-range-slider": "^3.0.8",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"socket.io-client": "^4.7.1",
@ -14403,6 +14404,20 @@
}
}
},
"node_modules/react-bootstrap-range-slider": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/react-bootstrap-range-slider/-/react-bootstrap-range-slider-3.0.8.tgz",
"integrity": "sha512-FpDd1J1BW23jNN3fXmpy5nNDJ3PwMZ2/0dNse9RORwQ/z2rmpMQp/g6iNRpW6SjQkLKyGeNHyctK6dP+3zUXQA==",
"dependencies": {
"classnames": "^2.3.1",
"prop-types": "^15.7.2"
},
"peerDependencies": {
"react": ">=17.0.0",
"react-bootstrap": ">=1.0.0",
"react-dom": ">=17.0.0"
}
},
"node_modules/react-dev-utils": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
@ -16459,16 +16474,16 @@
}
},
"node_modules/typescript": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
"integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
"node": ">=4.2.0"
}
},
"node_modules/unbox-primitive": {

View File

@ -9,6 +9,7 @@
"bootstrap": "^5.3.0",
"react": "^18.2.0",
"react-bootstrap": "^2.8.0",
"react-bootstrap-range-slider": "^3.0.8",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"socket.io-client": "^4.7.1",

View File

@ -3,8 +3,16 @@ import { Component, ComponentLabel } from './components/component';
import { ButtonComponent } from './components/ButtonComponent';
import { socket } from './socket';
import { NumberComponent } from './components/NumberComponent';
import { SliderComponent } from './components/SliderComponent';
type AttributeType = 'str' | 'bool' | 'float' | 'int' | 'method' | 'Subclass';
type AttributeType =
| 'str'
| 'bool'
| 'float'
| 'int'
| 'method'
| 'Subclass'
| 'NumberSlider';
type ValueType = boolean | string | number | object;
interface Attribute {
@ -145,6 +153,21 @@ const App = () => {
/>
</div>
);
} else if (value.type === 'NumberSlider') {
return (
<div key={key}>
<SliderComponent
name={key}
parent_path="DataService"
docString={value.doc}
readOnly={value.readonly}
value={value.value['value']['value']}
min={value.value['min']['value']}
max={value.value['max']['value']}
stepSize={value.value['step_size']['value']}
/>
</div>
);
} else if (!value.async) {
return (
<div key={key}>

View File

@ -0,0 +1,142 @@
import React, { useEffect, useRef, useState } from 'react';
import {
OverlayTrigger,
Badge,
Tooltip,
InputGroup,
Form,
Stack,
Row,
Col,
SplitButton,
Dropdown,
Button,
Collapse
} from 'react-bootstrap';
import { socket } from '../socket';
import './NumberComponent.css';
import RangeSlider from 'react-bootstrap-range-slider';
interface SliderComponentProps {
name: string;
min: number;
max: number;
parent_path?: string;
value: number;
readOnly: boolean;
docString: string;
stepSize: number;
}
export const SliderComponent = React.memo((props: SliderComponentProps) => {
const renderCount = useRef(0);
const [open, setOpen] = useState(false);
useEffect(() => {
renderCount.current++;
});
const { name, parent_path, value, readOnly, docString } = props;
const [min, setMin] = useState(props.min);
const [max, setMax] = useState(props.max);
const [stepSize, setStepSize] = useState(props.stepSize);
const tooltip = <Tooltip id="tooltip">{docString}</Tooltip>;
const socketEmit = (
newNumber: number,
min: number = props.min,
max: number = props.max,
stepSize: number = props.stepSize
) => {
socket.emit('frontend_update', {
name: name,
value: { value: newNumber, min: min, max: max, step_size: stepSize }
});
};
const handleOnChange = (event, newNumber: number) => {
socketEmit(newNumber, min, max, stepSize);
};
const handleValueChange = (newValue: number, valueType: string) => {
switch (valueType) {
case 'min':
setMin(newValue);
break;
case 'max':
setMax(newValue);
break;
case 'stepSize':
setStepSize(newValue);
break;
default:
break;
}
socketEmit(value, min, max, stepSize);
};
return (
<div className={'component boolean'} id={parent_path.concat('.' + name)}>
<p>Render count: {renderCount.current}</p>
<Row>
<Col className="col-2 d-flex align-items-center">
<InputGroup.Text style={{ height: '65px' }}>{name}</InputGroup.Text>
<Form.Group>
<RangeSlider
disabled={readOnly}
value={value}
onChange={(event, newNumber) => handleOnChange(event, newNumber)}
min={min}
max={max}
step={stepSize}
tooltip={'off'}
/>
<Form.Control type="text" value={value} name={name} disabled={true} />
</Form.Group>
</Col>
</Row>
<Row xs="auto">
<Button
onClick={() => setOpen(!open)}
aria-controls="slider-settings"
aria-expanded={open}>
Settings
</Button>
<Collapse in={open}>
<div id="slider-settings">
<Form.Group>
<Form.Label>Min Value</Form.Label>
<Form.Control
type="number"
value={min}
onChange={(e) => handleValueChange(Number(e.target.value), 'min')}
/>
<Form.Label>Max Value</Form.Label>
<Form.Control
type="number"
value={max}
onChange={(e) => handleValueChange(Number(e.target.value), 'max')}
/>
<Form.Label>Step Size</Form.Label>
<Form.Control
type="number"
value={stepSize}
onChange={(e) => handleValueChange(Number(e.target.value), 'stepSize')}
/>
</Form.Group>
</div>
</Collapse>
</Row>
{docString && (
<OverlayTrigger placement="bottom" overlay={tooltip}>
<Badge pill className="tooltip-trigger" bg="light" text="dark">
?
</Badge>
</OverlayTrigger>
)}
</div>
);
});