From cb0b7704e3eb29e00b46844411dfc4542d302e99 Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Mon, 6 Oct 2025 14:46:48 +0200 Subject: [PATCH 01/10] wip --- .../interfaces.ts | 4 +- .../settings/atoms/baseAtoms.ts | 10 ++- .../settings/atoms/derivedAtoms.ts | 71 ++++++++++++++++++ .../settings/components/parameterSelector.tsx | 24 ++---- .../settings/settings.tsx | 74 +++++++++++-------- .../view/view.tsx | 15 ++++ 6 files changed, 144 insertions(+), 54 deletions(-) create mode 100644 frontend/src/modules/ParameterResponseCorrelationMatrixPlot/settings/atoms/derivedAtoms.ts diff --git a/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/interfaces.ts b/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/interfaces.ts index 35a4d4f56..40c1d3808 100644 --- a/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/interfaces.ts +++ b/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/interfaces.ts @@ -2,12 +2,12 @@ import type { ParameterIdent } from "@framework/EnsembleParameters"; import type { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface"; import { - parameterIdentsAtom, showLabelsAtom, useFixedColorRangeAtom, plotTypeAtom, correlationSettingsAtom, } from "./settings/atoms/baseAtoms"; +import { selectedParameterIdentsAtom } from "./settings/atoms/derivedAtoms"; import type { PlotType, CorrelationSettings } from "./typesAndEnums"; type SettingsToViewInterface = { @@ -23,7 +23,7 @@ export type Interfaces = { }; export const settingsToViewInterfaceInitialization: InterfaceInitialization = { - parameterIdents: (get) => get(parameterIdentsAtom), + parameterIdents: (get) => get(selectedParameterIdentsAtom), showLabels: (get) => get(showLabelsAtom), useFixedColorRange: (get) => get(useFixedColorRangeAtom), plotType: (get) => get(plotTypeAtom), diff --git a/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/settings/atoms/baseAtoms.ts b/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/settings/atoms/baseAtoms.ts index dee211280..349c93019 100644 --- a/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/settings/atoms/baseAtoms.ts +++ b/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/settings/atoms/baseAtoms.ts @@ -1,14 +1,18 @@ import { atom } from "jotai"; +import type { KeyKind } from "@framework/DataChannelTypes"; import type { ParameterIdent } from "@framework/EnsembleParameters"; +import type { ChannelReceiverReturnData } from "@framework/internal/DataChannels/hooks/useChannelReceiver"; +import type { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; import { PlotType, type CorrelationSettings } from "../../typesAndEnums"; - -export const parameterIdentsAtom = atom([]); +export const receivedChannelAtom = atom[]>([]); +export const userSelectedParameterIdentsAtom = atom([]); +export const hasUserInteractedWithParameterSelectionAtom = atom(false); export const showLabelsAtom = atom(false); export const useFixedColorRangeAtom = atom(true); export const plotTypeAtom = atom(PlotType.ParameterResponseMatrix); - +export const regularEnsembleIdentsAtom = atom([]); export const correlationSettingsAtom = atom({ threshold: null, hideIndividualCells: true, diff --git a/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/settings/atoms/derivedAtoms.ts b/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/settings/atoms/derivedAtoms.ts new file mode 100644 index 000000000..438eebac7 --- /dev/null +++ b/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/settings/atoms/derivedAtoms.ts @@ -0,0 +1,71 @@ +import type { ParameterIdent } from "@framework/EnsembleParameters"; +import { EnsembleSetAtom } from "@framework/GlobalAtoms"; +import { RegularEnsemble } from "@framework/RegularEnsemble"; +import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent"; +import { getContinuousAndNonConstantParameterIdentsInEnsembles } from "@modules/_shared/parameterUnions"; +import { atom } from "jotai"; + +import { + receivedChannelAtom, + userSelectedParameterIdentsAtom, + hasUserInteractedWithParameterSelectionAtom, +} from "./baseAtoms"; + +export const availableParameterIdentsAtom = atom((get) => { + const receivedChannels = get(receivedChannelAtom); + if (!receivedChannels) { + return []; + } + + // Filter for channels that have content + const channelsWithContent = receivedChannels.filter((response) => response.channel?.contents); + if (!channelsWithContent.length) { + return []; + } + + // Extract ensemble identifiers from channels with content + const ensembleIdentStrings = channelsWithContent + .flatMap((channel) => channel.channel?.contents || []) + .map((content) => content.metaData.ensembleIdentString); + + // Get regular ensemble identifiers + const ensembleSet = get(EnsembleSetAtom); + const regularEnsembleIdents = ensembleIdentStrings + .map((id) => ensembleSet.findEnsembleByIdentString(id)) + .filter((ensemble) => ensemble instanceof RegularEnsemble) + .map((ensemble) => RegularEnsembleIdent.fromString(ensemble.getIdent().toString())); + return getContinuousAndNonConstantParameterIdentsInEnsembles(ensembleSet, regularEnsembleIdents); +}); + +export const selectedParameterIdentsAtom = atom((get) => { + const availableParameterIdents = get(availableParameterIdentsAtom); + const userParameterIdents: ParameterIdent[] = get(userSelectedParameterIdentsAtom); + const hasUserInteracted = get(hasUserInteractedWithParameterSelectionAtom); + + // Ensure that the selected parameters are still available + const filteredUserParameters = userParameterIdents.filter((param) => + availableParameterIdents.some( + (availableParam) => availableParam.name === param.name && availableParam.groupName === param.groupName, + ), + ); + + // Only auto-select if user has never interacted with the parameter selection (initial state) + // Don't auto-select when user has made selections but they became empty through filtering or explicit deselection + if (!hasUserInteracted && userParameterIdents.length === 0 && availableParameterIdents.length > 0) { + // If less than 100 parameters, select all + if (availableParameterIdents.length < 100) { + return availableParameterIdents; + } + + // If 100 or more parameters, select all from the first group name + const firstGroupName = availableParameterIdents.find((param) => param.groupName !== null)?.groupName; + if (firstGroupName) { + return availableParameterIdents.filter((param) => param.groupName === firstGroupName); + } + + // Fallback: if no groups found, return all parameters without group names + return availableParameterIdents.filter((param) => param.groupName === null); + } + + return filteredUserParameters; +}); diff --git a/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/settings/components/parameterSelector.tsx b/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/settings/components/parameterSelector.tsx index d54373a44..6837dca9d 100644 --- a/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/settings/components/parameterSelector.tsx +++ b/frontend/src/modules/ParameterResponseCorrelationMatrixPlot/settings/components/parameterSelector.tsx @@ -21,7 +21,7 @@ export function ParametersSelector({ selectedParameterIdents, onChange, }: ParametersSelectorProps): React.ReactNode { - const [autoSelectAllOnGroupChange, setAutoSelectAllOnGroupChange] = React.useState(false); + const [autoSelectAllOnGroupChange, setAutoSelectAllOnGroupChange] = React.useState(true); const [selectedGroupFilterValues, setSelectedGroupFilterValues] = React.useState(() => { if (selectedParameterIdents.length > 0) { @@ -30,38 +30,27 @@ export function ParametersSelector({ return []; }); - - const handleGroupChange = (newlySelectedGroupFilterStrings: string[]) => { setSelectedGroupFilterValues(newlySelectedGroupFilterStrings); if (newlySelectedGroupFilterStrings.length === 0) { onChange([]); - } else { + } else if (autoSelectAllOnGroupChange) { const parametersThatMatchNewGroups = allParameterIdents.filter((p) => newlySelectedGroupFilterStrings.some( (groupValue) => groupValue === (p.groupName ?? GroupType.NO_GROUP), ), ); - - let newSelectedParameters: ParameterIdent[] = []; - - if (autoSelectAllOnGroupChange) { - newSelectedParameters = parametersThatMatchNewGroups; - } else { - newSelectedParameters = selectedParameterIdents.filter((p) => - parametersThatMatchNewGroups.some((pg) => pg.equals(p)), - ); - } - - onChange(newSelectedParameters); + onChange(parametersThatMatchNewGroups); } + // If autoSelectAllOnGroupChange is false, don't change the selected parameters + // Just let the UI filter what's shown, but keep the current selection }; const handleParameterChange = (selectedValues: string[]) => { onChange(selectedValues.map((s) => ParameterIdent.fromString(s))); }; - + const groupSelectOptions: SelectOption[] = Array.from( new Set(allParameterIdents.map((p) => p.groupName ?? GroupType.NO_GROUP)), ).map((groupName) => ({ @@ -90,6 +79,7 @@ export function ParametersSelector({ onChange={handleGroupChange} multiple={true} size={Math.min(10, groupSelectOptions.length > 0 ? groupSelectOptions.length : 1)} + showQuickSelectButtons />