mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-04-20 00:10:03 +02:00
frontend: adding SliderComponent
This commit is contained in:
parent
44639222bf
commit
fa0d69feb8
23
frontend/package-lock.json
generated
23
frontend/package-lock.json
generated
@ -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": {
|
||||
|
@ -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",
|
||||
|
@ -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}>
|
||||
|
142
frontend/src/components/SliderComponent.tsx
Normal file
142
frontend/src/components/SliderComponent.tsx
Normal 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>
|
||||
);
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user