frontend: adding components, rendering serialized data service class

This commit is contained in:
Mose Müller 2023-08-02 12:06:19 +02:00
parent 8051775c07
commit ab38f034b9
3 changed files with 193 additions and 8 deletions

View File

@ -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
const App: React.FC = () => {
type AttributeType = 'str' | 'bool' | 'float' | 'int' | 'method' | 'Subclass';
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 (
<div>
<h1>Hello, world!</h1>
<p>Welcome to my React application. This is some test.</p>
<div className="App">
{Object.entries(data).map(([key, value]) => {
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>
);
};
export default App;
export default App;

View 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);

View 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>;
}
};