diff --git a/package-lock.json b/package-lock.json
index dc12bc155..973f138dc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -43,7 +43,7 @@
"ml-tree-similarity": "^2.2.0",
"multiplet-analysis": "^2.1.5",
"nmr-correlation": "^2.3.3",
- "nmr-load-save": "^2.2.0",
+ "nmr-load-save": "^2.3.0",
"nmr-processing": "^14.0.5",
"nmredata": "^0.9.11",
"numeral": "^2.0.6",
@@ -9127,9 +9127,9 @@
}
},
"node_modules/nmr-load-save": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/nmr-load-save/-/nmr-load-save-2.2.0.tgz",
- "integrity": "sha512-4j5Ich981WH965mk1zVDyspkxzbTfN2ZxuW2fj6zIZ81OlNMr8PxyGJzKQvNQVEtGZiHfbHnIHHxW7XZ/el51g==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/nmr-load-save/-/nmr-load-save-2.3.0.tgz",
+ "integrity": "sha512-MqO65p8YhOOoYcAgnWd5+GqMhPO2s2oEE7WteM3o7NqZZ4DxusVp5oNLGXDSnT2kb9Qz7acpver+O6YAYYBe/Q==",
"license": "MIT",
"dependencies": {
"@lukeed/uuid": "^2.0.1",
diff --git a/package.json b/package.json
index 966a8ebd8..1e415e6f8 100644
--- a/package.json
+++ b/package.json
@@ -88,7 +88,7 @@
"ml-tree-similarity": "^2.2.0",
"multiplet-analysis": "^2.1.5",
"nmr-correlation": "^2.3.3",
- "nmr-load-save": "^2.2.0",
+ "nmr-load-save": "^2.3.0",
"nmr-processing": "^14.0.5",
"nmredata": "^0.9.11",
"numeral": "^2.0.6",
diff --git a/src/component/hooks/useToolsFunctions.ts b/src/component/hooks/useToolsFunctions.ts
index dd6c6f213..c397669ea 100644
--- a/src/component/hooks/useToolsFunctions.ts
+++ b/src/component/hooks/useToolsFunctions.ts
@@ -7,7 +7,7 @@ import { useChartData } from '../context/ChartContext.js';
import { useDispatch } from '../context/DispatchContext.js';
import { usePreferences } from '../context/PreferencesContext.js';
import { useToaster } from '../context/ToasterContext.js';
-import { TOOLS_PANELS_ACCORDION } from '../panels/Panels.js';
+import { TOOLS_PANELS_ACCORDION } from '../panels/accordionItems.js';
import { options } from '../toolbar/ToolTypes.js';
export default function useToolsFunctions() {
diff --git a/src/component/main/InnerNMRiumContents.tsx b/src/component/main/InnerNMRiumContents.tsx
index 80a9bb95e..1dd10397a 100644
--- a/src/component/main/InnerNMRiumContents.tsx
+++ b/src/component/main/InnerNMRiumContents.tsx
@@ -14,7 +14,8 @@ import { ExportManagerController } from '../elements/export/ExportManager.js';
import { PrintContent } from '../elements/print/PrintContent.js';
import { Header } from '../header/Header.js';
import DropZone from '../loader/DropZone.js';
-import Panels from '../panels/Panels.js';
+import { PanelOpenProviderProvider, Panels } from '../panels/Panels.js';
+import { PanelsBar } from '../panels/PanelsBar.js';
import ToolBar from '../toolbar/ToolBar.js';
import { useNMRiumRefAPI } from './NMRiumRefAPI.js';
@@ -150,15 +151,21 @@ export function InnerNMRiumContents(props: InnerNMRiumContentsProps) {
height: '100%',
}}
>
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
{
+ const panelOptions = getPanelPreferences(item);
+ return panelOptions.display;
+ });
+ const hasDisplayedPanels = displayedPanels.length > 0;
+
+ if (items?.length === 0 || !hasDisplayedPanels) {
+ return
{children}
;
+ }
+
+ const closed: number | boolean = general?.hidePanelOnLoad
+ ? true
+ : verticalSplitterCloseThreshold;
+
return (
{children}
diff --git a/src/component/modal/setting/settings-tabs/DisplayTabContent.tsx b/src/component/modal/setting/settings-tabs/DisplayTabContent.tsx
index 96f39de6e..e43280989 100644
--- a/src/component/modal/setting/settings-tabs/DisplayTabContent.tsx
+++ b/src/component/modal/setting/settings-tabs/DisplayTabContent.tsx
@@ -93,6 +93,18 @@ function DisplayTabContent() {
},
{
index: 2,
+ Header: 'Visible',
+ style: { textAlign: 'center' },
+ Cell: ({ row }) => (
+
+ ),
+ },
+ {
+ index: 3,
Header: 'Active',
style: { textAlign: 'center' },
Cell: ({ row }) => (
@@ -104,7 +116,7 @@ function DisplayTabContent() {
),
},
{
- index: 3,
+ index: 4,
Header: 'Open on load',
style: { textAlign: 'center' },
Cell: ({ row }) => (
diff --git a/src/component/panels/Panels.tsx b/src/component/panels/Panels.tsx
index 3c4d6df71..5abc77afb 100644
--- a/src/component/panels/Panels.tsx
+++ b/src/component/panels/Panels.tsx
@@ -1,203 +1,74 @@
-import lodashGet from 'lodash/get.js';
-import type {
- NMRiumPanelPreferences,
- PanelPreferencesType,
-} from 'nmr-load-save';
-import type { CSSProperties, ReactElement } from 'react';
-import { memo, useCallback } from 'react';
+import type { NMRiumPanelPreferences } from 'nmr-load-save';
+import { createContext, memo, useContext, useMemo, useState } from 'react';
import { FaRegEdit } from 'react-icons/fa';
-import type { AccordionItemProps, ToolbarItemProps } from 'react-science/ui';
+import type { ToolbarItemProps } from 'react-science/ui';
import { Accordion, Toolbar } from 'react-science/ui';
-import { useChartData } from '../context/ChartContext.js';
-import { usePreferences } from '../context/PreferencesContext.js';
import { useActiveSpectrum } from '../hooks/useActiveSpectrum.js';
-import useCheckExperimentalFeature from '../hooks/useCheckExperimentalFeature.js';
import { useDialogToggle } from '../hooks/useDialogToggle.js';
-import type { DisplayerMode } from '../reducer/Reducer.js';
-import AutomaticAssignment from './AutomaticAssignment/AutomaticAssignment.js';
-import IntegralPanel from './IntegralsPanel/IntegralPanel.js';
-import { MatrixGenerationPanel } from './MatrixGenerationPanel/MatrixGenerationPanel.js';
-import MoleculePanel from './MoleculesPanel/MoleculePanel.js';
-import PeaksPanel from './PeaksPanel/PeaksPanel.js';
-import RangesPanel from './RangesPanel/RangesPanel.js';
-import SpectrumListPanel from './SpectraPanel/SpectrumListPanel.js';
-import SummaryPanel from './SummaryPanel/SummaryPanel.js';
-import ZonesPanel from './ZonesPanel/ZonesPanel.js';
-import DatabasePanel from './databasePanel/DatabasePanel.js';
-import FilterPanel from './filtersPanel/FilterPanel.js';
+import type { AccordionItem } from './accordionItems.js';
+import { useAccordionItems } from './hooks/useAccordionItems.js';
+import { useGetPanelOptions } from './hooks/useGetPanelOptions.js';
+import { useTogglePanel } from './hooks/useTogglePanel.js';
import { InformationEditionModal } from './informationPanel/InformationEditionModal.js';
-import { InformationPanel } from './informationPanel/InformationPanel.js';
-import MultipleSpectraAnalysisPanel from './multipleAnalysisPanel/MultipleSpectraAnalysisPanel.js';
-import PredictionPane from './predictionPanel/PredictionPanel.js';
-import SpectrumSimulation from './spectrumSimulation/SpectrumSimulation.js';
-interface AccordionItem
- extends Omit {
- id?: keyof NMRiumPanelPreferences;
- component: ReactElement;
- style?: CSSProperties;
- hidePreferenceKey: string;
- mode: DisplayerMode | null;
- isExperimental?: boolean;
+type PanelOpenState = Partial>;
+interface PanelStateContext {
+ setPanelOpenState: (id: keyof NMRiumPanelPreferences, value: boolean) => void;
+ panelOpenState: PanelOpenState;
}
-const accordionItems: AccordionItem[] = [
- {
- title: 'Spectra',
- component: ,
- hidePreferenceKey: 'spectraPanel',
- mode: null,
- },
- {
- id: 'informationPanel',
- title: 'Information',
- component: ,
- style: { overflow: 'hidden' },
- hidePreferenceKey: 'informationPanel',
- mode: null,
- },
- {
- title: 'Peaks',
- component: ,
- hidePreferenceKey: 'peaksPanel',
- mode: '1D',
- },
- {
- title: 'Processings',
- component: ,
- hidePreferenceKey: 'processingsPanel',
- mode: null,
- },
- {
- title: 'Integrals',
- component: ,
- hidePreferenceKey: 'integralsPanel',
- mode: '1D',
- },
- {
- title: 'Ranges / Multiplet analysis',
- component: ,
- hidePreferenceKey: 'rangesPanel',
- mode: '1D',
- },
- {
- title: 'Multiple spectra analysis',
- component: ,
- hidePreferenceKey: 'multipleSpectraAnalysisPanel',
- mode: null,
- },
- {
- title: 'Matrix generation',
- component: ,
- hidePreferenceKey: 'matrixGenerationPanel',
- mode: '1D',
- },
- {
- title: 'Zones',
- component: ,
- hidePreferenceKey: 'zonesPanel',
- mode: '2D',
- },
- {
- title: 'Summary',
- component: ,
- hidePreferenceKey: 'summaryPanel',
- mode: null,
- },
- {
- title: 'Chemical structures',
- component: ,
- hidePreferenceKey: 'structuresPanel',
- mode: null,
- },
- {
- title: 'Databases',
- component: ,
- hidePreferenceKey: 'databasePanel',
- mode: null,
- },
- {
- title: 'Automatic assignment',
- component: ,
- hidePreferenceKey: 'automaticAssignmentPanel',
- mode: null,
- },
- {
- title: 'Prediction',
- component: ,
- hidePreferenceKey: 'predictionPanel',
- mode: null,
- },
- {
- title: 'Spectrum simulation',
- component: ,
- hidePreferenceKey: 'simulationPanel',
- mode: '1D',
- },
-];
+const AccordionContext = createContext(null);
-export const TOOLS_PANELS_ACCORDION: Record = {
- null: 'Spectra',
- peakPicking: 'Peaks',
- integral: 'Integrals',
- rangePicking: 'Ranges / Multiplet analysis',
- zonePicking: 'Zones',
- multipleSpectraAnalysis: 'Multiple Spectra Analysis',
-};
+export function usePanelOpenState() {
+ const context = useContext(AccordionContext);
-function usePanelPreferences(): (item: AccordionItem) => PanelPreferencesType {
- const preferences = usePreferences();
+ if (!context) {
+ throw new Error('Panel open context must be used within PanelOpenProvider');
+ }
- return useCallback(
- (item: AccordionItem) => {
- const defaultValue: PanelPreferencesType = {
- display: false,
- open: false,
- };
+ return context;
+}
- if (item?.isExperimental && !item.hidePreferenceKey) {
- return {
- display: true,
- open: false,
- };
- }
+export function PanelOpenProviderProvider({ children }) {
+ const [panelOpenState, toggleAccordionItem] = useState({});
+
+ const state = useMemo(() => {
+ function setPanelOpenState(
+ id: keyof NMRiumPanelPreferences,
+ value: boolean,
+ ) {
+ toggleAccordionItem((prev) => ({
+ ...prev,
+ [id]: value,
+ }));
+ }
+ return { setPanelOpenState, panelOpenState };
+ }, [panelOpenState]);
- return lodashGet(
- preferences.current,
- `display.panels.${item.hidePreferenceKey}`,
- defaultValue,
- );
- },
- [preferences],
+ return (
+
+ {children}
+
);
}
-function PanelsInner({ displayerMode: displayedMode }) {
- const getPanelPreferences = usePanelPreferences();
- const isExperimental = useCheckExperimentalFeature();
+function PanelsInner() {
+ const getPanelPreferences = useGetPanelOptions();
+ const { panelOpenState } = usePanelOpenState();
const { dialog, openDialog, closeDialog } = useDialogToggle({
informationModal: false,
});
- const check = useCallback(
- (item) => {
- const panelOptions = getPanelPreferences(item);
-
- return (
- (panelOptions?.display &&
- item.isExperimental === undefined &&
- (item.mode == null || item.mode === displayedMode)) ||
- (item.isExperimental && isExperimental)
- );
- },
- [displayedMode, getPanelPreferences, isExperimental],
- );
-
+ const items = useAccordionItems();
function isOpened(item: AccordionItem) {
const panelOptions = getPanelPreferences(item);
- return panelOptions?.display && panelOptions?.open;
+ return panelOptions?.open;
+ }
+ function isVisible(item: AccordionItem) {
+ const panelOptions = getPanelPreferences(item);
+ return panelOptions?.display;
}
return (
@@ -207,56 +78,65 @@ function PanelsInner({ displayerMode: displayedMode }) {
onCloseDialog={closeDialog}
/>
- {accordionItems.map((item) => {
- const { title, component, id } = item;
- let toolbar;
-
- if (id === 'informationPanel') {
- toolbar = (
- {
- event.stopPropagation();
- openDialog('informationModal');
- }}
- />
- );
- }
- return (
- check(item) && (
+ {items
+ .filter((item) => isVisible(item))
+ .map((item) => {
+ const { title, component, id } = item;
+ return (
{
+ event.stopPropagation();
+ openDialog('informationModal');
+ }}
+ />
+ }
>
{component}
- )
- );
- })}
+ );
+ })}
);
}
-function InformationPanelToolBar(props: {
+function RightButtons(props: {
+ id: keyof NMRiumPanelPreferences;
onEdit: ToolbarItemProps['onClick'];
}) {
- const { onEdit } = props;
+ const { onEdit, id } = props;
const activeSpectrum = useActiveSpectrum();
+ const toggle = useTogglePanel();
+ function handleClosePanel(event) {
+ event?.stopPropagation();
+ toggle(id);
+ }
return (
+ {id === 'informationPanel' && (
+ }
+ onClick={onEdit}
+ tooltip={
+ !activeSpectrum
+ ? 'Select a spectrum to edit its meta information'
+ : 'Edit spectrum meta information'
+ }
+ />
+ )}
}
- onClick={onEdit}
- tooltip={
- !activeSpectrum
- ? 'Select a spectrum to edit its meta information'
- : 'Edit spectrum meta information'
- }
+ icon="cross"
+ tooltip="Close panel"
+ onClick={handleClosePanel}
/>
);
@@ -264,11 +144,6 @@ function InformationPanelToolBar(props: {
const MemoizedPanels = memo(PanelsInner);
-export default function Panels() {
- const {
- displayerMode,
- toolOptions: { selectedTool },
- } = useChartData();
-
- return ;
+export function Panels() {
+ return ;
}
diff --git a/src/component/panels/PanelsBar.tsx b/src/component/panels/PanelsBar.tsx
new file mode 100644
index 000000000..e24ff8ae1
--- /dev/null
+++ b/src/component/panels/PanelsBar.tsx
@@ -0,0 +1,97 @@
+import { ButtonGroup, Colors } from '@blueprintjs/core';
+import styled from '@emotion/styled';
+import type { NMRiumPanelPreferences } from 'nmr-load-save';
+import { ActivityBarItem, Toolbar } from 'react-science/ui';
+import useResizeObserver from 'use-resize-observer';
+
+import type { ToolbarPopoverMenuItem } from '../elements/ToolbarPopoverItem.js';
+import { ToolbarPopoverItem } from '../elements/ToolbarPopoverItem.js';
+
+import { useAccordionItems } from './hooks/useAccordionItems.js';
+import { useGetPanelOptions } from './hooks/useGetPanelOptions.js';
+import { useTogglePanel } from './hooks/useTogglePanel.js';
+
+const PanelsBarContainer = styled(ButtonGroup)`
+ flex-wrap: nowrap;
+ overflow: hidden;
+ height: 100%;
+ gap: 4px;
+ padding: 4px;
+ border-left: 1px solid ${Colors.LIGHT_GRAY4};
+ background-color: ${Colors.WHITE};
+`;
+
+function useHiddenItemsMenu(
+ items,
+ sliceIndex,
+): Array> {
+ const getPanelPreferences = useGetPanelOptions();
+ const hiddenItems = items.slice(sliceIndex);
+
+ return hiddenItems.map((item) => {
+ const panelOptions = getPanelPreferences(item);
+ return {
+ icon: item.icon,
+ text: item.title,
+ active: panelOptions.display,
+ data: {
+ id: item.id,
+ },
+ };
+ });
+}
+
+export function PanelsBar({ itemHeight = 44 }) {
+ const {
+ ref,
+ height,
+ // @ts-expect-error Module is not published correctly.
+ } = useResizeObserver();
+ const getPanelPreferences = useGetPanelOptions();
+
+ const sliceIndex = Math.floor((height - itemHeight) / itemHeight);
+
+ const items = useAccordionItems();
+ const visibleItems = items.filter((_, index) => index < sliceIndex);
+ const menu = useHiddenItemsMenu(items, sliceIndex);
+ const isMenuActive = menu.some((item) => item.active);
+
+ const togglePanel = useTogglePanel();
+
+ if (items.length === 0) return null;
+
+ return (
+
+ {visibleItems.map((item) => {
+ const panelOptions = getPanelPreferences(item);
+ return (
+ togglePanel(item.id)}
+ />
+ );
+ })}
+ {menu.length > 0 && (
+
+ {
+ if (data?.id) {
+ togglePanel(data.id);
+ }
+ }}
+ active={isMenuActive}
+ />
+
+ )}
+
+ );
+}
diff --git a/src/component/panels/accordionItems.tsx b/src/component/panels/accordionItems.tsx
new file mode 100644
index 000000000..c18f46b40
--- /dev/null
+++ b/src/component/panels/accordionItems.tsx
@@ -0,0 +1,158 @@
+import type { IconName } from '@blueprintjs/icons';
+import {
+ SvgNmrAssignment2,
+ SvgNmrIntegrate,
+ SvgNmrMultipleAnalysis,
+ SvgNmrPeakPicking,
+ SvgNmrRangePicking,
+} from 'cheminfo-font';
+import type { NMRiumPanelPreferences } from 'nmr-load-save';
+import type { CSSProperties, ReactElement } from 'react';
+import { FaDiceFour } from 'react-icons/fa';
+import type { AccordionItemProps } from 'react-science/ui';
+
+import type { DisplayerMode } from '../reducer/Reducer.js';
+
+import AutomaticAssignment from './AutomaticAssignment/AutomaticAssignment.js';
+import IntegralPanel from './IntegralsPanel/IntegralPanel.js';
+import { MatrixGenerationPanel } from './MatrixGenerationPanel/MatrixGenerationPanel.js';
+import MoleculePanel from './MoleculesPanel/MoleculePanel.js';
+import PeaksPanel from './PeaksPanel/PeaksPanel.js';
+import RangesPanel from './RangesPanel/RangesPanel.js';
+import SpectrumListPanel from './SpectraPanel/SpectrumListPanel.js';
+import SummaryPanel from './SummaryPanel/SummaryPanel.js';
+import ZonesPanel from './ZonesPanel/ZonesPanel.js';
+import DatabasePanel from './databasePanel/DatabasePanel.js';
+import FilterPanel from './filtersPanel/FilterPanel.js';
+import { InformationPanel } from './informationPanel/InformationPanel.js';
+import MultipleSpectraAnalysisPanel from './multipleAnalysisPanel/MultipleSpectraAnalysisPanel.js';
+import PredictionPane from './predictionPanel/PredictionPanel.js';
+import SpectrumSimulation from './spectrumSimulation/SpectrumSimulation.js';
+
+export interface AccordionItem
+ extends Omit {
+ id: keyof NMRiumPanelPreferences;
+ component: ReactElement;
+ style?: CSSProperties;
+ mode: DisplayerMode | null;
+ isExperimental?: boolean;
+ icon: IconName | ReactElement;
+}
+
+export const accordionItems: AccordionItem[] = [
+ {
+ id: 'spectraPanel',
+ title: 'Spectra',
+ component: ,
+ mode: null,
+ icon: 'list-columns',
+ },
+ {
+ id: 'informationPanel',
+ title: 'Information',
+ component: ,
+ style: { overflow: 'hidden' },
+ mode: null,
+ icon: 'info-sign',
+ },
+ {
+ id: 'peaksPanel',
+ title: 'Peaks',
+ component: ,
+ mode: '1D',
+ icon: ,
+ },
+ {
+ id: 'processingsPanel',
+ title: 'Processings',
+ component: ,
+ mode: null,
+ icon: 'series-derived',
+ },
+ {
+ id: 'integralsPanel',
+ title: 'Integrals',
+ component: ,
+ mode: '1D',
+ icon: ,
+ },
+ {
+ id: 'rangesPanel',
+ title: 'Ranges / Multiplet analysis',
+ component: ,
+ mode: '1D',
+ icon: ,
+ },
+ {
+ id: 'multipleSpectraAnalysisPanel',
+ title: 'Multiple spectra analysis',
+ component: ,
+ mode: null,
+ icon: ,
+ },
+ {
+ id: 'matrixGenerationPanel',
+ title: 'Matrix generation',
+ component: ,
+ mode: '1D',
+ icon: 'derive-column',
+ },
+ {
+ id: 'zonesPanel',
+ title: 'Zones',
+ component: ,
+ mode: '2D',
+ icon: ,
+ },
+ {
+ id: 'summaryPanel',
+ title: 'Summary',
+ component: ,
+ mode: null,
+ icon: 'document',
+ },
+ {
+ id: 'structuresPanel',
+ title: 'Chemical structures',
+ component: ,
+ mode: null,
+ icon: 'hexagon',
+ },
+ {
+ id: 'databasePanel',
+ title: 'Databases',
+ component: ,
+ mode: null,
+ icon: 'database',
+ },
+ {
+ id: 'automaticAssignmentPanel',
+ title: 'Automatic assignment',
+ component: ,
+ mode: null,
+ icon: ,
+ },
+ {
+ id: 'predictionPanel',
+ title: 'Prediction',
+ component: ,
+ mode: null,
+ icon: 'new-grid-item',
+ },
+ {
+ id: 'simulationPanel',
+ title: 'Spectrum simulation',
+ component: ,
+ mode: '1D',
+ icon: 'lab-test',
+ },
+];
+
+export const TOOLS_PANELS_ACCORDION: Record = {
+ null: 'Spectra',
+ peakPicking: 'Peaks',
+ integral: 'Integrals',
+ rangePicking: 'Ranges / Multiplet analysis',
+ zonePicking: 'Zones',
+ multipleSpectraAnalysis: 'Multiple Spectra Analysis',
+};
diff --git a/src/component/panels/checkAccordionItemMode.ts b/src/component/panels/checkAccordionItemMode.ts
new file mode 100644
index 000000000..ceca60be9
--- /dev/null
+++ b/src/component/panels/checkAccordionItemMode.ts
@@ -0,0 +1,10 @@
+import type { DisplayerMode } from '../reducer/Reducer.js';
+
+import type { AccordionItem } from './accordionItems.js';
+
+export function checkAccordionItemMode(
+ item: AccordionItem,
+ displayerMode: DisplayerMode,
+) {
+ return item.mode == null || item.mode === displayerMode;
+}
diff --git a/src/component/panels/hooks/useAccordionItems.tsx b/src/component/panels/hooks/useAccordionItems.tsx
new file mode 100644
index 000000000..39380fe12
--- /dev/null
+++ b/src/component/panels/hooks/useAccordionItems.tsx
@@ -0,0 +1,22 @@
+import { useChartData } from '../../context/ChartContext.js';
+import useCheckExperimentalFeature from '../../hooks/useCheckExperimentalFeature.js';
+import { accordionItems } from '../accordionItems.js';
+import { checkAccordionItemMode } from '../checkAccordionItemMode.js';
+
+import { useGetPanelOptions } from './useGetPanelOptions.js';
+
+export function useAccordionItems() {
+ const { displayerMode } = useChartData();
+ const isExperimentalPanel = useCheckExperimentalFeature();
+ const getPanelPreferences = useGetPanelOptions();
+
+ return accordionItems.filter((item) => {
+ const isVisible = getPanelPreferences(item).visible;
+ const isExperimental =
+ item.isExperimental === undefined ||
+ (item.isExperimental && isExperimentalPanel);
+ const isMatchDisplayerMode = checkAccordionItemMode(item, displayerMode);
+
+ return isExperimental && isMatchDisplayerMode && isVisible;
+ });
+}
diff --git a/src/component/panels/hooks/useGetPanelOptions.ts b/src/component/panels/hooks/useGetPanelOptions.ts
new file mode 100644
index 000000000..21a2be09c
--- /dev/null
+++ b/src/component/panels/hooks/useGetPanelOptions.ts
@@ -0,0 +1,37 @@
+import lodashGet from 'lodash/get.js';
+import type { PanelPreferencesType } from 'nmr-load-save';
+import { useCallback } from 'react';
+
+import { usePreferences } from '../../context/PreferencesContext.js';
+import type { AccordionItem } from '../accordionItems.js';
+
+export function useGetPanelOptions(): (
+ item: AccordionItem,
+) => PanelPreferencesType {
+ const preferences = usePreferences();
+
+ return useCallback(
+ (item: AccordionItem) => {
+ const defaultValue: PanelPreferencesType = {
+ display: false,
+ visible: false,
+ open: false,
+ };
+
+ if (item?.isExperimental && !item.id) {
+ return {
+ display: true,
+ visible: true,
+ open: false,
+ };
+ }
+
+ return lodashGet(
+ preferences.current,
+ `display.panels.${item.id}`,
+ defaultValue,
+ );
+ },
+ [preferences],
+ );
+}
diff --git a/src/component/panels/hooks/useTogglePanel.ts b/src/component/panels/hooks/useTogglePanel.ts
new file mode 100644
index 000000000..b382987c8
--- /dev/null
+++ b/src/component/panels/hooks/useTogglePanel.ts
@@ -0,0 +1,21 @@
+import lodashGet from 'lodash/get.js';
+import type { NMRiumPanelPreferences } from 'nmr-load-save';
+
+import { usePreferences } from '../../context/PreferencesContext.js';
+import { usePanelOpenState } from '../Panels.js';
+
+export function useTogglePanel() {
+ const { dispatch, current } = usePreferences();
+ const { setPanelOpenState } = usePanelOpenState();
+
+ function togglePanel(id: keyof NMRiumPanelPreferences) {
+ const flag = lodashGet(current, `display.panels.${id}.display`);
+ if (typeof flag === 'boolean') {
+ setPanelOpenState(id, !flag);
+ }
+
+ dispatch({ type: 'TOGGLE_PANEL', payload: { id } });
+ }
+
+ return togglePanel;
+}
diff --git a/src/component/reducer/preferences/actions/togglePanel.ts b/src/component/reducer/preferences/actions/togglePanel.ts
new file mode 100644
index 000000000..14a3bf391
--- /dev/null
+++ b/src/component/reducer/preferences/actions/togglePanel.ts
@@ -0,0 +1,30 @@
+import type { Draft } from 'immer';
+import type { PanelPreferencesType } from 'nmr-load-save';
+
+import type {
+ PreferencesState,
+ TogglePanelAction,
+} from '../preferencesReducer.js';
+import { getActiveWorkspace } from '../utilities/getActiveWorkspace.js';
+
+export function togglePanel(
+ draft: Draft,
+ action: TogglePanelAction,
+) {
+ const currentWorkspacePreferences = getActiveWorkspace(draft);
+ const { id } = action.payload || {};
+
+ if (!id) return;
+
+ const panels = currentWorkspacePreferences.display.panels || {};
+
+ const existingPanel: PanelPreferencesType = panels[id];
+
+ currentWorkspacePreferences.display.panels = {
+ ...panels,
+ [id]: {
+ ...existingPanel,
+ display: existingPanel ? !existingPanel.display : true,
+ },
+ };
+}
diff --git a/src/component/reducer/preferences/preferencesReducer.ts b/src/component/reducer/preferences/preferencesReducer.ts
index bac845cf1..3bd4b5a34 100644
--- a/src/component/reducer/preferences/preferencesReducer.ts
+++ b/src/component/reducer/preferences/preferencesReducer.ts
@@ -46,6 +46,7 @@ import { setPreferences } from './actions/setPreferences.js';
import { setVerticalSplitterPosition } from './actions/setVerticalSplitterPosition.js';
import { setWorkspace } from './actions/setWorkspace.js';
import { toggleInformationBlock } from './actions/toggleInformationBlock.js';
+import { togglePanel } from './actions/togglePanel.js';
import { mapWorkspaces } from './utilities/mapWorkspaces.js';
const LOCAL_STORAGE_SETTINGS_KEY = 'nmr-general-settings';
@@ -147,6 +148,8 @@ export type ChangePeaksLabelPositionAction = ActionType<
}
>;
+export type TogglePanelAction = ActionType<'TOGGLE_PANEL', { id: string }>;
+
type PreferencesActions =
| InitPreferencesAction
| SetPreferencesAction
@@ -165,7 +168,8 @@ type PreferencesActions =
| ToggleInformationBlock
| ChangePrintPageSettingsAction
| ChangeExportSettingsAction
- | ChangePeaksLabelPositionAction;
+ | ChangePeaksLabelPositionAction
+ | TogglePanelAction;
export const WORKSPACES: Array<{
key: NMRiumWorkspace;
@@ -322,6 +326,8 @@ function innerPreferencesReducer(
return changeExportSettings(draft, action);
case 'CHANGE_PEAKS_LABEL_POSITION':
return changePeaksLabelPosition(draft, action);
+ case 'TOGGLE_PANEL':
+ return togglePanel(draft, action);
default:
return draft;
diff --git a/src/component/workspaces/assignment.ts b/src/component/workspaces/assignment.ts
index 8dd3c9748..807ba5dbd 100644
--- a/src/component/workspaces/assignment.ts
+++ b/src/component/workspaces/assignment.ts
@@ -5,12 +5,12 @@ export const assignment: InnerWorkspace = {
visible: true,
display: {
panels: {
- spectraPanel: { display: true, open: true },
- informationPanel: { display: true, open: false },
- rangesPanel: { display: true, open: false },
- structuresPanel: { display: true, open: false },
- processingsPanel: { display: true, open: false },
- zonesPanel: { display: true, open: false },
+ spectraPanel: { display: true, visible: true, open: true },
+ informationPanel: { display: true, visible: true, open: false },
+ rangesPanel: { display: true, visible: true, open: false },
+ structuresPanel: { display: true, visible: true, open: false },
+ processingsPanel: { display: true, visible: true, open: false },
+ zonesPanel: { display: true, visible: true, open: false },
},
toolBarButtons: {
zoom: true,
diff --git a/src/component/workspaces/basic.ts b/src/component/workspaces/basic.ts
index 4853c20c9..1cda0e890 100644
--- a/src/component/workspaces/basic.ts
+++ b/src/component/workspaces/basic.ts
@@ -5,14 +5,14 @@ export const basic: InnerWorkspace = {
visible: true,
display: {
panels: {
- spectraPanel: { display: true, open: true },
- informationPanel: { display: true, open: false },
- peaksPanel: { display: true, open: false },
- integralsPanel: { display: true, open: false },
- rangesPanel: { display: true, open: false },
- structuresPanel: { display: true, open: false },
- processingsPanel: { display: true, open: false },
- zonesPanel: { display: true, open: false },
+ spectraPanel: { display: true, visible: true, open: true },
+ informationPanel: { display: true, visible: true, open: false },
+ peaksPanel: { display: true, visible: true, open: false },
+ integralsPanel: { display: true, visible: true, open: false },
+ rangesPanel: { display: true, visible: true, open: false },
+ structuresPanel: { display: true, visible: true, open: false },
+ processingsPanel: { display: true, visible: true, open: false },
+ zonesPanel: { display: true, visible: true, open: false },
},
toolBarButtons: {
zoom: true,
diff --git a/src/component/workspaces/embedded.ts b/src/component/workspaces/embedded.ts
index 7fceb8f4b..ec4c22c19 100644
--- a/src/component/workspaces/embedded.ts
+++ b/src/component/workspaces/embedded.ts
@@ -4,7 +4,7 @@ export const embedded: InnerWorkspace = {
label: 'Embedded',
display: {
general: {
- experimentalFeatures: { display: true },
+ experimentalFeatures: { display: true, visible: true },
hidePanelOnLoad: true,
hideHelp: true,
hideLogs: true,
@@ -14,14 +14,14 @@ export const embedded: InnerWorkspace = {
},
panels: {
- spectraPanel: { display: true, open: true },
- informationPanel: { display: true, open: false },
- peaksPanel: { display: true, open: false },
- integralsPanel: { display: true, open: false },
- rangesPanel: { display: true, open: false },
- structuresPanel: { display: true, open: false },
- processingsPanel: { display: true, open: false },
- zonesPanel: { display: true, open: false },
+ spectraPanel: { display: true, visible: true, open: true },
+ informationPanel: { display: true, visible: true, open: false },
+ peaksPanel: { display: true, visible: true, open: false },
+ integralsPanel: { display: true, visible: true, open: false },
+ rangesPanel: { display: true, visible: true, open: false },
+ structuresPanel: { display: true, visible: true, open: false },
+ processingsPanel: { display: true, visible: true, open: false },
+ zonesPanel: { display: true, visible: true, open: false },
},
toolBarButtons: {
peakPicking: true,
diff --git a/src/component/workspaces/exercise.ts b/src/component/workspaces/exercise.ts
index 8037e3fb5..a665fcac3 100644
--- a/src/component/workspaces/exercise.ts
+++ b/src/component/workspaces/exercise.ts
@@ -9,8 +9,8 @@ export const exercise: InnerWorkspace = {
hideWorkspaces: true,
},
panels: {
- spectraPanel: { display: true, open: true },
- integralsPanel: { display: true },
+ spectraPanel: { display: true, visible: true, open: true },
+ integralsPanel: { display: true, visible: true },
},
toolBarButtons: {
zoom: true,
diff --git a/src/component/workspaces/prediction.ts b/src/component/workspaces/prediction.ts
index 3aacdc422..3a872c6c6 100644
--- a/src/component/workspaces/prediction.ts
+++ b/src/component/workspaces/prediction.ts
@@ -18,10 +18,10 @@ export const prediction: InnerWorkspace = {
hideWorkspaces: true,
},
panels: {
- spectraPanel: { display: true },
- rangesPanel: { display: true },
- zonesPanel: { display: true },
- predictionPanel: { display: true, open: true },
+ spectraPanel: { display: true, visible: true },
+ rangesPanel: { display: true, visible: true },
+ zonesPanel: { display: true, visible: true },
+ predictionPanel: { display: true, visible: true, open: true },
},
toolBarButtons: {
baselineCorrection: true,
diff --git a/src/component/workspaces/process1D.ts b/src/component/workspaces/process1D.ts
index be57ea1e7..c269a4e26 100644
--- a/src/component/workspaces/process1D.ts
+++ b/src/component/workspaces/process1D.ts
@@ -7,11 +7,11 @@ export const process1D: InnerWorkspace = {
general: {},
panels: {
- spectraPanel: { display: true, open: true },
- informationPanel: { display: true },
- peaksPanel: { display: true },
- processingsPanel: { display: true },
- multipleSpectraAnalysisPanel: { display: true },
+ spectraPanel: { display: true, visible: true, open: true },
+ informationPanel: { display: true, visible: true },
+ peaksPanel: { display: true, visible: true },
+ processingsPanel: { display: true, visible: true },
+ multipleSpectraAnalysisPanel: { display: true, visible: true },
},
toolBarButtons: {
diff --git a/src/component/workspaces/simulation.ts b/src/component/workspaces/simulation.ts
index 25104cd87..ec322f086 100644
--- a/src/component/workspaces/simulation.ts
+++ b/src/component/workspaces/simulation.ts
@@ -18,8 +18,8 @@ export const simulation: InnerWorkspace = {
hideWorkspaces: true,
},
panels: {
- spectraPanel: { display: true },
- simulationPanel: { display: true, open: true },
+ spectraPanel: { display: true, visible: true },
+ simulationPanel: { display: true, visible: true, open: true },
},
toolBarButtons: {
exportAs: true,
diff --git a/src/component/workspaces/workspaceDefaultProperties.ts b/src/component/workspaces/workspaceDefaultProperties.ts
index c634dce85..e381afda1 100644
--- a/src/component/workspaces/workspaceDefaultProperties.ts
+++ b/src/component/workspaces/workspaceDefaultProperties.ts
@@ -7,7 +7,7 @@ export const workspaceDefaultProperties: Required = {
display: {
general: {
hideGeneralSettings: false,
- experimentalFeatures: { display: false },
+ experimentalFeatures: { display: false, visible: true },
hidePanelOnLoad: false,
hideLogs: false,
hideHelp: false,
@@ -16,20 +16,24 @@ export const workspaceDefaultProperties: Required = {
},
panels: {
- spectraPanel: { display: false, open: false },
- informationPanel: { display: false, open: false },
- integralsPanel: { display: false, open: false },
- rangesPanel: { display: false, open: false },
- structuresPanel: { display: false, open: false },
- processingsPanel: { display: false, open: false },
- zonesPanel: { display: false, open: false },
- automaticAssignmentPanel: { display: false, open: false },
- databasePanel: { display: false, open: false },
- multipleSpectraAnalysisPanel: { display: false, open: false },
- peaksPanel: { display: false, open: false },
- predictionPanel: { display: false, open: false },
- summaryPanel: { display: false, open: false },
- simulationPanel: { display: false, open: false },
+ spectraPanel: { display: false, visible: false, open: false },
+ informationPanel: { display: false, visible: false, open: false },
+ integralsPanel: { display: false, visible: false, open: false },
+ rangesPanel: { display: false, visible: false, open: false },
+ structuresPanel: { display: false, visible: false, open: false },
+ processingsPanel: { display: false, visible: false, open: false },
+ zonesPanel: { display: false, visible: false, open: false },
+ automaticAssignmentPanel: { display: false, visible: false, open: false },
+ databasePanel: { display: false, visible: false, open: false },
+ multipleSpectraAnalysisPanel: {
+ display: false,
+ visible: false,
+ open: false,
+ },
+ peaksPanel: { display: false, visible: false, open: false },
+ predictionPanel: { display: false, visible: false, open: false },
+ summaryPanel: { display: false, visible: false, open: false },
+ simulationPanel: { display: false, visible: false, open: false },
},
toolBarButtons: {
baselineCorrection: false,
diff --git a/src/demo/views/BenchtopNMRWorkspace.tsx b/src/demo/views/BenchtopNMRWorkspace.tsx
index 639e89e4e..59c612c51 100644
--- a/src/demo/views/BenchtopNMRWorkspace.tsx
+++ b/src/demo/views/BenchtopNMRWorkspace.tsx
@@ -9,6 +9,7 @@ const customWorkspaces: CustomWorkspaces = {
hideGeneralSettings: false,
experimentalFeatures: {
display: true,
+ visible: true,
},
hidePanelOnLoad: false,
hideLogs: false,
@@ -19,58 +20,72 @@ const customWorkspaces: CustomWorkspaces = {
panels: {
spectraPanel: {
display: true,
+ visible: true,
open: true,
},
informationPanel: {
display: true,
+ visible: true,
open: false,
},
integralsPanel: {
display: false,
+ visible: false,
open: false,
},
rangesPanel: {
display: true,
+ visible: true,
open: false,
},
structuresPanel: {
display: true,
+ visible: true,
open: false,
},
processingsPanel: {
display: true,
+ visible: true,
open: true,
},
zonesPanel: {
display: true,
+ visible: true,
open: false,
},
automaticAssignmentPanel: {
display: false,
+ visible: false,
open: false,
},
databasePanel: {
display: false,
+ visible: false,
open: false,
},
multipleSpectraAnalysisPanel: {
display: false,
+ visible: false,
open: false,
},
peaksPanel: {
display: false,
+ visible: false,
open: false,
},
predictionPanel: {
display: false,
+ visible: false,
open: false,
},
summaryPanel: {
display: false,
+ visible: false,
open: false,
},
simulationPanel: {
display: false,
+ visible: false,
open: false,
},
},
diff --git a/src/demo/views/CustomWorkspace.tsx b/src/demo/views/CustomWorkspace.tsx
index bcdebc8fe..c60ca4137 100644
--- a/src/demo/views/CustomWorkspace.tsx
+++ b/src/demo/views/CustomWorkspace.tsx
@@ -7,24 +7,32 @@ const customWorkspaces: CustomWorkspaces = {
display: {
general: {
hideGeneralSettings: false,
- experimentalFeatures: { display: false },
+ experimentalFeatures: { display: false, visible: false },
hidePanelOnLoad: false,
},
panels: {
- spectraPanel: { display: true, open: true },
- informationPanel: { display: true, open: false },
- integralsPanel: { display: false, open: false },
- rangesPanel: { display: false, open: false },
- structuresPanel: { display: false, open: false },
- processingsPanel: { display: false, open: false },
- zonesPanel: { display: false, open: false },
- automaticAssignmentPanel: { display: false, open: false },
- databasePanel: { display: true, open: false },
- multipleSpectraAnalysisPanel: { display: true, open: true },
- peaksPanel: { display: false, open: false },
- predictionPanel: { display: false, open: false },
- summaryPanel: { display: false, open: false },
- matrixGenerationPanel: { display: true },
+ spectraPanel: { display: true, visible: true, open: true },
+ informationPanel: { display: true, visible: true, open: false },
+ integralsPanel: { display: false, visible: false, open: false },
+ rangesPanel: { display: false, visible: false, open: false },
+ structuresPanel: { display: false, visible: false, open: false },
+ processingsPanel: { display: false, visible: false, open: false },
+ zonesPanel: { display: false, visible: false, open: false },
+ automaticAssignmentPanel: {
+ display: false,
+ visible: false,
+ open: false,
+ },
+ databasePanel: { display: true, visible: true, open: false },
+ multipleSpectraAnalysisPanel: {
+ display: true,
+ visible: true,
+ open: true,
+ },
+ peaksPanel: { display: false, visible: false, open: false },
+ predictionPanel: { display: false, visible: false, open: false },
+ summaryPanel: { display: false, visible: false, open: false },
+ matrixGenerationPanel: { display: true, visible: true },
},
toolBarButtons: {
baselineCorrection: false,
diff --git a/src/demo/views/SnapshotView.tsx b/src/demo/views/SnapshotView.tsx
index 5f49fce54..69f890788 100644
--- a/src/demo/views/SnapshotView.tsx
+++ b/src/demo/views/SnapshotView.tsx
@@ -9,60 +9,74 @@ const customWorkspaces: CustomWorkspaces = {
hideGeneralSettings: false,
experimentalFeatures: {
display: true,
+ visible: true,
},
hidePanelOnLoad: false,
},
panels: {
spectraPanel: {
display: true,
+ visible: true,
open: true,
},
informationPanel: {
display: true,
+ visible: true,
open: false,
},
integralsPanel: {
display: false,
+ visible: false,
open: false,
},
rangesPanel: {
display: true,
+ visible: true,
open: false,
},
structuresPanel: {
display: true,
+ visible: true,
open: false,
},
processingsPanel: {
display: true,
+ visible: true,
open: false,
},
zonesPanel: {
display: true,
+ visible: true,
open: false,
},
automaticAssignmentPanel: {
display: false,
+ visible: false,
open: false,
},
databasePanel: {
display: false,
+ visible: false,
open: false,
},
multipleSpectraAnalysisPanel: {
display: false,
+ visible: false,
open: false,
},
peaksPanel: {
display: false,
+ visible: false,
open: false,
},
predictionPanel: {
display: false,
+ visible: false,
open: false,
},
summaryPanel: {
display: false,
+ visible: false,
open: false,
},
},
diff --git a/src/demo/views/Teaching.tsx b/src/demo/views/Teaching.tsx
index b73a770ae..592805672 100644
--- a/src/demo/views/Teaching.tsx
+++ b/src/demo/views/Teaching.tsx
@@ -84,11 +84,14 @@ export default function Teaching(props) {
preferences={{
display: {
panels: {
- informationPanel: { display: true },
- peaksPanel: { display: true },
- integralsPanel: { display: true },
- processingsPanel: { display: true },
- multipleSpectraAnalysisPanel: { display: true },
+ informationPanel: { display: true, visible: true },
+ peaksPanel: { display: true, visible: true },
+ integralsPanel: { display: true, visible: true },
+ processingsPanel: { display: true, visible: true },
+ multipleSpectraAnalysisPanel: {
+ display: true,
+ visible: true,
+ },
},
},
}}
diff --git a/test-e2e/core/setting.test.ts b/test-e2e/core/setting.test.ts
index 32662f6cd..f8670fc1f 100644
--- a/test-e2e/core/setting.test.ts
+++ b/test-e2e/core/setting.test.ts
@@ -21,8 +21,11 @@ test('should Change the visibility of the panels', async ({ page }) => {
//click on the display tab
await nmrium.page.getByRole('tablist').locator('text=Panels').click();
- //change the checkbox value to tru
+ //change the checkbox value to true
await nmrium.page.locator('td:has-text("Databases panel") + td').click();
+ await nmrium.page
+ .locator('td:has-text("Databases panel") + td + td')
+ .click();
await nmrium.saveWorkspaceModal('test');
diff --git a/test-e2e/panels/autoassignment.test.ts b/test-e2e/panels/autoassignment.test.ts
index f7ba8c3b8..b3820f2d2 100644
--- a/test-e2e/panels/autoassignment.test.ts
+++ b/test-e2e/panels/autoassignment.test.ts
@@ -13,9 +13,13 @@ test('automatic assignment panel', async ({ page }) => {
await nmrium.page.click('_react=ToolbarItem[id="general-settings"]');
await nmrium.page.click('div[role="dialog"] >> text=Panels');
+ //change the checkbox value for 'visible' and 'display' to true
await nmrium.page
.locator('td:has-text("Automatic assignment panel") + td')
.click();
+ await nmrium.page
+ .locator('td:has-text("Automatic assignment panel") + td + td')
+ .click();
await nmrium.saveWorkspaceModal('test');
});
diff --git a/test-e2e/panels/ranges.test.ts b/test-e2e/panels/ranges.test.ts
index 811eefff4..24f54b26b 100644
--- a/test-e2e/panels/ranges.test.ts
+++ b/test-e2e/panels/ranges.test.ts
@@ -95,8 +95,8 @@ test('Should ranges Add/resize/delete', async ({ page }) => {
await test.step('Add ranges', async () => {
//add two ranges
- await addRange(nmrium, 200, 210, 1);
- await addRange(nmrium, 110, 120, 2);
+ await addRange(nmrium, 160, 170, 1);
+ await addRange(nmrium, 70, 80, 2);
});
await test.step('Clicking and subsequently releasing the resizing action should not crash or add a new range', async () => {