import React, { useEffect, useRef, useState } from 'react'; import { InputGroup, Form, Row, Col, Collapse, ToggleButton } from 'react-bootstrap'; import { DocStringComponent } from './DocStringComponent'; import { Slider } from '@mui/material'; import { NumberComponent, NumberObject } from './NumberComponent'; 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; changeCallback?: ( value: unknown, attributeName?: string, prefix?: string, callback?: (ack: unknown) => void ) => void; displayName: string; id: string; }; 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, changeCallback = () => {}, displayName, id } = props; const fullAccessPath = [parentPath, name].filter((element) => element).join('.'); useEffect(() => { renderCount.current++; }); useEffect(() => { addNotification(`${fullAccessPath} changed to ${value.value}.`); }, [props.value]); useEffect(() => { addNotification(`${fullAccessPath}.min changed to ${min.value}.`); }, [props.min]); useEffect(() => { addNotification(`${fullAccessPath}.max changed to ${max.value}.`); }, [props.max]); useEffect(() => { addNotification(`${fullAccessPath}.stepSize changed to ${stepSize.value}.`); }, [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]; } changeCallback(newNumber, `${name}.value`); }; const handleValueChange = (newValue: number, valueType: string) => { changeCallback(newValue, `${name}.${valueType}`); }; 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}` } ]} /> {}} changeCallback={(value) => changeCallback(value, name + '.value')} id={id + '-value'} /> 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')} />
); });