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 () => {