diff --git a/dist/actions/threshold.js b/dist/actions/threshold.js index 3feeeee2..5359753b 100644 --- a/dist/actions/threshold.js +++ b/dist/actions/threshold.js @@ -10,9 +10,9 @@ const updateThresholdValue = payload => ({ payload }); exports.updateThresholdValue = updateThresholdValue; -const resetThresholdValue = () => ({ +const resetThresholdValue = (payload = false) => ({ type: _action_type.THRESHOLD.RESET_VALUE, - payload: false + payload }); exports.resetThresholdValue = resetThresholdValue; const toggleThresholdIsEdit = payload => ({ diff --git a/dist/actions/ui.js b/dist/actions/ui.js index 590f7b16..88a2ab61 100644 --- a/dist/actions/ui.js +++ b/dist/actions/ui.js @@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.setUiViewerType = exports.setUiSweepType = exports.selectUiSweep = exports.scrollUiWheel = exports.displaySubViewerAt = exports.clickUiTarget = void 0; +exports.setUiViewerType = exports.setUiSweepType = exports.selectUiSweep = exports.scrollUiWheel = exports.restoreSweepExtent = exports.displaySubViewerAt = exports.clickUiTarget = void 0; var _action_type = require("../constants/action_type"); var _list_ui = require("../constants/list_ui"); var _integration_draft = require("../helpers/integration_draft.js"); @@ -45,15 +45,25 @@ const scrollUiWheel = payload => ({ payload }); exports.scrollUiWheel = scrollUiWheel; -const clickUiTarget = (payload, onPeak, voltammetryPeakIdx = 0, jcampIdx = 0, onPecker = false, sourceHint = null) => ({ - type: _action_type.UI.CLICK_TARGET, - payload, - onPeak, - voltammetryPeakIdx, - jcampIdx, - onPecker, - sourceHint +const restoreSweepExtent = payload => ({ + type: _action_type.UI.SWEEP.SELECT_ZOOMIN, + payload }); +exports.restoreSweepExtent = restoreSweepExtent; +const clickUiTarget = (payload, onPeak, voltammetryPeakIdx, jcampIdx, onPecker, sourceHint) => { + const action = { + type: _action_type.UI.CLICK_TARGET, + payload, + onPeak, + voltammetryPeakIdx: voltammetryPeakIdx ?? 0, + onPecker: onPecker ?? false, + sourceHint: sourceHint ?? null + }; + if (Number.isFinite(jcampIdx)) { + action.jcampIdx = jcampIdx; + } + return action; +}; exports.clickUiTarget = clickUiTarget; const displaySubViewerAt = payload => ({ type: _action_type.UI.SUB_VIEWER.DISPLAY_VIEWER_AT, diff --git a/dist/app.js b/dist/app.js index d66e1117..4c4217b1 100644 --- a/dist/app.js +++ b/dist/app.js @@ -72,6 +72,8 @@ const SpectraEditor = ({ multiMolSvgs, entityFileNames, userManualLink, + curveIdx, + sweepExtent, onLcmsPageRequest }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactRedux.Provider, { store: store, @@ -80,6 +82,8 @@ const SpectraEditor = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_layer_init.default, { entity: entity, multiEntities: multiEntities, + curveIdx: curveIdx, + sweepExtent: sweepExtent, entityFileNames: entityFileNames, userManualLink: userManualLink, others: others, @@ -103,6 +107,8 @@ exports.SpectraEditor = SpectraEditor; SpectraEditor.propTypes = { entity: _propTypes.default.object.isRequired, multiEntities: _propTypes.default.array, + curveIdx: _propTypes.default.number, + sweepExtent: _propTypes.default.object, entityFileNames: _propTypes.default.array, others: _propTypes.default.object, cLabel: _propTypes.default.string, diff --git a/dist/components/cmd_bar/01_viewer.js b/dist/components/cmd_bar/01_viewer.js index 87093240..c7c6adb0 100644 --- a/dist/components/cmd_bar/01_viewer.js +++ b/dist/components/cmd_bar/01_viewer.js @@ -49,19 +49,16 @@ const Viewer = ({ }) }) }) - }), hideCmdAnaViewerSt ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + }), hideCmdAnaViewerSt || disableCmdAnaViewerSt ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: "txt-sv-tp", children: "Analysis Viewer" }), - children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { - children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { - className: (0, _classnames.default)((0, _common.focusStyle)(isfocusAnalysisSt, classes), 'btn-sv-bar-analysis'), - disabled: disableCmdAnaViewerSt, - onClick: onViewAnalysis, - children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_SpellcheckOutlined.default, { - className: classes.icon - }) + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)((0, _common.focusStyle)(isfocusAnalysisSt, classes), 'btn-sv-bar-analysis'), + onClick: onViewAnalysis, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_SpellcheckOutlined.default, { + className: classes.icon }) }) })] diff --git a/dist/components/cmd_bar/03_peak.js b/dist/components/cmd_bar/03_peak.js index 4aeadb6c..a15b2862 100644 --- a/dist/components/cmd_bar/03_peak.js +++ b/dist/components/cmd_bar/03_peak.js @@ -65,6 +65,13 @@ const Peak = ({ }); } }; + const showAddPeak = !disableAddPeakSt; + const showRmPeak = !disableRmPeakSt; + const showSetRef = !disableSetRefSt; + const showClearAll = !disableRmPeakSt; + if (!showAddPeak && !showRmPeak && !showSetRef && !showClearAll) { + return null; + } if (isHandleMaxAndMinPeaksSt) { const { spectraList @@ -87,7 +94,7 @@ const Peak = ({ return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { className: classes.group, "data-testid": "Peak", - children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + children: [showAddPeak ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: "txt-sv-tp", children: "Add Peak" @@ -95,7 +102,6 @@ const Peak = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { className: (0, _classnames.default)((0, _common.focusStyle)(isFocusAddPeakSt, classes), 'btn-sv-bar-addpeak'), - disabled: disableAddPeakSt, onClick: onSweepPeakAdd, children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, 'txt-sv-bar-addpeak'), @@ -103,7 +109,7 @@ const Peak = ({ }) }) }) - }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + }) : null, showRmPeak ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: "txt-sv-tp", children: "Remove Peak" @@ -111,20 +117,19 @@ const Peak = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { className: (0, _classnames.default)((0, _common.focusStyle)(isFocusRmPeakSt, classes), 'btn-sv-bar-rmpeak'), - disabled: disableRmPeakSt, - onClick: onSweepPeakDelete, + onClick: onSweepPeakDELETE, children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, 'txt-sv-bar-rmpeak'), children: "P-" }) }) }) - }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_tri_btn.default, { + }) : null, showClearAll ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_tri_btn.default, { content: { tp: 'Clear All Peaks' }, cb: onClearAll, - isClearAllDisabled: disableRmPeakSt, + isClearAllDisabled: false, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, 'txt-sv-bar-rmallpeaks'), children: "P" @@ -132,7 +137,7 @@ const Peak = ({ className: (0, _classnames.default)(classes.txt, classes.txtIcon, 'txt-sv-bar-rmallpeaks'), children: "x" })] - }), !disableSetRefSt ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + }) : null, showSetRef ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: "txt-sv-tp", children: "Set Reference" @@ -140,7 +145,6 @@ const Peak = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { className: (0, _classnames.default)((0, _common.focusStyle)(isFocusSetRefSt, classes), 'btn-sv-bar-setref'), - disabled: disableSetRefSt, onClick: onSweepAnchorShift, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_AddLocationOutlined.default, { className: classes.icon diff --git a/dist/components/cmd_bar/04_integration.js b/dist/components/cmd_bar/04_integration.js index 7a61beb7..e4408c36 100644 --- a/dist/components/cmd_bar/04_integration.js +++ b/dist/components/cmd_bar/04_integration.js @@ -46,7 +46,7 @@ const styles = () => Object.assign({ } }, _common.commonStyle); const iconSize = '16px'; -const setFactor = (classes, isDisable, integrationSt, setIntegrationFkrAct, curveIdx) => { +const setFactor = (classes, integrationSt, setIntegrationFkrAct, curveIdx) => { const onFactorChanged = e => { e.target.blur(); setIntegrationFkrAct({ @@ -72,7 +72,6 @@ const setFactor = (classes, isDisable, integrationSt, setIntegrationFkrAct, curv } return /*#__PURE__*/(0, _jsxRuntime.jsx)(_TextField.default, { className: classes.field, - disabled: isDisable, id: "intg-factor-name", type: "number", value: refFactor, @@ -146,6 +145,9 @@ const Integration = ({ } setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_VISUAL_SPLIT, curveIdx); }; + if (isDisableSt) { + return null; + } return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { className: classes.group, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { @@ -156,12 +158,11 @@ const Integration = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_common.MuButton, { className: (0, _classnames.default)(isFocusAddIntgSt ? classes.cancelBtn : (0, _common.focusStyle)(false, classes), 'btn-add-inter'), - disabled: isDisableSt, onClick: onSweepIntegtAdd, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.default, { path: isFocusAddIntgSt ? _js.mdiClose : _js.mdiMathIntegral, size: iconSize, - color: isFocusAddIntgSt ? '#d32f2f' : iconColor(isDisableSt), + color: iconColor(isFocusAddIntgSt), className: (0, _classnames.default)(classes.iconMdi, 'icon-sv-bar-addint') }), isFocusAddIntgSt ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, classes.txtIcon, 'txt-sv-bar-addint'), @@ -177,12 +178,11 @@ const Integration = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_common.MuButton, { className: (0, _classnames.default)((0, _common.focusStyle)(isFocusRmIntgSt, classes), 'btn-remove-inter'), - disabled: isDisableSt, onClick: onSweepIntegtRm, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.default, { path: _js.mdiMathIntegral, size: iconSize, - color: iconColor(isFocusRmIntgSt || isDisableSt), + color: iconColor(isFocusRmIntgSt), className: (0, _classnames.default)(classes.iconMdi, 'icon-sv-bar-rmint') }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, classes.txtIcon, 'txt-sv-bar-rmint'), @@ -190,7 +190,7 @@ const Integration = ({ })] }) }) - }), ignoreRef ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: "txt-sv-tp", children: "Set Integration Reference" @@ -198,12 +198,11 @@ const Integration = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { className: (0, _classnames.default)((0, _common.focusStyle)(isFocusSetRefSt, classes), 'btn-set-inter-ref'), - disabled: isDisableSt, onClick: onSweepIntegtSR, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.default, { path: _js.mdiReflectVertical, size: iconSize, - color: iconColor(isFocusSetRefSt || isDisableSt), + color: iconColor(isFocusSetRefSt), className: (0, _classnames.default)(classes.iconMdi, 'icon-sv-bar-refint') }) }) @@ -217,12 +216,11 @@ const Integration = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_common.MuButton, { className: (0, _classnames.default)(isFocusSplitIntgSt ? classes.cancelBtn : (0, _common.focusStyle)(false, classes), 'btn-split-inter'), - disabled: isDisableSt, onClick: onSweepIntegtSplit, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.default, { path: isFocusSplitIntgSt ? _js.mdiClose : _js.mdiMathIntegral, size: iconSize, - color: isFocusSplitIntgSt ? '#d32f2f' : iconColor(isDisableSt), + color: isFocusSplitIntgSt ? '#d32f2f' : iconColor(false), className: (0, _classnames.default)(classes.iconMdi, 'icon-sv-bar-splitint') }), isFocusSplitIntgSt ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, classes.txtIcon, 'txt-sv-bar-splitint'), @@ -238,12 +236,11 @@ const Integration = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_common.MuButton, { className: (0, _classnames.default)(isFocusVisualSplitIntgSt ? classes.cancelBtn : (0, _common.focusStyle)(false, classes), 'btn-visual-split-inter'), - disabled: isDisableSt, onClick: onSweepIntegtVisualSplit, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.default, { path: isFocusVisualSplitIntgSt ? _js.mdiClose : _js.mdiMathIntegral, size: iconSize, - color: isFocusVisualSplitIntgSt ? '#d32f2f' : iconColor(isDisableSt), + color: isFocusVisualSplitIntgSt ? '#d32f2f' : iconColor(false), className: (0, _classnames.default)(classes.iconMdi, 'icon-sv-bar-visualsplitint') }), isFocusVisualSplitIntgSt ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, classes.txtIcon, 'txt-sv-bar-visualsplitint'), @@ -252,7 +249,7 @@ const Integration = ({ }) }) })] - }) : null, !ignoreRef ? setFactor(classes, isDisableSt, integrationSt, setIntegrationFkrAct, curveIdx) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_tri_btn.default, { + }) : null, !ignoreRef ? setFactor(classes, integrationSt, setIntegrationFkrAct, curveIdx) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_tri_btn.default, { content: { tp: 'Clear All Integration' }, @@ -260,7 +257,7 @@ const Integration = ({ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.default, { path: _js.mdiMathIntegral, size: iconSize, - color: iconColor(isDisableSt), + color: iconColor(false), className: (0, _classnames.default)(classes.iconMdi, 'icon-sv-bar-rmallint') }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, classes.txtIcon, 'txt-sv-bar-rmallint'), diff --git a/dist/components/cmd_bar/05_multiplicity.js b/dist/components/cmd_bar/05_multiplicity.js index c725320e..223e38aa 100644 --- a/dist/components/cmd_bar/05_multiplicity.js +++ b/dist/components/cmd_bar/05_multiplicity.js @@ -29,10 +29,8 @@ const Multiplicity = ({ isFocusAddMpySt, disableAddMpySt, isFocusRmMpySt, - disableRmMpySt, isFocusAddPeakSt, isFocusRmPeakSt, - disableMpyPeakSt, setUiSweepTypeAct, clearMpyAllAct, curveSt @@ -47,6 +45,9 @@ const Multiplicity = ({ const onClearAll = () => clearMpyAllAct({ curveIdx }); + if (disableAddMpySt) { + return null; + } return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { className: classes.group, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { @@ -57,7 +58,6 @@ const Multiplicity = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { className: (0, _classnames.default)((0, _common.focusStyle)(isFocusAddMpySt, classes), 'btn-sv-bar-addmpy'), - disabled: disableAddMpySt, onClick: onSweepMutAdd, children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, 'txt-sv-bar-addmpy'), @@ -73,7 +73,6 @@ const Multiplicity = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { className: (0, _classnames.default)((0, _common.focusStyle)(isFocusRmMpySt, classes), 'btn-sv-bar-rmmpy'), - disabled: disableRmMpySt, onClick: onOneMutAdd, children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, 'txt-sv-bar-rmmpy'), @@ -89,7 +88,6 @@ const Multiplicity = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { className: (0, _classnames.default)((0, _common.focusStyle)(isFocusAddPeakSt, classes), 'btn-sv-bar-addpeakmpy'), - disabled: disableMpyPeakSt, onClick: onPeakMutAdd, children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, 'txt-sv-bar-addpeakmpy'), @@ -105,7 +103,6 @@ const Multiplicity = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { className: (0, _classnames.default)((0, _common.focusStyle)(isFocusRmPeakSt, classes), 'btn-sv-bar-rmpeakmpy'), - disabled: disableMpyPeakSt, onClick: onPeakMutRm, children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, 'txt-sv-bar-rmpeakmpy'), @@ -113,14 +110,12 @@ const Multiplicity = ({ }) }) }) - }), disableAddMpySt ? null : - /*#__PURE__*/ - // eslint-disable-line - (0, _jsxRuntime.jsx)(_tri_btn.default, { + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_tri_btn.default, { content: { tp: 'Clear All Multiplicity' }, cb: onClearAll, + isClearAllDisabled: false, children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.txt, 'txt-sv-bar-rmallmpy'), children: "Jx" diff --git a/dist/components/cmd_bar/07_pecker.js b/dist/components/cmd_bar/07_pecker.js index d8fbf678..d19d4e9f 100644 --- a/dist/components/cmd_bar/07_pecker.js +++ b/dist/components/cmd_bar/07_pecker.js @@ -110,7 +110,11 @@ const Pecker = ({ } = spectra; hasRefPeaks = hasRefPeak; } - return !_cfg.default.hidePanelCyclicVolta(layoutSt) ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + if (_cfg.default.hidePanelCyclicVolta(layoutSt)) { + return null; + } + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: classes.group, "data-testid": "Pecker", children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { @@ -161,7 +165,7 @@ const Pecker = ({ }) }) })] - }) : /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {}); + }); }; const mapStateToProps = (state, _) => ( // eslint-disable-line diff --git a/dist/components/cmd_bar/index.js b/dist/components/cmd_bar/index.js index e82cf604..260e9bd1 100644 --- a/dist/components/cmd_bar/index.js +++ b/dist/components/cmd_bar/index.js @@ -26,6 +26,7 @@ var _r08_change_axes = _interopRequireDefault(require("./r08_change_axes")); var _r09_detector = _interopRequireDefault(require("./r09_detector")); var _r10_cv_density = _interopRequireDefault(require("./r10_cv_density")); var _format = _interopRequireDefault(require("../../helpers/format")); +var _cfg = _interopRequireDefault(require("../../helpers/cfg")); var _jsxRuntime = require("react/jsx-runtime"); /* eslint-disable prefer-object-spread, function-paren-newline, react/function-component-definition, react/require-default-props */ @@ -77,6 +78,8 @@ const CmdBar = ({ prependLcMsToolbar }) => { const isCvLayout = _format.default.isCyclicVoltaLayout(layoutSt); + const hideIntegration = isCvLayout || _cfg.default.btnCmdIntg(layoutSt); + const hideMultiplicity = isCvLayout || _cfg.default.btnCmdMpy(layoutSt); const rightCluster = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_r01_layout.default, { feature: feature, @@ -118,7 +121,7 @@ const CmdBar = ({ feature: feature }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_pecker.default, { jcampIdx: jcampIdx - }), isCvLayout ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_integration.default, {}), isCvLayout ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_multiplicity.default, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_undo_redo.default, {})] + }), hideIntegration ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_integration.default, {}), hideMultiplicity ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_multiplicity.default, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_undo_redo.default, {})] }), rightCluster] }); }; diff --git a/dist/components/cmd_bar/r03_threshold.js b/dist/components/cmd_bar/r03_threshold.js index 0ca008d9..c1016f8d 100644 --- a/dist/components/cmd_bar/r03_threshold.js +++ b/dist/components/cmd_bar/r03_threshold.js @@ -109,6 +109,16 @@ const Threshold = ({ } else { thresVal = thresValSt || (feature ? feature.thresRef : hplcMsSt?.threshold?.value); } + const { + curveIdx + } = curveSt; + const onResetThreshold = () => resetThresholdValueAct({ + value: false, + curveIdx + }); + const onToggleThreshold = () => toggleThresholdIsEditAct({ + curveIdx + }); return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { className: classes.groupRight, children: [setThreshold(classes, thresVal, updateThresholdValueAct, curveSt), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { @@ -120,7 +130,7 @@ const Threshold = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { className: (0, _classnames.default)('btn-sv-bar-thresref'), disabled: _cfg.default.btnCmdThres(thresVal), - onClick: resetThresholdValueAct, + onClick: onResetThreshold, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_RefreshOutlined.default, { className: classes.icon }) @@ -135,7 +145,7 @@ const Threshold = ({ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { className: (0, _classnames.default)('btn-sv-bar-thresrst'), disabled: _cfg.default.btnCmdThres(thresVal), - onClick: toggleThresholdIsEditAct, + onClick: onToggleThreshold, children: restoreIcon(classes, hasEdit, isEditSt) }) }) diff --git a/dist/components/cmd_bar/r05_submit_btn.js b/dist/components/cmd_bar/r05_submit_btn.js index ceb5ca36..d198f6da 100644 --- a/dist/components/cmd_bar/r05_submit_btn.js +++ b/dist/components/cmd_bar/r05_submit_btn.js @@ -17,6 +17,7 @@ var _chem = require("../../helpers/chem"); var _common = require("./common"); var _extractPeaksEdit = require("../../helpers/extractPeaksEdit"); var _format = _interopRequireDefault(require("../../helpers/format")); +var _shift = require("../../helpers/shift"); var _jsxRuntime = require("react/jsx-runtime"); /* eslint-disable prefer-object-spread, function-paren-newline, react/function-component-definition, function-call-argument-newline, @@ -89,7 +90,19 @@ const resolveLcmsIntegrationsExportForSubmit = (analysis, hplcMsSt) => { } return 'percent'; }; -const pickFromList = (list, index, fallback = null) => Array.isArray(list) && list[index] !== undefined ? list[index] : fallback; +const emptyIntegration = { + stack: [], + refArea: 1, + refFactor: 1, + shift: 0, + edited: false +}; +const emptyMultiplicity = { + stack: [], + shift: 0, + smExtext: false, + edited: false +}; const hasBoolean = value => typeof value === 'boolean'; const resolveOptionalBooleanFlags = analysis => { const flags = {}; @@ -124,9 +137,9 @@ const buildSpectrumPayload = ({ const peaksEdit = (0, _extractPeaksEdit.extractPeaksEdit)(feature, editPeakAtIndex, threshold, shiftSt, layoutSt, curveIdx); const scan = (0, _chem.Convert2Scan)(feature, scanSt); const thres = (0, _chem.Convert2Thres)(feature, threshold); - const shift = pickFromList(shiftSt?.shifts, curveIdx, shiftSt); - const integration = pickFromList(integrationSt?.integrations, curveIdx, integrationSt); - const multiplicity = pickFromList(multiplicitySt?.multiplicities, curveIdx, multiplicitySt); + const shift = (0, _shift.shiftEntryAtIndex)(shiftSt, curveIdx); + const integration = (0, _shift.listEntryAtIndex)(integrationSt?.integrations, curveIdx, emptyIntegration); + const multiplicity = (0, _shift.listEntryAtIndex)(multiplicitySt?.multiplicities, curveIdx, emptyMultiplicity); const { xLabel, yLabel @@ -149,6 +162,7 @@ const buildSpectrumPayload = ({ return { peaks: peaksEdit, layout: layoutSt, + xUnit: xLabel, shift, scan, thres, @@ -166,10 +180,11 @@ const buildSpectrumPayload = ({ axesUnitsSt, detectorSt, dscMetaData, + feature, ...optionalBooleanFlags }; }; -const onClickCb = (operationValue, isAscend, isIntensity, layoutSt, shiftSt, analysis, decimalSt, integrationSt, multiplicitySt, waveLengthSt, cyclicvoltaSt, curveSt, axesUnitsSt, detectorSt, dscMetaData, curveList, editPeakSt, thresList, scanSt, feature, hplcMsSt) => () => { +const onClickCb = (operationValue, isAscend, isIntensity, layoutSt, shiftSt, analysis, decimalSt, integrationSt, multiplicitySt, waveLengthSt, cyclicvoltaSt, curveSt, axesUnitsSt, detectorSt, dscMetaData, curveList, editPeakSt, thresList, scanSt, feature, hplcMsSt, sweepExtentSt) => () => { const defaultCurves = feature ? [{ feature }] : []; @@ -226,7 +241,10 @@ const onClickCb = (operationValue, isAscend, isIntensity, layoutSt, shiftSt, ana } return spectrumPayload; }); + const selectedIdx = Number.isFinite(curveSt?.curveIdx) ? curveSt.curveIdx : 0; + const selectedSpectrumPayload = spectraList[selectedIdx] || spectraList[0] || {}; const payload = { + ...selectedSpectrumPayload, spectra_list: spectraList }; if (lcmsGlobalFields) { @@ -237,6 +255,9 @@ const onClickCb = (operationValue, isAscend, isIntensity, layoutSt, shiftSt, ana curveIdx: curveSt.curveIdx }; } + if (sweepExtentSt?.xExtent || sweepExtentSt?.yExtent) { + payload.sweepExtent = sweepExtentSt; + } operationValue(payload); }; const BtnSubmit = ({ @@ -261,7 +282,11 @@ const BtnSubmit = ({ axesUnitsSt, detectorSt, metaSt, - hplcMsSt + hplcMsSt, + sweepExtentSt, + disabled, + className, + children }) => { // const disBtn = peaksEdit.length === 0 || statusSt.btnSubmit || disabled; const { @@ -290,13 +315,14 @@ const BtnSubmit = ({ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: "txt-sv-tp", - children: "Submit" + children: operation.name || 'Submit' }), children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { - className: (0, _classnames.default)('btn-sv-bar-submit'), + className: (0, _classnames.default)('btn-sv-bar-submit', className), color: "primary", - onClick: onClickCb(operation.value, isAscend, isIntensity, layoutSt, shiftSt, forecastSt.predictions, decimalSt, integrationSt, multiplicitySt, waveLengthSt, cyclicvoltaPayload, curveSt, axesUnitsSt, detectorSt, dscMetaData, curveList, editPeakSt, thresList, scanSt, feature, hplcMsSt), - children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_PlayCircleOutline.default, { + disabled: disabled, + onClick: onClickCb(operation.value, isAscend, isIntensity, layoutSt, shiftSt, forecastSt.predictions, decimalSt, integrationSt, multiplicitySt, waveLengthSt, cyclicvoltaPayload, curveSt, axesUnitsSt, detectorSt, dscMetaData, curveList, editPeakSt, thresList, scanSt, feature, hplcMsSt, sweepExtentSt), + children: children || /*#__PURE__*/(0, _jsxRuntime.jsx)(_PlayCircleOutline.default, { className: classes.icon }) }) @@ -321,7 +347,8 @@ const mapStateToProps = (state, props) => ( axesUnitsSt: state.axesUnits, detectorSt: state.detector, metaSt: state.meta, - hplcMsSt: state.hplcMs + hplcMsSt: state.hplcMs, + sweepExtentSt: state.ui.sweepExtent }); BtnSubmit.propTypes = { classes: _propTypes.default.object.isRequired, @@ -345,7 +372,11 @@ BtnSubmit.propTypes = { axesUnitsSt: _propTypes.default.object.isRequired, detectorSt: _propTypes.default.object.isRequired, metaSt: _propTypes.default.object.isRequired, - hplcMsSt: _propTypes.default.object + hplcMsSt: _propTypes.default.object, + sweepExtentSt: _propTypes.default.object, + disabled: _propTypes.default.bool, + className: _propTypes.default.string, + children: _propTypes.default.node }; BtnSubmit.defaultProps = { hplcMsSt: {} diff --git a/dist/components/cmd_bar/r07_wavelength_btn.js b/dist/components/cmd_bar/r07_wavelength_btn.js index 9b517427..183029cd 100644 --- a/dist/components/cmd_bar/r07_wavelength_btn.js +++ b/dist/components/cmd_bar/r07_wavelength_btn.js @@ -30,7 +30,7 @@ const styles = () => Object.assign({ }, _common.commonStyle); const wavelengthSelect = (classes, waveLengthSt, layoutSt, updateWavelengthAct) => { if (!_format.default.isXRDLayout(layoutSt)) { - return /*#__PURE__*/(0, _jsxRuntime.jsx)("i", {}); + return null; } const onChange = e => updateWavelengthAct(e.target.value); return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { @@ -65,10 +65,13 @@ const Wavelength = ({ waveLengthSt, layoutSt, updateWavelengthAct -}) => /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { - className: classes.groupRight, - children: wavelengthSelect(classes, waveLengthSt, layoutSt, updateWavelengthAct) -}); +}) => { + if (!_format.default.isXRDLayout(layoutSt)) return null; + return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: classes.groupRight, + children: wavelengthSelect(classes, waveLengthSt, layoutSt, updateWavelengthAct) + }); +}; const mapStateToProps = (state, props) => ( // eslint-disable-line { diff --git a/dist/components/cmd_bar/r08_change_axes.js b/dist/components/cmd_bar/r08_change_axes.js index b8d3ee75..4c8a1051 100644 --- a/dist/components/cmd_bar/r08_change_axes.js +++ b/dist/components/cmd_bar/r08_change_axes.js @@ -131,7 +131,7 @@ const axisY = (classes, layoutSt, axesUnitsSt, updateYAxisAct, curveSt) => { }; const showSelect = (classes, layoutSt, curveSt, axesUnitsSt, updateXAxisAct, updateYAxisAct) => { if (!listLayoutToShow.includes(layoutSt)) { - return /*#__PURE__*/(0, _jsxRuntime.jsx)("i", {}); + return null; } return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { children: [axisX(classes, layoutSt, axesUnitsSt, updateXAxisAct, curveSt), axisY(classes, layoutSt, axesUnitsSt, updateYAxisAct, curveSt)] @@ -161,6 +161,7 @@ const ChangeAxes = ({ }); } }, [layoutSt, axes, curveIdx, updateYAxisAct]); + if (!listLayoutToShow.includes(layoutSt)) return null; return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: classes.groupRight, "data-testid": "ChangeAxes", diff --git a/dist/components/cmd_bar/r09_detector.js b/dist/components/cmd_bar/r09_detector.js index 380e6240..1ad7948c 100644 --- a/dist/components/cmd_bar/r09_detector.js +++ b/dist/components/cmd_bar/r09_detector.js @@ -30,7 +30,7 @@ const styles = () => Object.assign({ }, _common.commonStyle); const detectorSelect = (classes, detectorSt, curveSt, layoutSt, updateDetectorAct) => { if (!_format.default.isSECLayout(layoutSt)) { - return /*#__PURE__*/(0, _jsxRuntime.jsx)("i", {}); + return null; } const { curveIdx @@ -80,10 +80,13 @@ const Detector = ({ curveSt, layoutSt, updateDetectorAct -}) => /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { - className: classes.groupRight, - children: detectorSelect(classes, detectorSt, curveSt, layoutSt, updateDetectorAct) -}); +}) => { + if (!_format.default.isSECLayout(layoutSt)) return null; + return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: classes.groupRight, + children: detectorSelect(classes, detectorSt, curveSt, layoutSt, updateDetectorAct) + }); +}; const mapStateToProps = (state, _props) => ( // eslint-disable-line { diff --git a/dist/components/cmd_bar/r10_cv_density.js b/dist/components/cmd_bar/r10_cv_density.js index 4f850b0b..c2cf51a3 100644 --- a/dist/components/cmd_bar/r10_cv_density.js +++ b/dist/components/cmd_bar/r10_cv_density.js @@ -51,7 +51,7 @@ const CvDensityControls = ({ setAreaUnitAct, toggleDensityAct }) => { - if (layoutSt !== _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY) return /*#__PURE__*/(0, _jsxRuntime.jsx)("i", {}); + if (layoutSt !== _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY) return null; const handleAreaChange = e => { const raw = e.target.value; if (raw === '') { diff --git a/dist/components/common/draw.js b/dist/components/common/draw.js index ad86fad9..0ae7d0c3 100644 --- a/dist/components/common/draw.js +++ b/dist/components/common/draw.js @@ -26,7 +26,7 @@ const drawDisplay = (klass, isHidden) => { } }; exports.drawDisplay = drawDisplay; -const drawDestroy = klass => d3.select(`${klass} > *`).remove(); +const drawDestroy = klass => d3.select(klass).selectAll('*').remove(); exports.drawDestroy = drawDestroy; const drawArrowOnCurve = (klass, isHidden) => { if (isHidden) { diff --git a/dist/components/d3_line/line_focus.js b/dist/components/d3_line/line_focus.js index 113c3abe..35aac661 100644 --- a/dist/components/d3_line/line_focus.js +++ b/dist/components/d3_line/line_focus.js @@ -314,6 +314,10 @@ class LineFocus { const dPks = this.mergedPeaks(editPeakSt); const mpp = this.tags.pPath.selectAll('path').data(dPks); mpp.exit().attr('class', 'exit').remove(); + const clearPeakLabels = () => { + const bpTxt = this.tags.bpTxt.selectAll('text').data([]); + bpTxt.exit().attr('class', 'exit').remove(); + }; const linePath = [{ x: -0.5, y: 10 @@ -351,6 +355,8 @@ class LineFocus { const bpTxt = this.tags.bpTxt.selectAll('text').data(dPks); bpTxt.exit().attr('class', 'exit').remove(); bpTxt.enter().append('text').attr('class', 'peak-text').attr('font-family', 'Helvetica').style('font-size', '12px').attr('fill', '#228B22').style('text-anchor', 'middle').merge(bpTxt).attr('id', d => `mpp${Math.round(1000 * d.x)}`).text(d => d.x.toFixed(2)).attr('transform', d => `translate(${xt(d.x)}, ${yt(d.y) - 25})`).on('click', (event, d) => this.onClickTarget(event, d)); + } else { + clearPeakLabels(); } } drawInteg(integrationState) { @@ -361,11 +367,29 @@ class LineFocus { sameData } = this.shouldUpdate; if (sameXY && sameLySt && sameItSt && sameData) return; + const clearIntegralPaths = () => { + const empty = []; + const igbp = this.tags.igbPath.selectAll('path').data(empty); + igbp.exit().attr('class', 'exit').remove(); + const igcp = this.tags.igcPath.selectAll('path').data(empty); + igcp.exit().attr('class', 'exit').remove(); + const igtp = this.tags.igtPath.selectAll('text').data(empty); + igtp.exit().attr('class', 'exit').remove(); + }; + const clearAUC = () => { + const auc = this.tags.aucPath.selectAll('path').data([]); + auc.exit().attr('class', 'exit').remove(); + }; const { selectedIdx, integrations } = integrationState; const selectedIntegration = integrations[selectedIdx]; + if (selectedIntegration === false || selectedIntegration === undefined) { + clearIntegralPaths(); + clearAUC(); + return; + } const { stack, refArea, @@ -393,16 +417,15 @@ class LineFocus { const igtp = this.tags.igtPath.selectAll('text').data(itgs); igtp.exit().attr('class', 'exit').remove(); if (itgs.length === 0 || isDisable) { - // remove drawn area under curve - const auc = this.tags.aucPath.selectAll('path').data(stack); - auc.exit().attr('class', 'exit').remove(); - auc.merge(auc); - this.drawVisualSplitLines(showIntegSplit ? itgs : [], shift, ignoreRef); + clearIntegralPaths(); + clearAUC(); return; } if (ignoreRef) { + clearIntegralPaths(); this.drawAUC(stack, shift); } else { + clearAUC(); // rescale for zoom const { xt @@ -469,14 +492,12 @@ class LineFocus { multiplicities = [] } = mtplySt || {}; const selectedMulti = multiplicities[selectedIdx] || {}; - const { - stack = [], - smExtext = false, - shift = 0 - } = selectedMulti; - const hasValidExtent = extent => extent && Number.isFinite(extent.xL) && Number.isFinite(extent.xU); - const mpys = stack.filter(m => hasValidExtent(m?.xExtent)); const isDisable = _cfg.default.btnCmdMpy(this.layout); + const hasMpy = !isDisable && selectedMulti?.stack?.length > 0; + const mpys = hasMpy ? selectedMulti.stack : []; + const smExtext = hasMpy ? selectedMulti.smExtext : false; + const shift = hasMpy ? selectedMulti.shift : 0; + const hasValidExtent = extent => extent && Number.isFinite(extent.xL) && Number.isFinite(extent.xU); if (mpys.length === 0 || isDisable) return; const activeExtent = hasValidExtent(smExtext) ? smExtext : mpys[0].xExtent; // rescale for zoom diff --git a/dist/components/d3_multi/index.js b/dist/components/d3_multi/index.js index 9096bb2c..98da6326 100644 --- a/dist/components/d3_multi/index.js +++ b/dist/components/d3_multi/index.js @@ -27,6 +27,16 @@ react/no-unused-prop-types */ const W = Math.round(window.innerWidth * 0.90 * 9 / 12); // ROI const H = Math.round(window.innerHeight * 0.90 * 0.85); // ROI +const buildResetPayload = (feature, curveSt) => ({ + ...feature, + curveIdx: curveSt.curveIdx +}); +const hasActiveZoom = sweepExtentSt => { + if (!sweepExtentSt) return false; + return !!(sweepExtentSt.xExtent || sweepExtentSt.yExtent); +}; +const canSkipReset = (sweepExtentSt, layoutSt, feature) => hasActiveZoom(sweepExtentSt) && layoutSt === feature?.operation?.layout; +const isSameMultiComparison = (oldEntities, newEntities) => Array.isArray(oldEntities) && Array.isArray(newEntities) && oldEntities.length > 0 && oldEntities.length === newEntities.length; class ViewerMulti extends _react.default.Component { constructor(props) { super(props); @@ -82,6 +92,7 @@ class ViewerMulti extends _react.default.Component { isUiNoBrushSt, isHidden, cyclicvoltaSt, + shiftSt, integrationSt, mtplySt, axesUnitsSt, @@ -113,6 +124,7 @@ class ViewerMulti extends _react.default.Component { isUiVisualSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, + shiftSt, integrationSt, mtplySt, uiSt @@ -232,11 +244,12 @@ class ViewerMulti extends _react.default.Component { const { feature, resetAllAct, - entities + entities, + curveSt } = this.props; const oldEntities = prevProps.entities; - if (oldEntities !== entities) { - resetAllAct(feature); + if (oldEntities !== entities && !isSameMultiComparison(oldEntities, entities)) { + resetAllAct(buildResetPayload(feature, curveSt)); } } createMultiFocus(size, props) { @@ -280,6 +293,7 @@ class ViewerMulti extends _react.default.Component { isHidden, resetAllAct, cyclicvoltaSt, + shiftSt, integrationSt, mtplySt, uiSt @@ -287,8 +301,8 @@ class ViewerMulti extends _react.default.Component { const size = this.getTargetSize(layoutSt); this.currentSize = size; (0, _draw.drawDestroy)(this.rootKlass); - if (shouldReset) { - resetAllAct(feature); + if (shouldReset && !canSkipReset(sweepExtentSt, layoutSt, feature)) { + resetAllAct(buildResetPayload(feature, curveSt)); } const { xxLabel, @@ -311,6 +325,7 @@ class ViewerMulti extends _react.default.Component { isUiVisualSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, + shiftSt, integrationSt, mtplySt, uiSt @@ -348,6 +363,7 @@ const mapStateToProps = (state, props) => ({ isUiVisualSplitIntgSt: _cfg.default.showIntegSplitTools(state.layout) && state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_VISUAL_SPLIT, isUiNoBrushSt: _list_ui.LIST_NON_BRUSH_TYPES.indexOf(state.ui.sweepType) < 0, cyclicvoltaSt: state.cyclicvolta, + shiftSt: state.shift, maxminPeakSt: (0, _chem.Feature2MaxMinPeak)(state, props), integrationSt: state.integration.present, mtplySt: state.multiplicity.present, @@ -394,6 +410,7 @@ ViewerMulti.propTypes = { removeVisualSplitLineAct: _propTypes.default.func.isRequired, isHidden: _propTypes.default.bool, cyclicvoltaSt: _propTypes.default.object.isRequired, + shiftSt: _propTypes.default.object.isRequired, maxminPeakSt: _propTypes.default.object, addNewCylicVoltaPairPeakAct: _propTypes.default.func.isRequired, addCylicVoltaMaxPeakAct: _propTypes.default.func.isRequired, diff --git a/dist/components/d3_multi/multi_focus.js b/dist/components/d3_multi/multi_focus.js index 05d11c69..1d4e1f43 100644 --- a/dist/components/d3_multi/multi_focus.js +++ b/dist/components/d3_multi/multi_focus.js @@ -15,6 +15,7 @@ var _integration_split = require("../../helpers/integration_split"); var _list_layout = require("../../constants/list_layout"); var _format = _interopRequireDefault(require("../../helpers/format")); var _chem = require("../../helpers/chem"); +var _shift = require("../../helpers/shift"); var _cfg = _interopRequireDefault(require("../../helpers/cfg")); var _focus = require("../../helpers/focus"); var _integration = require("../../helpers/integration"); @@ -127,7 +128,8 @@ class MultiFocus { prevDtPk, prevSfPk, prevData, - prevYFactor + prevYFactor, + prevJcampIdx } = this.shouldUpdate; const { xt, @@ -141,6 +143,7 @@ class MultiFocus { const sameSfPk = prevSfPk === this.tSfPeaks || Array.isArray(prevSfPk) && Array.isArray(this.tSfPeaks) && prevSfPk.length === this.tSfPeaks.length && prevSfPk.every((peak, idx) => peak === this.tSfPeaks[idx]); const sameData = prevData === this.data.length; const sameYFactor = prevYFactor === this.yTransformFactor; + const sameJcampIdx = prevJcampIdx === this.jcampIdx; this.shouldUpdate = Object.assign({}, this.shouldUpdate, { sameXY, sameEpSt, @@ -150,7 +153,8 @@ class MultiFocus { sameDtPk, sameSfPk, sameData, - sameYFactor // eslint-disable-line + sameYFactor, + sameJcampIdx // eslint-disable-line }); } resetShouldUpdate(prevEpSt) { @@ -166,6 +170,7 @@ class MultiFocus { const prevData = this.data.length; const prevLySt = this.layout; const prevYFactor = this.yTransformFactor; + const prevJcampIdx = this.jcampIdx; this.shouldUpdate = Object.assign({}, this.shouldUpdate, { prevXt, prevYt, @@ -176,7 +181,8 @@ class MultiFocus { prevDtPk, prevSfPk, prevData, - prevYFactor // eslint-disable-line + prevYFactor, + prevJcampIdx // eslint-disable-line }); } setTip() { @@ -202,7 +208,7 @@ class MultiFocus { transformYValue(y) { return y * this.yTransformFactor; } - setDataParams(filterSeed, peaks, tTrEndPts, tSfPeaks, layout, cyclicvoltaSt, jcampIdx = 0) { + setDataParams(filterSeed, peaks, tTrEndPts, tSfPeaks, layout, cyclicvoltaSt, shiftSt, jcampIdx = 0) { this.data = []; this.otherLineData = []; let filterSubLayoutValue = null; @@ -214,7 +220,7 @@ class MultiFocus { feature, color } = entry; - const offset = (0, _chem.GetCyclicVoltaPreviousShift)(cyclicvoltaSt, jcampIdx); + const offset = _format.default.isCyclicVoltaLayout(layout) ? (0, _chem.GetCyclicVoltaPreviousShift)(cyclicvoltaSt, idx) : (0, _shift.shiftOffsetAtIndex)(shiftSt, idx); let currData = (0, _chem.convertTopic)(topic, layout, feature, offset); if (idx === jcampIdx) { if (!_format.default.isCyclicVoltaLayout(layout)) { @@ -439,9 +445,10 @@ class MultiFocus { sameXY, sameEpSt, sameDtPk, - sameSfPk + sameSfPk, + sameJcampIdx } = this.shouldUpdate; - if (!_format.default.isCyclicVoltaLayout(this.layout) && sameXY && sameEpSt && sameDtPk && sameSfPk) return; + if (!_format.default.isCyclicVoltaLayout(this.layout) && sameXY && sameEpSt && sameDtPk && sameSfPk && sameJcampIdx) return; // rescale for zoom const { @@ -474,6 +481,10 @@ class MultiFocus { } const mpp = this.tags.pPath.selectAll('path').data(dPks); mpp.exit().attr('class', 'exit').remove(); + const clearPeakLabels = () => { + const bpTxt = this.tags.bpTxt.selectAll('text').data([]); + bpTxt.exit().attr('class', 'exit').remove(); + }; const linePath = [{ x: -0.5, y: 10 @@ -530,6 +541,8 @@ class MultiFocus { const bpTxt = this.tags.bpTxt.selectAll('text').data(dPks); bpTxt.exit().attr('class', 'exit').remove(); bpTxt.enter().append('text').attr('class', 'peak-text').attr('font-family', 'Helvetica').style('font-size', '12px').attr('fill', '#228B22').style('text-anchor', 'middle').merge(bpTxt).attr('id', d => `mpp${Math.round(1000 * d.x)}`).text(d => d.x.toFixed(2)).attr('transform', d => `translate(${xt(d.x)}, ${yt(d.y) - 25})`).on('click', (event, d) => this.onClickTarget(event, d)); + } else { + clearPeakLabels(); } mpp.attr('fill', (_, index) => { return indexOfCVRefPeaks[index] === -1 ? 'blue' : 'red'; @@ -543,9 +556,10 @@ class MultiFocus { sameXY, sameEpSt, sameDtPk, - sameSfPk + sameSfPk, + sameJcampIdx } = this.shouldUpdate; - if (!_format.default.isCyclicVoltaLayout(this.layout) && sameXY && sameEpSt && sameDtPk && sameSfPk) return; + if (!_format.default.isCyclicVoltaLayout(this.layout) && sameXY && sameEpSt && sameDtPk && sameSfPk && sameJcampIdx) return; // rescale for zoom const { @@ -594,29 +608,30 @@ class MultiFocus { sameXY, sameLySt, sameItSt, - sameData + sameData, + sameJcampIdx } = this.shouldUpdate; - if (sameXY && sameLySt && sameItSt && sameData) return; + if (sameXY && sameLySt && sameItSt && sameData && sameJcampIdx) return; + const clearIntegralPaths = () => { + const empty = []; + const igbp = this.tags.igbPath.selectAll('path').data(empty); + igbp.exit().attr('class', 'exit').remove(); + const igcp = this.tags.igcPath.selectAll('path').data(empty); + igcp.exit().attr('class', 'exit').remove(); + const igtp = this.tags.igtPath.selectAll('text').data(empty); + igtp.exit().attr('class', 'exit').remove(); + }; + const clearAUC = () => { + const auc = this.tags.aucPath.selectAll('path').data([]); + auc.exit().attr('class', 'exit').remove(); + }; const { integrations } = integrationState; const selectedIntegration = integrations[this.jcampIdx]; if (selectedIntegration === false || selectedIntegration === undefined) { - Object.assign(this, { - integrationSplitTargets: { - stack: [], - shift: 0, - ignoreRef: false - } - }); - const itgs = []; - const igbp = this.tags.igbPath.selectAll('path').data(itgs); - igbp.exit().attr('class', 'exit').remove(); - const igcp = this.tags.igcPath.selectAll('path').data(itgs); - igcp.exit().attr('class', 'exit').remove(); - const igtp = this.tags.igtPath.selectAll('text').data(itgs); - igtp.exit().attr('class', 'exit').remove(); - this.drawVisualSplitLines([], 0, false); + clearIntegralPaths(); + clearAUC(); return; } const { @@ -646,16 +661,15 @@ class MultiFocus { const igtp = this.tags.igtPath.selectAll('text').data(itgs); igtp.exit().attr('class', 'exit').remove(); if (itgs.length === 0 || isDisable) { - // remove drawn area under curve - const auc = this.tags.aucPath.selectAll('path').data(stack); - auc.exit().attr('class', 'exit').remove(); - auc.merge(auc); - this.drawVisualSplitLines(showIntegSplit ? itgs : [], shift, ignoreRef); + clearIntegralPaths(); + clearAUC(); return; } if (ignoreRef) { + clearIntegralPaths(); this.drawAUC(stack, shift); } else { + clearAUC(); // rescale for zoom const { xt @@ -714,43 +728,20 @@ class MultiFocus { const { sameXY, sameLySt, - sameMySt + sameMySt, + sameJcampIdx } = this.shouldUpdate; - if (sameXY && sameLySt && sameMySt) return; + if (sameXY && sameLySt && sameMySt && sameJcampIdx) return; const { multiplicities } = mtplySt; const selectedMulti = multiplicities[this.jcampIdx]; - if (selectedMulti === false || selectedMulti === undefined) { - const mpys = []; - const mpyb = this.tags.mpybPath.selectAll('path').data(mpys); - mpyb.exit().attr('class', 'exit').remove(); - const mpyt1 = this.tags.mpyt1Path.selectAll('text').data(mpys); - mpyt1.exit().attr('class', 'exit').remove(); - const mpyt2 = this.tags.mpyt2Path.selectAll('text').data(mpys); - mpyt2.exit().attr('class', 'exit').remove(); - let mPeaks = mpys.map(m => { - const { - peaks, - xExtent - } = m; - return peaks.map(p => Object.assign({}, p, { - xExtent - })); - }); - mPeaks = [].concat(...mPeaks); - const mpyp = this.tags.mpypPath.selectAll('path').data(mPeaks); - mpyp.exit().attr('class', 'exit').remove(); - return; - } - const { - stack = [], - smExtext = false, - shift = 0 - } = selectedMulti || {}; - const hasValidExtent = extent => extent && Number.isFinite(extent.xL) && Number.isFinite(extent.xU); - const mpys = stack.filter(m => hasValidExtent(m?.xExtent)); const isDisable = _cfg.default.btnCmdMpy(this.layout); + const hasMpy = !isDisable && selectedMulti?.stack?.length > 0; + const mpys = hasMpy ? selectedMulti.stack : []; + const smExtext = hasMpy ? selectedMulti.smExtext : false; + const shift = hasMpy ? selectedMulti.shift : 0; + const hasValidExtent = extent => extent && Number.isFinite(extent.xL) && Number.isFinite(extent.xU); if (mpys.length === 0 || isDisable) return; const activeExtent = hasValidExtent(smExtext) ? smExtext : mpys[0].xExtent; // rescale for zoom @@ -893,6 +884,7 @@ class MultiFocus { isUiVisualSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, + shiftSt, integrationSt, mtplySt, uiSt @@ -911,7 +903,7 @@ class MultiFocus { this.root = d3.select(this.rootKlass).selectAll('.focus-main'); this.scales = (0, _init.InitScale)(this, this.reverseXAxis(layoutSt)); this.setTip(); - this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, layoutSt, cyclicvoltaSt, jcampIdx); + this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, layoutSt, cyclicvoltaSt, shiftSt, jcampIdx); Object.assign(this, { isUiSplitIntgSt, isUiVisualSplitIntgSt @@ -956,6 +948,7 @@ class MultiFocus { isUiVisualSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, + shiftSt, integrationSt, mtplySt, uiSt @@ -971,7 +964,7 @@ class MultiFocus { const jcampIdx = curveIdx; this.isShowAllCurves = isShowAllCurve; this.entities = entities; - this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, layoutSt, cyclicvoltaSt, jcampIdx); + this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, layoutSt, cyclicvoltaSt, shiftSt, jcampIdx); Object.assign(this, { isUiSplitIntgSt, isUiVisualSplitIntgSt diff --git a/dist/components/panel/graph_selection.js b/dist/components/panel/graph_selection.js index ad38202d..7165b25e 100644 --- a/dist/components/panel/graph_selection.js +++ b/dist/components/panel/graph_selection.js @@ -25,31 +25,63 @@ const styles = () => ({ backgroundColor: '#eee', height: 22 }, - curve: { - width: '100%' - }, - line: { - height: '2px', - borderWidth: '0', - margin: '0' + curveItem: { + display: 'flex', + alignItems: 'center', + width: '100%', + fontSize: '0.8em', + padding: '6px 8px', + margin: 0, + cursor: 'pointer' }, curveDefault: { - backgroundColor: '#fff', - fontSize: '0.8em', - margin: '0', - padding: '10px 2px 2px 10px', - maxWidth: '95%', - overflowWrap: 'anywhere' + backgroundColor: '#fff' }, curveSelected: { - backgroundColor: '#2196f3', - fontSize: '0.8em', - color: '#fff', - padding: '10px 2px 2px 10px', - maxWidth: '95%', + backgroundColor: '#e0e0e0' + }, + curveIndex: { + flexShrink: 0, + marginRight: '4px', + fontStyle: 'italic' + }, + curveLabel: { + flexShrink: 0, + marginRight: '8px', overflowWrap: 'anywhere' + }, + colorBar: { + flex: 1, + height: '6px', + borderRadius: '1px', + minWidth: '24px', + alignSelf: 'center' } }); +const fallbackName = (entityFileNames, idx) => { + if (entityFileNames && idx < entityFileNames.length) { + return entityFileNames[idx]; + } + return ''; +}; +const displayName = (spectra, idx, entityFileNames) => spectra?.title || spectra?.feature?.title || spectra?.spectrum?.title || fallbackName(entityFileNames, idx) || `Spectrum ${idx + 1}`; +const renderCurveItem = (classes, item, curveIdx, onChange) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.ListItem, { + disablePadding: true, + onClick: () => onChange(item.idx), + className: (0, _classnames.default)(classes.curveItem, item.idx === curveIdx ? classes.curveSelected : classes.curveDefault), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: classes.curveIndex, + children: item.name + }), item.label !== '' ? /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: classes.curveLabel, + children: item.label + }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: classes.colorBar, + style: { + backgroundColor: item.color + } + })] +}, item.idx); const GraphSelectionPanel = ({ classes, curveSt, @@ -57,7 +89,9 @@ const GraphSelectionPanel = ({ subLayoutsInfo, layoutSt, selectCurveAct, - toggleShowAllCurveAct + toggleShowAllCurveAct, + expand, + onExapnd }) => { let subLayoutValues = []; if (subLayoutsInfo) { @@ -92,40 +126,38 @@ const GraphSelectionPanel = ({ let itemsSubLayout = []; if (selectedSubLayout && subLayoutValues.length > 1) { const subLayout = subLayoutsInfo?.[resolvedSelectedSubLayout]; - itemsSubLayout = Array.isArray(subLayout) ? subLayout.map((spectra, idx) => { - const spectraIdx = spectra.curveIdx; - const { - color - } = spectra; - let filename = ''; - if (entityFileNames && spectraIdx < entityFileNames.length) { - filename = entityFileNames[spectraIdx]; - } - return { - name: `${idx + 1}.`, - idx: spectraIdx, - color, - filename - }; - }) : []; + try { + itemsSubLayout = Array.isArray(subLayout) ? subLayout.map((spectra, idx) => { + const spectraIdx = spectra.curveIdx; + const { + color + } = spectra; + return { + name: `${idx + 1}.`, + idx: spectraIdx, + color, + label: displayName(spectra, spectraIdx, entityFileNames) + }; + }) : []; + } catch (e) { + console.log(e); //eslint-disable-line + } } const items = listCurves.map((spectra, idx) => { const { color } = spectra; - let filename = ''; - if (entityFileNames && idx < entityFileNames.length) { - filename = entityFileNames[idx]; - } return { name: `${idx + 1}.`, idx, color, - filename + label: displayName(spectra, idx, entityFileNames) }; }); return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Accordion, { "data-testid": "GraphSelectionPanel", + expanded: expand, + onChange: onExapnd, disableGutters: true, sx: { '&.MuiAccordion-root.Mui-expanded': { @@ -179,58 +211,10 @@ const GraphSelectionPanel = ({ }, subLayout); }) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.List, { - children: itemsSubLayout.map(item => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.ListItem, { - onClick: () => onChange(item.idx), - className: (0, _classnames.default)(item.idx === curveIdx ? classes.curveSelected : classes.curveDefault) // eslint-disable-line - , - children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { - className: (0, _classnames.default)(classes.curve), - children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("i", { - children: item.name - }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { - style: { - float: 'right', - width: '95%' - }, - children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("hr", { - className: (0, _classnames.default)(classes.line), - style: { - backgroundColor: item.color - } - }), item.filename !== '' ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { - children: ["File: ", item.filename] - }) : null // eslint-disable-line - ] - })] - }) - }, item.idx)) + children: itemsSubLayout.map(item => renderCurveItem(classes, item, curveIdx, onChange)) })] }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.List, { - children: items.map(item => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.ListItem, { - onClick: () => onChange(item.idx), - className: (0, _classnames.default)(item.idx === curveIdx ? classes.curveSelected : classes.curveDefault) // eslint-disable-line - , - children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { - className: (0, _classnames.default)(classes.curve), - children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("i", { - children: item.name - }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { - style: { - float: 'right', - width: '95%' - }, - children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("hr", { - className: (0, _classnames.default)(classes.line), - style: { - backgroundColor: item.color - } - }), item.filename !== '' ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { - children: ["File: ", item.filename] - }) : null // eslint-disable-line - ] - })] - }) - }, item.idx)) + children: items.map(item => renderCurveItem(classes, item, curveIdx, onChange)) })] }); }; diff --git a/dist/components/panel/index.js b/dist/components/panel/index.js index 80bc732a..9904b37e 100644 --- a/dist/components/panel/index.js +++ b/dist/components/panel/index.js @@ -48,7 +48,7 @@ class PanelViewer extends _react.default.Component { constructor(props) { super(props); this.state = { - expand: 'info' + expand: ['info', 'graph'] }; this.onToggleExpand = this.onToggleExpand.bind(this); this.handleDescriptionChanged = this.handleDescriptionChanged.bind(this); @@ -63,7 +63,7 @@ class PanelViewer extends _react.default.Component { const { expand } = this.state; - const nextExpand = input === expand ? '' : input; + const nextExpand = expand.includes(input) ? expand.filter(item => item !== input) : [...expand, input]; this.setState({ expand: nextExpand }); @@ -96,6 +96,7 @@ class PanelViewer extends _react.default.Component { const onExpandCompare = () => this.onToggleExpand('compare'); const onExpandCyclicVolta = () => this.onToggleExpand('cyclicvolta'); const onExpandGraphSelection = () => this.onToggleExpand('graph'); + const isExpanded = name => expand.includes(name); const { listCurves } = curveSt; @@ -110,15 +111,15 @@ class PanelViewer extends _react.default.Component { children: [hideGraphSelection ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_graph_selection.default, { jcampIdx: jcampIdx, entityFileNames: entityFileNames, - expand: expand === 'graph', - onExpand: onExpandGraphSelection, + expand: isExpanded('graph'), + onExapnd: onExpandGraphSelection, subLayoutsInfo: subLayoutsInfo }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_info.default, { entities: entities, feature: feature, integration: integration, editorOnly: editorOnly, - expand: expand === 'info', + expand: isExpanded('info'), molSvg: molSvg, exactMass: exactMass, onExpand: onExpandInfo, @@ -126,18 +127,18 @@ class PanelViewer extends _react.default.Component { canChangeDescription: canChangeDescription, onDescriptionChanged: this.handleDescriptionChanged }), _cfg.default.hidePanelPeak(layoutSt) ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_peaks.default, { - expand: expand === 'peak', + expand: isExpanded('peak'), onExapnd: onExpandPeak }), _cfg.default.hidePanelMpy(layoutSt) ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_multiplicity.default, { - expand: expand === 'mpy', + expand: isExpanded('mpy'), onExapnd: onExpandMpy - }), _cfg.default.hidePanelCompare(layoutSt) || curveCount > 1 ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_compare.default, { - expand: expand === 'compare', + }), _cfg.default.hidePanelCompare(layoutSt) || !listCurves || listCurves.length > 1 ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_compare.default, { + expand: isExpanded('compare'), onExapnd: onExpandCompare }), _cfg.default.hidePanelCyclicVolta(layoutSt) || hideCyclicVolta ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_cyclic_voltamery_data.default, { jcampIdx: jcampIdx, feature: feature, - expand: expand === 'cyclicvolta', + expand: isExpanded('cyclicvolta'), onExapnd: onExpandCyclicVolta, userManualLink: userManualLink ? userManualLink.cv : undefined })] diff --git a/dist/components/panel/info.js b/dist/components/panel/info.js index 7e3e96bf..72c13c62 100644 --- a/dist/components/panel/info.js +++ b/dist/components/panel/info.js @@ -165,44 +165,6 @@ const handleDescriptionChanged = (content, delta, source, editor, onDescriptionC if (!onDescriptionChanged) return; onDescriptionChanged(normalizeQuillValue(content), delta, source, editor); }; -const aucValue = (integration, hplcMsSt) => { - const values = []; - const stackIntegration = integration?.stack; - if (Array.isArray(stackIntegration)) { - let sumVal = 0.0; - stackIntegration.forEach(inte => { - if (inte.absoluteArea) { - sumVal += inte.absoluteArea; - } - }); - sumVal = sumVal.toFixed(2); - stackIntegration.forEach(inte => { - if (inte.absoluteArea) { - const areaVal = inte.absoluteArea.toFixed(2); - const percent = (areaVal * 100 / sumVal).toFixed(2); - const valStr = areaVal + " (" + percent + "%)"; // eslint-disable-line - values.push(valStr); - } - }); - } - const spectraList = hplcMsSt?.uvvis?.spectraList || []; - const listWaveLength = hplcMsSt?.uvvis?.listWaveLength || []; - spectraList.forEach((spectrum, idx) => { - const wavelength = listWaveLength[idx]; - const integrations = spectrum?.integrations || []; - if (integrations.length > 0) { - const sumArea = integrations.reduce((sum, integ) => sum + (integ.absoluteArea ?? integ.area ?? 0), 0); - const integrationStrings = integrations.map(integ => { - const rawArea = integ.absoluteArea ?? integ.area ?? 0; - const areaVal = rawArea.toFixed(2); - const percent = sumArea > 0 ? (rawArea * 100 / sumArea).toFixed(2) : '0.00'; - return `${areaVal} (${percent}%)`; - }); - values.push(`[${wavelength} nm]: ${integrationStrings.join(', ')}`); - } - }); - return values.join('\n'); -}; const SECData = ({ classes, layout, @@ -482,7 +444,7 @@ const InfoPanel = ({ children: "Area under curve (AUC):" }), /*#__PURE__*/(0, _jsxRuntime.jsx)("br", {}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { className: (0, _classnames.default)(classes.tTxt, classes.tTxtSim, 'txt-sv-panel-txt'), - children: aucValue(integration) + children: _format.default.formatHplcAucPanel(integration, feature) })] }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(DSCData, { classes: classes, diff --git a/dist/helpers/chem.js b/dist/helpers/chem.js index c22b0c25..ee5399d0 100644 --- a/dist/helpers/chem.js +++ b/dist/helpers/chem.js @@ -15,6 +15,7 @@ exports.convertTopic = exports.convertThresEndPts = void 0; var _jcampconverter = _interopRequireDefault(require("jcampconverter")); var _reselect = require("reselect"); var _shift = require("./shift"); +var _cfg = _interopRequireDefault(require("./cfg")); var _format = _interopRequireDefault(require("./format")); var _list_layout = require("../constants/list_layout"); var _integration = require("./integration"); @@ -63,21 +64,7 @@ const getShiftOffset = (state, _) => { if (layout === _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY && cyclicvolta) { return GetCyclicVoltaShiftOffset(cyclicvolta, curveIdx); } - const { - shift - } = state; - const { - shifts - } = shift; - const selectedShift = shifts[curveIdx]; - if (!selectedShift) { - return 0.0; - } - const { - ref, - peak - } = selectedShift; - return (0, _shift.FromManualToOffset)(ref, peak); + return (0, _shift.shiftOffsetAtIndex)(state.shift, curveIdx); }; const calcXYK = (xs, ys, maxY, offset) => { const sp = []; diff --git a/dist/helpers/compass.js b/dist/helpers/compass.js index 00e85277..c82f6da0 100644 --- a/dist/helpers/compass.js +++ b/dist/helpers/compass.js @@ -300,7 +300,7 @@ const ClickCompass = (event, focus) => { } else if (isLcmsTicGraph) { focus.clickUiTargetAct(pt, false, false, jcampIdx, false, 'lcms_tic'); } else { - focus.clickUiTargetAct(pt, false); + focus.clickUiTargetAct(pt, false, 0, focus.jcampIdx); } }; exports.ClickCompass = ClickCompass; diff --git a/dist/helpers/extractPeaksEdit.js b/dist/helpers/extractPeaksEdit.js index c0d0433b..5537003a 100644 --- a/dist/helpers/extractPeaksEdit.js +++ b/dist/helpers/extractPeaksEdit.js @@ -31,24 +31,8 @@ var _integration = require("./integration"); var _submit = require("../features/lc-ms/submit"); /* eslint-disable max-len */ -const niOffset = (shiftSt, atIndex = 0) => { - const { - shifts - } = shiftSt; - const selectedShift = shifts[atIndex]; - if (!selectedShift) { - return 0; - } - const { - ref, - peak - } = selectedShift; - const offset = (0, _shift.FromManualToOffset)(ref, peak); - return offset; -}; -const msOffset = () => 0; const extractAutoPeaks = (feature, thresSt, shiftSt, layoutSt, atIndex = 0) => { - const offset = _format.default.isMsLayout(layoutSt) || _format.default.isLCMsLayout(layoutSt) ? msOffset() : niOffset(shiftSt, atIndex); + const offset = _format.default.isMsLayout(layoutSt) || _format.default.isLCMsLayout(layoutSt) ? 0 : (0, _shift.shiftOffsetAtIndex)(shiftSt, atIndex); const peaks = (0, _chem.Convert2Peak)(feature, thresSt.value, offset); return peaks; }; diff --git a/dist/helpers/format.js b/dist/helpers/format.js index 24e07a64..109c814b 100644 --- a/dist/helpers/format.js +++ b/dist/helpers/format.js @@ -40,7 +40,7 @@ const spectraDigit = layout => { } }; const fixDigit = (input, precision) => { - const output = input || 0.0; + const output = Number(input) || 0.0; return output.toFixed(precision); }; @@ -184,13 +184,15 @@ const spectraOps = { tail: '' } }; +const shiftAnchorX = selectedShift => { + if (!selectedShift) return null; + const x = selectedShift.ref?.value ?? selectedShift.peak?.x; + return x == null ? null : x; +}; const rmRef = (peaks, shift, atIndex = 0) => { - if (!shift) return peaks; - const { - shifts - } = shift; - const selectedShift = shifts[atIndex]; - const refValue = selectedShift.ref.value || selectedShift.peak.x; + if (!shift?.shifts) return peaks; + const refValue = shiftAnchorX(shift.shifts[atIndex]); + if (refValue == null) return peaks; return peaks.map(p => (0, _converter.IsSame)(p.x, refValue) ? null : p).filter(r => r != null); }; const isValidLcmsWavelengthLabel = wl => Number.isFinite(wl) && wl > 0; @@ -452,35 +454,90 @@ const formatedDLSIntensity = (peaks, maxY, decimal = 2, isAscend = true, isInten })); return ordered.map(o => `${o.x} nm (${o.y} %)`).join(', '); }; -const formatedHplcUvVis = (peaks, decimal = 2, integration) => { +const HPLC_RT_DECIMAL = 2; +const HPLC_VALUE_DECIMAL = 2; +const HPLC_RT_UNIT = 'min'; +const formatHplcRetentionTime = rt => `${fixDigit(rt, HPLC_RT_DECIMAL)} ${HPLC_RT_UNIT}`; +const formatHplcAuc = area => fixDigit(area, HPLC_VALUE_DECIMAL); +const findHplcPeakRetentionTime = (feature, xL, xU) => { + if (!feature?.data?.[0]) { + return (xL + xU) / 2; + } + const { + x, + y + } = feature.data[0]; + let maxY = -Infinity; + let rt = (xL + xU) / 2; + for (let i = 0; i < x.length; i += 1) { + if (x[i] >= xL && x[i] <= xU && y[i] > maxY) { + maxY = y[i]; + rt = x[i]; + } + } + return rt; +}; +const isSameHplcRt = (a, b) => fixDigit(a, HPLC_RT_DECIMAL) === fixDigit(b, HPLC_RT_DECIMAL); +const integrationApexRt = (inte, feature, peaks) => { + if (feature?.data?.[0]) { + return findHplcPeakRetentionTime(feature, inte.xL, inte.xU); + } + if (!Array.isArray(peaks)) return null; + const peaksInRange = peaks.filter(p => p.x >= inte.xL && p.x <= inte.xU); + if (peaksInRange.length === 0) return null; + return peaksInRange.reduce((best, p) => p.y > best.y ? p : best, peaksInRange[0]).x; +}; +const findIntegrationForPeak = (peakX, stack, peaks, feature) => { + if (!Array.isArray(stack)) return null; + const x = parseFloat(peakX); + return stack.find(s => { + if (s.xL > x || x > s.xU) return false; + const apexRt = integrationApexRt(s, feature, peaks); + return apexRt != null && isSameHplcRt(x, apexRt); + }) || null; +}; +const formatHplcAucPanel = (integration, feature) => { + if (!integration) return ''; + const stackIntegration = integration.stack; + if (!Array.isArray(stackIntegration)) return ''; + const entries = stackIntegration.filter(inte => inte.absoluteArea).map(inte => ({ + rt: findHplcPeakRetentionTime(feature, inte.xL, inte.xU), + area: inte.absoluteArea + })); + if (entries.length === 0) return ''; + const sumVal = entries.reduce((sum, e) => sum + e.area, 0); + return entries.map(e => { + const areaVal = formatHplcAuc(e.area); + const percent = fixDigit(e.area * 100 / sumVal, HPLC_VALUE_DECIMAL); + return `${formatHplcRetentionTime(e.rt)}, AUC=${areaVal} (${percent}%)`; + }).join(', '); +}; +const formatedHplcUvVis = (peaks, decimal = 2, integration, feature, isAscend = false) => { let stack = []; if (integration) { stack = integration.stack; } - let ordered = {}; + let bestByX = {}; peaks.forEach(p => { - const x = fixDigit(p.x, decimal); - const better = !ordered[x] || p.y > ordered[x]; + const x = Number(p.x); + const better = bestByX[x] === undefined || p.y > bestByX[x]; if (better) { - ordered = Object.assign({}, ordered, { + bestByX = Object.assign({}, bestByX, { [x]: p.y }); } }); - ordered = Object.keys(ordered).map(k => ({ - x: k, - y: ordered[k] - })); + const sortFunc = isAscend ? (a, b) => a.x - b.x : (a, b) => b.x - a.x; + const ordered = Object.keys(bestByX).map(k => ({ + x: parseFloat(k), + y: bestByX[k] + })).sort(sortFunc); const arrResult = []; ordered.forEach(o => { - let pStr = `${o.x} (${o.y.toFixed(2)})`; - if (stack) { - stack.forEach(s => { - if (s.xL <= o.x && s.xU >= o.x) { - pStr = `${o.x} (${o.y.toFixed(2)}, AUC=${s.absoluteArea})`; - } - }); - } + const rt = formatHplcRetentionTime(o.x); + const yVal = o.y.toFixed(HPLC_VALUE_DECIMAL); + const inte = findIntegrationForPeak(o.x, stack, peaks, feature); + const pStr = inte?.absoluteArea ? `${rt} (${yVal}, AUC=${formatHplcAuc(inte.absoluteArea)})` : `${rt} (${yVal})`; arrResult.push(pStr); }); return arrResult.join(', '); @@ -509,15 +566,9 @@ const formatedXRD = (peaks, isAscend = true, waveLength, temperature) => { }; const rmShiftFromPeaks = (peaks, shift, atIndex = 0) => { const peaksXY = (0, _converter.ToXY)(peaks); - const { - shifts - } = shift; - const selectedShift = shifts[atIndex]; - if (!selectedShift) { - return peaks; - } - // const digit = spectraDigit(layout); - const rmShiftX = selectedShift.ref.value || selectedShift.peak.x; + if (!shift?.shifts) return peaks; + const rmShiftX = shiftAnchorX(shift.shifts[atIndex]); + if (rmShiftX == null) return peaks; const result = peaksXY.map(p => { const srcX = parseFloat(p[0]); const x = (0, _converter.IsSame)(srcX, rmShiftX) ? null : srcX; @@ -542,7 +593,8 @@ const peaksBody = ({ atIndex = 0, waveLength, temperature, - hplcMsSt = null + hplcMsSt = null, + feature }) => { const result = rmShiftFromPeaks(peaks, shift, atIndex); const ascendFunc = (a, b) => parseFloat(a.x) - parseFloat(b.x); @@ -563,7 +615,7 @@ const peaksBody = ({ return formatedUvVis(ordered, maxY, decimal, isAscend, isIntensity, boundary, false); } if (layout === _list_layout.LIST_LAYOUT.HPLC_UVVIS) { - return formatedHplcUvVis(ordered, decimal, integration); + return formatedHplcUvVis(ordered, decimal, integration, feature, isAscend); } if (layout === _list_layout.LIST_LAYOUT.EMISSIONS) { return formatedEmissions(ordered, maxY, decimal, isAscend, isIntensity, boundary, false); @@ -581,11 +633,8 @@ const peaksBody = ({ }; const peaksWrapper = (layout, shift, atIndex = 0) => { let solvTxt = ''; - const { - shifts - } = shift; - const selectedShift = shifts[atIndex]; - if (selectedShift.ref.label) { + const selectedShift = shift?.shifts ? shift.shifts[atIndex] : shift; + if (selectedShift?.ref?.label) { solvTxt = ` (${selectedShift.ref.label})`; } if (layout === _list_layout.LIST_LAYOUT.PLAIN || layout === _list_layout.LIST_LAYOUT.DLS_ACF) { @@ -836,6 +885,8 @@ const Format = { formatedXRD, strNumberFixedLength, inlineNotation, - formatedLCMS + formatedLCMS, + formatHplcAucPanel, + formatedHplcUvVis }; var _default = exports.default = Format; \ No newline at end of file diff --git a/dist/helpers/shift.js b/dist/helpers/shift.js index 917f161e..5051a840 100644 --- a/dist/helpers/shift.js +++ b/dist/helpers/shift.js @@ -3,11 +3,35 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.VirtalPts = exports.RealPts = exports.FromManualToOffset = exports.CalcResidualX = void 0; +exports.shiftOffsetAtIndex = exports.shiftEntryAtIndex = exports.normalizeShiftForFormatting = exports.listEntryAtIndex = exports.defaultEmptyShift = exports.VirtalPts = exports.RealPts = exports.FromManualToOffset = exports.CalcResidualX = void 0; var _list_shift = require("../constants/list_shift"); /* eslint-disable prefer-object-spread, default-param-last */ -const shiftNone = _list_shift.LIST_SHIFT_13C[0]; +const shiftNone = _list_shift.LIST_SHIFT_1H[0]; +const defaultEmptyShift = exports.defaultEmptyShift = { + ref: shiftNone, + peak: false, + enable: true +}; +const listEntryAtIndex = (list, index, fallback) => Array.isArray(list) && list[index] !== undefined ? list[index] : fallback; +exports.listEntryAtIndex = listEntryAtIndex; +const shiftEntryAtIndex = (shiftSt, atIndex = 0) => listEntryAtIndex(shiftSt?.shifts, atIndex, defaultEmptyShift); +exports.shiftEntryAtIndex = shiftEntryAtIndex; +const normalizeShiftForFormatting = (shift, atIndex = 0) => { + if (shift?.shifts) { + return { + shift, + atIndex + }; + } + return { + shift: { + shifts: [shift || defaultEmptyShift] + }, + atIndex: 0 + }; +}; +exports.normalizeShiftForFormatting = normalizeShiftForFormatting; const FromManualToOffset = (ref, peak) => { if (!peak || ref.name === shiftNone.name) return 0; const offset = peak.x - ref.value; @@ -32,4 +56,12 @@ const RealPts = (pts, resX) => pts.map(pt => Object.assign({ x: pt.x - resX, y: pt.y })); -exports.RealPts = RealPts; \ No newline at end of file +exports.RealPts = RealPts; +const shiftOffsetAtIndex = (shiftSt, atIndex = 0) => { + const { + ref, + peak + } = shiftEntryAtIndex(shiftSt, atIndex); + return FromManualToOffset(ref, peak); +}; +exports.shiftOffsetAtIndex = shiftOffsetAtIndex; \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index ac438caa..97acc0d8 100644 --- a/dist/index.js +++ b/dist/index.js @@ -8,6 +8,7 @@ var _reactQuill = _interopRequireDefault(require("react-quill")); var _app = require("./app"); var _extractEntityLCMS = require("./helpers/extractEntityLCMS"); var _utils = require("./reducers/reducer_hplc_ms/utils"); +var _shift = require("./helpers/shift"); var _nmr1h_jcamp = _interopRequireDefault(require("./__tests__/fixtures/nmr1h_jcamp")); var _nmr1h_2_jcamp = _interopRequireDefault(require("./__tests__/fixtures/nmr1h_2_jcamp")); var _nmr13c_dept_jcamp = _interopRequireDefault(require("./__tests__/fixtures/nmr13c_dept_jcamp")); @@ -68,17 +69,6 @@ const pickSelectedSpectrumFromPayload = payload => { const selectedIdx = Number.isFinite(payload?.curveSt?.curveIdx) ? payload.curveSt.curveIdx : 0; return spectraList[selectedIdx] || spectraList[0] || {}; }; -const normalizeShiftForFormatting = shift => { - if (shift && Array.isArray(shift.shifts)) return shift; - return { - selectedIdx: 0, - shifts: [shift || { - ref: {}, - peak: false, - enable: true - }] - }; -}; const nmr1HEntity = _app.FN.ExtractJcamp(_nmr1h_jcamp.default); const nmr1HEntity2 = _app.FN.ExtractJcamp(_nmr1h_2_jcamp.default); const nmr13CEntity = _app.FN.ExtractJcamp(_nmr13c_jcamp.default); @@ -549,7 +539,7 @@ class DemoWriteIr extends _react.default.Component { maxY, minY }; - const shiftForFormatting = normalizeShiftForFormatting(shift); + const shiftForFormatting = (0, _shift.normalizeShiftForFormatting)(shift); const body = _app.FN.peaksBody({ peaks, layout: safeLayout, @@ -748,7 +738,7 @@ class DemoWriteIr extends _react.default.Component { maxY, minY }; - const shiftForFormatting = normalizeShiftForFormatting(shift); + const shiftForFormatting = (0, _shift.normalizeShiftForFormatting)(shift); const body = _app.FN.peaksBody({ peaks, layout: safeLayout, diff --git a/dist/layer_init.js b/dist/layer_init.js index 17fd8df5..6a1ee548 100644 --- a/dist/layer_init.js +++ b/dist/layer_init.js @@ -22,9 +22,14 @@ var _multi_jcamps_viewer = _interopRequireDefault(require("./components/multi_jc var _hplc_viewer = _interopRequireDefault(require("./components/hplc_viewer")); var _curve = require("./actions/curve"); var _hplc_ms = require("./actions/hplc_ms"); +var _ui = require("./actions/ui"); var _jsxRuntime = require("react/jsx-runtime"); /* eslint-disable prefer-object-spread, default-param-last */ +const hasActiveZoom = sweepExtent => { + if (!sweepExtent) return false; + return !!(sweepExtent.xExtent || sweepExtent.yExtent); +}; const styles = () => ({}); class LayerInit extends _react.default.Component { static entitySignature(e) { @@ -41,6 +46,20 @@ class LayerInit extends _react.default.Component { } constructor(props) { super(props); + const { + sweepExtent, + restoreSweepExtentAct + } = props; + if (hasActiveZoom(sweepExtent)) { + restoreSweepExtentAct(sweepExtent); + } else { + // Clear any zoom left in the shared store by a previously opened + // spectrum, so it cannot block the RESETALL/layout sync on mount. + restoreSweepExtentAct({ + xExtent: false, + yExtent: false + }); + } this.normChange = this.normChange.bind(this); this.execReset = this.execReset.bind(this); this.initReducer = this.initReducer.bind(this); @@ -51,7 +70,7 @@ class LayerInit extends _react.default.Component { this.execReset(); this.initReducer(); this.updateOthers(); - this.updateMultiEntities(); + this.updateMultiEntities(true); } componentDidUpdate(prevProps) { const { @@ -68,7 +87,7 @@ class LayerInit extends _react.default.Component { this.updateOthers(); } if (prevProps.multiEntities !== multiEntities || prevProps.entity !== entity) { - this.updateMultiEntities(); + this.updateMultiEntities(false); } } normChange(prevProps) { @@ -95,14 +114,14 @@ class LayerInit extends _react.default.Component { const { entity, updateMetaPeaksAct, + updateLayoutAct, resetInitCommonAct, resetInitMsAct, resetInitNmrAct, resetInitCommonWithIntergationAct, resetDetectorAct, updateDSCMetaDataAct, - resetMultiplicityAct, - updateLayoutAct + resetMultiplicityAct } = this.props; if (!entity || !entity.layout) return; resetInitCommonAct(); @@ -111,9 +130,12 @@ class LayerInit extends _react.default.Component { layout, features = {} } = entity; + // Sync the layout deterministically on every spectrum open. Otherwise it + // only updates via the d3 RESETALL, which can be skipped (active zoom or + // same multi-comparison curve count), leaving a stale layout from the + // previously opened spectrum. updateLayoutAct(layout); if (_format.default.isMsLayout(layout)) { - // const { autoPeak, editPeak } = features; // TBD const autoPeak = features.autoPeak || features[0]; const editPeak = features.editPeak || features[0]; const baseFeat = editPeak || autoPeak; @@ -143,7 +165,8 @@ class LayerInit extends _react.default.Component { dscMetaData } = features; updateDSCMetaDataAct(dscMetaData); - } else { + } + if (!_format.default.isNmrLayout(layout)) { resetMultiplicityAct(); } } @@ -165,7 +188,19 @@ class LayerInit extends _react.default.Component { addOthersAct(others); } } - updateMultiEntities() { + buildSetAllCurvesPayload(entities, isInitial) { + const { + curveIdx + } = this.props; + if (isInitial && Number.isFinite(curveIdx)) { + return { + entities, + curveIdx + }; + } + return entities; + } + updateMultiEntities(isInitial = false) { const { multiEntities, setAllCurvesAct, @@ -196,17 +231,17 @@ class LayerInit extends _react.default.Component { const isMultiSpectra = Array.isArray(multiEntities) && multiEntities.length > 1; if (isMultiSpectra) { const meta = _format.default.isLCMsLayout(entity.layout) ? lcmsCurveMeta() : undefined; - setAllCurvesAct(multiEntities, meta); + setAllCurvesAct(this.buildSetAllCurvesPayload(multiEntities, isInitial), meta); return; } if (_format.default.isLCMsLayout(entity.layout)) { - const payload = Array.isArray(multiEntities) && multiEntities.length > 0 ? multiEntities : [entity]; - setAllCurvesAct(payload, lcmsCurveMeta()); + const entities = Array.isArray(multiEntities) && multiEntities.length > 0 ? multiEntities : [entity]; + setAllCurvesAct(this.buildSetAllCurvesPayload(entities, isInitial), lcmsCurveMeta()); return; } if (_format.default.isCyclicVoltaLayout(entity.layout)) { - const payload = Array.isArray(multiEntities) && multiEntities.length > 0 ? multiEntities : [entity]; - setAllCurvesAct(payload); + const entities = Array.isArray(multiEntities) && multiEntities.length > 0 ? multiEntities : [entity]; + setAllCurvesAct(this.buildSetAllCurvesPayload(entities, isInitial)); return; } setAllCurvesAct(false); @@ -320,14 +355,23 @@ const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ addOthersAct: _jcamp.addOthers, setAllCurvesAct: _curve.setAllCurves, updateDSCMetaDataAct: _meta.updateDSCMetaData, - clearHplcMsStateAct: _hplc_ms.clearHplcMsState + clearHplcMsStateAct: _hplc_ms.clearHplcMsState, + restoreSweepExtentAct: _ui.restoreSweepExtent }, dispatch); +LayerInit.defaultProps = { + multiEntities: undefined, + curveIdx: undefined, + sweepExtent: undefined +}; LayerInit.propTypes = { entity: _propTypes.default.object.isRequired, multiEntities: _propTypes.default.array, // eslint-disable-line + curveIdx: _propTypes.default.number, + sweepExtent: _propTypes.default.object, entityFileNames: _propTypes.default.array, // eslint-disable-line + restoreSweepExtentAct: _propTypes.default.func.isRequired, others: _propTypes.default.object.isRequired, cLabel: _propTypes.default.string.isRequired, xLabel: _propTypes.default.string.isRequired, diff --git a/dist/reducers/reducer_curve.js b/dist/reducers/reducer_curve.js index 50cf529c..8137fe4d 100644 --- a/dist/reducers/reducer_curve.js +++ b/dist/reducers/reducer_curve.js @@ -18,10 +18,40 @@ const initialState = { curveIdx: 0, isShowAllCurve: false }; +const normalizeSetAllCurvesPayload = payload => { + if (Array.isArray(payload)) { + return { + entities: payload, + curveIdx: undefined + }; + } + if (payload && Array.isArray(payload.entities)) { + return { + entities: payload.entities, + curveIdx: payload.curveIdx + }; + } + return { + entities: null, + curveIdx: undefined + }; +}; +const resolveCurveIdx = (entitiesLength, state, explicitIdx) => { + if (Number.isFinite(explicitIdx)) { + const maxIdx = Math.max(0, entitiesLength - 1); + return Math.min(Math.max(0, explicitIdx), maxIdx); + } + if (state.curveIdx >= 0 && state.curveIdx < entitiesLength) { + return state.curveIdx; + } + return 0; +}; const setAllCurves = (state, action) => { const { - payload - } = action; + entities: payloadEntities, + curveIdx: explicitIdx + } = normalizeSetAllCurvesPayload(action.payload); + const payload = payloadEntities ?? action.payload; if (!payload) return { ...state, curveIdx: 0, @@ -69,11 +99,10 @@ const setAllCurves = (state, action) => { spectra: spectra || entity.spectra }; }); - const maxIdx = entities.length - 1; - const safeCurveIdx = Math.min(state.curveIdx || 0, maxIdx); + const curveIdx = resolveCurveIdx(entities.length, state, explicitIdx); return { ...state, - curveIdx: safeCurveIdx, + curveIdx, listCurves: entities }; }; diff --git a/dist/reducers/reducer_edit_peak.js b/dist/reducers/reducer_edit_peak.js index 35d38bff..b563f3fe 100644 --- a/dist/reducers/reducer_edit_peak.js +++ b/dist/reducers/reducer_edit_peak.js @@ -216,6 +216,10 @@ const editPeakReducer = (state = initialState, action) => { return processShift(state, action); case _action_type.EDITPEAK.CLEAR_ALL: return clearAllPeaks(state, action); + case _action_type.CURVE.SELECT_WORKING_CURVE: + return Object.assign({}, state, { + selectedIdx: action.payload + }); case _action_type.MANAGER.RESETALL: return { selectedIdx: 0, diff --git a/dist/reducers/reducer_integration.js b/dist/reducers/reducer_integration.js index 1b606355..43c657ce 100644 --- a/dist/reducers/reducer_integration.js +++ b/dist/reducers/reducer_integration.js @@ -421,18 +421,25 @@ const setFkr = (state, action) => { }; const setShift = (state, action) => { const { - selectedIdx, integrations } = state; - const selectedIntegration = integrations[selectedIdx]; - const shift = action.payload.prevOffset; + const { + prevOffset, + curveIdx + } = action.payload; + const targetIdx = Number.isFinite(curveIdx) ? curveIdx : state.selectedIdx; + let selectedIntegration = integrations[targetIdx]; + if (selectedIntegration === false || selectedIntegration === undefined) { + selectedIntegration = defaultEmptyIntegration; + } const newIntegration = Object.assign({}, selectedIntegration, { - shift + shift: prevOffset }); const newArrIntegration = [...integrations]; - newArrIntegration[selectedIdx] = newIntegration; + newArrIntegration[targetIdx] = newIntegration; return Object.assign({}, state, { - integrations: newArrIntegration + integrations: newArrIntegration, + selectedIdx: targetIdx }); }; const resetAll = (state, action) => { @@ -481,6 +488,10 @@ const integrationReducer = (state = initialState, action) => { return clearAll(state, action); case _action_type.EDITPEAK.SHIFT: return setShift(state, action); + case _action_type.CURVE.SELECT_WORKING_CURVE: + return Object.assign({}, state, { + selectedIdx: action.payload + }); case _action_type.MANAGER.RESETALL: return state; default: diff --git a/dist/reducers/reducer_multiplicity.js b/dist/reducers/reducer_multiplicity.js index 651182e1..a203f2fa 100644 --- a/dist/reducers/reducer_multiplicity.js +++ b/dist/reducers/reducer_multiplicity.js @@ -26,19 +26,26 @@ const defaultEmptyMultiplicity = { edited: false }; const setShift = (state, action) => { - const shift = action.payload.prevOffset; const { - selectedIdx, + prevOffset, + curveIdx + } = action.payload; + const targetIdx = Number.isFinite(curveIdx) ? curveIdx : state.selectedIdx; + const { multiplicities } = state; - const selectedMulti = multiplicities[selectedIdx]; + let selectedMulti = multiplicities[targetIdx]; + if (selectedMulti === false || selectedMulti === undefined) { + selectedMulti = defaultEmptyMultiplicity; + } const newSelectedMulti = Object.assign({}, selectedMulti, { - shift + shift: prevOffset }); const newMultiplicities = [...multiplicities]; - newMultiplicities[selectedIdx] = newSelectedMulti; + newMultiplicities[targetIdx] = newSelectedMulti; return Object.assign({}, state, { - multiplicities: newMultiplicities + multiplicities: newMultiplicities, + selectedIdx: targetIdx }); }; const rmFromStack = (state, action) => { @@ -168,6 +175,10 @@ const multiplicityReducer = (state = initialState, action) => { switch (action.type) { case _action_type.EDITPEAK.SHIFT: return setShift(state, action); + case _action_type.CURVE.SELECT_WORKING_CURVE: + return Object.assign({}, state, { + selectedIdx: action.payload + }); case _action_type.INTEGRATION.RM_ONE: return rmFromStack(state, action); case _action_type.UI.SWEEP.SELECT_MULTIPLICITY_RDC: diff --git a/dist/reducers/reducer_shift.js b/dist/reducers/reducer_shift.js index 310ac7d8..81dd2645 100644 --- a/dist/reducers/reducer_shift.js +++ b/dist/reducers/reducer_shift.js @@ -26,11 +26,6 @@ const initialState = { enable: true }] }; -const defaultEmptyShift = { - ref: shiftNone, - peak: false, - enable: true -}; const resetRef = payload => { const { shift, @@ -71,22 +66,20 @@ const resetShift = (state, action) => { curveIdx, numberOfCurve } = curvesInfo; - const { - shifts - } = state; + const shifts = [...state.shifts]; let selectedShift = shifts[curveIdx]; if (selectedShift === false || selectedShift === undefined) { - selectedShift = defaultEmptyShift; + selectedShift = _shift.defaultEmptyShift; } if (isMultiCurve) { for (let idx = 0; idx < numberOfCurve; idx += 1) { const checkShift = shifts[idx]; if (!checkShift) { - shifts[idx] = defaultEmptyShift; + shifts[idx] = _shift.defaultEmptyShift; } } } - const newShift = Object.assign({}, defaultEmptyShift, { + const newShift = Object.assign({}, _shift.defaultEmptyShift, { ref: resetRef(payload), enable: resetEnable(payload) }); @@ -104,7 +97,7 @@ const updateShift = (state, action) => { } = state; let selectedShift = shifts[selectedIdx]; if (selectedShift === false || selectedShift === undefined) { - selectedShift = defaultEmptyShift; + selectedShift = _shift.defaultEmptyShift; } const newShift = Object.assign({}, selectedShift, { ref: false, @@ -129,7 +122,7 @@ const setRef = (state, action) => { } = state; let selectedShift = shifts[curveIdx]; if (selectedShift === false || selectedShift === undefined) { - selectedShift = defaultEmptyShift; + selectedShift = _shift.defaultEmptyShift; } const newShift = Object.assign({}, selectedShift, { ref: dataToSet, @@ -154,7 +147,7 @@ const setPeak = (state, action) => { } = state; let selectedShift = shifts[curveIdx]; if (selectedShift === false || selectedShift === undefined) { - selectedShift = defaultEmptyShift; + selectedShift = _shift.defaultEmptyShift; } const resX = (0, _shift.CalcResidualX)(selectedShift.ref, selectedShift.peak, dataToSet); const trueApex = (0, _shift.RealPts)([dataToSet], resX)[0]; @@ -178,7 +171,7 @@ const removePeak = (state, action) => { } = state; let selectedShift = shifts[selectedIdx]; if (selectedShift === false || selectedShift === undefined) { - selectedShift = defaultEmptyShift; + selectedShift = _shift.defaultEmptyShift; } const newShift = Object.assign({}, selectedShift, { peak: false, @@ -203,7 +196,7 @@ const addNegative = (state, action) => { } = state; let selectedShift = shifts[curveIdx]; if (selectedShift === false || selectedShift === undefined) { - selectedShift = defaultEmptyShift; + selectedShift = _shift.defaultEmptyShift; } const rmApex = selectedShift.peak.x === dataToAdd.x; if (!rmApex) { @@ -237,7 +230,11 @@ const shiftReducer = (state = initialState, action) => { return updateShift(initialState, action); case _action_type.MANAGER.RESETSHIFT: // case MANAGER.RESETALL: - return resetShift(initialState, action); + return resetShift(state, action); + case _action_type.CURVE.SELECT_WORKING_CURVE: + return Object.assign({}, state, { + selectedIdx: action.payload + }); default: return state; } diff --git a/dist/reducers/reducer_threshold.js b/dist/reducers/reducer_threshold.js index 7fc7f727..e60f7066 100644 --- a/dist/reducers/reducer_threshold.js +++ b/dist/reducers/reducer_threshold.js @@ -16,14 +16,12 @@ const initialState = { lower: false }] }; - -// const defaultThresHold = { -// isEdit: true, -// value: false, -// upper: false, -// lower: false, -// }; - +const defaultThresHold = { + isEdit: true, + value: false, + upper: false, + lower: false +}; const setThresHoldValue = (state, action) => { const { payload @@ -126,12 +124,16 @@ const setThresHoldLower = (state, action) => { list: newListThres }); }; -const setThresHoldIsEdit = state => { +const setThresHoldIsEdit = (state, action) => { + const { + payload + } = action; const { list, selectedIdx } = state; - const selectedThres = list[selectedIdx]; + const curveIdx = payload && payload.curveIdx !== undefined ? payload.curveIdx : selectedIdx; + const selectedThres = list[curveIdx] || defaultThresHold; const { isEdit } = selectedThres; @@ -139,9 +141,10 @@ const setThresHoldIsEdit = state => { isEdit: !isEdit }); const newListThres = [...list]; - newListThres[selectedIdx] = newSelectedThres; + newListThres[curveIdx] = newSelectedThres; return Object.assign({}, state, { - list: newListThres + list: newListThres, + selectedIdx: curveIdx }); }; const resetAll = (state, action) => { @@ -151,6 +154,21 @@ const resetAll = (state, action) => { const { list } = state; + if (payload && payload.curveIdx !== undefined) { + const { + curveIdx + } = payload; + const selectedThres = list[curveIdx] || defaultThresHold; + const newSelectedThres = Object.assign({}, selectedThres, { + value: payload.thresRef + }); + const newListThres = [...list]; + newListThres[curveIdx] = newSelectedThres; + return Object.assign({}, state, { + selectedIdx: curveIdx, + list: newListThres + }); + } const newList = list.map(item => ({ isEdit: item.isEdit, value: payload && payload.thresRef, @@ -201,6 +219,10 @@ const thresholdReducer = (state = initialState, action) => { switch (action.type) { case _action_type.CURVE.SET_ALL_CURVES: return setListThreshold(state, action); + case _action_type.CURVE.SELECT_WORKING_CURVE: + return Object.assign({}, state, { + selectedIdx: action.payload + }); case _action_type.THRESHOLD.UPDATE_VALUE: return setThresHoldValue(state, action); case _action_type.THRESHOLD.UPDATE_UPPER_VALUE: @@ -210,7 +232,7 @@ const thresholdReducer = (state = initialState, action) => { case _action_type.THRESHOLD.RESET_VALUE: return setThresHoldValue(state, action); case _action_type.THRESHOLD.TOGGLE_ISEDIT: - return setThresHoldIsEdit(state); + return setThresHoldIsEdit(state, action); case _action_type.MANAGER.RESET_INIT_COMMON: return resetInitCommon(state); case _action_type.MANAGER.RESETALL: diff --git a/dist/sagas/saga_manager.js b/dist/sagas/saga_manager.js index e87aec57..b4e04eaf 100644 --- a/dist/sagas/saga_manager.js +++ b/dist/sagas/saga_manager.js @@ -9,6 +9,13 @@ var _action_type = require("../constants/action_type"); const getLayout = state => state.layout; const getCurveSt = state => state.curve; const getIntegrationSt = state => state.integration.present; +const defaultEmptyIntegration = { + stack: [], + refArea: 1, + refFactor: 1, + shift: 0, + edited: false +}; function* resetShift(action) { const curveSt = yield (0, _effects.select)(getCurveSt); const layout = yield (0, _effects.select)(getLayout); @@ -19,7 +26,7 @@ function* resetShift(action) { curveIdx, listCurves } = curveSt; - const numberOfCurve = listCurves.length; + const numberOfCurve = Array.isArray(listCurves) ? listCurves.length : 0; yield (0, _effects.put)({ type: _action_type.MANAGER.RESETSHIFT, payload: Object.assign( @@ -44,22 +51,19 @@ function* resetInitNmr(action) { integration, simulation } = action.payload; - const { - integrations - } = integationSt; - const newArrIntegration = [...integrations]; - newArrIntegration[curveIdx] = integration; + // Always reset: keeping the previous spectrum's integrations would + // display stale data when the new entity has none saved. + const newArrIntegration = new Array(curveIdx + 1).fill(defaultEmptyIntegration); + newArrIntegration[curveIdx] = integration || defaultEmptyIntegration; const payload = Object.assign({}, integationSt, { integrations: newArrIntegration, selectedIdx: curveIdx }); // eslint-disable-line - if (integration) { - yield (0, _effects.put)({ - type: _action_type.INTEGRATION.RESET_ALL_RDC, - payload - }); - } + yield (0, _effects.put)({ + type: _action_type.INTEGRATION.RESET_ALL_RDC, + payload + }); if (simulation) { yield (0, _effects.put)({ type: _action_type.SIMULATION.RESET_ALL_RDC, @@ -76,22 +80,17 @@ function* resetInitCommonWithIntergation(action) { const { integration } = action.payload; - const { - integrations - } = integationSt; - const newArrIntegration = [...integrations]; - newArrIntegration[curveIdx] = integration; + const newArrIntegration = new Array(curveIdx + 1).fill(defaultEmptyIntegration); + newArrIntegration[curveIdx] = integration || defaultEmptyIntegration; const payload = Object.assign({}, integationSt, { integrations: newArrIntegration, selectedIdx: curveIdx }); // eslint-disable-line - if (integration) { - yield (0, _effects.put)({ - type: _action_type.INTEGRATION.RESET_ALL_RDC, - payload - }); - } + yield (0, _effects.put)({ + type: _action_type.INTEGRATION.RESET_ALL_RDC, + payload + }); } const managerSagas = [(0, _effects.takeEvery)(_action_type.MANAGER.RESETALL, resetShift), (0, _effects.takeEvery)(_action_type.MANAGER.RESET_INIT_NMR, resetInitNmr), (0, _effects.takeEvery)(_action_type.MANAGER.RESET_INIT_COMMON_WITH_INTERGATION, resetInitCommonWithIntergation)]; var _default = exports.default = managerSagas; \ No newline at end of file diff --git a/dist/sagas/saga_multi_entities.js b/dist/sagas/saga_multi_entities.js index ee648abd..16c89a4c 100644 --- a/dist/sagas/saga_multi_entities.js +++ b/dist/sagas/saga_multi_entities.js @@ -167,6 +167,36 @@ function* setCyclicVoltametryRef(action) { } }); } +function* setInitShifts() { + const curveSt = yield (0, _effects.select)(getCurveSt); + const { + listCurves + } = curveSt; + if (!listCurves || listCurves.length <= 1) { + return; + } + const layoutSt = yield (0, _effects.select)(getLayoutSt); + const numberOfCurve = listCurves.length; + for (let index = 0; index < listCurves.length; index += 1) { + const { + feature + } = listCurves[index]; + if (feature) { + yield (0, _effects.put)({ + type: _action_type.MANAGER.RESETSHIFT, + payload: { + ...feature, + layout: layoutSt, + curvesInfo: { + isMultiCurve: true, + curveIdx: index, + numberOfCurve + } + } + }); + } + } +} function* setInitIntegrations(action) { // eslint-disable-line const curveSt = yield (0, _effects.select)(getCurveSt); @@ -230,5 +260,5 @@ function* setInitIntegrations(action) { } } } -const multiEntitiesSagas = [(0, _effects.takeEvery)(_action_type.CURVE.SET_ALL_CURVES, setCyclicVoltametry), (0, _effects.takeEvery)(_action_type.CURVE.SET_ALL_CURVES, setInitIntegrations), (0, _effects.takeEvery)(_action_type.CYCLIC_VOLTA_METRY.SET_FACTOR, setCyclicVoltametryRef)]; +const multiEntitiesSagas = [(0, _effects.takeEvery)(_action_type.CURVE.SET_ALL_CURVES, setCyclicVoltametry), (0, _effects.takeEvery)(_action_type.CURVE.SET_ALL_CURVES, setInitShifts), (0, _effects.takeEvery)(_action_type.CURVE.SET_ALL_CURVES, setInitIntegrations), (0, _effects.takeEvery)(_action_type.CYCLIC_VOLTA_METRY.SET_FACTOR, setCyclicVoltametryRef)]; var _default = exports.default = multiEntitiesSagas; \ No newline at end of file diff --git a/dist/sagas/saga_multiplicity.js b/dist/sagas/saga_multiplicity.js index 56645954..86919b5b 100644 --- a/dist/sagas/saga_multiplicity.js +++ b/dist/sagas/saga_multiplicity.js @@ -11,6 +11,12 @@ var _multiplicity_manual = require("../helpers/multiplicity_manual"); const getMetaSt = state => state.meta; const getCurveSt = state => state.curve; const getMultiplicitySt = state => state.multiplicity.present; +const defaultEmptyMultiplicity = { + stack: [], + shift: 0, + smExtext: false, + edited: false +}; function* selectMpy(action) { const metaSt = yield (0, _effects.select)(getMetaSt); const mpySt = yield (0, _effects.select)(getMultiplicitySt); @@ -247,31 +253,19 @@ function* resetInitNmr(action) { const { curveIdx } = curveSt; - const { - multiplicities - } = mpySt; - const newMultiplicities = [...multiplicities]; - newMultiplicities[curveIdx] = multiplicity; + // Always reset: keeping the previous spectrum's multiplicities would + // display stale data when the new entity has none saved. + const newMultiplicities = new Array(curveIdx + 1).fill(defaultEmptyMultiplicity); + newMultiplicities[curveIdx] = multiplicity || defaultEmptyMultiplicity; const payload = Object.assign({}, mpySt, { multiplicities: newMultiplicities, selectedIdx: curveIdx }); // eslint-disable-line - if (multiplicity) { - yield (0, _effects.put)({ - type: _action_type.MULTIPLICITY.RESET_ALL_RDC, - payload - }); - } - // const metaSt = yield select(getMetaSt); - // const mpySt = yield select(getMultiplicitySt); - - // if (!multiplicity) { - // yield put({ - // type: MULTIPLICITY.RESET_ALL_RDC, - // payload: mpySt, - // }); - // } + yield (0, _effects.put)({ + type: _action_type.MULTIPLICITY.RESET_ALL_RDC, + payload + }); // const { stack } = multiplicity; // const newStack = stack.map((k) => { diff --git a/dist/sagas/saga_ui.js b/dist/sagas/saga_ui.js index b321663e..6836bf83 100644 --- a/dist/sagas/saga_ui.js +++ b/dist/sagas/saga_ui.js @@ -218,13 +218,15 @@ function* clickUiTarget(action) { onPeak, voltammetryPeakIdx, onPecker, - sourceHint + sourceHint, + jcampIdx } = action; const uiSweepType = yield (0, _effects.select)(getUiSweepType); const curveState = yield (0, _effects.select)(getCurveState); const { curveIdx } = curveState; + const targetCurveIdx = Number.isFinite(jcampIdx) ? jcampIdx : curveIdx; const hplcMsState = yield (0, _effects.select)(getHplcMsState); const { uvvis @@ -243,11 +245,11 @@ function* clickUiTarget(action) { return; } if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.PEAK_ADD && !onPeak) { - if (isLcmsLayout) { - const spectrumId = hplcMsState?.uvvis?.selectedWaveLength; - if (spectrumId == null) return; - const currentPeaks = hplcMsState?.uvvis?.currentSpectrum?.peaks || []; - const updatedPeaks = [...currentPeaks, payload]; + const spectrumId = hplcMsState?.uvvis?.selectedWaveLength; + if (isLcmsLayout && spectrumId == null) return; + const currentPeaks = hplcMsState?.uvvis?.currentSpectrum?.peaks || []; + const updatedPeaks = [...currentPeaks, payload]; + if (isLcmsLayout && spectrumId != null) { yield (0, _effects.put)({ type: _action_type.HPLC_MS.UPDATE_HPLCMS_PEAKS, payload: { @@ -260,7 +262,7 @@ function* clickUiTarget(action) { type: _action_type.EDITPEAK.ADD_POSITIVE, payload: { dataToAdd: payload, - curveIdx + curveIdx: targetCurveIdx } }); } @@ -275,7 +277,7 @@ function* clickUiTarget(action) { type: _action_type.EDITPEAK.ADD_NEGATIVE, payload: { dataToAdd: payload, - curveIdx + curveIdx: targetCurveIdx } }); } @@ -284,7 +286,7 @@ function* clickUiTarget(action) { type: _action_type.SHIFT.SET_PEAK, payload: { dataToSet: payload, - curveIdx + curveIdx: targetCurveIdx } }); } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_RM && onPeak) { @@ -298,7 +300,7 @@ function* clickUiTarget(action) { type: _action_type.INTEGRATION.RM_ONE, payload: { dataToRemove: payload, - curveIdx + curveIdx: targetCurveIdx } }); } @@ -307,7 +309,7 @@ function* clickUiTarget(action) { type: _action_type.INTEGRATION.RM_ONE, payload: { dataToRemove: payload, - curveIdx + curveIdx: targetCurveIdx } }); } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_SET_REF && onPeak) { @@ -315,7 +317,7 @@ function* clickUiTarget(action) { type: _action_type.INTEGRATION.SET_REF, payload: { refData: payload, - curveIdx + curveIdx: targetCurveIdx } }); } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_ONE_CLICK && onPeak) { @@ -329,7 +331,7 @@ function* clickUiTarget(action) { type: _action_type.MULTIPLICITY.ONE_CLICK_BY_UI, payload: { payloadData: xExtent, - curveIdx + curveIdx: targetCurveIdx } }); } else if (xL && xU) { @@ -340,7 +342,7 @@ function* clickUiTarget(action) { xL, xU }, - curveIdx + curveIdx: targetCurveIdx } }); } @@ -360,7 +362,7 @@ function* clickUiTarget(action) { payload: { peak: payload, index: voltammetryPeakIdx, - jcampIdx: curveIdx + jcampIdx: targetCurveIdx } }); } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MAX_PEAK && onPeak) { @@ -368,7 +370,7 @@ function* clickUiTarget(action) { type: _action_type.CYCLIC_VOLTA_METRY.REMOVE_MAX_PEAK, payload: { index: voltammetryPeakIdx, - jcampIdx: curveIdx + jcampIdx: targetCurveIdx } }); } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MIN_PEAK && !onPeak) { @@ -377,7 +379,7 @@ function* clickUiTarget(action) { payload: { peak: payload, index: voltammetryPeakIdx, - jcampIdx: curveIdx + jcampIdx: targetCurveIdx } }); } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MIN_PEAK && onPeak) { @@ -385,7 +387,7 @@ function* clickUiTarget(action) { type: _action_type.CYCLIC_VOLTA_METRY.REMOVE_MIN_PEAK, payload: { index: voltammetryPeakIdx, - jcampIdx: curveIdx + jcampIdx: targetCurveIdx } }); } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_PECKER && !onPecker) { @@ -394,7 +396,7 @@ function* clickUiTarget(action) { payload: { peak: payload, index: voltammetryPeakIdx, - jcampIdx: curveIdx + jcampIdx: targetCurveIdx } }); } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_PECKER && onPecker) { @@ -402,7 +404,7 @@ function* clickUiTarget(action) { type: _action_type.CYCLIC_VOLTA_METRY.REMOVE_PECKER, payload: { index: voltammetryPeakIdx, - jcampIdx: curveIdx + jcampIdx: targetCurveIdx } }); } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_SET_REF && onPeak) { @@ -410,7 +412,7 @@ function* clickUiTarget(action) { type: _action_type.CYCLIC_VOLTA_METRY.SET_REF, payload: { index: voltammetryPeakIdx, - jcampIdx: curveIdx + jcampIdx: targetCurveIdx } }); } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.PEAK_GROUP_SELECT) { diff --git a/src/__tests__/units/components/cmd_bar/03_peak.test.js b/src/__tests__/units/components/cmd_bar/03_peak.test.js index 32463fa3..cc4bbdc1 100644 --- a/src/__tests__/units/components/cmd_bar/03_peak.test.js +++ b/src/__tests__/units/components/cmd_bar/03_peak.test.js @@ -9,7 +9,7 @@ import { LIST_LAYOUT } from '../../../../constants/list_layout'; const mockStore = configureStore([]); const store = mockStore({ ui:{ sweepType: LIST_UI_SWEEP_TYPE.ZOOMIN }, - layout: LIST_LAYOUT.MS, + layout: LIST_LAYOUT.IR, curve: { curveIdx: 0 }, editPeak: { present: { diff --git a/src/__tests__/units/components/cmd_bar/r05_submit_btn.test.js b/src/__tests__/units/components/cmd_bar/r05_submit_btn.test.js index 07fc359c..f0f3c5c9 100644 --- a/src/__tests__/units/components/cmd_bar/r05_submit_btn.test.js +++ b/src/__tests__/units/components/cmd_bar/r05_submit_btn.test.js @@ -5,6 +5,7 @@ import '@testing-library/jest-dom'; import BtnSubmit, { computeCvYScaleFactor } from '../../../../components/cmd_bar/r05_submit_btn'; import Format from '../../../../helpers/format'; +import { defaultEmptyShift } from '../../../../helpers/shift'; jest.mock('../../../../helpers/extractPeaksEdit', () => ({ extractPeaksEdit: () => [{ x: 1, y: 2 }], @@ -32,6 +33,7 @@ const buildBaseState = (overrides = {}) => ({ detector: {}, meta: { dscMetaData: {} }, hplcMs: {}, + ui: { sweepExtent: { xExtent: false, yExtent: false } }, ...overrides, }); @@ -92,6 +94,29 @@ describe('', () => { expect(payload.curveSt).toEqual({ curveIdx: 1 }); }); + it('sends a default shift entry when the curve index is missing', () => { + const operationValue = jest.fn(); + const state = buildBaseState({ + layout: 'HPLC_UVVIS', + shift: { shifts: [defaultEmptyShift] }, + curve: { + curveIdx: 1, + listCurves: [ + { feature: { xUnit: 'min', yUnit: 'mAU', scanAutoTarget: 1, thresRef: 3 } }, + { feature: { xUnit: 'min', yUnit: 'mAU', scanAutoTarget: 2, thresRef: 4 } }, + ], + }, + threshold: { list: [{ value: 10 }, { value: 20 }] }, + axesUnits: { axes: [{ xUnit: '', yUnit: '' }, { xUnit: '', yUnit: '' }] }, + }); + + renderBtnSubmit(state, operationValue); + + const { shift } = operationValue.mock.calls[0][0].spectra_list[1]; + expect(shift).toEqual(defaultEmptyShift); + expect(shift).not.toHaveProperty('shifts'); + }); + it('controls presence/absence of keepPred and simulatenmr', () => { const withFlagsCb = jest.fn(); const noFlagsCb = jest.fn(); diff --git a/src/__tests__/units/helpers/format.test.tsx b/src/__tests__/units/helpers/format.test.tsx index 7e3d0e1e..b5f618ce 100644 --- a/src/__tests__/units/helpers/format.test.tsx +++ b/src/__tests__/units/helpers/format.test.tsx @@ -139,7 +139,60 @@ describe('Test format helper', () => { it('Get peaks for HPLC layout', () => { params.layout = LIST_LAYOUT.HPLC_UVVIS const body = Format.peaksBody(params) - expect(body).toEqual('2.0 (2.00), 1.0 (1.00)') + expect(body).toEqual('2.00 min (2.00), 1.00 min (1.00)') + }) + + it('Get peaks for HPLC layout with integration', () => { + params.layout = LIST_LAYOUT.HPLC_UVVIS + params.integration = { + stack: [ + { xL: 1.5, xU: 2.5, absoluteArea: 1234.5678 }, + { xL: 0.5, xU: 1.5, absoluteArea: 987.6543 }, + ], + } + const body = Format.peaksBody(params) + expect(body).toEqual( + '2.00 min (2.00, AUC=1234.57), 1.00 min (1.00, AUC=987.65)', + ) + }) + + it('Does not assign AUC to peaks inside integration but not at apex', () => { + params.layout = LIST_LAYOUT.HPLC_UVVIS + params.isAscend = true + params.peaks = [ + { x: 16.99, y: 319.83 }, + { x: 14.05, y: 401.21 }, + { x: 13.71, y: -0.77 }, + ] + params.integration = { + stack: [ + { xL: 13.5, xU: 14.5, absoluteArea: 31467.89 }, + { xL: 16.5, xU: 17.5, absoluteArea: 31447.95 }, + ], + } + const body = Format.peaksBody(params) + expect(body).toEqual( + '13.71 min (-0.77), 14.05 min (401.21, AUC=31467.89), 16.99 min (319.83, AUC=31447.95)', + ) + }) + + it('Format HPLC AUC panel with retention time', () => { + const integration = { + stack: [ + { xL: 12.5, xU: 13.5, absoluteArea: 1000 }, + { xL: 15.5, xU: 16.5, absoluteArea: 2000 }, + ], + } + const feature = { + data: [{ + x: [12, 13, 14, 15, 16, 17], + y: [10, 50, 20, 15, 40, 10], + }], + } + const panel = Format.formatHplcAucPanel(integration, feature) + expect(panel).toEqual( + '13.00 min, AUC=1000.00 (33.33%), 16.00 min, AUC=2000.00 (66.67%)', + ) }) it('Get peaks for Emission layout', () => { @@ -277,6 +330,12 @@ describe('Test format helper', () => { const removedPeaks = Format.rmRef(peaks, shift) expect(removedPeaks).toEqual([{x: 2.04, y: 2}]) }) + + it('Returns peaks unchanged when shift index is missing', () => { + const shift = { shifts: [{ ref: LIST_SHIFT_1H[1] }] } + const removedPeaks = Format.rmRef(peaks, shift, 1) + expect(removedPeaks).toEqual(peaks) + }) }) }) diff --git a/src/__tests__/units/helpers/shift.test.tsx b/src/__tests__/units/helpers/shift.test.tsx index 47df37ad..38c2a304 100644 --- a/src/__tests__/units/helpers/shift.test.tsx +++ b/src/__tests__/units/helpers/shift.test.tsx @@ -1,4 +1,6 @@ -import { CalcResidualX, FromManualToOffset, RealPts, VirtalPts } from "../../../helpers/shift"; +import { + CalcResidualX, FromManualToOffset, RealPts, VirtalPts, shiftOffsetAtIndex, +} from "../../../helpers/shift"; import { LIST_SHIFT_1H, LIST_SHIFT_13C } from "../../../constants/list_shift"; describe('Test helper for shift', () => { @@ -65,6 +67,23 @@ describe('Test helper for shift', () => { }) }) + describe('Test shift offset at index', () => { + it('Returns zero when shift state is missing', () => { + expect(shiftOffsetAtIndex(null, 0)).toEqual(0) + }) + + it('Returns offset for the requested curve index', () => { + const shiftSt = { + shifts: [ + { ref: LIST_SHIFT_1H[0], peak: false }, + { ref: LIST_SHIFT_1H[1], peak: { x: 1.5, y: 2 } }, + ], + } + expect(shiftOffsetAtIndex(shiftSt, 0)).toEqual(0) + expect(shiftOffsetAtIndex(shiftSt, 1)).toEqual(-0.54) + }) + }) + describe('Test get real points', () => { it('No points', () => { const virtualPoints = RealPts([], 18.0) diff --git a/src/__tests__/units/reducers/reducer_curve.test.tsx b/src/__tests__/units/reducers/reducer_curve.test.tsx index 531d50eb..8cfd1b97 100644 --- a/src/__tests__/units/reducers/reducer_curve.test.tsx +++ b/src/__tests__/units/reducers/reducer_curve.test.tsx @@ -58,6 +58,24 @@ describe('Test redux curve reducer', () => { expect(maxminPeak).toBeNull() }) + it('Preserves the selected curve index when reloading all curves', () => { + action.type = CURVE.SET_ALL_CURVES + const entity = ExtractJcamp(nmr1HJcamp) + curveState = { listCurves: [], curveIdx: 2 } + action.payload = [ entity, entity, entity ] + const { curveIdx } = curveReducer(curveState, action) + expect(curveIdx).toEqual(2) + }) + + it('Uses an explicit curve index when provided', () => { + action.type = CURVE.SET_ALL_CURVES + const entity = ExtractJcamp(nmr1HJcamp) + curveState = { listCurves: [], curveIdx: 0 } + action.payload = { entities: [ entity, entity, entity ], curveIdx: 1 } + const { curveIdx } = curveReducer(curveState, action) + expect(curveIdx).toEqual(1) + }) + it('Toggle show all curves', () => { action.type = CURVE.SET_SHOULD_SHOW_ALL_CURVES action.payload = true diff --git a/src/__tests__/units/reducers/reducer_integration.test.tsx b/src/__tests__/units/reducers/reducer_integration.test.tsx index 602d30e0..62ad9bcd 100644 --- a/src/__tests__/units/reducers/reducer_integration.test.tsx +++ b/src/__tests__/units/reducers/reducer_integration.test.tsx @@ -838,3 +838,42 @@ describe('Test redux reducer for integrations', () => { expect(newState).toBe(state); }); }); + +import { EDITPEAK, CURVE } from '../../../constants/action_type'; + +describe('Test redux integration reducer (per-curve)', () => { + const baseIntegration = { + stack: [{ xL: 1, xU: 2, area: 1, absoluteArea: 1 }], + refArea: 1, + refFactor: 1, + shift: 0, + edited: false, + }; + + it('updates shift on the curve index from the payload', () => { + const state = { + selectedIdx: 0, + integrations: [baseIntegration, { ...baseIntegration, shift: 0 }], + }; + const action = { + type: EDITPEAK.SHIFT, + payload: { prevOffset: 0.42, curveIdx: 1 }, + }; + const nextState = integrationReducer(state, action); + expect(nextState.integrations[1].shift).toEqual(0.42); + expect(nextState.integrations[0].shift).toEqual(0); + expect(nextState.selectedIdx).toEqual(1); + }); + + it('syncs selectedIdx when the working curve changes', () => { + const state = { + selectedIdx: 0, + integrations: [baseIntegration], + }; + const nextState = integrationReducer(state, { + type: CURVE.SELECT_WORKING_CURVE, + payload: 2, + }); + expect(nextState.selectedIdx).toEqual(2); + }); +}); diff --git a/src/actions/threshold.js b/src/actions/threshold.js index 001a4cdb..0b3fc4a8 100644 --- a/src/actions/threshold.js +++ b/src/actions/threshold.js @@ -7,10 +7,10 @@ const updateThresholdValue = (payload) => ( } ); -const resetThresholdValue = () => ( +const resetThresholdValue = (payload = false) => ( { type: THRESHOLD.RESET_VALUE, - payload: false, + payload, } ); diff --git a/src/actions/ui.js b/src/actions/ui.js index f1c43479..75c111f3 100644 --- a/src/actions/ui.js +++ b/src/actions/ui.js @@ -48,22 +48,27 @@ const scrollUiWheel = (payload) => ( } ); -const clickUiTarget = ( - payload, - onPeak, - voltammetryPeakIdx = 0, - jcampIdx = 0, - onPecker = false, - sourceHint = null, -) => ({ - type: UI.CLICK_TARGET, - payload, - onPeak, - voltammetryPeakIdx, - jcampIdx, - onPecker, - sourceHint, -}); +const restoreSweepExtent = (payload) => ( + { + type: UI.SWEEP.SELECT_ZOOMIN, + payload, + } +); + +const clickUiTarget = (payload, onPeak, voltammetryPeakIdx, jcampIdx, onPecker, sourceHint) => { + const action = { + type: UI.CLICK_TARGET, + payload, + onPeak, + voltammetryPeakIdx: voltammetryPeakIdx ?? 0, + onPecker: onPecker ?? false, + sourceHint: sourceHint ?? null, + }; + if (Number.isFinite(jcampIdx)) { + action.jcampIdx = jcampIdx; + } + return action; +}; const displaySubViewerAt = (payload) => ({ type: UI.SUB_VIEWER.DISPLAY_VIEWER_AT, @@ -75,6 +80,7 @@ export { setUiSweepType, selectUiSweep, scrollUiWheel, + restoreSweepExtent, clickUiTarget, displaySubViewerAt, }; diff --git a/src/app.js b/src/app.js index 8017f46e..dba0eb8e 100644 --- a/src/app.js +++ b/src/app.js @@ -44,7 +44,7 @@ const SpectraEditor = ({ entity, others, cLabel, xLabel, yLabel, operations, forecast, molSvg, editorOnly, descriptions, exactMass, canChangeDescription, onDescriptionChanged, - multiEntities, multiMolSvgs, entityFileNames, userManualLink, + multiEntities, multiMolSvgs, entityFileNames, userManualLink, curveIdx, sweepExtent, onLcmsPageRequest, }) => ( @@ -52,6 +52,8 @@ const SpectraEditor = ({ { - hideCmdAnaViewerSt + (hideCmdAnaViewerSt || disableCmdAnaViewerSt) ? null : ( Analysis Viewer}> - - - - - + + + ) } diff --git a/src/components/cmd_bar/03_peak.js b/src/components/cmd_bar/03_peak.js index aa219869..921448f9 100644 --- a/src/components/cmd_bar/03_peak.js +++ b/src/components/cmd_bar/03_peak.js @@ -50,6 +50,15 @@ const Peak = ({ clearAllPeaksAct({ curveIdx, dataPeaks }); } }; + const showAddPeak = !disableAddPeakSt; + const showRmPeak = !disableRmPeakSt; + const showSetRef = !disableSetRefSt; + const showClearAll = !disableRmPeakSt; + + if (!showAddPeak && !showRmPeak && !showSetRef && !showClearAll) { + return null; + } + if (isHandleMaxAndMinPeaksSt) { const { spectraList } = cyclicVoltaState; const spectra = spectraList[curveIdx]; @@ -68,48 +77,58 @@ const Peak = ({ return ( - Add Peak}> - - - P+ - - - - Remove Peak}> - - Add Peak}> + + + P+ + + + + ) : null + } + { + showRmPeak ? ( + Remove Peak}> + + + P- + + + + ) : null + } + { + showClearAll ? ( + - P- - - - - - P - x - + P + x + + ) : null + } { - !disableSetRefSt ? ( + showSetRef ? ( Set Reference}> diff --git a/src/components/cmd_bar/04_integration.js b/src/components/cmd_bar/04_integration.js index 679a86da..8ae4f84c 100644 --- a/src/components/cmd_bar/04_integration.js +++ b/src/components/cmd_bar/04_integration.js @@ -55,7 +55,7 @@ const styles = () => ( const iconSize = '16px'; const setFactor = ( - classes, isDisable, integrationSt, setIntegrationFkrAct, curveIdx, + classes, integrationSt, setIntegrationFkrAct, curveIdx, ) => { const onFactorChanged = (e) => { e.target.blur(); @@ -83,7 +83,6 @@ const setFactor = ( return ( Add Integration}> @@ -154,13 +157,12 @@ const Integration = ({ 'btn-add-inter', ) } - disabled={isDisableSt} onClick={onSweepIntegtAdd} > { @@ -180,46 +182,38 @@ const Integration = ({ 'btn-remove-inter', ) } - disabled={isDisableSt} onClick={onSweepIntegtRm} > - - { - ignoreRef - ? null - : ( - Set Integration Reference}> - - - - - - - ) - } + Set Integration Reference}> + + + + + + { showIntegSplitToolsSt ? ( @@ -233,13 +227,12 @@ const Integration = ({ 'btn-split-inter', ) } - disabled={isDisableSt} onClick={onSweepIntegtSplit} > { @@ -259,13 +252,12 @@ const Integration = ({ 'btn-visual-split-inter', ) } - disabled={isDisableSt} onClick={onSweepIntegtVisualSplit} > { @@ -283,7 +275,7 @@ const Integration = ({ { !ignoreRef ? setFactor( - classes, isDisableSt, integrationSt, setIntegrationFkrAct, curveIdx, + classes, integrationSt, setIntegrationFkrAct, curveIdx, ) : null } @@ -294,7 +286,7 @@ const Integration = ({ x diff --git a/src/components/cmd_bar/05_multiplicity.js b/src/components/cmd_bar/05_multiplicity.js index fca449f7..f7a4a41f 100644 --- a/src/components/cmd_bar/05_multiplicity.js +++ b/src/components/cmd_bar/05_multiplicity.js @@ -27,8 +27,8 @@ const styles = () => ( ); const Multiplicity = ({ - classes, isFocusAddMpySt, disableAddMpySt, isFocusRmMpySt, disableRmMpySt, - isFocusAddPeakSt, isFocusRmPeakSt, disableMpyPeakSt, + classes, isFocusAddMpySt, disableAddMpySt, isFocusRmMpySt, + isFocusAddPeakSt, isFocusRmPeakSt, setUiSweepTypeAct, clearMpyAllAct, curveSt, }) => { const onSweepMutAdd = () => setUiSweepTypeAct(LIST_UI_SWEEP_TYPE.MULTIPLICITY_SWEEP_ADD); @@ -38,6 +38,10 @@ const Multiplicity = ({ const { curveIdx } = curveSt; const onClearAll = () => clearMpyAllAct({ curveIdx }); + if (disableAddMpySt) { + return null; + } + return ( Add Multiplicity}> @@ -49,7 +53,6 @@ const Multiplicity = ({ 'btn-sv-bar-addmpy', ) } - disabled={disableAddMpySt} onClick={onSweepMutAdd} > J+ @@ -65,7 +68,6 @@ const Multiplicity = ({ 'btn-sv-bar-rmmpy', ) } - disabled={disableRmMpySt} onClick={onOneMutAdd} > J- @@ -81,7 +83,6 @@ const Multiplicity = ({ 'btn-sv-bar-addpeakmpy', ) } - disabled={disableMpyPeakSt} onClick={onPeakMutAdd} > JP+ @@ -97,25 +98,19 @@ const Multiplicity = ({ 'btn-sv-bar-rmpeakmpy', ) } - disabled={disableMpyPeakSt} onClick={onPeakMutRm} > JP- - { - disableAddMpySt ? null - : // eslint-disable-line - ( - - Jx - - ) - } + + Jx + ); }; diff --git a/src/components/cmd_bar/07_pecker.js b/src/components/cmd_bar/07_pecker.js index 4b8cf4ee..3959911c 100644 --- a/src/components/cmd_bar/07_pecker.js +++ b/src/components/cmd_bar/07_pecker.js @@ -97,68 +97,69 @@ const Pecker = ({ hasRefPeaks = hasRefPeak; } + if (Cfg.hidePanelCyclicVolta(layoutSt)) { + return null; + } + return ( - (!Cfg.hidePanelCyclicVolta(layoutSt)) ? ( - - Add Pecker}> - - - - I - λ0 - + - - - - - Remove Pecker}> - - - - I - λ0 - - - - - - - { - setRef(classes, cyclicVotaSt, curveIdx, setCylicVoltaRefFactorAct) - } - {hasRefPeaks ? 'Set Reference' : 'Set Shift'}}> - - - - - - - - ) - : () + + Add Pecker}> + + + + I + λ0 + + + + + + + Remove Pecker}> + + + + I + λ0 + - + + + + + { + setRef(classes, cyclicVotaSt, curveIdx, setCylicVoltaRefFactorAct) + } + {hasRefPeaks ? 'Set Reference' : 'Set Shift'}}> + + + + + + + ); }; diff --git a/src/components/cmd_bar/index.js b/src/components/cmd_bar/index.js index 450c109d..f4d98ee0 100644 --- a/src/components/cmd_bar/index.js +++ b/src/components/cmd_bar/index.js @@ -23,6 +23,7 @@ import ChangeAxes from './r08_change_axes'; import Detector from './r09_detector'; import CvDensityControls from './r10_cv_density'; import Format from '../../helpers/format'; +import Cfg from '../../helpers/cfg'; const styles = () => ( Object.assign( @@ -71,6 +72,8 @@ const CmdBar = ({ prependLcMsToolbar, }) => { const isCvLayout = Format.isCyclicVoltaLayout(layoutSt); + const hideIntegration = isCvLayout || Cfg.btnCmdIntg(layoutSt); + const hideMultiplicity = isCvLayout || Cfg.btnCmdMpy(layoutSt); const rightCluster = ( <> @@ -117,8 +120,8 @@ const CmdBar = ({ - {isCvLayout ? null : } - {isCvLayout ? null : } + {hideIntegration ? null : } + {hideMultiplicity ? null : } ) diff --git a/src/components/cmd_bar/r03_threshold.js b/src/components/cmd_bar/r03_threshold.js index 77b49a19..bca9be2b 100644 --- a/src/components/cmd_bar/r03_threshold.js +++ b/src/components/cmd_bar/r03_threshold.js @@ -102,6 +102,9 @@ const Threshold = ({ } else { thresVal = thresValSt || (feature ? feature.thresRef : hplcMsSt?.threshold?.value); } + const { curveIdx } = curveSt; + const onResetThreshold = () => resetThresholdValueAct({ value: false, curveIdx }); + const onToggleThreshold = () => toggleThresholdIsEditAct({ curveIdx }); return ( @@ -115,7 +118,7 @@ const Threshold = ({ ) } disabled={Cfg.btnCmdThres(thresVal)} - onClick={resetThresholdValueAct} + onClick={onResetThreshold} > @@ -134,7 +137,7 @@ const Threshold = ({ ) } disabled={Cfg.btnCmdThres(thresVal)} - onClick={toggleThresholdIsEditAct} + onClick={onToggleThreshold} > { restoreIcon(classes, hasEdit, isEditSt) } diff --git a/src/components/cmd_bar/r05_submit_btn.js b/src/components/cmd_bar/r05_submit_btn.js index 428b031c..f864a10a 100644 --- a/src/components/cmd_bar/r05_submit_btn.js +++ b/src/components/cmd_bar/r05_submit_btn.js @@ -22,6 +22,7 @@ import { getLcmsMzPageData, } from '../../helpers/extractPeaksEdit'; import Format from '../../helpers/format'; +import { shiftEntryAtIndex, listEntryAtIndex } from '../../helpers/shift'; const styles = () => ( Object.assign( @@ -97,9 +98,20 @@ const resolveLcmsIntegrationsExportForSubmit = (analysis, hplcMsSt) => { return 'percent'; }; -const pickFromList = (list, index, fallback = null) => ( - Array.isArray(list) && list[index] !== undefined ? list[index] : fallback -); +const emptyIntegration = { + stack: [], + refArea: 1, + refFactor: 1, + shift: 0, + edited: false, +}; + +const emptyMultiplicity = { + stack: [], + shift: 0, + smExtext: false, + edited: false, +}; const hasBoolean = (value) => typeof value === 'boolean'; @@ -142,9 +154,13 @@ const buildSpectrumPayload = ({ ); const scan = Convert2Scan(feature, scanSt); const thres = Convert2Thres(feature, threshold); - const shift = pickFromList(shiftSt?.shifts, curveIdx, shiftSt); - const integration = pickFromList(integrationSt?.integrations, curveIdx, integrationSt); - const multiplicity = pickFromList(multiplicitySt?.multiplicities, curveIdx, multiplicitySt); + const shift = shiftEntryAtIndex(shiftSt, curveIdx); + const integration = listEntryAtIndex( + integrationSt?.integrations, curveIdx, emptyIntegration, + ); + const multiplicity = listEntryAtIndex( + multiplicitySt?.multiplicities, curveIdx, emptyMultiplicity, + ); const { xLabel, yLabel } = resolveAxisLabels( feature?.xUnit, feature?.yUnit, @@ -172,6 +188,7 @@ const buildSpectrumPayload = ({ return { peaks: peaksEdit, layout: layoutSt, + xUnit: xLabel, shift, scan, thres, @@ -187,6 +204,7 @@ const buildSpectrumPayload = ({ axesUnitsSt, detectorSt, dscMetaData, + feature, ...optionalBooleanFlags, }; }; @@ -196,7 +214,7 @@ const onClickCb = ( layoutSt, shiftSt, analysis, decimalSt, integrationSt, multiplicitySt, waveLengthSt, cyclicvoltaSt, curveSt, axesUnitsSt, detectorSt, dscMetaData, - curveList, editPeakSt, thresList, scanSt, feature, hplcMsSt, + curveList, editPeakSt, thresList, scanSt, feature, hplcMsSt, sweepExtentSt, ) => ( () => { const defaultCurves = feature ? [{ feature }] : []; @@ -255,7 +273,10 @@ const onClickCb = ( } return spectrumPayload; }); + const selectedIdx = Number.isFinite(curveSt?.curveIdx) ? curveSt.curveIdx : 0; + const selectedSpectrumPayload = spectraList[selectedIdx] || spectraList[0] || {}; const payload = { + ...selectedSpectrumPayload, spectra_list: spectraList, }; if (lcmsGlobalFields) { @@ -264,6 +285,9 @@ const onClickCb = ( if (Number.isFinite(curveSt?.curveIdx)) { payload.curveSt = { curveIdx: curveSt.curveIdx }; } + if (sweepExtentSt?.xExtent || sweepExtentSt?.yExtent) { + payload.sweepExtent = sweepExtentSt; + } operationValue(payload); } ); @@ -275,6 +299,8 @@ const BtnSubmit = ({ waveLengthSt, cyclicvoltaSt, curveSt, curveList, axesUnitsSt, detectorSt, metaSt, hplcMsSt, + sweepExtentSt, + disabled, className, children, }) => { // const disBtn = peaksEdit.length === 0 || statusSt.btnSubmit || disabled; const { dscMetaData } = metaSt; @@ -303,23 +329,25 @@ const BtnSubmit = ({ if (!operation) return null; return ( - Submit}> + {operation.name || 'Submit'}}> - + {children || } ); @@ -344,6 +372,7 @@ const mapStateToProps = (state, props) => ( // eslint-disable-line detectorSt: state.detector, metaSt: state.meta, hplcMsSt: state.hplcMs, + sweepExtentSt: state.ui.sweepExtent, } ); @@ -375,6 +404,10 @@ BtnSubmit.propTypes = { detectorSt: PropTypes.object.isRequired, metaSt: PropTypes.object.isRequired, hplcMsSt: PropTypes.object, + sweepExtentSt: PropTypes.object, + disabled: PropTypes.bool, + className: PropTypes.string, + children: PropTypes.node, }; BtnSubmit.defaultProps = { diff --git a/src/components/cmd_bar/r07_wavelength_btn.js b/src/components/cmd_bar/r07_wavelength_btn.js index ebdabc32..a835245b 100644 --- a/src/components/cmd_bar/r07_wavelength_btn.js +++ b/src/components/cmd_bar/r07_wavelength_btn.js @@ -31,9 +31,7 @@ const styles = () => ( const wavelengthSelect = (classes, waveLengthSt, layoutSt, updateWavelengthAct) => { if (!Format.isXRDLayout(layoutSt)) { - return ( - - ); + return null; } const onChange = (e) => updateWavelengthAct(e.target.value); @@ -72,11 +70,14 @@ const wavelengthSelect = (classes, waveLengthSt, layoutSt, updateWavelengthAct) const Wavelength = ({ classes, waveLengthSt, layoutSt, updateWavelengthAct, -}) => ( - - { wavelengthSelect(classes, waveLengthSt, layoutSt, updateWavelengthAct) } - -); +}) => { + if (!Format.isXRDLayout(layoutSt)) return null; + return ( + + { wavelengthSelect(classes, waveLengthSt, layoutSt, updateWavelengthAct) } + + ); +}; const mapStateToProps = (state, props) => ( // eslint-disable-line { diff --git a/src/components/cmd_bar/r08_change_axes.js b/src/components/cmd_bar/r08_change_axes.js index 6ceb4199..62a04537 100644 --- a/src/components/cmd_bar/r08_change_axes.js +++ b/src/components/cmd_bar/r08_change_axes.js @@ -124,9 +124,7 @@ const axisY = (classes, layoutSt, axesUnitsSt, updateYAxisAct, curveSt) => { const showSelect = (classes, layoutSt, curveSt, axesUnitsSt, updateXAxisAct, updateYAxisAct) => { if (!listLayoutToShow.includes(layoutSt)) { - return ( - - ); + return null; } return ( @@ -151,6 +149,8 @@ const ChangeAxes = ({ } }, [layoutSt, axes, curveIdx, updateYAxisAct]); + if (!listLayoutToShow.includes(layoutSt)) return null; + return ( { showSelect(classes, layoutSt, curveSt, axesUnitsSt, updateXAxisAct, updateYAxisAct) } diff --git a/src/components/cmd_bar/r09_detector.js b/src/components/cmd_bar/r09_detector.js index 2ab696c9..d2116b5d 100644 --- a/src/components/cmd_bar/r09_detector.js +++ b/src/components/cmd_bar/r09_detector.js @@ -31,9 +31,7 @@ const styles = () => ( const detectorSelect = (classes, detectorSt, curveSt, layoutSt, updateDetectorAct) => { if (!Format.isSECLayout(layoutSt)) { - return ( - - ); + return null; } const { curveIdx } = curveSt; @@ -85,11 +83,14 @@ const detectorSelect = (classes, detectorSt, curveSt, layoutSt, updateDetectorAc const Detector = ({ classes, detectorSt, curveSt, layoutSt, updateDetectorAct, -}) => ( - - { detectorSelect(classes, detectorSt, curveSt, layoutSt, updateDetectorAct) } - -); +}) => { + if (!Format.isSECLayout(layoutSt)) return null; + return ( + + { detectorSelect(classes, detectorSt, curveSt, layoutSt, updateDetectorAct) } + + ); +}; const mapStateToProps = (state, _props) => ( // eslint-disable-line { diff --git a/src/components/cmd_bar/r10_cv_density.js b/src/components/cmd_bar/r10_cv_density.js index 12218bb1..a74ddd4e 100644 --- a/src/components/cmd_bar/r10_cv_density.js +++ b/src/components/cmd_bar/r10_cv_density.js @@ -42,7 +42,7 @@ const CvDensityControls = ({ classes, layoutSt, areaValue, areaUnit, useCurrentDensity, setAreaValueAct, setAreaUnitAct, toggleDensityAct, }) => { - if (layoutSt !== LIST_LAYOUT.CYCLIC_VOLTAMMETRY) return ; + if (layoutSt !== LIST_LAYOUT.CYCLIC_VOLTAMMETRY) return null; const handleAreaChange = (e) => { const raw = e.target.value; diff --git a/src/components/common/draw.js b/src/components/common/draw.js index c8478546..b1861001 100644 --- a/src/components/common/draw.js +++ b/src/components/common/draw.js @@ -22,7 +22,7 @@ const drawDisplay = (klass, isHidden) => { } }; -const drawDestroy = (klass) => d3.select(`${klass} > *`).remove(); +const drawDestroy = (klass) => d3.select(klass).selectAll('*').remove(); const drawArrowOnCurve = (klass, isHidden) => { if (isHidden) { diff --git a/src/components/d3_line/line_focus.js b/src/components/d3_line/line_focus.js index e819583e..af3b6c47 100644 --- a/src/components/d3_line/line_focus.js +++ b/src/components/d3_line/line_focus.js @@ -307,6 +307,12 @@ class LineFocus { mpp.exit() .attr('class', 'exit') .remove(); + const clearPeakLabels = () => { + const bpTxt = this.tags.bpTxt.selectAll('text').data([]); + bpTxt.exit() + .attr('class', 'exit') + .remove(); + }; const linePath = [ { x: -0.5, y: 10 }, @@ -367,6 +373,8 @@ class LineFocus { .text((d) => d.x.toFixed(2)) .attr('transform', (d) => `translate(${xt(d.x)}, ${yt(d.y) - 25})`) .on('click', (event, d) => this.onClickTarget(event, d)); + } else { + clearPeakLabels(); } } @@ -376,8 +384,35 @@ class LineFocus { } = this.shouldUpdate; if (sameXY && sameLySt && sameItSt && sameData) return; + const clearIntegralPaths = () => { + const empty = []; + const igbp = this.tags.igbPath.selectAll('path').data(empty); + igbp.exit() + .attr('class', 'exit') + .remove(); + const igcp = this.tags.igcPath.selectAll('path').data(empty); + igcp.exit() + .attr('class', 'exit') + .remove(); + const igtp = this.tags.igtPath.selectAll('text').data(empty); + igtp.exit() + .attr('class', 'exit') + .remove(); + }; + const clearAUC = () => { + const auc = this.tags.aucPath.selectAll('path').data([]); + auc.exit() + .attr('class', 'exit') + .remove(); + }; + const { selectedIdx, integrations } = integrationState; const selectedIntegration = integrations[selectedIdx]; + if (selectedIntegration === false || selectedIntegration === undefined) { + clearIntegralPaths(); + clearAUC(); + return; + } const { stack, refArea, refFactor, shift, @@ -406,19 +441,16 @@ class LineFocus { .remove(); if (itgs.length === 0 || isDisable) { - // remove drawn area under curve - const auc = this.tags.aucPath.selectAll('path').data(stack); - auc.exit() - .attr('class', 'exit') - .remove(); - auc.merge(auc); - this.drawVisualSplitLines(showIntegSplit ? itgs : [], shift, ignoreRef); + clearIntegralPaths(); + clearAUC(); return; } if (ignoreRef) { + clearIntegralPaths(); this.drawAUC(stack, shift); } else { + clearAUC(); // rescale for zoom const { xt } = TfRescale(this); @@ -556,16 +588,16 @@ class LineFocus { const { selectedIdx, multiplicities = [] } = mtplySt || {}; const selectedMulti = multiplicities[selectedIdx] || {}; - const { - stack = [], smExtext = false, shift = 0, - } = selectedMulti; + const isDisable = Cfg.btnCmdMpy(this.layout); + const hasMpy = !isDisable && selectedMulti?.stack?.length > 0; + const mpys = hasMpy ? selectedMulti.stack : []; + const smExtext = hasMpy ? selectedMulti.smExtext : false; + const shift = hasMpy ? selectedMulti.shift : 0; const hasValidExtent = (extent) => ( extent && Number.isFinite(extent.xL) && Number.isFinite(extent.xU) ); - const mpys = stack.filter((m) => hasValidExtent(m?.xExtent)); - const isDisable = Cfg.btnCmdMpy(this.layout); if (mpys.length === 0 || isDisable) return; const activeExtent = hasValidExtent(smExtext) ? smExtext : mpys[0].xExtent; // rescale for zoom diff --git a/src/components/d3_multi/index.js b/src/components/d3_multi/index.js index 1aa27a4a..0b74654e 100644 --- a/src/components/d3_multi/index.js +++ b/src/components/d3_multi/index.js @@ -29,6 +29,26 @@ import { const W = Math.round(window.innerWidth * 0.90 * 9 / 12); // ROI const H = Math.round(window.innerHeight * 0.90 * 0.85); // ROI +const buildResetPayload = (feature, curveSt) => ( + { ...feature, curveIdx: curveSt.curveIdx } +); + +const hasActiveZoom = (sweepExtentSt) => { + if (!sweepExtentSt) return false; + return !!(sweepExtentSt.xExtent || sweepExtentSt.yExtent); +}; + +const canSkipReset = (sweepExtentSt, layoutSt, feature) => ( + hasActiveZoom(sweepExtentSt) && layoutSt === feature?.operation?.layout +); + +const isSameMultiComparison = (oldEntities, newEntities) => ( + Array.isArray(oldEntities) + && Array.isArray(newEntities) + && oldEntities.length > 0 + && oldEntities.length === newEntities.length +); + class ViewerMulti extends React.Component { constructor(props) { super(props); @@ -70,7 +90,7 @@ class ViewerMulti extends React.Component { seed, peak, cLabel, xLabel, yLabel, tTrEndPts, tSfPeaks, editPeakSt, layoutSt, sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, - isHidden, cyclicvoltaSt, + isHidden, cyclicvoltaSt, shiftSt, integrationSt, mtplySt, axesUnitsSt, uiSt, } = this.props; @@ -118,6 +138,7 @@ class ViewerMulti extends React.Component { isUiVisualSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, + shiftSt, integrationSt, mtplySt, uiSt, @@ -215,10 +236,13 @@ class ViewerMulti extends React.Component { } normChange(prevProps) { - const { feature, resetAllAct, entities } = this.props; + const { + feature, resetAllAct, entities, curveSt, + } = this.props; const oldEntities = prevProps.entities; - if (oldEntities !== entities) { - resetAllAct(feature); + if (oldEntities !== entities + && !isSameMultiComparison(oldEntities, entities)) { + resetAllAct(buildResetPayload(feature, curveSt)); } } @@ -246,7 +270,7 @@ class ViewerMulti extends React.Component { seed, peak, cLabel, feature, tTrEndPts, tSfPeaks, editPeakSt, layoutSt, sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, - isHidden, resetAllAct, cyclicvoltaSt, + isHidden, resetAllAct, cyclicvoltaSt, shiftSt, integrationSt, mtplySt, uiSt, } = props; @@ -254,8 +278,8 @@ class ViewerMulti extends React.Component { this.currentSize = size; drawDestroy(this.rootKlass); - if (shouldReset) { - resetAllAct(feature); + if (shouldReset && !canSkipReset(sweepExtentSt, layoutSt, feature)) { + resetAllAct(buildResetPayload(feature, curveSt)); } const { xxLabel, yyLabel } = this.resolveAxisLabels(props); @@ -278,6 +302,7 @@ class ViewerMulti extends React.Component { isUiVisualSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, + shiftSt, integrationSt, mtplySt, uiSt, @@ -318,6 +343,7 @@ const mapStateToProps = (state, props) => ( && state.ui.sweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_VISUAL_SPLIT, isUiNoBrushSt: LIST_NON_BRUSH_TYPES.indexOf(state.ui.sweepType) < 0, cyclicvoltaSt: state.cyclicvolta, + shiftSt: state.shift, maxminPeakSt: Feature2MaxMinPeak(state, props), integrationSt: state.integration.present, mtplySt: state.multiplicity.present, @@ -369,6 +395,7 @@ ViewerMulti.propTypes = { removeVisualSplitLineAct: PropTypes.func.isRequired, isHidden: PropTypes.bool, cyclicvoltaSt: PropTypes.object.isRequired, + shiftSt: PropTypes.object.isRequired, maxminPeakSt: PropTypes.object, addNewCylicVoltaPairPeakAct: PropTypes.func.isRequired, addCylicVoltaMaxPeakAct: PropTypes.func.isRequired, diff --git a/src/components/d3_multi/multi_focus.js b/src/components/d3_multi/multi_focus.js index a1d20da8..1bd925f1 100644 --- a/src/components/d3_multi/multi_focus.js +++ b/src/components/d3_multi/multi_focus.js @@ -24,6 +24,7 @@ import Format from '../../helpers/format'; import { GetCyclicVoltaPreviousShift, convertTopic, } from '../../helpers/chem'; +import { shiftOffsetAtIndex } from '../../helpers/shift'; import Cfg from '../../helpers/cfg'; import { itgIdTag, mpyIdTag } from '../../helpers/focus'; import { @@ -126,6 +127,7 @@ class MultiFocus { const { prevXt, prevYt, prevEpSt, prevLySt, prevTePt, prevDtPk, prevSfPk, prevData, prevYFactor, + prevJcampIdx, } = this.shouldUpdate; const { xt, yt } = TfRescale(this); const sameXY = xt(1.1) === prevXt && prevYt === yt(1.1); @@ -142,12 +144,13 @@ class MultiFocus { ); const sameData = prevData === this.data.length; const sameYFactor = prevYFactor === this.yTransformFactor; + const sameJcampIdx = prevJcampIdx === this.jcampIdx; this.shouldUpdate = Object.assign( {}, this.shouldUpdate, { sameXY, sameEpSt, sameLySt, // eslint-disable-line - sameTePt, sameDtPk, sameSfPk, sameData, sameYFactor, // eslint-disable-line + sameTePt, sameDtPk, sameSfPk, sameData, sameYFactor, sameJcampIdx, // eslint-disable-line }, ); } @@ -162,12 +165,13 @@ class MultiFocus { const prevData = this.data.length; const prevLySt = this.layout; const prevYFactor = this.yTransformFactor; + const prevJcampIdx = this.jcampIdx; this.shouldUpdate = Object.assign( {}, this.shouldUpdate, { prevXt, prevYt, prevEpSt, prevLySt, // eslint-disable-line - prevTePt, prevDtPk, prevSfPk, prevData, prevYFactor, // eslint-disable-line + prevTePt, prevDtPk, prevSfPk, prevData, prevYFactor, prevJcampIdx, // eslint-disable-line }, ); } @@ -198,7 +202,7 @@ class MultiFocus { return y * this.yTransformFactor; } - setDataParams(filterSeed, peaks, tTrEndPts, tSfPeaks, layout, cyclicvoltaSt, jcampIdx = 0) { + setDataParams(filterSeed, peaks, tTrEndPts, tSfPeaks, layout, cyclicvoltaSt, shiftSt, jcampIdx = 0) { this.data = []; this.otherLineData = []; let filterSubLayoutValue = null; @@ -208,7 +212,9 @@ class MultiFocus { this.entities.forEach((entry, idx) => { const { topic, feature, color } = entry; - const offset = GetCyclicVoltaPreviousShift(cyclicvoltaSt, jcampIdx); + const offset = Format.isCyclicVoltaLayout(layout) + ? GetCyclicVoltaPreviousShift(cyclicvoltaSt, idx) + : shiftOffsetAtIndex(shiftSt, idx); let currData = convertTopic(topic, layout, feature, offset); if (idx === jcampIdx) { if (!Format.isCyclicVoltaLayout(layout)) { @@ -426,11 +432,11 @@ class MultiFocus { drawPeaks(editPeakSt) { const { - sameXY, sameEpSt, sameDtPk, sameSfPk, + sameXY, sameEpSt, sameDtPk, sameSfPk, sameJcampIdx, } = this.shouldUpdate; if (!Format.isCyclicVoltaLayout(this.layout) - && sameXY && sameEpSt && sameDtPk && sameSfPk) return; + && sameXY && sameEpSt && sameDtPk && sameSfPk && sameJcampIdx) return; // rescale for zoom const { xt, yt } = TfRescale(this); @@ -454,6 +460,12 @@ class MultiFocus { mpp.exit() .attr('class', 'exit') .remove(); + const clearPeakLabels = () => { + const bpTxt = this.tags.bpTxt.selectAll('text').data([]); + bpTxt.exit() + .attr('class', 'exit') + .remove(); + }; const linePath = [ { x: -0.5, y: 10 }, @@ -528,6 +540,8 @@ class MultiFocus { .text((d) => d.x.toFixed(2)) .attr('transform', (d) => `translate(${xt(d.x)}, ${yt(d.y) - 25})`) .on('click', (event, d) => this.onClickTarget(event, d)); + } else { + clearPeakLabels(); } mpp.attr('fill', (_, index) => { @@ -541,11 +555,11 @@ class MultiFocus { drawPeckers() { const { - sameXY, sameEpSt, sameDtPk, sameSfPk, + sameXY, sameEpSt, sameDtPk, sameSfPk, sameJcampIdx, } = this.shouldUpdate; if (!Format.isCyclicVoltaLayout(this.layout) && sameXY - && sameEpSt && sameDtPk && sameSfPk) return; + && sameEpSt && sameDtPk && sameSfPk && sameJcampIdx) return; // rescale for zoom const { xt, yt } = TfRescale(this); @@ -598,31 +612,37 @@ class MultiFocus { drawInteg(integrationState) { const { - sameXY, sameLySt, sameItSt, sameData, + sameXY, sameLySt, sameItSt, sameData, sameJcampIdx, } = this.shouldUpdate; - if (sameXY && sameLySt && sameItSt && sameData) return; + if (sameXY && sameLySt && sameItSt && sameData && sameJcampIdx) return; - const { integrations } = integrationState; - const selectedIntegration = integrations[this.jcampIdx]; - if (selectedIntegration === false || selectedIntegration === undefined) { - Object.assign(this, { - integrationSplitTargets: { stack: [], shift: 0, ignoreRef: false }, - }); - const itgs = []; - const igbp = this.tags.igbPath.selectAll('path').data(itgs); + const clearIntegralPaths = () => { + const empty = []; + const igbp = this.tags.igbPath.selectAll('path').data(empty); igbp.exit() .attr('class', 'exit') .remove(); - const igcp = this.tags.igcPath.selectAll('path').data(itgs); + const igcp = this.tags.igcPath.selectAll('path').data(empty); igcp.exit() .attr('class', 'exit') .remove(); - - const igtp = this.tags.igtPath.selectAll('text').data(itgs); + const igtp = this.tags.igtPath.selectAll('text').data(empty); igtp.exit() .attr('class', 'exit') .remove(); - this.drawVisualSplitLines([], 0, false); + }; + const clearAUC = () => { + const auc = this.tags.aucPath.selectAll('path').data([]); + auc.exit() + .attr('class', 'exit') + .remove(); + }; + + const { integrations } = integrationState; + const selectedIntegration = integrations[this.jcampIdx]; + if (selectedIntegration === false || selectedIntegration === undefined) { + clearIntegralPaths(); + clearAUC(); return; } @@ -653,19 +673,16 @@ class MultiFocus { .remove(); if (itgs.length === 0 || isDisable) { - // remove drawn area under curve - const auc = this.tags.aucPath.selectAll('path').data(stack); - auc.exit() - .attr('class', 'exit') - .remove(); - auc.merge(auc); - this.drawVisualSplitLines(showIntegSplit ? itgs : [], shift, ignoreRef); + clearIntegralPaths(); + clearAUC(); return; } if (ignoreRef) { + clearIntegralPaths(); this.drawAUC(stack, shift); } else { + clearAUC(); // rescale for zoom const { xt } = TfRescale(this); @@ -797,48 +814,24 @@ class MultiFocus { } drawMtply(mtplySt) { - const { sameXY, sameLySt, sameMySt } = this.shouldUpdate; - if (sameXY && sameLySt && sameMySt) return; + const { + sameXY, sameLySt, sameMySt, sameJcampIdx, + } = this.shouldUpdate; + if (sameXY && sameLySt && sameMySt && sameJcampIdx) return; const { multiplicities } = mtplySt; const selectedMulti = multiplicities[this.jcampIdx]; - if (selectedMulti === false || selectedMulti === undefined) { - const mpys = []; - const mpyb = this.tags.mpybPath.selectAll('path').data(mpys); - mpyb.exit() - .attr('class', 'exit') - .remove(); - const mpyt1 = this.tags.mpyt1Path.selectAll('text').data(mpys); - mpyt1.exit() - .attr('class', 'exit') - .remove(); - const mpyt2 = this.tags.mpyt2Path.selectAll('text').data(mpys); - mpyt2.exit() - .attr('class', 'exit') - .remove(); - let mPeaks = mpys.map((m) => { - const { peaks, xExtent } = m; - return peaks.map((p) => Object.assign({}, p, { xExtent })); - }); - mPeaks = [].concat(...mPeaks); - const mpyp = this.tags.mpypPath.selectAll('path').data(mPeaks); - mpyp.exit() - .attr('class', 'exit') - .remove(); - return; - } - - const { - stack = [], smExtext = false, shift = 0, - } = selectedMulti || {}; + const isDisable = Cfg.btnCmdMpy(this.layout); + const hasMpy = !isDisable && selectedMulti?.stack?.length > 0; + const mpys = hasMpy ? selectedMulti.stack : []; + const smExtext = hasMpy ? selectedMulti.smExtext : false; + const shift = hasMpy ? selectedMulti.shift : 0; const hasValidExtent = (extent) => ( extent && Number.isFinite(extent.xL) && Number.isFinite(extent.xU) ); - const mpys = stack.filter((m) => hasValidExtent(m?.xExtent)); - const isDisable = Cfg.btnCmdMpy(this.layout); if (mpys.length === 0 || isDisable) return; const activeExtent = hasValidExtent(smExtext) ? smExtext : mpys[0].xExtent; // rescale for zoom @@ -1077,7 +1070,7 @@ class MultiFocus { filterSeed, filterPeak, tTrEndPts, tSfPeaks, editPeakSt, layoutSt, sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, - cyclicvoltaSt, + cyclicvoltaSt, shiftSt, integrationSt, mtplySt, uiSt, }) { this.uiSt = uiSt; @@ -1093,7 +1086,7 @@ class MultiFocus { this.root = d3.select(this.rootKlass).selectAll('.focus-main'); this.scales = InitScale(this, this.reverseXAxis(layoutSt)); this.setTip(); - this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, layoutSt, cyclicvoltaSt, jcampIdx); + this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, layoutSt, cyclicvoltaSt, shiftSt, jcampIdx); Object.assign(this, { isUiSplitIntgSt, isUiVisualSplitIntgSt }); if (!isUiSplitIntgSt && !isUiVisualSplitIntgSt) this.clearSplitPreview(); MountCompass(this); @@ -1127,7 +1120,7 @@ class MultiFocus { entities, curveSt, filterSeed, filterPeak, tTrEndPts, tSfPeaks, editPeakSt, layoutSt, - sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, + sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, shiftSt, integrationSt, mtplySt, uiSt, }) { this.uiSt = uiSt; @@ -1140,7 +1133,7 @@ class MultiFocus { this.isShowAllCurves = isShowAllCurve; this.entities = entities; - this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, layoutSt, cyclicvoltaSt, jcampIdx); + this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, layoutSt, cyclicvoltaSt, shiftSt, jcampIdx); Object.assign(this, { isUiSplitIntgSt, isUiVisualSplitIntgSt }); if (!isUiSplitIntgSt && !isUiVisualSplitIntgSt) this.clearSplitPreview(); diff --git a/src/components/panel/graph_selection.js b/src/components/panel/graph_selection.js index 60316157..c50842aa 100644 --- a/src/components/panel/graph_selection.js +++ b/src/components/panel/graph_selection.js @@ -20,36 +20,78 @@ const styles = () => ({ backgroundColor: '#eee', height: 22, }, - curve: { + curveItem: { + display: 'flex', + alignItems: 'center', width: '100%', - }, - line: { - height: '2px', - borderWidth: '0', - margin: '0', + fontSize: '0.8em', + padding: '6px 8px', + margin: 0, + cursor: 'pointer', }, curveDefault: { backgroundColor: '#fff', - fontSize: '0.8em', - margin: '0', - padding: '10px 2px 2px 10px', - maxWidth: '95%', - overflowWrap: 'anywhere', }, curveSelected: { - backgroundColor: '#2196f3', - fontSize: '0.8em', - color: '#fff', - padding: '10px 2px 2px 10px', - maxWidth: '95%', + backgroundColor: '#e0e0e0', + }, + curveIndex: { + flexShrink: 0, + marginRight: '4px', + fontStyle: 'italic', + }, + curveLabel: { + flexShrink: 0, + marginRight: '8px', overflowWrap: 'anywhere', }, + colorBar: { + flex: 1, + height: '6px', + borderRadius: '1px', + minWidth: '24px', + alignSelf: 'center', + }, }); +const fallbackName = (entityFileNames, idx) => { + if (entityFileNames && idx < entityFileNames.length) { + return entityFileNames[idx]; + } + return ''; +}; + +const displayName = (spectra, idx, entityFileNames) => ( + spectra?.title + || spectra?.feature?.title + || spectra?.spectrum?.title + || fallbackName(entityFileNames, idx) + || `Spectrum ${idx + 1}` +); + +const renderCurveItem = (classes, item, curveIdx, onChange) => ( + onChange(item.idx)} + className={classNames( + classes.curveItem, + item.idx === curveIdx ? classes.curveSelected : classes.curveDefault, + )} + > + {item.name} + {item.label !== '' ? {item.label} : null} + + +); + const GraphSelectionPanel = ({ classes, curveSt, entityFileNames, subLayoutsInfo, layoutSt, - selectCurveAct, toggleShowAllCurveAct, + selectCurveAct, toggleShowAllCurveAct, expand, onExapnd, }) => { let subLayoutValues = []; if (subLayoutsInfo) { @@ -89,33 +131,37 @@ const GraphSelectionPanel = ({ let itemsSubLayout = []; if (selectedSubLayout && subLayoutValues.length > 1) { const subLayout = subLayoutsInfo?.[resolvedSelectedSubLayout]; - itemsSubLayout = Array.isArray(subLayout) ? subLayout.map((spectra, idx) => { - const spectraIdx = spectra.curveIdx; - const { color } = spectra; - let filename = ''; - if (entityFileNames && spectraIdx < entityFileNames.length) { - filename = entityFileNames[spectraIdx]; - } - return { - name: `${idx + 1}.`, idx: spectraIdx, color, filename, - }; - }) : []; + try { + itemsSubLayout = Array.isArray(subLayout) ? subLayout.map((spectra, idx) => { + const spectraIdx = spectra.curveIdx; + const { color } = spectra; + return { + name: `${idx + 1}.`, + idx: spectraIdx, + color, + label: displayName(spectra, spectraIdx, entityFileNames), + }; + }) : []; + } catch (e) { + console.log(e); //eslint-disable-line + } } const items = listCurves.map((spectra, idx) => { const { color } = spectra; - let filename = ''; - if (entityFileNames && idx < entityFileNames.length) { - filename = entityFileNames[idx]; - } return { - name: `${idx + 1}.`, idx, color, filename, + name: `${idx + 1}.`, + idx, + color, + label: displayName(spectra, idx, entityFileNames), }; }); return ( { - itemsSubLayout.map((item) => ( - onChange(item.idx)} - className={ - classNames((item.idx === curveIdx ? classes.curveSelected : classes.curveDefault)) // eslint-disable-line - } - > - - { item.name } - -
- { - item.filename !== '' ? File: { item.filename } : null // eslint-disable-line - } -
-
-
- )) + itemsSubLayout.map((item) => renderCurveItem(classes, item, curveIdx, onChange)) }
+ ) : ( + + { + items.map((item) => renderCurveItem(classes, item, curveIdx, onChange)) + } + ) - : ( - - { - items.map((item) => ( - onChange(item.idx)} - className={ - classNames((item.idx === curveIdx ? classes.curveSelected : classes.curveDefault)) // eslint-disable-line - } - > - - { item.name } - -
- { - item.filename !== '' ? File: { item.filename } : null // eslint-disable-line - } -
-
-
- )) - } -
- ) }
); diff --git a/src/components/panel/index.js b/src/components/panel/index.js index 7461dc40..dfdc79fb 100644 --- a/src/components/panel/index.js +++ b/src/components/panel/index.js @@ -47,7 +47,7 @@ class PanelViewer extends React.Component { super(props); this.state = { - expand: 'info', + expand: ['info', 'graph'], }; this.onToggleExpand = this.onToggleExpand.bind(this); @@ -63,7 +63,9 @@ class PanelViewer extends React.Component { onToggleExpand(input) { const { expand } = this.state; - const nextExpand = input === expand ? '' : input; + const nextExpand = expand.includes(input) + ? expand.filter((item) => item !== input) + : [...expand, input]; this.setState({ expand: nextExpand }); } @@ -81,6 +83,7 @@ class PanelViewer extends React.Component { const onExpandCompare = () => this.onToggleExpand('compare'); const onExpandCyclicVolta = () => this.onToggleExpand('cyclicvolta'); const onExpandGraphSelection = () => this.onToggleExpand('graph'); + const isExpanded = (name) => expand.includes(name); const { listCurves } = curveSt; const curveCount = Array.isArray(listCurves) ? listCurves.length : 0; const hideGraphSelection = curveCount <= 1 || Format.isLCMsLayout(layoutSt); @@ -91,13 +94,13 @@ class PanelViewer extends React.Component { - { hideGraphSelection ? null : } + { hideGraphSelection ? null : } - { Cfg.hidePanelPeak(layoutSt) ? null : } - { Cfg.hidePanelMpy(layoutSt) ? null : } - { (Cfg.hidePanelCompare(layoutSt) || curveCount > 1) ? null : } + { Cfg.hidePanelPeak(layoutSt) ? null : } + { Cfg.hidePanelMpy(layoutSt) ? null : } + { (Cfg.hidePanelCompare(layoutSt) || !listCurves || listCurves.length > 1) ? null : } { (Cfg.hidePanelCyclicVolta(layoutSt) || hideCyclicVolta) ? null : ( diff --git a/src/components/panel/info.js b/src/components/panel/info.js index 72bc74db..3a105c36 100644 --- a/src/components/panel/info.js +++ b/src/components/panel/info.js @@ -174,54 +174,6 @@ const handleDescriptionChanged = (content, delta, source, editor, onDescriptionC onDescriptionChanged(normalizeQuillValue(content), delta, source, editor); }; -const aucValue = (integration, hplcMsSt) => { - const values = []; - const stackIntegration = integration?.stack; - if (Array.isArray(stackIntegration)) { - let sumVal = 0.0; - stackIntegration.forEach((inte) => { - if (inte.absoluteArea) { - sumVal += inte.absoluteArea; - } - }); - sumVal = sumVal.toFixed(2); - stackIntegration.forEach((inte) => { - if (inte.absoluteArea) { - const areaVal = inte.absoluteArea.toFixed(2); - const percent = (areaVal * 100 / sumVal).toFixed(2); - const valStr = areaVal + " (" + percent + "%)"; // eslint-disable-line - values.push(valStr); - } - }); - } - - const spectraList = hplcMsSt?.uvvis?.spectraList || []; - const listWaveLength = hplcMsSt?.uvvis?.listWaveLength || []; - - spectraList.forEach((spectrum, idx) => { - const wavelength = listWaveLength[idx]; - const integrations = spectrum?.integrations || []; - - if (integrations.length > 0) { - const sumArea = integrations.reduce( - (sum, integ) => sum + (integ.absoluteArea ?? integ.area ?? 0), - 0, - ); - - const integrationStrings = integrations.map((integ) => { - const rawArea = integ.absoluteArea ?? integ.area ?? 0; - const areaVal = rawArea.toFixed(2); - const percent = sumArea > 0 ? ((rawArea * 100) / sumArea).toFixed(2) : '0.00'; - return `${areaVal} (${percent}%)`; - }); - - values.push(`[${wavelength} nm]: ${integrationStrings.join(', ')}`); - } - }); - - return values.join('\n'); -}; - const SECData = ({ classes, layout, detector, secData, }) => { @@ -313,7 +265,7 @@ const InfoPanel = ({ classes, expand, feature, integration, editorOnly, molSvg, descriptions, layoutSt, simulationSt, shiftSt, curveSt, exactMass, onExpand, canChangeDescription, onDescriptionChanged, detectorSt, - metaSt, updateDSCMetaDataAct, hplcMsSt, entities, + metaSt, updateDSCMetaDataAct, entities, }) => { if (!feature) return null; const msExactMass = Format.isMsLayout(layoutSt) && exactMass @@ -438,7 +390,7 @@ const InfoPanel = ({

- {aucValue(integration)} + {Format.formatHplcAucPanel(integration, feature)} ) : null @@ -505,7 +457,7 @@ const InfoPanel = ({ marginBottom: '100px', }} > - {aucValue(integration, hplcMsSt) + {Format.formatHplcAucPanel(integration, feature) .split('\n') .map((line, idx) => (
{line}
@@ -558,7 +510,6 @@ InfoPanel.propTypes = { detectorSt: PropTypes.object.isRequired, metaSt: PropTypes.object.isRequired, updateDSCMetaDataAct: PropTypes.func.isRequired, - hplcMsSt: PropTypes.object.isRequired, entities: PropTypes.array, }; diff --git a/src/helpers/chem.js b/src/helpers/chem.js index 338338ed..0ad74155 100644 --- a/src/helpers/chem.js +++ b/src/helpers/chem.js @@ -5,7 +5,7 @@ max-len */ import Jcampconverter from 'jcampconverter'; import { createSelector } from 'reselect'; -import { FromManualToOffset } from './shift'; +import { shiftOffsetAtIndex } from './shift'; import Format from './format'; import { LIST_LAYOUT } from '../constants/list_layout'; import { getArea } from './integration'; @@ -48,15 +48,7 @@ const getShiftOffset = (state, _) => { // eslint-disable-line return GetCyclicVoltaShiftOffset(cyclicvolta, curveIdx); } - const { shift } = state; - const { shifts } = shift; - const selectedShift = shifts[curveIdx]; - if (!selectedShift) { - return 0.0; - } - - const { ref, peak } = selectedShift; - return FromManualToOffset(ref, peak); + return shiftOffsetAtIndex(state.shift, curveIdx); }; const calcXYK = (xs, ys, maxY, offset) => { diff --git a/src/helpers/compass.js b/src/helpers/compass.js index 96f11e2d..a28d7c91 100644 --- a/src/helpers/compass.js +++ b/src/helpers/compass.js @@ -310,7 +310,7 @@ const ClickCompass = (event, focus) => { } else if (isLcmsTicGraph) { focus.clickUiTargetAct(pt, false, false, jcampIdx, false, 'lcms_tic'); } else { - focus.clickUiTargetAct(pt, false); + focus.clickUiTargetAct(pt, false, 0, focus.jcampIdx); } }; diff --git a/src/helpers/extractPeaksEdit.js b/src/helpers/extractPeaksEdit.js index 4930558b..8801cf6a 100644 --- a/src/helpers/extractPeaksEdit.js +++ b/src/helpers/extractPeaksEdit.js @@ -1,7 +1,7 @@ /* eslint-disable max-len */ import { Convert2Peak } from './chem'; import { PksEdit } from './converter'; -import { FromManualToOffset } from './shift'; +import { shiftOffsetAtIndex } from './shift'; import Format from './format'; import { calcArea } from './integration'; import { @@ -10,21 +10,8 @@ import { getLcmsMzPageData, } from '../features/lc-ms/submit'; -const niOffset = (shiftSt, atIndex = 0) => { - const { shifts } = shiftSt; - const selectedShift = shifts[atIndex]; - if (!selectedShift) { - return 0; - } - const { ref, peak } = selectedShift; - const offset = FromManualToOffset(ref, peak); - return offset; -}; - -const msOffset = () => 0; - const extractAutoPeaks = (feature, thresSt, shiftSt, layoutSt, atIndex = 0) => { - const offset = (Format.isMsLayout(layoutSt) || Format.isLCMsLayout(layoutSt)) ? msOffset() : niOffset(shiftSt, atIndex); + const offset = (Format.isMsLayout(layoutSt) || Format.isLCMsLayout(layoutSt)) ? 0 : shiftOffsetAtIndex(shiftSt, atIndex); const peaks = Convert2Peak(feature, thresSt.value, offset); return peaks; }; diff --git a/src/helpers/format.js b/src/helpers/format.js index 049cb08a..9f010a70 100644 --- a/src/helpers/format.js +++ b/src/helpers/format.js @@ -35,7 +35,7 @@ const spectraDigit = (layout) => { }; const fixDigit = (input, precision) => { - const output = input || 0.0; + const output = Number(input) || 0.0; return output.toFixed(precision); }; @@ -111,11 +111,16 @@ const spectraOps = { [LIST_LAYOUT.LC_MS]: { head: 'HPLC UV/VIS', tail: '' }, }; +const shiftAnchorX = (selectedShift) => { + if (!selectedShift) return null; + const x = selectedShift.ref?.value ?? selectedShift.peak?.x; + return x == null ? null : x; +}; + const rmRef = (peaks, shift, atIndex = 0) => { - if (!shift) return peaks; - const { shifts } = shift; - const selectedShift = shifts[atIndex]; - const refValue = selectedShift.ref.value || selectedShift.peak.x; + if (!shift?.shifts) return peaks; + const refValue = shiftAnchorX(shift.shifts[atIndex]); + if (refValue == null) return peaks; return peaks.map( (p) => (IsSame(p.x, refValue) ? null : p), ).filter((r) => r != null); @@ -425,37 +430,117 @@ const formatedDLSIntensity = ( return ordered.map((o) => `${o.x} nm (${o.y} %)`).join(', '); }; +const HPLC_RT_DECIMAL = 2; +const HPLC_VALUE_DECIMAL = 2; +const HPLC_RT_UNIT = 'min'; + +const formatHplcRetentionTime = (rt) => ( + `${fixDigit(rt, HPLC_RT_DECIMAL)} ${HPLC_RT_UNIT}` +); + +const formatHplcAuc = (area) => fixDigit(area, HPLC_VALUE_DECIMAL); + +const findHplcPeakRetentionTime = (feature, xL, xU) => { + if (!feature?.data?.[0]) { + return (xL + xU) / 2; + } + const { x, y } = feature.data[0]; + let maxY = -Infinity; + let rt = (xL + xU) / 2; + for (let i = 0; i < x.length; i += 1) { + if (x[i] >= xL && x[i] <= xU && y[i] > maxY) { + maxY = y[i]; + rt = x[i]; + } + } + return rt; +}; + +const isSameHplcRt = (a, b) => ( + fixDigit(a, HPLC_RT_DECIMAL) === fixDigit(b, HPLC_RT_DECIMAL) +); + +const integrationApexRt = (inte, feature, peaks) => { + if (feature?.data?.[0]) { + return findHplcPeakRetentionTime(feature, inte.xL, inte.xU); + } + if (!Array.isArray(peaks)) return null; + const peaksInRange = peaks.filter((p) => p.x >= inte.xL && p.x <= inte.xU); + if (peaksInRange.length === 0) return null; + return peaksInRange.reduce( + (best, p) => (p.y > best.y ? p : best), + peaksInRange[0], + ).x; +}; + +const findIntegrationForPeak = (peakX, stack, peaks, feature) => { + if (!Array.isArray(stack)) return null; + const x = parseFloat(peakX); + return stack.find((s) => { + if (s.xL > x || x > s.xU) return false; + const apexRt = integrationApexRt(s, feature, peaks); + return apexRt != null && isSameHplcRt(x, apexRt); + }) || null; +}; + +const formatHplcAucPanel = (integration, feature) => { + if (!integration) return ''; + const stackIntegration = integration.stack; + if (!Array.isArray(stackIntegration)) return ''; + + const entries = stackIntegration + .filter((inte) => inte.absoluteArea) + .map((inte) => ({ + rt: findHplcPeakRetentionTime(feature, inte.xL, inte.xU), + area: inte.absoluteArea, + })); + + if (entries.length === 0) return ''; + + const sumVal = entries.reduce((sum, e) => sum + e.area, 0); + + return entries + .map((e) => { + const areaVal = formatHplcAuc(e.area); + const percent = fixDigit((e.area * 100) / sumVal, HPLC_VALUE_DECIMAL); + return `${formatHplcRetentionTime(e.rt)}, AUC=${areaVal} (${percent}%)`; + }) + .join(', '); +}; + const formatedHplcUvVis = ( - peaks, decimal = 2, integration, + peaks, decimal = 2, integration, feature, isAscend = false, ) => { let stack = []; if (integration) { stack = integration.stack; } - let ordered = {}; + let bestByX = {}; peaks.forEach((p) => { - const x = fixDigit(p.x, decimal); - const better = !ordered[x] || (p.y > ordered[x]); + const x = Number(p.x); + const better = bestByX[x] === undefined || (p.y > bestByX[x]); if (better) { - ordered = Object.assign({}, ordered, { [x]: p.y }); + bestByX = Object.assign({}, bestByX, { [x]: p.y }); } }); - ordered = Object.keys(ordered) - .map((k) => ({ x: k, y: ordered[k] })); + const sortFunc = isAscend + ? (a, b) => a.x - b.x + : (a, b) => b.x - a.x; + const ordered = Object.keys(bestByX) + .map((k) => ({ x: parseFloat(k), y: bestByX[k] })) + .sort(sortFunc); const arrResult = []; ordered.forEach((o) => { - let pStr = `${o.x} (${o.y.toFixed(2)})`; - if (stack) { - stack.forEach((s) => { - if (s.xL <= o.x && s.xU >= o.x) { - pStr = `${o.x} (${o.y.toFixed(2)}, AUC=${s.absoluteArea})`; - } - }); - } + const rt = formatHplcRetentionTime(o.x); + const yVal = o.y.toFixed(HPLC_VALUE_DECIMAL); + const inte = findIntegrationForPeak(o.x, stack, peaks, feature); + const pStr = inte?.absoluteArea + ? `${rt} (${yVal}, AUC=${formatHplcAuc(inte.absoluteArea)})` + : `${rt} (${yVal})`; arrResult.push(pStr); }); @@ -488,13 +573,9 @@ const formatedXRD = ( const rmShiftFromPeaks = (peaks, shift, atIndex = 0) => { const peaksXY = ToXY(peaks); - const { shifts } = shift; - const selectedShift = shifts[atIndex]; - if (!selectedShift) { - return peaks; - } - // const digit = spectraDigit(layout); - const rmShiftX = selectedShift.ref.value || selectedShift.peak.x; + if (!shift?.shifts) return peaks; + const rmShiftX = shiftAnchorX(shift.shifts[atIndex]); + if (rmShiftX == null) return peaks; const result = peaksXY.map((p) => { const srcX = parseFloat(p[0]); const x = IsSame(srcX, rmShiftX) ? null : srcX; @@ -508,7 +589,7 @@ const rmShiftFromPeaks = (peaks, shift, atIndex = 0) => { const peaksBody = ({ peaks, layout, decimal, shift, isAscend, isIntensity = false, boundary = {}, - integration, atIndex = 0, waveLength, temperature, hplcMsSt = null, + integration, atIndex = 0, waveLength, temperature, hplcMsSt = null, feature, }) => { const result = rmShiftFromPeaks(peaks, shift, atIndex); @@ -531,7 +612,7 @@ const peaksBody = ({ return formatedUvVis(ordered, maxY, decimal, isAscend, isIntensity, boundary, false); } if (layout === LIST_LAYOUT.HPLC_UVVIS) { - return formatedHplcUvVis(ordered, decimal, integration); + return formatedHplcUvVis(ordered, decimal, integration, feature, isAscend); } if (layout === LIST_LAYOUT.EMISSIONS) { return formatedEmissions(ordered, maxY, decimal, isAscend, isIntensity, boundary, false); @@ -556,9 +637,8 @@ const peaksBody = ({ const peaksWrapper = (layout, shift, atIndex = 0) => { let solvTxt = ''; - const { shifts } = shift; - const selectedShift = shifts[atIndex]; - if (selectedShift.ref.label) { + const selectedShift = shift?.shifts ? shift.shifts[atIndex] : shift; + if (selectedShift?.ref?.label) { solvTxt = ` (${selectedShift.ref.label})`; } @@ -793,6 +873,8 @@ const Format = { strNumberFixedLength, inlineNotation, formatedLCMS, + formatHplcAucPanel, + formatedHplcUvVis, }; export default Format; diff --git a/src/helpers/shift.js b/src/helpers/shift.js index 2baaf83a..01af80f5 100644 --- a/src/helpers/shift.js +++ b/src/helpers/shift.js @@ -1,7 +1,31 @@ /* eslint-disable prefer-object-spread, default-param-last */ -import { LIST_SHIFT_13C } from '../constants/list_shift'; +import { LIST_SHIFT_1H } from '../constants/list_shift'; -const shiftNone = LIST_SHIFT_13C[0]; +const shiftNone = LIST_SHIFT_1H[0]; + +const defaultEmptyShift = { + ref: shiftNone, + peak: false, + enable: true, +}; + +const listEntryAtIndex = (list, index, fallback) => ( + Array.isArray(list) && list[index] !== undefined ? list[index] : fallback +); + +const shiftEntryAtIndex = (shiftSt, atIndex = 0) => ( + listEntryAtIndex(shiftSt?.shifts, atIndex, defaultEmptyShift) +); + +const normalizeShiftForFormatting = (shift, atIndex = 0) => { + if (shift?.shifts) { + return { shift, atIndex }; + } + return { + shift: { shifts: [shift || defaultEmptyShift] }, + atIndex: 0, + }; +}; const FromManualToOffset = (ref, peak) => { if (!peak || ref.name === shiftNone.name) return 0; @@ -40,6 +64,19 @@ const RealPts = (pts, resX) => ( ) ); +const shiftOffsetAtIndex = (shiftSt, atIndex = 0) => { + const { ref, peak } = shiftEntryAtIndex(shiftSt, atIndex); + return FromManualToOffset(ref, peak); +}; + export { - FromManualToOffset, CalcResidualX, VirtalPts, RealPts, + defaultEmptyShift, + listEntryAtIndex, + shiftEntryAtIndex, + normalizeShiftForFormatting, + FromManualToOffset, + CalcResidualX, + VirtalPts, + RealPts, + shiftOffsetAtIndex, }; diff --git a/src/index.js b/src/index.js index b789a2b4..4d7d76f0 100644 --- a/src/index.js +++ b/src/index.js @@ -11,6 +11,7 @@ import ReactQuill from 'react-quill'; import { SpectraEditor, FN, store } from './app'; import { getLcMsInfo } from './helpers/extractEntityLCMS'; import { snapRtToAxis } from './reducers/reducer_hplc_ms/utils'; +import { normalizeShiftForFormatting } from './helpers/shift'; import nmr1HJcamp from './__tests__/fixtures/nmr1h_jcamp'; import nmr1H2Jcamp from './__tests__/fixtures/nmr1h_2_jcamp'; import nmr13CDeptJcamp from './__tests__/fixtures/nmr13c_dept_jcamp'; @@ -70,14 +71,6 @@ const pickSelectedSpectrumFromPayload = (payload) => { return spectraList[selectedIdx] || spectraList[0] || {}; }; -const normalizeShiftForFormatting = (shift) => { - if (shift && Array.isArray(shift.shifts)) return shift; - return { - selectedIdx: 0, - shifts: [shift || { ref: {}, peak: false, enable: true }], - }; -}; - const nmr1HEntity = FN.ExtractJcamp(nmr1HJcamp); const nmr1HEntity2 = FN.ExtractJcamp(nmr1H2Jcamp); const nmr13CEntity = FN.ExtractJcamp(nmr13CJcamp); diff --git a/src/layer_init.js b/src/layer_init.js index f9804c5a..fdd99c25 100644 --- a/src/layer_init.js +++ b/src/layer_init.js @@ -21,6 +21,12 @@ import MultiJcampsViewer from './components/multi_jcamps_viewer'; import HPLCViewer from './components/hplc_viewer'; import { setAllCurves } from './actions/curve'; import { clearHplcMsState } from './actions/hplc_ms'; +import { restoreSweepExtent } from './actions/ui'; + +const hasActiveZoom = (sweepExtent) => { + if (!sweepExtent) return false; + return !!(sweepExtent.xExtent || sweepExtent.yExtent); +}; const styles = () => ({ }); @@ -44,6 +50,15 @@ class LayerInit extends React.Component { constructor(props) { super(props); + const { sweepExtent, restoreSweepExtentAct } = props; + if (hasActiveZoom(sweepExtent)) { + restoreSweepExtentAct(sweepExtent); + } else { + // Clear any zoom left in the shared store by a previously opened + // spectrum, so it cannot block the RESETALL/layout sync on mount. + restoreSweepExtentAct({ xExtent: false, yExtent: false }); + } + this.normChange = this.normChange.bind(this); this.execReset = this.execReset.bind(this); this.initReducer = this.initReducer.bind(this); @@ -55,7 +70,7 @@ class LayerInit extends React.Component { this.execReset(); this.initReducer(); this.updateOthers(); - this.updateMultiEntities(); + this.updateMultiEntities(true); } componentDidUpdate(prevProps) { @@ -71,7 +86,7 @@ class LayerInit extends React.Component { } if (prevProps.multiEntities !== multiEntities || prevProps.entity !== entity) { - this.updateMultiEntities(); + this.updateMultiEntities(false); } } @@ -96,17 +111,20 @@ class LayerInit extends React.Component { execReset() { const { - entity, updateMetaPeaksAct, + entity, updateMetaPeaksAct, updateLayoutAct, resetInitCommonAct, resetInitMsAct, resetInitNmrAct, resetInitCommonWithIntergationAct, - resetDetectorAct, updateDSCMetaDataAct, resetMultiplicityAct, updateLayoutAct, + resetDetectorAct, updateDSCMetaDataAct, resetMultiplicityAct, } = this.props; if (!entity || !entity.layout) return; resetInitCommonAct(); resetDetectorAct(); const { layout, features = {} } = entity; + // Sync the layout deterministically on every spectrum open. Otherwise it + // only updates via the d3 RESETALL, which can be skipped (active zoom or + // same multi-comparison curve count), leaving a stale layout from the + // previously opened spectrum. updateLayoutAct(layout); if (Format.isMsLayout(layout)) { - // const { autoPeak, editPeak } = features; // TBD const autoPeak = features.autoPeak || features[0]; const editPeak = features.editPeak || features[0]; const baseFeat = editPeak || autoPeak; @@ -126,7 +144,8 @@ class LayerInit extends React.Component { } else if (Format.isDSCLayout(layout)) { const { dscMetaData } = features; updateDSCMetaDataAct(dscMetaData); - } else { + } + if (!Format.isNmrLayout(layout)) { resetMultiplicityAct(); } } @@ -145,7 +164,15 @@ class LayerInit extends React.Component { } } - updateMultiEntities() { + buildSetAllCurvesPayload(entities, isInitial) { + const { curveIdx } = this.props; + if (isInitial && Number.isFinite(curveIdx)) { + return { entities, curveIdx }; + } + return entities; + } + + updateMultiEntities(isInitial = false) { const { multiEntities, setAllCurvesAct, entity } = this.props; if (!entity || !entity.layout) return; const lcmsCurveMeta = () => { @@ -182,23 +209,23 @@ class LayerInit extends React.Component { const isMultiSpectra = Array.isArray(multiEntities) && multiEntities.length > 1; if (isMultiSpectra) { const meta = Format.isLCMsLayout(entity.layout) ? lcmsCurveMeta() : undefined; - setAllCurvesAct(multiEntities, meta); + setAllCurvesAct(this.buildSetAllCurvesPayload(multiEntities, isInitial), meta); return; } if (Format.isLCMsLayout(entity.layout)) { - const payload = (Array.isArray(multiEntities) && multiEntities.length > 0) + const entities = (Array.isArray(multiEntities) && multiEntities.length > 0) ? multiEntities : [entity]; - setAllCurvesAct(payload, lcmsCurveMeta()); + setAllCurvesAct(this.buildSetAllCurvesPayload(entities, isInitial), lcmsCurveMeta()); return; } if (Format.isCyclicVoltaLayout(entity.layout)) { - const payload = (Array.isArray(multiEntities) && multiEntities.length > 0) + const entities = (Array.isArray(multiEntities) && multiEntities.length > 0) ? multiEntities : [entity]; - setAllCurvesAct(payload); + setAllCurvesAct(this.buildSetAllCurvesPayload(entities, isInitial)); return; } @@ -318,13 +345,23 @@ const mapDispatchToProps = (dispatch) => ( setAllCurvesAct: setAllCurves, updateDSCMetaDataAct: updateDSCMetaData, clearHplcMsStateAct: clearHplcMsState, + restoreSweepExtentAct: restoreSweepExtent, }, dispatch) ); +LayerInit.defaultProps = { + multiEntities: undefined, + curveIdx: undefined, + sweepExtent: undefined, +}; + LayerInit.propTypes = { entity: PropTypes.object.isRequired, multiEntities: PropTypes.array, // eslint-disable-line + curveIdx: PropTypes.number, + sweepExtent: PropTypes.object, entityFileNames: PropTypes.array, // eslint-disable-line + restoreSweepExtentAct: PropTypes.func.isRequired, others: PropTypes.object.isRequired, cLabel: PropTypes.string.isRequired, xLabel: PropTypes.string.isRequired, diff --git a/src/reducers/reducer_curve.js b/src/reducers/reducer_curve.js index 099c95bf..3d49e08c 100644 --- a/src/reducers/reducer_curve.js +++ b/src/reducers/reducer_curve.js @@ -13,8 +13,30 @@ const initialState = { isShowAllCurve: false, }; +const normalizeSetAllCurvesPayload = (payload) => { + if (Array.isArray(payload)) { + return { entities: payload, curveIdx: undefined }; + } + if (payload && Array.isArray(payload.entities)) { + return { entities: payload.entities, curveIdx: payload.curveIdx }; + } + return { entities: null, curveIdx: undefined }; +}; + +const resolveCurveIdx = (entitiesLength, state, explicitIdx) => { + if (Number.isFinite(explicitIdx)) { + const maxIdx = Math.max(0, entitiesLength - 1); + return Math.min(Math.max(0, explicitIdx), maxIdx); + } + if (state.curveIdx >= 0 && state.curveIdx < entitiesLength) { + return state.curveIdx; + } + return 0; +}; + const setAllCurves = (state, action) => { - const { payload } = action; + const { entities: payloadEntities, curveIdx: explicitIdx } = normalizeSetAllCurvesPayload(action.payload); + const payload = payloadEntities ?? action.payload; if (!payload) return { ...state, curveIdx: 0, listCurves: [] }; const isLcmsGroup = isLcMsGroup(payload); @@ -55,12 +77,11 @@ const setAllCurves = (state, action) => { }; }); - const maxIdx = entities.length - 1; - const safeCurveIdx = Math.min(state.curveIdx || 0, maxIdx); + const curveIdx = resolveCurveIdx(entities.length, state, explicitIdx); return { ...state, - curveIdx: safeCurveIdx, + curveIdx, listCurves: entities, }; }; diff --git a/src/reducers/reducer_edit_peak.js b/src/reducers/reducer_edit_peak.js index 45079f7c..d3fbd43a 100644 --- a/src/reducers/reducer_edit_peak.js +++ b/src/reducers/reducer_edit_peak.js @@ -1,6 +1,6 @@ /* eslint-disable prefer-object-spread, default-param-last */ import undoable from 'redux-undo'; -import { EDITPEAK, MANAGER } from '../constants/action_type'; +import { EDITPEAK, MANAGER, CURVE } from '../constants/action_type'; import { undoRedoConfig, undoRedoActions } from './undo_redo_config'; import { almostEqual } from '../helpers/calc'; @@ -170,6 +170,8 @@ const editPeakReducer = (state = initialState, action) => { return processShift(state, action); case EDITPEAK.CLEAR_ALL: return clearAllPeaks(state, action); + case CURVE.SELECT_WORKING_CURVE: + return Object.assign({}, state, { selectedIdx: action.payload }); case MANAGER.RESETALL: return { selectedIdx: 0, diff --git a/src/reducers/reducer_integration.js b/src/reducers/reducer_integration.js index 22fd0667..992e2b30 100644 --- a/src/reducers/reducer_integration.js +++ b/src/reducers/reducer_integration.js @@ -1,7 +1,7 @@ /* eslint-disable prefer-object-spread, default-param-last */ import undoable from 'redux-undo'; import { - UI, INTEGRATION, EDITPEAK, MANAGER, + UI, INTEGRATION, EDITPEAK, MANAGER, CURVE, } from '../constants/action_type'; import { generateVisualSplitGroupId, @@ -455,15 +455,18 @@ const setFkr = (state, action) => { }; const setShift = (state, action) => { - const { selectedIdx, integrations } = state; - const selectedIntegration = integrations[selectedIdx]; - - const shift = action.payload.prevOffset; + const { integrations } = state; + const { prevOffset, curveIdx } = action.payload; + const targetIdx = Number.isFinite(curveIdx) ? curveIdx : state.selectedIdx; + let selectedIntegration = integrations[targetIdx]; + if (selectedIntegration === false || selectedIntegration === undefined) { + selectedIntegration = defaultEmptyIntegration; + } - const newIntegration = Object.assign({}, selectedIntegration, { shift }); + const newIntegration = Object.assign({}, selectedIntegration, { shift: prevOffset }); const newArrIntegration = [...integrations]; - newArrIntegration[selectedIdx] = newIntegration; - return Object.assign({}, state, { integrations: newArrIntegration }); + newArrIntegration[targetIdx] = newIntegration; + return Object.assign({}, state, { integrations: newArrIntegration, selectedIdx: targetIdx }); }; const resetAll = (state, action) => { @@ -504,6 +507,8 @@ const integrationReducer = (state = initialState, action) => { return clearAll(state, action); case EDITPEAK.SHIFT: return setShift(state, action); + case CURVE.SELECT_WORKING_CURVE: + return Object.assign({}, state, { selectedIdx: action.payload }); case MANAGER.RESETALL: return state; default: diff --git a/src/reducers/reducer_multiplicity.js b/src/reducers/reducer_multiplicity.js index d8195147..f531f809 100644 --- a/src/reducers/reducer_multiplicity.js +++ b/src/reducers/reducer_multiplicity.js @@ -1,7 +1,7 @@ /* eslint-disable prefer-object-spread, default-param-last */ import undoable from 'redux-undo'; import { - UI, EDITPEAK, INTEGRATION, MULTIPLICITY, MANAGER, + UI, EDITPEAK, INTEGRATION, MULTIPLICITY, MANAGER, CURVE, } from '../constants/action_type'; import { undoRedoConfig, undoRedoActions } from './undo_redo_config'; @@ -26,15 +26,19 @@ const defaultEmptyMultiplicity = { }; const setShift = (state, action) => { - const shift = action.payload.prevOffset; + const { prevOffset, curveIdx } = action.payload; + const targetIdx = Number.isFinite(curveIdx) ? curveIdx : state.selectedIdx; - const { selectedIdx, multiplicities } = state; - const selectedMulti = multiplicities[selectedIdx]; + const { multiplicities } = state; + let selectedMulti = multiplicities[targetIdx]; + if (selectedMulti === false || selectedMulti === undefined) { + selectedMulti = defaultEmptyMultiplicity; + } - const newSelectedMulti = Object.assign({}, selectedMulti, { shift }); + const newSelectedMulti = Object.assign({}, selectedMulti, { shift: prevOffset }); const newMultiplicities = [...multiplicities]; - newMultiplicities[selectedIdx] = newSelectedMulti; - return Object.assign({}, state, { multiplicities: newMultiplicities }); + newMultiplicities[targetIdx] = newSelectedMulti; + return Object.assign({}, state, { multiplicities: newMultiplicities, selectedIdx: targetIdx }); }; const rmFromStack = (state, action) => { @@ -127,6 +131,8 @@ const multiplicityReducer = (state = initialState, action) => { switch (action.type) { case EDITPEAK.SHIFT: return setShift(state, action); + case CURVE.SELECT_WORKING_CURVE: + return Object.assign({}, state, { selectedIdx: action.payload }); case INTEGRATION.RM_ONE: return rmFromStack(state, action); case UI.SWEEP.SELECT_MULTIPLICITY_RDC: diff --git a/src/reducers/reducer_shift.js b/src/reducers/reducer_shift.js index ad1446c5..57924a68 100644 --- a/src/reducers/reducer_shift.js +++ b/src/reducers/reducer_shift.js @@ -1,9 +1,9 @@ /* eslint-disable prefer-object-spread, default-param-last */ import { - SHIFT, EDITPEAK, MANAGER, LAYOUT, + SHIFT, EDITPEAK, MANAGER, LAYOUT, CURVE, } from '../constants/action_type'; import { getListShift, LIST_SHIFT_1H } from '../constants/list_shift'; -import { CalcResidualX, RealPts } from '../helpers/shift'; +import { CalcResidualX, RealPts, defaultEmptyShift } from '../helpers/shift'; const shiftNone = LIST_SHIFT_1H[0]; const normalizeShiftName = (input) => ( @@ -31,12 +31,6 @@ const initialState = { ], }; -const defaultEmptyShift = { - ref: shiftNone, - peak: false, - enable: true, -}; - const resetRef = (payload) => { const { shift, layout } = payload; if (!shift || !shift.solventName || !shift.solventValue) return shiftNone; @@ -68,7 +62,7 @@ const resetShift = (state, action) => { const { curvesInfo } = payload; const { isMultiCurve, curveIdx, numberOfCurve } = curvesInfo; - const { shifts } = state; + const shifts = [...state.shifts]; let selectedShift = shifts[curveIdx]; if (selectedShift === false || selectedShift === undefined) { selectedShift = defaultEmptyShift; @@ -238,7 +232,9 @@ const shiftReducer = (state = initialState, action) => { case LAYOUT.UPDATE: return updateShift(initialState, action); case MANAGER.RESETSHIFT: // case MANAGER.RESETALL: - return resetShift(initialState, action); + return resetShift(state, action); + case CURVE.SELECT_WORKING_CURVE: + return Object.assign({}, state, { selectedIdx: action.payload }); default: return state; } diff --git a/src/reducers/reducer_threshold.js b/src/reducers/reducer_threshold.js index 9755c67d..b62b6d72 100644 --- a/src/reducers/reducer_threshold.js +++ b/src/reducers/reducer_threshold.js @@ -13,12 +13,12 @@ const initialState = { ], }; -// const defaultThresHold = { -// isEdit: true, -// value: false, -// upper: false, -// lower: false, -// }; +const defaultThresHold = { + isEdit: true, + value: false, + upper: false, + lower: false, +}; const setThresHoldValue = (state, action) => { const { payload } = action; @@ -77,19 +77,34 @@ const setThresHoldLower = (state, action) => { return Object.assign({}, state, { list: newListThres }); }; -const setThresHoldIsEdit = (state) => { +const setThresHoldIsEdit = (state, action) => { + const { payload } = action; const { list, selectedIdx } = state; - const selectedThres = list[selectedIdx]; + const curveIdx = payload && payload.curveIdx !== undefined ? payload.curveIdx : selectedIdx; + const selectedThres = list[curveIdx] || defaultThresHold; const { isEdit } = selectedThres; const newSelectedThres = Object.assign({}, selectedThres, { isEdit: !isEdit }); const newListThres = [...list]; - newListThres[selectedIdx] = newSelectedThres; - return Object.assign({}, state, { list: newListThres }); + newListThres[curveIdx] = newSelectedThres; + return Object.assign({}, state, { list: newListThres, selectedIdx: curveIdx }); }; const resetAll = (state, action) => { const { payload } = action; const { list } = state; + if (payload && payload.curveIdx !== undefined) { + const { curveIdx } = payload; + const selectedThres = list[curveIdx] || defaultThresHold; + const newSelectedThres = Object.assign( + {}, + selectedThres, + { value: payload.thresRef }, + ); + const newListThres = [...list]; + newListThres[curveIdx] = newSelectedThres; + return Object.assign({}, state, { selectedIdx: curveIdx, list: newListThres }); + } + const newList = list.map((item) => ( { isEdit: item.isEdit, @@ -149,6 +164,8 @@ const thresholdReducer = (state = initialState, action) => { switch (action.type) { case CURVE.SET_ALL_CURVES: return setListThreshold(state, action); + case CURVE.SELECT_WORKING_CURVE: + return Object.assign({}, state, { selectedIdx: action.payload }); case THRESHOLD.UPDATE_VALUE: return setThresHoldValue(state, action); case THRESHOLD.UPDATE_UPPER_VALUE: @@ -158,7 +175,7 @@ const thresholdReducer = (state = initialState, action) => { case THRESHOLD.RESET_VALUE: return setThresHoldValue(state, action); case THRESHOLD.TOGGLE_ISEDIT: - return setThresHoldIsEdit(state); + return setThresHoldIsEdit(state, action); case MANAGER.RESET_INIT_COMMON: return resetInitCommon(state); case MANAGER.RESETALL: diff --git a/src/sagas/saga_manager.js b/src/sagas/saga_manager.js index 9ac6e748..17ab5aab 100644 --- a/src/sagas/saga_manager.js +++ b/src/sagas/saga_manager.js @@ -8,6 +8,14 @@ const getLayout = (state) => state.layout; const getCurveSt = (state) => state.curve; const getIntegrationSt = (state) => state.integration.present; +const defaultEmptyIntegration = { + stack: [], + refArea: 1, + refFactor: 1, + shift: 0, + edited: false, +}; + function* resetShift(action) { const curveSt = yield select(getCurveSt); const layout = yield select(getLayout); @@ -16,7 +24,7 @@ function* resetShift(action) { const { curveIdx, listCurves } = curveSt; - const numberOfCurve = listCurves.length; + const numberOfCurve = Array.isArray(listCurves) ? listCurves.length : 0; yield put({ type: MANAGER.RESETSHIFT, @@ -41,18 +49,17 @@ function* resetInitNmr(action) { const { curveIdx } = curveSt; const { integration, simulation } = action.payload; - const { integrations } = integationSt; - const newArrIntegration = [...integrations]; - newArrIntegration[curveIdx] = integration; + // Always reset: keeping the previous spectrum's integrations would + // display stale data when the new entity has none saved. + const newArrIntegration = new Array(curveIdx + 1).fill(defaultEmptyIntegration); + newArrIntegration[curveIdx] = integration || defaultEmptyIntegration; const payload = Object.assign({}, integationSt, { integrations: newArrIntegration, selectedIdx: curveIdx }); // eslint-disable-line - if (integration) { - yield put({ - type: INTEGRATION.RESET_ALL_RDC, - payload, - }); - } + yield put({ + type: INTEGRATION.RESET_ALL_RDC, + payload, + }); if (simulation) { yield put({ type: SIMULATION.RESET_ALL_RDC, @@ -68,18 +75,15 @@ function* resetInitCommonWithIntergation(action) { const { integration } = action.payload; - const { integrations } = integationSt; - const newArrIntegration = [...integrations]; - newArrIntegration[curveIdx] = integration; + const newArrIntegration = new Array(curveIdx + 1).fill(defaultEmptyIntegration); + newArrIntegration[curveIdx] = integration || defaultEmptyIntegration; const payload = Object.assign({}, integationSt, { integrations: newArrIntegration, selectedIdx: curveIdx }); // eslint-disable-line - if (integration) { - yield put({ - type: INTEGRATION.RESET_ALL_RDC, - payload, - }); - } + yield put({ + type: INTEGRATION.RESET_ALL_RDC, + payload, + }); } const managerSagas = [ diff --git a/src/sagas/saga_multi_entities.js b/src/sagas/saga_multi_entities.js index 3a72db3a..e2d3a7f6 100644 --- a/src/sagas/saga_multi_entities.js +++ b/src/sagas/saga_multi_entities.js @@ -2,7 +2,7 @@ import { put, takeEvery, select } from 'redux-saga/effects'; import { - CURVE, CYCLIC_VOLTA_METRY, INTEGRATION, SIMULATION, MULTIPLICITY, + CURVE, CYCLIC_VOLTA_METRY, INTEGRATION, SIMULATION, MULTIPLICITY, MANAGER, } from '../constants/action_type'; import { LIST_LAYOUT } from '../constants/list_layout'; @@ -121,6 +121,34 @@ function* setCyclicVoltametryRef(action) { // eslint-disable-line })); } +function* setInitShifts() { + const curveSt = yield select(getCurveSt); + const { listCurves } = curveSt; + if (!listCurves || listCurves.length <= 1) { + return; + } + + const layoutSt = yield select(getLayoutSt); + const numberOfCurve = listCurves.length; + for (let index = 0; index < listCurves.length; index += 1) { + const { feature } = listCurves[index]; + if (feature) { + yield put({ + type: MANAGER.RESETSHIFT, + payload: { + ...feature, + layout: layoutSt, + curvesInfo: { + isMultiCurve: true, + curveIdx: index, + numberOfCurve, + }, + }, + }); + } + } +} + function* setInitIntegrations(action) { // eslint-disable-line const curveSt = yield select(getCurveSt); const { listCurves } = curveSt; @@ -173,6 +201,7 @@ function* setInitIntegrations(action) { // eslint-disable-line const multiEntitiesSagas = [ takeEvery(CURVE.SET_ALL_CURVES, setCyclicVoltametry), + takeEvery(CURVE.SET_ALL_CURVES, setInitShifts), takeEvery(CURVE.SET_ALL_CURVES, setInitIntegrations), takeEvery(CYCLIC_VOLTA_METRY.SET_FACTOR, setCyclicVoltametryRef), ]; diff --git a/src/sagas/saga_multiplicity.js b/src/sagas/saga_multiplicity.js index d70a0446..b32bdcdf 100644 --- a/src/sagas/saga_multiplicity.js +++ b/src/sagas/saga_multiplicity.js @@ -9,6 +9,13 @@ const getCurveSt = (state) => state.curve; const getMultiplicitySt = (state) => state.multiplicity.present; +const defaultEmptyMultiplicity = { + stack: [], + shift: 0, + smExtext: false, + edited: false, +}; + function* selectMpy(action) { const metaSt = yield select(getMetaSt); const mpySt = yield select(getMultiplicitySt); @@ -201,26 +208,16 @@ function* resetInitNmr(action) { const mpySt = yield select(getMultiplicitySt); const { curveIdx } = curveSt; - const { multiplicities } = mpySt; - const newMultiplicities = [...multiplicities]; - newMultiplicities[curveIdx] = multiplicity; + // Always reset: keeping the previous spectrum's multiplicities would + // display stale data when the new entity has none saved. + const newMultiplicities = new Array(curveIdx + 1).fill(defaultEmptyMultiplicity); + newMultiplicities[curveIdx] = multiplicity || defaultEmptyMultiplicity; const payload = Object.assign({}, mpySt, { multiplicities: newMultiplicities, selectedIdx: curveIdx }); // eslint-disable-line - if (multiplicity) { - yield put({ - type: MULTIPLICITY.RESET_ALL_RDC, - payload, - }); - } - // const metaSt = yield select(getMetaSt); - // const mpySt = yield select(getMultiplicitySt); - - // if (!multiplicity) { - // yield put({ - // type: MULTIPLICITY.RESET_ALL_RDC, - // payload: mpySt, - // }); - // } + yield put({ + type: MULTIPLICITY.RESET_ALL_RDC, + payload, + }); // const { stack } = multiplicity; // const newStack = stack.map((k) => { diff --git a/src/sagas/saga_ui.js b/src/sagas/saga_ui.js index 8458dc86..25f0976c 100644 --- a/src/sagas/saga_ui.js +++ b/src/sagas/saga_ui.js @@ -168,12 +168,13 @@ export const shouldDisplayLcmsSubViewerAt = ({ function* clickUiTarget(action) { const { - payload, onPeak, voltammetryPeakIdx, onPecker, sourceHint, + payload, onPeak, voltammetryPeakIdx, onPecker, sourceHint, jcampIdx, } = action; const uiSweepType = yield select(getUiSweepType); const curveState = yield select(getCurveState); const { curveIdx } = curveState; + const targetCurveIdx = Number.isFinite(jcampIdx) ? jcampIdx : curveIdx; const hplcMsState = yield select(getHplcMsState); const { uvvis } = hplcMsState; @@ -208,7 +209,7 @@ function* clickUiTarget(action) { } else { yield put({ type: EDITPEAK.ADD_POSITIVE, - payload: { dataToAdd: payload, curveIdx }, + payload: { dataToAdd: payload, curveIdx: targetCurveIdx }, }); } } else if (uiSweepType === LIST_UI_SWEEP_TYPE.PEAK_DELETE && onPeak) { @@ -217,13 +218,13 @@ function* clickUiTarget(action) { } else { yield put({ type: EDITPEAK.ADD_NEGATIVE, - payload: { dataToAdd: payload, curveIdx }, + payload: { dataToAdd: payload, curveIdx: targetCurveIdx }, }); } } else if (uiSweepType === LIST_UI_SWEEP_TYPE.ANCHOR_SHIFT && onPeak) { yield put({ type: SHIFT.SET_PEAK, - payload: { dataToSet: payload, curveIdx }, + payload: { dataToSet: payload, curveIdx: targetCurveIdx }, }); } else if (uiSweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_RM && onPeak) { if (uvvis.selectedWaveLength && isLcmsLayout) { @@ -231,30 +232,30 @@ function* clickUiTarget(action) { } else { yield put({ type: INTEGRATION.RM_ONE, - payload: { dataToRemove: payload, curveIdx }, + payload: { dataToRemove: payload, curveIdx: targetCurveIdx }, }); } } else if (uiSweepType === LIST_UI_SWEEP_TYPE.MULTIPLICITY_ONE_RM && onPeak) { yield put({ type: INTEGRATION.RM_ONE, - payload: { dataToRemove: payload, curveIdx }, + payload: { dataToRemove: payload, curveIdx: targetCurveIdx }, }); } else if (uiSweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_SET_REF && onPeak) { yield put({ type: INTEGRATION.SET_REF, - payload: { refData: payload, curveIdx }, + payload: { refData: payload, curveIdx: targetCurveIdx }, }); } else if (uiSweepType === LIST_UI_SWEEP_TYPE.MULTIPLICITY_ONE_CLICK && onPeak) { const { xExtent, xL, xU } = payload; if (xExtent) { yield put({ type: MULTIPLICITY.ONE_CLICK_BY_UI, - payload: { payloadData: xExtent, curveIdx }, + payload: { payloadData: xExtent, curveIdx: targetCurveIdx }, }); } else if (xL && xU) { yield put({ type: MULTIPLICITY.ONE_CLICK_BY_UI, - payload: { payloadData: { xL, xU }, curveIdx }, + payload: { payloadData: { xL, xU }, curveIdx: targetCurveIdx }, }); } } else if (uiSweepType === LIST_UI_SWEEP_TYPE.MULTIPLICITY_PEAK_ADD) { @@ -270,37 +271,37 @@ function* clickUiTarget(action) { } else if (uiSweepType === LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MAX_PEAK && !onPeak) { yield put({ type: CYCLIC_VOLTA_METRY.ADD_MAX_PEAK, - payload: { peak: payload, index: voltammetryPeakIdx, jcampIdx: curveIdx }, + payload: { peak: payload, index: voltammetryPeakIdx, jcampIdx: targetCurveIdx }, }); } else if (uiSweepType === LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MAX_PEAK && onPeak) { yield put({ type: CYCLIC_VOLTA_METRY.REMOVE_MAX_PEAK, - payload: { index: voltammetryPeakIdx, jcampIdx: curveIdx }, + payload: { index: voltammetryPeakIdx, jcampIdx: targetCurveIdx }, }); } else if (uiSweepType === LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MIN_PEAK && !onPeak) { yield put({ type: CYCLIC_VOLTA_METRY.ADD_MIN_PEAK, - payload: { peak: payload, index: voltammetryPeakIdx, jcampIdx: curveIdx }, + payload: { peak: payload, index: voltammetryPeakIdx, jcampIdx: targetCurveIdx }, }); } else if (uiSweepType === LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MIN_PEAK && onPeak) { yield put({ type: CYCLIC_VOLTA_METRY.REMOVE_MIN_PEAK, - payload: { index: voltammetryPeakIdx, jcampIdx: curveIdx }, + payload: { index: voltammetryPeakIdx, jcampIdx: targetCurveIdx }, }); } else if (uiSweepType === LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_PECKER && !onPecker) { yield put({ type: CYCLIC_VOLTA_METRY.ADD_PECKER, - payload: { peak: payload, index: voltammetryPeakIdx, jcampIdx: curveIdx }, + payload: { peak: payload, index: voltammetryPeakIdx, jcampIdx: targetCurveIdx }, }); } else if (uiSweepType === LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_PECKER && onPecker) { yield put({ type: CYCLIC_VOLTA_METRY.REMOVE_PECKER, - payload: { index: voltammetryPeakIdx, jcampIdx: curveIdx }, + payload: { index: voltammetryPeakIdx, jcampIdx: targetCurveIdx }, }); } else if (uiSweepType === LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_SET_REF && onPeak) { yield put({ type: CYCLIC_VOLTA_METRY.SET_REF, - payload: { index: voltammetryPeakIdx, jcampIdx: curveIdx }, + payload: { index: voltammetryPeakIdx, jcampIdx: targetCurveIdx }, }); } else if (uiSweepType === LIST_UI_SWEEP_TYPE.PEAK_GROUP_SELECT) { yield put({