mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-04-20 08:20:02 +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",
|
"bootstrap": "^5.3.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-bootstrap": "^2.8.0",
|
"react-bootstrap": "^2.8.0",
|
||||||
|
"react-bootstrap-range-slider": "^3.0.8",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"socket.io-client": "^4.7.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": {
|
"node_modules/react-dev-utils": {
|
||||||
"version": "12.0.1",
|
"version": "12.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
|
||||||
@ -16459,16 +16474,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.1.6",
|
"version": "4.9.5",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||||
"integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
|
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.17"
|
"node": ">=4.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/unbox-primitive": {
|
"node_modules/unbox-primitive": {
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"bootstrap": "^5.3.0",
|
"bootstrap": "^5.3.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-bootstrap": "^2.8.0",
|
"react-bootstrap": "^2.8.0",
|
||||||
|
"react-bootstrap-range-slider": "^3.0.8",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"socket.io-client": "^4.7.1",
|
"socket.io-client": "^4.7.1",
|
||||||
|
@ -3,8 +3,16 @@ import { Component, ComponentLabel } from './components/component';
|
|||||||
import { ButtonComponent } from './components/ButtonComponent';
|
import { ButtonComponent } from './components/ButtonComponent';
|
||||||
import { socket } from './socket';
|
import { socket } from './socket';
|
||||||
import { NumberComponent } from './components/NumberComponent';
|
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;
|
type ValueType = boolean | string | number | object;
|
||||||
interface Attribute {
|
interface Attribute {
|
||||||
@ -145,6 +153,21 @@ const App = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</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) {
|
} else if (!value.async) {
|
||||||
return (
|
return (
|
||||||
<div key={key}>
|
<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