From 335ca66fea46f0171dc758388fe1f9d8221bac08 Mon Sep 17 00:00:00 2001 From: hamed musallam Date: Fri, 8 May 2026 11:53:35 +0200 Subject: [PATCH 1/7] feat: display spectrum label in stack mode --- src/component/1d/LinesSeries.tsx | 2 + src/component/1d/SpectrumLabel.tsx | 63 +++++++++++++++++++ .../SpectraPanel/SpectraPanelHeader.tsx | 20 +++++- .../SpectraPanel/SpectraPreferences.tsx | 1 + .../base/SpectraColumnsManager.tsx | 10 +++ .../preferences/actions/toggleSpectraLabel.ts | 27 ++++++++ .../panelsPreferencesDefaultValues.ts | 5 ++ .../reducer/preferences/preferencesReducer.ts | 9 ++- 8 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 src/component/1d/SpectrumLabel.tsx create mode 100644 src/component/reducer/preferences/actions/toggleSpectraLabel.ts diff --git a/src/component/1d/LinesSeries.tsx b/src/component/1d/LinesSeries.tsx index f01cbc16c7..59b18e5959 100644 --- a/src/component/1d/LinesSeries.tsx +++ b/src/component/1d/LinesSeries.tsx @@ -10,6 +10,7 @@ import { useVerticalAlign } from '../hooks/useVerticalAlign.js'; import { useVisibleSpectra1D } from '../hooks/use_visible_spectra_1d.ts'; import Line from './Line.js'; +import { SpectrumLabel } from './SpectrumLabel.tsx'; import { useInsetOptions } from './inset/InsetProvider.js'; const BOX_SIZE = 10; @@ -31,6 +32,7 @@ function LinesSeries() { {spectra.map((d, i) => ( + diff --git a/src/component/1d/SpectrumLabel.tsx b/src/component/1d/SpectrumLabel.tsx new file mode 100644 index 0000000000..3cedacc864 --- /dev/null +++ b/src/component/1d/SpectrumLabel.tsx @@ -0,0 +1,63 @@ +import type { SpectraTableColumn, Spectrum } from '@zakodium/nmrium-core'; +import dlv from 'dlv'; + +import { useChartData } from '../context/ChartContext.tsx'; +import { useScaleChecked } from '../context/ScaleContext.tsx'; +import { usePanelPreferences } from '../hooks/usePanelPreferences.ts'; +import { useVerticalAlign } from '../hooks/useVerticalAlign.ts'; + +export function SpectrumLabel({ + index, + spectrum, +}: { + index: number; + spectrum: Spectrum; +}) { + const { spectraBottomMargin, shiftY } = useScaleChecked(); + const { + height, + margin, + toolOptions: { selectedTool }, + view: { + spectra: { activeTab }, + }, + } = useChartData(); + const { columns, enableSpectraLabel } = usePanelPreferences( + 'spectra', + activeTab, + ); + const verticalAlign = useVerticalAlign(); + + if ( + verticalAlign !== 'stack' || + selectedTool !== 'zoom' || + !Array.isArray(columns) || + columns.length === 0 || + !enableSpectraLabel + ) { + return null; + } + + const innerHeight = height - margin.bottom - spectraBottomMargin; + const label = getSpectrumLabel(columns, spectrum); + + return ( + + {label} + + ); +} + +function getSpectrumLabel( + columns: SpectraTableColumn[], + spectrum: Spectrum, +): string { + return columns + .filter((column) => column.isSpectrumLabel) + .map((column) => (column.jpath ? dlv(spectrum, column.jpath, '') : '')) + .join(', '); +} diff --git a/src/component/panels/SpectraPanel/SpectraPanelHeader.tsx b/src/component/panels/SpectraPanel/SpectraPanelHeader.tsx index b5761e7647..d82ad34eb5 100644 --- a/src/component/panels/SpectraPanel/SpectraPanelHeader.tsx +++ b/src/component/panels/SpectraPanel/SpectraPanelHeader.tsx @@ -7,7 +7,7 @@ import { SvgNmrResetScale, SvgNmrSameTop } from 'cheminfo-font'; import { memo, useCallback } from 'react'; import { FaCreativeCommonsSamplingPlus } from 'react-icons/fa'; import { IoColorPaletteOutline } from 'react-icons/io5'; -import { MdFormatColorFill } from 'react-icons/md'; +import { MdFormatColorFill, MdOutlineFormatColorText } from 'react-icons/md'; import { useChartData } from '../../context/ChartContext.js'; import { useDispatch } from '../../context/DispatchContext.js'; @@ -15,9 +15,11 @@ import { usePreferences } from '../../context/PreferencesContext.js'; import { useToaster } from '../../context/ToasterContext.js'; import { useAlert } from '../../elements/Alert.js'; import { useActiveSpectra } from '../../hooks/useActiveSpectra.js'; +import { usePanelPreferences } from '../../hooks/usePanelPreferences.ts'; import useSpectrum from '../../hooks/useSpectrum.js'; import { useToggleSpectraVisibility } from '../../hooks/useToggleSpectraVisibility.js'; import type { DisplayerMode } from '../../reducer/Reducer.js'; +import { booleanToString } from '../../utility/booleanToString.ts'; import { getSpectraByNucleus } from '../../utility/getSpectraByNucleus.js'; import type { ToolbarItemProps } from '../header/DefaultPanelHeader.js'; import DefaultPanelHeader from '../header/DefaultPanelHeader.js'; @@ -60,7 +62,9 @@ function SpectraPanelHeaderInner({ const dispatch = useDispatch(); const { current: { spectraColors }, + dispatch: dispatchPreferences, } = usePreferences(); + const { enableSpectraLabel } = usePanelPreferences('spectra', activeTab); const handleDelete = useCallback(() => { alert.showAlert({ @@ -116,6 +120,14 @@ function SpectraPanelHeaderInner({ payload: {}, }); } + + function toggleSpectraLabelHandler() { + dispatchPreferences({ + type: 'TOGGLE_SPECTRA_LABEL', + payload: { nucleus: activeTab }, + }); + } + const hasActiveSpectra = activeSpectra && activeSpectra?.length > 0; const spectraLengthPerTab = getSpectraByNucleus(activeTab, data)?.length; const { getToggleVisibilityButtons } = useToggleSpectraVisibility( @@ -168,6 +180,12 @@ function SpectraPanelHeaderInner({ tooltip: 'Distinct spectra recoloring', onClick: distinctReColorSpectraHandler, }, + { + icon: , + tooltip: `${booleanToString(!enableSpectraLabel)} spectra label`, + active: enableSpectraLabel, + onClick: toggleSpectraLabelHandler, + }, ]); return ( diff --git a/src/component/panels/SpectraPanel/SpectraPreferences.tsx b/src/component/panels/SpectraPanel/SpectraPreferences.tsx index de80bc8765..f078b15a9e 100644 --- a/src/component/panels/SpectraPanel/SpectraPreferences.tsx +++ b/src/component/panels/SpectraPanel/SpectraPreferences.tsx @@ -129,6 +129,7 @@ function SpectraPreferences(props: object, ref: Ref) { jpath: [], label: '', visible: true, + isSpectrumLabel: false, }, ...columns.slice(index), ]; diff --git a/src/component/panels/SpectraPanel/base/SpectraColumnsManager.tsx b/src/component/panels/SpectraPanel/base/SpectraColumnsManager.tsx index ea0c8074f3..8b8958780f 100644 --- a/src/component/panels/SpectraPanel/base/SpectraColumnsManager.tsx +++ b/src/component/panels/SpectraPanel/base/SpectraColumnsManager.tsx @@ -115,6 +115,16 @@ export function SpectraColumnsManager({ /> ), }, + { + Header: 'spectrum label', + style: { width: '30px', textAlign: 'center' }, + Cell: ({ row }: CellProps) => ( + + ), + }, { Header: '', style: { width: '65px' }, diff --git a/src/component/reducer/preferences/actions/toggleSpectraLabel.ts b/src/component/reducer/preferences/actions/toggleSpectraLabel.ts new file mode 100644 index 0000000000..8208882058 --- /dev/null +++ b/src/component/reducer/preferences/actions/toggleSpectraLabel.ts @@ -0,0 +1,27 @@ +import type { Draft } from 'immer'; + +import type { + PreferencesState, + ToggleSpectraLabelAction, +} from '../preferencesReducer.js'; +import { getActiveWorkspace } from '../utilities/getActiveWorkspace.js'; + +export function toggleSpectraLabel( + draft: Draft, + action: ToggleSpectraLabelAction, +) { + if (action.payload) { + const currentWorkspacePreferences = getActiveWorkspace(draft); + + const { nucleus } = action.payload; + const nucleusPreferences = + currentWorkspacePreferences.panels.spectra?.nuclei[nucleus]; + + if (!nucleusPreferences) { + return; + } + + nucleusPreferences.enableSpectraLabel = + !nucleusPreferences.enableSpectraLabel; + } +} diff --git a/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts b/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts index af9b459435..7097a3e8ac 100644 --- a/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts +++ b/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts @@ -33,21 +33,25 @@ const getSpectraDefaultValues = ( label: 'Spectrum Name', jpath: ['info', 'name'], visible: true, + isSpectrumLabel: true, }, { label: 'Solvent', jpath: ['info', 'solvent'], visible: true, + isSpectrumLabel: false, }, { jpath: ['info', 'pulseSequence'], label: 'Pulse', visible: true, + isSpectrumLabel: false, }, { jpath: ['info', 'experiment'], label: 'Experiment', visible: true, + isSpectrumLabel: false, }, { name: 'color', @@ -56,6 +60,7 @@ const getSpectraDefaultValues = ( visible: true, }, ], + enableSpectraLabel: true, }; return getPreferences(preferences, nucleus); diff --git a/src/component/reducer/preferences/preferencesReducer.ts b/src/component/reducer/preferences/preferencesReducer.ts index b640855254..e8b768884c 100644 --- a/src/component/reducer/preferences/preferencesReducer.ts +++ b/src/component/reducer/preferences/preferencesReducer.ts @@ -57,6 +57,7 @@ import { setWorkspace } from './actions/setWorkspace.js'; import { toggleInformationBlock } from './actions/toggleInformationBlock.js'; import { toggleOpenSplitPanel } from './actions/toggleOpenSplitPanel.js'; import { togglePanel } from './actions/togglePanel.js'; +import { toggleSpectraLabel } from './actions/toggleSpectraLabel.ts'; import { mapWorkspaces } from './utilities/mapWorkspaces.js'; const LOCAL_STORAGE_SETTINGS_KEY = 'nmr-general-settings'; @@ -91,6 +92,10 @@ export type SetPanelsPreferencesAction = ActionType< 'SET_PANELS_PREFERENCES', { key: keyof PanelsPreferences; value: any } >; +export type ToggleSpectraLabelAction = ActionType< + 'TOGGLE_SPECTRA_LABEL', + { nucleus: string } +>; export type SetWorkspaceAction = ActionType< 'SET_WORKSPACE', @@ -196,6 +201,7 @@ export type PreferencesActions = | InitPreferencesAction | SetPreferencesAction | SetPanelsPreferencesAction + | ToggleSpectraLabelAction | SetWorkspaceAction | WorkspaceAction | AddWorkspaceAction @@ -345,7 +351,8 @@ function innerPreferencesReducer( return changeDefaultMoleculeSettings(draft, action); case 'TOGGLE_SPLIT_PANEL': return toggleOpenSplitPanel(draft, action); - + case 'TOGGLE_SPECTRA_LABEL': + return toggleSpectraLabel(draft, action); default: return draft; } From 0495fe08eec22673cb8b190bebe262bb05ec4ed6 Mon Sep 17 00:00:00 2001 From: hamed musallam Date: Mon, 11 May 2026 10:32:25 +0200 Subject: [PATCH 2/7] feat: format numeric columns in spectra table --- .../SpectraPanel/SpectraPreferences.tsx | 1 + .../panels/SpectraPanel/SpectraTable.tsx | 31 +++++++------ .../base/SpectraColumnsManager.tsx | 17 +++++++ .../panelsPreferencesDefaultValues.ts | 4 ++ src/component/workspaces/prediction.ts | 44 +++++++++++++++++++ src/component/workspaces/simulation.ts | 7 +++ 6 files changed, 91 insertions(+), 13 deletions(-) diff --git a/src/component/panels/SpectraPanel/SpectraPreferences.tsx b/src/component/panels/SpectraPanel/SpectraPreferences.tsx index f078b15a9e..1559233547 100644 --- a/src/component/panels/SpectraPanel/SpectraPreferences.tsx +++ b/src/component/panels/SpectraPanel/SpectraPreferences.tsx @@ -130,6 +130,7 @@ function SpectraPreferences(props: object, ref: Ref) { label: '', visible: true, isSpectrumLabel: false, + format: '', }, ...columns.slice(index), ]; diff --git a/src/component/panels/SpectraPanel/SpectraTable.tsx b/src/component/panels/SpectraPanel/SpectraTable.tsx index fc2f625c54..a4c8a79674 100644 --- a/src/component/panels/SpectraPanel/SpectraTable.tsx +++ b/src/component/panels/SpectraPanel/SpectraTable.tsx @@ -28,6 +28,7 @@ import type { Column } from '../../elements/ReactTable/ReactTable.js'; import ReactTable from '../../elements/ReactTable/ReactTable.js'; import { usePanelPreferences } from '../../hooks/usePanelPreferences.js'; import ExportAsJcampModal from '../../modal/ExportAsJcampModal.js'; +import { formatNumber } from '../../utility/formatNumber.ts'; import { saveAs } from '../../utility/save_as.js'; import { RenderAsHTML } from './base/RenderAsHTML.js'; @@ -298,7 +299,7 @@ export function SpectraTable(props: SpectraTableProps) { ); for (const col of visibleColumns) { const name = (col as PredefinedTableColumn)?.name; - const path = (col as JpathTableColumn)?.jpath; + const { jpath, format } = col as JpathTableColumn; if (name && COLUMNS[name]) { columns.push({ @@ -307,7 +308,7 @@ export function SpectraTable(props: SpectraTableProps) { id: name, }); } else { - const pathString = pathToString(path); + const pathString = pathToString(jpath); let style: CSSProperties = columnStyle; let cellRender: Column['Cell'] | null = null; if (pathString === 'info.name') { @@ -327,7 +328,7 @@ export function SpectraTable(props: SpectraTableProps) { const cell: Column = { Header: () => , - accessor: (row) => getValue(row, path), + accessor: (row) => getValue(row, jpath, format), ...(cellRender && { Cell: cellRender }), id: `${index}`, style, @@ -444,20 +445,24 @@ function pathToString(path: string[]) { return Array.isArray(path) ? path.join('.') : ''; } -function getValue(row: any, path: any) { +function formatValue(value: any, format = '') { + if (format.trim()) { + return formatNumber(value, format); + } + return value; +} + +function getValue(row: any, path: any, format = ' ') { const value = dlv(row, path, ''); const pathString = pathToString(path); - if ( - Array.isArray(value) && - ['info.baseFrequency', 'info.originFrequency'].includes(pathString) - ) { - return value[0]; - } - if (Array.isArray(value)) { - return value.join(','); + if (['info.baseFrequency', 'info.originFrequency'].includes(pathString)) { + return formatValue(value[0], format); + } + + return value.map((v: any) => formatValue(v, format)).join(','); } - return value; + return formatValue(value, format); } diff --git a/src/component/panels/SpectraPanel/base/SpectraColumnsManager.tsx b/src/component/panels/SpectraPanel/base/SpectraColumnsManager.tsx index 8b8958780f..c3f55f59fb 100644 --- a/src/component/panels/SpectraPanel/base/SpectraColumnsManager.tsx +++ b/src/component/panels/SpectraPanel/base/SpectraColumnsManager.tsx @@ -105,6 +105,23 @@ export function SpectraColumnsManager({ ); }, }, + { + Header: 'Format', + style: { padding: 0 }, + Cell: ({ row }: CellProps) => { + const column: any = row.original; + const name = getObjectKey(nucleus, row.index, 'format'); + return ( + + ); + }, + }, { Header: 'Visible', style: { width: '30px', textAlign: 'center' }, diff --git a/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts b/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts index 7097a3e8ac..30b8c98f1e 100644 --- a/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts +++ b/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts @@ -34,24 +34,28 @@ const getSpectraDefaultValues = ( jpath: ['info', 'name'], visible: true, isSpectrumLabel: true, + format: '', }, { label: 'Solvent', jpath: ['info', 'solvent'], visible: true, isSpectrumLabel: false, + format: '', }, { jpath: ['info', 'pulseSequence'], label: 'Pulse', visible: true, isSpectrumLabel: false, + format: '', }, { jpath: ['info', 'experiment'], label: 'Experiment', visible: true, isSpectrumLabel: false, + format: '', }, { name: 'color', diff --git a/src/component/workspaces/prediction.ts b/src/component/workspaces/prediction.ts index e8e95285c9..45f6ac48a5 100644 --- a/src/component/workspaces/prediction.ts +++ b/src/component/workspaces/prediction.ts @@ -67,6 +67,7 @@ export const prediction: InnerWorkspace = { spectra: { nuclei: { '1H,1H': { + enableSpectraLabel: true, columns: [ { name: 'visible', @@ -78,27 +79,37 @@ export const prediction: InnerWorkspace = { label: 'Spectrum Name', jpath: ['info', 'name'], visible: true, + isSpectrumLabel: true, + format: '', }, { label: 'Frequency', description: 'frequency', jpath: ['info', 'baseFrequency'], visible: true, + isSpectrumLabel: false, + format: '', }, { label: 'Solvent', jpath: ['info', 'solvent'], visible: false, + isSpectrumLabel: false, + format: '', }, { jpath: ['info', 'pulseSequence'], label: 'Pulse', visible: true, + isSpectrumLabel: false, + format: '', }, { jpath: ['info', 'experiment'], label: 'Experiment', visible: true, + isSpectrumLabel: false, + format: '', }, { name: 'color', @@ -109,6 +120,7 @@ export const prediction: InnerWorkspace = { ], }, '1H,13C': { + enableSpectraLabel: true, columns: [ { name: 'visible', @@ -120,27 +132,37 @@ export const prediction: InnerWorkspace = { label: 'Spectrum Name', jpath: ['info', 'name'], visible: true, + isSpectrumLabel: true, + format: '', }, { label: 'Frequency', description: 'frequency', jpath: ['info', 'baseFrequency'], visible: true, + isSpectrumLabel: false, + format: '', }, { label: 'Solvent', jpath: ['info', 'solvent'], visible: false, + isSpectrumLabel: false, + format: '', }, { jpath: ['info', 'pulseSequence'], label: 'Pulse', visible: true, + isSpectrumLabel: false, + format: '', }, { jpath: ['info', 'experiment'], label: 'Experiment', visible: true, + isSpectrumLabel: false, + format: '', }, { name: 'color', @@ -151,6 +173,7 @@ export const prediction: InnerWorkspace = { ], }, '1H': { + enableSpectraLabel: true, columns: [ { name: 'visible', @@ -162,27 +185,37 @@ export const prediction: InnerWorkspace = { label: 'Spectrum Name', jpath: ['info', 'name'], visible: true, + isSpectrumLabel: true, + format: '', }, { label: 'Frequency', description: 'frequency', jpath: ['info', 'baseFrequency'], visible: true, + isSpectrumLabel: false, + format: '', }, { label: 'Solvent', jpath: ['info', 'solvent'], visible: false, + isSpectrumLabel: false, + format: '', }, { jpath: ['info', 'pulseSequence'], label: 'Pulse', visible: true, + isSpectrumLabel: false, + format: '', }, { jpath: ['info', 'experiment'], label: 'Experiment', visible: true, + isSpectrumLabel: false, + format: '', }, { name: 'color', @@ -193,6 +226,7 @@ export const prediction: InnerWorkspace = { ], }, '13C': { + enableSpectraLabel: true, columns: [ { name: 'visible', @@ -204,27 +238,37 @@ export const prediction: InnerWorkspace = { label: 'Spectrum Name', jpath: ['info', 'name'], visible: true, + isSpectrumLabel: true, + format: '', }, { label: 'Frequency', description: 'frequency', jpath: ['info', 'baseFrequency'], visible: true, + isSpectrumLabel: false, + format: '', }, { label: 'Solvent', jpath: ['info', 'solvent'], visible: false, + isSpectrumLabel: false, + format: '', }, { jpath: ['info', 'pulseSequence'], label: 'Pulse', visible: true, + isSpectrumLabel: false, + format: '', }, { jpath: ['info', 'experiment'], label: 'Experiment', visible: true, + isSpectrumLabel: false, + format: '', }, { name: 'color', diff --git a/src/component/workspaces/simulation.ts b/src/component/workspaces/simulation.ts index fb005d8dcc..580428a2c8 100644 --- a/src/component/workspaces/simulation.ts +++ b/src/component/workspaces/simulation.ts @@ -44,6 +44,7 @@ export const simulation: InnerWorkspace = { spectra: { nuclei: { '1H': { + enableSpectraLabel: true, columns: [ { name: 'visible', @@ -55,17 +56,23 @@ export const simulation: InnerWorkspace = { label: 'Spectrum Name', jpath: ['info', 'name'], visible: true, + isSpectrumLabel: true, + format: '', }, { label: 'Frequency', description: 'frequency', jpath: ['info', 'originFrequency'], visible: true, + isSpectrumLabel: false, + format: '', }, { jpath: ['info', 'nucleus'], label: 'Experiment', visible: true, + isSpectrumLabel: false, + format: '', }, { name: 'color', From 89251db0bb81446ded20b417af397d0052c48f45 Mon Sep 17 00:00:00 2001 From: hamed musallam Date: Tue, 12 May 2026 09:01:20 +0200 Subject: [PATCH 3/7] fix: toggle spectra label --- .../preferences/actions/toggleSpectraLabel.ts | 24 +++++++++++-------- .../panelsPreferencesDefaultValues.ts | 11 ++++++--- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/component/reducer/preferences/actions/toggleSpectraLabel.ts b/src/component/reducer/preferences/actions/toggleSpectraLabel.ts index 8208882058..732929779c 100644 --- a/src/component/reducer/preferences/actions/toggleSpectraLabel.ts +++ b/src/component/reducer/preferences/actions/toggleSpectraLabel.ts @@ -1,5 +1,6 @@ import type { Draft } from 'immer'; +import { getBaseSpectraPreferences } from '../panelsPreferencesDefaultValues.ts'; import type { PreferencesState, ToggleSpectraLabelAction, @@ -10,18 +11,21 @@ export function toggleSpectraLabel( draft: Draft, action: ToggleSpectraLabelAction, ) { - if (action.payload) { - const currentWorkspacePreferences = getActiveWorkspace(draft); + const { panels } = getActiveWorkspace(draft); - const { nucleus } = action.payload; - const nucleusPreferences = - currentWorkspacePreferences.panels.spectra?.nuclei[nucleus]; + const { nucleus } = action.payload; - if (!nucleusPreferences) { - return; - } + panels.spectra ??= { nuclei: {} }; + const { nuclei } = panels.spectra; - nucleusPreferences.enableSpectraLabel = - !nucleusPreferences.enableSpectraLabel; + if (!nuclei[nucleus]) { + const defaultValues = getBaseSpectraPreferences(); + nuclei[nucleus] = { + ...defaultValues, + enableSpectraLabel: !defaultValues.enableSpectraLabel, + }; + return; } + + nuclei[nucleus].enableSpectraLabel = !nuclei[nucleus].enableSpectraLabel; } diff --git a/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts b/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts index 30b8c98f1e..4bd3bfacbf 100644 --- a/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts +++ b/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts @@ -18,9 +18,7 @@ function getPreferences(data: T, nucleus?: string) { return { nuclei: { ...(nucleus ? { [nucleus]: data } : {}) } }; } -const getSpectraDefaultValues = ( - nucleus?: string, -): PanelsPreferences['spectra'] => { +export function getBaseSpectraPreferences(): SpectraPreferences { const preferences: SpectraPreferences = { columns: [ { @@ -67,6 +65,13 @@ const getSpectraDefaultValues = ( enableSpectraLabel: true, }; + return preferences; +} + +const getSpectraDefaultValues = ( + nucleus?: string, +): PanelsPreferences['spectra'] => { + const preferences = getBaseSpectraPreferences(); return getPreferences(preferences, nucleus); }; From 254a25690afad05548f10bbe8b65f868272daa40 Mon Sep 17 00:00:00 2001 From: hamed musallam Date: Wed, 13 May 2026 14:33:01 +0200 Subject: [PATCH 4/7] refactor: spectra label options --- src/component/1d/SpectrumLabel.tsx | 36 ++-- .../general_settings_dialog_body.tsx | 7 + .../tabs/spectra_label_tab.tsx | 201 ++++++++++++++++++ .../tanstack_general_settings/validation.ts | 2 + .../spectra_label_tab_validation.ts | 24 +++ .../SpectraPanel/SpectraPanelHeader.tsx | 10 +- .../SpectraPanel/SpectraPreferences.tsx | 1 - .../base/SpectraColumnsManager.tsx | 10 - .../preferences/actions/toggleSpectraLabel.ts | 22 +- .../panelsPreferencesDefaultValues.ts | 17 +- .../reducer/preferences/preferencesReducer.ts | 2 +- src/component/workspaces/prediction.ts | 24 --- src/component/workspaces/simulation.ts | 4 - .../workspaces/workspaceDefaultProperties.ts | 8 + 14 files changed, 271 insertions(+), 97 deletions(-) create mode 100644 src/component/modal/setting/tanstack_general_settings/tabs/spectra_label_tab.tsx create mode 100644 src/component/modal/setting/tanstack_general_settings/validation/spectra_label_tab_validation.ts diff --git a/src/component/1d/SpectrumLabel.tsx b/src/component/1d/SpectrumLabel.tsx index 3cedacc864..1107db27c6 100644 --- a/src/component/1d/SpectrumLabel.tsx +++ b/src/component/1d/SpectrumLabel.tsx @@ -1,9 +1,10 @@ -import type { SpectraTableColumn, Spectrum } from '@zakodium/nmrium-core'; +import type { Spectrum, SpectrumLabelField } from '@zakodium/nmrium-core'; import dlv from 'dlv'; +import { SVGStyledText } from 'react-science/ui'; import { useChartData } from '../context/ChartContext.tsx'; +import { usePreferences } from '../context/PreferencesContext.tsx'; import { useScaleChecked } from '../context/ScaleContext.tsx'; -import { usePanelPreferences } from '../hooks/usePanelPreferences.ts'; import { useVerticalAlign } from '../hooks/useVerticalAlign.ts'; export function SpectrumLabel({ @@ -18,46 +19,41 @@ export function SpectrumLabel({ height, margin, toolOptions: { selectedTool }, - view: { - spectra: { activeTab }, - }, } = useChartData(); - const { columns, enableSpectraLabel } = usePanelPreferences( - 'spectra', - activeTab, - ); + const { current } = usePreferences(); const verticalAlign = useVerticalAlign(); + const { fields, visible, valueStyle } = current.spectraLabel; if ( verticalAlign !== 'stack' || selectedTool !== 'zoom' || - !Array.isArray(columns) || - columns.length === 0 || - !enableSpectraLabel + !Array.isArray(fields) || + fields.length === 0 || + !visible ) { return null; } const innerHeight = height - margin.bottom - spectraBottomMargin; - const label = getSpectrumLabel(columns, spectrum); + const label = getSpectrumLabel(fields, spectrum); return ( - {label} - + ); } function getSpectrumLabel( - columns: SpectraTableColumn[], + fields: SpectrumLabelField[], spectrum: Spectrum, ): string { - return columns - .filter((column) => column.isSpectrumLabel) - .map((column) => (column.jpath ? dlv(spectrum, column.jpath, '') : '')) + return fields + .filter((field) => field.visible) + .map((field) => (field.jpath ? dlv(spectrum, field.jpath, '') : '')) .join(', '); } diff --git a/src/component/modal/setting/tanstack_general_settings/general_settings_dialog_body.tsx b/src/component/modal/setting/tanstack_general_settings/general_settings_dialog_body.tsx index 2bd74d90f8..923f1a70d8 100644 --- a/src/component/modal/setting/tanstack_general_settings/general_settings_dialog_body.tsx +++ b/src/component/modal/setting/tanstack_general_settings/general_settings_dialog_body.tsx @@ -16,6 +16,7 @@ import { ImportFiltersTab } from './tabs/import_filters_tab.js'; import { NucleiTab } from './tabs/nuclei_tab.js'; import { PanelsTab } from './tabs/panels_tab.js'; import { SpectraColorsTab } from './tabs/spectra_colors_tab.tsx'; +import { SpectraLabelTab } from './tabs/spectra_label_tab.tsx'; import { TitleBlockTab } from './tabs/title_block_tab.js'; import { ToolsTab } from './tabs/tools_tab.tsx'; import { @@ -107,6 +108,12 @@ export const GeneralSettingsDialogBody = withForm({ panel={} /> + } + /> + {isExperimentalEnabled && ( { + const { Section, AppField } = form; + + return ( + <> +
+ + {({ Checkbox }) => } + +
+ + + +
+ +
+ + ); + }, +}); + +type Field = z.input; +function getEmptyField(): Field { + return { + format: '', + jpath: '', + visible: true, + uuid: crypto.randomUUID(), + }; +} +const TableFields = withForm({ + defaultValues: defaultGeneralSettingsFormValues, + render: function Fields({ form }) { + const { Field } = form; + const fields = useField({ + form, + name: 'spectraLabel.fields', + mode: 'array', + }); + const { removeValue, setValue, pushValue, name, store } = fields; + + const [autoFocus, setAutoFocus] = useState(''); + function onAddField() { + const value = getEmptyField(); + pushValue(value, { dontRunListeners: true }); + setAutoFocus(value.uuid); + } + + const { data } = useChartData(); + const { datalist } = useMemo(() => getSpectraObjectPaths(data), [data]); + + const onDeleteAt = useCallback( + (index: number) => { + removeValue(index); + }, + [removeValue], + ); + + const columns = useMemo(() => { + const columnHelper = createTableColumnHelper(); + return [ + columnHelper.display({ + id: 'dnd', + header: '', + meta: { + tdStyle: { textAlign: 'center' }, + }, + cell: () => , + }), + columnHelper.accessor('jpath', { + header: 'Field', + cell: ({ row: { index, original } }) => ( + + {(field) => ( + setAutoFocus('')} + /> + )} + + ), + }), + columnHelper.accessor('format', { + header: 'Format', + cell: ({ row: { index } }) => ( + + {(field) => } + + ), + }), + columnHelper.accessor('visible', { + header: 'Visible', + meta: { + tdStyle: { textAlign: 'center' }, + }, + cell: ({ row: { index } }) => ( + + {(field) => } + + ), + }), + columnHelper.display({ + id: 'actions', + header: '', + meta: { + thStyle: { + width: '60px', + }, + }, + cell: ({ row: { index } }) => { + return ( + + onDeleteAt(index)} + > + + + + ); + }, + }), + ]; + }, [Field, autoFocus, datalist, name, onDeleteAt]); + + const onRowOrderChanged = useCallback( + (value: Field[]) => { + setValue(value); + }, + [setValue], + ); + + const fieldsData = useStore(store, (s) => s.value); + + return ( + + Add Field + + } + > + + + ); + }, +}); + +function getRowId(row: Field) { + return row.uuid; +} + +const TableDragRowHandlerStyled = styled(TableDragRowHandler)` + margin: 0 2px; +`; diff --git a/src/component/modal/setting/tanstack_general_settings/validation.ts b/src/component/modal/setting/tanstack_general_settings/validation.ts index 4fbe414f95..dc05c06d61 100644 --- a/src/component/modal/setting/tanstack_general_settings/validation.ts +++ b/src/component/modal/setting/tanstack_general_settings/validation.ts @@ -16,6 +16,7 @@ import { nmrLoadersValidation } from './validation/import_filters_tab_validation import { nucleiValidation } from './validation/nuclei_tab_validation.js'; import { displayPanelsValidation } from './validation/panels_tab_validation.js'; import { spectraColorsTabValidation } from './validation/spectra_colors_tab_validation.ts'; +import { spectraLabelTabValidation } from './validation/spectra_label_tab_validation.ts'; import { infoBlockTabValidation } from './validation/title_block_tab_validation.js'; import { toolBarButtonsValidation } from './validation/tools_tab_validation.ts'; @@ -45,6 +46,7 @@ export const workspaceValidation = z.object({ export: exportPreferencesValidation, peaksLabel: peaksLabelValidation, axis: axisValidation, + spectraLabel: spectraLabelTabValidation, }); export const defaultGeneralSettingsFormValues: z.input< diff --git a/src/component/modal/setting/tanstack_general_settings/validation/spectra_label_tab_validation.ts b/src/component/modal/setting/tanstack_general_settings/validation/spectra_label_tab_validation.ts new file mode 100644 index 0000000000..54278203f6 --- /dev/null +++ b/src/component/modal/setting/tanstack_general_settings/validation/spectra_label_tab_validation.ts @@ -0,0 +1,24 @@ +import { svgTextStyleFieldsSchema } from 'react-science/ui'; +import { z } from 'zod'; + +import { jpathCodec, withUUID } from './utils.ts'; + +const spectraLabelFieldTabValidation = z.object({ + format: z.string(), + jpath: jpathCodec, + visible: z.boolean(), +}); + +export const spectraLabelFieldTabValidationWithUUID = withUUID( + spectraLabelFieldTabValidation, +); + +const spectraLabelFieldsTabValidation = z.array( + spectraLabelFieldTabValidationWithUUID, +); + +export const spectraLabelTabValidation = z.object({ + visible: z.boolean(), + fields: spectraLabelFieldsTabValidation, + valueStyle: svgTextStyleFieldsSchema.optional(), +}); diff --git a/src/component/panels/SpectraPanel/SpectraPanelHeader.tsx b/src/component/panels/SpectraPanel/SpectraPanelHeader.tsx index d82ad34eb5..e956c29a25 100644 --- a/src/component/panels/SpectraPanel/SpectraPanelHeader.tsx +++ b/src/component/panels/SpectraPanel/SpectraPanelHeader.tsx @@ -15,7 +15,6 @@ import { usePreferences } from '../../context/PreferencesContext.js'; import { useToaster } from '../../context/ToasterContext.js'; import { useAlert } from '../../elements/Alert.js'; import { useActiveSpectra } from '../../hooks/useActiveSpectra.js'; -import { usePanelPreferences } from '../../hooks/usePanelPreferences.ts'; import useSpectrum from '../../hooks/useSpectrum.js'; import { useToggleSpectraVisibility } from '../../hooks/useToggleSpectraVisibility.js'; import type { DisplayerMode } from '../../reducer/Reducer.js'; @@ -61,10 +60,9 @@ function SpectraPanelHeaderInner({ const toaster = useToaster(); const dispatch = useDispatch(); const { - current: { spectraColors }, + current: { spectraColors, spectraLabel }, dispatch: dispatchPreferences, } = usePreferences(); - const { enableSpectraLabel } = usePanelPreferences('spectra', activeTab); const handleDelete = useCallback(() => { alert.showAlert({ @@ -124,7 +122,7 @@ function SpectraPanelHeaderInner({ function toggleSpectraLabelHandler() { dispatchPreferences({ type: 'TOGGLE_SPECTRA_LABEL', - payload: { nucleus: activeTab }, + payload: {}, }); } @@ -182,8 +180,8 @@ function SpectraPanelHeaderInner({ }, { icon: , - tooltip: `${booleanToString(!enableSpectraLabel)} spectra label`, - active: enableSpectraLabel, + tooltip: `${booleanToString(!spectraLabel.visible)} spectra label`, + active: spectraLabel.visible, onClick: toggleSpectraLabelHandler, }, ]); diff --git a/src/component/panels/SpectraPanel/SpectraPreferences.tsx b/src/component/panels/SpectraPanel/SpectraPreferences.tsx index 1559233547..3cf5023018 100644 --- a/src/component/panels/SpectraPanel/SpectraPreferences.tsx +++ b/src/component/panels/SpectraPanel/SpectraPreferences.tsx @@ -129,7 +129,6 @@ function SpectraPreferences(props: object, ref: Ref) { jpath: [], label: '', visible: true, - isSpectrumLabel: false, format: '', }, ...columns.slice(index), diff --git a/src/component/panels/SpectraPanel/base/SpectraColumnsManager.tsx b/src/component/panels/SpectraPanel/base/SpectraColumnsManager.tsx index c3f55f59fb..f309a477bd 100644 --- a/src/component/panels/SpectraPanel/base/SpectraColumnsManager.tsx +++ b/src/component/panels/SpectraPanel/base/SpectraColumnsManager.tsx @@ -132,16 +132,6 @@ export function SpectraColumnsManager({ /> ), }, - { - Header: 'spectrum label', - style: { width: '30px', textAlign: 'center' }, - Cell: ({ row }: CellProps) => ( - - ), - }, { Header: '', style: { width: '65px' }, diff --git a/src/component/reducer/preferences/actions/toggleSpectraLabel.ts b/src/component/reducer/preferences/actions/toggleSpectraLabel.ts index 732929779c..e9007825aa 100644 --- a/src/component/reducer/preferences/actions/toggleSpectraLabel.ts +++ b/src/component/reducer/preferences/actions/toggleSpectraLabel.ts @@ -1,6 +1,5 @@ import type { Draft } from 'immer'; -import { getBaseSpectraPreferences } from '../panelsPreferencesDefaultValues.ts'; import type { PreferencesState, ToggleSpectraLabelAction, @@ -11,21 +10,10 @@ export function toggleSpectraLabel( draft: Draft, action: ToggleSpectraLabelAction, ) { - const { panels } = getActiveWorkspace(draft); + const { value } = action.payload; + const currentWorkspace = getActiveWorkspace(draft); + if (!currentWorkspace) return; - const { nucleus } = action.payload; - - panels.spectra ??= { nuclei: {} }; - const { nuclei } = panels.spectra; - - if (!nuclei[nucleus]) { - const defaultValues = getBaseSpectraPreferences(); - nuclei[nucleus] = { - ...defaultValues, - enableSpectraLabel: !defaultValues.enableSpectraLabel, - }; - return; - } - - nuclei[nucleus].enableSpectraLabel = !nuclei[nucleus].enableSpectraLabel; + currentWorkspace.spectraLabel.visible = + value ?? !currentWorkspace.spectraLabel.visible; } diff --git a/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts b/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts index 4bd3bfacbf..4bb5f7d31e 100644 --- a/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts +++ b/src/component/reducer/preferences/panelsPreferencesDefaultValues.ts @@ -18,7 +18,9 @@ function getPreferences(data: T, nucleus?: string) { return { nuclei: { ...(nucleus ? { [nucleus]: data } : {}) } }; } -export function getBaseSpectraPreferences(): SpectraPreferences { +const getSpectraDefaultValues = ( + nucleus?: string, +): PanelsPreferences['spectra'] => { const preferences: SpectraPreferences = { columns: [ { @@ -31,28 +33,24 @@ export function getBaseSpectraPreferences(): SpectraPreferences { label: 'Spectrum Name', jpath: ['info', 'name'], visible: true, - isSpectrumLabel: true, format: '', }, { label: 'Solvent', jpath: ['info', 'solvent'], visible: true, - isSpectrumLabel: false, format: '', }, { jpath: ['info', 'pulseSequence'], label: 'Pulse', visible: true, - isSpectrumLabel: false, format: '', }, { jpath: ['info', 'experiment'], label: 'Experiment', visible: true, - isSpectrumLabel: false, format: '', }, { @@ -62,16 +60,7 @@ export function getBaseSpectraPreferences(): SpectraPreferences { visible: true, }, ], - enableSpectraLabel: true, }; - - return preferences; -} - -const getSpectraDefaultValues = ( - nucleus?: string, -): PanelsPreferences['spectra'] => { - const preferences = getBaseSpectraPreferences(); return getPreferences(preferences, nucleus); }; diff --git a/src/component/reducer/preferences/preferencesReducer.ts b/src/component/reducer/preferences/preferencesReducer.ts index e8b768884c..c635a6823c 100644 --- a/src/component/reducer/preferences/preferencesReducer.ts +++ b/src/component/reducer/preferences/preferencesReducer.ts @@ -94,7 +94,7 @@ export type SetPanelsPreferencesAction = ActionType< >; export type ToggleSpectraLabelAction = ActionType< 'TOGGLE_SPECTRA_LABEL', - { nucleus: string } + { value?: boolean } >; export type SetWorkspaceAction = ActionType< diff --git a/src/component/workspaces/prediction.ts b/src/component/workspaces/prediction.ts index 45f6ac48a5..f822b592b4 100644 --- a/src/component/workspaces/prediction.ts +++ b/src/component/workspaces/prediction.ts @@ -67,7 +67,6 @@ export const prediction: InnerWorkspace = { spectra: { nuclei: { '1H,1H': { - enableSpectraLabel: true, columns: [ { name: 'visible', @@ -79,7 +78,6 @@ export const prediction: InnerWorkspace = { label: 'Spectrum Name', jpath: ['info', 'name'], visible: true, - isSpectrumLabel: true, format: '', }, { @@ -87,28 +85,24 @@ export const prediction: InnerWorkspace = { description: 'frequency', jpath: ['info', 'baseFrequency'], visible: true, - isSpectrumLabel: false, format: '', }, { label: 'Solvent', jpath: ['info', 'solvent'], visible: false, - isSpectrumLabel: false, format: '', }, { jpath: ['info', 'pulseSequence'], label: 'Pulse', visible: true, - isSpectrumLabel: false, format: '', }, { jpath: ['info', 'experiment'], label: 'Experiment', visible: true, - isSpectrumLabel: false, format: '', }, { @@ -120,7 +114,6 @@ export const prediction: InnerWorkspace = { ], }, '1H,13C': { - enableSpectraLabel: true, columns: [ { name: 'visible', @@ -132,7 +125,6 @@ export const prediction: InnerWorkspace = { label: 'Spectrum Name', jpath: ['info', 'name'], visible: true, - isSpectrumLabel: true, format: '', }, { @@ -140,28 +132,24 @@ export const prediction: InnerWorkspace = { description: 'frequency', jpath: ['info', 'baseFrequency'], visible: true, - isSpectrumLabel: false, format: '', }, { label: 'Solvent', jpath: ['info', 'solvent'], visible: false, - isSpectrumLabel: false, format: '', }, { jpath: ['info', 'pulseSequence'], label: 'Pulse', visible: true, - isSpectrumLabel: false, format: '', }, { jpath: ['info', 'experiment'], label: 'Experiment', visible: true, - isSpectrumLabel: false, format: '', }, { @@ -173,7 +161,6 @@ export const prediction: InnerWorkspace = { ], }, '1H': { - enableSpectraLabel: true, columns: [ { name: 'visible', @@ -185,7 +172,6 @@ export const prediction: InnerWorkspace = { label: 'Spectrum Name', jpath: ['info', 'name'], visible: true, - isSpectrumLabel: true, format: '', }, { @@ -193,28 +179,24 @@ export const prediction: InnerWorkspace = { description: 'frequency', jpath: ['info', 'baseFrequency'], visible: true, - isSpectrumLabel: false, format: '', }, { label: 'Solvent', jpath: ['info', 'solvent'], visible: false, - isSpectrumLabel: false, format: '', }, { jpath: ['info', 'pulseSequence'], label: 'Pulse', visible: true, - isSpectrumLabel: false, format: '', }, { jpath: ['info', 'experiment'], label: 'Experiment', visible: true, - isSpectrumLabel: false, format: '', }, { @@ -226,7 +208,6 @@ export const prediction: InnerWorkspace = { ], }, '13C': { - enableSpectraLabel: true, columns: [ { name: 'visible', @@ -238,7 +219,6 @@ export const prediction: InnerWorkspace = { label: 'Spectrum Name', jpath: ['info', 'name'], visible: true, - isSpectrumLabel: true, format: '', }, { @@ -246,28 +226,24 @@ export const prediction: InnerWorkspace = { description: 'frequency', jpath: ['info', 'baseFrequency'], visible: true, - isSpectrumLabel: false, format: '', }, { label: 'Solvent', jpath: ['info', 'solvent'], visible: false, - isSpectrumLabel: false, format: '', }, { jpath: ['info', 'pulseSequence'], label: 'Pulse', visible: true, - isSpectrumLabel: false, format: '', }, { jpath: ['info', 'experiment'], label: 'Experiment', visible: true, - isSpectrumLabel: false, format: '', }, { diff --git a/src/component/workspaces/simulation.ts b/src/component/workspaces/simulation.ts index 580428a2c8..5b3ae9406a 100644 --- a/src/component/workspaces/simulation.ts +++ b/src/component/workspaces/simulation.ts @@ -44,7 +44,6 @@ export const simulation: InnerWorkspace = { spectra: { nuclei: { '1H': { - enableSpectraLabel: true, columns: [ { name: 'visible', @@ -56,7 +55,6 @@ export const simulation: InnerWorkspace = { label: 'Spectrum Name', jpath: ['info', 'name'], visible: true, - isSpectrumLabel: true, format: '', }, { @@ -64,14 +62,12 @@ export const simulation: InnerWorkspace = { description: 'frequency', jpath: ['info', 'originFrequency'], visible: true, - isSpectrumLabel: false, format: '', }, { jpath: ['info', 'nucleus'], label: 'Experiment', visible: true, - isSpectrumLabel: false, format: '', }, { diff --git a/src/component/workspaces/workspaceDefaultProperties.ts b/src/component/workspaces/workspaceDefaultProperties.ts index 365b9bd6dc..5749a4744e 100644 --- a/src/component/workspaces/workspaceDefaultProperties.ts +++ b/src/component/workspaces/workspaceDefaultProperties.ts @@ -352,4 +352,12 @@ export const workspaceDefaultProperties: RequiredWorkspacePreferences = { }, }, }, + spectraLabel: { + visible: true, + fields: [{ jpath: ['info', 'name'], visible: true, format: '' }], + valueStyle: { + fontSize: 12, + fill: 'black', + }, + }, }; From cedb797299c699b8b4ce500b063be638821a7bf0 Mon Sep 17 00:00:00 2001 From: hamed musallam Date: Wed, 13 May 2026 15:10:49 +0200 Subject: [PATCH 5/7] refacrtor: spectrum label format and placehold --- src/component/1d/SpectrumLabel.tsx | 4 +-- .../tabs/spectra_label_tab.tsx | 2 +- .../panels/SpectraPanel/SpectraTable.tsx | 32 ++----------------- src/component/utility/getValueByPath.ts | 26 +++++++++++++++ src/component/utility/pathToString.ts | 3 ++ 5 files changed, 35 insertions(+), 32 deletions(-) create mode 100644 src/component/utility/getValueByPath.ts create mode 100644 src/component/utility/pathToString.ts diff --git a/src/component/1d/SpectrumLabel.tsx b/src/component/1d/SpectrumLabel.tsx index 1107db27c6..420f46ec6b 100644 --- a/src/component/1d/SpectrumLabel.tsx +++ b/src/component/1d/SpectrumLabel.tsx @@ -1,11 +1,11 @@ import type { Spectrum, SpectrumLabelField } from '@zakodium/nmrium-core'; -import dlv from 'dlv'; import { SVGStyledText } from 'react-science/ui'; import { useChartData } from '../context/ChartContext.tsx'; import { usePreferences } from '../context/PreferencesContext.tsx'; import { useScaleChecked } from '../context/ScaleContext.tsx'; import { useVerticalAlign } from '../hooks/useVerticalAlign.ts'; +import { getValueByPath } from '../utility/getValueByPath.ts'; export function SpectrumLabel({ index, @@ -54,6 +54,6 @@ function getSpectrumLabel( ): string { return fields .filter((field) => field.visible) - .map((field) => (field.jpath ? dlv(spectrum, field.jpath, '') : '')) + .map((field) => getValueByPath(spectrum, field.jpath, field.format)) .join(', '); } diff --git a/src/component/modal/setting/tanstack_general_settings/tabs/spectra_label_tab.tsx b/src/component/modal/setting/tanstack_general_settings/tabs/spectra_label_tab.tsx index e487d0f26d..e5e12a02b8 100644 --- a/src/component/modal/setting/tanstack_general_settings/tabs/spectra_label_tab.tsx +++ b/src/component/modal/setting/tanstack_general_settings/tabs/spectra_label_tab.tsx @@ -42,7 +42,7 @@ export const SpectraLabelTab = withForm({ form={form} fields="spectraLabel.valueStyle" label="Field value" - previewText="lon1027" + previewText="Placeholder" /> diff --git a/src/component/panels/SpectraPanel/SpectraTable.tsx b/src/component/panels/SpectraPanel/SpectraTable.tsx index a4c8a79674..089df7585e 100644 --- a/src/component/panels/SpectraPanel/SpectraTable.tsx +++ b/src/component/panels/SpectraPanel/SpectraTable.tsx @@ -7,7 +7,6 @@ import type { Spectrum, StateMolecule, } from '@zakodium/nmrium-core'; -import dlv from 'dlv'; import type { CSSProperties, MouseEvent } from 'react'; import { useCallback, useMemo, useState } from 'react'; import { FaCopy, FaFileExport, FaRegTrashAlt } from 'react-icons/fa'; @@ -28,7 +27,8 @@ import type { Column } from '../../elements/ReactTable/ReactTable.js'; import ReactTable from '../../elements/ReactTable/ReactTable.js'; import { usePanelPreferences } from '../../hooks/usePanelPreferences.js'; import ExportAsJcampModal from '../../modal/ExportAsJcampModal.js'; -import { formatNumber } from '../../utility/formatNumber.ts'; +import { getValueByPath } from '../../utility/getValueByPath.ts'; +import { pathToString } from '../../utility/pathToString.ts'; import { saveAs } from '../../utility/save_as.js'; import { RenderAsHTML } from './base/RenderAsHTML.js'; @@ -328,7 +328,7 @@ export function SpectraTable(props: SpectraTableProps) { const cell: Column = { Header: () => , - accessor: (row) => getValue(row, jpath, format), + accessor: (row) => getValueByPath(row, jpath, format), ...(cellRender && { Cell: cellRender }), id: `${index}`, style, @@ -440,29 +440,3 @@ function convertSpectrumToText(spectrum: Spectrum) { } return lines.join('\n'); } - -function pathToString(path: string[]) { - return Array.isArray(path) ? path.join('.') : ''; -} - -function formatValue(value: any, format = '') { - if (format.trim()) { - return formatNumber(value, format); - } - return value; -} - -function getValue(row: any, path: any, format = ' ') { - const value = dlv(row, path, ''); - const pathString = pathToString(path); - - if (Array.isArray(value)) { - if (['info.baseFrequency', 'info.originFrequency'].includes(pathString)) { - return formatValue(value[0], format); - } - - return value.map((v: any) => formatValue(v, format)).join(','); - } - - return formatValue(value, format); -} diff --git a/src/component/utility/getValueByPath.ts b/src/component/utility/getValueByPath.ts new file mode 100644 index 0000000000..fa9c65172e --- /dev/null +++ b/src/component/utility/getValueByPath.ts @@ -0,0 +1,26 @@ +import dlv from 'dlv'; + +import { formatNumber } from './formatNumber.ts'; +import { pathToString } from './pathToString.ts'; + +function formatValue(value: any, format = '') { + if (format.trim()) { + return formatNumber(value, format); + } + return value; +} + +export function getValueByPath(obj: any, path: string[], format = ' ') { + const value = dlv(obj, path, ''); + const pathString = pathToString(path); + + if (Array.isArray(value)) { + if (['info.baseFrequency', 'info.originFrequency'].includes(pathString)) { + return formatValue(value[0], format); + } + + return value.map((v: any) => formatValue(v, format)).join(','); + } + + return formatValue(value, format); +} diff --git a/src/component/utility/pathToString.ts b/src/component/utility/pathToString.ts new file mode 100644 index 0000000000..bbd6d604b1 --- /dev/null +++ b/src/component/utility/pathToString.ts @@ -0,0 +1,3 @@ +export function pathToString(path: string[]) { + return Array.isArray(path) ? path.join('.') : ''; +} From dab7e5a46ff4d39514f31b13ef3150d41dd73a11 Mon Sep 17 00:00:00 2001 From: jobo322 Date: Tue, 2 Jun 2026 15:09:55 -0500 Subject: [PATCH 6/7] fix: ensure nbPoints as integer in peaksSum --- src/component/1d/peaks/usePeakShapesPath.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/component/1d/peaks/usePeakShapesPath.ts b/src/component/1d/peaks/usePeakShapesPath.ts index 530f7cad2f..5e368624c6 100644 --- a/src/component/1d/peaks/usePeakShapesPath.ts +++ b/src/component/1d/peaks/usePeakShapesPath.ts @@ -43,7 +43,7 @@ export function usePeakShapesPath(spectrum: Spectrum1D) { const { peaks } = options; pathSeries = peaksToXY(peaks, { frequency, - nbPoints: width, + nbPoints: Math.ceil(width), from: xDomain[0], to: xDomain[1], }); From 0bbb98a0dbbe9ecfbdf8552dcf4acb0ae7cf70e5 Mon Sep 17 00:00:00 2001 From: jobo322 Date: Tue, 2 Jun 2026 15:10:31 -0500 Subject: [PATCH 7/7] feat: set pseudoVoigt as default shape for auto ranges picking --- src/data/data1d/Spectrum1D/ranges/autoRangesDetection.ts | 2 +- src/data/data1d/Spectrum1D/ranges/detectSignals.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/data1d/Spectrum1D/ranges/autoRangesDetection.ts b/src/data/data1d/Spectrum1D/ranges/autoRangesDetection.ts index 2f9042ef34..939f9fca61 100644 --- a/src/data/data1d/Spectrum1D/ranges/autoRangesDetection.ts +++ b/src/data/data1d/Spectrum1D/ranges/autoRangesDetection.ts @@ -10,7 +10,7 @@ const defaultPeakPickingOptions: Omit< 'frequency' > = { minMaxRatio: 1, - shape: { kind: 'lorentzian' }, + shape: { kind: 'pseudoVoigt', mu: 0.5 }, realTopDetection: true, maxCriteria: true, smoothY: true, diff --git a/src/data/data1d/Spectrum1D/ranges/detectSignals.ts b/src/data/data1d/Spectrum1D/ranges/detectSignals.ts index f4bf987e3c..72ee4da08a 100644 --- a/src/data/data1d/Spectrum1D/ranges/detectSignals.ts +++ b/src/data/data1d/Spectrum1D/ranges/detectSignals.ts @@ -35,6 +35,7 @@ export default function detectSignals( from, frequency, broadRatio: 0.0025, + shape: { kind: 'pseudoVoigt', mu: 0.5 }, smoothY: true, }, ranges: {