diff --git a/.gitignore b/.gitignore index ee630317..77bf3019 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ pkpdapp/static frontend/node_modules logfile.log* + +.vscode \ No newline at end of file diff --git a/frontend-v2/src/components/DropdownButton.tsx b/frontend-v2/src/components/DropdownButton.tsx index 2967a10d..4684fae7 100644 --- a/frontend-v2/src/components/DropdownButton.tsx +++ b/frontend-v2/src/components/DropdownButton.tsx @@ -21,7 +21,8 @@ type Props = { disabled?: boolean; data_cy?: string; useIcon?: boolean; - sx?: SxProps + sx?: SxProps; + variant?: "text" | "outlined" | "contained"; }; const DropdownButton: FC = ({ @@ -32,7 +33,7 @@ const DropdownButton: FC = ({ disabled, useIcon, sx, - variant = 'contained' + variant = "contained", }) => { const [anchorEl, setAnchorEl] = useState(null); diff --git a/frontend-v2/src/features/main/Sidebar.tsx b/frontend-v2/src/features/main/Sidebar.tsx index b30d2262..a4e9a4e2 100644 --- a/frontend-v2/src/features/main/Sidebar.tsx +++ b/frontend-v2/src/features/main/Sidebar.tsx @@ -108,6 +108,9 @@ export default function Sidebar() { const doses = groups?.flatMap((group) => group.protocols.map((p) => p.doses)); const groupsAreIncomplete = doses?.some((dosing) => !dosing[0]?.amount); + const noSecondaryParameters = model ? + model.derived_variables.reduce((acc, dv) => { return acc && dv.type !== "AUC"; }, true) : + false; const warnings: { [key: string]: string } = {}; const errors: { [key: string]: string } = {}; @@ -151,7 +154,7 @@ export default function Sidebar() { setMobileOpen(!mobileOpen); }; - const { onCollapse, onExpand, isExpanded, animationClasses } = useCollapsibleSidebar(); + const { onCollapse, onExpand, setHasSimulationsExpandedChanged, isExpanded, animationClasses } = useCollapsibleSidebar(); const pageKeys = Object.keys(PageName); const pageValues = Object.values(PageName); @@ -165,6 +168,9 @@ export default function Sidebar() { const handlePageClick = (key: string) => () => { const chosenPage = PageName[key as keyof typeof PageName]; + if (key !== PageName.SIMULATIONS) { + setHasSimulationsExpandedChanged(false); + } dispatch(setPage(chosenPage)); switch (chosenPage) { @@ -188,6 +194,9 @@ export default function Sidebar() { if (page === PageName.TRIAL_DESIGN && PageName.MODEL in errors) { return true; } + if (page === PageName.RESULTS && noSecondaryParameters) { + return true; + } if (page === PageName.DATA && PageName.MODEL in errors) { return true; } @@ -294,7 +303,6 @@ export default function Sidebar() { )} - = ({ if (type === "AUC") { setThresholds({ ...thresholds, - [variable.name]: 0, + [variable.name]: { + lower: 0, + upper: Infinity, + }, }); } }; diff --git a/frontend-v2/src/features/results/Results.tsx b/frontend-v2/src/features/results/Results.tsx index c79d188b..49615b87 100644 --- a/frontend-v2/src/features/results/Results.tsx +++ b/frontend-v2/src/features/results/Results.tsx @@ -32,10 +32,8 @@ const Results: FC = () => { setTab(newValue); }; - const handleTabRemove = async (table) => { - if ( - window.confirm("Are you sure you want to delete the current Table?") - ) { + const handleTabRemove = async (table: Table) => { + if (window.confirm("Are you sure you want to delete the current Table?")) { const removedIndex = tables.map(({ id }) => id).indexOf(table.id); setTables(tables.filter(({ id }) => id !== table.id)); @@ -79,9 +77,9 @@ const Results: FC = () => { key={table.id} label={table.name} {...a11yProps(index)} - sx={{ maxHeight: '48px', minHeight: 0}} + sx={{ maxHeight: "48px", minHeight: 0 }} icon={ - index === 0 ? null : ( + index === 0 ? undefined : ( { e.stopPropagation(); diff --git a/frontend-v2/src/features/results/ResultsTab.tsx b/frontend-v2/src/features/results/ResultsTab.tsx index dfcf5963..76889d6f 100644 --- a/frontend-v2/src/features/results/ResultsTab.tsx +++ b/frontend-v2/src/features/results/ResultsTab.tsx @@ -31,6 +31,18 @@ export type RowData = | TimeInterval[] | VariableRead[]; +type Item = + | { name: string } + | Parameter + | (TimeInterval & { name: string }) + | VariableRead; +type RowFilter = { + filter: (event: SelectChangeEvent) => void; + value: number; + items: Item[]; + label: string; +}; + const ResultsTab: FC = () => { const { groups = [] } = useSubjectGroups(); const { intervals } = useContext(SimulationContext); @@ -102,8 +114,8 @@ const ResultsTab: FC = () => { setParameter(newValue); } - let rowFilter1; - let rowFilter2; + let rowFilter1: RowFilter | undefined; + let rowFilter2: RowFilter | undefined; const groupSelect = { filter: handleGroupChange, @@ -155,23 +167,23 @@ const ResultsTab: FC = () => {
All Secondary PK parameters of all selected {var1} of{" "} {rowFilter1?.label?.toLowerCase()} ' - {rowFilter1?.items?.[rowFilter1?.value.toString()]?.name}' and{" "} + {rowFilter1?.items?.[rowFilter1?.value]?.name}' and{" "} {rowFilter2?.label.toLowerCase()} ' - {rowFilter2?.items?.[rowFilter2?.value.toString()]?.name}' + {rowFilter2?.items?.[rowFilter2?.value]?.name}'
); } const var1 = rowFilter1?.label === "Parameter" - ? rowFilter1?.items?.[rowFilter1?.value.toString()]?.name - : rowFilter2?.items?.[rowFilter2?.value.toString()]?.name; + ? rowFilter1?.items?.[rowFilter1?.value]?.name + : rowFilter2?.items?.[rowFilter2?.value]?.name; const var2 = rowFilter1?.label === "Parameter" ? rowFilter2?.label : rowFilter1?.label; const var3 = rowFilter1?.label === "Parameter" - ? rowFilter2?.items?.[rowFilter2?.value.toString()]?.name - : rowFilter1?.items?.[rowFilter1?.value.toString()]?.name; + ? rowFilter2?.items?.[rowFilter2?.value]?.name + : rowFilter1?.items?.[rowFilter1?.value]?.name; const desc1 = () => { if (var2 === "Interval") return "and time interval"; diff --git a/frontend-v2/src/features/results/ResultsTable.tsx b/frontend-v2/src/features/results/ResultsTable.tsx index aa1f2d34..046d9573 100644 --- a/frontend-v2/src/features/results/ResultsTable.tsx +++ b/frontend-v2/src/features/results/ResultsTable.tsx @@ -20,31 +20,31 @@ import { getTableHeight } from "../../shared/calculateTableHeights"; const RESULTS_TABLE_HEIGHTS = [ { - minHeight: "1100", + minHeight: 1100, tableHeight: "70vh", }, { - minHeight: "1000", + minHeight: 1000, tableHeight: "65vh", }, { - minHeight: "900", + minHeight: 900, tableHeight: "60vh", }, { - minHeight: "800", + minHeight: 800, tableHeight: "58vh", }, { - minHeight: "700", + minHeight: 700, tableHeight: "53vh", }, { - minHeight: "600", + minHeight: 600, tableHeight: "40vh", }, { - minHeight: "500", + minHeight: 500, tableHeight: "40vh", }, ]; @@ -55,9 +55,11 @@ const IntervalRow: FC<{ }> = ({ header, values }) => { return ( - {header} + + {header} + {values.map((value, i) => ( - {value} + {value} ))} ); @@ -127,13 +129,21 @@ export const ResultsTable: FC = ({ : []; return ( - + {rowColumn} {columnHeadings.map((column, i) => ( - {column} + + {column} + ))} diff --git a/frontend-v2/src/features/results/utils.ts b/frontend-v2/src/features/results/utils.ts index b88568b0..090efd46 100644 --- a/frontend-v2/src/features/results/utils.ts +++ b/frontend-v2/src/features/results/utils.ts @@ -34,23 +34,26 @@ export function valuesPerInterval( const times = simulation?.time || []; const values = variable && simulation ? simulation.outputs[variable.id] : []; return timeIntervals.map((interval) => { + if (values.length === 0) { + return []; + } const startIndex = times.findIndex((t) => t >= interval.start); const endIndex = times.findIndex((t) => t >= interval.end); const start = startIndex > 0 ? interpolate( - [times[startIndex - 1], times[startIndex]], - [values[startIndex - 1], values[startIndex]], - interval.start, - ) + [times[startIndex - 1], times[startIndex]], + [values[startIndex - 1], values[startIndex]], + interval.start, + ) : values[0]; const end = endIndex > -1 ? interpolate( - [times[endIndex - 1], times[endIndex]], - [values[endIndex - 1], values[endIndex]], - interval.end, - ) + [times[endIndex - 1], times[endIndex]], + [values[endIndex - 1], values[endIndex]], + interval.end, + ) : values[values.length - 1]; const intervalValues = [ start, @@ -106,20 +109,20 @@ export function thresholdCrossingPoints( const startTime = thresholdStart > 0 ? interpolate( - [intervalValues[thresholdStart - 1], intervalValues[thresholdStart]], - [intervalTimes[thresholdStart - 1], intervalTimes[thresholdStart]], - threshold, - ) + [intervalValues[thresholdStart - 1], intervalValues[thresholdStart]], + [intervalTimes[thresholdStart - 1], intervalTimes[thresholdStart]], + threshold, + ) : intervalTimes[0]; const endTime = thresholdStart === -1 // threshold never reached ? startTime : thresholdEnd > 0 ? interpolate( - [intervalValues[thresholdEnd - 1], intervalValues[thresholdEnd]], - [intervalTimes[thresholdEnd - 1], intervalTimes[thresholdEnd]], - threshold, - ) + [intervalValues[thresholdEnd - 1], intervalValues[thresholdEnd]], + [intervalTimes[thresholdEnd - 1], intervalTimes[thresholdEnd]], + threshold, + ) : intervalTimes[intervalTimes.length - 1]; // threshold reached beyond the end of the interval return [endTime - startTime, thresholdEnd]; } diff --git a/frontend-v2/src/features/simulation/SimulationSliderView.tsx b/frontend-v2/src/features/simulation/SimulationSliderView.tsx index f793de3c..f371dab4 100644 --- a/frontend-v2/src/features/simulation/SimulationSliderView.tsx +++ b/frontend-v2/src/features/simulation/SimulationSliderView.tsx @@ -64,9 +64,13 @@ const SimulationSliderView: FC = ({ const [value, setValue] = useState(defaultValue); useEffect(() => { - setValue(defaultValue); - onChange(slider.variable, defaultValue); - }, [defaultValue, onChange, slider.variable]); + // don't set the value of the slider until the variable is loaded + if (variable) { + const defaultValue = variable.default_value || 1.0; + setValue(defaultValue); + onChange(slider.variable, defaultValue); + } + }, [onChange, variable]); const handleSliderChange = ( event: Event | SyntheticEvent, @@ -78,8 +82,10 @@ const SimulationSliderView: FC = ({ }; const handleReset = () => { - setValue(defaultValue); - onChange(slider.variable, defaultValue); + if (variable) { + setValue(defaultValue); + onChange(slider.variable, defaultValue); + } }; const handleSave = () => { @@ -145,9 +151,9 @@ const SimulationSliderView: FC = ({ - + - + @@ -156,18 +162,18 @@ const SimulationSliderView: FC = ({ aria-label="save" onClick={handleSave} disabled={isSharedWithMe} - sx={{ padding: '2px'}} + sx={{ padding: '2px' }} > - + - + @@ -176,7 +182,7 @@ const SimulationSliderView: FC = ({ aria-label="delete" onClick={handleDelete} disabled={isSharedWithMe} - sx={{ padding: '2px'}} + sx={{ padding: '2px' }} > diff --git a/frontend-v2/src/features/simulation/Simulations.tsx b/frontend-v2/src/features/simulation/Simulations.tsx index 421b6582..73d54a78 100644 --- a/frontend-v2/src/features/simulation/Simulations.tsx +++ b/frontend-v2/src/features/simulation/Simulations.tsx @@ -1,9 +1,4 @@ -import { - Alert, - Grid, - Snackbar, - Box -} from "@mui/material"; +import { Alert, Grid, Snackbar, Box } from "@mui/material"; import { useSelector } from "react-redux"; import { RootState } from "../../app/store"; import { @@ -35,7 +30,6 @@ import { import SimulationPlotView from "./SimulationPlotView"; import useSimulation from "./useSimulation"; import useSimulationInputs from "./useSimulationInputs"; -import useSimulatedVariables from "./useSimulatedVariables"; import useDirty from "../../hooks/useDirty"; import paramPriority from "../model/paramPriority"; import { selectIsProjectShared } from "../login/loginSlice"; @@ -164,12 +158,11 @@ const Simulations: FC = () => { variables, timeMax, ); - const simulatedVariables = useSimulatedVariables(variables, sliderValues); const { loadingSimulate, data, error: simulateError, - } = useSimulation(simInputs, simulatedVariables, model); + } = useSimulation(simInputs, model); const refSimInputs = useSimulationInputs( model, @@ -178,11 +171,9 @@ const Simulations: FC = () => { variables, timeMax, ); - const referenceVariables = useSimulatedVariables(variables, EMPTY_OBJECT); const { data: dataReference } = useSimulation( refSimInputs, - referenceVariables, - showReference ? model : undefined + showReference ? model : undefined, ); const { @@ -236,7 +227,6 @@ const Simulations: FC = () => { const [exportSimulation, { error: exportSimulateErrorBase }] = useExportSimulation({ simInputs, - simulatedVariables, model, project, }); @@ -340,7 +330,7 @@ const Simulations: FC = () => { }; const orderedSliders = sliders.map((slider, i) => { - const variable = variables.find((v) => v.id === slider.variable); + const variable = variables?.find((v) => v.id === slider.variable); return { ...slider, priority: variable ? paramPriority(variable) : 0, diff --git a/frontend-v2/src/features/simulation/SimulationsSidePanel.tsx b/frontend-v2/src/features/simulation/SimulationsSidePanel.tsx index 1ab4df17..14ca1c3c 100644 --- a/frontend-v2/src/features/simulation/SimulationsSidePanel.tsx +++ b/frontend-v2/src/features/simulation/SimulationsSidePanel.tsx @@ -17,12 +17,19 @@ import UnitField from "../../components/UnitField"; import SimulationSliderView from "./SimulationSliderView"; import HelpButton from "../../components/HelpButton"; import { ExpandLess, ExpandMore } from "@mui/icons-material"; -import { ChangeEvent, useState } from "react"; +import { ChangeEvent, useEffect, useState } from "react"; import { getTableHeight } from "../../shared/calculateTableHeights"; -import { Simulation, SimulationPlot, SimulationRead, SimulationSlider, SubjectGroupRead, UnitRead } from "../../app/backendApi"; -import { UseFormReset } from "react-hook-form"; -import '../../App.css' +import { + Simulation, + SimulationPlot, + SimulationRead, + SimulationSlider, + SubjectGroupRead, + UnitRead, +} from "../../app/backendApi"; +import { Control } from "react-hook-form"; import { useCollapsibleSidebar } from "../../shared/contexts/CollapsibleSidebarContext"; +import '../../App.css' type SimulationsSidePanelType = { portalId: string; @@ -32,11 +39,11 @@ type SimulationsSidePanelType = { }[]; handleAddPlot: (plot: number) => void; isSharedWithMe: boolean; - layoutOptions: { value: string, label: string }[]; + layoutOptions: { value: string; label: string }[]; layout: string; setLayout: (layout: string) => void; plots: SimulationPlot[]; - control: UseFormReset; + control: Control; units: UnitRead[]; simulation: SimulationRead; groups?: SubjectGroupRead[]; @@ -47,15 +54,15 @@ type SimulationsSidePanelType = { label: string; }[]; handleAddSlider: (slider: number) => void; - orderedSliders: SimulationSlider[]; + orderedSliders: (SimulationSlider & { fieldArrayIndex: number })[]; handleChangeSlider: (variable: number, value: number) => void; - handleRemoveSlider: (index: number) => void; - handleSaveSlider: (slider: SimulationSlider) => void; + handleRemoveSlider: (index: number) => () => void; + handleSaveSlider: (slider: SimulationSlider) => (value: number) => void; exportSimulation: () => void; showReference: boolean; setShowReference: (reference: boolean) => void; shouldShowLegend: boolean; - setShouldShowLegend: (value: boolean) => void + setShouldShowLegend: (value: boolean) => void; }; const ButtonSx = { @@ -75,31 +82,31 @@ const ButtonSx = { const SidePanelSteps = [ { - minHeight: "1100", + minHeight: 1100, tableHeight: "75vh", }, { - minHeight: "1000", + minHeight: 1000, tableHeight: "72vh", }, { - minHeight: "900", + minHeight: 900, tableHeight: "70vh", }, { - minHeight: "800", + minHeight: 800, tableHeight: "65vh", }, { - minHeight: "700", + minHeight: 700, tableHeight: "60vh", }, { - minHeight: "600", + minHeight: 600, tableHeight: "55vh", }, { - minHeight: "500", + minHeight: 500, tableHeight: "53vh", }, ]; @@ -129,7 +136,7 @@ export const SimulationsSidePanel = ({ showReference, setShowReference, shouldShowLegend, - setShouldShowLegend + setShouldShowLegend, }: SimulationsSidePanelType) => { const portalRoot = document.getElementById(portalId); const [collapseLayout, setCollapseLayout] = useState(true); @@ -138,13 +145,13 @@ export const SimulationsSidePanel = ({ const [collapseParameters, setCollapseParameters] = useState(true); const [collapseReference, setCollapseReference] = useState(true); const [collapseLegend, setCollapseLegend] = useState(true); - const { animationClasses } = useCollapsibleSidebar(); + const { simulationAnimationClasses } = useCollapsibleSidebar(); if (!portalRoot) return null; return ReactDOM.createPortal( { export default function useExportSimulation({ simInputs, - simulatedVariables, model, project, }: iExportSimulation): [() => void, { error: any }] { @@ -70,12 +68,12 @@ export default function useExportSimulation({ project && groups ) { - console.log("Export to CSV: simulating with params", simulatedVariables); + console.log("Export to CSV: simulating with params", simInputs.variables); simulate({ id: model.id, simulate: simInputs, }).then((response) => { - let rows = simulatedVariables.map((p) => [p.qname, p.value]); + let rows = Object.keys(simInputs.variables).map((key) => [key, simInputs.variables[key]]); if (response?.data) { const cols = Object.keys(response.data[0].outputs); const vars = cols.map((vid) => diff --git a/frontend-v2/src/features/simulation/useSimulatedVariables.ts b/frontend-v2/src/features/simulation/useSimulatedVariables.ts deleted file mode 100644 index 78b1cccf..00000000 --- a/frontend-v2/src/features/simulation/useSimulatedVariables.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { useMemo } from "react"; -import { VariableRead } from "../../app/backendApi"; - -type SliderValues = { [key: number]: number }; - -const DEFAULT_VARS: { qname: string; value: number | undefined }[] = []; - -const getVariablesSimulated = ( - variables?: VariableRead[], - sliderValues?: SliderValues, -) => { - const constantVariables = variables?.filter((v) => v.constant) || []; - const merged = constantVariables.map((v: VariableRead) => { - const result = { qname: v.qname, value: v.default_value }; - if (sliderValues && sliderValues[v.id]) { - result.value = sliderValues[v.id]; - } - return result; - }); - return merged; -}; - -export default function useSimulatedVariables( - variables: VariableRead[] | undefined, - sliderValues: SliderValues | undefined, -) { - return useMemo( - () => - variables && sliderValues - ? getVariablesSimulated(variables, sliderValues) - : DEFAULT_VARS, - [variables, sliderValues], - ); -} diff --git a/frontend-v2/src/features/simulation/useSimulation.ts b/frontend-v2/src/features/simulation/useSimulation.ts index df4fc7bc..dbc2e54e 100644 --- a/frontend-v2/src/features/simulation/useSimulation.ts +++ b/frontend-v2/src/features/simulation/useSimulation.ts @@ -19,7 +19,6 @@ const SIMULATION_PAGES = [PageName.SIMULATIONS, PageName.RESULTS]; export default function useSimulation( simInputs: Simulate, - simulatedVariables: { qname: string; value: number | undefined }[], model: CombinedModelRead | undefined, ) { const { compound, protocols } = useProtocols(); @@ -46,7 +45,7 @@ export default function useSimulation( SIMULATION_PAGES.includes(page) ) { setLoadingSimulate(true); - console.log("Simulating with params", simulatedVariables); + console.log("Simulating with params", simInputs.variables); simulate({ id: model.id, simulate: simInputs, @@ -70,7 +69,6 @@ export default function useSimulation( protocols, simulate, JSON.stringify(simInputs), - JSON.stringify(simulatedVariables), page, ]); diff --git a/frontend-v2/src/features/simulation/useSimulationInputs.ts b/frontend-v2/src/features/simulation/useSimulationInputs.ts index 4bd8f9a5..f36349c9 100644 --- a/frontend-v2/src/features/simulation/useSimulationInputs.ts +++ b/frontend-v2/src/features/simulation/useSimulationInputs.ts @@ -14,31 +14,55 @@ const DEFAULT_INPUTS = { time_max: 0, }; +enum simulationInputMode { + REQUESTED_OUTPUTS = "REQUESTED", + ALL_OUTPUTS = "ALL", + ALL_OUTPUTS_NO_AMOUNTS = "ALL_NO_AMOUNTS", +} + const getSimulateInput = ( model: CombinedModelRead, simulation: SimulationRead, sliderValues: SliderValues, variables?: VariableRead[], timeMax?: number, - allOutputs: boolean = false, + mode: simulationInputMode = simulationInputMode.REQUESTED_OUTPUTS, ): Simulate => { const outputs: string[] = []; + + const constantVariables = variables?.filter((v) => v.constant) || []; const simulateVariables: { [key: string]: number } = {}; - for (const slider of simulation?.sliders || []) { - if (sliderValues[slider.variable]) { - const variable = variables?.find((v) => v.id === slider.variable); - if (variable) { - simulateVariables[variable.qname] = sliderValues[slider.variable]; - } + constantVariables.forEach((v: VariableRead) => { + const result = { qname: v.qname, value: v.default_value || 0 }; + if (sliderValues && sliderValues[v.id]) { + result.value = sliderValues[v.id]; } - } - if (allOutputs) { + simulateVariables[result.qname] = result.value; + }); + + if (mode == simulationInputMode.ALL_OUTPUTS) { for (const v of variables || []) { if (!v.constant) { outputs.push(v.qname); } } - } else { + } else if (mode == simulationInputMode.ALL_OUTPUTS_NO_AMOUNTS) { + const amountNames = ["Aa", "A1", "A2", "A3"]; + for (const v of variables || []) { + let isAmount = model.is_library_model && amountNames.includes(v.name); + if (!v.constant && !isAmount) { + outputs.push(v.qname); + } + } + for (const plot of simulation?.plots || []) { + for (const y_axis of plot.y_axes) { + const variable = variables?.find((v) => v.id === y_axis.variable); + if (variable && !outputs.includes(variable.qname)) { + outputs.push(variable.qname); + } + } + } + } else if (mode == simulationInputMode.REQUESTED_OUTPUTS) { for (const plot of simulation?.plots || []) { for (const y_axis of plot.y_axes) { const variable = variables?.find((v) => v.id === y_axis.variable); @@ -91,7 +115,7 @@ export default function useSimulationInputs( return useMemo( () => model && simulation && sliderValues - ? getSimulateInput(model, simulation, sliderValues, variables, timeMax) + ? getSimulateInput(model, simulation, sliderValues, variables, timeMax, simulationInputMode.ALL_OUTPUTS_NO_AMOUNTS) : DEFAULT_INPUTS, [simulation, sliderValues, variables, timeMax], ); diff --git a/frontend-v2/src/shared/contexts/CollapsibleSidebarContext.tsx b/frontend-v2/src/shared/contexts/CollapsibleSidebarContext.tsx index ed661ebd..9b5e5c3a 100644 --- a/frontend-v2/src/shared/contexts/CollapsibleSidebarContext.tsx +++ b/frontend-v2/src/shared/contexts/CollapsibleSidebarContext.tsx @@ -1,41 +1,62 @@ -import { createContext, ReactNode, useContext, useState } from 'react'; - +import { createContext, ReactNode, useContext, useState } from "react"; const CollapsibleSidebarContext = createContext({ - onCollapse: () => { return }, - onExpand: () => { return }, + onCollapse: () => { + return; + }, + onExpand: () => { + return; + }, + setHasSimulationsExpandedChanged: (isChanged: boolean) => { return; }, isExpanded: true, - animationClasses: '' + animationClasses: "", + simulationAnimationClasses: "", }); -export const CollapsibleSidebarProvider = ({ children }: { children: ReactNode }) => { - const [isExpanded, setIsExpanded] = useState(true); - const [hasExpandedChanged, setHasExpandedChanged] = useState(false); - const onCollapse = () => { - setHasExpandedChanged(true); - setIsExpanded(false); - }; - - const onExpand = () => { - setHasExpandedChanged(true); - setIsExpanded(true); - }; - - - const getAnimationClasses = () => { - if (!hasExpandedChanged) return ''; - return isExpanded ? 'on-expand' : 'on-collapse'; - } - - const animationClasses = getAnimationClasses(); +export const CollapsibleSidebarProvider = ({ + children, +}: { + children: ReactNode; +}) => { + const [isExpanded, setIsExpanded] = useState(true); + const [hasExpandedChanged, setHasExpandedChanged] = useState(false); + const [hasSimulationsExpandedChanged, setHasSimulationsExpandedChanged] = + useState(false); + const onCollapse = () => { + setHasExpandedChanged(true); + setHasSimulationsExpandedChanged(true); + setIsExpanded(false); + }; + + const onExpand = () => { + setHasExpandedChanged(true); + setHasSimulationsExpandedChanged(true); + setIsExpanded(true); + }; + + const getAnimationClasses = () => { + if (!hasExpandedChanged) return ""; + return isExpanded ? "on-expand" : "on-collapse"; + }; + + const animationClasses = getAnimationClasses(); + + const getSimulationsAnimationClasses = () => { + if (!hasExpandedChanged || !hasSimulationsExpandedChanged) return ""; + return isExpanded ? "on-expand" : "on-collapse"; + }; + + const simulationAnimationClasses = getSimulationsAnimationClasses(); return ( {children} @@ -43,4 +64,5 @@ export const CollapsibleSidebarProvider = ({ children }: { children: ReactNode } ); }; -export const useCollapsibleSidebar = () => useContext(CollapsibleSidebarContext); +export const useCollapsibleSidebar = () => + useContext(CollapsibleSidebarContext);