import React, { useEffect, 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"; import { SerializedObject } from "../types/SerializedObject"; import { QuantityMap } from "../types/QuantityMap"; import { propsAreEqual } from "../utils/propsAreEqual"; import useRenderCount from "../hooks/useRenderCount"; interface SliderComponentProps { fullAccessPath: string; min: NumberObject; max: NumberObject; value: NumberObject; readOnly: boolean; docString: string | null; stepSize: NumberObject; isInstantUpdate: boolean; addNotification: (message: string, levelname?: LevelName) => void; changeCallback?: (value: SerializedObject, callback?: (ack: unknown) => void) => void; displayName: string; id: string; } const deconstructNumberDict = ( numberDict: NumberObject, ): [number, boolean, string | undefined] => { let numberMagnitude = 0; let numberUnit: string | undefined = undefined; 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]; }; export const SliderComponent = React.memo((props: SliderComponentProps) => { const renderCount = useRenderCount(); const [open, setOpen] = useState(false); const { fullAccessPath, value, min, max, stepSize, docString, isInstantUpdate, addNotification, changeCallback = () => {}, displayName, id, } = props; useEffect(() => { addNotification(`${fullAccessPath} changed to ${value.value}.`); }, [props.value.value]); useEffect(() => { addNotification(`${fullAccessPath}.min changed to ${min.value}.`); }, [props.min.value, props.min.type]); useEffect(() => { addNotification(`${fullAccessPath}.max changed to ${max.value}.`); }, [props.max.value, props.max.type]); useEffect(() => { addNotification(`${fullAccessPath}.stepSize changed to ${stepSize.value}.`); }, [props.stepSize.value, props.stepSize.type]); 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]; } let serializedObject: SerializedObject; if (value.type === "Quantity") { serializedObject = { type: "Quantity", value: { magnitude: newNumber, unit: value.value.unit, } as QuantityMap, full_access_path: `${fullAccessPath}.value`, readonly: value.readonly, doc: docString, }; } else { serializedObject = { type: value.type, value: newNumber, full_access_path: `${fullAccessPath}.value`, readonly: value.readonly, doc: docString, }; } changeCallback(serializedObject); }; const handleValueChange = ( newValue: number, name: string, valueObject: NumberObject, ) => { let serializedObject: SerializedObject; if (valueObject.type === "Quantity") { serializedObject = { type: valueObject.type, value: { magnitude: newValue, unit: valueObject.value.unit, } as QuantityMap, full_access_path: `${fullAccessPath}.${name}`, readonly: valueObject.readonly, doc: null, }; } else { serializedObject = { type: valueObject.type, value: newValue, full_access_path: `${fullAccessPath}.${name}`, readonly: valueObject.readonly, doc: null, }; } changeCallback(serializedObject); }; 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}
} {displayName} handleOnChange(event, newNumber)} min={minMagnitude} max={maxMagnitude} step={stepSizeMagnitude} marks={[ { value: minMagnitude, label: `${minMagnitude}` }, { value: maxMagnitude, label: `${maxMagnitude}` }, ]} /> {}} changeCallback={changeCallback} 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", min)} /> Max Value handleValueChange(Number(e.target.value), "max", max)} /> Step Size handleValueChange(Number(e.target.value), "step_size", stepSize) } />
); }, propsAreEqual); SliderComponent.displayName = "SliderComponent";