mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-04-19 16:10:01 +02:00
feat: adding instant_update switch
This commit is contained in:
parent
80fe1051f1
commit
5cb30688cf
@ -1,4 +1,5 @@
|
||||
import { useEffect, useReducer } from 'react';
|
||||
import { useEffect, useReducer, useState } from 'react';
|
||||
import { Form } from 'react-bootstrap';
|
||||
import { hostname, port, socket } from './socket';
|
||||
import {
|
||||
DataServiceComponent,
|
||||
@ -105,6 +106,7 @@ const reducer = (state: State, action: Action): State => {
|
||||
|
||||
const App = () => {
|
||||
const [state, dispatch] = useReducer(reducer, null);
|
||||
const [isInstantUpdate, setIsInstantUpdate] = useState(true);
|
||||
// const [isConnected, setIsConnected] = useState(socket.connected);
|
||||
|
||||
useEffect(() => {
|
||||
@ -134,9 +136,20 @@ const App = () => {
|
||||
return <p>Loading...</p>;
|
||||
}
|
||||
return (
|
||||
<div className="App">
|
||||
<DataServiceComponent props={state as DataServiceJSON} />
|
||||
</div>
|
||||
<>
|
||||
<Form.Check
|
||||
checked={isInstantUpdate}
|
||||
onChange={(e) => setIsInstantUpdate(e.target.checked)}
|
||||
type="switch"
|
||||
/>
|
||||
|
||||
<div className="App">
|
||||
<DataServiceComponent
|
||||
props={state as DataServiceJSON}
|
||||
isInstantUpdate={isInstantUpdate}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -7,12 +7,13 @@ import { Attribute, GenericComponent } from './GenericComponent';
|
||||
type DataServiceProps = {
|
||||
props: DataServiceJSON;
|
||||
parentPath?: string;
|
||||
isInstantUpdate: boolean;
|
||||
};
|
||||
|
||||
export type DataServiceJSON = Record<string, Attribute>;
|
||||
|
||||
export const DataServiceComponent = React.memo(
|
||||
({ props, parentPath = 'DataService' }: DataServiceProps) => {
|
||||
({ props, parentPath = 'DataService', isInstantUpdate }: DataServiceProps) => {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
||||
return (
|
||||
@ -33,6 +34,7 @@ export const DataServiceComponent = React.memo(
|
||||
attribute={value}
|
||||
name={key}
|
||||
parentPath={parentPath}
|
||||
isInstantUpdate={isInstantUpdate}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
@ -34,10 +34,11 @@ type GenericComponentProps = {
|
||||
attribute: Attribute;
|
||||
name: string;
|
||||
parentPath: string;
|
||||
isInstantUpdate: boolean;
|
||||
};
|
||||
|
||||
export const GenericComponent = React.memo(
|
||||
({ attribute, name, parentPath }: GenericComponentProps) => {
|
||||
({ attribute, name, parentPath, isInstantUpdate }: GenericComponentProps) => {
|
||||
if (attribute.type === 'bool') {
|
||||
return (
|
||||
<ButtonComponent
|
||||
@ -57,6 +58,7 @@ export const GenericComponent = React.memo(
|
||||
docString={attribute.doc}
|
||||
readOnly={attribute.readonly}
|
||||
value={Number(attribute.value)}
|
||||
isInstantUpdate={isInstantUpdate}
|
||||
/>
|
||||
);
|
||||
} else if (attribute.type === 'NumberSlider') {
|
||||
@ -70,6 +72,7 @@ export const GenericComponent = React.memo(
|
||||
min={attribute.value['min']['value']}
|
||||
max={attribute.value['max']['value']}
|
||||
stepSize={attribute.value['step_size']['value']}
|
||||
isInstantUpdate={isInstantUpdate}
|
||||
/>
|
||||
);
|
||||
} else if (attribute.type === 'Enum') {
|
||||
@ -111,6 +114,7 @@ export const GenericComponent = React.memo(
|
||||
readOnly={attribute.readonly}
|
||||
docString={attribute.doc}
|
||||
parent_path={parentPath}
|
||||
isInstantUpdate={isInstantUpdate}
|
||||
/>
|
||||
);
|
||||
} else if (attribute.type === 'DataService') {
|
||||
@ -118,15 +122,17 @@ export const GenericComponent = React.memo(
|
||||
<DataServiceComponent
|
||||
props={attribute.value as DataServiceJSON}
|
||||
parentPath={parentPath.concat('.', name)}
|
||||
isInstantUpdate={isInstantUpdate}
|
||||
/>
|
||||
);
|
||||
} else if (attribute.type === 'list') {
|
||||
return (
|
||||
<ListComponent
|
||||
name={name}
|
||||
value={attribute.value as object[]}
|
||||
value={attribute.value as Attribute[]}
|
||||
docString={attribute.doc}
|
||||
parent_path={parentPath}
|
||||
isInstantUpdate={isInstantUpdate}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
|
@ -1,12 +1,13 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { DocStringComponent } from './DocStringComponent';
|
||||
import { GenericComponent } from './GenericComponent';
|
||||
import { Attribute, GenericComponent } from './GenericComponent';
|
||||
|
||||
interface ListComponentProps {
|
||||
name: string;
|
||||
parent_path?: string;
|
||||
value: object[];
|
||||
value: Attribute[];
|
||||
docString: string;
|
||||
isInstantUpdate: boolean;
|
||||
}
|
||||
|
||||
export const ListComponent = React.memo((props: ListComponentProps) => {
|
||||
@ -16,7 +17,7 @@ export const ListComponent = React.memo((props: ListComponentProps) => {
|
||||
renderCount.current++;
|
||||
});
|
||||
|
||||
const { name, parent_path, value, docString } = props;
|
||||
const { name, parent_path, value, docString, isInstantUpdate } = props;
|
||||
|
||||
return (
|
||||
<div className={'component list'} id={parent_path.concat(name)}>
|
||||
@ -31,6 +32,7 @@ export const ListComponent = React.memo((props: ListComponentProps) => {
|
||||
attribute={item}
|
||||
name={`${name}[${index}]`}
|
||||
parentPath={parent_path}
|
||||
isInstantUpdate={isInstantUpdate}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Form, InputGroup, Button } from 'react-bootstrap';
|
||||
import { socket } from '../socket';
|
||||
import { emit_update } from '../socket';
|
||||
import { DocStringComponent } from './DocStringComponent';
|
||||
|
||||
// TODO: add button functionality
|
||||
@ -12,6 +12,7 @@ interface NumberComponentProps {
|
||||
value: number;
|
||||
readOnly: boolean;
|
||||
docString: string;
|
||||
isInstantUpdate: boolean;
|
||||
}
|
||||
|
||||
// TODO: highlight the digit that is being changed by setting both selectionStart and
|
||||
@ -98,7 +99,7 @@ const handleDeleteKey = (
|
||||
};
|
||||
|
||||
export const NumberComponent = React.memo((props: NumberComponentProps) => {
|
||||
const { name, parent_path, readOnly, docString } = props;
|
||||
const { name, parent_path, readOnly, docString, isInstantUpdate } = props;
|
||||
const renderCount = useRef(0);
|
||||
// Create a state for the cursor position
|
||||
const [cursorPosition, setCursorPosition] = useState(null);
|
||||
@ -114,7 +115,7 @@ export const NumberComponent = React.memo((props: NumberComponentProps) => {
|
||||
|
||||
// Update inputString only when value is different from numericInputString
|
||||
// preventing the removal of trailing decimals or zeros after the decimal.
|
||||
if (props.value !== numericInputString) {
|
||||
if (isInstantUpdate && props.value !== numericInputString) {
|
||||
setInputString(props.value.toString());
|
||||
}
|
||||
|
||||
@ -195,17 +196,18 @@ export const NumberComponent = React.memo((props: NumberComponentProps) => {
|
||||
selectionStart,
|
||||
selectionEnd
|
||||
));
|
||||
} else if (key === 'Enter' && !isInstantUpdate) {
|
||||
emit_update(name, parent_path, Number(newValue));
|
||||
return;
|
||||
} else {
|
||||
console.debug(key);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the input value and maintain the cursor position
|
||||
socket.emit('frontend_update', {
|
||||
name: name,
|
||||
parent_path: parent_path,
|
||||
value: Number(newValue)
|
||||
});
|
||||
if (isInstantUpdate) {
|
||||
emit_update(name, parent_path, Number(newValue));
|
||||
}
|
||||
|
||||
setInputString(newValue);
|
||||
|
||||
@ -213,6 +215,13 @@ export const NumberComponent = React.memo((props: NumberComponentProps) => {
|
||||
setCursorPosition(selectionStart);
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
if (!isInstantUpdate) {
|
||||
// If not in "instant update" mode, emit an update when the input field loses focus
|
||||
emit_update(name, parent_path, Number(inputString));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'number component'} id={parent_path.concat(name)}>
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
@ -229,6 +238,7 @@ export const NumberComponent = React.memo((props: NumberComponentProps) => {
|
||||
disabled={readOnly}
|
||||
name={parent_path.concat(name)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</InputGroup>
|
||||
{!readOnly && (
|
||||
|
@ -13,6 +13,7 @@ interface SliderComponentProps {
|
||||
readOnly: boolean;
|
||||
docString: string;
|
||||
stepSize: number;
|
||||
isInstantUpdate: boolean;
|
||||
}
|
||||
|
||||
export const SliderComponent = React.memo((props: SliderComponentProps) => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Form, InputGroup } from 'react-bootstrap';
|
||||
import { socket } from '../socket';
|
||||
import { emit_update } from '../socket';
|
||||
import { DocStringComponent } from './DocStringComponent';
|
||||
|
||||
// TODO: add button functionality
|
||||
@ -11,22 +11,38 @@ interface StringComponentProps {
|
||||
value: string;
|
||||
readOnly: boolean;
|
||||
docString: string;
|
||||
isInstantUpdate: boolean;
|
||||
}
|
||||
|
||||
export const StringComponent = React.memo((props: StringComponentProps) => {
|
||||
const renderCount = useRef(0);
|
||||
const [inputString, setInputString] = useState(props.value);
|
||||
|
||||
const { name, parent_path, readOnly, docString, isInstantUpdate } = props;
|
||||
useEffect(() => {
|
||||
renderCount.current++;
|
||||
});
|
||||
if (isInstantUpdate && props.value !== inputString) {
|
||||
setInputString(props.value);
|
||||
}
|
||||
}, [isInstantUpdate, props.value, inputString, renderCount]);
|
||||
|
||||
const { name, parent_path, value, readOnly, docString } = props;
|
||||
const handleChange = (event) => {
|
||||
socket.emit('frontend_update', {
|
||||
name: name,
|
||||
parent_path: parent_path,
|
||||
value: event.target.value
|
||||
});
|
||||
setInputString(event.target.value);
|
||||
if (isInstantUpdate) {
|
||||
emit_update(name, parent_path, event.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
if (event.key === 'Enter' && !isInstantUpdate) {
|
||||
emit_update(name, parent_path, inputString);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
if (!isInstantUpdate) {
|
||||
emit_update(name, parent_path, inputString);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@ -41,10 +57,12 @@ export const StringComponent = React.memo((props: StringComponentProps) => {
|
||||
<InputGroup.Text>{name}</InputGroup.Text>
|
||||
<Form.Control
|
||||
type="text"
|
||||
value={value}
|
||||
value={inputString}
|
||||
disabled={readOnly}
|
||||
name={name}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user