import React, { useContext, useEffect, useRef, useState } from 'react'; import { WebSettingsContext } from '../WebSettings'; import { InputGroup, Form, Row, Col, Collapse, ToggleButton } from 'react-bootstrap'; import { setAttribute } from '../socket'; import { DocStringComponent } from './DocStringComponent'; import { Slider } from '@mui/material'; import { NumberComponent, NumberObject } from './NumberComponent'; import { getIdFromFullAccessPath } from '../utils/stringUtils'; import { LevelName } from './NotificationsComponent'; type SliderComponentProps = { name: string; min: NumberObject; max: NumberObject; parentPath?: string; value: NumberObject; readOnly: boolean; docString: string; stepSize: NumberObject; isInstantUpdate: boolean; addNotification: (message: string, levelname?: LevelName) => void; }; export const SliderComponent = React.memo((props: SliderComponentProps) => { const renderCount = useRef(0); const [open, setOpen] = useState(false); const { name, parentPath, value, min, max, stepSize, docString, isInstantUpdate, addNotification } = props; const fullAccessPath = [parentPath, name].filter((element) => element).join('.'); const id = getIdFromFullAccessPath(fullAccessPath); const webSettings = useContext(WebSettingsContext); let displayName = name; if (webSettings[fullAccessPath] && webSettings[fullAccessPath].displayName) { displayName = webSettings[fullAccessPath].displayName; } useEffect(() => { renderCount.current++; }); useEffect(() => { addNotification(`${parentPath}.${name} changed to ${value}.`); }, [props.value]); useEffect(() => { addNotification(`${parentPath}.${name}.min changed to ${min}.`); }, [props.min]); useEffect(() => { addNotification(`${parentPath}.${name}.max changed to ${max}.`); }, [props.max]); useEffect(() => { addNotification(`${parentPath}.${name}.stepSize changed to ${stepSize}.`); }, [props.stepSize]); const handleOnChange = (event, newNumber: number | number[]) => { // This will never be the case as we do not have a range slider. However, we should // make sure this is properly handled. if (Array.isArray(newNumber)) { newNumber = newNumber[0]; } setAttribute(`${name}.value`, parentPath, newNumber); }; const handleValueChange = (newValue: number, valueType: string) => { setAttribute(`${name}.${valueType}`, parentPath, newValue); }; const deconstructNumberDict = ( numberDict: NumberObject ): [number, boolean, string | null] => { let numberMagnitude: number; let numberUnit: string | null = null; const numberReadOnly = numberDict.readonly; if (numberDict.type === 'int' || numberDict.type === 'float') { numberMagnitude = numberDict.value; } else if (numberDict.type === 'Quantity') { numberMagnitude = numberDict.value.magnitude; numberUnit = numberDict.value.unit; } return [numberMagnitude, numberReadOnly, numberUnit]; }; const [valueMagnitude, valueReadOnly, valueUnit] = deconstructNumberDict(value); const [minMagnitude, minReadOnly] = deconstructNumberDict(min); const [maxMagnitude, maxReadOnly] = deconstructNumberDict(max); const [stepSizeMagnitude, stepSizeReadOnly] = deconstructNumberDict(stepSize); return (
{process.env.NODE_ENV === 'development' && (
Render count: {renderCount.current}
)} {displayName} handleOnChange(event, newNumber)} min={minMagnitude} max={maxMagnitude} step={stepSizeMagnitude} marks={[ { value: minMagnitude, label: `${minMagnitude}` }, { value: maxMagnitude, label: `${maxMagnitude}` } ]} /> null} /> setOpen(!open)} type="checkbox" checked={open} value="" className="btn" variant="light" aria-controls="slider-settings" aria-expanded={open}> Min Value handleValueChange(Number(e.target.value), 'min')} /> Max Value handleValueChange(Number(e.target.value), 'max')} /> Step Size handleValueChange(Number(e.target.value), 'step_size')} />
); });