diff --git a/package-lock.json b/package-lock.json index 0649e74e91..f3bd216e86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,10 @@ "@tanstack/react-store": "^0.11.0", "@tanstack/react-table": "^8.21.3", "@zakodium/nmr-types": "^0.5.16", - "@zakodium/nmrium-core": "^0.7.33", - "@zakodium/nmrium-core-plugins": "^0.7.42", + "@zakodium/nmrium-core": "^0.7.35", + "@zakodium/nmrium-core-plugins": "^0.7.45", "@zakodium/pdnd-esm": "^1.1.0", + "@zakodium/utils": "^0.2.0", "@zip.js/zip.js": "^2.8.26", "cheminfo-font": "^1.27.0", "cheminfo-types": "^1.15.0", @@ -3993,11 +3994,12 @@ } }, "node_modules/@zakodium/nmrium-core": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/@zakodium/nmrium-core/-/nmrium-core-0.7.33.tgz", - "integrity": "sha512-ksJDX86pQK8EKPqeQkgzsbs/bE/KA913xFIDskR9CPjjz9sYHCOmwyTYwpKJNPySDRGlkLlGygsKnsn/QZk9rg==", + "version": "0.7.35", + "resolved": "https://registry.npmjs.org/@zakodium/nmrium-core/-/nmrium-core-0.7.35.tgz", + "integrity": "sha512-T7503fU/PT7zkSzPCuFxHZvzY0j1rNIkxtY/uFOsoACq2k71GTbNeyW5nQZsk5+e1EzhxnnmThCgK1w1TjiyCQ==", "license": "CC-BY-NC-SA-4.0", "dependencies": { + "@zakodium/utils": "^0.2.0", "cheminfo-types": "^1.15.0", "fifo-logger": "^2.0.1", "file-collection": "^6.6.1", @@ -4011,13 +4013,13 @@ } }, "node_modules/@zakodium/nmrium-core-plugins": { - "version": "0.7.42", - "resolved": "https://registry.npmjs.org/@zakodium/nmrium-core-plugins/-/nmrium-core-plugins-0.7.42.tgz", - "integrity": "sha512-48ua0tB/GKPKVsb7shEoJlQ1rJzjM2nElPKbVjSZ8tGF+3UDnm9IZFHGZ19+5VYbVaLDszIO85gEdx0kHyZbCA==", + "version": "0.7.45", + "resolved": "https://registry.npmjs.org/@zakodium/nmrium-core-plugins/-/nmrium-core-plugins-0.7.45.tgz", + "integrity": "sha512-VbTxLiIDv2Kt/WmTxZdIrk5sVkeCGadn6F79xYHZQAOAUT3OjxX3zCERLLp/k32Os22u+IJgSY/+BDe7IlUNkA==", "license": "CC-BY-NC-SA-4.0", "dependencies": { "@date-fns/utc": "^2.1.1", - "@zakodium/nmrium-core": "^0.7.33", + "@zakodium/nmrium-core": "^0.7.35", "cheminfo-types": "^1.15.0", "convert-to-jcamp": "^7.0.0", "date-fns": "^4.4.0", @@ -4028,12 +4030,24 @@ "linear-sum-assignment": "^1.0.9", "lodash.merge": "^4.6.2", "ml-spectra-processing": "^14.29.1", - "nmr-processing": "^22.16.0", + "nmr-processing": "^22.17.0", "openchemlib": "^9.23.0", "openchemlib-utils": "^8.17.0", "sdf-parser": "^9.0.0", "ts-pattern": "^5.9.0", "zod": "^4.4.3" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } } }, "node_modules/@zakodium/nmrium-core-plugins/node_modules/sdf-parser": { @@ -4073,6 +4087,12 @@ "integrity": "sha512-wCtQhZlsEu2uEAJstDG8UerBrGru/RaDAX8c6RpEy9Iwqz9npZPclY5zorCLqYXkfefG1G7dhlDMVPpraCr6/w==", "dev": true }, + "node_modules/@zakodium/utils": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@zakodium/utils/-/utils-0.2.0.tgz", + "integrity": "sha512-DwFKtAMkbzqcywi1O+JDFQXy3CV2sQUsfED/F0u9a5hDF6ABd4joDUFCXkvogr2/fGp1qmpbu31XHldJpehvjw==", + "license": "MIT" + }, "node_modules/@zeit/schemas": { "version": "2.36.0", "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz", @@ -9715,9 +9735,9 @@ } }, "node_modules/nmr-processing": { - "version": "22.16.0", - "resolved": "https://registry.npmjs.org/nmr-processing/-/nmr-processing-22.16.0.tgz", - "integrity": "sha512-tbqPKE0s5fXvx8TTQHTl2NJ9hNzXtlaa7F4MeIR2i2GhMrIBsZmZbe8623NSnV41ybzN3Jh14/JSFgpeJ0K47w==", + "version": "22.17.0", + "resolved": "https://registry.npmjs.org/nmr-processing/-/nmr-processing-22.17.0.tgz", + "integrity": "sha512-Ez4CNc0pT6l5KMU9bUraWs9xPy8UMsjVON7ltO29jo5Yy46BSDsI9TXuLIJHkdPbLr1GZm+5L95XAy7K6hqbnw==", "license": "CC-BY-NC-SA-4.0", "dependencies": { "binary-search": "^1.3.6", diff --git a/package.json b/package.json index 4f25b97c17..b34c1e8dd2 100644 --- a/package.json +++ b/package.json @@ -70,9 +70,10 @@ "@tanstack/react-store": "^0.11.0", "@tanstack/react-table": "^8.21.3", "@zakodium/nmr-types": "^0.5.16", - "@zakodium/nmrium-core": "^0.7.33", - "@zakodium/nmrium-core-plugins": "^0.7.42", + "@zakodium/nmrium-core": "^0.7.35", + "@zakodium/nmrium-core-plugins": "^0.7.45", "@zakodium/pdnd-esm": "^1.1.0", + "@zakodium/utils": "^0.2.0", "@zip.js/zip.js": "^2.8.26", "cheminfo-font": "^1.27.0", "cheminfo-types": "^1.15.0", diff --git a/src/component/elements/Sections.tsx b/src/component/elements/Sections.tsx index c702dd8233..50e68f3165 100644 --- a/src/component/elements/Sections.tsx +++ b/src/component/elements/Sections.tsx @@ -207,7 +207,7 @@ const InnerHeader = styled.div` `; interface BaseSectionProps { - title: string; + title: ReactNode; serial?: number; rightElement?: ReactNode | ((isOpen: boolean) => ReactNode); leftElement?: ReactNode | ((isOpen: boolean) => ReactNode); @@ -216,14 +216,14 @@ interface BaseSectionProps { } interface SectionItemProps extends BaseSectionProps { - id?: string; + id: string; index?: number; onClick?: (id: any, event?: MouseEvent) => void; children?: ReactNode | ((options: { isOpen?: boolean }) => ReactNode); isOpen: boolean; sticky?: boolean; onReorder?: (sourceId: number, targetId: number) => void; - dragLabel?: string; + dragLabel?: ReactNode; } interface SectionProps { @@ -271,7 +271,7 @@ function SectionItem(props: SectionItemProps) { const { title, dragLabel = title, - id = title, + id, onClick, serial, rightElement, diff --git a/src/component/header/Header.tsx b/src/component/header/Header.tsx index 068bc84c86..1f4549bd80 100644 --- a/src/component/header/Header.tsx +++ b/src/component/header/Header.tsx @@ -10,7 +10,6 @@ import { Toolbar, useFullscreen } from 'react-science/ui'; import { docsBaseUrl } from '../../constants.js'; import { useChartData } from '../context/ChartContext.js'; -import { useCore } from '../context/CoreContext.js'; import { usePreferences, useWorkspacesList, @@ -28,7 +27,7 @@ import AboutUsModal from '../modal/aboutUs/AboutUsModal.js'; import WorkspaceItem from '../modal/setting/WorkspaceItem.js'; import { GeneralSettingsToolbarItem } from '../modal/setting/general_settings.js'; import { options } from '../toolbar/ToolTypes.js'; -import { renderCoreSlot } from '../utility/renderCoreSlot.js'; +import { CoreSlot } from '../utility/CoreSlot.tsx'; import { AutoPeakPickingOptionPanel } from './AutoPeakPickingOptionPanel.js'; import { HeaderWrapper } from './HeaderWrapper.js'; @@ -63,7 +62,6 @@ interface HeaderInnerProps { function HeaderInner(props: HeaderInnerProps) { const { selectedOptionPanel, height } = props; - const core = useCore(); const { current: { @@ -149,7 +147,7 @@ function HeaderInner(props: HeaderInnerProps) { }} > - {renderCoreSlot(core, 'topbar.right')} + {!hideWorkspaces && ( diff --git a/src/component/modal/aboutUs/AboutUsModal.tsx b/src/component/modal/aboutUs/AboutUsModal.tsx index 640452b8e0..7969144a2e 100644 --- a/src/component/modal/aboutUs/AboutUsModal.tsx +++ b/src/component/modal/aboutUs/AboutUsModal.tsx @@ -3,11 +3,10 @@ import { SvgLogoNmrium } from 'cheminfo-font'; import { Toolbar, useOnOff } from 'react-science/ui'; import versionInfo from '../../../versionInfo.js'; -import { useCore } from '../../context/CoreContext.js'; import Logo from '../../elements/Logo.js'; import { StandardDialog } from '../../elements/StandardDialog.tsx'; import { StyledDialogBody } from '../../elements/StyledDialogBody.js'; -import { renderCoreSlot } from '../../utility/renderCoreSlot.js'; +import { CoreSlot } from '../../utility/CoreSlot.tsx'; import AboutUsZakodium from './AboutUsZakodium.js'; @@ -116,7 +115,6 @@ const modalContentFallback = ( function AboutUsModal() { const [isOpenDialog, openDialog, closeDialog] = useOnOff(false); - const core = useCore(); return ( <> @@ -137,7 +135,10 @@ function AboutUsModal() { title="About NMRium" > - {renderCoreSlot(core, 'topbar.about_us.modal', modalContentFallback)} + diff --git a/src/component/panels/filtersPanel/FilterPanel.tsx b/src/component/panels/filtersPanel/FilterPanel.tsx index b6db5a60af..9a2da618a6 100644 --- a/src/component/panels/filtersPanel/FilterPanel.tsx +++ b/src/component/panels/filtersPanel/FilterPanel.tsx @@ -2,17 +2,20 @@ import { useDispatch } from '../../context/DispatchContext.js'; import { useToaster } from '../../context/ToasterContext.js'; import type { AlertButton } from '../../elements/Alert.js'; import { useAlert } from '../../elements/Alert.js'; +import useCheckExperimentalFeature from '../../hooks/useCheckExperimentalFeature.ts'; import useSpectrum from '../../hooks/useSpectrum.js'; import { TablePanel } from '../extra/BasicPanelStyle.js'; import DefaultPanelHeader from '../header/DefaultPanelHeader.js'; import { FiltersSectionsPanel } from './Filters/FiltersSectionsPanel.js'; +import { ProcessingsSectionsPanel } from './processings_sections_panel.tsx'; export default function FiltersPanel() { const dispatch = useDispatch(); const toaster = useToaster(); const { showAlert } = useAlert(); const { filters } = useSpectrum({ filters: [] }); + const isExperimental = useCheckExperimentalFeature(); function handleDeleteFilter() { const buttons: AlertButton[] = [ @@ -46,6 +49,13 @@ export default function FiltersPanel() { />
+ + {isExperimental && ( + <> +
+ + + )}
); diff --git a/src/component/panels/filtersPanel/Filters/base/BaseApodizationOptions.tsx b/src/component/panels/filtersPanel/Filters/base/BaseApodizationOptions.tsx index 5a5e5332cf..61a881ee26 100644 --- a/src/component/panels/filtersPanel/Filters/base/BaseApodizationOptions.tsx +++ b/src/component/panels/filtersPanel/Filters/base/BaseApodizationOptions.tsx @@ -330,6 +330,7 @@ function OptionsSection(options: OptionsSectionProps) { return ( > = { + apodization: '@zakodium/nmrium-core-plugins#apodization1D', + zeroFilling: '@zakodium/nmrium-core-plugins#zeroFilling1D', + fft: '@zakodium/nmrium-core-plugins#fft1D', + phaseCorrection: '@zakodium/nmrium-core-plugins#phaseCorrection1D', +}; + +type FilterName = Filter1D['name'] | Filter2D['name']; +const mapFiltersToProcessings: Partial< + Record +> = { + apodization: '@zakodium/nmrium-core-plugins#apodization1D', + digitalFilter: '@zakodium/nmrium-core-plugins#digitalFilter1D', + fft: '@zakodium/nmrium-core-plugins#fft1D', + phaseCorrection: '@zakodium/nmrium-core-plugins#phaseCorrection1D', + shiftX: '@zakodium/nmrium-core-plugins#shiftX1D', + zeroFilling: '@zakodium/nmrium-core-plugins#zeroFilling1D', +}; + +const unremoveableProcessings = new Set([ + '@zakodium/nmrium-core-plugins#digitalFilter1D', + '@zakodium/nmrium-core-plugins#digitalFilter2D', +]); + +export function ProcessingsSectionsPanel() { + const core = useCore(); + const { showAlert } = useAlert(); + const spectrum: Spectrum | undefined = useSpectrum(); + const { toolOptions } = useChartData(); + + cast(toolOptions.data.activeFilterID); + const selectedToolProcessing = mapToolsToProcessing[toolOptions.selectedTool]; + const activeProcessing = + mapFiltersToProcessings[toolOptions.data.activeFilterID]; + const [openedOperation, setOpenedOperation] = useState(); + + const [processings, setProcessings] = useState( + () => spectrum?.processings ?? [], + ); + useEffect(() => { + setProcessings(spectrum?.processings ?? []); + }, [spectrum?.processings]); + + const selectedProcessing = useMemo<{ + operatorId?: ProcessingOperatorId; + operationId?: string; + }>(() => { + if (selectedToolProcessing) return { operatorId: selectedToolProcessing }; + if ( + activeProcessing && + spectrum?.processings?.some((p) => p.operatorId === activeProcessing) + ) { + return { operatorId: activeProcessing }; + } + + return { operationId: openedOperation }; + }, [ + activeProcessing, + openedOperation, + selectedToolProcessing, + spectrum?.processings, + ]); + + function handleDeleteFilter() { + const buttons: AlertButton[] = [ + { + text: 'Yes', + intent: 'danger', + onClick: () => setProcessings([]), + }, + { text: 'No' }, + ]; + + showAlert({ + message: + 'You are about to delete all processing steps, Are you sure?. Experimental, not implemented yet', + buttons, + }); + } + + function toggleSection(operationId: string) { + setOpenedOperation( + openedOperation === operationId ? undefined : operationId, + ); + } + + function onReorder(sourceIndex: number, targetIndex: number) { + const newProcessings = [...processings]; + [newProcessings[sourceIndex], newProcessings[targetIndex]] = [ + newProcessings[targetIndex], + newProcessings[sourceIndex], + ]; + setProcessings(newProcessings); + } + + if (!spectrum) return null; + + return ( + <> + + + {processings.length === 0 && } + {processings.length > 0 && ( + + {processings?.map((operation, index) => { + const operatorUI = core.slotOperator(operation.operatorId); + const isOpen = + selectedProcessing.operatorId === operation.operatorId || + selectedProcessing.operationId === operation.uid; + const isEditable = operatorUI?.isEditable; + + return ( + + } + isOpen={isOpen} + index={index} + serial={index + 1} + sticky + onReorder={onReorder} + onClick={() => toggleSection(operation.uid)} + rightElement={ + + } + > + + } + operation={operation} + core={core} + onChange={(operation) => { + const newProcessings = processings.map((p) => + p.uid === operation.uid + ? { ...operation, options: undefined } + : p, + ); + setProcessings(newProcessings); + setOpenedOperation(undefined); + }} + > + {(children) => ( + + + + + {children} + + + )} + + + + ); + })} + + )} + + ); +} + +interface ProcessingItemExtraProps { + operation: SpectrumProcessingOperation; + isOpen: boolean; + isEditable: boolean | undefined; + setOpenedOperation: Dispatch>; + setProcessings: Dispatch< + SetStateAction>> + >; +} + +function ProcessingItemExtra(props: ProcessingItemExtraProps) { + const { operation, isOpen, isEditable, setOpenedOperation, setProcessings } = + props; + + return ( + + {isEditable && ( +