mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-06-07 05:50:41 +02:00
frontend: adding components, rendering serialized data service class
This commit is contained in:
parent
8051775c07
commit
ab38f034b9
@ -1,14 +1,90 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Component, ComponentLabel } from './components/component';
|
||||||
|
import { ButtonComponent } from './components/button';
|
||||||
|
import { socket } from './socket';
|
||||||
|
|
||||||
// A simple functional component
|
type AttributeType = 'str' | 'bool' | 'float' | 'int' | 'method' | 'Subclass';
|
||||||
const App: React.FC = () => {
|
|
||||||
|
interface Attribute {
|
||||||
|
type: AttributeType;
|
||||||
|
value?: any;
|
||||||
|
readonly: boolean;
|
||||||
|
doc?: string | null;
|
||||||
|
parameters?: Record<string, string>;
|
||||||
|
async?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyData = Record<string, Attribute>;
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
const [data, setData] = useState<MyData | null>(null);
|
||||||
|
const [isConnected, setIsConnected] = useState(socket.connected);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Fetch data from the API when the component mounts
|
||||||
|
fetch('http://localhost:8001/service-properties')
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then(setData);
|
||||||
|
function onConnect() {
|
||||||
|
setIsConnected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDisconnect() {
|
||||||
|
setIsConnected(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onNotify(value: Record<string, any>) {
|
||||||
|
console.log(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.on('connect', onConnect);
|
||||||
|
socket.on('disconnect', onDisconnect);
|
||||||
|
socket.on('notify', onNotify);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off('connect', onConnect);
|
||||||
|
socket.off('disconnect', onDisconnect);
|
||||||
|
socket.off('notify', onNotify);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// While the data is loading
|
||||||
|
if (!data) {
|
||||||
|
return <p>Loading...</p>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="App">
|
||||||
<h1>Hello, world!</h1>
|
{Object.entries(data).map(([key, value]) => {
|
||||||
<p>Welcome to my React application. This is some test.</p>
|
if (value.type === 'bool') {
|
||||||
|
return (
|
||||||
|
<div key={key}>
|
||||||
|
<ButtonComponent
|
||||||
|
name={key}
|
||||||
|
docString={value.doc}
|
||||||
|
readOnly={value.readonly}
|
||||||
|
value={value.value}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (!value.async) {
|
||||||
|
return (
|
||||||
|
<div key={key}>
|
||||||
|
<ComponentLabel name={key} docString={value.doc} />
|
||||||
|
<Component
|
||||||
|
name={key}
|
||||||
|
value={value.value}
|
||||||
|
readOnly={value.readonly}
|
||||||
|
type={value.type}
|
||||||
|
docString={value.doc}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <div key={key}></div>;
|
||||||
|
}
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default App;
|
export default App;
|
44
frontend/src/components/button.tsx
Normal file
44
frontend/src/components/button.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React, { MouseEventHandler } from 'react';
|
||||||
|
import { OverlayTrigger, Badge, Button, Tooltip } from 'react-bootstrap';
|
||||||
|
|
||||||
|
interface ButtonComponentProps {
|
||||||
|
name: string;
|
||||||
|
fullname?: string;
|
||||||
|
value: boolean;
|
||||||
|
readOnly: boolean;
|
||||||
|
docString: string;
|
||||||
|
onToggle?: MouseEventHandler;
|
||||||
|
mapping?: [string, string]; // Enforce a tuple of two strings
|
||||||
|
}
|
||||||
|
|
||||||
|
const ButtonComponentRef = React.forwardRef<HTMLDivElement, ButtonComponentProps>(
|
||||||
|
(props, ref) => {
|
||||||
|
const { name, fullname, value, readOnly, docString, onToggle, mapping } = props;
|
||||||
|
|
||||||
|
const buttonName = mapping ? (value ? mapping[0] : mapping[1]) : name;
|
||||||
|
|
||||||
|
const tooltip = <Tooltip id="tooltip">{docString}</Tooltip>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'component boolean'} id={fullname} ref={ref}>
|
||||||
|
<Button
|
||||||
|
type={'button'}
|
||||||
|
variant={value ? 'success' : 'secondary'}
|
||||||
|
onMouseUp={onToggle}
|
||||||
|
disabled={readOnly}>
|
||||||
|
<p>{buttonName}</p>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{docString && (
|
||||||
|
<OverlayTrigger placement="bottom" overlay={tooltip}>
|
||||||
|
<Badge pill className="tooltip-trigger" bg="light" text="dark">
|
||||||
|
?
|
||||||
|
</Badge>
|
||||||
|
</OverlayTrigger>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ButtonComponent = React.memo(ButtonComponentRef);
|
65
frontend/src/components/component.tsx
Normal file
65
frontend/src/components/component.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface ComponentProps {
|
||||||
|
name: string;
|
||||||
|
value: any;
|
||||||
|
readOnly: boolean;
|
||||||
|
type: string;
|
||||||
|
docString: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ComponentLabel = ({
|
||||||
|
name,
|
||||||
|
docString
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
docString: string;
|
||||||
|
}) => {
|
||||||
|
return <label title={docString}>{name}</label>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Component = ({
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
readOnly,
|
||||||
|
type,
|
||||||
|
docString
|
||||||
|
}: ComponentProps) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'int':
|
||||||
|
case 'float':
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name={name}
|
||||||
|
value={value}
|
||||||
|
readOnly={readOnly}
|
||||||
|
title={docString}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'str':
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name={name}
|
||||||
|
value={value}
|
||||||
|
readOnly={readOnly}
|
||||||
|
title={docString}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'bool':
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name={name}
|
||||||
|
checked={value}
|
||||||
|
disabled={readOnly}
|
||||||
|
title={docString}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'method':
|
||||||
|
return <p>Method: {name}</p>;
|
||||||
|
default:
|
||||||
|
return <p>Unsupported type: {type}</p>;
|
||||||
|
}
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user