diff --git a/dist/actions/axes.js b/dist/actions/axes.js new file mode 100644 index 00000000..3cde6370 --- /dev/null +++ b/dist/actions/axes.js @@ -0,0 +1,19 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.updateYAxis = exports.updateXAxis = void 0; +var _action_type = require("../constants/action_type"); +const updateXAxis = payload => ({ + type: _action_type.AXES.UPDATE_X_AXIS, + payload +}); +exports.updateXAxis = updateXAxis; +const updateYAxis = payload => ({ + type: _action_type.AXES.UPDATE_Y_AXIS, + payload +}); + +// eslint-disable-line +exports.updateYAxis = updateYAxis; \ No newline at end of file diff --git a/dist/actions/curve.js b/dist/actions/curve.js new file mode 100644 index 00000000..85bd0062 --- /dev/null +++ b/dist/actions/curve.js @@ -0,0 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.toggleShowAllCurves = exports.setAllCurves = exports.selectCurve = void 0; +var _action_type = require("../constants/action_type"); +const selectCurve = payload => ({ + type: _action_type.CURVE.SELECT_WORKING_CURVE, + payload +}); +exports.selectCurve = selectCurve; +const setAllCurves = payload => ({ + type: _action_type.CURVE.SET_ALL_CURVES, + payload +}); +exports.setAllCurves = setAllCurves; +const toggleShowAllCurves = payload => ({ + type: _action_type.CURVE.SET_SHOULD_SHOW_ALL_CURVES, + payload +}); +exports.toggleShowAllCurves = toggleShowAllCurves; \ No newline at end of file diff --git a/dist/actions/cyclic_voltammetry.js b/dist/actions/cyclic_voltammetry.js new file mode 100644 index 00000000..a6e14b76 --- /dev/null +++ b/dist/actions/cyclic_voltammetry.js @@ -0,0 +1,91 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.toggleCyclicVoltaDensity = exports.setWorkWithMaxPeak = exports.setCylicVoltaRefFactor = exports.setCylicVoltaRef = exports.setCyclicVoltaAreaValue = exports.setCyclicVoltaAreaUnit = exports.selectRefPeaks = exports.selectPairPeak = exports.removeCylicVoltaPecker = exports.removeCylicVoltaPairPeak = exports.removeCylicVoltaMinPeak = exports.removeCylicVoltaMaxPeak = exports.addNewCylicVoltaPairPeak = exports.addCylicVoltaPecker = exports.addCylicVoltaMinPeak = exports.addCylicVoltaMaxPeak = void 0; +var _action_type = require("../constants/action_type"); +const addNewCylicVoltaPairPeak = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.ADD_PAIR_PEAKS, + payload +}); +exports.addNewCylicVoltaPairPeak = addNewCylicVoltaPairPeak; +const removeCylicVoltaPairPeak = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.REMOVE_PAIR_PEAKS, + payload +}); +exports.removeCylicVoltaPairPeak = removeCylicVoltaPairPeak; +const addCylicVoltaMaxPeak = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.ADD_MAX_PEAK, + payload +}); +exports.addCylicVoltaMaxPeak = addCylicVoltaMaxPeak; +const removeCylicVoltaMaxPeak = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.REMOVE_MAX_PEAK, + payload +}); +exports.removeCylicVoltaMaxPeak = removeCylicVoltaMaxPeak; +const addCylicVoltaMinPeak = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.ADD_MIN_PEAK, + payload +}); +exports.addCylicVoltaMinPeak = addCylicVoltaMinPeak; +const removeCylicVoltaMinPeak = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.REMOVE_MIN_PEAK, + payload +}); +exports.removeCylicVoltaMinPeak = removeCylicVoltaMinPeak; +const setWorkWithMaxPeak = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.WORK_WITH_MAX_PEAK, + payload +}); +exports.setWorkWithMaxPeak = setWorkWithMaxPeak; +const selectPairPeak = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.SELECT_PAIR_PEAK, + payload +}); +exports.selectPairPeak = selectPairPeak; +const addCylicVoltaPecker = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.ADD_PECKER, + payload +}); +exports.addCylicVoltaPecker = addCylicVoltaPecker; +const removeCylicVoltaPecker = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.REMOVE_PECKER, + payload +}); +exports.removeCylicVoltaPecker = removeCylicVoltaPecker; +const selectRefPeaks = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.SELECT_REF_PEAK, + payload +}); +exports.selectRefPeaks = selectRefPeaks; +const setCylicVoltaRefFactor = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.SET_FACTOR, + payload +}); +exports.setCylicVoltaRefFactor = setCylicVoltaRefFactor; +const setCylicVoltaRef = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.SET_REF, + payload +}); +exports.setCylicVoltaRef = setCylicVoltaRef; +const setCyclicVoltaAreaValue = value => ({ + type: _action_type.CYCLIC_VOLTA_METRY.SET_AREA_VALUE, + payload: { + value + } +}); +exports.setCyclicVoltaAreaValue = setCyclicVoltaAreaValue; +const setCyclicVoltaAreaUnit = unit => ({ + type: _action_type.CYCLIC_VOLTA_METRY.SET_AREA_UNIT, + payload: { + unit + } +}); +exports.setCyclicVoltaAreaUnit = setCyclicVoltaAreaUnit; +const toggleCyclicVoltaDensity = payload => ({ + type: _action_type.CYCLIC_VOLTA_METRY.TOGGLE_DENSITY, + payload +}); +exports.toggleCyclicVoltaDensity = toggleCyclicVoltaDensity; \ No newline at end of file diff --git a/dist/actions/detector.js b/dist/actions/detector.js new file mode 100644 index 00000000..4736ec21 --- /dev/null +++ b/dist/actions/detector.js @@ -0,0 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.updateDetector = void 0; +var _action_type = require("../constants/action_type"); +/* eslint-disable import/prefer-default-export */ + +const updateDetector = payload => ({ + type: _action_type.SEC.UPDATE_DETECTOR, + payload +}); +exports.updateDetector = updateDetector; \ No newline at end of file diff --git a/dist/actions/edit_peak.js b/dist/actions/edit_peak.js new file mode 100644 index 00000000..cbb80814 --- /dev/null +++ b/dist/actions/edit_peak.js @@ -0,0 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.rmFromPosList = exports.rmFromNegList = exports.clearAllPeaks = void 0; +var _action_type = require("../constants/action_type"); +const rmFromPosList = payload => ({ + type: _action_type.EDITPEAK.RM_POSITIVE, + payload +}); +exports.rmFromPosList = rmFromPosList; +const rmFromNegList = payload => ({ + type: _action_type.EDITPEAK.RM_NEGATIVE, + payload +}); +exports.rmFromNegList = rmFromNegList; +const clearAllPeaks = payload => ({ + type: _action_type.EDITPEAK.CLEAR_ALL, + payload +}); +exports.clearAllPeaks = clearAllPeaks; \ No newline at end of file diff --git a/dist/actions/forecast.js b/dist/actions/forecast.js new file mode 100644 index 00000000..6ef4cae6 --- /dev/null +++ b/dist/actions/forecast.js @@ -0,0 +1,27 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.setNmrStatus = exports.setIrStatus = exports.initForecastStatus = exports.clearForecastStatus = void 0; +var _action_type = require("../constants/action_type"); +const initForecastStatus = payload => ({ + type: _action_type.FORECAST.INIT_STATUS, + payload +}); +exports.initForecastStatus = initForecastStatus; +const setIrStatus = payload => ({ + type: _action_type.FORECAST.SET_IR_STATUS, + payload +}); +exports.setIrStatus = setIrStatus; +const setNmrStatus = payload => ({ + type: _action_type.FORECAST.SET_NMR_STATUS, + payload +}); +exports.setNmrStatus = setNmrStatus; +const clearForecastStatus = payload => ({ + type: _action_type.FORECAST.CLEAR_STATUS, + payload +}); +exports.clearForecastStatus = clearForecastStatus; \ No newline at end of file diff --git a/dist/actions/integration.js b/dist/actions/integration.js new file mode 100644 index 00000000..1366a30e --- /dev/null +++ b/dist/actions/integration.js @@ -0,0 +1,29 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.sweepIntegration = exports.splitIntegration = exports.setIntegrationFkr = exports.clearIntegrationAll = void 0; +var _action_type = require("../constants/action_type"); +const sweepIntegration = payload => ({ + type: _action_type.INTEGRATION.SWEEP, + payload +}); +exports.sweepIntegration = sweepIntegration; +const setIntegrationFkr = payload => ({ + type: _action_type.INTEGRATION.SET_FKR, + payload +}); +exports.setIntegrationFkr = setIntegrationFkr; +const clearIntegrationAll = payload => ({ + type: _action_type.INTEGRATION.CLEAR_ALL, + payload +}); +exports.clearIntegrationAll = clearIntegrationAll; +const splitIntegration = payload => ({ + type: _action_type.INTEGRATION.SPLIT, + payload +}); + +// eslint-disable-line +exports.splitIntegration = splitIntegration; \ No newline at end of file diff --git a/dist/actions/jcamp.js b/dist/actions/jcamp.js new file mode 100644 index 00000000..302fb464 --- /dev/null +++ b/dist/actions/jcamp.js @@ -0,0 +1,27 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.toggleShow = exports.rmOthersOne = exports.clearAll = exports.addOthers = void 0; +var _action_type = require("../constants/action_type"); +const addOthers = payload => ({ + type: _action_type.JCAMP.ADD_OTHERS, + payload +}); +exports.addOthers = addOthers; +const rmOthersOne = payload => ({ + type: _action_type.JCAMP.RM_OTHERS_ONE, + payload +}); +exports.rmOthersOne = rmOthersOne; +const toggleShow = payload => ({ + type: _action_type.JCAMP.TOGGLE_SHOW, + payload +}); +exports.toggleShow = toggleShow; +const clearAll = payload => ({ + type: _action_type.JCAMP.CLEAR_ALL, + payload +}); +exports.clearAll = clearAll; \ No newline at end of file diff --git a/dist/actions/layout.js b/dist/actions/layout.js new file mode 100644 index 00000000..7651a640 --- /dev/null +++ b/dist/actions/layout.js @@ -0,0 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.updateLayout = void 0; +var _action_type = require("../constants/action_type"); +const updateLayout = payload => ({ + type: _action_type.LAYOUT.UPDATE, + payload +}); + +// eslint-disable-line +exports.updateLayout = updateLayout; \ No newline at end of file diff --git a/dist/actions/manager.js b/dist/actions/manager.js new file mode 100644 index 00000000..f2effe9c --- /dev/null +++ b/dist/actions/manager.js @@ -0,0 +1,42 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.resetMultiplicity = exports.resetInitNmr = exports.resetInitMs = exports.resetInitCommonWithIntergation = exports.resetInitCommon = exports.resetDetector = exports.resetAll = void 0; +var _action_type = require("../constants/action_type"); +const resetAll = payload => ({ + type: _action_type.MANAGER.RESETALL, + payload +}); +exports.resetAll = resetAll; +const resetInitCommon = payload => ({ + type: _action_type.MANAGER.RESET_INIT_COMMON, + payload +}); +exports.resetInitCommon = resetInitCommon; +const resetInitNmr = payload => ({ + type: _action_type.MANAGER.RESET_INIT_NMR, + payload +}); +exports.resetInitNmr = resetInitNmr; +const resetInitMs = payload => ({ + type: _action_type.MANAGER.RESET_INIT_MS, + payload +}); +exports.resetInitMs = resetInitMs; +const resetInitCommonWithIntergation = payload => ({ + type: _action_type.MANAGER.RESET_INIT_COMMON_WITH_INTERGATION, + payload +}); +exports.resetInitCommonWithIntergation = resetInitCommonWithIntergation; +const resetDetector = () => ({ + type: _action_type.MANAGER.RESET_DETECTOR +}); +exports.resetDetector = resetDetector; +const resetMultiplicity = () => ({ + type: _action_type.MANAGER.RESET_MULTIPLICITY +}); + +// eslint-disable-line +exports.resetMultiplicity = resetMultiplicity; \ No newline at end of file diff --git a/dist/actions/meta.js b/dist/actions/meta.js new file mode 100644 index 00000000..dce5acf9 --- /dev/null +++ b/dist/actions/meta.js @@ -0,0 +1,17 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.updateMetaPeaks = exports.updateDSCMetaData = void 0; +var _action_type = require("../constants/action_type"); +const updateMetaPeaks = payload => ({ + type: _action_type.META.UPDATE_PEAKS, + payload +}); +exports.updateMetaPeaks = updateMetaPeaks; +const updateDSCMetaData = payload => ({ + type: _action_type.META.UPDATE_META_DATA, + payload +}); +exports.updateDSCMetaData = updateDSCMetaData; \ No newline at end of file diff --git a/dist/actions/multiplicity.js b/dist/actions/multiplicity.js new file mode 100644 index 00000000..d057f481 --- /dev/null +++ b/dist/actions/multiplicity.js @@ -0,0 +1,39 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.updateMpyJ = exports.selectMpyType = exports.rmMpyPeakByPanel = exports.resetMpyOne = exports.clickMpyOne = exports.clearMpyAll = void 0; +var _action_type = require("../constants/action_type"); +const clickMpyOne = payload => ({ + type: _action_type.MULTIPLICITY.ONE_CLICK, + payload +}); +exports.clickMpyOne = clickMpyOne; +const rmMpyPeakByPanel = payload => ({ + type: _action_type.MULTIPLICITY.PEAK_RM_BY_PANEL, + payload +}); +exports.rmMpyPeakByPanel = rmMpyPeakByPanel; +const selectMpyType = payload => ({ + type: _action_type.MULTIPLICITY.TYPE_SELECT, + payload +}); +exports.selectMpyType = selectMpyType; +const clearMpyAll = payload => ({ + type: _action_type.MULTIPLICITY.CLEAR_ALL, + payload +}); +exports.clearMpyAll = clearMpyAll; +const resetMpyOne = payload => ({ + type: _action_type.MULTIPLICITY.RESET_ONE, + payload +}); +exports.resetMpyOne = resetMpyOne; +const updateMpyJ = payload => ({ + type: _action_type.MULTIPLICITY.UPDATE_J, + payload +}); + +// eslint-disable-line +exports.updateMpyJ = updateMpyJ; \ No newline at end of file diff --git a/dist/actions/scan.js b/dist/actions/scan.js new file mode 100644 index 00000000..c8069390 --- /dev/null +++ b/dist/actions/scan.js @@ -0,0 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.toggleScanIsAuto = exports.setScanTarget = exports.resetScanTarget = void 0; +var _action_type = require("../constants/action_type"); +const setScanTarget = payload => ({ + type: _action_type.SCAN.SET_TARGET, + payload +}); +exports.setScanTarget = setScanTarget; +const resetScanTarget = () => ({ + type: _action_type.SCAN.SET_TARGET, + payload: false +}); +exports.resetScanTarget = resetScanTarget; +const toggleScanIsAuto = payload => ({ + type: _action_type.SCAN.TOGGLE_ISAUTO, + payload +}); +exports.toggleScanIsAuto = toggleScanIsAuto; \ No newline at end of file diff --git a/dist/actions/shift.js b/dist/actions/shift.js new file mode 100644 index 00000000..62adf12a --- /dev/null +++ b/dist/actions/shift.js @@ -0,0 +1,19 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.setShiftRef = exports.rmShiftPeak = void 0; +var _action_type = require("../constants/action_type"); +const setShiftRef = payload => ({ + type: _action_type.SHIFT.SET_REF, + payload +}); +exports.setShiftRef = setShiftRef; +const rmShiftPeak = () => ({ + type: _action_type.SHIFT.RM_PEAK, + payload: null +}); + +// eslint-disable-line +exports.rmShiftPeak = rmShiftPeak; \ No newline at end of file diff --git a/dist/actions/status.js b/dist/actions/status.js new file mode 100644 index 00000000..d7efff6f --- /dev/null +++ b/dist/actions/status.js @@ -0,0 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.toggleSubmitBtn = exports.toggleAllBtn = exports.enableAllBtn = void 0; +var _action_type = require("../constants/action_type"); +const toggleSubmitBtn = () => ({ + type: _action_type.STATUS.TOGGLEBTNSUBMIT, + payload: [] +}); +exports.toggleSubmitBtn = toggleSubmitBtn; +const toggleAllBtn = () => ({ + type: _action_type.STATUS.TOGGLEBTNALL, + payload: [] +}); +exports.toggleAllBtn = toggleAllBtn; +const enableAllBtn = () => ({ + type: _action_type.STATUS.ENABLEBTNALL, + payload: [] +}); +exports.enableAllBtn = enableAllBtn; \ No newline at end of file diff --git a/dist/actions/submit.js b/dist/actions/submit.js new file mode 100644 index 00000000..66611588 --- /dev/null +++ b/dist/actions/submit.js @@ -0,0 +1,27 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.updateOperation = exports.updateDecimal = exports.toggleIsIntensity = exports.toggleIsAscend = void 0; +var _action_type = require("../constants/action_type"); +const toggleIsAscend = () => ({ + type: _action_type.SUBMIT.TOGGLE_IS_ASCEND, + payload: false +}); +exports.toggleIsAscend = toggleIsAscend; +const toggleIsIntensity = () => ({ + type: _action_type.SUBMIT.TOGGLE_IS_INTENSITY, + payload: false +}); +exports.toggleIsIntensity = toggleIsIntensity; +const updateOperation = payload => ({ + type: _action_type.SUBMIT.UPDATE_OPERATION, + payload +}); +exports.updateOperation = updateOperation; +const updateDecimal = payload => ({ + type: _action_type.SUBMIT.UPDATE_DECIMAL, + payload +}); +exports.updateDecimal = updateDecimal; \ No newline at end of file diff --git a/dist/actions/threshold.js b/dist/actions/threshold.js new file mode 100644 index 00000000..3feeeee2 --- /dev/null +++ b/dist/actions/threshold.js @@ -0,0 +1,32 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.updateUpperThresholdValue = exports.updateThresholdValue = exports.updateLowerThresholdValue = exports.toggleThresholdIsEdit = exports.resetThresholdValue = void 0; +var _action_type = require("../constants/action_type"); +const updateThresholdValue = payload => ({ + type: _action_type.THRESHOLD.UPDATE_VALUE, + payload +}); +exports.updateThresholdValue = updateThresholdValue; +const resetThresholdValue = () => ({ + type: _action_type.THRESHOLD.RESET_VALUE, + payload: false +}); +exports.resetThresholdValue = resetThresholdValue; +const toggleThresholdIsEdit = payload => ({ + type: _action_type.THRESHOLD.TOGGLE_ISEDIT, + payload +}); +exports.toggleThresholdIsEdit = toggleThresholdIsEdit; +const updateUpperThresholdValue = payload => ({ + type: _action_type.THRESHOLD.UPDATE_UPPER_VALUE, + payload +}); +exports.updateUpperThresholdValue = updateUpperThresholdValue; +const updateLowerThresholdValue = payload => ({ + type: _action_type.THRESHOLD.UPDATE_LOWER_VALUE, + payload +}); +exports.updateLowerThresholdValue = updateLowerThresholdValue; \ No newline at end of file diff --git a/dist/actions/ui.js b/dist/actions/ui.js new file mode 100644 index 00000000..da219839 --- /dev/null +++ b/dist/actions/ui.js @@ -0,0 +1,56 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.setUiViewerType = exports.setUiSweepType = exports.selectUiSweep = exports.scrollUiWheel = 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"); +// eslint-disable-line import/extensions + +const keepIntegrationMode = (jcampIdx = 0) => ({ + type: _action_type.UI.SWEEP.SET_TYPE, + payload: _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_ADD, + jcampIdx +}); +const setUiViewerType = payload => { + if (!(0, _integration_draft.confirmCancelPendingIntegration)()) { + return keepIntegrationMode(); + } + return { + type: _action_type.UI.VIEWER.SET_TYPE, + payload + }; +}; +exports.setUiViewerType = setUiViewerType; +const setUiSweepType = (payload, jcampIdx = 0) => { + if (payload !== _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_ADD && !(0, _integration_draft.confirmCancelPendingIntegration)()) { + return keepIntegrationMode(jcampIdx); + } + return { + type: _action_type.UI.SWEEP.SET_TYPE, + payload, + jcampIdx + }; +}; +exports.setUiSweepType = setUiSweepType; +const selectUiSweep = payload => ({ + type: _action_type.UI.SWEEP.SELECT, + payload +}); +exports.selectUiSweep = selectUiSweep; +const scrollUiWheel = payload => ({ + type: _action_type.UI.WHEEL.SCROLL, + payload +}); +exports.scrollUiWheel = scrollUiWheel; +const clickUiTarget = (payload, onPeak, voltammetryPeakIdx = 0, jcampIdx = 0, onPecker = false) => ({ + type: _action_type.UI.CLICK_TARGET, + payload, + onPeak, + voltammetryPeakIdx, + jcampIdx, + onPecker +}); +exports.clickUiTarget = clickUiTarget; \ No newline at end of file diff --git a/dist/actions/wavelength.js b/dist/actions/wavelength.js new file mode 100644 index 00000000..e8008550 --- /dev/null +++ b/dist/actions/wavelength.js @@ -0,0 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.updateWaveLength = void 0; +var _action_type = require("../constants/action_type"); +const updateWaveLength = payload => ({ + type: _action_type.XRD.UPDATE_WAVE_LENGTH, + payload +}); + +// eslint-disable-line +exports.updateWaveLength = updateWaveLength; \ No newline at end of file diff --git a/dist/app.js b/dist/app.js new file mode 100644 index 00000000..a6bd495c --- /dev/null +++ b/dist/app.js @@ -0,0 +1,130 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "FN", { + enumerable: true, + get: function get() { + return _fn.default; + } +}); +exports.SpectraEditor = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _material = require("@mui/material"); +require("regenerator-runtime/runtime"); +var _reduxSaga = _interopRequireDefault(require("redux-saga")); +var _index = _interopRequireDefault(require("./reducers/index")); +var _index2 = _interopRequireDefault(require("./sagas/index")); +var _layer_init = _interopRequireDefault(require("./layer_init")); +var _fn = _interopRequireDefault(require("./fn")); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable react/function-component-definition, react/require-default-props */ + +// eslint-disable-line + +// import { logger } from 'redux-logger'; + +// - - - store & middleware - - - +const sagaMiddleware = (0, _reduxSaga.default)(); +const middlewares = [sagaMiddleware]; // logger + +const store = (0, _redux.compose)((0, _redux.applyMiddleware)(...middlewares))(_redux.createStore)(_index.default); +sagaMiddleware.run(_index2.default); + +// - - - helper - - - +const ensureQuillDelta = descs => { + const isArr = Array.isArray(descs); + return isArr ? descs : [{ + insert: descs + }]; +}; + +// - - - React - - - +const SpectraEditor = ({ + entity, + others, + cLabel, + xLabel, + yLabel, + operations, + forecast, + molSvg, + editorOnly, + descriptions, + exactMass, + canChangeDescription, + onDescriptionChanged, + multiEntities, + multiMolSvgs, + entityFileNames, + userManualLink +}) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactRedux.Provider, { + store: store, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.StyledEngineProvider, { + injectFirst: true, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_layer_init.default, { + entity: entity, + multiEntities: multiEntities, + entityFileNames: entityFileNames, + userManualLink: userManualLink, + others: others, + cLabel: cLabel, + xLabel: xLabel, + yLabel: yLabel, + forecast: forecast, + operations: operations, + descriptions: ensureQuillDelta(descriptions), + molSvg: molSvg, + multiMolSvgs: multiMolSvgs, + editorOnly: editorOnly, + exactMass: exactMass, + canChangeDescription: canChangeDescription, + onDescriptionChanged: onDescriptionChanged + }) + }) +}); +exports.SpectraEditor = SpectraEditor; +SpectraEditor.propTypes = { + entity: _propTypes.default.object.isRequired, + multiEntities: _propTypes.default.array, + entityFileNames: _propTypes.default.array, + others: _propTypes.default.object, + cLabel: _propTypes.default.string, + xLabel: _propTypes.default.string, + yLabel: _propTypes.default.string, + forecast: _propTypes.default.object, + operations: _propTypes.default.array, + descriptions: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.array]), + molSvg: _propTypes.default.string, + multiMolSvgs: _propTypes.default.array, + editorOnly: _propTypes.default.bool, + canChangeDescription: _propTypes.default.bool, + onDescriptionChanged: _propTypes.default.func, + userManualLink: _propTypes.default.object, + exactMass: _propTypes.default.string +}; +SpectraEditor.defaultProps = { + others: { + others: [], + addOthersCb: false + }, + multiEntities: false, + entityFileNames: false, + cLabel: '', + xLabel: '', + yLabel: '', + forecast: {}, + operations: [], + descriptions: [], + molSvg: '', + exactMass: '', + multiMolSvgs: [], + editorOnly: false, + canChangeDescription: false, + userManualLink: {} +}; \ No newline at end of file diff --git a/dist/components/cmd_bar/01_viewer.js b/dist/components/cmd_bar/01_viewer.js new file mode 100644 index 00000000..637df919 --- /dev/null +++ b/dist/components/cmd_bar/01_viewer.js @@ -0,0 +1,85 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _SpellcheckOutlined = _interopRequireDefault(require("@mui/icons-material/SpellcheckOutlined")); +var _TimelineOutlined = _interopRequireDefault(require("@mui/icons-material/TimelineOutlined")); +var _Tooltip = _interopRequireDefault(require("@mui/material/Tooltip")); +var _ui = require("../../actions/ui"); +var _cfg = _interopRequireDefault(require("../../helpers/cfg")); +var _common = require("./common"); +var _list_ui = require("../../constants/list_ui"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, react/function-component-definition */ + +const styles = () => Object.assign({}, _common.commonStyle); +const Viewer = ({ + classes, + isfocusSpectrumSt, + isfocusAnalysisSt, + hideCmdAnaViewerSt, + disableCmdAnaViewerSt, + setUiViewerTypeAct +}) => { + const onViewSpectrum = () => setUiViewerTypeAct(_list_ui.LIST_UI_VIEWER_TYPE.SPECTRUM); + const onViewAnalysis = () => setUiViewerTypeAct(_list_ui.LIST_UI_VIEWER_TYPE.ANALYSIS); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: classes.group, + "data-testid": "Viewer", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Spectrum Viewer" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)((0, _common.focusStyle)(isfocusSpectrumSt, classes), 'btn-sv-bar-spctrum'), + onClick: onViewSpectrum, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_TimelineOutlined.default, { + className: classes.icon + }) + }) + }), hideCmdAnaViewerSt ? 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)(_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 + }) + }) + })] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + isfocusSpectrumSt: state.ui.viewer === _list_ui.LIST_UI_VIEWER_TYPE.SPECTRUM, + isfocusAnalysisSt: state.ui.viewer === _list_ui.LIST_UI_VIEWER_TYPE.ANALYSIS, + hideCmdAnaViewerSt: _cfg.default.hideCmdAnaViewer(state.layout) || props.editorOnly, + disableCmdAnaViewerSt: _cfg.default.btnCmdAnaViewer(state.layout) +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setUiViewerTypeAct: _ui.setUiViewerType +}, dispatch); +Viewer.propTypes = { + classes: _propTypes.default.object.isRequired, + isfocusSpectrumSt: _propTypes.default.bool.isRequired, + isfocusAnalysisSt: _propTypes.default.bool.isRequired, + hideCmdAnaViewerSt: _propTypes.default.bool.isRequired, + disableCmdAnaViewerSt: _propTypes.default.bool.isRequired, + setUiViewerTypeAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _withStyles.default)(styles))(Viewer); \ No newline at end of file diff --git a/dist/components/cmd_bar/02_zoom.js b/dist/components/cmd_bar/02_zoom.js new file mode 100644 index 00000000..beed42c9 --- /dev/null +++ b/dist/components/cmd_bar/02_zoom.js @@ -0,0 +1,78 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _ZoomInOutlined = _interopRequireDefault(require("@mui/icons-material/ZoomInOutlined")); +var _FindReplaceOutlined = _interopRequireDefault(require("@mui/icons-material/FindReplaceOutlined")); +var _Tooltip = _interopRequireDefault(require("@mui/material/Tooltip")); +var _ui = require("../../actions/ui"); +var _common = require("./common"); +var _list_ui = require("../../constants/list_ui"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, react/function-component-definition */ + +const styles = () => Object.assign({}, _common.commonStyle); +const Zoom = ({ + classes, + isfocusZoomSt, + setUiSweepTypeAct +}) => { + const onSweepZoomIn = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.ZOOMIN); + const onSweepZoomReset = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.ZOOMRESET); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: classes.group, + "data-testid": "Zoom", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Zoom In" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)((0, _common.focusStyle)(isfocusZoomSt, classes), 'btn-sv-bar-zoomin'), + onClick: onSweepZoomIn, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ZoomInOutlined.default, { + className: (0, _classnames.default)(classes.icon, classes.iconWp) + }) + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Reset Zoom" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)('btn-sv-bar-zoomreset'), + onClick: onSweepZoomReset, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_FindReplaceOutlined.default, { + className: classes.icon + }) + }) + }) + })] + }); +}; +const mapStateToProps = (state, _) => ( +// eslint-disable-line +{ + isfocusZoomSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.ZOOMIN +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setUiSweepTypeAct: _ui.setUiSweepType +}, dispatch); +Zoom.propTypes = { + classes: _propTypes.default.object.isRequired, + isfocusZoomSt: _propTypes.default.bool.isRequired, + setUiSweepTypeAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _withStyles.default)(styles))(Zoom); \ No newline at end of file diff --git a/dist/components/cmd_bar/03_peak.js b/dist/components/cmd_bar/03_peak.js new file mode 100644 index 00000000..ea2777d2 --- /dev/null +++ b/dist/components/cmd_bar/03_peak.js @@ -0,0 +1,186 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _Tooltip = _interopRequireDefault(require("@mui/material/Tooltip")); +var _AddLocationOutlined = _interopRequireDefault(require("@mui/icons-material/AddLocationOutlined")); +var _ui = require("../../actions/ui"); +var _cfg = _interopRequireDefault(require("../../helpers/cfg")); +var _common = require("./common"); +var _list_ui = require("../../constants/list_ui"); +var _tri_btn = _interopRequireDefault(require("./tri_btn")); +var _edit_peak = require("../../actions/edit_peak"); +var _extractPeaksEdit = require("../../helpers/extractPeaksEdit"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, function-paren-newline, no-unused-vars, +react/function-component-definition, react/require-default-props, max-len, +react/no-unused-prop-types */ + +const styles = () => Object.assign({}, _common.commonStyle); +const Peak = ({ + classes, + setUiSweepTypeAct, + isFocusAddPeakSt, + disableAddPeakSt, + isFocusRmPeakSt, + disableRmPeakSt, + isFocusSetRefSt, + disableSetRefSt, + isHandleMaxAndMinPeaksSt, + cyclicVotaSt, + curveSt, + clearAllPeaksAct, + feature, + editPeakSt, + thresSt, + shiftSt, + layoutSt +}) => { + let onSweepPeakAdd = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.PEAK_ADD); + let onSweepPeakDELETE = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.PEAK_DELETE); + let onSweepAnchorShift = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.ANCHOR_SHIFT); + const { + curveIdx + } = curveSt; + const onClearAll = () => { + const dataPeaks = (0, _extractPeaksEdit.extractAutoPeaks)(feature, thresSt, shiftSt, layoutSt); + clearAllPeaksAct({ + curveIdx, + dataPeaks + }); + }; + if (isHandleMaxAndMinPeaksSt) { + const { + spectraList + } = cyclicVotaSt; + const spectra = spectraList[curveIdx]; + if (spectra) { + const { + isWorkMaxPeak + } = spectra; + if (isWorkMaxPeak) { + onSweepPeakAdd = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MAX_PEAK, curveIdx); + onSweepPeakDELETE = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MAX_PEAK, curveIdx); + } else { + onSweepPeakAdd = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MIN_PEAK, curveIdx); + onSweepPeakDELETE = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MIN_PEAK, curveIdx); + } + onSweepAnchorShift = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_SET_REF, curveIdx); + } + } + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: classes.group, + "data-testid": "Peak", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Add 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'), + children: "P+" + }) + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Remove 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, + 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, { + content: { + tp: 'Clear All Peaks' + }, + cb: onClearAll, + isClearAllDisabled: disableRmPeakSt, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txt, 'txt-sv-bar-rmallpeaks'), + children: "P" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txt, classes.txtIcon, 'txt-sv-bar-rmallpeaks'), + children: "x" + })] + }), !disableSetRefSt ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Set Reference" + }), + 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 + }) + }) + }) + }) : null] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + isFocusAddPeakSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.PEAK_ADD || state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MAX_PEAK || state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MIN_PEAK, + disableAddPeakSt: _cfg.default.btnCmdAddPeak(state.layout), + isFocusRmPeakSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.PEAK_DELETE || state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MAX_PEAK || state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MIN_PEAK, + disableRmPeakSt: _cfg.default.btnCmdRmPeak(state.layout), + isFocusSetRefSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.ANCHOR_SHIFT || state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_SET_REF, + disableSetRefSt: _cfg.default.btnCmdSetRef(state.layout), + isHandleMaxAndMinPeaksSt: !_cfg.default.hidePanelCyclicVolta(state.layout), + cyclicVotaSt: state.cyclicvolta, + curveSt: state.curve, + editPeakSt: state.editPeak.present, + thresSt: state.threshold.list[state.curve.curveIdx], + layoutSt: state.layout, + shiftSt: state.shift +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setUiSweepTypeAct: _ui.setUiSweepType, + clearAllPeaksAct: _edit_peak.clearAllPeaks +}, dispatch); +Peak.propTypes = { + classes: _propTypes.default.object.isRequired, + isFocusAddPeakSt: _propTypes.default.bool.isRequired, + disableAddPeakSt: _propTypes.default.bool.isRequired, + isFocusRmPeakSt: _propTypes.default.bool.isRequired, + disableRmPeakSt: _propTypes.default.bool.isRequired, + isFocusSetRefSt: _propTypes.default.bool.isRequired, + disableSetRefSt: _propTypes.default.bool.isRequired, + setUiSweepTypeAct: _propTypes.default.func.isRequired, + isHandleMaxAndMinPeaksSt: _propTypes.default.bool.isRequired, + cyclicVotaSt: _propTypes.default.object.isRequired, + curveSt: _propTypes.default.object.isRequired, + clearAllPeaksAct: _propTypes.default.func.isRequired, + feature: _propTypes.default.object.isRequired, + editPeakSt: _propTypes.default.object.isRequired, + thresSt: _propTypes.default.object.isRequired, + layoutSt: _propTypes.default.string.isRequired, + shiftSt: _propTypes.default.object.isRequired +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _withStyles.default)(styles))(Peak); \ No newline at end of file diff --git a/dist/components/cmd_bar/04_integration.js b/dist/components/cmd_bar/04_integration.js new file mode 100644 index 00000000..e065928d --- /dev/null +++ b/dist/components/cmd_bar/04_integration.js @@ -0,0 +1,261 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _Tooltip = _interopRequireDefault(require("@mui/material/Tooltip")); +var _TextField = _interopRequireDefault(require("@mui/material/TextField")); +var _react2 = _interopRequireDefault(require("@mdi/react")); +var _js = require("@mdi/js"); +var _integration = require("../../actions/integration"); +var _ui = require("../../actions/ui"); +var _list_ui = require("../../constants/list_ui"); +var _integration_draft = require("../../helpers/integration_draft.js"); +var _cfg = _interopRequireDefault(require("../../helpers/cfg")); +var _tri_btn = _interopRequireDefault(require("./tri_btn")); +var _common = require("./common"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, function-paren-newline, +react/function-component-definition, react/require-default-props, max-len, +react/no-unused-prop-types */ + +// eslint-disable-line import/extensions + +const styles = () => Object.assign({ + field: { + width: 80 + }, + txtIcon: {}, + cancelBtn: { + borderColor: '#d32f2f', + color: '#d32f2f', + '&:hover': { + backgroundColor: '#ffebee' + } + } +}, _common.commonStyle); +const iconSize = '16px'; +const setFactor = (classes, isDisable, integrationSt, setIntegrationFkrAct, curveIdx) => { + const onFactorChanged = e => { + e.target.blur(); + setIntegrationFkrAct({ + factor: e.target.value, + curveIdx + }); + }; + const onEnterPress = e => { + if (e.key === 'Enter') { + setIntegrationFkrAct({ + factor: e.target.value, + curveIdx + }); + } + }; + let refFactor = 1.00; + const { + integrations + } = integrationSt; + if (integrations && curveIdx < integrations.length) { + const selectedIntegration = integrations[curveIdx]; + refFactor = selectedIntegration.refFactor || 1.00; + } + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_TextField.default, { + className: classes.field, + disabled: isDisable, + id: "intg-factor-name", + type: "number", + value: refFactor, + margin: "none", + InputProps: { + className: (0, _classnames.default)(classes.txtInput, 'txtfield-sv-bar-input') + }, + label: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtLabel, 'txtfield-sv-bar-label'), + children: "Ref Area" + }), + onChange: onFactorChanged, + onBlur: onFactorChanged, + onKeyUp: onEnterPress, + variant: "outlined" + }); +}; +const iconColor = criteria => criteria ? '#fff' : '#000'; +const Integration = ({ + classes, + ignoreRef, + isDisableSt, + isFocusAddIntgSt, + isFocusRmIntgSt, + isFocusSetRefSt, + isFocusSplitIntgSt, + setUiSweepTypeAct, + setIntegrationFkrAct, + clearIntegrationAllAct, + curveSt, + integrationSt +}) => { + const { + curveIdx + } = curveSt; + const onCancelTool = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.ZOOMIN, curveIdx); + const onSweepIntegtAdd = () => { + if (isFocusAddIntgSt) { + (0, _integration_draft.clearPendingIntegrationDraft)(); + onCancelTool(); + return; + } + setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_ADD, curveIdx); + }; + const onSweepIntegtRm = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_RM); + const onSweepIntegtSR = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_SET_REF); + const onSweepIntegtSplit = () => { + if (isFocusSplitIntgSt) { + onCancelTool(); + return; + } + setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_SPLIT, curveIdx); + }; + const onClearAll = () => clearIntegrationAllAct({ + curveIdx + }); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: classes.group, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Add 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), + 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'), + children: "+" + })] + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Remove 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), + 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'), + children: "-" + })] + }) + }) + }), ignoreRef ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Set Integration Reference" + }), + 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), + className: (0, _classnames.default)(classes.iconMdi, 'icon-sv-bar-refint') + }) + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Split 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), + 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'), + children: "/" + })] + }) + }) + }), !ignoreRef ? setFactor(classes, isDisableSt, integrationSt, setIntegrationFkrAct, curveIdx) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_tri_btn.default, { + content: { + tp: 'Clear All Integration' + }, + cb: onClearAll, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_react2.default, { + path: _js.mdiMathIntegral, + size: iconSize, + color: iconColor(isDisableSt), + 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'), + children: "x" + })] + })] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + isDisableSt: _cfg.default.btnCmdIntg(state.layout), + isFocusAddIntgSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_ADD, + isFocusRmIntgSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_RM, + isFocusSetRefSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_SET_REF, + isFocusSplitIntgSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_SPLIT, + ignoreRef: _format.default.isHplcUvVisLayout(state.layout), + curveSt: state.curve, + integrationSt: state.integration.present +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setUiSweepTypeAct: _ui.setUiSweepType, + setIntegrationFkrAct: _integration.setIntegrationFkr, + clearIntegrationAllAct: _integration.clearIntegrationAll +}, dispatch); +Integration.propTypes = { + classes: _propTypes.default.object.isRequired, + isDisableSt: _propTypes.default.bool.isRequired, + isFocusAddIntgSt: _propTypes.default.bool.isRequired, + isFocusRmIntgSt: _propTypes.default.bool.isRequired, + isFocusSetRefSt: _propTypes.default.bool.isRequired, + isFocusSplitIntgSt: _propTypes.default.bool.isRequired, + ignoreRef: _propTypes.default.bool.isRequired, + setUiSweepTypeAct: _propTypes.default.func.isRequired, + setIntegrationFkrAct: _propTypes.default.func.isRequired, + clearIntegrationAllAct: _propTypes.default.func.isRequired, + curveSt: _propTypes.default.object.isRequired, + integrationSt: _propTypes.default.object.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _withStyles.default)(styles)(Integration)); \ No newline at end of file diff --git a/dist/components/cmd_bar/05_multiplicity.js b/dist/components/cmd_bar/05_multiplicity.js new file mode 100644 index 00000000..c725320e --- /dev/null +++ b/dist/components/cmd_bar/05_multiplicity.js @@ -0,0 +1,160 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _Tooltip = _interopRequireDefault(require("@mui/material/Tooltip")); +var _ui = require("../../actions/ui"); +var _multiplicity = require("../../actions/multiplicity"); +var _list_ui = require("../../constants/list_ui"); +var _cfg = _interopRequireDefault(require("../../helpers/cfg")); +var _tri_btn = _interopRequireDefault(require("./tri_btn")); +var _common = require("./common"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, function-paren-newline, +react/function-component-definition, react/require-default-props, max-len, +react/no-unused-prop-types */ + +const styles = () => Object.assign({}, _common.commonStyle); +const Multiplicity = ({ + classes, + isFocusAddMpySt, + disableAddMpySt, + isFocusRmMpySt, + disableRmMpySt, + isFocusAddPeakSt, + isFocusRmPeakSt, + disableMpyPeakSt, + setUiSweepTypeAct, + clearMpyAllAct, + curveSt +}) => { + const onSweepMutAdd = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_SWEEP_ADD); + const onOneMutAdd = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_ONE_RM); + const onPeakMutAdd = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_PEAK_ADD); + const onPeakMutRm = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_PEAK_RM); + const { + curveIdx + } = curveSt; + const onClearAll = () => clearMpyAllAct({ + curveIdx + }); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: classes.group, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Add 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'), + children: "J+" + }) + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Remove 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'), + children: "J-" + }) + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Add Peak for 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'), + children: "JP+" + }) + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Remove Peak for 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'), + children: "JP-" + }) + }) + }) + }), disableAddMpySt ? null : + /*#__PURE__*/ + // eslint-disable-line + (0, _jsxRuntime.jsx)(_tri_btn.default, { + content: { + tp: 'Clear All Multiplicity' + }, + cb: onClearAll, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txt, 'txt-sv-bar-rmallmpy'), + children: "Jx" + }) + })] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + isFocusAddMpySt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_SWEEP_ADD, + disableAddMpySt: _cfg.default.btnCmdMpy(state.layout), + isFocusRmMpySt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_ONE_RM, + disableRmMpySt: _cfg.default.btnCmdMpy(state.layout), + isFocusAddPeakSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_PEAK_ADD, + isFocusRmPeakSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_PEAK_RM, + disableMpyPeakSt: _cfg.default.btnCmdMpyPeak(state.layout, state.multiplicity.present, state.curve.curveIdx), + curveSt: state.curve +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setUiSweepTypeAct: _ui.setUiSweepType, + clearMpyAllAct: _multiplicity.clearMpyAll +}, dispatch); +Multiplicity.propTypes = { + classes: _propTypes.default.object.isRequired, + isFocusAddMpySt: _propTypes.default.bool.isRequired, + disableAddMpySt: _propTypes.default.bool.isRequired, + isFocusRmMpySt: _propTypes.default.bool.isRequired, + disableRmMpySt: _propTypes.default.bool.isRequired, + isFocusAddPeakSt: _propTypes.default.bool.isRequired, + isFocusRmPeakSt: _propTypes.default.bool.isRequired, + disableMpyPeakSt: _propTypes.default.bool.isRequired, + setUiSweepTypeAct: _propTypes.default.func.isRequired, + clearMpyAllAct: _propTypes.default.func.isRequired, + curveSt: _propTypes.default.object.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _withStyles.default)(styles)(Multiplicity)); \ No newline at end of file diff --git a/dist/components/cmd_bar/06_undo_redo.js b/dist/components/cmd_bar/06_undo_redo.js new file mode 100644 index 00000000..24a8bae7 --- /dev/null +++ b/dist/components/cmd_bar/06_undo_redo.js @@ -0,0 +1,84 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reduxUndo = require("redux-undo"); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _Tooltip = _interopRequireDefault(require("@mui/material/Tooltip")); +var _RedoOutlined = _interopRequireDefault(require("@mui/icons-material/RedoOutlined")); +var _UndoOutlined = _interopRequireDefault(require("@mui/icons-material/UndoOutlined")); +var _common = require("./common"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, function-paren-newline, +react/function-component-definition, react/require-default-props, max-len, +react/no-unused-prop-types */ + +const styles = () => Object.assign({}, _common.commonStyle); +const UndoRedo = ({ + classes, + canUndo, + canRedo, + onUndoAct, + onRedoAct +}) => /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: classes.group, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Undo" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)('btn-sv-bar-undo'), + disabled: !canUndo, + onClick: onUndoAct, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_UndoOutlined.default, { + className: classes.icon + }) + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Redo" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)('btn-sv-bar-redo'), + disabled: !canRedo, + onClick: onRedoAct, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_RedoOutlined.default, { + className: classes.icon + }) + }) + }) + })] +}); +const canUndoFunc = state => state.editPeak.past.length > 0 || state.integration.past.length > 0 || state.multiplicity.past.length > 0; +const canRedoFunc = state => state.editPeak.future.length > 0 || state.integration.future.length > 0 || state.multiplicity.future.length > 0; +const mapStateToProps = (state, _) => ( +// eslint-disable-line +{ + canUndo: canUndoFunc(state), + canRedo: canRedoFunc(state) +}); +const mapDispatchToProps = dispatch => ({ + onUndoAct: () => dispatch(_reduxUndo.ActionCreators.undo()), + onRedoAct: () => dispatch(_reduxUndo.ActionCreators.redo()) +}); +UndoRedo.propTypes = { + classes: _propTypes.default.object.isRequired, + canUndo: _propTypes.default.bool.isRequired, + canRedo: _propTypes.default.bool.isRequired, + onUndoAct: _propTypes.default.func.isRequired, + onRedoAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _withStyles.default)(styles))(UndoRedo); \ No newline at end of file diff --git a/dist/components/cmd_bar/07_pecker.js b/dist/components/cmd_bar/07_pecker.js new file mode 100644 index 00000000..d8fbf678 --- /dev/null +++ b/dist/components/cmd_bar/07_pecker.js @@ -0,0 +1,193 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _Tooltip = _interopRequireDefault(require("@mui/material/Tooltip")); +var _material = require("@mui/material"); +var _AddLocationOutlined = _interopRequireDefault(require("@mui/icons-material/AddLocationOutlined")); +var _ui = require("../../actions/ui"); +var _common = require("./common"); +var _list_ui = require("../../constants/list_ui"); +var _cfg = _interopRequireDefault(require("../../helpers/cfg")); +var _cyclic_voltammetry = require("../../actions/cyclic_voltammetry"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, function-paren-newline, +react/function-component-definition, react/require-default-props, max-len, +react/no-unused-prop-types */ + +const styles = () => Object.assign({ + field: { + width: 80 + }, + txtIcon: {} +}, _common.commonStyle); +const setRef = (classes, cyclicVotaSt, curveIdx, setCylicVoltaRefFactorAct) => { + const { + spectraList + } = cyclicVotaSt; + const spectra = spectraList[curveIdx]; + let refFactor = 0.0; + let hasRefPeaks = false; + if (spectra) { + const { + shift, + hasRefPeak + } = spectra; + const { + val + } = shift; + refFactor = val; + hasRefPeaks = hasRefPeak; + } + const onFactorChanged = e => setCylicVoltaRefFactorAct({ + factor: e.target.value, + curveIdx + }); + const onEnterPress = e => { + if (e.key === 'Enter') { + setCylicVoltaRefFactorAct({ + factor: e.target.value, + curveIdx + }); + } + }; + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TextField, { + className: classes.field, + id: "intg-factor-name", + type: "number", + value: refFactor, + margin: "none", + InputProps: { + className: (0, _classnames.default)(classes.txtInput, 'txtfield-sv-bar-input') + }, + label: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtLabel, 'txtfield-sv-bar-label'), + children: hasRefPeaks ? 'Ref Value (V)' : 'Shift' + }), + variant: "outlined", + onChange: onFactorChanged, + onBlur: onFactorChanged, + onKeyUp: onEnterPress + }); +}; +const Pecker = ({ + classes, + layoutSt, + isFocusAddPeckerSt, + isFocusRmPeckerSt, + setUiSweepTypeAct, + curveSt, + cyclicVotaSt, + setCylicVoltaRefFactorAct, + isFocusSetRefSt, + setCylicVoltaRefAct +}) => { + const { + curveIdx + } = curveSt; + const onSweepPeckerAdd = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_PECKER, curveIdx); + const onSweepPeckerDELETE = () => setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_PECKER, curveIdx); + const onConfirmSetRef = () => setCylicVoltaRefAct({ + jcampIdx: curveIdx + }); + const { + spectraList + } = cyclicVotaSt; + const spectra = spectraList[curveIdx]; + let hasRefPeaks = false; + if (spectra) { + const { + hasRefPeak + } = spectra; + hasRefPeaks = hasRefPeak; + } + return !_cfg.default.hidePanelCyclicVolta(layoutSt) ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + "data-testid": "Pecker", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Add Pecker" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)((0, _common.focusStyle)(isFocusAddPeckerSt, classes), 'btn-sv-bar-addpecker'), + onClick: onSweepPeckerAdd, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: (0, _classnames.default)(classes.txt, 'txt-sv-bar-addpeak'), + children: ["I", /*#__PURE__*/(0, _jsxRuntime.jsx)("sub", { + children: "\u03BB0" + }), "+"] + }) + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Remove Pecker" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)((0, _common.focusStyle)(isFocusRmPeckerSt, classes), 'btn-sv-bar-rmpecker'), + onClick: onSweepPeckerDELETE, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: (0, _classnames.default)(classes.txt, 'txt-sv-bar-rmpeak'), + children: ["I", /*#__PURE__*/(0, _jsxRuntime.jsx)("sub", { + children: "\u03BB0" + }), "-"] + }) + }) + }) + }), setRef(classes, cyclicVotaSt, curveIdx, setCylicVoltaRefFactorAct), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: hasRefPeaks ? 'Set Reference' : 'Set Shift' + }), + 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'), + onClick: onConfirmSetRef, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_AddLocationOutlined.default, { + className: classes.icon + }) + }) + }) + })] + }) : /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {}); +}; +const mapStateToProps = (state, _) => ( +// eslint-disable-line +{ + layoutSt: state.layout, + isFocusAddPeckerSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_PECKER, + isFocusRmPeckerSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_PECKER, + isFocusSetRefSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.ANCHOR_SHIFT || state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_SET_REF, + cyclicVotaSt: state.cyclicvolta, + curveSt: state.curve +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setUiSweepTypeAct: _ui.setUiSweepType, + setCylicVoltaRefFactorAct: _cyclic_voltammetry.setCylicVoltaRefFactor, + setCylicVoltaRefAct: _cyclic_voltammetry.setCylicVoltaRef +}, dispatch); +Pecker.propTypes = { + classes: _propTypes.default.object.isRequired, + layoutSt: _propTypes.default.string.isRequired, + isFocusAddPeckerSt: _propTypes.default.bool.isRequired, + isFocusRmPeckerSt: _propTypes.default.bool.isRequired, + setUiSweepTypeAct: _propTypes.default.func.isRequired, + isFocusSetRefSt: _propTypes.default.bool.isRequired, + cyclicVotaSt: _propTypes.default.object.isRequired, + curveSt: _propTypes.default.object.isRequired, + setCylicVoltaRefFactorAct: _propTypes.default.func.isRequired, + setCylicVoltaRefAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _withStyles.default)(styles))(Pecker); \ No newline at end of file diff --git a/dist/components/cmd_bar/common.js b/dist/components/cmd_bar/common.js new file mode 100644 index 00000000..f99639f0 --- /dev/null +++ b/dist/components/cmd_bar/common.js @@ -0,0 +1,127 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.focusStyle = exports.commonStyle = exports.MuButton = void 0; +var _react = _interopRequireDefault(require("react")); +var _styles = require("@mui/styles"); +var _Button = _interopRequireDefault(require("@mui/material/Button")); +var _classnames = _interopRequireDefault(require("classnames")); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable no-unused-vars, max-len, indent, react/function-component-definition, react/self-closing-comp, react/prop-types, react/jsx-props-no-spreading */ + +const useStyles = (0, _styles.makeStyles)(theme => ({ + muiBtn: { + border: '1px solid #ccc', + borderRadius: 4, + fontFamily: 'Helvetica', + fontSize: 20, + height: 30, + lineHeight: '20px', + minWidth: 30, + padding: 0, + width: 30, + color: 'black' + } +})); +const MuButton = props => { + const classes = useStyles(); + const { + className, + ...other + } = props; + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, { + className: (0, _classnames.default)(classes.muiBtn, className), + ...other + }); +}; +exports.MuButton = MuButton; +const commonStyle = exports.commonStyle = { + card: { + margin: '0 0 5px 52px', + border: '1px solid white', + borderRadius: 4 + }, + group: { + display: 'inline-block', + margin: '0px 0px 0px 10px', + verticalAlign: 'middle' + }, + groupRightMost: { + display: 'inline-block', + float: 'right', + margin: '0px 0px 0px 10px', + verticalAlign: 'middle' + }, + groupRight: { + display: 'inline-block', + float: 'right', + margin: '0px 0px 0px 10px', + verticalAlign: 'middle' + }, + btnHt: { + backgroundColor: '#2196f3', + color: '#fff', + '&:hover': { + backgroundColor: '#51c6f3' + } + }, + iconWp: { + border: '1px dashed', + borderRadius: '4px' + }, + icon: { + fontSize: 20 + }, + iconMdi: { + fontSize: 20 + }, + txt: { + fontFamily: 'Helvetica', + fontSize: 12, + fontStyle: 'italic', + fontWeight: 'bold' + }, + txtLabel: { + fontFamily: 'Helvetica', + fontSize: 12 + }, + txtInput: { + fontFamily: 'Helvetica', + fontSize: 12, + height: 30 + }, + txtOpt: { + fontFamily: 'Helvetica', + fontSize: 12 + }, + selectLabel: { + fontFamily: 'Helvetica', + fontSize: 12 + }, + selectInput: { + height: 30 + }, + txtLabelBottomInput: { + fontFamily: 'Helvetica', + backgroundColor: 'white', + fontSize: 12, + margin: '22% 0 0 7px', + padding: '0 10px 0 10px', + transform: 'scale(0.75)' + }, + txtLabelTopInput: { + fontFamily: 'Helvetica', + backgroundColor: 'white', + fontSize: 12, + margin: '-8% 0 0 7px', + padding: '0 10px 0 10px', + transform: 'scale(0.75)' + } +}; +const focusStyle = (criteria, cls) => criteria ? [cls.btnHt] : []; + +// eslint-disable-line +exports.focusStyle = focusStyle; \ No newline at end of file diff --git a/dist/components/cmd_bar/index.js b/dist/components/cmd_bar/index.js new file mode 100644 index 00000000..5b000116 --- /dev/null +++ b/dist/components/cmd_bar/index.js @@ -0,0 +1,88 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _common = require("./common"); +var _viewer = _interopRequireDefault(require("./01_viewer")); +var _zoom = _interopRequireDefault(require("./02_zoom")); +var _peak = _interopRequireDefault(require("./03_peak")); +var _integration = _interopRequireDefault(require("./04_integration")); +var _multiplicity = _interopRequireDefault(require("./05_multiplicity")); +var _undo_redo = _interopRequireDefault(require("./06_undo_redo")); +var _r01_layout = _interopRequireDefault(require("./r01_layout")); +var _r03_threshold = _interopRequireDefault(require("./r03_threshold")); +var _r04_submit = _interopRequireDefault(require("./r04_submit")); +var _r07_wavelength_btn = _interopRequireDefault(require("./r07_wavelength_btn")); +var _pecker = _interopRequireDefault(require("./07_pecker")); +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 _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, function-paren-newline, +react/function-component-definition, react/require-default-props */ + +const styles = () => Object.assign({}, {}, _common.commonStyle); +const CmdBar = ({ + classes, + feature, + hasEdit, + forecast, + operations, + editorOnly, + jcampIdx, + hideThreshold, + layoutSt +}) => { + const isCvLayout = _format.default.isCyclicVoltaLayout(layoutSt); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: classes.card, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_viewer.default, { + editorOnly: editorOnly + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_zoom.default, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_peak.default, { + jcampIdx: jcampIdx, + 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, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_r04_submit.default, { + operations: operations, + feature: feature, + forecast: forecast, + editorOnly: editorOnly, + hideSwitch: false, + disabled: false + }), hideThreshold ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_r03_threshold.default, { + feature: feature, + hasEdit: hasEdit + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_r01_layout.default, { + feature: feature, + hasEdit: hasEdit + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_r07_wavelength_btn.default, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_r10_cv_density.default, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_r08_change_axes.default, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_r09_detector.default, {})] + }); +}; +const mapStateToProps = (state, _) => ( +// eslint-disable-line +{ + layoutSt: state.layout +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({}, dispatch); +CmdBar.propTypes = { + classes: _propTypes.default.object.isRequired, + feature: _propTypes.default.object.isRequired, + forecast: _propTypes.default.object.isRequired, + hasEdit: _propTypes.default.bool.isRequired, + operations: _propTypes.default.array.isRequired, + editorOnly: _propTypes.default.bool.isRequired, + layoutSt: _propTypes.default.string.isRequired, + jcampIdx: _propTypes.default.any, + hideThreshold: _propTypes.default.bool +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _withStyles.default)(styles))(CmdBar); \ No newline at end of file diff --git a/dist/components/cmd_bar/r01_layout.js b/dist/components/cmd_bar/r01_layout.js new file mode 100644 index 00000000..9c52d9b7 --- /dev/null +++ b/dist/components/cmd_bar/r01_layout.js @@ -0,0 +1,381 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireWildcard(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _r02_scan = _interopRequireDefault(require("./r02_scan")); +var _layout = require("../../actions/layout"); +var _shift = require("../../actions/shift"); +var _list_layout = require("../../constants/list_layout"); +var _list_shift = require("../../constants/list_shift"); +var _cfg = _interopRequireDefault(require("../../helpers/cfg")); +var _common = require("./common"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _jsxRuntime = require("react/jsx-runtime"); +function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } +/* eslint-disable prefer-object-spread, function-paren-newline, +react/function-component-definition */ + +const styles = () => Object.assign({ + fieldShift: { + width: 160 + }, + fieldLayout: { + width: 100 + } +}, _common.commonStyle); +const chemSubStyle = { + fontSize: '0.85em', + position: 'relative', + top: '0.24em', + lineHeight: 1 +}; +const renderReadableSubscript = (txt = '') => { + if (typeof txt !== 'string') return txt; + const regex = /([A-Za-z])(\d+)/g; + if (!regex.test(txt)) return txt; + regex.lastIndex = 0; + const parts = []; + let cursor = 0; + let match = regex.exec(txt); + while (match) { + const [raw, prefix, digits] = match; + const at = match.index; + if (at > cursor) parts.push(txt.slice(cursor, at)); + parts.push(prefix); + parts.push(/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + style: chemSubStyle, + children: digits + }, `${at}-${digits}`)); + cursor = at + raw.length; + match = regex.exec(txt); + } + if (cursor < txt.length) parts.push(txt.slice(cursor)); + return parts; +}; +const shiftSelect = (classes, layoutSt, setShiftRefAct, shiftSt, curveSt) => { + if (_cfg.default.hideSolvent(layoutSt)) { + return null; + } + const { + curveIdx + } = curveSt; + const { + shifts + } = shiftSt; + const selectedShift = shifts[curveIdx] || {}; + const listShift = (0, _list_shift.getListShift)(layoutSt) || []; + const shiftRefName = selectedShift?.ref?.name || ''; + const isInList = listShift.some(r => r.name === shiftRefName); + const selectValue = isInList ? shiftRefName : ''; + const onChange = e => { + const name = e.target.value; + const refObj = listShift.find(r => r.name === name); + if (refObj) { + setShiftRefAct({ + dataToSet: refObj, + curveIdx + }); + } + }; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldShift), + variant: "outlined", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + id: "select-solvent-label", + className: (0, _classnames.default)(classes.selectLabel, 'select-sv-bar-label'), + children: "Reference" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Select, { + value: selectValue, + labelId: "select-solvent-label", + label: "Solvent", + onChange: onChange, + className: (0, _classnames.default)(classes.selectInput, 'input-sv-bar-shift'), + children: listShift.map(ref => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: ref.name, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-shift'), + children: [renderReadableSubscript(ref.name), `: ${_format.default.strNumberFixedDecimal(ref.value, 2)} ppm`] + }) + }, ref.name)) + })] + }); +}; +const layoutSelect = (classes, layoutSt, updateLayoutAct) => { + const onChange = e => updateLayoutAct(e.target.value); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldLayout), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + id: "select-layout-label", + className: (0, _classnames.default)(classes.selectLabel, 'select-sv-bar-label'), + children: "Layout" + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Select, { + labelId: "select-layout-label", + label: "Layout", + value: layoutSt, + onChange: onChange, + className: (0, _classnames.default)(classes.selectInput, 'input-sv-bar-layout'), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.PLAIN, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "plain" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.IR, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "IR" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.RAMAN, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "RAMAN" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.UVVIS, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "UV/VIS" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.HPLC_UVVIS, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "HPLC UV/VIS" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.TGA, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "TGA (THERMOGRAVIMETRIC ANALYSIS)" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.DSC, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "DSC (DIFFERENTIAL SCANNING CALORIMETRY)" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.XRD, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "XRD (X-RAY DIFFRACTION)" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.H1, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("sup", { + children: "1" + }), "H"] + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.C13, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("sup", { + children: "13" + }), "C"] + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.F19, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("sup", { + children: "19" + }), "F"] + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.P31, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("sup", { + children: "31" + }), "P"] + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.N15, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("sup", { + children: "15" + }), "N"] + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.Si29, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("sup", { + children: "29" + }), "Si"] + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.MS, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "MS" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "CV (CYCLIC VOLTAMMETRY)" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.CDS, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "CDS (CIRCULAR DICHROISM SPECTROSCOPY)" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.SEC, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "SEC" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.GC, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "GC (GAS CHROMATOGRAPHY)" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.AIF, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "SORPTION-DESORPTION" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.EMISSIONS, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "EMISSIONS" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.DLS_ACF, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "DLS ACF" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: _list_layout.LIST_LAYOUT.DLS_INTENSITY, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: "DLS INTENSITY" + }) + })] + })] + }); +}; +const PLACEHOLDER = '- - -'; +const norm = s => (s || '').toString().toLowerCase().normalize('NFKD').replace(/[^a-z0-9]+/g, ''); +function solventKeyOf(feature) { + const r = feature?.metadata?.solventName ?? feature?.metadata?.solvent ?? feature?.meta?.solventName ?? feature?.meta?.solvent ?? feature?.solventName ?? feature?.solvent ?? null; + const a = feature?.metadata?.solvent_label ?? feature?.metadata?.solventLabel ?? null; + const raw = r && r !== PLACEHOLDER ? r : null; + const alt = a && a !== PLACEHOLDER ? a : null; + return norm(raw || alt || ''); +} +function pickBestRef(list, key) { + if (!key || !list?.length) return null; + const scored = []; + list.forEach(r => { + const nLabel = norm(r.label); + const nName = norm(r.name); + const nNsdb = norm(r.nsdb); + let s = 0; + if (nLabel && nLabel === key) s += 3; + if (nNsdb && nNsdb.includes(key)) s += 2; + if (nName && nName.includes(key)) s += 1; + if (s > 0) scored.push({ + r, + s + }); + }); + if (!scored.length) return null; + let max = 0; + scored.forEach(x => { + if (x.s > max) max = x.s; + }); + let cand = scored.filter(x => x.s === max).map(x => x.r); + if (cand.length > 1) { + const vals = cand.map(c => typeof c.value === 'number' ? c.value : null).filter(v => v != null).sort((a, b) => a - b); + if (vals.length) { + const m = vals[Math.floor(vals.length / 2)]; + cand = cand.slice().sort((a, b) => Math.abs((a.value ?? m) - m) - Math.abs((b.value ?? m) - m)); + } + if (cand.length > 1) { + cand.sort((a, b) => (a.name?.length || 0) - (b.name?.length || 0)); + } + } + return cand[0] || null; +} +function isRefUnset(shiftSt, curveIdx, list) { + const name = shiftSt?.shifts?.[curveIdx]?.ref?.name || ''; + if (!name || name === PLACEHOLDER) return true; + return !(list || []).some(r => r.name === name); +} +const Layout = ({ + classes, + feature, + hasEdit, + layoutSt, + setShiftRefAct, + updateLayoutAct, + curveSt, + shiftSt +}) => { + const { + curveIdx + } = curveSt; + const list = (0, _list_shift.getListShift)(layoutSt) || []; + const unset = isRefUnset(shiftSt, curveIdx, list); + const key = solventKeyOf(feature); + const best = pickBestRef(list, key); + (0, _react.useEffect)(() => { + if (unset && best) setShiftRefAct({ + dataToSet: best, + curveIdx + }); + }, [unset, best, curveIdx, setShiftRefAct]); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: classes.groupRight, + children: [layoutSelect(classes, layoutSt, updateLayoutAct), shiftSelect(classes, layoutSt, setShiftRefAct, shiftSt, curveSt), /*#__PURE__*/(0, _jsxRuntime.jsx)(_r02_scan.default, { + feature: feature, + hasEdit: hasEdit + })] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + layoutSt: state.layout, + curveSt: state.curve, + shiftSt: state.shift +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setShiftRefAct: _shift.setShiftRef, + updateLayoutAct: _layout.updateLayout +}, dispatch); +Layout.propTypes = { + classes: _propTypes.default.object.isRequired, + feature: _propTypes.default.object.isRequired, + hasEdit: _propTypes.default.bool.isRequired, + layoutSt: _propTypes.default.string.isRequired, + setShiftRefAct: _propTypes.default.func.isRequired, + updateLayoutAct: _propTypes.default.func.isRequired, + curveSt: _propTypes.default.object.isRequired, + shiftSt: _propTypes.default.object.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _withStyles.default)(styles)(Layout)); \ No newline at end of file diff --git a/dist/components/cmd_bar/r02_scan.js b/dist/components/cmd_bar/r02_scan.js new file mode 100644 index 00000000..89c5b7a3 --- /dev/null +++ b/dist/components/cmd_bar/r02_scan.js @@ -0,0 +1,132 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _styles = require("@mui/styles"); +var _CloudDoneOutlined = _interopRequireDefault(require("@mui/icons-material/CloudDoneOutlined")); +var _HowToRegOutlined = _interopRequireDefault(require("@mui/icons-material/HowToRegOutlined")); +var _RefreshOutlined = _interopRequireDefault(require("@mui/icons-material/RefreshOutlined")); +var _scan = require("../../actions/scan"); +var _common = require("./common"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, function-paren-newline, +react/function-component-definition */ + +const styles = () => Object.assign({ + fieldScan: { + width: 90 + } +}, _common.commonStyle); +const restoreIcon = (classes, hasEdit, isEdit) => hasEdit && isEdit ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_HowToRegOutlined.default, { + className: classes.icon +}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_CloudDoneOutlined.default, { + className: classes.icon +}); +const restoreTp = (hasEdit, isEdit) => hasEdit && isEdit ? 'User Defined Scan' : 'Auto Picked Scan'; +const btnRestore = (classes, hasEdit, isEdit, toggleEditAct) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: restoreTp(hasEdit, isEdit) + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)('btn-sv-bar-scanrst'), + disabled: !hasEdit, + onClick: toggleEditAct, + children: restoreIcon(classes, hasEdit, isEdit) + }) +}); +const btnRrfresh = (classes, disabled, refreshAct) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Refresh Scan" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)('btn-sv-bar-scanrfs'), + disabled: disabled, + onClick: refreshAct, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_RefreshOutlined.default, { + className: classes.icon + }) + }) +}); +const scanSelect = (classes, feature, layoutSt, scanSt, onChange) => { + const { + target, + count + } = scanSt; + if (!count) return null; + const range = [...Array(count + 1).keys()].slice(1); + const content = range.map(num => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: num, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-scan'), + children: `scan ${num}` + }) + }, num)); + const defaultValue = scanSt.isAuto || !feature.scanEditTarget ? feature.scanAutoTarget : feature.scanEditTarget; + const selValue = target || defaultValue || 1; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldScan), + variant: "outlined", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + id: "select-scan-label", + className: (0, _classnames.default)(classes.selectLabel, 'select-sv-bar-label'), + children: "Current Scan" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Select, { + labelId: "select-scan-label", + label: "Current Scan", + value: selValue, + onChange: onChange, + className: (0, _classnames.default)(classes.selectInput, 'input-sv-bar-scan'), + children: content + })] + }); +}; +const Scan = ({ + classes, + feature, + hasEdit, + layoutSt, + scanSt, + setScanTargetAct, + resetScanTargetAct, + toggleScanIsAutoAct +}) => { + const isMs = ['MS'].indexOf(layoutSt) >= 0; + if (!isMs) return null; + const onChange = e => setScanTargetAct(e.target.value); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + children: [scanSelect(classes, feature, layoutSt, scanSt, onChange), btnRrfresh(classes, false, resetScanTargetAct), btnRestore(classes, hasEdit, !scanSt.isAuto, toggleScanIsAutoAct)] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + layoutSt: state.layout, + scanSt: state.scan +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setScanTargetAct: _scan.setScanTarget, + resetScanTargetAct: _scan.resetScanTarget, + toggleScanIsAutoAct: _scan.toggleScanIsAuto +}, dispatch); +Scan.propTypes = { + classes: _propTypes.default.object.isRequired, + feature: _propTypes.default.object.isRequired, + hasEdit: _propTypes.default.bool.isRequired, + layoutSt: _propTypes.default.string.isRequired, + scanSt: _propTypes.default.object.isRequired, + setScanTargetAct: _propTypes.default.func.isRequired, + resetScanTargetAct: _propTypes.default.func.isRequired, + toggleScanIsAutoAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _styles.withStyles)(styles))(Scan); \ No newline at end of file diff --git a/dist/components/cmd_bar/r03_threshold.js b/dist/components/cmd_bar/r03_threshold.js new file mode 100644 index 00000000..4e2e6b38 --- /dev/null +++ b/dist/components/cmd_bar/r03_threshold.js @@ -0,0 +1,161 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _styles = require("@mui/styles"); +var _CloudDoneOutlined = _interopRequireDefault(require("@mui/icons-material/CloudDoneOutlined")); +var _HowToRegOutlined = _interopRequireDefault(require("@mui/icons-material/HowToRegOutlined")); +var _RefreshOutlined = _interopRequireDefault(require("@mui/icons-material/RefreshOutlined")); +var _cfg = _interopRequireDefault(require("../../helpers/cfg")); +var _threshold = require("../../actions/threshold"); +var _common = require("./common"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, function-paren-newline, +react/function-component-definition */ + +const styles = () => Object.assign({ + field: { + width: 110 + }, + txtIcon: {} +}, _common.commonStyle); +const txtPercent = () => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputAdornment, { + position: "end", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-percent", + children: "%" + }) +}); +const setThreshold = (classes, thresVal, updateThresholdValueAct, curveSt) => { + const { + curveIdx + } = curveSt; + const onBlur = e => updateThresholdValueAct({ + value: e.target.value, + curveIdx + }); + const onChange = e => updateThresholdValueAct({ + value: e.target.value, + curveIdx + }); + const onEnterPress = e => { + if (e.key === 'Enter') { + updateThresholdValueAct({ + value: e.target.value, + curveIdx + }); + } + }; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + variant: "outlined", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TextField, { + className: classes.field, + id: "outlined-name", + placeholder: "N.A.", + type: "number", + value: thresVal || 0.01, + margin: "none", + InputProps: { + endAdornment: txtPercent(), + className: (0, _classnames.default)(classes.txtInput, 'txtfield-sv-bar-input'), + inputProps: { + min: 0.01 + } + }, + onChange: onChange, + onBlur: onBlur, + onKeyPress: onEnterPress, + variant: "outlined" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + className: (0, _classnames.default)(classes.txtLabelBottomInput), + children: "Threshold" + })] + }); +}; +const restoreIcon = (classes, hasEdit, isEdit) => hasEdit && isEdit ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_HowToRegOutlined.default, { + className: classes.icon +}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_CloudDoneOutlined.default, { + className: classes.icon +}); +const restoreTp = (hasEdit, isEdit) => hasEdit && isEdit ? 'User Defined Threshold' : 'Auto Picked Threshold'; +const Threshold = ({ + classes, + feature, + hasEdit, + hideThresSt, + thresValSt, + isEditSt, + curveSt, + updateThresholdValueAct, + resetThresholdValueAct, + toggleThresholdIsEditAct +}) => { + const thresVal = thresValSt || feature.thresRef; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: classes.groupRight, + children: [setThreshold(classes, thresVal, updateThresholdValueAct, curveSt), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Restore Threshold" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)('btn-sv-bar-thresref'), + disabled: _cfg.default.btnCmdThres(thresVal), + onClick: resetThresholdValueAct, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_RefreshOutlined.default, { + className: classes.icon + }) + }) + }) + }), hideThresSt ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: restoreTp(hasEdit, isEditSt) + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)('btn-sv-bar-thresrst'), + disabled: _cfg.default.btnCmdThres(thresVal), + onClick: toggleThresholdIsEditAct, + children: restoreIcon(classes, hasEdit, isEditSt) + }) + }) + })] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + hideThresSt: _cfg.default.hideCmdThres(state.layout), + isEditSt: state.threshold.list[state.curve.curveIdx].isEdit, + thresValSt: parseFloat(state.threshold.list[state.curve.curveIdx].value) || 0, + curveSt: state.curve +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + updateThresholdValueAct: _threshold.updateThresholdValue, + resetThresholdValueAct: _threshold.resetThresholdValue, + toggleThresholdIsEditAct: _threshold.toggleThresholdIsEdit +}, dispatch); +Threshold.propTypes = { + classes: _propTypes.default.object.isRequired, + feature: _propTypes.default.object.isRequired, + hasEdit: _propTypes.default.bool.isRequired, + hideThresSt: _propTypes.default.bool.isRequired, + isEditSt: _propTypes.default.bool.isRequired, + thresValSt: _propTypes.default.number.isRequired, + curveSt: _propTypes.default.object.isRequired, + updateThresholdValueAct: _propTypes.default.func.isRequired, + resetThresholdValueAct: _propTypes.default.func.isRequired, + toggleThresholdIsEditAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _styles.withStyles)(styles)(Threshold)); \ No newline at end of file diff --git a/dist/components/cmd_bar/r04_submit.js b/dist/components/cmd_bar/r04_submit.js new file mode 100644 index 00000000..3a9431f9 --- /dev/null +++ b/dist/components/cmd_bar/r04_submit.js @@ -0,0 +1,256 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _styles = require("@mui/styles"); +var _submit = require("../../actions/submit"); +var _r05_submit_btn = _interopRequireDefault(require("./r05_submit_btn")); +var _r06_predict_btn = _interopRequireDefault(require("./r06_predict_btn")); +var _common = require("./common"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, function-paren-newline, +react/function-component-definition */ + +const styles = () => Object.assign({ + fieldOrder: { + width: 90 + }, + fieldIntensity: { + width: 90 + }, + fieldDecimal: { + width: 80 + }, + fieldOpertaion: { + width: 120 + } +}, _common.commonStyle); +const ascendSelect = (classes, hideSwitch, isAscendSt, toggleIsAscendAct) => { + if (hideSwitch) return null; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldOrder), + variant: "outlined", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + id: "select-sort-peaks-label", + className: (0, _classnames.default)(classes.selectLabel, 'select-sv-bar-label'), + children: "Write Peaks" + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Select, { + labelId: "select-sort-peaks-label", + label: "Write Peaks", + value: isAscendSt, + onChange: toggleIsAscendAct, + className: (0, _classnames.default)(classes.selectInput, 'input-sv-bar-order'), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: true, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-ascend'), + children: "Ascend" + }) + }, "ascend"), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: false, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-descend'), + children: "Descend" + }) + }, "descend")] + })] + }); +}; +const intensitySelect = (classes, hideSwitch, isIntensitySt, toggleIsIntensityAct) => { + if (hideSwitch) return null; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldIntensity), + variant: "outlined", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + id: "select-intensity-label", + className: (0, _classnames.default)(classes.selectLabel, 'select-sv-bar-label'), + children: "Write Intensity" + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Select, { + labelId: "select-intensity-label", + label: "Write Intensity", + value: isIntensitySt, + onChange: toggleIsIntensityAct, + className: (0, _classnames.default)(classes.selectInput, 'input-sv-bar-intensity') + // input={ + // ( + // + // ) + // } + , + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: true, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-show'), + children: "Show" + }) + }, "ascend"), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: false, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-hide'), + children: "Hide" + }) + }, "descend")] + })] + }); +}; +const decimalSelect = (classes, hideSwitch, decimalSt, updateDecimalAct) => { + if (hideSwitch) return null; + const decimals = [0, 1, 2, 3, 4]; + const options = decimals.map(d => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: d, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-decimal'), + children: d + }) + }, d)); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldDecimal), + variant: "outlined", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + id: "select-decimal-label", + className: (0, _classnames.default)(classes.selectLabel, 'select-sv-bar-label'), + children: "Decimal" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Select, { + labelId: "select-decimal-label", + label: "Decimal", + value: decimalSt, + onChange: updateDecimalAct, + className: (0, _classnames.default)(classes.selectInput, 'input-sv-bar-decimal') + // input={ + // ( + // + // ) + // } + , + children: options + })] + }); +}; +const operationSelect = (classes, operations, operation, onChangeSelect) => { + const options = operations.map(o => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: o.name, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-operation'), + children: o.name + }) + }, o.name)); + const selectedValue = operation.name || operations[0].name; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldOpertaion), + variant: "outlined", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + id: "select-submit-label", + className: (0, _classnames.default)(classes.selectLabel, 'select-sv-bar-label'), + children: "Submit" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Select, { + labelId: "select-submit-label", + label: "Submit", + value: selectedValue, + onChange: onChangeSelect, + className: (0, _classnames.default)(classes.selectInput, 'input-sv-bar-operation') + // input={ + // ( + // + // ) + // } + , + children: options + })] + }); +}; +const selectOperation = (name, operations, updateOperationAct) => { + let operation = false; + operations.forEach(o => { + if (o.name === name) { + operation = o; + } + }); + updateOperationAct(operation); +}; +const Submit = ({ + operations, + classes, + feature, + forecast, + editorOnly, + hideSwitch, + disabled, + isAscendSt, + isIntensitySt, + operationSt, + decimalSt, + isEmWaveSt, + toggleIsAscendAct, + toggleIsIntensityAct, + updateOperationAct, + updateDecimalAct +}) => { + const onChangeSelect = e => selectOperation(e.target.value, operations, updateOperationAct); + if (!operations || operations.length === 0) return null; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: classes.groupRightMost, + children: [ascendSelect(classes, hideSwitch, isAscendSt, toggleIsAscendAct), intensitySelect(classes, hideSwitch || !isEmWaveSt, isIntensitySt, toggleIsIntensityAct), decimalSelect(classes, hideSwitch, decimalSt, updateDecimalAct), editorOnly ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_r06_predict_btn.default, { + feature: feature, + forecast: forecast + }), operationSelect(classes, operations, operationSt, onChangeSelect), /*#__PURE__*/(0, _jsxRuntime.jsx)(_r05_submit_btn.default, { + feature: feature, + isAscend: isAscendSt, + isIntensity: isIntensitySt, + operation: operationSt, + disabled: disabled + })] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + isEmWaveSt: _format.default.isEmWaveLayout(state.layout), + isAscendSt: state.submit.isAscend, + isIntensitySt: state.submit.isIntensity, + decimalSt: state.submit.decimal, + operationSt: state.submit.operation +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + toggleIsAscendAct: _submit.toggleIsAscend, + toggleIsIntensityAct: _submit.toggleIsIntensity, + updateOperationAct: _submit.updateOperation, + updateDecimalAct: _submit.updateDecimal +}, dispatch); +Submit.propTypes = { + classes: _propTypes.default.object.isRequired, + feature: _propTypes.default.object.isRequired, + forecast: _propTypes.default.object.isRequired, + editorOnly: _propTypes.default.bool.isRequired, + operations: _propTypes.default.array.isRequired, + operationSt: _propTypes.default.object.isRequired, + hideSwitch: _propTypes.default.bool.isRequired, + disabled: _propTypes.default.bool.isRequired, + isAscendSt: _propTypes.default.bool.isRequired, + isIntensitySt: _propTypes.default.bool.isRequired, + isEmWaveSt: _propTypes.default.bool.isRequired, + decimalSt: _propTypes.default.number.isRequired, + toggleIsAscendAct: _propTypes.default.func.isRequired, + toggleIsIntensityAct: _propTypes.default.func.isRequired, + updateOperationAct: _propTypes.default.func.isRequired, + updateDecimalAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _styles.withStyles)(styles))(Submit); \ No newline at end of file diff --git a/dist/components/cmd_bar/r05_submit_btn.js b/dist/components/cmd_bar/r05_submit_btn.js new file mode 100644 index 00000000..78747a3d --- /dev/null +++ b/dist/components/cmd_bar/r05_submit_btn.js @@ -0,0 +1,306 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _reactRedux = require("react-redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _redux = require("redux"); +var _Tooltip = _interopRequireDefault(require("@mui/material/Tooltip")); +var _PlayCircleOutline = _interopRequireDefault(require("@mui/icons-material/PlayCircleOutline")); +var _styles = require("@mui/styles"); +var _chem = require("../../helpers/chem"); +var _common = require("./common"); +var _extractPeaksEdit = require("../../helpers/extractPeaksEdit"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, function-paren-newline, +react/function-component-definition, function-call-argument-newline, +react/require-default-props */ + +const styles = () => Object.assign({}, _common.commonStyle); +const getAxesSelection = (axesUnitsSt, curveIdx) => { + const axes = axesUnitsSt?.axes; + if (!Array.isArray(axes) || axes.length === 0) return { + xUnit: '', + yUnit: '' + }; + const idx = Number.isFinite(curveIdx) ? curveIdx : 0; + return axes[idx] || axes[0] || { + xUnit: '', + yUnit: '' + }; +}; +const resolveAxisLabels = (xLabel, yLabel, axesUnitsSt, curveIdx) => { + const { + xUnit, + yUnit + } = getAxesSelection(axesUnitsSt, curveIdx); + return { + xLabel: xUnit === '' ? xLabel : xUnit, + yLabel: yUnit === '' ? yLabel : yUnit + }; +}; +const getBaseCurrentUnit = label => /mA/i.test(String(label)) ? 'mA' : 'A'; +const buildCvAxisYLabel = (yLabel, cyclicvoltaSt) => { + const baseUnit = getBaseCurrentUnit(yLabel); + const areaUnit = cyclicvoltaSt?.areaUnit || 'cm²'; + if (cyclicvoltaSt?.useCurrentDensity) { + return `Current density in ${baseUnit}/${areaUnit}`; + } + return `Current in ${baseUnit}`; +}; +const computeCvYScaleFactor = (feature, cyclicvoltaSt) => { + if (!cyclicvoltaSt?.useCurrentDensity) return 1.0; + const rawArea = (cyclicvoltaSt.areaValue === '' ? 1.0 : cyclicvoltaSt.areaValue) || 1.0; + const areaUnit = cyclicvoltaSt.areaUnit || 'cm²'; + const safeArea = rawArea > 0 ? rawArea : 1.0; + const areaInCm2 = areaUnit === 'mm²' ? safeArea / 100.0 : safeArea; + let factor = 1.0 / areaInCm2; + const baseY = feature && feature.yUnit ? String(feature.yUnit) : 'A'; + if (/mA/i.test(baseY)) { + factor *= 1000.0; + } + if (areaUnit === 'mm²') { + factor /= 100.0; + } + return factor; +}; +const defaultThreshold = { + isEdit: true, + value: false, + upper: false, + lower: false +}; +const pickFromList = (list, index, fallback = null) => Array.isArray(list) && list[index] !== undefined ? list[index] : fallback; +const hasBoolean = value => typeof value === 'boolean'; +const resolveOptionalBooleanFlags = analysis => { + const flags = {}; + if (hasBoolean(analysis?.keepPred)) flags.keepPred = analysis.keepPred; + if (hasBoolean(analysis?.simulatenmr)) flags.simulatenmr = analysis.simulatenmr; + return flags; +}; +const buildSpectrumPayload = ({ + feature, + curveIdx, + editPeakSt, + thresList, + shiftSt, + layoutSt, + scanSt, + integrationSt, + multiplicitySt, + waveLengthSt, + cyclicvoltaSt, + axesUnitsSt, + detectorSt, + dscMetaData, + isAscend, + isIntensity, + analysis, + decimalSt +}) => { + const threshold = thresList?.[curveIdx] || thresList?.[0] || defaultThreshold; + const editPeakAtIndex = Object.assign({}, editPeakSt, { + selectedIdx: curveIdx + }); + 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 { + xLabel, + yLabel + } = resolveAxisLabels(feature?.xUnit, feature?.yUnit, axesUnitsSt, curveIdx); + const axisYLabel = _format.default.isCyclicVoltaLayout(layoutSt) ? buildCvAxisYLabel(yLabel, cyclicvoltaSt) : yLabel; + const axisDisplay = { + xLabel, + yLabel: axisYLabel + }; + const cvDisplay = _format.default.isCyclicVoltaLayout(layoutSt) ? { + mode: cyclicvoltaSt?.useCurrentDensity ? 'density' : 'current', + yScaleFactor: computeCvYScaleFactor(feature, cyclicvoltaSt) + } : null; + const cyclicvoltaPayload = { + ...cyclicvoltaSt, + axisDisplay, + cvDisplay + }; + const optionalBooleanFlags = resolveOptionalBooleanFlags(analysis); + return { + peaks: peaksEdit, + layout: layoutSt, + shift, + scan, + thres, + isAscend, + isIntensity, + analysis, + decimal: decimalSt, + integration, + multiplicity, + waveLength: waveLengthSt, + cyclicvoltaSt: cyclicvoltaPayload, + curveSt: { + curveIdx + }, + axesUnitsSt, + detectorSt, + dscMetaData, + ...optionalBooleanFlags + }; +}; +const onClickCb = (operationValue, isAscend, isIntensity, layoutSt, shiftSt, analysis, decimalSt, integrationSt, multiplicitySt, waveLengthSt, cyclicvoltaSt, curveSt, axesUnitsSt, detectorSt, dscMetaData, curveList, editPeakSt, thresList, scanSt, feature) => () => { + const defaultCurves = feature ? [{ + feature + }] : []; + const curves = Array.isArray(curveList) && curveList.length > 0 ? curveList : defaultCurves; + const fallbackIdx = Number.isFinite(curveSt?.curveIdx) ? curveSt.curveIdx : 0; + const indicesToSend = curves.length > 0 ? curves.map((_, index) => index) : [fallbackIdx]; + const spectraList = indicesToSend.map(curveIdx => { + const curve = curves[curveIdx] || {}; + const curveFeature = curve.feature || feature; + return buildSpectrumPayload({ + feature: curveFeature, + curveIdx, + editPeakSt, + thresList, + shiftSt, + layoutSt, + scanSt, + integrationSt, + multiplicitySt, + waveLengthSt, + cyclicvoltaSt, + axesUnitsSt, + detectorSt, + dscMetaData, + isAscend, + isIntensity, + analysis, + decimalSt + }); + }); + const payload = { + spectra_list: spectraList + }; + if (Number.isFinite(curveSt?.curveIdx)) { + payload.curveSt = { + curveIdx: curveSt.curveIdx + }; + } + operationValue(payload); +}; +const BtnSubmit = ({ + classes, + operation, + feature, + isAscend, + isIntensity, + editPeakSt, + thresList, + layoutSt, + shiftSt, + scanSt, + forecastSt, + decimalSt, + integrationSt, + multiplicitySt, + waveLengthSt, + cyclicvoltaSt, + curveSt, + curveList, + axesUnitsSt, + detectorSt, + metaSt +}) => { + // const disBtn = peaksEdit.length === 0 || statusSt.btnSubmit || disabled; + const { + dscMetaData + } = metaSt; + const isCvLayout = _format.default.isCyclicVoltaLayout(layoutSt); + const { + xLabel, + yLabel + } = resolveAxisLabels(feature?.xUnit, feature?.yUnit, axesUnitsSt, curveSt?.curveIdx); + const axisYLabel = isCvLayout ? buildCvAxisYLabel(yLabel, cyclicvoltaSt) : yLabel; + const axisDisplay = { + xLabel, + yLabel: axisYLabel + }; + const cvDisplay = isCvLayout ? { + mode: cyclicvoltaSt?.useCurrentDensity ? 'density' : 'current', + yScaleFactor: computeCvYScaleFactor(feature, cyclicvoltaSt) + } : null; + const cyclicvoltaPayload = { + ...cyclicvoltaSt, + axisDisplay, + cvDisplay + }; + if (!operation) return null; + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Submit" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)('btn-sv-bar-submit'), + 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), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_PlayCircleOutline.default, { + className: classes.icon + }) + }) + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + editPeakSt: state.editPeak.present, + thresList: state.threshold.list, + layoutSt: state.layout, + shiftSt: state.shift, + scanSt: state.scan, + forecastSt: state.forecast, + decimalSt: state.submit.decimal, + integrationSt: state.integration.present, + multiplicitySt: state.multiplicity.present, + waveLengthSt: state.wavelength, + cyclicvoltaSt: state.cyclicvolta, + curveSt: state.curve, + curveList: state.curve.listCurves, + axesUnitsSt: state.axesUnits, + detectorSt: state.detector, + metaSt: state.meta +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({}, dispatch); +BtnSubmit.propTypes = { + classes: _propTypes.default.object.isRequired, + feature: _propTypes.default.object.isRequired, + isAscend: _propTypes.default.bool.isRequired, + isIntensity: _propTypes.default.bool.isRequired, + operation: _propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.bool]).isRequired, + editPeakSt: _propTypes.default.object.isRequired, + thresList: _propTypes.default.array.isRequired, + layoutSt: _propTypes.default.string.isRequired, + shiftSt: _propTypes.default.object.isRequired, + scanSt: _propTypes.default.object.isRequired, + forecastSt: _propTypes.default.object.isRequired, + decimalSt: _propTypes.default.number.isRequired, + integrationSt: _propTypes.default.object.isRequired, + multiplicitySt: _propTypes.default.object.isRequired, + waveLengthSt: _propTypes.default.object.isRequired, + cyclicvoltaSt: _propTypes.default.object.isRequired, + curveSt: _propTypes.default.object, + curveList: _propTypes.default.array.isRequired, + axesUnitsSt: _propTypes.default.object.isRequired, + detectorSt: _propTypes.default.object.isRequired, + metaSt: _propTypes.default.object.isRequired +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _styles.withStyles)(styles))(BtnSubmit); \ No newline at end of file diff --git a/dist/components/cmd_bar/r06_predict_btn.js b/dist/components/cmd_bar/r06_predict_btn.js new file mode 100644 index 00000000..0a6b1163 --- /dev/null +++ b/dist/components/cmd_bar/r06_predict_btn.js @@ -0,0 +1,234 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _reactRedux = require("react-redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _GpsFixedOutlined = _interopRequireDefault(require("@mui/icons-material/GpsFixedOutlined")); +var _HelpOutlineOutlined = _interopRequireDefault(require("@mui/icons-material/HelpOutlineOutlined")); +var _styles = require("@mui/styles"); +var _common = require("./common"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _carbonFeatures = require("../../helpers/carbonFeatures"); +var _extractPeaksEdit = require("../../helpers/extractPeaksEdit"); +var _ui = require("../../actions/ui"); +var _list_ui = require("../../constants/list_ui"); +var _chem = require("../../helpers/chem"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, function-paren-newline, +max-len, react/function-component-definition, +function-call-argument-newline, react/require-default-props */ + +const styles = () => Object.assign({}, _common.commonStyle, { + tTxt: { + fontSize: '0.8rem', + fontFamily: 'Helvetica', + marginRight: 5 + }, + btnWidthUnknown: { + minWidth: 30, + width: 30 + }, + btnWidthIr: { + minWidth: 30, + width: 30 + }, + btnWidthNmr: { + minWidth: 80, + width: 80 + } +}); +const MuPredictButton = (0, _styles.withStyles)({ + root: { + border: '1px solid #ccc', + borderRadius: 4, + fontFamily: 'Helvetica', + fontSize: 20, + height: 30, + lineHeight: '20px', + padding: 0 + } +})(_material.Button); +const onClickFail = (layoutSt, simuCount, realCount) => { + const feature = _format.default.is13CLayout(layoutSt) ? 'peak' : 'multiplet'; + return () => alert(`Selected ${feature} count (${realCount}) must be larger than 0, and must be eqal or less than simulated count (${simuCount}).`); // eslint-disable-line +}; +const onClickReady = (forecast, peaksEdit, layoutSt, scan, shiftSt, thres, analysis, integrationSt, multiplicitySt, setUiViewerTypeAct, curveSt) => { + const { + btnCb + } = forecast; + if (!btnCb) { + return () => alert('[Developer Warning] You need to implement btnCb in forecast!'); // eslint-disable-line + } + return () => { + setUiViewerTypeAct(_list_ui.LIST_UI_VIEWER_TYPE.ANALYSIS); + return btnCb({ + peaks: peaksEdit, + layout: layoutSt, + scan, + thres, + analysis, + integration: integrationSt, + multiplicity: multiplicitySt, + shift: shiftSt, + curveSt + }); + }; +}; +const onClicUnknown = (feature, forecast, peaksEdit, layoutSt, scan, shiftSt, thres, analysis, integrationSt, multiplicitySt, curveSt) => { + const { + refreshCb + } = forecast; + if (!refreshCb) { + return () => alert('[Developer Warning] You need to implement refreshCb in forecast!'); // eslint-disable-line + } + return () => refreshCb({ + peaks: peaksEdit, + layout: layoutSt, + scan, + shift: shiftSt, + thres, + analysis, + integration: integrationSt, + multiplicity: multiplicitySt, + curveSt + }); +}; +const counterText = (classes, isIr, realCount, uniqCount, simuCount) => isIr ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: `${realCount}/${uniqCount}/${simuCount}` +}); +const renderBtnPredict = (classes, isIr, realCount, uniqCount, simuCount, color, btnWidthCls, onClick) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Predict" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("br", {}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "- Selected features must be eqal or less than simulated features." + })] + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(MuPredictButton, { + className: (0, _classnames.default)('btn-sv-bar-submit', btnWidthCls), + style: { + color + }, + onClick: onClick, + children: [counterText(classes, isIr, realCount, uniqCount, simuCount), /*#__PURE__*/(0, _jsxRuntime.jsx)(_GpsFixedOutlined.default, { + className: classes.icon + })] + }) +}); +const renderBtnUnknown = (classes, onClick) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Refresh Simulation" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("br", {}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "- Simulation must be refreshed before making a prediction." + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("br", {}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "- If you continue to see this button after clicking it, the server is not ready. Please wait for a while." + })] + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(MuPredictButton, { + className: (0, _classnames.default)('btn-sv-bar-submit', classes.btnWidthUnknown), + style: { + color: 'orange' + }, + onClick: onClick, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_HelpOutlineOutlined.default, { + className: classes.icon + }) + }) +}); +const BtnPredict = ({ + classes, + feature, + forecast, + layoutSt, + simulationSt, + editPeakSt, + scanSt, + shiftSt, + thresSt, + integrationSt, + multiplicitySt, + setUiViewerTypeAct, + curveSt +}) => { + const is13Cor1H = _format.default.is13CLayout(layoutSt) || _format.default.is1HLayout(layoutSt); + const isIr = _format.default.isIrLayout(layoutSt); + if (!(is13Cor1H || isIr)) return null; + const oriPeaksEdit = (0, _extractPeaksEdit.extractPeaksEdit)(feature, editPeakSt, thresSt, shiftSt, layoutSt); + const peaksEdit = _format.default.rmShiftFromPeaks(oriPeaksEdit, shiftSt); + const scan = (0, _chem.Convert2Scan)(feature, scanSt); + const thres = (0, _chem.Convert2Thres)(feature, thresSt); + const simuCount = simulationSt.nmrSimPeaks.length; + const uniqCount = [...new Set(simulationSt.nmrSimPeaks)].length; + let realCount = 0; + if (_format.default.is13CLayout(layoutSt)) { + realCount = (0, _carbonFeatures.carbonFeatures)(peaksEdit, multiplicitySt).length; + } else { + const { + curveIdx + } = curveSt; + const { + multiplicities + } = multiplicitySt; + const selectedMultiplicity = multiplicities[curveIdx]; + const { + stack + } = selectedMultiplicity; + realCount = stack.length; + } + if (is13Cor1H && simuCount === 0) { + const onClickUnknownCb = onClicUnknown(feature, forecast, peaksEdit, layoutSt, scan, shiftSt, thres, forecast.predictions, integrationSt, multiplicitySt, curveSt); + return renderBtnUnknown(classes, onClickUnknownCb); + } + const predictable = isIr || simuCount >= realCount && realCount > 0; + const color = predictable ? 'green' : 'red'; + const onClick = predictable ? onClickReady(forecast, peaksEdit, layoutSt, scan, shiftSt, thres, forecast.predictions, integrationSt, multiplicitySt, setUiViewerTypeAct, curveSt) : onClickFail(layoutSt, simuCount, realCount); + const btnWidthCls = isIr ? classes.btnWidthIr : classes.btnWidthNmr; + return renderBtnPredict(classes, isIr, realCount, uniqCount, simuCount, color, btnWidthCls, onClick); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + layoutSt: state.layout, + simulationSt: state.simulation, + editPeakSt: state.editPeak.present, + scanSt: state.scan, + shiftSt: state.shift, + thresSt: state.threshold.list[state.curve.curveIdx], + integrationSt: state.integration.present, + multiplicitySt: state.multiplicity.present, + curveSt: state.curve +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setUiViewerTypeAct: _ui.setUiViewerType +}, dispatch); +BtnPredict.propTypes = { + classes: _propTypes.default.object.isRequired, + feature: _propTypes.default.object.isRequired, + forecast: _propTypes.default.object.isRequired, + layoutSt: _propTypes.default.string.isRequired, + simulationSt: _propTypes.default.array.isRequired, + editPeakSt: _propTypes.default.object.isRequired, + scanSt: _propTypes.default.object.isRequired, + shiftSt: _propTypes.default.object.isRequired, + thresSt: _propTypes.default.object.isRequired, + integrationSt: _propTypes.default.object.isRequired, + multiplicitySt: _propTypes.default.object.isRequired, + setUiViewerTypeAct: _propTypes.default.func.isRequired, + curveSt: _propTypes.default.object +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _styles.withStyles)(styles))(BtnPredict); \ No newline at end of file diff --git a/dist/components/cmd_bar/r07_wavelength_btn.js b/dist/components/cmd_bar/r07_wavelength_btn.js new file mode 100644 index 00000000..326026e6 --- /dev/null +++ b/dist/components/cmd_bar/r07_wavelength_btn.js @@ -0,0 +1,86 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _reactRedux = require("react-redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _wavelength = require("../../actions/wavelength"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _common = require("./common"); +var _list_wavelength = require("../../constants/list_wavelength"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, react/jsx-one-expression-per-line, +react/function-component-definition */ + +const styles = () => Object.assign({ + fieldShift: { + width: 160 + }, + fieldLayout: { + width: 100 + } +}, _common.commonStyle); +const wavelengthSelect = (classes, waveLengthSt, layoutSt, updateWaveLengthAct) => { + if (!_format.default.isXRDLayout(layoutSt)) { + return /*#__PURE__*/(0, _jsxRuntime.jsx)("i", {}); + } + const onChange = e => updateWaveLengthAct(e.target.value); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldLayout), + variant: "outlined", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + id: "select-wavelength-label", + className: (0, _classnames.default)(classes.selectLabel, 'select-sv-bar-label'), + children: "Wavelength" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Select, { + labelId: "select-wavelength-label", + label: "Wavelength", + value: waveLengthSt, + onChange: onChange, + className: (0, _classnames.default)(classes.selectInput, 'input-sv-bar-layout'), + children: _list_wavelength.LIST_WAVE_LENGTH.map(item => { + // eslint-disable-line + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: item, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: [item.label, " (", item.value, " ", item.unit, ")"] + }) + }); + }) + })] + }); +}; +const Wavelength = ({ + classes, + waveLengthSt, + layoutSt, + updateWaveLengthAct +}) => /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: classes.groupRight, + children: wavelengthSelect(classes, waveLengthSt, layoutSt, updateWaveLengthAct) +}); +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + waveLengthSt: state.wavelength, + layoutSt: state.layout +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + updateWaveLengthAct: _wavelength.updateWaveLength +}, dispatch); +Wavelength.propTypes = { + classes: _propTypes.default.object.isRequired, + layoutSt: _propTypes.default.string.isRequired, + waveLengthSt: _propTypes.default.object.isRequired, + updateWaveLengthAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _withStyles.default)(styles)(Wavelength)); \ No newline at end of file diff --git a/dist/components/cmd_bar/r08_change_axes.js b/dist/components/cmd_bar/r08_change_axes.js new file mode 100644 index 00000000..b8d3ee75 --- /dev/null +++ b/dist/components/cmd_bar/r08_change_axes.js @@ -0,0 +1,189 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireWildcard(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _reactRedux = require("react-redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _common = require("./common"); +var _list_layout = require("../../constants/list_layout"); +var _list_axes = require("../../constants/list_axes"); +var _axes = require("../../actions/axes"); +var _jsxRuntime = require("react/jsx-runtime"); +function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } +/* eslint-disable prefer-object-spread, react/jsx-one-expression-per-line, +react/function-component-definition, +max-len, no-unused-vars, no-multiple-empty-lines */ + +const listLayoutToShow = [_list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY]; +const styles = () => Object.assign({ + fieldShift: { + width: 160 + }, + fieldLayout: { + width: 100 + } +}, _common.commonStyle); +const axisX = (classes, layoutSt, axesUnitsSt, updateXAxisAct, curveSt) => { + const optionsAxisX = _list_axes.LIST_AXES.x; + const options = optionsAxisX[layoutSt]; + const { + curveIdx + } = curveSt; + const onChange = e => updateXAxisAct({ + value: e.target.value, + curveIndex: curveIdx + }); + const { + axes + } = axesUnitsSt; + let selectedAxes = axes[curveIdx]; + if (!selectedAxes) { + selectedAxes = { + xUnit: '', + yUnit: '' + }; + } + const { + xUnit + } = selectedAxes; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldLayout), + variant: "outlined", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Select, { + labelId: "select-x-axis-label", + label: "x-Axis", + value: xUnit, + onChange: onChange, + className: (0, _classnames.default)(classes.selectInput, 'input-sv-bar-layout'), + children: options.map(item => { + // eslint-disable-line + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: item, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: item === '' ? 'Default' : item + }) + }, item); + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + id: "select-x-axis-label", + className: (0, _classnames.default)(classes.txtLabelTopInput), + children: "x-Axis" + })] + }); +}; +const axisY = (classes, layoutSt, axesUnitsSt, updateYAxisAct, curveSt) => { + const optionsAxisX = _list_axes.LIST_AXES.y; + const options = optionsAxisX[layoutSt]; + const { + curveIdx + } = curveSt; + const onChange = e => updateYAxisAct({ + value: e.target.value, + curveIndex: curveIdx + }); + const { + axes + } = axesUnitsSt; + let selectedAxes = axes[curveIdx]; + if (!selectedAxes) { + selectedAxes = { + xUnit: '', + yUnit: '' + }; + } + const yUnit = ''; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldLayout), + variant: "outlined", + disabled: true, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Select, { + labelId: "select-y-axis-label", + label: "y-Axis", + value: yUnit, + onChange: onChange, + className: (0, _classnames.default)(classes.selectInput, 'input-sv-bar-layout'), + disabled: true, + children: options.map(item => { + // eslint-disable-line + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: item, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: item === '' ? 'Default' : item + }) + }, item); + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + id: "select-y-axis-label", + className: (0, _classnames.default)(classes.txtLabelTopInput), + children: "y-Axis" + })] + }); +}; +const showSelect = (classes, layoutSt, curveSt, axesUnitsSt, updateXAxisAct, updateYAxisAct) => { + if (!listLayoutToShow.includes(layoutSt)) { + return /*#__PURE__*/(0, _jsxRuntime.jsx)("i", {}); + } + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + children: [axisX(classes, layoutSt, axesUnitsSt, updateXAxisAct, curveSt), axisY(classes, layoutSt, axesUnitsSt, updateYAxisAct, curveSt)] + }); +}; +const ChangeAxes = ({ + classes, + layoutSt, + curveSt, + axesUnitsSt, + updateXAxisAct, + updateYAxisAct +}) => { + const { + curveIdx + } = curveSt; + const axes = axesUnitsSt?.axes || []; + (0, _react.useEffect)(() => { + if (layoutSt !== _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY) return; + const selectedAxes = axes[curveIdx] || { + yUnit: '' + }; + if (selectedAxes.yUnit !== '') { + updateYAxisAct({ + value: '', + curveIndex: curveIdx + }); + } + }, [layoutSt, axes, curveIdx, updateYAxisAct]); + return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: classes.groupRight, + "data-testid": "ChangeAxes", + children: showSelect(classes, layoutSt, curveSt, axesUnitsSt, updateXAxisAct, updateYAxisAct) + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + layoutSt: state.layout, + curveSt: state.curve, + axesUnitsSt: state.axesUnits +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + updateXAxisAct: _axes.updateXAxis, + updateYAxisAct: _axes.updateYAxis +}, dispatch); +ChangeAxes.propTypes = { + classes: _propTypes.default.object.isRequired, + layoutSt: _propTypes.default.string.isRequired, + curveSt: _propTypes.default.object.isRequired, + axesUnitsSt: _propTypes.default.object.isRequired, + updateXAxisAct: _propTypes.default.func.isRequired, + updateYAxisAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _withStyles.default)(styles)(ChangeAxes)); \ No newline at end of file diff --git a/dist/components/cmd_bar/r09_detector.js b/dist/components/cmd_bar/r09_detector.js new file mode 100644 index 00000000..3efdcf7c --- /dev/null +++ b/dist/components/cmd_bar/r09_detector.js @@ -0,0 +1,104 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _reactRedux = require("react-redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _detector = require("../../actions/detector"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _common = require("./common"); +var _list_detectors = require("../../constants/list_detectors"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, react/jsx-one-expression-per-line, +react/function-component-definition */ + +const styles = () => Object.assign({ + fieldShift: { + width: 160 + }, + fieldLayout: { + width: 100 + } +}, _common.commonStyle); +const detectorSelect = (classes, detectorSt, curveSt, layoutSt, updateDetectorAct) => { + if (!_format.default.isSECLayout(layoutSt)) { + return /*#__PURE__*/(0, _jsxRuntime.jsx)("i", {}); + } + const { + curveIdx + } = curveSt; + const { + curves + } = detectorSt; + const getSelectedDetectorForCurve = (_detectorSt, targetCurveIdx) => { + const targetCurve = curves.find(curve => curve.curveIdx === targetCurveIdx); + return targetCurve ? targetCurve.selectedDetector : ''; + }; + const selectedDetector = getSelectedDetectorForCurve(detectorSt, curveIdx); + const onChange = e => updateDetectorAct({ + curveIdx, + selectedDetector: e.target.value + }); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldLayout), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + id: "select-detector-label", + className: (0, _classnames.default)(classes.selectLabel, 'select-sv-bar-label'), + children: "Detector" + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Select, { + labelId: "select-detector-label", + label: "Detector", + value: selectedDetector, + onChange: onChange, + className: (0, _classnames.default)(classes.selectInput, 'input-sv-bar-layout'), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: "", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout') + }) + }), _list_detectors.LIST_DETECTORS.map(item => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: item, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: item.name + }) + }))] + })] + }); +}; +const Detector = ({ + classes, + detectorSt, + curveSt, + layoutSt, + updateDetectorAct +}) => /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: classes.groupRight, + children: detectorSelect(classes, detectorSt, curveSt, layoutSt, updateDetectorAct) +}); +const mapStateToProps = (state, _props) => ( +// eslint-disable-line +{ + detectorSt: state.detector, + curveSt: state.curve, + layoutSt: state.layout +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + updateDetectorAct: _detector.updateDetector +}, dispatch); +Detector.propTypes = { + classes: _propTypes.default.object.isRequired, + layoutSt: _propTypes.default.string.isRequired, + curveSt: _propTypes.default.object.isRequired, + updateDetectorAct: _propTypes.default.func.isRequired, + detectorSt: _propTypes.default.object.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _withStyles.default)(styles)(Detector)); \ No newline at end of file diff --git a/dist/components/cmd_bar/r10_cv_density.js b/dist/components/cmd_bar/r10_cv_density.js new file mode 100644 index 00000000..4f850b0b --- /dev/null +++ b/dist/components/cmd_bar/r10_cv_density.js @@ -0,0 +1,180 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _reactRedux = require("react-redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _list_layout = require("../../constants/list_layout"); +var _common = require("./common"); +var _cyclic_voltammetry = require("../../actions/cyclic_voltammetry"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable react/function-component-definition, react/jsx-one-expression-per-line, +react/jsx-boolean-value */ + +const styles = () => ({ + ..._common.commonStyle, + fieldArea: { + width: 100 + }, + fieldUnit: { + width: 75 + }, + toggleGroup: { + height: 30, + marginLeft: 8 + }, + toggleBtn: { + fontSize: 10, + padding: '0 6px', + height: 30, + minHeight: 30, + lineHeight: '30px', + textTransform: 'none' + } +}); +const units = ['cm²', 'mm²']; +const CvDensityControls = ({ + classes, + layoutSt, + areaValue, + areaUnit, + useCurrentDensity, + setAreaValueAct, + setAreaUnitAct, + toggleDensityAct +}) => { + if (layoutSt !== _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY) return /*#__PURE__*/(0, _jsxRuntime.jsx)("i", {}); + const handleAreaChange = e => { + const raw = e.target.value; + if (raw === '') { + setAreaValueAct(''); + return; + } + const val = parseFloat(raw); + if (Number.isNaN(val)) return; + if (val < 0) return; + setAreaValueAct(val); + }; + const handleAreaBlur = e => { + const raw = e.target.value; + const val = parseFloat(raw); + if (raw === '' || Number.isNaN(val) || val <= 0) { + setAreaValueAct(1.0); + } + }; + const handleUnitChange = e => { + const newUnit = e.target.value; + const currVal = areaValue; + if (currVal !== '' && Number.isFinite(Number(currVal))) { + const num = Number(currVal); + const from = areaUnit; + const to = newUnit; + let converted = num; + if (from === 'cm²' && to === 'mm²') converted = num * 100.0; + if (from === 'mm²' && to === 'cm²') converted = num / 100.0; + setAreaValueAct(converted); + } + setAreaUnitAct(newUnit); + }; + const handleToggle = (_, val) => { + if (val === null) return; + const shouldBeDensity = val === 'density'; + if (shouldBeDensity !== useCurrentDensity) { + toggleDensityAct(); + } + }; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: classes.group, + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldArea), + variant: "outlined", + disabled: !useCurrentDensity, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + htmlFor: "cv-area", + className: (0, _classnames.default)(classes.selectLabel, 'select-sv-bar-label'), + children: "WE-ECSA" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.OutlinedInput, { + id: "cv-area", + label: "WE-ECSA", + type: "number", + inputProps: { + step: '0.0001', + min: '0' + }, + value: areaValue, + onChange: handleAreaChange, + onBlur: handleAreaBlur, + className: (0, _classnames.default)(classes.txtInput, 'input-sv-bar-layout'), + disabled: !useCurrentDensity + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, { + className: (0, _classnames.default)(classes.fieldUnit), + variant: "outlined", + disabled: !useCurrentDensity, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputLabel, { + id: "cv-area-unit", + className: (0, _classnames.default)(classes.selectLabel, 'select-sv-bar-label'), + children: "Unit" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Select, { + value: areaUnit, + onChange: handleUnitChange, + labelId: "cv-area-unit", + label: "Unit", + className: (0, _classnames.default)(classes.selectInput, 'input-sv-bar-layout'), + disabled: !useCurrentDensity, + children: units.map(u => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: u, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtOpt, 'option-sv-bar-layout'), + children: u + }) + }, u)) + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.ToggleButtonGroup, { + exclusive: true, + size: "small", + value: useCurrentDensity ? 'density' : 'current', + onChange: handleToggle, + className: (0, _classnames.default)(classes.selectInput, classes.toggleGroup, 'input-sv-bar-layout'), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.ToggleButton, { + value: "current", + className: (0, _classnames.default)(classes.txtOpt, classes.toggleBtn), + children: "Current" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.ToggleButton, { + value: "density", + className: (0, _classnames.default)(classes.txtOpt, classes.toggleBtn), + children: "Current / Area" + })] + })] + }); +}; +CvDensityControls.propTypes = { + classes: _propTypes.default.object.isRequired, + layoutSt: _propTypes.default.string.isRequired, + areaValue: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]).isRequired, + areaUnit: _propTypes.default.string.isRequired, + useCurrentDensity: _propTypes.default.bool.isRequired, + setAreaValueAct: _propTypes.default.func.isRequired, + setAreaUnitAct: _propTypes.default.func.isRequired, + toggleDensityAct: _propTypes.default.func.isRequired +}; +const mapStateToProps = state => ({ + layoutSt: state.layout, + areaValue: state.cyclicvolta.areaValue, + areaUnit: state.cyclicvolta.areaUnit, + useCurrentDensity: state.cyclicvolta.useCurrentDensity +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setAreaValueAct: _cyclic_voltammetry.setCyclicVoltaAreaValue, + setAreaUnitAct: _cyclic_voltammetry.setCyclicVoltaAreaUnit, + toggleDensityAct: _cyclic_voltammetry.toggleCyclicVoltaDensity +}, dispatch); +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _withStyles.default)(styles)(CvDensityControls)); \ No newline at end of file diff --git a/dist/components/cmd_bar/tri_btn.js b/dist/components/cmd_bar/tri_btn.js new file mode 100644 index 00000000..64800f48 --- /dev/null +++ b/dist/components/cmd_bar/tri_btn.js @@ -0,0 +1,131 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _styles = require("@mui/styles"); +var _Tooltip = _interopRequireDefault(require("@mui/material/Tooltip")); +var _cfg = _interopRequireDefault(require("../../helpers/cfg")); +var _common = require("./common"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread */ + +const styles = () => Object.assign({ + btnYes: { + color: 'green' + }, + btnNo: { + color: 'red' + }, + btnTxtConfirm: { + fontFamily: 'Helvetica', + fontSize: 12 + } +}, _common.commonStyle); +class TriBtn extends _react.default.Component { + constructor(props) { + super(props); + this.state = { + toggled: false + }; + this.onToggle = this.onToggle.bind(this); + this.renderStageOne = this.renderStageOne.bind(this); + this.renderStageTwo = this.renderStageTwo.bind(this); + } + onToggle(e) { + e.stopPropagation(); + e.preventDefault(); + const { + toggled + } = this.state; + this.setState({ + toggled: !toggled + }); + } + renderStageOne() { + const { + content, + layoutSt, + children, + isClearAllDisabled + } = this.props; + const { + tp + } = content; + const title = /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: tp + }); + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { + title: title, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)('btn-sv-bar-one'), + disabled: isClearAllDisabled === false ? false : _cfg.default.btnCmdMpy(layoutSt) && _cfg.default.btnCmdIntg(layoutSt), + onClick: this.onToggle, + children: children + }) + }) + }); + } + renderStageTwo() { + const { + classes, + layoutSt, + cb + } = this.props; + const onExec = e => { + cb(); + this.onToggle(e); + }; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + disabled: _cfg.default.btnCmdMpy(layoutSt) && _cfg.default.btnCmdIntg(layoutSt), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtLabel, 'txt-sv-bar-desc'), + children: "Delete ALL?" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)('btn-sv-bar-yes'), + onClick: onExec, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txt, classes.btnYes, 'txt-sv-bar-yes'), + children: "Y" + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_common.MuButton, { + className: (0, _classnames.default)('btn-sv-bar-no'), + onClick: this.onToggle, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txt, classes.btnNo, 'txt-sv-bar-no'), + children: "N" + }) + })] + }); + } + render() { + const { + toggled + } = this.state; + return !toggled ? this.renderStageOne() : this.renderStageTwo(); + } +} +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + layoutSt: state.layout +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({}, dispatch); +TriBtn.propTypes = { + classes: _propTypes.default.object.isRequired, + layoutSt: _propTypes.default.string.isRequired, + content: _propTypes.default.object.isRequired, + cb: _propTypes.default.func.isRequired, + children: _propTypes.default.node.isRequired, + isClearAllDisabled: _propTypes.default.bool.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _styles.withStyles)(styles)(TriBtn)); \ No newline at end of file diff --git a/dist/components/common/chem.js b/dist/components/common/chem.js new file mode 100644 index 00000000..7a15e0be --- /dev/null +++ b/dist/components/common/chem.js @@ -0,0 +1,115 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +/* eslint-disable */ + +const SmaToSvg = sma => { + switch (sma) { + case 'C-,:O': + return ' O '; + case 'C-,:C(=O)-,:O-,:C': + return ' O O '; + case 'C-,:O-,:c': + return ' O '; + case 'c-,:[Cl]': + return ' Cl '; + case 'c:,-[n&H1]:,-c': + return ' N '; + case 'c-,:O': + return ' O '; + case 'C-,:[Cl]': + return ' Cl '; + case 'C-,:C(-,:C)=O': + return ' O '; + case 'c-,:[N&+](=O)-,:[O&-]': + return ' N + O O - '; + case 'C-,:C=C': + return ' '; + case 'c-,:[Br]': + return ' Br '; + case 'C-,:O-,:C': + return ' O '; + case 'C-,:[Br]': + return ' Br '; + case 'C-,:C(-,:c)=O': + return ' O '; + case 'c-,:N': + return ' N '; + case 'C-,:F': + return ' F '; + case 'c-,:C(=O)-,:O-,:C': + return ' O O '; + case 'c:,-o:,-c': + return ' O '; + case 'C-,:C(=O)-,:O': + return ' O O '; + case 'c-,:F': + return ' F '; + case 'c=O': + return ' O '; + case 'C-,:N-,:C': + return ' N '; + case 'C-,:N(-,:C)-,:C': + return ' N '; + case 'C-,:N': + return ' N '; + case 'C-,:n(-,:c):,-c': + return ' N '; + case 'c-,:C(=O)-,:O': + return ' O O '; + case 'c-,:C=O': + return ' O '; + case 'C-,:C#N': + return ' N '; + case 'C-,:C=C-,:C': + return ' '; + case 'C-,:C(=O)-,:N-,:C': + return ' O N '; + case 'c-,:C#N': + return ' N '; + case 'C-,:C(=O)-,:N-,:c': + return ' O N '; + case 'C-,:C(-,:C)=C': + return ' '; + case 'c:,-s:,-c': + return ' S '; + case 'C-,:C#C-,:C': + return ' '; + case 'C#C-,:C': + return ' '; + case 'c-,:C(-,:c)=O': + return ' O '; + case 'C-,:C(-,:C)=C-,:C': + return ' '; + case 'c-,:N(-,:C)-,:C': + return ' N '; + case 'C-,:N-,:c': + return ' N '; + case 'C-,:O-,:C(-,:C)=O': + return ' O O '; + case 'c-,:O-,:C': + return ' O '; + case 'c:,-n:,-c': + return ' N '; + case 'C=C-,:C': + return ' '; + case 'c-,:C(-,:C)=O': + return ' O '; + case 'c:,-n(-,:c)-,:C': + return ' N '; + case 'C-,:N-,:C(-,:C)=O': + return ' N O '; + case 'c-,:N-,:C(-,:C)=O': + return ' N O '; + case 'C=C(-,:C)-,:C': + return ' '; + default: + return ' N.A. '; + } +}; + +/* eslint-enable */ +var _default = exports.default = SmaToSvg; \ No newline at end of file diff --git a/dist/components/common/comps.js b/dist/components/common/comps.js new file mode 100644 index 00000000..c366d9f5 --- /dev/null +++ b/dist/components/common/comps.js @@ -0,0 +1,15 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.TabLabel = void 0; +var _react = _interopRequireDefault(require("react")); +var _classnames = _interopRequireDefault(require("classnames")); +var _jsxRuntime = require("react/jsx-runtime"); +const TabLabel = (classes, label, extClsName = 'txt-tab-label') => /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tabLabel, extClsName), + children: label +}); +exports.TabLabel = TabLabel; \ No newline at end of file diff --git a/dist/components/common/draw.js b/dist/components/common/draw.js new file mode 100644 index 00000000..deba70ef --- /dev/null +++ b/dist/components/common/draw.js @@ -0,0 +1,51 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.drawMain = exports.drawLabel = exports.drawDisplay = exports.drawDestroy = exports.drawArrowOnCurve = void 0; +const d3 = require('d3'); +const drawMain = (klass, w, h) => { + d3.select(klass).append('svg').attr('class', 'd3Svg').attr('preserveAspectRatio', 'xMinYMin meet').attr('viewBox', `0 0 ${w} ${h}`); +}; +exports.drawMain = drawMain; +const drawLabel = (klass, cLabel, xLabel, yLabel) => { + d3.select(klass).selectAll('.xLabel').text(xLabel); + d3.select(klass).selectAll('.yLabel').text(yLabel); + if (cLabel) { + d3.select(klass).selectAll('.mark-text').text(cLabel); + } +}; +exports.drawLabel = drawLabel; +const drawDisplay = (klass, isHidden) => { + if (isHidden) { + d3.select(klass).selectAll('svg').style('width', 0); + } else { + d3.select(klass).selectAll('svg').style('width', '100%'); + } +}; +exports.drawDisplay = drawDisplay; +const drawDestroy = klass => d3.select(`${klass} > *`).remove(); +exports.drawDestroy = drawDestroy; +const drawArrowOnCurve = (klass, isHidden) => { + if (isHidden) { + d3.select(klass).selectAll('marker').remove(); + } else { + d3.select(klass).selectAll('marker').remove(); + const arrowLeft = d3.select(klass).selectAll('defs').append('marker').attr('id', 'arrow-left').attr('viewBox', '0 0 10 10').attr('refX', 5).attr('refY', 5).attr('markerWidth', 6).attr('markerHeight', 6).attr('orient', 'auto').attr('fill', '#00AA0099'); + arrowLeft.append('path').attr('d', 'M 0 0 L 10 5 L 0 10 z'); + + // const arrowRight = d3.select(klass).selectAll('defs') + // .append('marker') + // .attr('id', 'arrow-right') + // .attr('viewBox', '0 0 10 10') + // .attr('refX', 5) + // .attr('refY', 5) + // .attr('markerWidth', 6) + // .attr('markerHeight', 6) + // .attr('orient', 'auto-start-reverse'); + // arrowRight.append('path') + // .attr('d', 'M 0 0 L 10 5 L 0 10 z'); + } +}; +exports.drawArrowOnCurve = drawArrowOnCurve; \ No newline at end of file diff --git a/dist/components/d3_line/index.js b/dist/components/d3_line/index.js new file mode 100644 index 00000000..a9827f20 --- /dev/null +++ b/dist/components/d3_line/index.js @@ -0,0 +1,243 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _chem = require("../../helpers/chem"); +var _manager = require("../../actions/manager"); +var _ui = require("../../actions/ui"); +var _integration = require("../../actions/integration"); +var _line_focus = _interopRequireDefault(require("./line_focus")); +var _draw = require("../common/draw"); +var _list_ui = require("../../constants/list_ui"); +var _cyclic_voltammetry = require("../../actions/cyclic_voltammetry"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable no-mixed-operators */ + +const W = Math.round(window.innerWidth * 0.90 * 9 / 12); // ROI +const H = Math.round(window.innerHeight * 0.90 * 0.85); // ROI + +class ViewerLine extends _react.default.Component { + constructor(props) { + super(props); + const { + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct, + splitIntegrationAct + } = props; + this.rootKlass = '.d3Line'; + this.focus = new _line_focus.default({ + W, + H, + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct, + splitIntegrationAct + }); + this.normChange = this.normChange.bind(this); + } + componentDidMount() { + const { + seed, + peak, + cLabel, + xLabel, + yLabel, + feature, + freq, + comparisons, + tTrEndPts, + tSfPeaks, + editPeakSt, + layoutSt, + integationSt, + mtplySt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + isHidden, + wavelength, + axesUnitsSt, + resetAllAct + } = this.props; + (0, _draw.drawDestroy)(this.rootKlass); + resetAllAct(feature); + let xxLabel = xLabel; + let yyLabel = yLabel; + if (axesUnitsSt) { + const { + axes + } = axesUnitsSt; + const { + xUnit, + yUnit + } = axes[0]; + xxLabel = xUnit === '' ? xLabel : xUnit; + yyLabel = yUnit === '' ? yLabel : yUnit; + } + const filterSeed = seed; + const filterPeak = peak; + (0, _draw.drawMain)(this.rootKlass, W, H); + this.focus.create({ + filterSeed, + filterPeak, + freq, + comparisons, + tTrEndPts, + tSfPeaks, + editPeakSt, + layoutSt, + integationSt, + mtplySt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + wavelength + }); + (0, _draw.drawLabel)(this.rootKlass, cLabel, xxLabel, yyLabel); + (0, _draw.drawDisplay)(this.rootKlass, isHidden); + } + componentDidUpdate(prevProps) { + const { + seed, + peak, + cLabel, + xLabel, + yLabel, + freq, + comparisons, + tTrEndPts, + tSfPeaks, + editPeakSt, + layoutSt, + integationSt, + mtplySt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + isHidden, + wavelength, + axesUnitsSt + } = this.props; + this.normChange(prevProps); + let xxLabel = xLabel; + let yyLabel = yLabel; + if (axesUnitsSt) { + const { + axes + } = axesUnitsSt; + const { + xUnit, + yUnit + } = axes[0]; + xxLabel = xUnit === '' ? xLabel : xUnit; + yyLabel = yUnit === '' ? yLabel : yUnit; + } + const filterSeed = seed; + const filterPeak = peak; + this.focus.update({ + filterSeed, + filterPeak, + freq, + comparisons, + tTrEndPts, + tSfPeaks, + editPeakSt, + layoutSt, + integationSt, + mtplySt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + wavelength + }); + (0, _draw.drawLabel)(this.rootKlass, cLabel, xxLabel, yyLabel); + (0, _draw.drawDisplay)(this.rootKlass, isHidden); + } + componentWillUnmount() { + (0, _draw.drawDestroy)(this.rootKlass); + } + normChange(prevProps) { + const { + feature, + resetAllAct + } = this.props; + const oldFeature = prevProps.feature; + if (oldFeature !== feature) { + resetAllAct(feature); + } + } + render() { + return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: "d3Line" + }); + } +} +const mapStateToProps = (state, props) => ({ + seed: (0, _chem.Topic2Seed)(state, props), + peak: (0, _chem.Feature2Peak)(state, props), + freq: (0, _chem.ToFrequency)(state, props), + comparisons: (0, _chem.GetComparisons)(state, props), + tTrEndPts: (0, _chem.ToThresEndPts)(state, props), + tSfPeaks: (0, _chem.ToShiftPeaks)(state, props), + editPeakSt: state.editPeak.present, + layoutSt: state.layout, + integationSt: state.integration.present, + mtplySt: state.multiplicity.present, + sweepExtentSt: state.ui.sweepExtent, + isUiAddIntgSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_ADD, + isUiSplitIntgSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_SPLIT, + isUiNoBrushSt: _list_ui.LIST_NON_BRUSH_TYPES.indexOf(state.ui.sweepType) < 0, + wavelength: state.wavelength, + axesUnitsSt: state.axesUnits +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + resetAllAct: _manager.resetAll, + clickUiTargetAct: _ui.clickUiTarget, + selectUiSweepAct: _ui.selectUiSweep, + scrollUiWheelAct: _ui.scrollUiWheel, + splitIntegrationAct: _integration.splitIntegration, + addNewCylicVoltaPairPeakAct: _cyclic_voltammetry.addNewCylicVoltaPairPeak, + addCylicVoltaMaxPeakAct: _cyclic_voltammetry.addCylicVoltaMaxPeak, + addCylicVoltaMinPeakAct: _cyclic_voltammetry.addCylicVoltaMinPeak +}, dispatch); +ViewerLine.propTypes = { + seed: _propTypes.default.array.isRequired, + peak: _propTypes.default.array.isRequired, + freq: _propTypes.default.oneOfType([_propTypes.default.bool, _propTypes.default.number]).isRequired, + comparisons: _propTypes.default.array.isRequired, + cLabel: _propTypes.default.string.isRequired, + xLabel: _propTypes.default.string.isRequired, + yLabel: _propTypes.default.string.isRequired, + feature: _propTypes.default.object.isRequired, + tTrEndPts: _propTypes.default.array.isRequired, + tSfPeaks: _propTypes.default.array.isRequired, + editPeakSt: _propTypes.default.object.isRequired, + layoutSt: _propTypes.default.string.isRequired, + integationSt: _propTypes.default.object.isRequired, + mtplySt: _propTypes.default.object.isRequired, + sweepExtentSt: _propTypes.default.object.isRequired, + isUiAddIntgSt: _propTypes.default.bool.isRequired, + isUiSplitIntgSt: _propTypes.default.bool.isRequired, + isUiNoBrushSt: _propTypes.default.bool.isRequired, + resetAllAct: _propTypes.default.func.isRequired, + clickUiTargetAct: _propTypes.default.func.isRequired, + selectUiSweepAct: _propTypes.default.func.isRequired, + scrollUiWheelAct: _propTypes.default.func.isRequired, + splitIntegrationAct: _propTypes.default.func.isRequired, + isHidden: _propTypes.default.bool.isRequired, + wavelength: _propTypes.default.object.isRequired, + axesUnitsSt: _propTypes.default.object.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)(ViewerLine); \ No newline at end of file diff --git a/dist/components/d3_line/line_focus.js b/dist/components/d3_line/line_focus.js new file mode 100644 index 00000000..291dc803 --- /dev/null +++ b/dist/components/d3_line/line_focus.js @@ -0,0 +1,725 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _init = require("../../helpers/init"); +var _mount = require("../../helpers/mount"); +var _brush = _interopRequireDefault(require("../../helpers/brush")); +var _compass = require("../../helpers/compass"); +var _integration_split = require("../../helpers/integration_split"); +var _converter = require("../../helpers/converter"); +var _focus = require("../../helpers/focus"); +var _integration = require("../../helpers/integration"); +var _multiplicity_calc = require("../../helpers/multiplicity_calc"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _cfg = _interopRequireDefault(require("../../helpers/cfg")); +var _list_layout = require("../../constants/list_layout"); +/* eslint-disable prefer-object-spread, no-mixed-operators */ + +const d3 = require('d3'); +class LineFocus { + constructor(props) { + const { + W, + H, + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct, + splitIntegrationAct + } = props; + this.jcampIdx = 0; + this.rootKlass = '.d3Line'; + this.margin = { + t: 5, + b: 40, + l: 60, + r: 5 + }; + this.w = W - this.margin.l - this.margin.r; + this.h = H - this.margin.t - this.margin.b; + this.clickUiTargetAct = clickUiTargetAct; + this.selectUiSweepAct = selectUiSweepAct; + this.scrollUiWheelAct = scrollUiWheelAct; + this.splitIntegrationAct = splitIntegrationAct; + this.brush = d3.brush(); + this.brushX = d3.brushX(); + this.axis = null; + this.path = null; + this.thresLineUp = null; + this.thresLineDw = null; + this.grid = null; + this.tags = null; + this.ref = null; + this.ccPattern = null; + this.data = []; + this.dataPks = []; + this.tTrEndPts = null; + this.tSfPeaks = null; + this.root = null; + this.svg = null; + this.axisCall = (0, _init.InitAxisCall)(5); + this.pathCall = null; + this.tip = null; + this.factor = 0.125; + this.currentExtent = null; + this.shouldUpdate = {}; + this.freq = false; + this.layout = _list_layout.LIST_LAYOUT.H1; + this.isUiAddIntgSt = false; + this.isUiSplitIntgSt = false; + this.integrationSplitTargets = null; + this.firstIntegrationPoint = null; + this.getShouldUpdate = this.getShouldUpdate.bind(this); + this.resetShouldUpdate = this.resetShouldUpdate.bind(this); + this.setTip = this.setTip.bind(this); + this.setDataParams = this.setDataParams.bind(this); + this.create = this.create.bind(this); + this.update = this.update.bind(this); + this.setConfig = this.setConfig.bind(this); + this.drawLine = this.drawLine.bind(this); + this.drawThres = this.drawThres.bind(this); + this.drawGrid = this.drawGrid.bind(this); + this.drawAUC = this.drawAUC.bind(this); + this.drawPeaks = this.drawPeaks.bind(this); + this.drawRef = this.drawRef.bind(this); + this.drawInteg = this.drawInteg.bind(this); + this.drawMtply = this.drawMtply.bind(this); + this.drawComparisons = this.drawComparisons.bind(this); + this.onClickTarget = this.onClickTarget.bind(this); + this.onClickIntegrationTarget = this.onClickIntegrationTarget.bind(this); + this.onIntegrationMouseMove = this.onIntegrationMouseMove.bind(this); + this.clearSplitPreview = this.clearSplitPreview.bind(this); + this.mergedPeaks = this.mergedPeaks.bind(this); + this.isFirefox = typeof InstallTrigger !== 'undefined'; + this.wavelength = null; + } + getShouldUpdate(nextEpSt, nextItSt, nextMySt) { + const { + prevXt, + prevYt, + prevEpSt, + prevLySt, + prevItSt, + prevMySt, + prevTePt, + prevDtPk, + prevSfPk, + prevData + } = this.shouldUpdate; + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + const sameXY = xt(1.1) === prevXt && prevYt === yt(1.1); + const sameEpSt = prevEpSt === nextEpSt; + const sameLySt = prevLySt === this.layout; + const sameItSt = prevItSt === nextItSt; + const sameMySt = prevMySt === nextMySt; + const sameTePt = prevTePt === this.tTrEndPts.length; + const sameDtPk = prevDtPk === this.dataPks.length; + const sameSfPk = JSON.stringify(prevSfPk) === JSON.stringify(this.tSfPeaks); + const sameData = prevData === this.data.length; + const sameRef = prevEpSt.prevOffset === nextEpSt.prevOffset; + this.shouldUpdate = Object.assign({}, this.shouldUpdate, { + sameXY, + sameEpSt, + sameLySt, + sameItSt, + sameMySt, + // eslint-disable-line + sameTePt, + sameDtPk, + sameSfPk, + sameData, + sameRef // eslint-disable-line + }); + } + resetShouldUpdate(prevEpSt, prevItSt, prevMySt) { + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + const prevXt = xt(1.1); + const prevYt = yt(1.1); + const prevTePt = this.tTrEndPts.length; + const prevDtPk = this.dataPks.length; + const prevSfPk = this.tSfPeaks; + const prevData = this.data.length; + const prevLySt = this.layout; + this.shouldUpdate = Object.assign({}, this.shouldUpdate, { + prevXt, + prevYt, + prevEpSt, + prevLySt, + prevItSt, + prevMySt, + // eslint-disable-line + prevTePt, + prevDtPk, + prevSfPk, + prevData // eslint-disable-line + }); + } + setTip() { + this.tip = (0, _init.InitTip)(); + this.root.call(this.tip); + } + setDataParams(data, peaks, tTrEndPts, tSfPeaks, freq, layout, wavelength) { + this.data = [...data]; + this.dataPks = [...peaks]; + this.tTrEndPts = tTrEndPts; + this.tSfPeaks = tSfPeaks; + this.freq = freq; + this.layout = layout; + this.wavelength = wavelength; + } + updatePathCall(xt, yt) { + this.pathCall = d3.line().x(d => xt(d.x)).y(d => yt(d.y)); + } + setConfig(sweepExtentSt) { + // Domain Calculate + let { + xExtent, + yExtent + } = sweepExtentSt || { + xExtent: false, + yExtent: false + }; + if (!xExtent || !yExtent) { + const xes = d3.extent(this.data, d => d.x).sort((a, b) => a - b); + xExtent = { + xL: xes[0], + xU: xes[1] + }; + const btm = d3.min(this.data, d => d.y); + const top = d3.max(this.data, d => d.y); + const height = top - btm; + yExtent = { + yL: btm - this.factor * height, + yU: top + this.factor * height + }; + } + this.scales.x.domain([xExtent.xL, xExtent.xU]); + this.scales.y.domain([yExtent.yL, yExtent.yU]); + + // rescale for zoom + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + + // Axis Call + this.axisCall.x.scale(xt); + this.axisCall.y.scale(yt); + this.currentExtent = { + xExtent, + yExtent + }; + } + drawLine() { + const { + sameXY, + sameRef, + sameSfPk + } = this.shouldUpdate; + if (sameXY && sameRef && sameSfPk) return; + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + this.updatePathCall(xt, yt); + this.path.attr('d', this.pathCall(this.data)); + } + drawThres() { + if (this.tTrEndPts.length > 0) { + this.thresLineUp.attr('d', this.pathCall(this.tTrEndPts)); + this.thresLineUp.attr('visibility', 'visible'); + const [left, right] = this.tTrEndPts; + const dwMirrorEndPts = [Object.assign({}, left, { + y: -left.y + }), Object.assign({}, right, { + y: -right.y + })]; + this.thresLineDw.attr('d', this.pathCall(dwMirrorEndPts)); + this.thresLineDw.attr('visibility', 'visible'); + } else { + this.thresLineUp.attr('visibility', 'hidden'); + this.thresLineDw.attr('visibility', 'hidden'); + } + } + drawGrid() { + const { + sameXY, + sameSfPk + } = this.shouldUpdate; + if (sameXY && sameSfPk) return; + this.grid.x.call(this.axisCall.x.tickSize(-this.h, 0, 0)).selectAll('line').attr('stroke', '#ddd').attr('stroke-opacity', 0.6).attr('fill', 'none'); + this.grid.y.call(this.axisCall.y.tickSize(-this.w, 0, 0)).selectAll('line').attr('stroke', '#ddd').attr('stroke-opacity', 0.6).attr('fill', 'none'); + } + onClickTarget(event, data) { + event.stopPropagation(); + event.preventDefault(); + const onPeak = true; + this.clickUiTargetAct(data, onPeak); + } + clearSplitPreview() { + (0, _integration_split.clearIntegrationSplitPreview)(this); + } + onIntegrationMouseMove(event, data, shift, ignoreRef) { + if (!this.isUiSplitIntgSt) return; + const splitX = (0, _integration_split.getSplitXFromEvent)(event, this); + (0, _integration_split.drawIntegrationSplitPreview)(this, data, splitX, shift, ignoreRef); + } + onClickIntegrationTarget(event, data) { + if (!this.isUiSplitIntgSt) { + this.onClickTarget(event, data); + return; + } + event.stopPropagation(); + event.preventDefault(); + const splitX = (0, _integration_split.getSplitXFromEvent)(event, this); + this.clearSplitPreview(); + this.splitIntegrationAct({ + curveIdx: this.jcampIdx, + target: data, + splitX, + data: this.data + }); + } + mergedPeaks(editPeakSt) { + if (!editPeakSt) return this.dataPks; + this.dataPks = (0, _converter.PksEdit)(this.dataPks, editPeakSt); + return this.dataPks; + } + drawAUC(stack, shift = 0) { + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + const auc = this.tags.aucPath.selectAll('path').data(stack); + auc.exit().attr('class', 'exit').remove(); + const integCurve = border => { + const { + xL, + xU + } = border; + const ps = (0, _integration.getIntegrationPoints)(xL, xU, this.data); + if (!ps[0]) return null; + const baselineY = (0, _integration.getLinearBaseline)(ps); + return d3.area().x(d => xt(d.x)).y0(d => yt(baselineY(d))).y1(d => yt(d.y))(ps); + }; + auc.enter().append('path').attr('class', 'auc').attr('fill', 'red').attr('stroke', 'none').attr('fill-opacity', 0.2).attr('stroke-width', 2).merge(auc).attr('d', d => integCurve(d)).attr('id', d => `auc${(0, _focus.itgIdTag)(d)}`).on('mouseover', (event, d) => { + d3.select(`#auc${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#auc${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#auc${(0, _focus.itgIdTag)(d)}`).style('fill', 'blue'); + }).on('mouseout', (event, d) => { + d3.select(`#auc${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'none'); + d3.select(`#auc${(0, _focus.itgIdTag)(d)}`).style('fill', 'red'); + d3.select(`#auc${(0, _focus.itgIdTag)(d)}`).style('fill-opacity', 0.2); + this.clearSplitPreview(); + }).on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, true)).on('click', (event, d) => this.onClickIntegrationTarget(event, d)); + } + drawPeaks(editPeakSt) { + const { + sameXY, + sameEpSt, + sameDtPk, + sameSfPk + } = this.shouldUpdate; + if (!_format.default.isCyclicVoltaLayout(this.layout) && sameXY && sameEpSt && sameDtPk && sameSfPk) return; + + // rescale for zoom + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + const dPks = this.mergedPeaks(editPeakSt); + const mpp = this.tags.pPath.selectAll('path').data(dPks); + mpp.exit().attr('class', 'exit').remove(); + const linePath = [{ + x: -0.5, + y: 10 + }, { + x: -0.5, + y: -20 + }, { + x: 0.5, + y: -20 + }, { + x: 0.5, + y: 10 + }]; + // const faktor = layoutSt === LIST_LAYOUT.IR ? -1 : 1; + const lineSymbol = d3.line().x(d => d.x).y(d => d.y)(linePath); + mpp.enter().append('path').attr('d', lineSymbol).attr('class', 'enter-peak').attr('fill', 'red').attr('stroke', 'pink').attr('stroke-width', 3).attr('stroke-opacity', 0.0).merge(mpp).attr('id', d => `mpp${Math.round(1000 * d.x)}`).attr('transform', d => `translate(${xt(d.x)}, ${yt(d.y)})`).on('mouseover', (event, d) => { + d3.select(`#mpp${Math.round(1000 * d.x)}`).attr('stroke-opacity', '1.0'); + d3.select(`#bpt${Math.round(1000 * d.x)}`).style('fill', 'blue'); + const tipParams = { + d, + layout: this.layout + }; + this.tip.show(tipParams, event.target); + }).on('mouseout', (event, d) => { + d3.select(`#mpp${Math.round(1000 * d.x)}`).attr('stroke-opacity', '0.0'); + d3.select(`#bpt${Math.round(1000 * d.x)}`).style('fill', 'red'); + const tipParams = { + d, + layout: this.layout + }; + this.tip.hide(tipParams, event.target); + }).on('click', (event, d) => this.onClickTarget(event, d)); + const ignoreRef = _format.default.isHplcUvVisLayout(this.layout); + if (ignoreRef) { + 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)); + } + } + drawInteg(integationSt) { + const { + sameXY, + sameLySt, + sameItSt, + sameData + } = this.shouldUpdate; + if (sameXY && sameLySt && sameItSt && sameData) return; + const { + selectedIdx, + integrations + } = integationSt; + const selectedIntegration = integrations[selectedIdx]; + const { + stack, + refArea, + refFactor, + shift + } = selectedIntegration; + const isDisable = _cfg.default.btnCmdIntg(this.layout); + const ignoreRef = _format.default.isHplcUvVisLayout(this.layout); + const itgs = isDisable ? [] : stack; + Object.assign(this, { + integrationSplitTargets: { + stack: itgs, + shift, + ignoreRef + } + }); + 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(); + 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); + return; + } + if (ignoreRef) { + this.drawAUC(stack, shift); + } else { + // rescale for zoom + const { + xt + } = (0, _compass.TfRescale)(this); + const dh = 50; + const integBar = data => d3.line()([[xt(data.xL - shift), dh], [xt(data.xL - shift), dh - 10], [xt(data.xL - shift), dh - 5], [xt(data.xU - shift), dh - 5], [xt(data.xU - shift), dh - 10], [xt(data.xU - shift), dh]]); + igbp.enter().append('path').attr('class', 'igbp').attr('fill', 'none').attr('stroke', '#228B22').attr('stroke-width', 2).merge(igbp).attr('id', d => `igbp${(0, _focus.itgIdTag)(d)}`).attr('d', d => integBar(d)).on('mouseover', (event, d) => { + d3.select(`#igbp${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#igbc${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#igtp${(0, _focus.itgIdTag)(d)}`).style('fill', 'blue'); + }).on('mouseout', (event, d) => { + d3.select(`#igbp${(0, _focus.itgIdTag)(d)}`).attr('stroke', '#228B22'); + d3.select(`#igbc${(0, _focus.itgIdTag)(d)}`).attr('stroke', '#228B22'); + d3.select(`#igtp${(0, _focus.itgIdTag)(d)}`).style('fill', '#228B22'); + this.clearSplitPreview(); + }).on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, ignoreRef)).on('click', (event, d) => this.onClickIntegrationTarget(event, d)); + const integCurve = border => { + const { + xL, + xU + } = border; + const [nXL, nXU] = [xL - shift, xU - shift]; + const ps = this.data.filter(d => d.x > nXL && d.x < nXU); + const kMax = this.data[this.data.length - 1].k; + if (!ps[0]) return null; + const kRef = ps[0].k; + if (!this.reverseXAxis(this.layout)) { + return d3.line().x(d => xt(d.x)).y(d => 100 - (kRef - d.k) * 400 / kMax)(ps); + } + return d3.line().x(d => xt(d.x)).y(d => 300 - (d.k - kRef) * 400 / kMax)(ps); + }; + igcp.enter().append('path').attr('class', 'igcp').attr('fill', 'none').attr('stroke', '#228B22').attr('stroke-width', 2).merge(igcp).attr('id', d => `igbc${(0, _focus.itgIdTag)(d)}`).attr('d', d => integCurve(d)).on('mouseover', (event, d) => { + d3.select(`#igbp${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#igbc${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#igtp${(0, _focus.itgIdTag)(d)}`).style('fill', 'blue'); + }).on('mouseout', (event, d) => { + d3.select(`#igbp${(0, _focus.itgIdTag)(d)}`).attr('stroke', '#228B22'); + d3.select(`#igbc${(0, _focus.itgIdTag)(d)}`).attr('stroke', '#228B22'); + d3.select(`#igtp${(0, _focus.itgIdTag)(d)}`).style('fill', '#228B22'); + this.clearSplitPreview(); + }).on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, ignoreRef)).on('click', (event, d) => this.onClickIntegrationTarget(event, d)); + igtp.enter().append('text').attr('class', 'igtp').attr('font-family', 'Helvetica').style('font-size', '12px').attr('fill', '#228B22').style('text-anchor', 'middle').merge(igtp).attr('id', d => `igtp${(0, _focus.itgIdTag)(d)}`).text(d => (0, _integration.calcArea)(d, refArea, refFactor, ignoreRef)).attr('transform', d => `translate(${xt((d.xL + d.xU) / 2 - shift)}, ${dh - 12})`).on('mouseover', (event, d) => { + d3.select(`#igbp${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#igbc${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#igtp${(0, _focus.itgIdTag)(d)}`).style('fill', 'blue'); + }).on('mouseout', (event, d) => { + d3.select(`#igbp${(0, _focus.itgIdTag)(d)}`).attr('stroke', '#228B22'); + d3.select(`#igbc${(0, _focus.itgIdTag)(d)}`).attr('stroke', '#228B22'); + d3.select(`#igtp${(0, _focus.itgIdTag)(d)}`).style('fill', '#228B22'); + this.clearSplitPreview(); + }).on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, ignoreRef)).on('click', (event, d) => this.onClickIntegrationTarget(event, d)); + } + } + drawMtply(mtplySt) { + const { + sameXY, + sameLySt, + sameMySt + } = this.shouldUpdate; + if (sameXY && sameLySt && sameMySt) return; + const { + selectedIdx, + multiplicities + } = mtplySt; + const selectedMulti = multiplicities[selectedIdx]; + const { + stack, + smExtext, + shift + } = selectedMulti; + const mpys = stack; + const isDisable = _cfg.default.btnCmdMpy(this.layout); + if (mpys === 0 || isDisable) return; + // rescale for zoom + const { + xt + } = (0, _compass.TfRescale)(this); + 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(); + const height = this.h; + const dh = Math.abs(0.06 * height); + const mpyBar = data => d3.line()([[xt(data.xExtent.xL - shift), height - dh], [xt(data.xExtent.xL - shift), height - dh - 10], [xt(data.xExtent.xL - shift), height - dh - 5], [xt(data.xExtent.xU - shift), height - dh - 5], [xt(data.xExtent.xU - shift), height - dh - 10], [xt(data.xExtent.xU - shift), height - dh]]); + const mpyColor = d => { + const { + xL, + xU + } = d.xExtent; + return smExtext.xL === xL && smExtext.xU === xU ? 'purple' : '#DA70D6'; + }; + mpyb.enter().append('path').attr('class', 'mpyb').attr('fill', 'none').attr('stroke-width', 2).merge(mpyb).attr('stroke', d => mpyColor(d)).attr('id', d => `mpyb${(0, _focus.mpyIdTag)(d)}`).attr('d', d => mpyBar(d)).on('mouseover', (event, d) => { + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + }).on('mouseout', (event, d) => { + const dColor = mpyColor(d); + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + }).on('click', (event, d) => this.onClickTarget(event, d)); + mpyt1.enter().append('text').attr('class', 'mpyt1').attr('font-family', 'Helvetica').style('font-size', '12px').style('text-anchor', 'middle').merge(mpyt1).attr('fill', d => mpyColor(d)).attr('id', d => `mpyt1${(0, _focus.mpyIdTag)(d)}`).text(d => `${(0, _multiplicity_calc.calcMpyCenter)(d.peaks, shift, d.mpyType).toFixed(3)}`).attr('transform', d => `translate(${xt((d.xExtent.xL + d.xExtent.xU) / 2 - shift)}, ${height - dh + 12})`).on('mouseover', (event, d) => { + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + }).on('mouseout', (event, d) => { + const dColor = mpyColor(d); + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + }).on('click', (event, d) => this.onClickTarget(event, d)); + mpyt2.enter().append('text').attr('class', 'mpyt2').attr('font-family', 'Helvetica').style('font-size', '12px').style('text-anchor', 'middle').merge(mpyt2).attr('fill', d => mpyColor(d)).attr('id', d => `mpyt2${(0, _focus.mpyIdTag)(d)}`).text(d => `(${d.mpyType})`).attr('transform', d => `translate(${xt((d.xExtent.xL + d.xExtent.xU) / 2 - shift)}, ${height - dh + 24})`).on('mouseover', (event, d) => { + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + }).on('mouseout', (event, d) => { + const dColor = mpyColor(d); + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + }).on('click', (event, d) => this.onClickTarget(event, d)); + const mpypH = height - dh; + const mpypPath = pk => [{ + x: xt(pk.x - shift) - 0.5, + y: mpypH - 5 + }, { + x: xt(pk.x - shift) - 0.5, + y: mpypH - 20 + }, { + x: xt(pk.x - shift) + 0.5, + y: mpypH - 20 + }, { + x: xt(pk.x - shift) + 0.5, + y: mpypH - 5 + }]; + // const faktor = layoutSt === LIST_LAYOUT.IR ? -1 : 1; + const lineSymbol = d3.line().x(d => d.x).y(d => d.y); + mpyp.enter().append('path').attr('class', 'mpyp').attr('fill', 'none').merge(mpyp).attr('stroke', d => mpyColor(d)).attr('d', d => lineSymbol(mpypPath(d))).attr('id', d => `mpyp${(0, _focus.mpyIdTag)(d)}`).on('mouseover', (event, d) => { + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + }).on('mouseout', (event, d) => { + const dColor = mpyColor(d); + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + }).on('click', (event, d) => this.onClickTarget(event, d)); + } + drawRef() { + // rescale for zoom + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + const ccp = this.ref.selectAll('path').data(this.tSfPeaks); + ccp.exit().attr('class', 'exit').remove(); + const linePath = [{ + x: -0.5, + y: 10 + }, { + x: -4, + y: -20 + }, { + x: 4, + y: -20 + }, { + x: 0.5, + y: 10 + }]; + const faktor = _format.default.isIrLayout(this.layout) ? -1 : 1; + const lineSymbol = d3.line().x(d => d.x).y(d => faktor * d.y)(linePath); + ccp.enter().append('path').attr('d', lineSymbol).attr('class', 'enter-ref').attr('fill', 'green').attr('fill-opacity', 0.8).merge(ccp).attr('transform', d => `translate(${xt(d.x)}, ${yt(d.y)})`); + } + drawComparisons(comparisons) { + d3.selectAll('.line-clip-compare').remove(); + if (!comparisons) return null; + comparisons.forEach((c, idx) => { + if (!c.show) return; + const path = (0, _mount.MountComparePath)(this, _format.default.compareColors(idx), idx); // #D5D8DC + path.attr('d', this.pathCall(c.data)); + }); + return null; + } + reverseXAxis(layoutSt) { + return [_list_layout.LIST_LAYOUT.UVVIS, _list_layout.LIST_LAYOUT.HPLC_UVVIS, _list_layout.LIST_LAYOUT.TGA, _list_layout.LIST_LAYOUT.DSC, _list_layout.LIST_LAYOUT.XRD, _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY, _list_layout.LIST_LAYOUT.CDS, _list_layout.LIST_LAYOUT.DLS_ACF, _list_layout.LIST_LAYOUT.SEC, _list_layout.LIST_LAYOUT.GC, _list_layout.LIST_LAYOUT.EMISSIONS, _list_layout.LIST_LAYOUT.DLS_INTENSITY].indexOf(layoutSt) < 0; + } + create({ + filterSeed, + filterPeak, + tTrEndPts, + tSfPeaks, + freq, + comparisons, + editPeakSt, + layoutSt, + integationSt, + mtplySt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + wavelength + }) { + this.svg = d3.select('.d3Svg'); + (0, _mount.MountMainFrame)(this, 'focus'); + (0, _mount.MountClip)(this); + 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, freq, layoutSt, wavelength); + Object.assign(this, { + isUiSplitIntgSt + }); + if (!isUiSplitIntgSt) this.clearSplitPreview(); + (0, _compass.MountCompass)(this); + this.axis = (0, _mount.MountAxis)(this); + this.path = (0, _mount.MountPath)(this, 'steelblue'); + [this.thresLineUp, this.thresLineDw] = (0, _mount.MountThresLine)(this, 'green'); + this.grid = (0, _mount.MountGrid)(this); + this.tags = (0, _mount.MountTags)(this); + this.ref = (0, _mount.MountRef)(this); + (0, _mount.MountAxisLabelX)(this); + (0, _mount.MountAxisLabelY)(this); + if (this.data && this.data.length > 0) { + this.setConfig(sweepExtentSt); + this.drawLine(); + this.drawThres(); + this.drawGrid(); + this.drawRef(); + this.drawPeaks(editPeakSt); + this.drawInteg(integationSt); + this.drawMtply(mtplySt); + this.drawComparisons(comparisons); + } + (0, _brush.default)(this, isUiAddIntgSt, isUiNoBrushSt); + this.resetShouldUpdate(editPeakSt, integationSt, mtplySt); + } + update({ + filterSeed, + filterPeak, + tTrEndPts, + tSfPeaks, + freq, + comparisons, + editPeakSt, + layoutSt, + integationSt, + mtplySt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + wavelength + }) { + this.root = d3.select(this.rootKlass).selectAll('.focus-main'); + this.scales = (0, _init.InitScale)(this, this.reverseXAxis(layoutSt)); + this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, freq, layoutSt, wavelength); + Object.assign(this, { + isUiSplitIntgSt + }); + if (!isUiSplitIntgSt) this.clearSplitPreview(); + if (this.data && this.data.length > 0) { + this.setConfig(sweepExtentSt); + this.getShouldUpdate(editPeakSt, integationSt, mtplySt); + this.drawLine(); + this.drawThres(); + this.drawGrid(); + this.drawRef(); + this.drawPeaks(editPeakSt); + this.drawInteg(integationSt); + this.drawMtply(mtplySt); + this.drawComparisons(comparisons); + } + (0, _brush.default)(this, isUiAddIntgSt, isUiNoBrushSt); + this.resetShouldUpdate(editPeakSt, integationSt, mtplySt); + } +} +var _default = exports.default = LineFocus; \ No newline at end of file diff --git a/dist/components/d3_multi/index.js b/dist/components/d3_multi/index.js new file mode 100644 index 00000000..d38ce0ab --- /dev/null +++ b/dist/components/d3_multi/index.js @@ -0,0 +1,436 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _chem = require("../../helpers/chem"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _manager = require("../../actions/manager"); +var _ui = require("../../actions/ui"); +var _integration = require("../../actions/integration"); +var _list_ui = require("../../constants/list_ui"); +var _cyclic_voltammetry = require("../../actions/cyclic_voltammetry"); +var _multi_focus = _interopRequireDefault(require("./multi_focus")); +var _draw = require("../common/draw"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable no-mixed-operators, react/require-default-props, +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 + +class ViewerMulti extends _react.default.Component { + constructor(props) { + super(props); + const { + entities, + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct, + splitIntegrationAct + } = this.props; + this.rootKlass = '.d3Line'; + this.containerRef = /*#__PURE__*/_react.default.createRef(); + this.currentSize = null; + this.resizeObserver = null; + this.focus = new _multi_focus.default({ + W, + H, + entities, + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct, + splitIntegrationAct + }); + this.normChange = this.normChange.bind(this); + this.handleResize = this.handleResize.bind(this); + } + componentDidMount() { + this.renderChart(this.props, true); + this.setupResizeObserver(); + const { + curveSt, + seed, + peak, + cLabel, + xLabel, + yLabel, + feature, + tTrEndPts, + tSfPeaks, + editPeakSt, + layoutSt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + isHidden, + resetAllAct, + cyclicvoltaSt, + integationSt, + mtplySt, + axesUnitsSt + } = this.props; + (0, _draw.drawDestroy)(this.rootKlass); + resetAllAct(feature); + let xxLabel = xLabel; + let yyLabel = yLabel; + if (axesUnitsSt) { + const { + curveIdx + } = curveSt; + const { + axes + } = axesUnitsSt; + let selectedAxes = axes[curveIdx]; + if (!selectedAxes) { + selectedAxes = { + xUnit: '', + yUnit: '' + }; + } + const { + xUnit, + yUnit + } = selectedAxes; + xxLabel = xUnit === '' ? xLabel : xUnit; + yyLabel = yUnit === '' ? yLabel : yUnit; + } + if (cyclicvoltaSt && cyclicvoltaSt.useCurrentDensity) { + const areaUnit = cyclicvoltaSt.areaUnit || 'cm²'; + const baseUnit = /mA/i.test(String(yyLabel)) ? 'mA' : 'A'; + yyLabel = `Current density in ${baseUnit}/${areaUnit}`; + } + const filterSeed = seed; + const filterPeak = peak; + (0, _draw.drawMain)(this.rootKlass, W, H); + this.focus.create({ + curveSt, + filterSeed, + filterPeak, + tTrEndPts, + tSfPeaks, + editPeakSt, + layoutSt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + cyclicvoltaSt, + integationSt, + mtplySt + }); + (0, _draw.drawLabel)(this.rootKlass, cLabel, xxLabel, yyLabel); + (0, _draw.drawDisplay)(this.rootKlass, isHidden); + (0, _draw.drawArrowOnCurve)(this.rootKlass, isHidden); + } + componentDidUpdate(prevProps) { + const { + entities, + curveSt, + seed, + peak, + cLabel, + xLabel, + yLabel, + tTrEndPts, + tSfPeaks, + editPeakSt, + layoutSt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + isHidden, + cyclicvoltaSt, + integationSt, + mtplySt, + axesUnitsSt + } = this.props; + this.normChange(prevProps); + let xxLabel = xLabel; + let yyLabel = yLabel; + if (axesUnitsSt) { + const { + curveIdx + } = curveSt; + const { + axes + } = axesUnitsSt; + let selectedAxes = axes[curveIdx]; + if (!selectedAxes) { + selectedAxes = { + xUnit: '', + yUnit: '' + }; + } + const { + xUnit, + yUnit + } = selectedAxes; + xxLabel = xUnit === '' ? xLabel : xUnit; + yyLabel = yUnit === '' ? yLabel : yUnit; + } + if (cyclicvoltaSt && cyclicvoltaSt.useCurrentDensity) { + const areaUnit = cyclicvoltaSt.areaUnit || 'cm²'; + const baseUnit = /mA/i.test(String(yyLabel)) ? 'mA' : 'A'; + yyLabel = `Current density in ${baseUnit}/${areaUnit}`; + } + const filterSeed = seed; + const filterPeak = peak; + if (_format.default.isCyclicVoltaLayout(layoutSt)) { + this.handleResize(); + } + this.focus.update({ + entities, + curveSt, + filterSeed, + filterPeak, + tTrEndPts, + tSfPeaks, + editPeakSt, + layoutSt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + cyclicvoltaSt, + integationSt, + mtplySt + }); + (0, _draw.drawLabel)(this.rootKlass, cLabel, xxLabel, yyLabel); + (0, _draw.drawDisplay)(this.rootKlass, isHidden); + (0, _draw.drawArrowOnCurve)(this.rootKlass, isHidden); + } + componentWillUnmount() { + (0, _draw.drawDestroy)(this.rootKlass); + this.teardownResizeObserver(); + } + handleResize() { + const { + layoutSt + } = this.props; + if (!_format.default.isCyclicVoltaLayout(layoutSt)) return; + const size = this.getContainerSize(); + if (!size) return; + if (!this.currentSize || size.width !== this.currentSize.width || size.height !== this.currentSize.height) { + this.renderChart(this.props, false); + } + } + getContainerSize() { + const node = this.containerRef.current; + if (!node) return null; + const { + clientWidth, + clientHeight + } = node; + if (!clientWidth || !clientHeight) return null; + return { + width: clientWidth, + height: clientHeight + }; + } + getTargetSize(layoutSt) { + if (_format.default.isCyclicVoltaLayout(layoutSt)) { + const size = this.getContainerSize(); + if (size) return size; + } + return { + width: W, + height: H + }; + } + setupResizeObserver() { + if (typeof ResizeObserver === 'undefined') return; + if (!this.containerRef.current || this.resizeObserver) return; + this.resizeObserver = new ResizeObserver(this.handleResize); + this.resizeObserver.observe(this.containerRef.current); + } + teardownResizeObserver() { + if (this.resizeObserver) { + this.resizeObserver.disconnect(); + this.resizeObserver = null; + } + } + normChange(prevProps) { + const { + feature, + resetAllAct, + entities + } = this.props; + const oldEntities = prevProps.entities; + if (oldEntities !== entities) { + resetAllAct(feature); + } + } + renderChart(props, shouldReset) { + const { + curveSt, + seed, + peak, + cLabel, + xLabel, + yLabel, + feature, + tTrEndPts, + tSfPeaks, + editPeakSt, + layoutSt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + isHidden, + resetAllAct, + cyclicvoltaSt, + integationSt, + mtplySt, + axesUnitsSt, + entities, + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct, + splitIntegrationAct + } = props; + const size = this.getTargetSize(layoutSt); + this.currentSize = size; + (0, _draw.drawDestroy)(this.rootKlass); + if (shouldReset) { + resetAllAct(feature); + } + let xxLabel = xLabel; + let yyLabel = yLabel; + if (axesUnitsSt) { + const { + curveIdx + } = curveSt; + const { + axes + } = axesUnitsSt; + let selectedAxes = axes[curveIdx]; + if (!selectedAxes) { + selectedAxes = { + xUnit: '', + yUnit: '' + }; + } + const { + xUnit, + yUnit + } = selectedAxes; + xxLabel = xUnit === '' ? xLabel : xUnit; + yyLabel = yUnit === '' ? yLabel : yUnit; + } + const filterSeed = seed; + const filterPeak = peak; + this.focus = new _multi_focus.default({ + W: size.width, + H: size.height, + entities, + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct, + splitIntegrationAct + }); + (0, _draw.drawMain)(this.rootKlass, size.width, size.height); + this.focus.create({ + curveSt, + filterSeed, + filterPeak, + tTrEndPts, + tSfPeaks, + editPeakSt, + layoutSt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + cyclicvoltaSt, + integationSt, + mtplySt + }); + (0, _draw.drawLabel)(this.rootKlass, cLabel, xxLabel, yyLabel); + (0, _draw.drawDisplay)(this.rootKlass, isHidden); + (0, _draw.drawArrowOnCurve)(this.rootKlass, isHidden); + } + render() { + const { + layoutSt + } = this.props; + const isCyclicVolta = _format.default.isCyclicVoltaLayout(layoutSt); + return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: "d3Line", + ref: this.containerRef, + style: isCyclicVolta ? { + height: '100%' + } : undefined + }); + } +} +const mapStateToProps = (state, props) => ({ + curveSt: state.curve, + seed: (0, _chem.Topic2Seed)(state, props), + peak: (0, _chem.Feature2Peak)(state, props), + tTrEndPts: (0, _chem.ToThresEndPts)(state, props), + tSfPeaks: (0, _chem.ToShiftPeaks)(state, props), + editPeakSt: state.editPeak.present, + layoutSt: state.layout, + sweepExtentSt: state.ui.sweepExtent, + isUiAddIntgSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_ADD, + isUiSplitIntgSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_SPLIT, + isUiNoBrushSt: _list_ui.LIST_NON_BRUSH_TYPES.indexOf(state.ui.sweepType) < 0, + cyclicvoltaSt: state.cyclicvolta, + maxminPeakSt: (0, _chem.Feature2MaxMinPeak)(state, props), + integationSt: state.integration.present, + mtplySt: state.multiplicity.present, + axesUnitsSt: state.axesUnits +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + resetAllAct: _manager.resetAll, + clickUiTargetAct: _ui.clickUiTarget, + selectUiSweepAct: _ui.selectUiSweep, + scrollUiWheelAct: _ui.scrollUiWheel, + splitIntegrationAct: _integration.splitIntegration, + addNewCylicVoltaPairPeakAct: _cyclic_voltammetry.addNewCylicVoltaPairPeak, + addCylicVoltaMaxPeakAct: _cyclic_voltammetry.addCylicVoltaMaxPeak, + addCylicVoltaMinPeakAct: _cyclic_voltammetry.addCylicVoltaMinPeak +}, dispatch); +ViewerMulti.propTypes = { + curveSt: _propTypes.default.object.isRequired, + entities: _propTypes.default.array.isRequired, + seed: _propTypes.default.array.isRequired, + peak: _propTypes.default.array.isRequired, + xLabel: _propTypes.default.string.isRequired, + yLabel: _propTypes.default.string.isRequired, + feature: _propTypes.default.object.isRequired, + tTrEndPts: _propTypes.default.array.isRequired, + tSfPeaks: _propTypes.default.array.isRequired, + editPeakSt: _propTypes.default.object.isRequired, + layoutSt: _propTypes.default.string.isRequired, + integationSt: _propTypes.default.object.isRequired, + mtplySt: _propTypes.default.object.isRequired, + sweepExtentSt: _propTypes.default.object.isRequired, + isUiAddIntgSt: _propTypes.default.bool.isRequired, + isUiSplitIntgSt: _propTypes.default.bool.isRequired, + isUiNoBrushSt: _propTypes.default.bool.isRequired, + resetAllAct: _propTypes.default.func.isRequired, + clickUiTargetAct: _propTypes.default.func.isRequired, + selectUiSweepAct: _propTypes.default.func.isRequired, + scrollUiWheelAct: _propTypes.default.func.isRequired, + splitIntegrationAct: _propTypes.default.func.isRequired, + isHidden: _propTypes.default.bool, + cyclicvoltaSt: _propTypes.default.object.isRequired, + maxminPeakSt: _propTypes.default.object, + addNewCylicVoltaPairPeakAct: _propTypes.default.func.isRequired, + addCylicVoltaMaxPeakAct: _propTypes.default.func.isRequired, + addCylicVoltaMinPeakAct: _propTypes.default.func.isRequired, + cLabel: _propTypes.default.string, + axesUnitsSt: _propTypes.default.object.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)(ViewerMulti); \ No newline at end of file diff --git a/dist/components/d3_multi/multi_focus.js b/dist/components/d3_multi/multi_focus.js new file mode 100644 index 00000000..b4c60a34 --- /dev/null +++ b/dist/components/d3_multi/multi_focus.js @@ -0,0 +1,1000 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _init = require("../../helpers/init"); +var _mount = require("../../helpers/mount"); +var _converter = require("../../helpers/converter"); +var _brush = _interopRequireDefault(require("../../helpers/brush")); +var _compass = require("../../helpers/compass"); +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 _cfg = _interopRequireDefault(require("../../helpers/cfg")); +var _focus = require("../../helpers/focus"); +var _integration = require("../../helpers/integration"); +var _multiplicity_calc = require("../../helpers/multiplicity_calc"); +/* eslint-disable no-unused-vars, prefer-object-spread, no-mixed-operators, +no-unneeded-ternary, arrow-body-style, max-len */ + +const d3 = require('d3'); +class MultiFocus { + constructor(props) { + const { + W, + H, + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct, + entities, + splitIntegrationAct + } = props; + this.entities = entities; + this.jcampIdx = 0; + this.isShowAllCurves = false; + this.rootKlass = '.d3Line'; + this.margin = { + t: 5, + b: 40, + l: 60, + r: 5 + }; + this.w = W - this.margin.l - this.margin.r; + this.h = H - this.margin.t - this.margin.b; + this.clickUiTargetAct = clickUiTargetAct; + this.selectUiSweepAct = selectUiSweepAct; + this.scrollUiWheelAct = scrollUiWheelAct; + this.splitIntegrationAct = splitIntegrationAct; + this.brush = d3.brush(); + this.brushX = d3.brushX(); + this.axis = null; + this.path = null; + this.thresLineUp = null; + this.thresLineDw = null; + this.grid = null; + this.tags = null; + this.ref = null; + this.data = []; + this.otherLineData = []; + this.pathColor = 'steelblue'; + this.dataPks = []; + this.dataPeckers = []; + this.tTrEndPts = null; + this.tSfPeaks = null; + this.root = null; + this.svg = null; + this.axisCall = (0, _init.InitAxisCall)(5); + this.pathCall = null; + this.tip = null; + this.factor = 0.125; + this.currentExtent = null; + this.shouldUpdate = {}; + // this.freq = false; + this.layout = _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY; + this.isUiAddIntgSt = false; + this.isUiSplitIntgSt = false; + this.integrationSplitTargets = null; + this.firstIntegrationPoint = null; + this.getShouldUpdate = this.getShouldUpdate.bind(this); + this.resetShouldUpdate = this.resetShouldUpdate.bind(this); + this.setTip = this.setTip.bind(this); + this.setDataParams = this.setDataParams.bind(this); + this.create = this.create.bind(this); + this.update = this.update.bind(this); + this.setConfig = this.setConfig.bind(this); + this.drawLine = this.drawLine.bind(this); + this.drawThres = this.drawThres.bind(this); + this.drawOtherLines = this.drawOtherLines.bind(this); + this.drawGrid = this.drawGrid.bind(this); + this.drawPeaks = this.drawPeaks.bind(this); + this.drawRef = this.drawRef.bind(this); + this.drawInteg = this.drawInteg.bind(this); + this.drawMtply = this.drawMtply.bind(this); + this.drawAUC = this.drawAUC.bind(this); + this.onClickTarget = this.onClickTarget.bind(this); + this.onClickIntegrationTarget = this.onClickIntegrationTarget.bind(this); + this.onIntegrationMouseMove = this.onIntegrationMouseMove.bind(this); + this.clearSplitPreview = this.clearSplitPreview.bind(this); + this.mergedPeaks = this.mergedPeaks.bind(this); + this.setDataPecker = this.setDataPecker.bind(this); + this.drawPeckers = this.drawPeckers.bind(this); + this.onClickPecker = this.onClickPecker.bind(this); + this.isFirefox = typeof InstallTrigger !== 'undefined'; + this.cyclicvoltaSt = null; + this.yTransformFactor = 1.0; + } + getShouldUpdate(nextEpSt) { + const { + prevXt, + prevYt, + prevEpSt, + prevLySt, + prevTePt, + prevDtPk, + prevSfPk, + prevData, + prevYFactor + } = this.shouldUpdate; + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + const sameXY = xt(1.1) === prevXt && prevYt === yt(1.1); + const sameEpSt = prevEpSt === nextEpSt; + const sameLySt = prevLySt === this.layout; + const sameTePt = prevTePt === this.tTrEndPts.length; + const sameDtPk = prevDtPk === this.dataPks.length; + const sameSfPk = JSON.stringify(prevSfPk) === JSON.stringify(this.tSfPeaks); + const sameData = prevData === this.data.length; + const sameYFactor = prevYFactor === this.yTransformFactor; + this.shouldUpdate = Object.assign({}, this.shouldUpdate, { + sameXY, + sameEpSt, + sameLySt, + // eslint-disable-line + sameTePt, + sameDtPk, + sameSfPk, + sameData, + sameYFactor // eslint-disable-line + }); + } + resetShouldUpdate(prevEpSt) { + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + const prevXt = xt(1.1); + const prevYt = yt(1.1); + const prevTePt = this.tTrEndPts.length; + const prevDtPk = this.dataPks.length; + const prevSfPk = this.tSfPeaks; + const prevData = this.data.length; + const prevLySt = this.layout; + const prevYFactor = this.yTransformFactor; + this.shouldUpdate = Object.assign({}, this.shouldUpdate, { + prevXt, + prevYt, + prevEpSt, + prevLySt, + // eslint-disable-line + prevTePt, + prevDtPk, + prevSfPk, + prevData, + prevYFactor // eslint-disable-line + }); + } + setTip() { + this.tip = (0, _init.InitTip)(); + this.root.call(this.tip); + } + computeYTransformFactor(layout, cyclicvoltaSt, feature) { + let factor = 1.0; + if (layout === _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY && cyclicvoltaSt && cyclicvoltaSt.useCurrentDensity) { + const rawArea = (cyclicvoltaSt.areaValue === '' ? 1.0 : cyclicvoltaSt.areaValue) || 1.0; + const areaUnit = cyclicvoltaSt.areaUnit || 'cm²'; + const safeArea = rawArea > 0 ? rawArea : 1.0; + const areaInCm2 = areaUnit === 'mm²' ? safeArea / 100.0 : safeArea; + factor = 1.0 / areaInCm2; + const baseY = feature && feature.yUnit ? String(feature.yUnit) : 'A'; + if (/mA/i.test(baseY)) { + factor *= 1000.0; + } + if (areaUnit === 'mm²') { + factor /= 100.0; + } + } + return factor; + } + transformYValue(y) { + return y * this.yTransformFactor; + } + setDataParams(filterSeed, peaks, tTrEndPts, tSfPeaks, layout, cyclicvoltaSt, jcampIdx = 0) { + this.data = []; + this.otherLineData = []; + let filterSubLayoutValue = null; + const currFeature = this.entities && this.entities[0] ? this.entities[0].feature : null; + this.yTransformFactor = this.computeYTransformFactor(layout, cyclicvoltaSt, currFeature); + this.entities.forEach((entry, idx) => { + const { + topic, + feature, + color + } = entry; + const offset = (0, _chem.GetCyclicVoltaPreviousShift)(cyclicvoltaSt, jcampIdx); + let currData = (0, _chem.convertTopic)(topic, layout, feature, offset); + if (idx === jcampIdx) { + if (!_format.default.isCyclicVoltaLayout(layout)) { + currData = filterSeed; + } + this.data = [...currData]; + this.pathColor = color; + filterSubLayoutValue = _format.default.isSECLayout(layout) ? feature.xUnit : feature.yUnit; + } else { + const filterValue = _format.default.isSECLayout(layout) ? feature.xUnit : feature.yUnit; + this.otherLineData.push({ + data: currData, + color, + filterSublayout: filterValue + }); + } + }); + if (_format.default.isSECLayout(layout) || _format.default.isGCLayout(layout)) { + this.otherLineData = this.otherLineData.filter(data => { + return data.filterSublayout === filterSubLayoutValue; + }); + } + if (this.jcampIdx === jcampIdx) { + this.dataPks = [...peaks]; + } else { + this.dataPks = peaks; + } + this.tTrEndPts = tTrEndPts; + this.tSfPeaks = tSfPeaks; + this.layout = layout; + this.cyclicvoltaSt = cyclicvoltaSt; + this.jcampIdx = jcampIdx; + } + updatePathCall(xt, yt) { + this.pathCall = d3.line().x(d => xt(d.x)).y(d => yt(d.y)); + } + setYAxisTickFormat() { + const f = this.yTransformFactor || 1; + const format = d3.format('.2n'); + this.axisCall.y.tickFormat(v => format(v * f)); + } + setConfig(sweepExtentSt) { + // Domain Calculate + let { + xExtent, + yExtent + } = sweepExtentSt || { + xExtent: false, + yExtent: false + }; + if (!xExtent || !yExtent) { + let allData = [...this.data]; + if (this.otherLineData) { + this.otherLineData.forEach(lineData => { + allData = [...allData, ...lineData.data]; + }); + } + const xes = d3.extent(allData, d => d.x).sort((a, b) => a - b); + xExtent = { + xL: xes[0], + xU: xes[1] + }; + const btm = d3.min(allData, d => d.y); + const top = d3.max(allData, d => d.y); + const height = top - btm; + yExtent = { + yL: btm - this.factor * height, + yU: top + this.factor * height + }; + } + this.scales.x.domain([xExtent.xL, xExtent.xU]); + this.scales.y.domain([yExtent.yL, yExtent.yU]); + + // rescale for zoom + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + + // Axis Call + this.axisCall.x.scale(xt); + this.axisCall.y.scale(yt); + if (this.layout === _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY) { + this.setYAxisTickFormat(); + } + this.currentExtent = { + xExtent, + yExtent + }; + } + drawLine() { + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + this.updatePathCall(xt, yt); + this.path.attr('d', this.pathCall(this.data)); + this.path.style('stroke', this.pathColor); + if (this.layout === _list_layout.LIST_LAYOUT.AIF) { + this.path.attr('marker-mid', 'url(#arrow-left)'); + } + } + drawThres() { + if (this.tTrEndPts.length > 0) { + this.thresLineUp.attr('d', this.pathCall(this.tTrEndPts)); + this.thresLineUp.attr('visibility', 'visible'); + const [left, right] = this.tTrEndPts; + const dwMirrorEndPts = [Object.assign({}, left, { + y: -left.y + }), Object.assign({}, right, { + y: -right.y + })]; + this.thresLineDw.attr('d', this.pathCall(dwMirrorEndPts)); + this.thresLineDw.attr('visibility', 'visible'); + } else { + this.thresLineUp.attr('visibility', 'hidden'); + this.thresLineDw.attr('visibility', 'hidden'); + } + } + drawOtherLines(layout) { + d3.selectAll('.line-clip-compare').remove(); + if (!this.otherLineData) return null; + this.otherLineData.forEach((entry, idx) => { + const { + data, + color + } = entry; + const pathColor = color ? color : _format.default.mutiEntitiesColors(idx); + const path = (0, _mount.MountComparePath)(this, pathColor, idx, 0.4); + path.attr('d', this.pathCall(data)); + if (this.layout === _list_layout.LIST_LAYOUT.AIF && this.isShowAllCurves === true) { + path.attr('marker-mid', 'url(#arrow-left)'); + } + }); + return null; + } + drawGrid() { + const { + sameXY, + sameYFactor + } = this.shouldUpdate; + if (sameXY && sameYFactor) return; + this.grid.x.call(this.axisCall.x.tickSize(-this.h, 0, 0)).selectAll('line').attr('stroke', '#ddd').attr('stroke-opacity', 0.6).attr('fill', 'none'); + this.grid.y.call(this.axisCall.y.tickSize(-this.w, 0, 0)).selectAll('line').attr('stroke', '#ddd').attr('stroke-opacity', 0.6).attr('fill', 'none'); + } + onClickTarget(event, data) { + event.stopPropagation(); + event.preventDefault(); + const onPeak = true; + if (this.layout === _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY) { + const { + spectraList + } = this.cyclicvoltaSt; + const spectra = spectraList[this.jcampIdx]; + const voltammetryPeakIdx = spectra.selectedIdx; + this.clickUiTargetAct(data, onPeak, voltammetryPeakIdx, this.jcampIdx); + } else { + this.clickUiTargetAct(data, onPeak, false, this.jcampIdx); + } + } + clearSplitPreview() { + (0, _integration_split.clearIntegrationSplitPreview)(this); + } + onIntegrationMouseMove(event, data, shift, ignoreRef) { + if (!this.isUiSplitIntgSt) return; + const splitX = (0, _integration_split.getSplitXFromEvent)(event, this); + (0, _integration_split.drawIntegrationSplitPreview)(this, data, splitX, shift, ignoreRef); + } + onClickIntegrationTarget(event, data) { + if (!this.isUiSplitIntgSt) { + this.onClickTarget(event, data); + return; + } + event.stopPropagation(); + event.preventDefault(); + const splitX = (0, _integration_split.getSplitXFromEvent)(event, this); + this.clearSplitPreview(); + this.splitIntegrationAct({ + curveIdx: this.jcampIdx, + target: data, + splitX, + data: this.data + }); + } + onClickPecker(event, data) { + event.stopPropagation(); + event.preventDefault(); + const onPecker = true; + const { + spectraList + } = this.cyclicvoltaSt; + const spectra = spectraList[this.jcampIdx]; + const voltammetryPeakIdx = spectra.selectedIdx; + this.clickUiTargetAct(data, false, voltammetryPeakIdx, this.jcampIdx, onPecker); + } + mergedPeaks(editPeakSt) { + if (!editPeakSt) return this.dataPks; + const { + spectraList + } = this.cyclicvoltaSt; + const spectra = spectraList[this.jcampIdx]; + if (spectra) { + this.dataPks = []; + this.dataPks = (0, _converter.PksEdit)(this.dataPks, editPeakSt, spectra.list); + } else { + const newEditPeaks = Object.assign({}, editPeakSt, { + selectedIdx: this.jcampIdx + }); + this.dataPks = (0, _converter.PksEdit)(this.dataPks, newEditPeaks, []); + } + return this.dataPks; + } + setDataPecker() { + const { + spectraList + } = this.cyclicvoltaSt; + const spectra = spectraList[this.jcampIdx]; + if (spectra) { + this.dataPeckers = (0, _converter.PeckersEdit)(spectra.list); + } + return this.dataPeckers; + } + drawAUC(stack, shift = 0) { + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + const auc = this.tags.aucPath.selectAll('path').data(stack); + auc.exit().attr('class', 'exit').remove(); + const integCurve = border => { + const { + xL, + xU + } = border; + const ps = (0, _integration.getIntegrationPoints)(xL, xU, this.data); + if (!ps[0]) return null; + const baselineY = (0, _integration.getLinearBaseline)(ps); + return d3.area().x(d => xt(d.x)).y0(d => yt(baselineY(d))).y1(d => yt(d.y))(ps); + }; + auc.enter().append('path').attr('class', 'auc').attr('fill', 'red').attr('stroke', 'none').attr('fill-opacity', 0.2).attr('stroke-width', 2).merge(auc).attr('d', d => integCurve(d)).attr('id', d => `auc${(0, _focus.itgIdTag)(d)}`).on('mouseover', (event, d) => { + d3.select(`#auc${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#auc${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#auc${(0, _focus.itgIdTag)(d)}`).style('fill', 'blue'); + }).on('mouseout', (event, d) => { + d3.select(`#auc${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'none'); + d3.select(`#auc${(0, _focus.itgIdTag)(d)}`).style('fill', 'red'); + d3.select(`#auc${(0, _focus.itgIdTag)(d)}`).style('fill-opacity', 0.2); + this.clearSplitPreview(); + }).on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, true)).on('click', (event, d) => this.onClickIntegrationTarget(event, d)); + } + drawPeaks(editPeakSt) { + const { + sameXY, + sameEpSt, + sameDtPk, + sameSfPk + } = this.shouldUpdate; + if (!_format.default.isCyclicVoltaLayout(this.layout) && sameXY && sameEpSt && sameDtPk && sameSfPk) return; + + // rescale for zoom + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + const dPks = this.mergedPeaks(editPeakSt); + const { + spectraList + } = this.cyclicvoltaSt; + const spectra = spectraList[this.jcampIdx]; + let indexOfCVRefPeaks = []; + if (spectra) { + const { + shift, + hasRefPeak + } = spectra; + const { + ref + } = shift; + if (ref && hasRefPeak) { + const { + min, + max + } = ref; + indexOfCVRefPeaks = dPks.map((p, index) => { + return p === min || p === max ? -1 : index; + }); + } + } + const mpp = this.tags.pPath.selectAll('path').data(dPks); + mpp.exit().attr('class', 'exit').remove(); + const linePath = [{ + x: -0.5, + y: 10 + }, { + x: -0.5, + y: -20 + }, { + x: 0.5, + y: -20 + }, { + x: 0.5, + y: 10 + }]; + const lineSymbol = d3.line().x(d => d.x).y(d => d.y)(linePath); + const lineRefPath = [{ + x: -0.5, + y: 10 + }, { + x: -4, + y: -20 + }, { + x: 4, + y: -20 + }, { + x: 0.5, + y: 10 + }]; + const lineSymbolRef = d3.line().x(d => d.x).y(d => d.y)(lineRefPath); + mpp.enter().append('path').attr('d', (_, index) => { + return indexOfCVRefPeaks[index] === -1 ? lineSymbolRef : lineSymbol; + }).attr('class', 'enter-peak').attr('fill', (_, index) => { + return indexOfCVRefPeaks[index] === -1 ? 'blue' : 'red'; + }).attr('stroke', 'pink').attr('stroke-width', 3).attr('stroke-opacity', 0.0).merge(mpp).attr('id', d => `mpp${Math.round(1000 * d.x)}`).attr('transform', d => `translate(${xt(d.x)}, ${yt(d.y)})`).on('mouseover', (event, d) => { + d3.select(`#mpp${Math.round(1000 * d.x)}`).attr('stroke-opacity', '1.0'); + d3.select(`#bpt${Math.round(1000 * d.x)}`).style('fill', 'blue'); + const tipParams = { + d, + layout: this.layout, + yFactor: this.yTransformFactor + }; + this.tip.show(tipParams, event.target); + }).on('mouseout', (event, d) => { + d3.select(`#mpp${Math.round(1000 * d.x)}`).attr('stroke-opacity', '0.0'); + d3.select(`#bpt${Math.round(1000 * d.x)}`).style('fill', 'red'); + const tipParams = { + d, + layout: this.layout, + yFactor: this.yTransformFactor + }; + this.tip.hide(tipParams, event.target); + }).on('click', (event, d) => this.onClickTarget(event, d)); + const ignoreRef = _format.default.isHplcUvVisLayout(this.layout); + if (ignoreRef) { + 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)); + } + mpp.attr('fill', (_, index) => { + return indexOfCVRefPeaks[index] === -1 ? 'blue' : 'red'; + }); + mpp.attr('d', (_, index) => { + return indexOfCVRefPeaks[index] === -1 ? lineSymbolRef : lineSymbol; + }); + } + drawPeckers() { + const { + sameXY, + sameEpSt, + sameDtPk, + sameSfPk + } = this.shouldUpdate; + if (!_format.default.isCyclicVoltaLayout(this.layout) && sameXY && sameEpSt && sameDtPk && sameSfPk) return; + + // rescale for zoom + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + const dPks = this.setDataPecker(); + const mpp = this.tags.peckerPath.selectAll('path').data(dPks); + mpp.exit().attr('class', 'exit').remove(); + const linePath = [{ + x: -0.5, + y: 10 + }, { + x: -0.5, + y: -20 + }, { + x: 0.5, + y: -20 + }, { + x: 0.5, + y: 10 + }]; + const lineSymbol = d3.line().x(d => d.x).y(d => d.y)(linePath); + mpp.enter().append('path').attr('d', lineSymbol).attr('class', 'enter-peak').attr('fill', '#228B22').attr('stroke', 'pink').attr('stroke-width', 3).attr('stroke-opacity', 0.0).merge(mpp).attr('id', d => `mpp${Math.round(1000 * d.x)}`).attr('transform', d => `translate(${xt(d.x)}, ${yt(d.y)})`).on('mouseover', (event, d) => { + d3.select(`#mpp${Math.round(1000 * d.x)}`).attr('stroke-opacity', '1.0'); + d3.select(`#bpt${Math.round(1000 * d.x)}`).style('fill', 'blue'); + const tipParams = { + d, + layout: this.layout, + yFactor: this.yTransformFactor + }; + this.tip.show(tipParams, event.target); + }).on('mouseout', (event, d) => { + d3.select(`#mpp${Math.round(1000 * d.x)}`).attr('stroke-opacity', '0.0'); + d3.select(`#bpt${Math.round(1000 * d.x)}`).style('fill', '#228B22'); + const tipParams = { + d, + layout: this.layout, + yFactor: this.yTransformFactor + }; + this.tip.hide(tipParams, event.target); + }).on('click', (event, d) => this.onClickPecker(event, d)); + } + drawInteg(integationSt) { + const { + sameXY, + sameLySt, + sameItSt, + sameData + } = this.shouldUpdate; + if (sameXY && sameLySt && sameItSt && sameData) return; + const { + integrations + } = integationSt; + 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(); + return; + } + const { + stack, + refArea, + refFactor, + shift + } = selectedIntegration; + const isDisable = _cfg.default.btnCmdIntg(this.layout); + const ignoreRef = _format.default.isHplcUvVisLayout(this.layout); + const itgs = isDisable ? [] : stack; + Object.assign(this, { + integrationSplitTargets: { + stack: itgs, + shift, + ignoreRef + } + }); + 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(); + 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); + return; + } + if (ignoreRef) { + this.drawAUC(stack, shift); + } else { + // rescale for zoom + const { + xt + } = (0, _compass.TfRescale)(this); + const dh = 50; + const integBar = data => d3.line()([[xt(data.xL - shift), dh], [xt(data.xL - shift), dh - 10], [xt(data.xL - shift), dh - 5], [xt(data.xU - shift), dh - 5], [xt(data.xU - shift), dh - 10], [xt(data.xU - shift), dh]]); + igbp.enter().append('path').attr('class', 'igbp').attr('fill', 'none').attr('stroke', '#228B22').attr('stroke-width', 2).merge(igbp).attr('id', d => `igbp${(0, _focus.itgIdTag)(d)}`).attr('d', d => integBar(d)).on('mouseover', (event, d) => { + d3.select(`#igbp${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#igbc${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#igtp${(0, _focus.itgIdTag)(d)}`).style('fill', 'blue'); + }).on('mouseout', (event, d) => { + d3.select(`#igbp${(0, _focus.itgIdTag)(d)}`).attr('stroke', '#228B22'); + d3.select(`#igbc${(0, _focus.itgIdTag)(d)}`).attr('stroke', '#228B22'); + d3.select(`#igtp${(0, _focus.itgIdTag)(d)}`).style('fill', '#228B22'); + this.clearSplitPreview(); + }).on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, ignoreRef)).on('click', (event, d) => this.onClickIntegrationTarget(event, d)); + const integCurve = border => { + const { + xL, + xU + } = border; + const [nXL, nXU] = [xL - shift, xU - shift]; + const ps = this.data.filter(d => d.x > nXL && d.x < nXU); + const kMax = this.data[this.data.length - 1].k; + if (!ps[0]) return null; + const kRef = ps[0].k; + if (!this.reverseXAxis(this.layout)) { + return d3.line().x(d => xt(d.x)).y(d => 100 - (kRef - d.k) * 400 / kMax)(ps); + } + return d3.line().x(d => xt(d.x)).y(d => 300 - (d.k - kRef) * 400 / kMax)(ps); + }; + igcp.enter().append('path').attr('class', 'igcp').attr('fill', 'none').attr('stroke', '#228B22').attr('stroke-width', 2).merge(igcp).attr('id', d => `igbc${(0, _focus.itgIdTag)(d)}`).attr('d', d => integCurve(d)).on('mouseover', (event, d) => { + d3.select(`#igbp${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#igbc${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#igtp${(0, _focus.itgIdTag)(d)}`).style('fill', 'blue'); + }).on('mouseout', (event, d) => { + d3.select(`#igbp${(0, _focus.itgIdTag)(d)}`).attr('stroke', '#228B22'); + d3.select(`#igbc${(0, _focus.itgIdTag)(d)}`).attr('stroke', '#228B22'); + d3.select(`#igtp${(0, _focus.itgIdTag)(d)}`).style('fill', '#228B22'); + this.clearSplitPreview(); + }).on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, ignoreRef)).on('click', (event, d) => this.onClickIntegrationTarget(event, d)); + igtp.enter().append('text').attr('class', 'igtp').attr('font-family', 'Helvetica').style('font-size', '12px').attr('fill', '#228B22').style('text-anchor', 'middle').merge(igtp).attr('id', d => `igtp${(0, _focus.itgIdTag)(d)}`).text(d => (0, _integration.calcArea)(d, refArea, refFactor, ignoreRef)).attr('transform', d => `translate(${xt((d.xL + d.xU) / 2 - shift)}, ${dh - 12})`).on('mouseover', (event, d) => { + d3.select(`#igbp${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#igbc${(0, _focus.itgIdTag)(d)}`).attr('stroke', 'blue'); + d3.select(`#igtp${(0, _focus.itgIdTag)(d)}`).style('fill', 'blue'); + }).on('mouseout', (event, d) => { + d3.select(`#igbp${(0, _focus.itgIdTag)(d)}`).attr('stroke', '#228B22'); + d3.select(`#igbc${(0, _focus.itgIdTag)(d)}`).attr('stroke', '#228B22'); + d3.select(`#igtp${(0, _focus.itgIdTag)(d)}`).style('fill', '#228B22'); + this.clearSplitPreview(); + }).on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, ignoreRef)).on('click', (event, d) => this.onClickIntegrationTarget(event, d)); + } + } + drawMtply(mtplySt) { + const { + sameXY, + sameLySt, + sameMySt + } = this.shouldUpdate; + if (sameXY && sameLySt && sameMySt) 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, + shift + } = selectedMulti; + const mpys = stack; + const isDisable = _cfg.default.btnCmdMpy(this.layout); + if (mpys === 0 || isDisable) return; + // rescale for zoom + const { + xt + } = (0, _compass.TfRescale)(this); + 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(); + const height = this.h; + const dh = Math.abs(0.06 * height); + const mpyBar = data => d3.line()([[xt(data.xExtent.xL - shift), height - dh], [xt(data.xExtent.xL - shift), height - dh - 10], [xt(data.xExtent.xL - shift), height - dh - 5], [xt(data.xExtent.xU - shift), height - dh - 5], [xt(data.xExtent.xU - shift), height - dh - 10], [xt(data.xExtent.xU - shift), height - dh]]); + const mpyColor = d => { + const { + xL, + xU + } = d.xExtent; + return smExtext.xL === xL && smExtext.xU === xU ? 'purple' : '#DA70D6'; + }; + mpyb.enter().append('path').attr('class', 'mpyb').attr('fill', 'none').attr('stroke-width', 2).merge(mpyb).attr('stroke', d => mpyColor(d)).attr('id', d => `mpyb${(0, _focus.mpyIdTag)(d)}`).attr('d', d => mpyBar(d)).on('mouseover', (event, d) => { + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + }).on('mouseout', (event, d) => { + const dColor = mpyColor(d); + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + }).on('click', (event, d) => this.onClickTarget(event, d)); + mpyt1.enter().append('text').attr('class', 'mpyt1').attr('font-family', 'Helvetica').style('font-size', '12px').style('text-anchor', 'middle').merge(mpyt1).attr('fill', d => mpyColor(d)).attr('id', d => `mpyt1${(0, _focus.mpyIdTag)(d)}`).text(d => `${(0, _multiplicity_calc.calcMpyCenter)(d.peaks, shift, d.mpyType).toFixed(3)}`).attr('transform', d => `translate(${xt((d.xExtent.xL + d.xExtent.xU) / 2 - shift)}, ${height - dh + 12})`).on('mouseover', (event, d) => { + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + }).on('mouseout', (event, d) => { + const dColor = mpyColor(d); + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + }).on('click', (event, d) => this.onClickTarget(event, d)); + mpyt2.enter().append('text').attr('class', 'mpyt2').attr('font-family', 'Helvetica').style('font-size', '12px').style('text-anchor', 'middle').merge(mpyt2).attr('fill', d => mpyColor(d)).attr('id', d => `mpyt2${(0, _focus.mpyIdTag)(d)}`).text(d => `(${d.mpyType})`).attr('transform', d => `translate(${xt((d.xExtent.xL + d.xExtent.xU) / 2 - shift)}, ${height - dh + 24})`).on('mouseover', (event, d) => { + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + }).on('mouseout', (event, d) => { + const dColor = mpyColor(d); + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + }).on('click', (event, d) => this.onClickTarget(event, d)); + const mpypH = height - dh; + const mpypPath = pk => [{ + x: xt(pk.x - shift) - 0.5, + y: mpypH - 5 + }, { + x: xt(pk.x - shift) - 0.5, + y: mpypH - 20 + }, { + x: xt(pk.x - shift) + 0.5, + y: mpypH - 20 + }, { + x: xt(pk.x - shift) + 0.5, + y: mpypH - 5 + }]; + // const faktor = layoutSt === LIST_LAYOUT.IR ? -1 : 1; + const lineSymbol = d3.line().x(d => d.x).y(d => d.y); + mpyp.enter().append('path').attr('class', 'mpyp').attr('fill', 'none').merge(mpyp).attr('stroke', d => mpyColor(d)).attr('d', d => lineSymbol(mpypPath(d))).attr('id', d => `mpyp${(0, _focus.mpyIdTag)(d)}`).on('mouseover', (event, d) => { + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', 'blue'); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', 'blue'); + }).on('mouseout', (event, d) => { + const dColor = mpyColor(d); + d3.selectAll(`#mpyb${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + d3.selectAll(`#mpyt1${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyt2${(0, _focus.mpyIdTag)(d)}`).style('fill', dColor); + d3.selectAll(`#mpyp${(0, _focus.mpyIdTag)(d)}`).attr('stroke', dColor); + }).on('click', (event, d) => this.onClickTarget(event, d)); + } + drawRef() { + // rescale for zoom + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + const ccp = this.ref.selectAll('path').data(this.tSfPeaks); + ccp.exit().attr('class', 'exit').remove(); + const linePath = [{ + x: -0.5, + y: 10 + }, { + x: -4, + y: -20 + }, { + x: 4, + y: -20 + }, { + x: 0.5, + y: 10 + }]; + const faktor = _format.default.isIrLayout(this.layout) ? -1 : 1; + const lineSymbol = d3.line().x(d => d.x).y(d => faktor * d.y)(linePath); + ccp.enter().append('path').attr('d', lineSymbol).attr('class', 'enter-ref').attr('fill', 'green').attr('fill-opacity', 0.8).merge(ccp).attr('transform', d => `translate(${xt(d.x)}, ${yt(d.y)})`); + } + reverseXAxis(layoutSt) { + return [_list_layout.LIST_LAYOUT.UVVIS, _list_layout.LIST_LAYOUT.HPLC_UVVIS, _list_layout.LIST_LAYOUT.TGA, _list_layout.LIST_LAYOUT.DSC, _list_layout.LIST_LAYOUT.XRD, _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY, _list_layout.LIST_LAYOUT.CDS, _list_layout.LIST_LAYOUT.SEC, _list_layout.LIST_LAYOUT.GC, _list_layout.LIST_LAYOUT.AIF].indexOf(layoutSt) < 0; + } + create({ + curveSt, + filterSeed, + filterPeak, + tTrEndPts, + tSfPeaks, + editPeakSt, + layoutSt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + cyclicvoltaSt, + integationSt, + mtplySt + }) { + this.svg = d3.select(this.rootKlass).select('.d3Svg'); + (0, _mount.MountMainFrame)(this, 'focus'); + (0, _mount.MountClip)(this); + const { + curveIdx, + isShowAllCurve + } = curveSt; + const jcampIdx = curveIdx; + this.isShowAllCurves = isShowAllCurve; + 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); + Object.assign(this, { + isUiSplitIntgSt + }); + if (!isUiSplitIntgSt) this.clearSplitPreview(); + (0, _compass.MountCompass)(this); + this.axis = (0, _mount.MountAxis)(this); + this.path = (0, _mount.MountPath)(this, this.pathColor); + [this.thresLineUp, this.thresLineDw] = (0, _mount.MountThresLine)(this, 'green'); + this.grid = (0, _mount.MountGrid)(this); + this.tags = (0, _mount.MountTags)(this); + this.ref = (0, _mount.MountRef)(this); + (0, _mount.MountAxisLabelX)(this); + (0, _mount.MountAxisLabelY)(this); + if (this.data && this.data.length > 0) { + this.setConfig(sweepExtentSt); + this.drawLine(); + this.drawThres(); + this.drawGrid(); + this.drawOtherLines(layoutSt); + this.drawPeaks(editPeakSt); + this.drawRef(); + this.drawPeckers(); + this.drawInteg(integationSt); + this.drawMtply(mtplySt); + } + (0, _brush.default)(this, isUiAddIntgSt, isUiNoBrushSt); + this.resetShouldUpdate(editPeakSt); + } + update({ + entities, + curveSt, + filterSeed, + filterPeak, + tTrEndPts, + tSfPeaks, + editPeakSt, + layoutSt, + sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiNoBrushSt, + cyclicvoltaSt, + integationSt, + mtplySt + }) { + this.root = d3.select(this.rootKlass).selectAll('.focus-main'); + this.scales = (0, _init.InitScale)(this, this.reverseXAxis(layoutSt)); + const { + curveIdx, + isShowAllCurve + } = curveSt; + const jcampIdx = curveIdx; + this.isShowAllCurves = isShowAllCurve; + this.entities = entities; + this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, layoutSt, cyclicvoltaSt, jcampIdx); + Object.assign(this, { + isUiSplitIntgSt + }); + if (!isUiSplitIntgSt) this.clearSplitPreview(); + if (this.data && this.data.length > 0) { + this.setConfig(sweepExtentSt); + this.getShouldUpdate(editPeakSt); + this.drawLine(); + this.drawThres(); + this.drawGrid(); + this.drawOtherLines(layoutSt); + this.drawPeaks(editPeakSt); + this.drawRef(); + this.drawPeckers(); + this.drawInteg(integationSt); + this.drawMtply(mtplySt); + } + (0, _brush.default)(this, isUiAddIntgSt, isUiNoBrushSt); + this.resetShouldUpdate(editPeakSt); + } +} +var _default = exports.default = MultiFocus; \ No newline at end of file diff --git a/dist/components/d3_rect/index.js b/dist/components/d3_rect/index.js new file mode 100644 index 00000000..8c537de3 --- /dev/null +++ b/dist/components/d3_rect/index.js @@ -0,0 +1,152 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _chem = require("../../helpers/chem"); +var _manager = require("../../actions/manager"); +var _ui = require("../../actions/ui"); +var _rect_focus = _interopRequireDefault(require("./rect_focus")); +var _draw = require("../common/draw"); +var _list_ui = require("../../constants/list_ui"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable no-mixed-operators */ + +const W = Math.round(window.innerWidth * 0.90 * 9 / 12); // ROI +const H = Math.round(window.innerHeight * 0.90 * 0.85); // ROI + +class ViewerRect extends _react.default.Component { + constructor(props) { + super(props); + const { + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct + } = props; + this.rootKlass = '.d3Rect'; + this.focus = new _rect_focus.default({ + W, + H, + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct + }); + this.normChange = this.normChange.bind(this); + } + componentDidMount() { + const { + seed, + peak, + cLabel, + xLabel, + yLabel, + feature, + tTrEndPts, + tSfPeaks, + isHidden, + sweepExtentSt, + isUiAddIntgSt, + isUiNoBrushSt, + resetAllAct + } = this.props; + (0, _draw.drawDestroy)(this.rootKlass); + resetAllAct(feature); + const filterSeed = seed; + const filterPeak = peak; + (0, _draw.drawMain)(this.rootKlass, W, H); + this.focus.create({ + filterSeed, + filterPeak, + tTrEndPts, + tSfPeaks, + sweepExtentSt, + isUiAddIntgSt, + isUiNoBrushSt + }); + (0, _draw.drawLabel)(this.rootKlass, cLabel, xLabel, yLabel); + (0, _draw.drawDisplay)(this.rootKlass, isHidden); + } + componentDidUpdate(prevProps) { + const { + seed, + peak, + tTrEndPts, + tSfPeaks, + isHidden, + sweepExtentSt, + isUiAddIntgSt, + isUiNoBrushSt + } = this.props; + this.normChange(prevProps); + const filterSeed = seed; + const filterPeak = peak; + this.focus.update({ + filterSeed, + filterPeak, + tTrEndPts, + tSfPeaks, + sweepExtentSt, + isUiAddIntgSt, + isUiNoBrushSt + }); + (0, _draw.drawDisplay)(this.rootKlass, isHidden); + } + componentWillUnmount() { + (0, _draw.drawDestroy)(this.rootKlass); + } + normChange(prevProps) { + const { + feature, + resetAllAct + } = this.props; + const oldFeature = prevProps.feature; + if (oldFeature !== feature) { + resetAllAct(feature); + } + } + render() { + return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: "d3Rect" + }); + } +} +const mapStateToProps = (state, props) => ({ + seed: (0, _chem.Topic2Seed)(state, props), + peak: (0, _chem.Feature2Peak)(state, props), + tTrEndPts: (0, _chem.ToThresEndPts)(state, props), + tSfPeaks: (0, _chem.ToShiftPeaks)(state, props), + sweepExtentSt: state.ui.sweepExtent, + isUiAddIntgSt: state.ui.sweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_ADD, + isUiNoBrushSt: _list_ui.LIST_NON_BRUSH_TYPES.indexOf(state.ui.sweepType) < 0 +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + resetAllAct: _manager.resetAll, + clickUiTargetAct: _ui.clickUiTarget, + selectUiSweepAct: _ui.selectUiSweep, + scrollUiWheelAct: _ui.scrollUiWheel +}, dispatch); +ViewerRect.propTypes = { + seed: _propTypes.default.array.isRequired, + peak: _propTypes.default.array.isRequired, + cLabel: _propTypes.default.string.isRequired, + xLabel: _propTypes.default.string.isRequired, + yLabel: _propTypes.default.string.isRequired, + feature: _propTypes.default.object.isRequired, + tTrEndPts: _propTypes.default.array.isRequired, + tSfPeaks: _propTypes.default.array.isRequired, + sweepExtentSt: _propTypes.default.object.isRequired, + isUiAddIntgSt: _propTypes.default.bool.isRequired, + isUiNoBrushSt: _propTypes.default.bool.isRequired, + resetAllAct: _propTypes.default.func.isRequired, + clickUiTargetAct: _propTypes.default.func.isRequired, + selectUiSweepAct: _propTypes.default.func.isRequired, + scrollUiWheelAct: _propTypes.default.func.isRequired, + isHidden: _propTypes.default.bool.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)(ViewerRect); \ No newline at end of file diff --git a/dist/components/d3_rect/rect_focus.js b/dist/components/d3_rect/rect_focus.js new file mode 100644 index 00000000..e9bb0ff5 --- /dev/null +++ b/dist/components/d3_rect/rect_focus.js @@ -0,0 +1,225 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _init = require("../../helpers/init"); +var _brush = _interopRequireDefault(require("../../helpers/brush")); +var _mount = require("../../helpers/mount"); +var _compass = require("../../helpers/compass"); +var _converter = require("../../helpers/converter"); +var _list_layout = require("../../constants/list_layout"); +const d3 = require('d3'); +class RectFocus { + constructor(props) { + const { + W, + H, + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct + } = props; + this.rootKlass = '.d3Rect'; + this.margin = { + t: 5, + b: 40, + l: 60, + r: 5 + }; + this.w = W - this.margin.l - this.margin.r; + this.h = H - this.margin.t - this.margin.b; + this.clickUiTargetAct = clickUiTargetAct; + this.selectUiSweepAct = selectUiSweepAct; + this.scrollUiWheelAct = scrollUiWheelAct; + this.brush = d3.brush(); + this.axis = null; + this.thresLine = null; + this.grid = null; + this.ref = null; + this.ccPattern = null; + this.data = []; + this.dataPks = []; + this.tTrEndPts = null; + this.tSfPeaks = null; + this.root = null; + this.svg = null; + this.bars = null; + this.scales = (0, _init.InitScale)(this, false); + this.axisCall = (0, _init.InitAxisCall)(5); + this.pathCall = null; + this.tip = null; + this.factor = 0.125; + this.currentExtent = null; + this.layout = _list_layout.LIST_LAYOUT.MS; + this.isUiAddIntgSt = false; + this.firstIntegrationPoint = null; + this.setTip = this.setTip.bind(this); + this.setDataParams = this.setDataParams.bind(this); + this.create = this.create.bind(this); + this.update = this.update.bind(this); + this.setConfig = this.setConfig.bind(this); + this.drawBar = this.drawBar.bind(this); + this.drawThres = this.drawThres.bind(this); + this.drawGrid = this.drawGrid.bind(this); + this.mergedPeaks = this.mergedPeaks.bind(this); + this.isFirefox = typeof InstallTrigger !== 'undefined'; + } + setTip() { + this.tip = (0, _init.InitTip)(); + this.root.call(this.tip); + } + setDataParams(data, peaks, tTrEndPts, tSfPeaks) { + this.data = [...data]; + this.dataPks = [...peaks]; + this.tTrEndPts = tTrEndPts; + this.tSfPeaks = tSfPeaks; + } + updatePathCall(xt, yt) { + this.pathCall = d3.line().x(d => xt(d.x)).y(d => yt(d.y)); + } + setConfig(sweepExtentSt) { + // Domain Calculate + let { + xExtent, + yExtent + } = sweepExtentSt || { + xExtent: false, + yExtent: false + }; + if (!xExtent || !yExtent) { + const xes = d3.extent(this.data, d => d.x).sort((a, b) => a - b); + xExtent = { + xL: xes[0] - 10, + xU: xes[1] + 10 + }; + const btm = 0; // MS baseline is always 0. + const top = d3.max(this.data, d => d.y); + const height = top - btm; + yExtent = { + yL: btm - this.factor * height, + yU: top + this.factor * height + }; + } + this.scales.x.domain([xExtent.xL, xExtent.xU]); + this.scales.y.domain([yExtent.yL, yExtent.yU]); + + // rescale for zoom + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + + // Axis Call + this.axisCall.x.scale(xt); + this.axisCall.y.scale(yt); + this.currentExtent = { + xExtent, + yExtent + }; + } + posHeight(gnd, val) { + const h = gnd - val; + return h >= 0 ? h : 0; + } + barColor(y, yRef) { + return y >= yRef ? 'steelblue' : '#aaa'; + } + drawBar() { + const { + xt, + yt + } = (0, _compass.TfRescale)(this); + this.updatePathCall(xt, yt); + const yRef = this.tTrEndPts[0].y; + const bars = this.bars.selectAll('rect').data(this.data); + bars.exit().attr('class', 'exit').remove(); + const gnd = yt(0); + bars.enter().append('rect').attr('class', 'enter-bar').attr('width', 1.5).merge(bars).attr('fill', d => this.barColor(d.y, yRef)).attr('height', d => this.posHeight(gnd, yt(d.y))).attr('id', d => `mpp${Math.round(1000 * d.x)}`).attr('transform', d => `translate(${xt(d.x)}, ${yt(d.y)})`).on('mouseover', (event, d) => { + d3.select(`#mpp${Math.round(1000 * d.x)}`).attr('stroke-opacity', '1.0'); + d3.select(`#bpt${Math.round(1000 * d.x)}`).style('fill', 'blue'); + const tipParams = { + d, + layout: this.layout + }; + this.tip.show(tipParams, event.target); + }).on('mouseout', (event, d) => { + d3.select(`#mpp${Math.round(1000 * d.x)}`).attr('stroke-opacity', '1.0'); + d3.select(`#bpt${Math.round(1000 * d.x)}`).style('fill', 'red'); + const tipParams = { + d, + layout: this.layout + }; + this.tip.hide(tipParams, event.target); + }); + } + drawThres() { + if (this.tTrEndPts.length > 0) { + this.thresLine.attr('d', this.pathCall(this.tTrEndPts)); + this.thresLine.attr('visibility', 'visible'); + } else { + this.thresLine.attr('visibility', 'hidden'); + } + } + drawGrid() { + this.grid.x.call(this.axisCall.x.tickSize(-this.h, 0, 0)).selectAll('line').attr('stroke', '#ddd').attr('stroke-opacity', 0.6).attr('fill', 'none'); + this.grid.y.call(this.axisCall.y.tickSize(-this.w, 0, 0)).selectAll('line').attr('stroke', '#ddd').attr('stroke-opacity', 0.6).attr('fill', 'none'); + } + mergedPeaks(editPeakSt) { + if (!editPeakSt) return this.dataPks; + this.dataPks = (0, _converter.PksEdit)(this.dataPks, editPeakSt); + return this.dataPks; + } + create({ + filterSeed, + filterPeak, + tTrEndPts, + tSfPeaks, + sweepExtentSt, + isUiAddIntgSt, + isUiNoBrushSt + }) { + this.svg = d3.select('.d3Svg'); + (0, _mount.MountMainFrame)(this, 'focus'); + (0, _mount.MountClip)(this); + this.root = d3.select(this.rootKlass).selectAll('.focus-main'); + this.setTip(); + this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks); + (0, _compass.MountCompass)(this); + this.axis = (0, _mount.MountAxis)(this); + [this.thresLine] = (0, _mount.MountThresLine)(this, 'green'); + this.grid = (0, _mount.MountGrid)(this); + this.ref = (0, _mount.MountRef)(this); + this.bars = (0, _mount.MountBars)(this); + (0, _mount.MountAxisLabelX)(this); + (0, _mount.MountAxisLabelY)(this); + if (this.data && this.data.length > 0) { + this.setConfig(sweepExtentSt); + this.drawBar(); + this.drawThres(); + this.drawGrid(); + } + (0, _brush.default)(this, isUiAddIntgSt, isUiNoBrushSt); + } + update({ + filterSeed, + filterPeak, + tTrEndPts, + tSfPeaks, + sweepExtentSt, + isUiAddIntgSt, + isUiNoBrushSt + }) { + this.root = d3.select(this.rootKlass).selectAll('.focus-main'); + this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks); + if (this.data && this.data.length > 0) { + this.setConfig(sweepExtentSt); + this.drawBar(); + this.drawThres(); + this.drawGrid(); + } + (0, _brush.default)(this, isUiAddIntgSt, isUiNoBrushSt); + } +} +var _default = exports.default = RectFocus; \ No newline at end of file diff --git a/dist/components/forecast/comps.js b/dist/components/forecast/comps.js new file mode 100644 index 00000000..263de551 --- /dev/null +++ b/dist/components/forecast/comps.js @@ -0,0 +1,239 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.sectionSvg = exports.sectionInput = exports.notToRenderAnalysis = exports.TxtLabel = exports.StatusIcon = exports.ConfidenceLabel = void 0; +var _react = _interopRequireDefault(require("react")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactSvgFileZoomPan = _interopRequireDefault(require("@complat/react-svg-file-zoom-pan")); +var _CheckCircleOutline = _interopRequireDefault(require("@mui/icons-material/CheckCircleOutline")); +var _ErrorOutline = _interopRequireDefault(require("@mui/icons-material/ErrorOutline")); +var _HighlightOff = _interopRequireDefault(require("@mui/icons-material/HighlightOff")); +var _HelpOutline = _interopRequireDefault(require("@mui/icons-material/HelpOutline")); +var _Help = _interopRequireDefault(require("@mui/icons-material/Help")); +var _material = require("@mui/material"); +var _CloudOff = _interopRequireDefault(require("@mui/icons-material/CloudOff")); +var _section_loading = _interopRequireDefault(require("./section_loading")); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable react/function-component-definition, react/destructuring-assignment, +max-len */ + +const titleStyle = { + backgroundColor: '#f5f5f5', + border: '2px solid #e3e3e3', + borderRadius: '10px', + lineHeight: '200px', + marginBottom: 10, + marginTop: 10, + marginLeft: 40, + textAlign: 'center', + width: '70%' +}; +const txtStyle = { + lineHeight: '20px' +}; +const TxtLabel = (classes, label, extClsName = 'txt-label') => /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtLabel, extClsName), + children: label +}); +exports.TxtLabel = TxtLabel; +const StatusIcon = status => { + switch (status) { + case 'accept': + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Accept" + }), + placement: "left", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_CheckCircleOutline.default, { + style: { + color: '#4caf50' + } + }) + }); + case 'warning': + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Warning" + }), + placement: "left", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorOutline.default, { + style: { + color: '#ffc107' + } + }) + }); + case 'reject': + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Reject" + }), + placement: "left", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_HighlightOff.default, { + style: { + color: '#e91e63' + } + }) + }); + case 'missing': + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Missing" + }), + placement: "left", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_HelpOutline.default, { + style: { + color: '#5d4037' + } + }) + }); + case 'unknown': + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Not Support" + }), + placement: "left", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Help.default, { + style: { + color: '#5d4037' + } + }) + }); + default: + return null; + } +}; +exports.StatusIcon = StatusIcon; +const ConfidenceLabel = (classes, confidence, extClsName = 'txt-label') => { + if (!confidence) return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + children: " - - " + }); + const confidenceDp = parseFloat(Math.round(confidence * 100) / 100).toFixed(2); + return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtLabel, extClsName), + children: `${confidenceDp} %` + }); +}; +exports.ConfidenceLabel = ConfidenceLabel; +const sectionInput = (classes, molecule, inputFuncCb) => { + if (!inputFuncCb) return null; + return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: (0, _classnames.default)(classes.inputRoot), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, { + container: true, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, { + item: true, + xs: 6, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TextField, { + fullWidth: true, + label: TxtLabel(classes, 'Molfile', 'txt-mol-label'), + margin: "normal", + multiline: true, + onChange: inputFuncCb, + rows: "2", + variant: "outlined", + value: molecule + }) + }) + }) + }); +}; +exports.sectionInput = sectionInput; +const SectionRunning = () => /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + style: titleStyle, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("h2", { + style: txtStyle, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.CircularProgress, { + style: { + color: 'blue', + fontSize: 50 + } + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("br", {}), /*#__PURE__*/(0, _jsxRuntime.jsx)("br", {}), /*#__PURE__*/(0, _jsxRuntime.jsx)("p", { + children: "The server is making predictions..." + })] + }) +}); +const SectionMissMatch = () => /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + style: titleStyle, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("h2", { + style: txtStyle, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorOutline.default, { + style: { + color: 'red', + fontSize: 50 + } + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("p", { + className: "txt-predict-fail", + children: "Peak & Element count mismatch!" + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("p", { + className: "txt-predict-fail", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("sup", { + children: "1" + }), "H multiplicity count should not be more than the proton group count. Multiplicity must be assigned manulally before predictions."] + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("p", { + className: "txt-predict-fail", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("sup", { + children: "13" + }), "C peak count should not be more than the carbon count, and solvent peaks should be excluded."] + })] + }) +}); +const SectionNoService = () => /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + style: titleStyle, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("h2", { + style: txtStyle, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_CloudOff.default, { + style: { + color: 'red', + fontSize: 50 + } + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("p", { + children: "Service is not available." + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("p", { + children: "Please try it again later." + })] + }) +}); +const SectionUnknown = () => /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + style: titleStyle, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("h2", { + style: txtStyle, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_HelpOutline.default, { + style: { + color: 'purple', + fontSize: 50 + } + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("p", { + children: "Unknown state." + })] + }) +}); +const notToRenderAnalysis = pds => { + if (pds.running) return /*#__PURE__*/(0, _jsxRuntime.jsx)(SectionRunning, {}); + if (!pds.outline || !pds.outline.code) return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {}); + if (pds.outline.code >= 500) return /*#__PURE__*/(0, _jsxRuntime.jsx)(SectionNoService, {}); + if (pds.outline.code === 400) return /*#__PURE__*/(0, _jsxRuntime.jsx)(SectionMissMatch, {}); + if (pds.outline.code >= 300) return /*#__PURE__*/(0, _jsxRuntime.jsx)(SectionUnknown, {}); + return false; +}; +exports.notToRenderAnalysis = notToRenderAnalysis; +const sectionSvg = (classes, predictions) => { + const renderMsg = notToRenderAnalysis(predictions); + if (renderMsg) return null; + if (!predictions.output) return null; + const targetSvg = predictions.output.result[0].svgs[0]; + if (!targetSvg) return /*#__PURE__*/(0, _jsxRuntime.jsx)(_section_loading.default, {}); + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactSvgFileZoomPan.default, { + svg: targetSvg, + duration: 300, + resize: true + }); +}; +exports.sectionSvg = sectionSvg; \ No newline at end of file diff --git a/dist/components/forecast/ir_comps.js b/dist/components/forecast/ir_comps.js new file mode 100644 index 00000000..0b432458 --- /dev/null +++ b/dist/components/forecast/ir_comps.js @@ -0,0 +1,152 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.IrTableHeader = exports.IrTableBodyRow = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _material = require("@mui/material"); +var _CheckCircleOutline = _interopRequireDefault(require("@mui/icons-material/CheckCircleOutline")); +var _HighlightOff = _interopRequireDefault(require("@mui/icons-material/HighlightOff")); +var _comps = require("./comps"); +var _forecast = require("../../actions/forecast"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable react/function-component-definition, function-paren-newline, +prefer-object-spread */ + +// import SmaToSvg from '../common/chem'; +const baseSelectIrStatus = ({ + sma, + status, + identity, + setIrStatusAct +}) => { + const theStatus = ['accept', 'reject'].includes(status) ? status : ''; + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormControl, { + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Select, { + value: theStatus, + onChange: e => { + setIrStatusAct({ + predictions: { + sma, + identity, + value: e.target.value + }, + svgs: [] + }); + }, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: "accept", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_CheckCircleOutline.default, { + style: { + color: '#4caf50' + } + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: "reject", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_HighlightOff.default, { + style: { + color: '#e91e63' + } + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: "", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {}) + })] + }) + }); +}; +const bssMapStateToProps = (state, props) => ( +// eslint-disable-line +{}); +const bssMapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setIrStatusAct: _forecast.setIrStatus +}, dispatch); +baseSelectIrStatus.propTypes = { + sma: _propTypes.default.string.isRequired, + status: _propTypes.default.string, + identity: _propTypes.default.string.isRequired, + setIrStatusAct: _propTypes.default.func.isRequired +}; +baseSelectIrStatus.defaultProps = { + status: '' +}; +const SelectIrStatus = (0, _reactRedux.connect)(bssMapStateToProps, bssMapDispatchToProps)(baseSelectIrStatus); +const IrTableHeader = classes => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableHead, { + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableRow, { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + children: (0, _comps.TxtLabel)(classes, 'FG SMARTS', 'txt-prd-table-title') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.TxtLabel)(classes, 'Machine Confidence', 'txt-prd-table-title') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.TxtLabel)(classes, 'Machine', 'txt-prd-table-title') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.TxtLabel)(classes, 'Owner', 'txt-prd-table-title') + })] + }) +}); +exports.IrTableHeader = IrTableHeader; +const colorStyles = [{ + backgroundColor: '#FFFF00' +}, { + backgroundColor: '#87CEFA' +}, { + backgroundColor: '#FFB6C1' +}, { + backgroundColor: '#00FF00' +}, { + backgroundColor: '#E6E6FA' +}, { + backgroundColor: '#FFD700' +}, { + backgroundColor: '#F0FFFF' +}, { + backgroundColor: '#F5F5DC' +}]; +const colorLabel = (classes, idx, extClsName = 'txt-label') => { + const style = Object.assign({}, colorStyles[idx % 8], { + width: 20, + borderRadius: 20, + textAlign: 'center' + }); + return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + style: style, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtLabel, extClsName), + children: idx + 1 + }) + }); +}; +const IrTableBodyRow = (classes, idx, fg) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableRow, { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + component: "th", + scope: "row", + children: colorLabel(classes, idx) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + children: (0, _comps.TxtLabel)(classes, fg.sma, 'txt-prd-table-content') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.ConfidenceLabel)(classes, fg.confidence, 'txt-prd-table-content') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.StatusIcon)(fg.status) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectIrStatus, { + sma: fg.sma, + status: fg.statusOwner, + identity: "Owner" + }) + })] +}, `${idx}-${fg.name}`); +exports.IrTableBodyRow = IrTableBodyRow; \ No newline at end of file diff --git a/dist/components/forecast/ir_viewer.js b/dist/components/forecast/ir_viewer.js new file mode 100644 index 00000000..e1d9f482 --- /dev/null +++ b/dist/components/forecast/ir_viewer.js @@ -0,0 +1,117 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _styles = require("@mui/styles"); +var _material = require("@mui/material"); +var _comps = require("./comps"); +var _ir_comps = require("./ir_comps"); +var _jsxRuntime = require("react/jsx-runtime"); +const Styles = () => ({ + root: { + overflowX: 'hidden', + overflowY: 'auto' + }, + container: { + minHeight: '400px' + }, + svgRoot: { + margin: '10px 40px 0px 40px', + height: 'calc(70vh)', + overflowY: 'hidden' + }, + tableRoot: { + margin: '10px 40px 0px 40px', + maxHeight: 'calc(70vh)', + overflowY: 'scroll' + }, + title: { + textAlign: 'left' + }, + btn: { + marginLeft: 40 + }, + reference: { + borderTop: '1px solid #cfd8dc', + margin: '10px 40px 0px 40px', + padding: 5 + }, + inputRoot: { + margin: '10px 40px 0px 40px' + }, + txtLabel: { + fontSize: '12px' + }, + submit: { + margin: '0 0 0 30px', + width: 300 + } +}); +const sectionTable = (classes, pds) => { + const renderMsg = (0, _comps.notToRenderAnalysis)(pds); + if (renderMsg) return renderMsg; + if (!pds.output.result || !pds.output.result[0]) return null; + const { + fgs + } = pds.output.result[0]; + if (!fgs) return null; + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Paper, { + className: classes.tableRoot, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Table, { + className: classes.table, + size: "small", + children: [(0, _ir_comps.IrTableHeader)(classes), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableBody, { + children: fgs.sort((a, b) => b.confidence - a.confidence).map((fg, idx) => (0, _ir_comps.IrTableBodyRow)(classes, idx, fg)) + })] + }) + }); +}; +const IrViewer = ({ + // eslint-disable-line + classes, + molecule, + inputCb, + forecastSt +}) => /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.root, 'card-forecast-viewer'), + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Grid, { + className: (0, _classnames.default)(classes.container), + container: true, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, { + item: true, + xs: 4, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Paper, { + className: classes.svgRoot, + children: (0, _comps.sectionSvg)(classes, forecastSt.predictions) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, { + item: true, + xs: 8, + children: sectionTable(classes, forecastSt.predictions) + })] + }), (0, _comps.sectionInput)(classes, molecule, inputCb)] +}); +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + forecastSt: state.forecast +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({}, dispatch); +IrViewer.propTypes = { + classes: _propTypes.default.object.isRequired, + molecule: _propTypes.default.string.isRequired, + inputCb: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.bool]), + forecastSt: _propTypes.default.object.isRequired +}; +IrViewer.defaultProps = { + inputCb: false +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _styles.withStyles)(Styles))(IrViewer); \ No newline at end of file diff --git a/dist/components/forecast/nmr_comps.js b/dist/components/forecast/nmr_comps.js new file mode 100644 index 00000000..3ce7da6d --- /dev/null +++ b/dist/components/forecast/nmr_comps.js @@ -0,0 +1,156 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.SectionReference = exports.NmrTableHeader = exports.NmrTableBodyRow = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _CheckCircleOutline = _interopRequireDefault(require("@mui/icons-material/CheckCircleOutline")); +var _HighlightOff = _interopRequireDefault(require("@mui/icons-material/HighlightOff")); +var _comps = require("./comps"); +var _forecast = require("../../actions/forecast"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable react/function-component-definition, react/destructuring-assignment */ + +const baseSelectNmrStatus = ({ + // eslint-disable-line + idx, + atom, + status, + identity, + setNmrStatusAct +}) => { + const theStatus = ['accept', 'reject'].includes(status) ? status : ''; + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormControl, { + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Select, { + value: theStatus, + onChange: e => { + setNmrStatusAct({ + predictions: { + idx, + atom, + identity, + value: e.target.value + }, + svgs: [] + }); + }, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: "accept", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_CheckCircleOutline.default, { + style: { + color: '#4caf50' + } + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: "reject", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_HighlightOff.default, { + style: { + color: '#e91e63' + } + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.MenuItem, { + value: "", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {}) + })] + }) + }); +}; +const bssMapStateToProps = (state, props) => ( +// eslint-disable-line +{}); +const bssMapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setNmrStatusAct: _forecast.setNmrStatus +}, dispatch); +baseSelectNmrStatus.propTypes = { + idx: _propTypes.default.number.isRequired, + atom: _propTypes.default.number.isRequired, + status: _propTypes.default.string, + identity: _propTypes.default.string.isRequired, + setNmrStatusAct: _propTypes.default.func.isRequired +}; +baseSelectNmrStatus.defaultProps = { + status: '' +}; +const SelectNmrStatus = (0, _reactRedux.connect)( +// eslint-disable-line +bssMapStateToProps, bssMapDispatchToProps)(baseSelectNmrStatus); // eslint-disable-line + +const numFormat = input => parseFloat(input).toFixed(2); +const realFormat = (val, status) => { + if (status === 'missing') { + return '- - -'; + } + return numFormat(val); +}; +const NmrTableHeader = classes => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableHead, { + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableRow, { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + children: (0, _comps.TxtLabel)(classes, 'Atom', 'txt-prd-table-title') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.TxtLabel)(classes, 'Prediction (ppm)', 'txt-prd-table-title') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.TxtLabel)(classes, 'Real (ppm)', 'txt-prd-table-title') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.TxtLabel)(classes, 'Diff (ppm)', 'txt-prd-table-title') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.TxtLabel)(classes, 'Machine', 'txt-prd-table-title') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.TxtLabel)(classes, 'Owner', 'txt-prd-table-title') + })] + }) +}); +exports.NmrTableHeader = NmrTableHeader; +const NmrTableBodyRow = (classes, row, idx) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableRow, { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + component: "th", + scope: "row", + children: (0, _comps.TxtLabel)(classes, row.atom, 'txt-prd-table-content') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.TxtLabel)(classes, numFormat(row.prediction), 'txt-prd-table-content') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.TxtLabel)(classes, realFormat(row.real, row.status), 'txt-prd-table-content') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.TxtLabel)(classes, realFormat(row.diff, row.status), 'txt-prd-table-content') + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: (0, _comps.StatusIcon)(row.status) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(SelectNmrStatus, { + idx: idx, + atom: row.atom, + status: row.statusOwner, + identity: "Owner" + }) + })] +}, `${idx}-${row.atom}`); +exports.NmrTableBodyRow = NmrTableBodyRow; +const SectionReference = classes => /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: (0, _classnames.default)(classes.reference), + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("p", { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + children: "NMR prediction source: " + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("a", { + href: "https://www.ncbi.nlm.nih.gov/pubmed/15464159", + target: "_blank", + rel: "noopener noreferrer", + children: "nmrshiftdb" + })] + }) +}); +exports.SectionReference = SectionReference; \ No newline at end of file diff --git a/dist/components/forecast/nmr_viewer.js b/dist/components/forecast/nmr_viewer.js new file mode 100644 index 00000000..c397d56b --- /dev/null +++ b/dist/components/forecast/nmr_viewer.js @@ -0,0 +1,114 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _styles = require("@mui/styles"); +var _material = require("@mui/material"); +var _comps = require("./comps"); +var _nmr_comps = require("./nmr_comps"); +var _jsxRuntime = require("react/jsx-runtime"); +const Styles = () => ({ + root: { + overflowX: 'hidden', + overflowY: 'auto' + }, + container: { + minHeight: '400px' + }, + svgRoot: { + margin: '10px 40px 0px 40px', + height: 'calc(70vh)', + overflowY: 'hidden' + }, + tableRoot: { + margin: '10px 40px 0px 40px', + maxHeight: 'calc(70vh)', + overflowY: 'scroll' + }, + title: { + textAlign: 'left' + }, + btn: { + marginLeft: 40 + }, + reference: { + borderTop: '1px solid #cfd8dc', + margin: '10px 40px 0px 40px', + padding: 5 + }, + inputRoot: { + margin: '10px 40px 0px 40px' + }, + txtLabel: { + fontSize: '12px' + }, + submit: { + margin: '0 0 0 30px', + width: 300 + } +}); +const sectionTable = (classes, pds) => { + const renderMsg = (0, _comps.notToRenderAnalysis)(pds); + if (renderMsg) return renderMsg; + const dict = pds.output.result[0]; + if (!dict) return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {}); + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Paper, { + className: classes.tableRoot, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Table, { + className: classes.table, + size: "small", + children: [(0, _nmr_comps.NmrTableHeader)(classes), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableBody, { + children: dict.shifts.sort((a, b) => a.atom - b.atom).map((row, idx) => (0, _nmr_comps.NmrTableBodyRow)(classes, row, idx)) + })] + }) + }); +}; +const NmrViewer = ({ + // eslint-disable-line + classes, + molecule, + inputCb, + forecastSt +}) => /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.root, 'card-forecast-viewer'), + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Grid, { + className: (0, _classnames.default)(classes.container), + container: true, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, { + item: true, + xs: 4, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Paper, { + className: classes.svgRoot, + children: (0, _comps.sectionSvg)(classes, forecastSt.predictions) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, { + item: true, + xs: 8, + children: sectionTable(classes, forecastSt.predictions) + })] + }), (0, _comps.sectionInput)(classes, molecule, inputCb), (0, _nmr_comps.SectionReference)(classes)] +}); +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + forecastSt: state.forecast +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({}, dispatch); +NmrViewer.propTypes = { + classes: _propTypes.default.object.isRequired, + molecule: _propTypes.default.string.isRequired, + inputCb: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.bool]), + forecastSt: _propTypes.default.object.isRequired +}; +NmrViewer.defaultProps = { + inputCb: false +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _styles.withStyles)(Styles))(NmrViewer); \ No newline at end of file diff --git a/dist/components/forecast/section_loading.js b/dist/components/forecast/section_loading.js new file mode 100644 index 00000000..62d38f8d --- /dev/null +++ b/dist/components/forecast/section_loading.js @@ -0,0 +1,62 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _CircularProgress = _interopRequireDefault(require("@mui/material/CircularProgress")); +var _ErrorOutline = _interopRequireDefault(require("@mui/icons-material/ErrorOutline")); +var _jsxRuntime = require("react/jsx-runtime"); +const styleLoading = { + alignItems: 'center', + display: 'flex', + height: '100%', + justifyContent: 'center' +}; +class SectionLoading extends _react.default.Component { + constructor(props) { + super(props); + this.state = { + loading: true + }; + } + componentDidMount() { + setTimeout(() => this.setState({ + loading: false + }), 5000); + } + renderLoading() { + return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + style: styleLoading, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_CircularProgress.default, { + style: { + color: 'blue', + fontSize: 50 + } + }) + }); + } + renderNotFound() { + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + style: styleLoading, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_ErrorOutline.default, { + style: { + color: '#ffc107', + fontSize: 50, + margin: 20 + } + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("h3", { + children: "Structure Not Found" + })] + }); + } + render() { + const { + loading + } = this.state; + return loading ? this.renderLoading() : this.renderNotFound(); + } +} +var _default = exports.default = SectionLoading; \ No newline at end of file diff --git a/dist/components/forecast_viewer.js b/dist/components/forecast_viewer.js new file mode 100644 index 00000000..cfce89e4 --- /dev/null +++ b/dist/components/forecast_viewer.js @@ -0,0 +1,149 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _styles = require("@mui/styles"); +var _index = _interopRequireDefault(require("./d3_line/index")); +var _nmr_viewer = _interopRequireDefault(require("./forecast/nmr_viewer")); +var _ir_viewer = _interopRequireDefault(require("./forecast/ir_viewer")); +var _forecast = require("../actions/forecast"); +var _ui = require("../actions/ui"); +var _list_ui = require("../constants/list_ui"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable react/no-unused-prop-types */ + +const styles = () => ({ + root: { + flexGrow: 1 + }, + appBar: { + backgroundColor: '#fff', + boxShadow: 'none' + }, + tabLabel: { + fontSize: '14px' + } +}); +class ForecastViewer extends _react.default.Component { + constructor(props) { + super(props); + this.initForecastReducer = this.initForecastReducer.bind(this); + } + componentDidMount() { + this.initForecastReducer(); + } + componentDidUpdate(prevProps) { + const { + forecast + } = this.props; + const prevForecast = forecast; + const nextForecast = prevProps.forecast; + if (prevForecast !== nextForecast) { + this.initForecastReducer(); + } + } + initForecastReducer() { + const { + forecast, + initForecastStatusAct, + setUiViewerTypeAct + } = this.props; + initForecastStatusAct(forecast); + if (forecast && forecast.predictions) { + const { + running, + refreshed + } = forecast.predictions; + if (running || refreshed) setUiViewerTypeAct(_list_ui.LIST_UI_VIEWER_TYPE.ANALYSIS); + } + } + render() { + const { + classes, + topic, + feature, + cLabel, + xLabel, + yLabel, + forecast, + isNmr, + isIr, + uiSt, + isXRD, + wavelength, + curveSt, + jcampSt + } = this.props; + const { + viewer + } = uiSt; + const { + inputCb, + molecule + } = forecast; + const { + curveIdx + } = curveSt; + const { + jcamps + } = jcampSt; + const comparisons = jcamps[curveIdx].others; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: classes.root, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index.default, { + topic: topic, + feature: feature, + cLabel: cLabel, + xLabel: isXRD && wavelength ? `${xLabel}, WL=${wavelength.value} ${wavelength.unit}` : xLabel, + yLabel: yLabel, + comparisons: comparisons, + isHidden: viewer !== _list_ui.LIST_UI_VIEWER_TYPE.SPECTRUM + }), viewer === _list_ui.LIST_UI_VIEWER_TYPE.ANALYSIS && isNmr && /*#__PURE__*/(0, _jsxRuntime.jsx)(_nmr_viewer.default, { + molecule: molecule, + inputCb: inputCb + }), viewer === _list_ui.LIST_UI_VIEWER_TYPE.ANALYSIS && isIr && /*#__PURE__*/(0, _jsxRuntime.jsx)(_ir_viewer.default, { + molecule: molecule, + inputCb: inputCb + })] + }); + } +} +const mapStateToProps = (state, _) => ( +// eslint-disable-line +{ + uiSt: state.ui, + jcampSt: state.jcamp, + wavelength: state.wavelength, + curveSt: state.curve +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + initForecastStatusAct: _forecast.initForecastStatus, + setUiViewerTypeAct: _ui.setUiViewerType +}, dispatch); +ForecastViewer.propTypes = { + classes: _propTypes.default.object.isRequired, + topic: _propTypes.default.object.isRequired, + feature: _propTypes.default.object.isRequired, + cLabel: _propTypes.default.string.isRequired, + xLabel: _propTypes.default.string.isRequired, + yLabel: _propTypes.default.string.isRequired, + forecast: _propTypes.default.object.isRequired, + isNmr: _propTypes.default.bool.isRequired, + isIr: _propTypes.default.bool.isRequired, + isUvvis: _propTypes.default.bool.isRequired, + isXRD: _propTypes.default.bool.isRequired, + uiSt: _propTypes.default.object.isRequired, + jcampSt: _propTypes.default.object.isRequired, + initForecastStatusAct: _propTypes.default.func.isRequired, + setUiViewerTypeAct: _propTypes.default.func.isRequired, + wavelength: _propTypes.default.object.isRequired, + curveSt: _propTypes.default.object.isRequired +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _styles.withStyles)(styles))(ForecastViewer); \ No newline at end of file diff --git a/dist/components/multi_jcamps_viewer.js b/dist/components/multi_jcamps_viewer.js new file mode 100644 index 00000000..7f0dca3e --- /dev/null +++ b/dist/components/multi_jcamps_viewer.js @@ -0,0 +1,223 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _Grid = _interopRequireDefault(require("@mui/material/Grid")); +var _styles = require("@mui/styles"); +var _index = _interopRequireDefault(require("./panel/index")); +var _cyclic_voltamery_data = _interopRequireDefault(require("./panel/cyclic_voltamery_data")); +var _index2 = _interopRequireDefault(require("./cmd_bar/index")); +var _index3 = _interopRequireDefault(require("./d3_multi/index")); +var _curve = require("../actions/curve"); +var _cyclic_voltammetry = require("../actions/cyclic_voltammetry"); +var _list_layout = require("../constants/list_layout"); +var _format = _interopRequireDefault(require("../helpers/format")); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable react/default-props-match-prop-types, +react/require-default-props, react/no-unused-prop-types, react/jsx-boolean-value, +prefer-object-spread */ + +const styles = () => ({ + root: { + flexGrow: 1 + }, + appBar: { + backgroundColor: '#fff', + boxShadow: 'none' + }, + tabLabel: { + fontSize: '14px' + }, + cvEditor: { + height: 'calc(90vh - 220px)', + display: 'flex', + flexDirection: 'column', + minHeight: 0, + overflow: 'hidden' + }, + cvTopRow: { + flex: '1 1 auto', + minHeight: 0, + overflow: 'hidden' + }, + cvViewerCol: { + height: '100%', + minHeight: 0, + display: 'flex', + flexDirection: 'column' + }, + cvViewerWrap: { + flex: '1 1 auto', + minHeight: 0 + }, + cvPanelBelow: { + marginTop: 16, + width: '100%' + } +}); +const seperatingSubLayout = (entities, featureCondition, layoutSt) => { + if (layoutSt === _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY) { + return null; + } + const storedDict = {}; + entities.forEach(entity => { + const { + feature + } = entity; + const keyValue = feature[featureCondition]; + if (keyValue in storedDict) { + storedDict[keyValue].push(entity); + } else { + storedDict[keyValue] = [entity]; + } + }); + return Object.assign({}, storedDict); +}; +class MultiJcampsViewer extends _react.default.Component { + // eslint-disable-line + render() { + const { + classes, + curveSt, + operations, + entityFileNames, + entities, + userManualLink, + molSvg, + exactMass, + layoutSt, + integrationSt, + descriptions, + canChangeDescription, + onDescriptionChanged + } = this.props; + if (!entities || entities.length === 0) return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {}); + const separateCondition = _format.default.isGCLayout(layoutSt) ? 'yUnit' : 'xUnit'; + const seperatedSubLayouts = seperatingSubLayout(entities, separateCondition, layoutSt); + const { + curveIdx + } = curveSt; + const entity = entities[curveIdx]; + const { + feature, + topic + } = entity; + const { + integrations + } = integrationSt; + const currentIntegration = integrations[curveIdx]; + const isCyclicVolta = _format.default.isCyclicVoltaLayout(layoutSt); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: classes.root, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index2.default, { + feature: feature, + operations: operations, + editorOnly: true, + hideThreshold: !_format.default.isNmrLayout(layoutSt) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: (0, _classnames.default)('react-spectrum-editor', isCyclicVolta && classes.cvEditor), + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Grid.default, { + container: true, + className: isCyclicVolta ? classes.cvTopRow : undefined, + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_Grid.default, { + item: true, + xs: 9, + className: isCyclicVolta ? classes.cvViewerCol : undefined, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: isCyclicVolta ? classes.cvViewerWrap : undefined, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_index3.default, { + entities: entities, + topic: topic, + xLabel: feature.xUnit, + yLabel: feature.yUnit, + feature: feature + }) + }), isCyclicVolta ? /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: classes.cvPanelBelow, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_cyclic_voltamery_data.default, { + jcampIdx: curveIdx, + feature: feature, + userManualLink: userManualLink ? userManualLink.cv : undefined + }) + }) : null] + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Grid.default, { + item: true, + xs: 3, + align: "center", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_index.default, { + jcampIdx: curveIdx, + entityFileNames: entityFileNames, + userManualLink: userManualLink, + feature: feature, + molSvg: molSvg, + exactMass: exactMass, + subLayoutsInfo: seperatedSubLayouts, + integration: currentIntegration, + descriptions: descriptions, + canChangeDescription: canChangeDescription, + onDescriptionChanged: onDescriptionChanged, + hideCyclicVolta: isCyclicVolta + }) + })] + }) + })] + }); + } +} +const mapStateToProps = (state, _) => ( +// eslint-disable-line +{ + curveSt: state.curve, + cyclicVoltaSt: state.cyclicvolta, + entities: state.curve.listCurves, + layoutSt: state.layout, + integrationSt: state.integration.present +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + setAllCurvesAct: _curve.setAllCurves, + addNewCylicVoltaPairPeakAct: _cyclic_voltammetry.addNewCylicVoltaPairPeak, + addCylicVoltaMaxPeakAct: _cyclic_voltammetry.addCylicVoltaMaxPeak, + addCylicVoltaMinPeakAct: _cyclic_voltammetry.addCylicVoltaMinPeak, + addCylicVoltaPeckerAct: _cyclic_voltammetry.addCylicVoltaPecker +}, dispatch); +MultiJcampsViewer.propTypes = { + classes: _propTypes.default.object.isRequired, + multiEntities: _propTypes.default.array.isRequired, + entityFileNames: _propTypes.default.array.isRequired, + molSvg: _propTypes.default.string.isRequired, + setAllCurvesAct: _propTypes.default.func.isRequired, + curveSt: _propTypes.default.object.isRequired, + cyclicVoltaSt: _propTypes.default.object.isRequired, + addNewCylicVoltaPairPeakAct: _propTypes.default.func.isRequired, + addCylicVoltaMaxPeakAct: _propTypes.default.func.isRequired, + addCylicVoltaMinPeakAct: _propTypes.default.func.isRequired, + operations: _propTypes.default.func.isRequired, + userManualLink: _propTypes.default.object, + entities: _propTypes.default.array, + layoutSt: _propTypes.default.string.isRequired, + exactMass: _propTypes.default.string, + integrationSt: _propTypes.default.object.isRequired, + descriptions: _propTypes.default.array.isRequired, + canChangeDescription: _propTypes.default.bool.isRequired, + onDescriptionChanged: _propTypes.default.func +}; +MultiJcampsViewer.defaultProps = { + multiEntities: [], + entityFileNames: [], + molSvg: '', + cLabel: '', + xLabel: '', + yLabel: '', + entities: [], + descriptions: [], + canChangeDescription: false +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps), (0, _styles.withStyles)(styles))(MultiJcampsViewer); \ No newline at end of file diff --git a/dist/components/panel/compare.js b/dist/components/panel/compare.js new file mode 100644 index 00000000..391903d6 --- /dev/null +++ b/dist/components/panel/compare.js @@ -0,0 +1,272 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _reactDropzone = _interopRequireDefault(require("react-dropzone")); +var _material = require("@mui/material"); +var _ExpandMore = _interopRequireDefault(require("@mui/icons-material/ExpandMore")); +var _HighlightOff = _interopRequireDefault(require("@mui/icons-material/HighlightOff")); +var _VisibilityOutlined = _interopRequireDefault(require("@mui/icons-material/VisibilityOutlined")); +var _VisibilityOffOutlined = _interopRequireDefault(require("@mui/icons-material/VisibilityOffOutlined")); +var _styles = require("@mui/styles"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _jcamp = require("../../actions/jcamp"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable function-paren-newline, react/jsx-props-no-spreading, +react/function-component-definition */ + +const styles = theme => ({ + panel: { + backgroundColor: '#eee', + display: 'table-row' + }, + panelSummary: { + backgroundColor: '#eee', + height: 32 + }, + txtBadge: {}, + panelDetail: { + backgroundColor: '#fff', + maxHeight: 'calc(90vh - 220px)', + // ROI + overflow: 'auto' + }, + table: { + width: '100%' + }, + tTxt: { + padding: 0 + }, + tTxtHide: { + color: '#D5D8DC' + }, + tRow: { + height: 28, + '&:nth-of-type(even)': { + backgroundColor: theme.palette ? theme.palette.background.default : '#d3d3d3' + } + }, + rmBtn: { + color: 'red', + padding: '0 5px 0 5px', + '&:hover': { + borderRadius: 12, + backgroundColor: 'red', + color: 'white' + } + }, + showBtn: { + color: 'steelblue', + padding: '0 5px 0 5px', + '&:hover': { + borderRadius: 12, + backgroundColor: 'steelblue', + color: 'white' + } + }, + hideBtn: { + color: 'gray', + padding: '0 5px 0 5px', + '&:hover': { + borderRadius: 12, + backgroundColor: 'gray', + color: 'white' + } + }, + square: { + textAlign: 'center !important' + }, + baseDD: { + backgroundColor: 'white', + border: '1px dashed black', + borderRadius: 5, + height: 26, + lineHeight: '26px', + margin: '7px 0 7px 0', + textAlign: 'center', + verticalAlign: 'middle', + width: '90%' + }, + enableDD: { + border: '2px dashed #000', + color: '#000' + }, + disableDD: { + border: '2px dashed #aaa', + color: '#aaa' + }, + tpCard: {}, + tpMoreTxt: { + padding: '0 0 0 60px' + }, + tpLabel: { + fontSize: 16 + } +}); +const msgDefault = 'Add spectra to compare.'; +const tpHint = classes => /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: (0, _classnames.default)(classes.tpCard), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("p", { + className: (0, _classnames.default)(classes.tpLabel, 'txt-sv-tp'), + children: "- Accept *.dx, *.jdx, *.JCAMP," + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("p", { + className: (0, _classnames.default)(classes.tpLabel, 'txt-sv-tp'), + children: "- Max 5 spectra." + })] +}); +const content = (classes, desc) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: tpHint(classes), + placement: "bottom", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tpLabel, 'txt-sv-tp'), + children: desc + }) +}); +const inputOthers = (classes, jcampSt) => { + const { + selectedIdx, + jcamps + } = jcampSt; + const selectedJcamp = jcamps[selectedIdx]; + const { + addOthersCb + } = selectedJcamp; + const fileName = ''; + const desc = fileName || msgDefault; + const onDrop = jcampFiles => { + if (!addOthersCb) return; + addOthersCb({ + jcamps: jcampFiles + }); + }; + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactDropzone.default, { + className: "dropbox", + onDrop: onDrop, + children: ({ + getRootProps, + getInputProps + }) => /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + ...getRootProps(), + className: (0, _classnames.default)(classes.baseDD), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("input", { + ...getInputProps() + }), content(classes, desc)] + }) + }); +}; +const compareList = (classes, jcampSt, rmOthersOneAct, toggleShowAct) => { + const { + selectedIdx, + jcamps + } = jcampSt; + const selectedJcamp = jcamps[selectedIdx]; + const { + others + } = selectedJcamp; + const rows = others.map((o, idx) => ({ + idx, + title: o.spectra[0].title, + color: _format.default.compareColors(idx), + rmCb: () => rmOthersOneAct(idx), + isShow: o.show, + toggleShowCb: () => toggleShowAct(idx) + })); + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Table, { + className: classes.table, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableBody, { + children: rows.map(row => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableRow, { + className: classes.tRow, + hover: true, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxt, classes.square, 'txt-sv-panel-txt'), + style: { + backgroundColor: row.color + }, + children: row.idx + 1 + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt', row.isShow ? null : classes.tTxtHide), + children: row.title + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: [row.isShow ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_VisibilityOutlined.default, { + onClick: row.toggleShowCb, + className: classes.showBtn + }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_VisibilityOffOutlined.default, { + onClick: row.toggleShowCb, + className: classes.hideBtn + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_HighlightOff.default, { + onClick: row.rmCb, + className: classes.rmBtn + })] + })] + }, row.idx)) + }) + }); +}; +const ComparePanel = ({ + classes, + expand, + onExapnd, + jcampSt, + rmOthersOneAct, + toggleShowAct +}) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Accordion, { + expanded: expand, + onChange: onExapnd, + disableGutters: true, + sx: { + '&.MuiAccordion-root.Mui-expanded': { + margin: 0 + }, + '&:before': { + display: 'none' + } + }, + TransitionProps: { + unmountOnExit: true + } // increase Accordion performance + , + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.AccordionSummary, { + expandIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ExpandMore.default, {}), + className: (0, _classnames.default)(classes.panelSummary), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, { + className: "txt-panel-header", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtBadge, 'txt-sv-panel-title'), + children: "Spectra Comparisons" + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Divider, {}), inputOthers(classes, jcampSt), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: (0, _classnames.default)(classes.panelDetail), + children: compareList(classes, jcampSt, rmOthersOneAct, toggleShowAct) + })] +}); +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + jcampSt: state.jcamp +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + rmOthersOneAct: _jcamp.rmOthersOne, + toggleShowAct: _jcamp.toggleShow +}, dispatch); +ComparePanel.propTypes = { + classes: _propTypes.default.object.isRequired, + expand: _propTypes.default.bool.isRequired, + onExapnd: _propTypes.default.func.isRequired, + jcampSt: _propTypes.default.object.isRequired, + rmOthersOneAct: _propTypes.default.func.isRequired, + toggleShowAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _styles.withStyles)(styles)(ComparePanel)); \ No newline at end of file diff --git a/dist/components/panel/cyclic_voltamery_data.js b/dist/components/panel/cyclic_voltamery_data.js new file mode 100644 index 00000000..9024bda0 --- /dev/null +++ b/dist/components/panel/cyclic_voltamery_data.js @@ -0,0 +1,467 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireWildcard(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _ExpandLess = _interopRequireDefault(require("@mui/icons-material/ExpandLess")); +var _ExpandMore = _interopRequireDefault(require("@mui/icons-material/ExpandMore")); +var _AddCircleOutline = _interopRequireDefault(require("@mui/icons-material/AddCircleOutline")); +var _RemoveCircle = _interopRequireDefault(require("@mui/icons-material/RemoveCircle")); +var _Info = _interopRequireDefault(require("@mui/icons-material/Info")); +var _Help = _interopRequireDefault(require("@mui/icons-material/Help")); +var _styles = require("@mui/styles"); +var _material = require("@mui/material"); +var _cyclic_voltammetry = require("../../actions/cyclic_voltammetry"); +var _ui = require("../../actions/ui"); +var _list_ui = require("../../constants/list_ui"); +var _chem = require("../../helpers/chem"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _jsxRuntime = require("react/jsx-runtime"); +function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } +/* eslint-disable function-paren-newline, react/require-default-props, +react/no-unused-prop-types, react/jsx-closing-tag-location, max-len, react/jsx-one-expression-per-line, +react/jsx-indent, react/no-unescaped-entities, react/jsx-wrap-multilines, camelcase, no-shadow, +arrow-body-style, react/function-component-definition */ + +const MAX_VISIBLE_ROWS = 6; +const ROW_HEIGHT_PX = 28; +const HEADER_HEIGHT_PX = 24; +const EXPANDED_HEIGHT = HEADER_HEIGHT_PX + MAX_VISIBLE_ROWS * ROW_HEIGHT_PX; +const styles = () => ({ + panel: { + backgroundColor: '#f7f7f7', + border: '1px solid #e6e6e6', + borderRadius: 8, + overflow: 'hidden' + }, + panelHeader: { + backgroundColor: '#eee', + padding: '4px 8px', + display: 'flex', + alignItems: 'center' + }, + panelActions: { + marginLeft: 'auto', + display: 'flex', + alignItems: 'center', + gap: 8 + }, + howToWrap: { + display: 'inline-flex', + alignItems: 'center', + gap: 4 + }, + toggleBtn: { + padding: 0 + }, + table: { + width: '100%', + overflowWrap: 'anywhere', + fontSize: '11px !important' + }, + tableWrap: { + padding: '3px 6px 5px', + height: 'auto', + maxHeight: EXPANDED_HEIGHT, + overflowY: 'auto', + overflowX: 'hidden', + transition: 'max-height 200ms ease' + }, + tableWrapCollapsed: { + maxHeight: 0, + padding: '0 5px', + overflow: 'hidden' + }, + tableHeadRow: { + backgroundColor: '#f5f5f5' + }, + tableHeadCell: { + fontWeight: 600, + color: '#333', + fontSize: '11px !important' + }, + tableBodyRow: { + '&:nth-of-type(even)': { + backgroundColor: '#fafafa' + } + }, + td: { + wordWrap: 'break-all', + fontSize: '14px !important' + }, + cellSelected: { + backgroundColor: '#2196f3', + color: '#fff', + fontSize: '13px !important' + }, + btnRemove: { + color: 'red' + }, + btnAddRow: { + color: 'green' + }, + tTxt: { + padding: '5px 2px', + lineHeight: 1.2, + verticalAlign: 'top', + fontSize: '11px !important' + }, + infoIcon: { + width: '15px', + height: '16px' + }, + txtToolTip: { + lineHeight: 'normal !important', + fontSize: '14px !important' + }, + rowRoot: { + border: '1px solid #e6e6e6', + borderRadius: 6, + minHeight: 40, + lineHeight: 1.4, + padding: '10px 16px', + textAlign: 'left' + }, + rowOdd: { + backgroundColor: '#fff', + textOverflow: 'clip', + whiteSpace: 'normal' + }, + rowEven: { + backgroundColor: '#fafafa', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap' + }, + cvModeWarning: { + color: 'red', + marginLeft: 12 + } +}); +const CyclicVoltammetryPanel = ({ + classes, + cyclicVotaSt, + feature, + addNewPairPeakAct, + setWorkWithMaxPeakAct, + selectPairPeakAct, + removePairPeakAct, + selectRefPeaksAct, + sweepTypeSt, + setUiSweepTypeAct, + jcampIdx, + userManualLink +}) => { + const [isExpanded, setIsExpanded] = (0, _react.useState)(false); + const { + spectraList + } = cyclicVotaSt; + const spectra = spectraList[jcampIdx]; + let list = []; + if (spectra !== undefined) { + list = spectra.list; + } + const formatCurrent = (y, feature, cyclicVotaSt) => { + const baseY = feature && feature.yUnit ? String(feature.yUnit) : 'A'; + const isMilli = /mA/i.test(baseY); + const useDensity = cyclicVotaSt && cyclicVotaSt.useCurrentDensity; + const rawArea = (cyclicVotaSt && cyclicVotaSt.areaValue === '' ? 1.0 : cyclicVotaSt?.areaValue) || 1.0; + const areaUnit = cyclicVotaSt && cyclicVotaSt.areaUnit ? cyclicVotaSt.areaUnit : 'cm²'; + const safeArea = rawArea > 0 ? rawArea : 1.0; + let val = y; + let unit = isMilli ? 'mA' : 'A'; + if (useDensity) { + val = y / safeArea; + unit = `${unit}/${areaUnit}`; + } + if (isMilli) { + val *= 1000.0; + } + return `${parseFloat(val).toExponential(2)} ${unit}`; + }; + const selectCell = (idx, isMax) => { + setWorkWithMaxPeakAct({ + isMax, + jcampIdx + }); + selectPairPeakAct({ + index: idx, + jcampIdx + }); + if (sweepTypeSt === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MAX_PEAK || sweepTypeSt === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MIN_PEAK) { + if (isMax) { + setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MAX_PEAK, jcampIdx); + } else { + setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MIN_PEAK, jcampIdx); + } + } else if (sweepTypeSt === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MAX_PEAK || sweepTypeSt === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MIN_PEAK) { + if (isMax) { + setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MAX_PEAK, jcampIdx); + } else { + setUiSweepTypeAct(_list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MIN_PEAK, jcampIdx); + } + } + }; + const changeCheckRefPeaks = (idx, event) => { + selectRefPeaksAct({ + index: idx, + jcampIdx, + checked: event.target.checked + }); + }; + const getDelta = data => { + return data.max && data.min ? _format.default.strNumberFixedLength((0, _chem.GetCyclicVoltaPeakSeparate)(data.max.x, data.min.x) * 1000, 3) : 'nd'; + }; + const getRatio = (feature, data) => { + const featureData = feature.data[0]; + const idx = featureData.x.indexOf(feature.maxX); + const y_pecker = data.pecker ? data.pecker.y : featureData.y[idx]; + return data.max && data.min ? _format.default.strNumberFixedLength((0, _chem.GetCyclicVoltaRatio)(data.max.y, data.min.y, y_pecker), 3) : 'nd'; + }; + const rows = list.map((o, idx) => ({ + idx, + max: o.max ? `E: ${_format.default.strNumberFixedLength(parseFloat(o.max.x), 3)} V,\nI: ${formatCurrent(o.max.y, feature, cyclicVotaSt)}` : 'nd', + min: o.min ? `E: ${_format.default.strNumberFixedLength(parseFloat(o.min.x), 3)} V,\nI: ${formatCurrent(o.min.y, feature, cyclicVotaSt)}` : 'nd', + pecker: o.pecker ? `${formatCurrent(o.pecker.y, feature, cyclicVotaSt)}` : 'nd', + delta: `${getDelta(o)} mV`, + ratio: getRatio(feature, o), + e12: typeof o.e12 === 'number' ? `${_format.default.strNumberFixedLength(o.e12, 3)} V` : 'nd', + isRef: o.isRef, + onClickMax: () => selectCell(idx, true), + onClickMin: () => selectCell(idx, false), + remove: () => removePairPeakAct({ + index: idx, + jcampIdx + }), + onCheckRefChanged: e => changeCheckRefPeaks(idx, e) + })); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: classes.panel, + "data-testid": "PanelVoltammetry", + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: classes.panelHeader, + role: "button", + tabIndex: 0, + onClick: () => setIsExpanded(prev => !prev), + onKeyDown: event => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + setIsExpanded(prev => !prev); + } + }, + "aria-expanded": isExpanded, + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Typography, { + className: "txt-panel-header", + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtBadge, 'txt-sv-panel-title'), + children: "Voltammetry data" + }), ' - ', /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + children: ["Mode: ", cyclicVotaSt.useCurrentDensity ? 'Current Density' : 'Current'] + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: classes.cvModeWarning, + children: "WE-ECSA must be set when switching to Current Density." + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: classes.panelActions, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtToolTip), + children: "Click here to open the User manual document" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { + className: classes.howToWrap, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("a", { + target: "_blank", + rel: "noopener noreferrer", + href: userManualLink, + children: "How-To" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Help.default, { + className: (0, _classnames.default)(classes.infoIcon) + })] + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.IconButton, { + size: "small", + className: classes.toggleBtn, + onClick: event => { + event.stopPropagation(); + setIsExpanded(prev => !prev); + }, + "aria-label": isExpanded ? 'Collapse table' : 'Expand table', + children: isExpanded ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_ExpandLess.default, {}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_ExpandMore.default, {}) + })] + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Divider, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: (0, _classnames.default)(classes.tableWrap, !isExpanded && classes.tableWrapCollapsed), + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Table, { + className: classes.table, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableHead, { + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableRow, { + className: classes.tableHeadRow, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.tableHeadCell, classes.square, 'txt-sv-panel-txt'), + children: "Ref" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.tableHeadCell, classes.square, 'txt-sv-panel-txt'), + children: "Anodic" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.tableHeadCell, classes.square, 'txt-sv-panel-txt'), + children: "Cathodic" + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.tableHeadCell, classes.square, 'txt-sv-panel-txt'), + children: ["I ", /*#__PURE__*/(0, _jsxRuntime.jsx)("sub", { + children: "\u03BB0" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsxs)("p", { + className: (0, _classnames.default)(classes.txtToolTip), + children: ["Baseline correction value for I ratio ", /*#__PURE__*/(0, _jsxRuntime.jsx)("br", {}), "(a.k.a y value of pecker)"] + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Info.default, { + className: (0, _classnames.default)(classes.infoIcon) + }) + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.tableHeadCell, classes.square, 'txt-sv-panel-txt'), + children: ["I ratio", /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.txtToolTip), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("p", { + children: "Nicholson's method" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("i", { + children: "NICHOLSON, Rl S. Semiempirical Procedure for Measuring with Stationary Electrode Polarography Rates of Chemical Reactions Involving the Product of Electron Transfer. Analytical Chemistry, 1966, 38. Jg., Nr. 10, S. 1406-1406. https://doi.org/10.1021/ac60242a030" + })] + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Info.default, { + className: (0, _classnames.default)(classes.infoIcon) + }) + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.tableHeadCell, classes.square, 'txt-sv-panel-txt'), + children: ["E", /*#__PURE__*/(0, _jsxRuntime.jsx)("sub", { + children: "1/2" + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.tableHeadCell, classes.square, 'txt-sv-panel-txt'), + children: ["\u0394E", /*#__PURE__*/(0, _jsxRuntime.jsx)("sub", { + children: "p" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtToolTip), + children: "| Epa - Epc |" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Info.default, { + className: (0, _classnames.default)(classes.infoIcon) + }) + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.tableHeadCell, classes.square, 'txt-sv-panel-txt'), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_AddCircleOutline.default, { + onClick: () => addNewPairPeakAct(jcampIdx), + className: (0, _classnames.default)(classes.btnAddRow) + }) + })] + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableBody, { + children: rows.map(row => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableRow, { + className: classes.tableBodyRow, + hover: true, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.square, 'txt-sv-panel-txt'), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Checkbox, { + size: "small", + checked: row.isRef, + onChange: row.onCheckRefChanged, + sx: { + padding: 0 + } + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.square, spectra.isWorkMaxPeak && spectra.selectedIdx === row.idx ? classes.cellSelected : 'txt-sv-panel-txt'), + onClick: row.onClickMax, + children: row.max.split('\n').map((s, index) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.default.Fragment, { + children: [s, /*#__PURE__*/(0, _jsxRuntime.jsx)("br", {})] + }, index)) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.square, !spectra.isWorkMaxPeak && spectra.selectedIdx === row.idx ? classes.cellSelected : 'txt-sv-panel-txt'), + onClick: row.onClickMin, + children: row.min.split('\n').map((s, index) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.default.Fragment, { + children: [s, /*#__PURE__*/(0, _jsxRuntime.jsx)("br", {})] + }, index)) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.square, 'txt-sv-panel-txt'), + children: row.pecker + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.square, 'txt-sv-panel-txt'), + children: row.ratio + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.square, 'txt-sv-panel-txt'), + children: row.e12 + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.square, 'txt-sv-panel-txt'), + children: row.delta + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "left", + className: (0, _classnames.default)(classes.tTxt, classes.square, 'txt-sv-panel-txt'), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_RemoveCircle.default, { + className: (0, _classnames.default)(classes.btnRemove), + onClick: row.remove + }) + })] + }, row.idx)) + })] + }) + })] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + cyclicVotaSt: state.cyclicvolta, + sweepTypeSt: state.ui.sweepType +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + addNewPairPeakAct: _cyclic_voltammetry.addNewCylicVoltaPairPeak, + setWorkWithMaxPeakAct: _cyclic_voltammetry.setWorkWithMaxPeak, + selectPairPeakAct: _cyclic_voltammetry.selectPairPeak, + removePairPeakAct: _cyclic_voltammetry.removeCylicVoltaPairPeak, + selectRefPeaksAct: _cyclic_voltammetry.selectRefPeaks, + setUiSweepTypeAct: _ui.setUiSweepType +}, dispatch); +CyclicVoltammetryPanel.propTypes = { + classes: _propTypes.default.object.isRequired, + feature: _propTypes.default.object.isRequired, + cyclicVotaSt: _propTypes.default.object.isRequired, + addNewPairPeakAct: _propTypes.default.func.isRequired, + setWorkWithMaxPeakAct: _propTypes.default.func.isRequired, + selectPairPeakAct: _propTypes.default.func.isRequired, + removePairPeakAct: _propTypes.default.func.isRequired, + selectRefPeaksAct: _propTypes.default.func.isRequired, + setUiSweepTypeAct: _propTypes.default.func.isRequired, + sweepTypeSt: _propTypes.default.string.isRequired, + userManualLink: _propTypes.default.string, + jcampIdx: _propTypes.default.number +}; +CyclicVoltammetryPanel.defaultProps = { + jcampIdx: 0 +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _styles.withStyles)(styles)(CyclicVoltammetryPanel)); \ No newline at end of file diff --git a/dist/components/panel/graph_selection.js b/dist/components/panel/graph_selection.js new file mode 100644 index 00000000..5ab8adf3 --- /dev/null +++ b/dist/components/panel/graph_selection.js @@ -0,0 +1,260 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireWildcard(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _ExpandMore = _interopRequireDefault(require("@mui/icons-material/ExpandMore")); +var _styles = require("@mui/styles"); +var _material = require("@mui/material"); +var _curve = require("../../actions/curve"); +var _list_layout = require("../../constants/list_layout"); +var _jsxRuntime = require("react/jsx-runtime"); +function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } +/* eslint-disable react/function-component-definition, function-paren-newline, +react/require-default-props, react/no-unused-prop-types */ + +const styles = () => ({ + panelSummary: { + backgroundColor: '#eee', + height: 22 + }, + curve: { + width: '100%' + }, + line: { + height: '2px', + borderWidth: '0', + margin: '0' + }, + 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%', + overflowWrap: 'anywhere' + } +}); +const GraphSelectionPanel = ({ + classes, + curveSt, + entityFileNames, + subLayoutsInfo, + layoutSt, + selectCurveAct, + toggleShowAllCurveAct +}) => { + let subLayoutValues = []; + if (subLayoutsInfo) { + subLayoutValues = Object.keys(subLayoutsInfo); + } + const [selectedSubLayout, setSelectedSublayout] = (0, _react.useState)(subLayoutValues[0]); + (0, _react.useEffect)(() => { + setSelectedSublayout(subLayoutValues[0]); + }, subLayoutValues); + if (!curveSt) { + return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {}); + } + const { + curveIdx, + listCurves, + isShowAllCurve + } = curveSt; + if (!listCurves) { + return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {}); + } + const onChange = idx => { + selectCurveAct(idx); + }; + const onChangeTabSubLayout = (event, newValue) => { + setSelectedSublayout(newValue); + }; + const onChangeSwitch = event => { + toggleShowAllCurveAct(event.target.checked); + }; + let itemsSubLayout = []; + if (selectedSubLayout && subLayoutValues.length > 1) { + const subLayout = subLayoutsInfo[selectedSubLayout]; + try { + itemsSubLayout = 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 + }; + }); + } 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 + }; + }); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Accordion, { + "data-testid": "GraphSelectionPanel", + disableGutters: true, + sx: { + '&.MuiAccordion-root.Mui-expanded': { + margin: 0 + }, + '&:before': { + display: 'none' + } + }, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.AccordionSummary, { + expandIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ExpandMore.default, {}), + className: (0, _classnames.default)(classes.panelSummary), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, { + className: "txt-panel-header", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtBadge, 'txt-sv-panel-title'), + children: "Graph selection" + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Divider, {}), layoutSt === _list_layout.LIST_LAYOUT.AIF ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormControlLabel, { + control: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Switch, { + checked: isShowAllCurve, + onChange: onChangeSwitch + }), + label: "Show all curves" + }) : null, subLayoutValues && subLayoutValues.length > 1 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tabs, { + value: selectedSubLayout, + onChange: onChangeTabSubLayout, + children: subLayoutValues.map((subLayout, i) => { + let subLayoutName = ''; + switch (subLayout.toUpperCase()) { + case 'G/MOL': + subLayoutName = 'MWD'; + break; + case 'MILLILITERS': + subLayoutName = 'ELU'; + break; + case 'INTENSITY': + subLayoutName = 'Chromatogram'; + break; + case 'DEGREES CELSIUS': + subLayoutName = 'Temperature'; + break; + default: + break; + } + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tab, { + value: subLayout, + label: subLayoutName + }, i); + }) + }), /*#__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)) + })] + }) : /*#__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)) + })] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + layoutSt: state.layout, + curveSt: state.curve +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + selectCurveAct: _curve.selectCurve, + toggleShowAllCurveAct: _curve.toggleShowAllCurves +}, dispatch); +GraphSelectionPanel.propTypes = { + classes: _propTypes.default.object.isRequired, + expand: _propTypes.default.bool.isRequired, + layoutSt: _propTypes.default.string.isRequired, + onExapnd: _propTypes.default.func.isRequired, + curveSt: _propTypes.default.object.isRequired, + selectCurveAct: _propTypes.default.func.isRequired, + entityFileNames: _propTypes.default.array.isRequired, + subLayoutsInfo: _propTypes.default.array, + toggleShowAllCurveAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _styles.withStyles)(styles)(GraphSelectionPanel)); \ No newline at end of file diff --git a/dist/components/panel/index.js b/dist/components/panel/index.js new file mode 100644 index 00000000..66cbcca9 --- /dev/null +++ b/dist/components/panel/index.js @@ -0,0 +1,172 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _styles = require("@mui/material/styles"); +var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles")); +var _info = _interopRequireDefault(require("./info")); +var _peaks = _interopRequireDefault(require("./peaks")); +var _compare = _interopRequireDefault(require("./compare")); +var _multiplicity = _interopRequireDefault(require("./multiplicity")); +var _cyclic_voltamery_data = _interopRequireDefault(require("./cyclic_voltamery_data")); +var _graph_selection = _interopRequireDefault(require("./graph_selection")); +var _cfg = _interopRequireDefault(require("../../helpers/cfg")); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable react/prop-types, react/require-default-props */ + +const theme = (0, _styles.createTheme)({ + typography: { + useNextVariants: true + }, + palette: { + background: { + default: '#D3D3D3' + } + } +}); +const styles = () => ({ + panels: { + maxHeight: 'calc(90vh - 220px)', + // ROI + display: 'table', + overflowX: 'hidden', + overflowY: 'auto', + margin: '5px 0 0 0', + padding: '0 0 0 0', + width: '100%' + } +}); +class PanelViewer extends _react.default.Component { + constructor(props) { + super(props); + this.state = { + expand: 'info' + }; + this.onExapnd = this.onExapnd.bind(this); + this.handleDescriptionChanged = this.handleDescriptionChanged.bind(this); + } + handleDescriptionChanged(content, delta, source, editor) { + if (source === 'user') { + const contentChanged = editor.getContents(); + this.props.onDescriptionChanged(contentChanged); // eslint-disable-line + } + } + onExapnd(input) { + const { + expand + } = this.state; + const nextExpand = input === expand ? '' : input; + this.setState({ + expand: nextExpand + }); + } + render() { + const { + expand + } = this.state; + const { + classes, + feature, + integration, + editorOnly, + molSvg, + descriptions, + layoutSt, + canChangeDescription, + jcampIdx, + entityFileNames, + curveSt, + userManualLink, + subLayoutsInfo, + exactMass, + hideCyclicVolta + } = this.props; + const onExapndInfo = () => this.onExapnd('info'); + const onExapndPeak = () => this.onExapnd('peak'); + const onExapndMpy = () => this.onExapnd('mpy'); + const onExapndCompare = () => this.onExapnd('compare'); + const onExapndCyclicVolta = () => this.onExapnd('cyclicvolta'); + const onExapndGraphSelection = () => this.onExapnd('graph'); + const { + listCurves + } = curveSt; + const hideGraphSelection = listCurves === false || listCurves === undefined; + return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: (0, _classnames.default)(classes.panels), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.StyledEngineProvider, { + injectFirst: true, + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.ThemeProvider, { + theme: theme, + children: [hideGraphSelection ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_graph_selection.default, { + jcampIdx: jcampIdx, + entityFileNames: entityFileNames, + expand: expand === 'graph', + onExapnd: onExapndGraphSelection, + subLayoutsInfo: subLayoutsInfo + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_info.default, { + feature: feature, + integration: integration, + editorOnly: editorOnly, + expand: expand === 'info', + molSvg: molSvg, + exactMass: exactMass, + onExapnd: onExapndInfo, + descriptions: descriptions, + canChangeDescription: canChangeDescription, + onDescriptionChanged: this.handleDescriptionChanged + }), _cfg.default.hidePanelPeak(layoutSt) ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_peaks.default, { + expand: expand === 'peak', + onExapnd: onExapndPeak + }), _cfg.default.hidePanelMpy(layoutSt) ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_multiplicity.default, { + expand: expand === 'mpy', + onExapnd: onExapndMpy + }), _cfg.default.hidePanelCompare(layoutSt) || listCurves.length > 1 ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_compare.default, { + expand: expand === 'compare', + onExapnd: onExapndCompare + }), _cfg.default.hidePanelCyclicVolta(layoutSt) || hideCyclicVolta ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_cyclic_voltamery_data.default, { + jcampIdx: jcampIdx, + feature: feature, + expand: expand === 'cyclicvolta', + onExapnd: onExapndCyclicVolta, + userManualLink: userManualLink ? userManualLink.cv : undefined + })] + }) + }) + }); + } +} +const mapStateToProps = (state, _) => ( +// eslint-disable-line +{ + layoutSt: state.layout, + curveSt: state.curve +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({}, dispatch); +PanelViewer.propTypes = { + classes: _propTypes.default.object.isRequired, + feature: _propTypes.default.object.isRequired, + integration: _propTypes.default.object.isRequired, + editorOnly: _propTypes.default.bool.isRequired, + molSvg: _propTypes.default.string.isRequired, + descriptions: _propTypes.default.array.isRequired, + layoutSt: _propTypes.default.string.isRequired, + canChangeDescription: _propTypes.default.bool.isRequired, + onDescriptionChanged: _propTypes.default.func, + entityFileNames: _propTypes.default.array, + userManualLink: _propTypes.default.object, + curveSt: _propTypes.default.object.isRequired, + subLayoutsInfo: _propTypes.default.object, + exactMass: _propTypes.default.string, + hideCyclicVolta: _propTypes.default.bool +}; +var _default = exports.default = (0, _reactRedux.connect)( +// eslint-disable-line +mapStateToProps, mapDispatchToProps)((0, _withStyles.default)(styles)(PanelViewer)); // eslint-disable-line \ No newline at end of file diff --git a/dist/components/panel/info.js b/dist/components/panel/info.js new file mode 100644 index 00000000..72928355 --- /dev/null +++ b/dist/components/panel/info.js @@ -0,0 +1,521 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _reactSvgFileZoomPan = _interopRequireDefault(require("@complat/react-svg-file-zoom-pan")); +var _reactQuill = _interopRequireDefault(require("react-quill")); +var _material = require("@mui/material"); +var _ExpandMore = _interopRequireDefault(require("@mui/icons-material/ExpandMore")); +var _styles = require("@mui/styles"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _meta = require("../../actions/meta"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable no-mixed-operators, react/function-component-definition, +react/require-default-props, max-len */ + +const styles = () => ({ + chip: { + margin: '1px 0 1px 0' + }, + panel: { + backgroundColor: '#eee', + display: 'table-row' + }, + panelSummary: { + backgroundColor: '#eee', + height: 32 + }, + subSectionHeader: { + backgroundColor: '#eee', + height: 32, + lineHeight: '32px', + paddingLeft: 10, + textAlign: 'left', + fontWeight: 'bold', + fontSize: '0.8rem', + fontFamily: 'Helvetica', + borderTop: '1px solid #dcdcdc', + color: 'rgba(0, 0, 0, 0.87)' + }, + panelDetail: { + backgroundColor: '#fff', + maxHeight: 'calc(90vh - 220px)', + // ROI + overflow: 'auto' + }, + table: { + width: 'auto' + }, + rowRoot: { + border: '1px solid #eee', + height: 36, + lineHeight: '36px', + overflow: 'hidden', + paddingLeft: 24, + textAlign: 'left' + }, + rowOdd: { + backgroundColor: '#fff', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap' + }, + rowEven: { + backgroundColor: '#fafafa', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap' + }, + rowOddSim: { + backgroundColor: '#fff', + height: 108, + lineHeight: '24px', + overflowY: 'hidden', + overflowWrap: 'word-break' + }, + tHead: { + fontWeight: 'bold', + float: 'left', + fontSize: '0.8rem', + fontFamily: 'Helvetica' + }, + tTxt: { + fontSize: '0.8rem', + fontFamily: 'Helvetica', + marginRight: 3 + }, + quill: { + fontSize: '0.8rem', + fontFamily: 'Helvetica', + textAlign: 'left' + }, + quillContainer: { + margin: '10px 10px', + backgroundColor: '#fff', + '& .ql-container': { + border: 'none' + }, + '& .ql-editor': { + minHeight: '60px' + }, + '& .ql-editor.ql-blank::before': { + fontStyle: 'normal', + color: 'rgba(0, 0, 0, 0.54)' + } + } +}); +const simTitle = () => 'Simulated signals from NMRshiftDB'; +const simContent = nmrSimPeaks => nmrSimPeaks && nmrSimPeaks.sort((a, b) => a - b).join(', '); +const normalizeQuillValue = val => { + if (!val) return ''; + if (val === '


' || val === '

') return ''; + return val; +}; +const chemSubStyle = { + fontSize: '0.85em', + position: 'relative', + top: '0.24em', + lineHeight: 1 +}; +const renderReadableSubscript = (txt = '') => { + if (typeof txt !== 'string') return txt; + const regex = /([A-Za-z])(\d+)/g; + if (!regex.test(txt)) return txt; + regex.lastIndex = 0; + const parts = []; + let cursor = 0; + let match = regex.exec(txt); + while (match) { + const [raw, prefix, digits] = match; + const at = match.index; + if (at > cursor) parts.push(txt.slice(cursor, at)); + parts.push(prefix); + parts.push(/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + style: chemSubStyle, + children: digits + }, `${at}-${digits}`)); + cursor = at + raw.length; + match = regex.exec(txt); + } + if (cursor < txt.length) parts.push(txt.slice(cursor)); + return parts; +}; +const handleDescriptionChanged = (content, delta, source, editor, onDescriptionChanged) => { + if (!onDescriptionChanged) return; + onDescriptionChanged(normalizeQuillValue(content), delta, source, editor); +}; +const aucValue = integration => { + if (!integration) { + return ''; + } + 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); + } + }); + } + return values.join(', '); +}; +const SECData = ({ + classes, + layout, + detector, + secData +}) => { + if (_format.default.isSECLayout(layout) && secData) { + const { + d, + mn, + mp, + mw + } = secData; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowOdd), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tHead, 'txt-sv-panel-txt'), + children: "Detector: " + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: detector + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowEven), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tHead, 'txt-sv-panel-txt'), + children: "D: " + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: d + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowOdd), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tHead, 'txt-sv-panel-txt'), + children: "MN: " + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: mn + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowEven), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tHead, 'txt-sv-panel-txt'), + children: "MP: " + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: mp + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowOdd), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tHead, 'txt-sv-panel-txt'), + children: "MW: " + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: mw + })] + })] + }); + } + return null; +}; +SECData.propTypes = { + classes: _propTypes.default.object.isRequired, + layout: _propTypes.default.string.isRequired, + detector: _propTypes.default.object.isRequired, + secData: _propTypes.default.object.isRequired +}; +const DSCData = ({ + classes, + layout, + dscMetaData, + updateAction +}) => { + if (_format.default.isDSCLayout(layout) && dscMetaData !== undefined) { + const { + meltingPoint, + tg + } = dscMetaData; + const onChange = e => { + const { + name, + value + } = e.target; + const dataToUpdate = { + meltingPoint, + tg + }; + dataToUpdate[name] = value; + updateAction(dataToUpdate); + }; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowOdd), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tHead, 'txt-sv-panel-txt'), + children: "Melting Point: " + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("input", { + type: "text", + name: "meltingPoint", + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + value: meltingPoint, + onChange: onChange + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowEven), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tHead, 'txt-sv-panel-txt'), + children: "TG: " + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("input", { + type: "text", + name: "tg", + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + value: tg, + onChange: onChange + })] + })] + }); + } + return null; +}; +DSCData.propTypes = { + classes: _propTypes.default.object.isRequired, + layout: _propTypes.default.string.isRequired, + dscMetaData: _propTypes.default.object.isRequired, + updateAction: _propTypes.default.func.isRequired +}; +const InfoPanel = ({ + classes, + expand, + feature, + integration, + editorOnly, + molSvg, + descriptions, + layoutSt, + simulationSt, + shiftSt, + curveSt, + exactMass, + onExapnd, + canChangeDescription, + onDescriptionChanged, + detectorSt, + metaSt, + updateDSCMetaDataAct +}) => { + if (!feature) return null; + const { + title, + observeFrequency, + solventName, + secData + } = feature; + const { + dscMetaData + } = metaSt; + const { + curveIdx + } = curveSt; + const { + curves + } = detectorSt; + const getSelectedDetectorForCurve = (_detectorSt, targetCurveIdx) => { + const targetCurve = curves.find(curve => curve.curveIdx === targetCurveIdx); + return targetCurve ? targetCurve.selectedDetector.name : ''; + }; + let selectedDetector = getSelectedDetectorForCurve(detectorSt, curveIdx); + + // default detector from jcamp + if (!selectedDetector && feature.detector) { + selectedDetector = feature.detector; + } + const { + shifts + } = shiftSt; + const selectedShift = shifts[curveIdx]; + let showSolvName = solventName; + if (selectedShift !== undefined) { + const shiftName = selectedShift.ref.name; + showSolvName = shiftName === '- - -' ? solventName : shiftName; + } + let originStack = null; + if (integration) { + originStack = integration.originStack; // eslint-disable-line + } + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Accordion, { + "data-testid": "PanelInfo", + expanded: expand, + onChange: onExapnd, + disableGutters: true, + sx: { + '&.MuiAccordion-root.Mui-expanded': { + margin: 0 + }, + '&:before': { + display: 'none' + } + }, + TransitionProps: { + unmountOnExit: true + } // increase Accordion performance + , + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.AccordionSummary, { + expandIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ExpandMore.default, {}), + className: (0, _classnames.default)(classes.panelSummary), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, { + className: "txt-panel-header", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtBadge, 'txt-sv-panel-title'), + children: "Info" + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Divider, {}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.panelDetail), + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowOdd), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tHead, 'txt-sv-panel-txt'), + children: "Title : " + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: title + })] + }), _format.default.isNmrLayout(layoutSt) ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowEven), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tHead, 'txt-sv-panel-txt'), + children: "Freq : " + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: `${parseInt(observeFrequency, 10)} MHz` || ' - ' + })] + }) : null, _format.default.isNmrLayout(layoutSt) ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowOdd), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tHead, 'txt-sv-panel-txt'), + children: "Solv : " + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: renderReadableSubscript(showSolvName) + })] + }) : null, _format.default.isMsLayout(layoutSt) && exactMass ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowOdd), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tHead, 'txt-sv-panel-txt'), + children: "Exact mass: " + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: `${parseFloat(exactMass).toFixed(6)} g/mol` + })] + }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(SECData, { + classes: classes, + layout: layoutSt, + detector: selectedDetector, + secData: secData + }), !molSvg ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactSvgFileZoomPan.default, { + svg: molSvg, + duration: 300, + resize: true + }), _format.default.isHplcUvVisLayout(layoutSt) ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowOddSim), + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tHead, 'txt-sv-panel-txt'), + 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) + })] + }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(DSCData, { + classes: classes, + layout: layoutSt, + dscMetaData: dscMetaData, + updateAction: updateDSCMetaDataAct + }), !_format.default.isCyclicVoltaLayout(layoutSt) ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: classes.subSectionHeader, + children: "Content" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: classes.quillContainer, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactQuill.default, { + className: (0, _classnames.default)(classes.quill, 'card-sv-quill'), + value: normalizeQuillValue(descriptions), + placeholder: canChangeDescription ? 'Peaks will be written here...' : undefined, + readOnly: !canChangeDescription, + modules: { + toolbar: false + }, + onChange: (content, delta, source, editor) => handleDescriptionChanged(content, delta, source, editor, onDescriptionChanged) + }) + })] + }) : null, !editorOnly && _format.default.isNmrLayout(layoutSt) ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: classes.subSectionHeader, + children: simTitle() + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: (0, _classnames.default)(classes.rowRoot, classes.rowOddSim), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.tTxt, classes.tTxtSim, 'txt-sv-panel-txt'), + children: simContent(simulationSt.nmrSimPeaks) + }) + })] + }) : null] + })] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + layoutSt: state.layout, + simulationSt: state.simulation, + shiftSt: state.shift, + curveSt: state.curve, + detectorSt: state.detector, + metaSt: state.meta +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + updateDSCMetaDataAct: _meta.updateDSCMetaData +}, dispatch); +InfoPanel.propTypes = { + classes: _propTypes.default.object.isRequired, + expand: _propTypes.default.bool.isRequired, + feature: _propTypes.default.object.isRequired, + integration: _propTypes.default.object.isRequired, + editorOnly: _propTypes.default.bool.isRequired, + molSvg: _propTypes.default.string.isRequired, + descriptions: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.array]).isRequired, + layoutSt: _propTypes.default.string.isRequired, + simulationSt: _propTypes.default.array.isRequired, + shiftSt: _propTypes.default.object.isRequired, + curveSt: _propTypes.default.object.isRequired, + onExapnd: _propTypes.default.func.isRequired, + canChangeDescription: _propTypes.default.bool.isRequired, + onDescriptionChanged: _propTypes.default.func, + exactMass: _propTypes.default.string, + detectorSt: _propTypes.default.object.isRequired, + metaSt: _propTypes.default.object.isRequired, + updateDSCMetaDataAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)( +// eslint-disable-line +mapStateToProps, mapDispatchToProps)((0, _styles.withStyles)(styles)(InfoPanel)); // eslint-disable-line \ No newline at end of file diff --git a/dist/components/panel/multiplicity.js b/dist/components/panel/multiplicity.js new file mode 100644 index 00000000..52d50227 --- /dev/null +++ b/dist/components/panel/multiplicity.js @@ -0,0 +1,311 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _ExpandMore = _interopRequireDefault(require("@mui/icons-material/ExpandMore")); +var _HighlightOff = _interopRequireDefault(require("@mui/icons-material/HighlightOff")); +var _styles = require("@mui/styles"); +var _RefreshOutlined = _interopRequireDefault(require("@mui/icons-material/RefreshOutlined")); +var _multiplicity = require("../../actions/multiplicity"); +var _multiplicity_select = _interopRequireDefault(require("./multiplicity_select")); +var _multiplicity_coupling = _interopRequireDefault(require("./multiplicity_coupling")); +var _multiplicity_calc = require("../../helpers/multiplicity_calc"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable function-paren-newline, +function-call-argument-newline, react/function-component-definition */ + +const styles = theme => ({ + panel: { + backgroundColor: '#eee', + display: 'table-row' + }, + panelSummary: { + backgroundColor: '#eee', + height: 32 + }, + txtBadge: {}, + panelDetail: { + backgroundColor: '#fff', + maxHeight: 'calc(90vh - 220px)', + // ROI + overflow: 'auto' + }, + table: { + width: '100%' + }, + tRowHeadPos: { + backgroundColor: '#2196f3', + height: 32 + }, + tRowHeadNeg: { + backgroundColor: '#fa004f', + height: 32 + }, + tTxtHead: { + color: 'white', + padding: '4px 0 4px 5px' + }, + tTxtHeadXY: { + color: 'white', + padding: '4px 0 4px 90px' + }, + tTxt: { + padding: 0 + }, + tRow: { + height: 28, + '&:nth-of-type(even)': { + backgroundColor: theme.palette ? theme.palette.background.default : '#d3d3d3' + } + }, + rmBtn: { + color: 'red', + '&:hover': { + borderRadius: 12, + backgroundColor: 'red', + color: 'white' + } + }, + moCard: { + textAlign: 'left' + }, + moCardHead: { + backgroundColor: '#999', + color: 'white' + }, + moExtId: { + border: '2px solid white', + borderRadius: 12, + color: 'white', + margin: '0 5px 0 5px', + padding: '0 5px 0 5px' + }, + moExtTxt: { + margin: '0 5px 0 5x', + fontSize: '0.8rem', + fontFamily: 'Helvetica' + }, + moSelect: { + margin: '0 5x 0 5px', + fontSize: '0.8rem', + fontFamily: 'Helvetica' + }, + moCBox: { + marginLeft: 24, + padding: '4px 0 4px 4px' + }, + btnRf: { + color: '#fff', + float: 'right', + minWidth: 40, + right: 15 + } +}); +const cBoxStyle = () => ({ + root: { + color: 'white', + '&$checked': { + color: 'white' + } + }, + checked: {} +}); +const MUCheckbox = (0, _styles.withStyles)(cBoxStyle)(_material.Checkbox); +const createData = (idx, xExtent, peaks, shift, smExtext, mpyType, js, onClick, onRefresh) => ({ + idx: idx + 1, + xExtent, + onClick, + onRefresh, + peaks, + center: (0, _multiplicity_calc.calcMpyCenter)(peaks, shift, mpyType), + jStr: (0, _multiplicity_calc.calcMpyJStr)(js), + mpyType, + isCheck: smExtext.xL === xExtent.xL && smExtext.xU === xExtent.xU +}); +const pkList = (classes, row, shift, digits, rmMpyPeakByPanelAct) => row.peaks.map(pk => { + const { + xExtent + } = row; + const cb = () => rmMpyPeakByPanelAct({ + peak: pk, + xExtent + }); + const rmBtn = /*#__PURE__*/(0, _jsxRuntime.jsx)(_HighlightOff.default, { + onClick: cb, + className: classes.rmBtn + }); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableRow, { + className: classes.tRow, + hover: true, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: `(${(pk.x - shift).toFixed(digits)}, ${pk.y.toExponential(2)})` + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: rmBtn + })] + }, pk.x); +}); +const refreshBtn = (classes, onRefresh) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, { + placement: "left", + title: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-sv-tp", + children: "Calculate Multiplicity" + }), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + className: classes.btnRf, + onClick: onRefresh, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_RefreshOutlined.default, {}) + }) +}); +const mpyList = (classes, digits, multiplicitySt, curveSt, clickMpyOneAct, rmMpyPeakByPanelAct, resetMpyOneAct) => { + const { + curveIdx + } = curveSt; + const { + multiplicities + } = multiplicitySt; + let selectedMulti = multiplicities[curveIdx]; + if (selectedMulti === undefined) { + selectedMulti = { + stack: [], + shift: 0, + smExtext: false, + edited: false + }; + } + const { + stack, + shift, + smExtext + } = selectedMulti; + const rows = stack.map((k, idx) => { + const { + peaks, + xExtent, + mpyType, + js + } = k; + const onRefresh = () => resetMpyOneAct(xExtent); + const onClick = e => { + e.stopPropagation(); + e.preventDefault(); + const payload = { + curveIdx, + payloadData: xExtent + }; + clickMpyOneAct(payload); + }; + return createData(idx, xExtent, peaks, shift, smExtext, mpyType, js, onClick, onRefresh); + }); + return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + children: rows.map(row => /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: classes.moCard, + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + className: classes.moCardHead, + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(MUCheckbox, { + className: classes.moCBox, + checked: row.isCheck, + onChange: row.onClick + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.moExtTxt, classes.moExtId, 'txt-sv-panel-head'), + children: row.idx + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.moExtTxt, 'txt-sv-panel-head'), + children: `${row.center.toFixed(3)} (ppm)` + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.moSelect, 'txt-sv-panel-head'), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_multiplicity_select.default, { + target: row + }) + }), refreshBtn(classes, row.onRefresh)] + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_multiplicity_coupling.default, { + row: row + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Table, { + className: classes.table, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableBody, { + children: pkList(classes, row, shift, digits, rmMpyPeakByPanelAct) + }) + })] + }, row.idx)) + }); +}; +const MultiplicityPanel = ({ + classes, + expand, + onExapnd, + multiplicitySt, + curveSt, + clickMpyOneAct, + rmMpyPeakByPanelAct, + resetMpyOneAct +}) => { + const digits = 4; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Accordion, { + expanded: expand, + onChange: onExapnd, + disableGutters: true, + sx: { + '&.MuiAccordion-root.Mui-expanded': { + margin: 0 + }, + '&:before': { + display: 'none' + } + }, + TransitionProps: { + unmountOnExit: true + } // increase Accordion performance + , + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.AccordionSummary, { + expandIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ExpandMore.default, {}), + className: (0, _classnames.default)(classes.panelSummary), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, { + className: "txt-panel-header", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtBadge, 'txt-sv-panel-title'), + children: "Multiplicity" + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Divider, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: (0, _classnames.default)(classes.panelDetail), + children: mpyList(classes, digits, multiplicitySt, curveSt, clickMpyOneAct, rmMpyPeakByPanelAct, resetMpyOneAct) + })] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + integrationSt: state.integration.present, + multiplicitySt: state.multiplicity.present, + curveSt: state.curve +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + clickMpyOneAct: _multiplicity.clickMpyOne, + rmMpyPeakByPanelAct: _multiplicity.rmMpyPeakByPanel, + resetMpyOneAct: _multiplicity.resetMpyOne +}, dispatch); +MultiplicityPanel.propTypes = { + classes: _propTypes.default.object.isRequired, + expand: _propTypes.default.bool.isRequired, + onExapnd: _propTypes.default.func.isRequired, + multiplicitySt: _propTypes.default.object.isRequired, + clickMpyOneAct: _propTypes.default.func.isRequired, + rmMpyPeakByPanelAct: _propTypes.default.func.isRequired, + resetMpyOneAct: _propTypes.default.func.isRequired, + curveSt: _propTypes.default.object.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _styles.withStyles)(styles)(MultiplicityPanel)); \ No newline at end of file diff --git a/dist/components/panel/multiplicity_coupling.js b/dist/components/panel/multiplicity_coupling.js new file mode 100644 index 00000000..e245ceee --- /dev/null +++ b/dist/components/panel/multiplicity_coupling.js @@ -0,0 +1,139 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _classnames = _interopRequireDefault(require("classnames")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _styles = require("@mui/styles"); +var _material = require("@mui/material"); +var _multiplicity = require("../../actions/multiplicity"); +var _jsxRuntime = require("react/jsx-runtime"); +const styles = () => ({ + jDiv: { + height: 28 + }, + jTxt: { + margin: '0 5px 4px 60px' + }, + moExtTxt: { + margin: '0 5px 0 5x', + fontSize: '0.8rem', + fontFamily: 'Helvetica' + }, + txtField: { + width: 260, + margin: '0 3px 0 3px' + }, + txtInput: { + color: 'white', + fontSize: '0.9rem', + fontFamily: 'Helvetica', + height: 24 + } +}); +const txtJ = () => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputAdornment, { + position: "start", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-cmd-j", + children: "J\xA0=" + }) +}); +const txtHz = () => /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputAdornment, { + position: "end", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: "txt-cmd-hz", + children: "Hz" + }) +}); +class MpyCoupling extends _react.default.Component { + constructor(props) { + super(props); + this.state = { + focus: false, + tmpVal: false + }; + this.onFocus = this.onFocus.bind(this); + this.onBlur = this.onBlur.bind(this); + this.onChange = this.onChange.bind(this); + } + onFocus() { + this.setState({ + focus: true + }); + } + onBlur() { + const { + row, + updateMpyJAct + } = this.props; + const { + tmpVal + } = this.state; + const { + xExtent + } = row; + this.setState({ + focus: false, + tmpVal: false + }); + updateMpyJAct({ + xExtent, + value: tmpVal + }); + } + onChange(e) { + this.setState({ + tmpVal: e.target.value + }); + } + render() { + const { + classes, + row + } = this.props; + const { + focus, + tmpVal + } = this.state; + const value = focus && (tmpVal || tmpVal === '') ? tmpVal : row.jStr; + return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: (0, _classnames.default)(classes.jDiv), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.moExtTxt, classes.jTxt, 'txt-sv-panel-head'), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TextField, { + className: (0, _classnames.default)(classes.txtField, 'txt-cmd-field'), + placeholder: "-", + value: value, + margin: "none", + InputProps: { + startAdornment: txtJ(), + endAdornment: txtHz(), + className: (0, _classnames.default)(classes.txtInput, 'txt-sv-input-label') + }, + onChange: this.onChange, + onFocus: this.onFocus, + onBlur: this.onBlur, + variant: "outlined" + }) + }) + }); + } +} +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + updateMpyJAct: _multiplicity.updateMpyJ +}, dispatch); +MpyCoupling.propTypes = { + classes: _propTypes.default.object.isRequired, + row: _propTypes.default.object.isRequired, + updateMpyJAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _styles.withStyles)(styles)(MpyCoupling)); \ No newline at end of file diff --git a/dist/components/panel/multiplicity_select.js b/dist/components/panel/multiplicity_select.js new file mode 100644 index 00000000..056ae028 --- /dev/null +++ b/dist/components/panel/multiplicity_select.js @@ -0,0 +1,88 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _styles = require("@mui/styles"); +var _multiplicity = require("../../actions/multiplicity"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable react/function-component-definition */ + +const Styles = () => ({ + formControl: { + minWidth: 50, + margin: '2px 3px 0 3px' + }, + txtField: { + width: 120, + margin: '3px 3px 3px 3px' + }, + txtInput: { + height: 24, + fontSize: '0.9rem', + fontFamily: 'Helvetica', + color: 'white' + } +}); +const MpySelect = ({ + classes, + target, + selectMpyTypeAct +}) => { + const { + mpyType, + xExtent + } = target; + const onBlur = e => selectMpyTypeAct({ + xExtent, + mpyType: e.target.value + }); + const onChange = e => selectMpyTypeAct({ + xExtent, + mpyType: e.target.value + }); + const onEnterPress = e => { + if (e.key === 'Enter') { + selectMpyTypeAct({ + xExtent, + mpyType: e.target.value + }); + } + }; + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormControl, { + className: (0, _classnames.default)(classes.formControl), + variant: "outlined", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TextField, { + className: (0, _classnames.default)(classes.txtField, 'txt-cmd-field'), + value: mpyType, + margin: "none", + variant: "outlined", + InputProps: { + className: (0, _classnames.default)(classes.txtInput, 'txt-sv-input-label') + }, + onChange: onChange, + onBlur: onBlur, + onKeyPress: onEnterPress + }) + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + selectMpyTypeAct: _multiplicity.selectMpyType +}, dispatch); +MpySelect.propTypes = { + classes: _propTypes.default.object.isRequired, + target: _propTypes.default.object.isRequired, + selectMpyTypeAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)((0, _styles.withStyles)(Styles)(MpySelect)); \ No newline at end of file diff --git a/dist/components/panel/peaks.js b/dist/components/panel/peaks.js new file mode 100644 index 00000000..4513c8f0 --- /dev/null +++ b/dist/components/panel/peaks.js @@ -0,0 +1,240 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _classnames = _interopRequireDefault(require("classnames")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _material = require("@mui/material"); +var _ExpandMore = _interopRequireDefault(require("@mui/icons-material/ExpandMore")); +var _HighlightOff = _interopRequireDefault(require("@mui/icons-material/HighlightOff")); +var _styles = require("@mui/styles"); +var _chem = require("../../helpers/chem"); +var _edit_peak = require("../../actions/edit_peak"); +var _format = _interopRequireDefault(require("../../helpers/format")); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable react/function-component-definition, no-unused-vars */ + +const styles = theme => ({ + chip: { + margin: '1px 0 1px 0' + }, + panel: { + backgroundColor: '#eee', + display: 'table-row' + }, + panelSummary: { + backgroundColor: '#eee', + height: 32 + }, + txtBadge: {}, + panelDetail: { + backgroundColor: '#fff', + maxHeight: 'calc(90vh - 220px)', + // ROI + overflow: 'auto' + }, + table: { + width: '100%' + }, + tRowHeadPos: { + backgroundColor: '#999', + height: 32 + }, + tRowHeadNeg: { + backgroundColor: '#999', + height: 32 + }, + tTxtHead: { + color: 'white', + padding: '5px 5px 5px 5px' + }, + tTxtHeadXY: { + color: 'white', + padding: '4px 0 4px 90px' + }, + tTxt: { + padding: '4px 0 4px 0' + }, + tRow: { + height: 28, + '&:nth-of-type(even)': { + backgroundColor: theme.palette ? theme.palette.background.default : '#d3d3d3' + } + }, + rmBtn: { + color: 'red', + '&:hover': { + borderRadius: 12, + backgroundColor: 'red', + color: 'white' + } + } +}); +const createData = (classes, idx, x, y, cb, digits) => ({ + idx: idx + 1, + x: x.toFixed(digits), + y, + rmBtn: /*#__PURE__*/(0, _jsxRuntime.jsx)(_HighlightOff.default, { + onClick: cb, + className: classes.rmBtn + }) +}); +const peakList = (peaks, digits, cbAct, classes, isPos) => { + const rows = peaks.map((pp, idx) => { + const onDelete = () => cbAct(pp); + return createData(classes, idx, pp.x, pp.y, onDelete, digits); + }); + const rowKlass = isPos ? classes.tRowHeadPos : classes.tRowHeadNeg; + const headTxt = isPos ? 'P+' : 'P-'; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Table, { + className: classes.table, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableHead, { + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableRow, { + className: rowKlass, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxtHead, 'txt-sv-panel-head'), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("i", { + children: headTxt + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxtHeadXY, 'txt-sv-panel-head'), + children: "X" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxtHeadXY, 'txt-sv-panel-head'), + children: "Y" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxtHead, 'txt-sv-panel-head'), + children: "-" + })] + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableBody, { + children: rows.map(row => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.TableRow, { + className: classes.tRow, + hover: true, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: row.idx + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: row.x + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: row.y.toExponential(2) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.TableCell, { + align: "right", + className: (0, _classnames.default)(classes.tTxt, 'txt-sv-panel-txt'), + children: row.rmBtn + })] + }, row.idx)) + })] + }); +}; +const PeakPanel = ({ + editPeakSt, + layoutSt, + classes, + expand, + onExapnd, + rmFromPosListAct, + rmFromNegListAct, + curveSt +}) => { + const { + curveIdx, + listCurves + } = curveSt; + const { + peaks + } = editPeakSt; + if (curveIdx >= peaks.length) { + return null; + } + const selectedEditPeaks = peaks[curveIdx]; + if (!selectedEditPeaks) { + return null; + } + const { + pos, + neg + } = selectedEditPeaks; + const selectedCurve = listCurves[curveIdx]; + if (!selectedCurve) { + return null; + } + const { + feature + } = selectedCurve; + const currentPeakOfCurve = (0, _chem.Convert2Peak)(feature); + const filteredArray = currentPeakOfCurve.filter(element => neg.includes(element)); + const peaksData = [].concat(filteredArray).concat(pos); + const digits = _format.default.isEmWaveLayout(layoutSt) ? 0 : 4; + return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Accordion, { + "data-testid": "PeaksPanelInfo", + expanded: expand, + onChange: onExapnd, + disableGutters: true, + sx: { + '&.MuiAccordion-root.Mui-expanded': { + margin: 0 + }, + '&:before': { + display: 'none' + } + }, + TransitionProps: { + unmountOnExit: true + } // increase Accordion performance + , + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.AccordionSummary, { + expandIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ExpandMore.default, {}), + className: (0, _classnames.default)(classes.panelSummary), + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, { + className: "txt-panel-header", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + className: (0, _classnames.default)(classes.txtBadge, 'txt-sv-panel-title'), + children: "Peaks" + }) + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Divider, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: (0, _classnames.default)(classes.panelDetail), + children: peakList(peaksData, digits, rmFromPosListAct, classes, true) + })] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + editPeakSt: state.editPeak.present, + layoutSt: state.layout, + curveSt: state.curve +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + rmFromPosListAct: _edit_peak.rmFromPosList, + rmFromNegListAct: _edit_peak.rmFromNegList +}, dispatch); +PeakPanel.propTypes = { + classes: _propTypes.default.object.isRequired, + expand: _propTypes.default.bool.isRequired, + editPeakSt: _propTypes.default.object.isRequired, + layoutSt: _propTypes.default.string.isRequired, + onExapnd: _propTypes.default.func.isRequired, + rmFromPosListAct: _propTypes.default.func.isRequired, + rmFromNegListAct: _propTypes.default.func.isRequired, + curveSt: _propTypes.default.object.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)( +// eslint-disable-line +mapStateToProps, mapDispatchToProps)((0, _styles.withStyles)(styles)(PeakPanel)); // eslint-disable-line \ No newline at end of file diff --git a/dist/constants/action_type.js b/dist/constants/action_type.js new file mode 100644 index 00000000..820cb066 --- /dev/null +++ b/dist/constants/action_type.js @@ -0,0 +1,154 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.XRD = exports.UI = exports.THRESHOLD = exports.SUBMIT = exports.STATUS = exports.SIMULATION = exports.SHIFT = exports.SEC = exports.SCAN = exports.MULTIPLICITY = exports.META = exports.MANAGER = exports.LAYOUT = exports.JCAMP = exports.INTEGRATION = exports.FORECAST = exports.EDITPEAK = exports.CYCLIC_VOLTA_METRY = exports.CURVE = exports.AXES = void 0; +const THRESHOLD = exports.THRESHOLD = { + UPDATE_VALUE: 'THRESHOLD_UPDATE_VALUE', + RESET_VALUE: 'THRESHOLD_RESET_VALUE', + TOGGLE_ISEDIT: 'THRESHOLD_TOGGLE_ISEDIT', + UPDATE_UPPER_VALUE: 'THRESHOLD_UPDATE_UPPER_VALUE', + UPDATE_LOWER_VALUE: 'THRESHOLD_UPDATE_LOWER_VALUE' +}; +const EDITPEAK = exports.EDITPEAK = { + ADD_POSITIVE: 'ADD_TO_POSITIVE_EDITPEAK_LIST', + ADD_NEGATIVE: 'ADD_TO_NEGATIVE_EDITPEAK_LIST', + RM_NEGATIVE: 'RM_FROM_NEGATIVE_EDITPEAK_LIST', + RM_POSITIVE: 'RM_FROM_POSITIVE_EDITPEAK_LIST', + SHIFT: 'EDITPEAK_SHIFT', + CLEAR_ALL: 'EDITPEAK_CLEAR_ALL' +}; +const STATUS = exports.STATUS = { + TOGGLEBTNSUBMIT: 'TOGGLE_BTN_SUBMIT', + TOGGLEBTNALL: 'TOGGLE_BTN_ALL', + ENABLEBTNALL: 'ENABLE_BTN_ALL' +}; +const MANAGER = exports.MANAGER = { + RESETALL: 'RESET_ALL', + RESETSHIFT: 'RESET_SHIFT', + RESET_INIT_COMMON: 'RESET_INIT_COMMON', + RESET_INIT_NMR: 'RESET_INIT_NMR', + RESET_INIT_MS: 'RESET_INIT_MS', + RESET_INIT_COMMON_WITH_INTERGATION: 'RESET_INIT_COMMON_WITH_INTERGATION', + RESET_DETECTOR: 'RESET_DETECTOR', + RESET_MULTIPLICITY: 'RESET_MULTIPLICITY' +}; +const LAYOUT = exports.LAYOUT = { + UPDATE: 'UPDATE_LAYOUT' +}; +const SHIFT = exports.SHIFT = { + SET_REF: 'SHIFT_SET_REF', + SET_PEAK: 'SHIFT_SET_PEAK', + RM_PEAK: 'SHIFT_RM_PEAK' +}; +const SCAN = exports.SCAN = { + SET_TARGET: 'SCAN_SET_TARGET', + RESET_TARGET: 'SCAN_RESET_TARGET', + TOGGLE_ISAUTO: 'SCAN_TOGGLE_ISAUTO' +}; +const UI = exports.UI = { + CLICK_TARGET: 'UI_CLICK_TARGET', + VIEWER: { + SET_TYPE: 'UI_VIEWER_SET_TYPE' + }, + SWEEP: { + SET_TYPE: 'UI_SWEEP_SET_TYPE', + SELECT: 'UI_SWEEP_SELECT', + SELECT_ZOOMIN: 'UI_SWEEP_SELECT_ZOOMIN', + SELECT_ZOOMRESET: 'UI_SWEEP_SELECT_ZOOMRESET', + SELECT_INTEGRATION: 'UI_SWEEP_SELECT_INTEGRATION', + SELECT_MULTIPLICITY: 'UI_SWEEP_SELECT_MULTIPLICITY', + SELECT_MULTIPLICITY_RDC: 'UI_SWEEP_SELECT_MULTIPLICITY_RDC' + }, + WHEEL: { + SCROLL: 'UI_WHEEL_SCROLL' + } +}; +const FORECAST = exports.FORECAST = { + INIT_STATUS: 'FORECAST_INIT_STATUS', + SET_IR_STATUS: 'FORECAST_SET_IR_STATUS', + SET_NMR_STATUS: 'FORECAST_SET_NMR_STATUS', + CLEAR_STATUS: 'FORECAST_CLEAR_STATUS' +}; +const SUBMIT = exports.SUBMIT = { + TOGGLE_IS_ASCEND: 'SUBMIT_TOGGLE_IS_ASCEND', + TOGGLE_IS_INTENSITY: 'SUBMIT_TOGGLE_IS_INTENSITY', + UPDATE_OPERATION: 'SUBMIT_UPDATE_OPERATION', + UPDATE_DECIMAL: 'SUBMIT_UPDATE_DECIMAL' +}; +const INTEGRATION = exports.INTEGRATION = { + RM_ONE: 'INTEGRATION_RM_ONE', + SET_REF: 'INTEGRATION_SET_REF', + SET_FKR: 'INTEGRATION_SET_FKR', + RESET_ALL_RDC: 'INTEGRATION_RESET_ALL_RDC', + CLEAR_ALL: 'INTEGRATION_CLEAR_ALL', + SWEEP: 'INTEGRATION_SWEEP', + SPLIT: 'INTEGRATION_SPLIT' +}; +const SIMULATION = exports.SIMULATION = { + RESET_ALL_RDC: 'SIMULATION_RESET_ALL_RDC' +}; +const MULTIPLICITY = exports.MULTIPLICITY = { + ONE_CLICK: 'MULTIPLICITY_ONE_CLICK', + ONE_CLICK_BY_UI: 'MULTIPLICITY_ONE_CLICK_BY_UI', + PEAK_RM_BY_PANEL: 'MULTIPLICITY_PEAK_RM_BY_PANEL', + PEAK_RM_BY_PANEL_RDC: 'MULTIPLICITY_PEAK_RM_BY_PANEL_RDC', + PEAK_ADD_BY_UI_SAG: 'MULTIPLICITY_PEAK_ADD_BY_UI_SAG', + PEAK_ADD_BY_UI_RDC: 'MULTIPLICITY_PEAK_ADD_BY_UI_RDC', + PEAK_RM_BY_UI: 'MULTIPLICITY_PEAK_RM_BY_UI', + PEAK_RM_BY_UI_RDC: 'MULTIPLICITY_PEAK_RM_BY_UI_RDC', + TYPE_SELECT: 'MULTIPLICITY_TYPE_SELECT', + TYPE_SELECT_RDC: 'MULTIPLICITY_TYPE_SELECT_RDC', + RESET_ALL_RDC: 'MULTIPLICITY_RESET_ALL_RDC', + RESET_ONE: 'MULTIPLICITY_RESET_ONE', + RESET_ONE_RDC: 'MULTIPLICITY_RESET_ONE_RDC', + UPDATE_J: 'MULTIPLICITY_UPDATE_J', + CLEAR_ALL: 'MULTIPLICITY_CLEAR_ALL' +}; +const META = exports.META = { + UPDATE_PEAKS: 'META_UPDATE_PEAKS', + UPDATE_PEAKS_RDC: 'META_UPDATE_PEAKS_RDC', + UPDATE_META_DATA: 'UPDATE_META_DATA', + UPDATE_META_DATA_RDC: 'UPDATE_META_DATA_RDC' +}; +const JCAMP = exports.JCAMP = { + ADD_OTHERS: 'JCAMP_ADD_OTHERS', + RM_OTHERS_ONE: 'JCAMP_RM_OTHERS_ONE', + TOGGLE_SHOW: 'JCAMP_TOGGLE_SHOW', + CLEAR_ALL: 'JCAMP_CLEAR_ALL' +}; +const XRD = exports.XRD = { + UPDATE_WAVE_LENGTH: 'UPDATE_WAVE_LENGTH' +}; +const CYCLIC_VOLTA_METRY = exports.CYCLIC_VOLTA_METRY = { + ADD_PAIR_PEAKS: 'ADD_PAIR_PEAKS', + REMOVE_PAIR_PEAKS: 'REMOVE_PAIR_PEAKS', + ADD_MAX_PEAK: 'ADD_MAX_PEAK', + REMOVE_MAX_PEAK: 'REMOVE_MAX_PEAK', + ADD_MIN_PEAK: 'ADD_MIN_PEAK', + REMOVE_MIN_PEAK: 'REMOVE_MIN_PEAK', + SELECT_PAIR_PEAK: 'SELECT_PAIR_PEAK', + WORK_WITH_MAX_PEAK: 'WORK_WITH_MAX_PEAK', + ADD_PECKER: 'ADD_PECKER', + REMOVE_PECKER: 'REMOVE_PECKER', + RESETALL: 'RESETALL_VOLTA_METRY', + SET_REF: 'VOLTA_METRY_SET_REF', + SELECT_REF_PEAK: 'SELECT_REF_PEAK', + SET_FACTOR: 'CYCLIC_VOLTA_METRY_SET_FACTOR', + SET_AREA_VALUE: 'CYCLIC_VOLTA_METRY_SET_AREA_VALUE', + SET_AREA_UNIT: 'CYCLIC_VOLTA_METRY_SET_AREA_UNIT', + TOGGLE_DENSITY: 'CYCLIC_VOLTA_METRY_TOGGLE_DENSITY' +}; +const CURVE = exports.CURVE = { + SELECT_WORKING_CURVE: 'SELECT_WORKING_CURVE', + SET_ALL_CURVES: 'SET_ALL_CURVES', + SET_SHOULD_SHOW_ALL_CURVES: 'SET_SHOULD_SHOW_ALL_CURVES' +}; +const AXES = exports.AXES = { + UPDATE_X_AXIS: 'UPDATE_X_AXIS', + UPDATE_Y_AXIS: 'UPDATE_Y_AXIS' +}; +const SEC = exports.SEC = { + UPDATE_DETECTOR: 'UPDATE_DETECTOR' +}; \ No newline at end of file diff --git a/dist/constants/list_axes.js b/dist/constants/list_axes.js new file mode 100644 index 00000000..0936a991 --- /dev/null +++ b/dist/constants/list_axes.js @@ -0,0 +1,17 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.LIST_AXES = void 0; +var _list_layout = require("./list_layout"); +const optionsAxisX = { + [_list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY]: ['', 'Voltage in V', 'Voltage vs Ref in V', 'Potential in V', 'Potential vs Ref in V'] +}; +const optionsAxisY = { + [_list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY]: ['', 'Current in A', 'Current in mA'] +}; +const LIST_AXES = exports.LIST_AXES = { + x: optionsAxisX, + y: optionsAxisY +}; \ No newline at end of file diff --git a/dist/constants/list_detectors.js b/dist/constants/list_detectors.js new file mode 100644 index 00000000..fcbda7ec --- /dev/null +++ b/dist/constants/list_detectors.js @@ -0,0 +1,15 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.LIST_DETECTORS = void 0; +const rI = { + name: 'Refractive index', + label: 'RI' +}; +const uV = { + name: 'Ultraviolet', + label: 'UV' +}; +const LIST_DETECTORS = exports.LIST_DETECTORS = [rI, uV]; \ No newline at end of file diff --git a/dist/constants/list_layout.js b/dist/constants/list_layout.js new file mode 100644 index 00000000..cf5830ed --- /dev/null +++ b/dist/constants/list_layout.js @@ -0,0 +1,31 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.LIST_LAYOUT = void 0; +const LIST_LAYOUT = exports.LIST_LAYOUT = { + PLAIN: 'PLAIN', + IR: 'IR', + RAMAN: 'RAMAN', + UVVIS: 'UV/VIS', + H1: '1H', + C13: '13C', + F19: '19F', + P31: '31P', + N15: '15N', + Si29: '29Si', + MS: 'MS', + TGA: 'THERMOGRAVIMETRIC ANALYSIS', + XRD: 'X-RAY DIFFRACTION', + HPLC_UVVIS: 'HPLC UV/VIS', + CYCLIC_VOLTAMMETRY: 'CYCLIC VOLTAMMETRY', + CDS: 'CIRCULAR DICHROISM SPECTROSCOPY', + SEC: 'SIZE EXCLUSION CHROMATOGRAPHY', + AIF: 'AIF', + EMISSIONS: 'Emissions', + DLS_ACF: 'DLS ACF', + DLS_INTENSITY: 'DLS intensity', + DSC: 'DIFFERENTIAL SCANNING CALORIMETRY', + GC: 'GAS CHROMATOGRAPHY' +}; \ No newline at end of file diff --git a/dist/constants/list_shift.js b/dist/constants/list_shift.js new file mode 100644 index 00000000..63eaf99d --- /dev/null +++ b/dist/constants/list_shift.js @@ -0,0 +1,421 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getListShift = exports.LIST_SHIFT_31P = exports.LIST_SHIFT_29Si = exports.LIST_SHIFT_1H = exports.LIST_SHIFT_19F = exports.LIST_SHIFT_15N = exports.LIST_SHIFT_13C = void 0; +var _list_layout = require("./list_layout"); +/* eslint-disable camelcase */ + +const noReference = { + name: '- - -', + value: 0.0, + label: false +}; +const cActicAcidD4Sept = { + name: 'Acetic acid-d4 (sept)', + value: 20.0, + label: 'Acetic acid-d4' +}; +const cActicAcidD4S = { + name: 'Acetic acid-d4 (s)', + value: 178.990, + label: 'Acetic acid-d4' +}; +const cAcetoneD6Sep = { + name: 'Acetone-d6 (sep)', + value: 29.640, + label: 'Acetone-d6', + nsdb: 'Acetone-D6 ((CD3)2CO)' +}; +const cAcetoneD6Broad = { + name: 'Acetone-d6 (broad)', + value: 206.260, + label: 'Acetone-d6', + nsdb: 'Acetone-D6 ((CD3)2CO)' +}; +const cAcetonitrileD3Sep = { + name: 'Acetonitrile-d3 (sep)', + value: 1.32, + label: 'Acetonitrile-d3', + nsdb: 'Acetonitrile-D3(CD3CN)' +}; +const cAcetonitrileD3S = { + name: 'Acetonitrile-d3 (s)', + value: 118.26, + label: 'Acetonitrile-d3', + nsdb: 'Acetonitrile-D3(CD3CN)' +}; +const cBenzeneT = { + name: 'Benzene (t)', + value: 128.060, + label: 'Benzene-d6', + nsdb: 'Benzene-D6 (C6D6)' +}; +const cChloroformDT = { + name: 'Chloroform-d (t)', + value: 77.16, + label: 'CDCl$3', + nsdb: 'Chloroform-D1 (CDCl3)' +}; +const cCyclohexaneD12Quin = { + name: 'Cyclohexane-d12 (quin)', + value: 26.430, + label: 'C$6D$1$2' +}; +const cDichloromethaneD2Quin = { + name: 'Dichloromethane-d2 (quin)', + value: 53.84, + label: 'CD$2Cl$2' +}; +const cDmfD7Sep1 = { + name: 'DMF-d7 (sep)-1', + value: 29.76, + label: 'DMF-d7' +}; +const cDmfD7Sep2 = { + name: 'DMF-d7 (sep)-2', + value: 34.89, + label: 'DMF-d7' +}; +const cDmfD7T3 = { + name: 'DMF-d7 (t)-3', + value: 163.15, + label: 'DMF-d7' +}; +const cDioxaneD8Quin = { + name: 'Dioxane-d8 (quin)', + value: 66.660, + label: 'Dioxane-d8' +}; +const cDmsoD6 = { + name: 'DMSO-d6', + value: 39.52, + label: 'DMSO-d6', + nsdb: 'Dimethylsulphoxide-D6 (DMSO-D6, C2D6SO)' +}; +const cEthanolD6Sep = { + name: 'Ethanol-d6 (sep)', + value: 17.3, + label: 'Ethanol-d6' +}; +const cEthanolD6Quin = { + name: 'Ethanol-d6 (quin)', + value: 56.96, + label: 'Ethanol-d6' +}; +const cMethanolD4Sep = { + name: 'Methanol-d4 (sep)', + value: 49.00, + label: 'Methanol-d4', + nsdb: 'Methanol-D4 (CD3OD)' +}; +const cPyridineD5T1 = { + name: 'Pyridine-d5 (t)-1', + value: 123.87, + label: 'Pyridine-d5', + nsdb: 'Pyridin-D5 (C5D5N)' +}; +const cPyridineD5T2 = { + name: 'Pyridine-d5 (t)-2', + value: 135.91, + label: 'Pyridine-d5', + nsdb: 'Pyridin-D5 (C5D5N)' +}; +const cPyridineD5T3 = { + name: 'Pyridine-d5 (t)-3', + value: 150.35, + label: 'Pyridine-d5', + nsdb: 'Pyridin-D5 (C5D5N)' +}; +const cThfD8Quin1 = { + name: 'THF-d8 (quin)-1', + value: 25.37, + label: 'THF-d8 ', + nsdb: 'Tetrahydrofuran-D8 (THF-D8, C4D4O)' +}; +const cThfD8Quin2 = { + name: 'THF-d8 (quin)-2', + value: 67.57, + label: 'THF-d8 ', + nsdb: 'Tetrahydrofuran-D8 (THF-D8, C4D4O)' +}; +const cTmsS = { + name: 'TMS (s)', + value: 0.00, + label: 'TMS' +}; +const cTolueneD8Sep1 = { + name: 'Toluene-d8 (sep)-1', + value: 20.4, + label: 'Toluene-d8' +}; +const cTolueneD8T2 = { + name: 'Toluene-d8 (t)-2', + value: 125.49, + label: 'Toluene-d8' +}; +const cTolueneD8T3 = { + name: 'Toluene-d8 (t)-3', + value: 128.33, + label: 'Toluene-d8' +}; +const cTolueneD8T4 = { + name: 'Toluene-d8 (t)-4', + value: 129.24, + label: 'Toluene-d8' +}; +const cTolueneD8T5 = { + name: 'Toluene-d8 (s)-5', + value: 137.86, + label: 'Toluene-d8' +}; +const cTfaDQ1 = { + name: 'TFA-d (q)-1', + value: 116.60, + label: 'TFA-d' +}; +const cTfaDQ2 = { + name: 'TFA-d (q)-2', + value: 164.20, + label: 'TFA-d' +}; +const cTrifluoroethanolD3Quin = { + name: 'Trifluoroethanol-d3 (quin)', + value: 61.80, + label: 'Trifluoroethanol-d3' +}; +const cTrifluoroethanolD3Broad = { + name: 'Trifluoroethanol-d3 (broad)', + value: 126.28, + label: 'Trifluoroethanol-d3' +}; +const cC6D5Cl1 = { + name: 'C6D5Cl (s)', + value: 134.19, + label: 'C6D5Cl' +}; +const cC6D5Cl2 = { + name: 'C6D5Cl (t)-1', + value: 129.26, + label: 'C6D5Cl' +}; +const cC6D5Cl3 = { + name: 'C6D5Cl (t)-2', + value: 128.25, + label: 'C6D5Cl' +}; +const cC6D5Cl4 = { + name: 'C6D5Cl (t)-3', + value: 125.96, + label: 'C6D5Cl' +}; +const LIST_SHIFT_13C = exports.LIST_SHIFT_13C = [noReference, cActicAcidD4Sept, cActicAcidD4S, cAcetoneD6Sep, cAcetoneD6Broad, cAcetonitrileD3Sep, cAcetonitrileD3S, cBenzeneT, cChloroformDT, cCyclohexaneD12Quin, cDichloromethaneD2Quin, cDmfD7Sep1, cDmfD7Sep2, cDmfD7T3, cDioxaneD8Quin, cDmsoD6, cEthanolD6Sep, cEthanolD6Quin, cMethanolD4Sep, cPyridineD5T1, cPyridineD5T2, cPyridineD5T3, cThfD8Quin1, cThfD8Quin2, cTmsS, cTolueneD8Sep1, cTolueneD8T2, cTolueneD8T3, cTolueneD8T4, cTolueneD8T5, cTfaDQ1, cTfaDQ2, cTrifluoroethanolD3Quin, cTrifluoroethanolD3Broad, cC6D5Cl1, cC6D5Cl2, cC6D5Cl3, cC6D5Cl4]; +const hActicAcidD4Quin = { + name: 'Acetic acid-d4 (quin)', + value: 2.04, + label: 'Acetic acid-d4' +}; +const hActicAcidD4S = { + name: 'Acetic acid-d4 (s)', + value: 11.65, + label: 'Acetic acid-d4' +}; +const hAcetoneD6Quin = { + name: 'Acetone-d6 (quin)', + value: 2.05, + label: 'Acetone-d6', + nsdb: 'Acetone-D6 ((CD3)2CO)' +}; +const hAcetonitrileD3Qquin = { + name: 'Acetonitrile-d3 (quin)', + value: 1.94, + label: 'Acetonitrile-d3', + nsdb: 'Acetonitrile-D3(CD3CN)' +}; +const hBenzeneS = { + name: 'Benzene (s)', + value: 7.16, + label: 'Benzene-d6', + nsdb: 'Benzene-D6 (C6D6)' +}; +const hChloroformDS = { + name: 'Chloroform-d (s)', + value: 7.26, + label: 'CDCl$3', + nsdb: 'Chloroform-D1 (CDCl3)' +}; +const hCyclohexaneD12S = { + name: 'Cyclohexane-d12 (s)', + value: 1.38, + label: 'C$6D$1$2' +}; +const hDeuteriumOxideS = { + name: 'D2O (s)', + value: 4.79, + label: 'D$2O', + nsdb: 'Deuteriumoxide (D2O)' +}; +const hDichloroethaneD4S = { + name: 'Dichloroethane-d4 (s)', + value: 3.72, + label: 'Dichloroethane-d4' +}; +const hDichloromethaneD2T = { + name: 'Dichloromethane-d2 (t)', + value: 5.32, + label: 'CD2Cl2', + nsdb: 'Methylenchloride-D2 (CD2Cl2)' +}; +const hDMFD7Quin1 = { + name: 'DMF-d7 (quin)-1', + value: 2.75, + label: 'DMF-d7' +}; +const hDMFD7Quin2 = { + name: 'DMF-d7 (quin)-2', + value: 2.92, + label: 'DMF-d7' +}; +const hDMFD7Broad3 = { + name: 'DMF-d7 (broad)-3', + value: 8.03, + label: 'DMF-d7' +}; +const hDioxaneD8Broad = { + name: 'Dioxane-d8 (broad)', + value: 3.53, + label: 'Dioxane-d8' +}; +const hDMSOD6Quin = { + name: 'DMSO-d6 (quin)', + value: 2.50, + label: 'DMSO-d6', + nsdb: 'Dimethylsulphoxide-D6 (DMSO-D6, C2D6SO)' +}; +const hEthanolD6Broad1 = { + name: 'Ethanol-d6 (broad)-1', + value: 1.11, + label: 'Ethanol-d6' +}; +const hEthanolD6S2 = { + name: 'Ethanol-d6 (s)-2', + value: 3.56, + label: 'Ethanol-d6' +}; +const hEthanolD6S3 = { + name: 'Ethanol-d6 (s)-3', + value: 5.29, + label: 'Ethanol-d6' +}; +const hMethanolD4Quin = { + name: 'Methanol-d4 (quin)', + value: 3.31, + label: 'Methanol-d4', + nsdb: 'Methanol-D4 (CD3OD)' +}; +const hMethanolD4S = { + name: 'Methanol-d4 (s)', + value: 4.87, + label: 'Methanol-d4', + nsdb: 'Methanol-D4 (CD3OD)' +}; +const hNitromethaneD3S = { + name: 'Nitromethane-d3 (s)', + value: 4.33, + label: 'Nitromethane-d3' +}; +const hPyridineD5Broad1 = { + name: 'Pyridine-d5 (broad)-1', + value: 7.22, + label: 'Pyridine-d5', + nsdb: 'Pyridin-D5 (C5D5N)' +}; +const hPyridineD5Broad2 = { + name: 'Pyridine-d5 (broad)-2', + value: 7.58, + label: 'Pyridine-d5', + nsdb: 'Pyridin-D5 (C5D5N)' +}; +const hPyridineD5Broad3 = { + name: 'Pyridine-d5 (broad)-3', + value: 8.74, + label: 'Pyridine-d5', + nsdb: 'Pyridin-D5 (C5D5N)' +}; +const hTHFD8S1 = { + name: 'THF-d8 (s)-1', + value: 1.73, + label: 'THF-d8', + nsdb: 'Tetrahydrofuran-D8 (THF-D8, C4D4O)' +}; +const hTHFD8S2 = { + name: 'THF-d8 (s)-2', + value: 3.58, + label: 'THF-d8', + nsdb: 'Tetrahydrofuran-D8 (THF-D8, C4D4O)' +}; +const hTMSS = { + name: 'TMS (s)', + value: 0.0, + label: 'TMS' +}; +const hTolueneD8Quin = { + name: 'Toluene-d8 (quin)-1', + value: 2.09, + label: 'Toluene-d8' +}; +const hTolueneD8Boad2 = { + name: 'Toluene-d8 (boad)-2', + value: 6.98, + label: 'Toluene-d8' +}; +const hTolueneD8S3 = { + name: 'Toluene-d8 (s)-3', + value: 7.00, + label: 'Toluene-d8' +}; +const hTolueneD8Broad4 = { + name: 'Toluene-d8 (broad)-4', + value: 7.09, + label: 'Toluene-d8' +}; +const hTFADS = { + name: 'TFA-d (s)', + value: 11.5, + label: 'TFA-d' +}; +const hTrifluoroethanolD31 = { + name: 'Trifluoroethanol-d3-1', + value: 3.88, + label: 'Trifluoroethanol-d3' +}; +const hTrifluoroethanolD32 = { + name: 'Trifluoroethanol-d3-2', + value: 5.02, + label: 'Trifluoroethanol-d3' +}; +const LIST_SHIFT_1H = exports.LIST_SHIFT_1H = [noReference, hActicAcidD4Quin, hActicAcidD4S, hAcetoneD6Quin, hAcetonitrileD3Qquin, hBenzeneS, hChloroformDS, hCyclohexaneD12S, hDeuteriumOxideS, hDichloroethaneD4S, hDichloromethaneD2T, hDMFD7Quin1, hDMFD7Quin2, hDMFD7Broad3, hDioxaneD8Broad, hDMSOD6Quin, hEthanolD6Broad1, hEthanolD6S2, hEthanolD6S3, hMethanolD4Quin, hMethanolD4S, hNitromethaneD3S, hPyridineD5Broad1, hPyridineD5Broad2, hPyridineD5Broad3, hTHFD8S1, hTHFD8S2, hTMSS, hTolueneD8Quin, hTolueneD8Boad2, hTolueneD8S3, hTolueneD8Broad4, hTFADS, hTrifluoroethanolD31, hTrifluoroethanolD32]; +const LIST_SHIFT_19F = exports.LIST_SHIFT_19F = [noReference, hActicAcidD4Quin, hActicAcidD4S, hAcetoneD6Quin, hAcetonitrileD3Qquin, hBenzeneS, hChloroformDS, hCyclohexaneD12S, hDeuteriumOxideS, hDichloroethaneD4S, hDichloromethaneD2T, hDMFD7Quin1, hDMFD7Quin2, hDMFD7Broad3, hDioxaneD8Broad, hDMSOD6Quin, hEthanolD6Broad1, hEthanolD6S2, hEthanolD6S3, hMethanolD4Quin, hMethanolD4S, hNitromethaneD3S, hPyridineD5Broad1, hPyridineD5Broad2, hPyridineD5Broad3, hTHFD8S1, hTHFD8S2, hTMSS, hTolueneD8Quin, hTolueneD8Boad2, hTolueneD8S3, hTolueneD8Broad4, hTFADS, hTrifluoroethanolD31, hTrifluoroethanolD32]; +const LIST_SHIFT_31P = exports.LIST_SHIFT_31P = [noReference, hActicAcidD4Quin, hActicAcidD4S, hAcetoneD6Quin, hAcetonitrileD3Qquin, hBenzeneS, hChloroformDS, hCyclohexaneD12S, hDeuteriumOxideS, hDichloroethaneD4S, hDichloromethaneD2T, hDMFD7Quin1, hDMFD7Quin2, hDMFD7Broad3, hDioxaneD8Broad, hDMSOD6Quin, hEthanolD6Broad1, hEthanolD6S2, hEthanolD6S3, hMethanolD4Quin, hMethanolD4S, hNitromethaneD3S, hPyridineD5Broad1, hPyridineD5Broad2, hPyridineD5Broad3, hTHFD8S1, hTHFD8S2, hTMSS, hTolueneD8Quin, hTolueneD8Boad2, hTolueneD8S3, hTolueneD8Broad4, hTFADS, hTrifluoroethanolD31, hTrifluoroethanolD32]; +const LIST_SHIFT_15N = exports.LIST_SHIFT_15N = [noReference, hActicAcidD4Quin, hActicAcidD4S, hAcetoneD6Quin, hAcetonitrileD3Qquin, hBenzeneS, hChloroformDS, hCyclohexaneD12S, hDeuteriumOxideS, hDichloroethaneD4S, hDichloromethaneD2T, hDMFD7Quin1, hDMFD7Quin2, hDMFD7Broad3, hDioxaneD8Broad, hDMSOD6Quin, hEthanolD6Broad1, hEthanolD6S2, hEthanolD6S3, hMethanolD4Quin, hMethanolD4S, hNitromethaneD3S, hPyridineD5Broad1, hPyridineD5Broad2, hPyridineD5Broad3, hTHFD8S1, hTHFD8S2, hTMSS, hTolueneD8Quin, hTolueneD8Boad2, hTolueneD8S3, hTolueneD8Broad4, hTFADS, hTrifluoroethanolD31, hTrifluoroethanolD32]; +const LIST_SHIFT_29Si = exports.LIST_SHIFT_29Si = [noReference, hActicAcidD4Quin, hActicAcidD4S, hAcetoneD6Quin, hAcetonitrileD3Qquin, hBenzeneS, hChloroformDS, hCyclohexaneD12S, hDeuteriumOxideS, hDichloroethaneD4S, hDichloromethaneD2T, hDMFD7Quin1, hDMFD7Quin2, hDMFD7Broad3, hDioxaneD8Broad, hDMSOD6Quin, hEthanolD6Broad1, hEthanolD6S2, hEthanolD6S3, hMethanolD4Quin, hMethanolD4S, hNitromethaneD3S, hPyridineD5Broad1, hPyridineD5Broad2, hPyridineD5Broad3, hTHFD8S1, hTHFD8S2, hTMSS, hTolueneD8Quin, hTolueneD8Boad2, hTolueneD8S3, hTolueneD8Broad4, hTFADS, hTrifluoroethanolD31, hTrifluoroethanolD32]; +const getListShift = layoutSt => { + switch (layoutSt) { + case _list_layout.LIST_LAYOUT.H1: + return LIST_SHIFT_1H; + case _list_layout.LIST_LAYOUT.C13: + return LIST_SHIFT_13C; + case _list_layout.LIST_LAYOUT.F19: + return LIST_SHIFT_19F; + case _list_layout.LIST_LAYOUT.P31: + return LIST_SHIFT_31P; + case _list_layout.LIST_LAYOUT.N15: + return LIST_SHIFT_15N; + case _list_layout.LIST_LAYOUT.Si29: + return LIST_SHIFT_29Si; + default: + return []; + } +}; +exports.getListShift = getListShift; \ No newline at end of file diff --git a/dist/constants/list_ui.js b/dist/constants/list_ui.js new file mode 100644 index 00000000..2eb5549d --- /dev/null +++ b/dist/constants/list_ui.js @@ -0,0 +1,36 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.LIST_UI_VIEWER_TYPE = exports.LIST_UI_SWEEP_TYPE = exports.LIST_NON_BRUSH_TYPES = void 0; +const LIST_UI_VIEWER_TYPE = exports.LIST_UI_VIEWER_TYPE = { + SPECTRUM: 'spectrum', + ANALYSIS: 'analysis' +}; +const LIST_UI_SWEEP_TYPE = exports.LIST_UI_SWEEP_TYPE = { + ZOOMIN: 'zoom in', + ZOOMRESET: 'zoom reset', + INTEGRATION_ADD: 'integration add', + INTEGRATION_RM: 'integration remove', + INTEGRATION_REF: 'integration reference', + INTEGRATION_SET_REF: 'integration set ref', + INTEGRATION_SPLIT: 'integration split', + MULTIPLICITY_SWEEP_ADD: 'multiplicity sweep add', + MULTIPLICITY_ONE_CLICK: 'multiplicity one click', + MULTIPLICITY_ONE_RM: 'multiplicity one remove', + MULTIPLICITY_PEAK_ADD: 'multiplicity peak add', + MULTIPLICITY_PEAK_RM: 'multiplicity peak remove', + MULTIPLICITY_ALL_CLEAR: 'multiplicity all clear', + PEAK_ADD: 'peak add', + PEAK_DELETE: 'peak delete', + ANCHOR_SHIFT: 'anchor shift', + CYCLIC_VOLTA_ADD_MAX_PEAK: 'cyclic voltammetry add max peak', + CYCLIC_VOLTA_RM_MAX_PEAK: 'cyclic voltammetry remove max peak', + CYCLIC_VOLTA_ADD_MIN_PEAK: 'cyclic voltammetry add min peak', + CYCLIC_VOLTA_RM_MIN_PEAK: 'cyclic voltammetry remove min peak', + CYCLIC_VOLTA_ADD_PECKER: 'cyclic voltammetry add pecker', + CYCLIC_VOLTA_RM_PECKER: 'cyclic voltammetry remove pecker', + CYCLIC_VOLTA_SET_REF: 'cyclic voltammetry set ref' +}; +const LIST_NON_BRUSH_TYPES = exports.LIST_NON_BRUSH_TYPES = [LIST_UI_SWEEP_TYPE.PEAK_ADD, LIST_UI_SWEEP_TYPE.PEAK_DELETE, LIST_UI_SWEEP_TYPE.ANCHOR_SHIFT, LIST_UI_SWEEP_TYPE.INTEGRATION_RM, LIST_UI_SWEEP_TYPE.INTEGRATION_SET_REF, LIST_UI_SWEEP_TYPE.INTEGRATION_SPLIT, LIST_UI_SWEEP_TYPE.MULTIPLICITY_PEAK_ADD, LIST_UI_SWEEP_TYPE.MULTIPLICITY_PEAK_RM, LIST_UI_SWEEP_TYPE.MULTIPLICITY_ONE_CLICK, LIST_UI_SWEEP_TYPE.MULTIPLICITY_ONE_RM, LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MAX_PEAK, LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MAX_PEAK, LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MIN_PEAK, LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MIN_PEAK, LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_PECKER, LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_PECKER, LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_SET_REF]; \ No newline at end of file diff --git a/dist/constants/list_wavelength.js b/dist/constants/list_wavelength.js new file mode 100644 index 00000000..2643b05d --- /dev/null +++ b/dist/constants/list_wavelength.js @@ -0,0 +1,31 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.LIST_WAVE_LENGTH = void 0; +const CuKalpha = { + name: 'CuKalpha', + value: 0.15406, + label: 'Cu K\u03B1', + unit: 'nm' +}; +const Fe = { + name: 'Fe', + value: 0.19373, + label: 'Fe', + unit: 'nm' +}; +const Co = { + name: 'Co', + value: 0.17902, + label: 'Co', + unit: 'nm' +}; +const MoKalpha = { + name: 'MoKalpha', + value: 0.07107, + label: 'Mo K\u03B1', + unit: 'nm' +}; +const LIST_WAVE_LENGTH = exports.LIST_WAVE_LENGTH = [CuKalpha, Fe, Co, MoKalpha]; \ No newline at end of file diff --git a/dist/fn.js b/dist/fn.js new file mode 100644 index 00000000..348a8fe8 --- /dev/null +++ b/dist/fn.js @@ -0,0 +1,23 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _format = _interopRequireDefault(require("./helpers/format")); +var _chem = require("./helpers/chem"); +var _converter = require("./helpers/converter"); +var _multiplicity_calc = require("./helpers/multiplicity_calc"); +var _carbonFeatures = require("./helpers/carbonFeatures"); +var _list_layout = require("./constants/list_layout"); +/* eslint-disable prefer-object-spread */ + +const FN = Object.assign({}, _format.default, { + ExtractJcamp: _chem.ExtractJcamp, + ToXY: _converter.ToXY, + LIST_LAYOUT: _list_layout.LIST_LAYOUT, + CalcMpyCenter: _multiplicity_calc.calcMpyCenter, + CarbonFeatures: _carbonFeatures.carbonFeatures +}); +var _default = exports.default = FN; \ No newline at end of file diff --git a/dist/helpers/brush.js b/dist/helpers/brush.js new file mode 100644 index 00000000..03994697 --- /dev/null +++ b/dist/helpers/brush.js @@ -0,0 +1,116 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _compass = require("./compass"); +var _integration_draft = require("./integration_draft.js"); +var _sweep = require("./sweep.js"); +/* eslint-disable prefer-object-spread */ + +// eslint-disable-line import/extensions +// eslint-disable-line import/extensions + +const d3 = require('d3'); +const selectBrushLayer = (focus, selector) => { + const svg = focus.svg || d3.select('.d3Svg'); + return svg.selectAll(selector); +}; +const wheeled = (focus, event) => { + const { + currentExtent, + scrollUiWheelAct + } = focus; + // WORKAROUND: firefox wheel compatibilty + const wheelEvent = focus.isFirefox ? -event.deltaY : event.wheelDelta; // eslint-disable-line + const direction = wheelEvent > 0; + scrollUiWheelAct(Object.assign({}, currentExtent, { + direction + })); +}; +const brushed = (focus, isUiAddIntgSt, event) => { + const { + selectUiSweepAct, + data, + dataPks, + brush, + brushX, + w, + h, + scales + } = focus; + const selection = event.selection && event.selection.reverse(); + if (!selection) return; + let xes = [w, 0].map(scales.x.invert).sort((a, b) => a - b); + let yes = [h, 0].map(scales.y.invert).sort((a, b) => a - b); + let xExtent = { + xL: xes[0], + xU: xes[1] + }; + let yExtent = { + yL: yes[0], + yU: yes[1] + }; + if (isUiAddIntgSt) { + const payload = (0, _sweep.buildSweepPayloadFromXBounds)(focus, scales.x.invert(selection[0]), scales.x.invert(selection[1])); + selectUiSweepAct(payload); + if (brushX) { + selectBrushLayer(focus, '.brushX').call(brushX.move, null); + } + return; + } + const [begPt, endPt] = selection; + xes = [begPt[0], endPt[0]].map(scales.x.invert).sort((a, b) => a - b); + yes = [begPt[1], endPt[1]].map(scales.y.invert).sort((a, b) => a - b); + xExtent = { + xL: xes[0], + xU: xes[1] + }; + yExtent = { + yL: yes[0], + yU: yes[1] + }; + selectUiSweepAct({ + xExtent, + yExtent, + data, + dataPks + }); + selectBrushLayer(focus, '.brush').call(brush.move, null); +}; +const MountBrush = (focus, isUiAddIntgSt, isUiNoBrushSt) => { + const { + root, + svg, + brush, + w, + h + } = focus; + svg.selectAll('.brush').remove(); + svg.selectAll('.brushX').remove(); + Object.assign(focus, { + isUiAddIntgSt + }); + const { + firstIntegrationPoint, + data, + jcampIdx + } = focus; + const isSameIntegrationDraft = firstIntegrationPoint && firstIntegrationPoint.jcampIdx === jcampIdx && firstIntegrationPoint.dataLength === data.length; + if (!isUiAddIntgSt || firstIntegrationPoint && !isSameIntegrationDraft) { + (0, _integration_draft.clearPendingIntegrationDraft)(); + Object.assign(focus, { + firstIntegrationPoint: null + }); + (0, _compass.clearIntegrationPreview)(focus); + } + const brushedCb = event => brushed(focus, isUiAddIntgSt, event); + const wheeledCb = event => wheeled(focus, event); + if (isUiNoBrushSt && !isUiAddIntgSt) { + brush.handleSize(10).extent([[0, 0], [w, h]]).on('end', brushedCb); + root.append('g').attr('class', 'brush').on('mousemove', event => (0, _compass.MouseMove)(event, focus)).call(brush); + } + svg.on('wheel', wheeledCb); +}; +var _default = exports.default = MountBrush; \ No newline at end of file diff --git a/dist/helpers/calc.js b/dist/helpers/calc.js new file mode 100644 index 00000000..664ffdf8 --- /dev/null +++ b/dist/helpers/calc.js @@ -0,0 +1,15 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.calcSlope = exports.almostEqual = void 0; +const almostEqual = (a, b) => Math.abs(a - b) < 0.00000001 * Math.abs(a + b); +exports.almostEqual = almostEqual; +const calcSlope = (x1, y1, x2, y2) => { + if (x2 === x1) { + return 0; + } + return (y2 - y1) / (x2 - x1); +}; +exports.calcSlope = calcSlope; \ No newline at end of file diff --git a/dist/helpers/carbonFeatures.js b/dist/helpers/carbonFeatures.js new file mode 100644 index 00000000..3c97f566 --- /dev/null +++ b/dist/helpers/carbonFeatures.js @@ -0,0 +1,51 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.carbonFeatures = void 0; +var _multiplicity_calc = require("./multiplicity_calc"); +const carbonFeatures = (peaksEdit, multiplicitySt) => { + const { + selectedIdx, + multiplicities + } = multiplicitySt; + const selectedMultiplicity = multiplicities[selectedIdx]; + const { + stack, + shift + } = selectedMultiplicity; + const nmrMpyCenters = stack.map(stk => { + // eslint-disable-line + return { + x: (0, _multiplicity_calc.calcMpyCenter)(stk.peaks, shift, stk.mpyType), + y: 0 + }; + }); + let targetIdxs = []; + stack.forEach(stk => { + // find peak idxs to be removed + stk.peaks.forEach(p => { + let targetIdx = -1; + let minDiff = 999999999; + peaksEdit.forEach((pe, idx) => { + const xDiff = Math.abs(pe.x - p.x); + if (xDiff < minDiff) { + targetIdx = idx; + minDiff = xDiff; + } + }); + targetIdxs = [...targetIdxs, targetIdx]; + }); + }); + let features = [...nmrMpyCenters]; + peaksEdit.forEach((pe, idx) => { + if (targetIdxs.indexOf(idx) < 0) { + features = [...features, pe]; + } + }); + return features; +}; + +// eslint-disable-line +exports.carbonFeatures = carbonFeatures; \ No newline at end of file diff --git a/dist/helpers/cfg.js b/dist/helpers/cfg.js new file mode 100644 index 00000000..4cc3b51c --- /dev/null +++ b/dist/helpers/cfg.js @@ -0,0 +1,64 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _format = _interopRequireDefault(require("./format")); +const btnCmdAnaViewer = layoutSt => _format.default.isMsLayout(layoutSt) || _format.default.isRamanLayout(layoutSt) || _format.default.is19FLayout(layoutSt) || _format.default.isUvVisLayout(layoutSt) || _format.default.isHplcUvVisLayout(layoutSt) || _format.default.isTGALayout(layoutSt) || _format.default.isDSCLayout(layoutSt) || _format.default.isXRDLayout(layoutSt) || _format.default.is31PLayout(layoutSt) || _format.default.is15NLayout(layoutSt) || _format.default.is29SiLayout(layoutSt) || _format.default.isCyclicVoltaLayout(layoutSt) || _format.default.isCDSLayout(layoutSt) || _format.default.isSECLayout(layoutSt) || _format.default.isGCLayout(layoutSt); +const hideCmdAnaViewer = () => false; +const btnCmdAddPeak = layoutSt => _format.default.isMsLayout(layoutSt); +const btnCmdRmPeak = layoutSt => _format.default.isMsLayout(layoutSt); +const btnCmdSetRef = layoutSt => !_format.default.isNmrLayout(layoutSt); // eslint-disable-line + +const btnCmdIntg = layoutSt => !(_format.default.isNmrLayout(layoutSt) || _format.default.isHplcUvVisLayout(layoutSt)); // eslint-disable-line + +const btnCmdMpy = layoutSt => !_format.default.isNmrLayout(layoutSt); +const btnCmdMpyPeak = (layoutSt, mpySt, curveIdx = 0) => { + const { + multiplicities + } = mpySt; + let smExtextVal = false; + if (multiplicities) { + const selectedMultiplicity = multiplicities[curveIdx]; + if (selectedMultiplicity) { + const { + smExtext + } = selectedMultiplicity; + smExtextVal = smExtext; + } + } + return btnCmdMpy(layoutSt) || !smExtextVal; +}; +const hideCmdThres = layoutSt => _format.default.isMsLayout(layoutSt); +const btnCmdThres = thresVal => !thresVal; + +// const hidePanelPeak = layoutSt => Format.isMsLayout(layoutSt); +const hidePanelPeak = layoutSt => !_format.default.isSECLayout(layoutSt); // eslint-disable-line + +const hidePanelMpy = layoutSt => !_format.default.isNmrLayout(layoutSt); +const hidePanelCompare = layoutSt => !(_format.default.isIrLayout(layoutSt) || _format.default.isHplcUvVisLayout(layoutSt) || _format.default.isXRDLayout(layoutSt)); // eslint-disable-line + +const hideSolvent = layoutSt => !_format.default.isNmrLayout(layoutSt); +const showTwoThreshold = layoutSt => _format.default.isCyclicVoltaLayout(layoutSt); +const hidePanelCyclicVolta = layoutSt => !_format.default.isCyclicVoltaLayout(layoutSt); +const Config = { + btnCmdAnaViewer, + hideCmdAnaViewer, + btnCmdAddPeak, + btnCmdRmPeak, + btnCmdSetRef, + btnCmdIntg, + btnCmdMpy, + btnCmdMpyPeak, + hideCmdThres, + btnCmdThres, + hidePanelPeak, + hidePanelMpy, + hidePanelCompare, + hideSolvent, + showTwoThreshold, + hidePanelCyclicVolta +}; +var _default = exports.default = Config; \ No newline at end of file diff --git a/dist/helpers/chem.js b/dist/helpers/chem.js new file mode 100644 index 00000000..65dd81ef --- /dev/null +++ b/dist/helpers/chem.js @@ -0,0 +1,935 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.convertTopic = exports.Topic2Seed = exports.ToThresEndPts = exports.ToShiftPeaks = exports.ToFrequency = exports.GetCyclicVoltaShiftOffset = exports.GetCyclicVoltaRatio = exports.GetCyclicVoltaPreviousShift = exports.GetCyclicVoltaPeakSeparate = exports.GetComparisons = exports.Feature2Peak = exports.Feature2MaxMinPeak = exports.ExtractJcamp = exports.Convert2Thres = exports.Convert2Scan = exports.Convert2Peak = exports.Convert2MaxMinPeak = exports.Convert2DValue = 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"); +/* eslint-disable +no-mixed-operators, react/function-component-definition, +prefer-object-spread, camelcase, no-plusplus, prefer-destructuring, +max-len */ + +const getTopic = (_, props) => props.topic; +const getFeature = (_, props) => props.feature; +const getLayout = (state, _) => state.layout; // eslint-disable-line + +const GetCyclicVoltaShiftOffset = (volammetryData = null, curveIdx = 0) => { + if (!volammetryData) return 0.0; + const { + spectraList + } = volammetryData; + const spectra = spectraList[curveIdx]; + if (!spectra) return 0.0; + const { + shift + } = spectra; + const { + ref, + val + } = shift; + if (!ref) return 0.0; + const { + e12 + } = ref; + return e12 - val; +}; +exports.GetCyclicVoltaShiftOffset = GetCyclicVoltaShiftOffset; +const getShiftOffset = (state, _) => { + // eslint-disable-line + const { + curve, + layout, + cyclicvolta + } = state; + const { + curveIdx + } = curve; + 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); +}; +const calcXYK = (xs, ys, maxY, offset) => { + const sp = []; + let k = 0; + for (let i = 0; i < ys.length; i += 1) { + // no-downsample + const x = xs[i] - offset; + const y = ys[i]; + const cy = y / maxY; + if (cy > 0.0) { + k += cy; + } + sp.push({ + x, + y, + k + }); + } + return sp; +}; +const calcXY = (xs, ys, maxY, offset) => { + const sp = []; + for (let i = 0; i < ys.length; i += 1) { + // no-downsample + const x = xs[i] - offset; + const y = ys[i]; + sp.push({ + x, + y + }); + } + return sp; +}; +const convertTopic = (topic, layout, feature, offset) => { + const { + maxY + } = feature; + const xs = topic.x; + const ys = topic.y; + const isItgDisable = _cfg.default.btnCmdIntg(layout); + if (!isItgDisable) return calcXYK(xs, ys, maxY, offset); + return calcXY(xs, ys, maxY, offset); +}; +exports.convertTopic = convertTopic; +const Topic2Seed = exports.Topic2Seed = (0, _reselect.createSelector)(getTopic, getLayout, getFeature, getShiftOffset, convertTopic); +const getOthers = (_, props) => props.comparisons; +const calcRescaleXY = (xs, ys, minY, maxY, show) => { + const sp = []; + if (xs.length < 1) return sp; + const [lowerY, upperY] = [Math.min(...ys), Math.max(...ys)]; + const faktor = (maxY - minY) / (upperY - lowerY); + for (let i = 0; i < ys.length; i += 2) { + // downsample + const x = xs[i]; + const y = (ys[i] - lowerY) * faktor + minY; + sp.push({ + x, + y + }); + } + return { + data: sp, + show + }; +}; +const convertComparisons = (layout, comparisons, feature) => { + const { + minY, + maxY + } = feature; + if (!comparisons || !(_format.default.isIrLayout(layout) || _format.default.isHplcUvVisLayout(layout) || _format.default.isXRDLayout(layout))) return []; + return comparisons.map(c => { + const { + spectra, + show + } = c; + const topic = spectra[0].data[0]; + const xs = topic.x; + const ys = topic.y; + return calcRescaleXY(xs, ys, minY, maxY, show); + }); +}; +const GetComparisons = exports.GetComparisons = (0, _reselect.createSelector)(getLayout, getOthers, getFeature, convertComparisons); +const convertFrequency = (layout, feature) => { + if (['1H', '13C', '19F', '31P', '15N', '29Si'].indexOf(layout) < 0) return false; + const { + observeFrequency + } = feature; + const freq = Array.isArray(observeFrequency) ? observeFrequency[0] : observeFrequency; + return parseFloat(freq) || false; +}; +const ToFrequency = exports.ToFrequency = (0, _reselect.createSelector)(getLayout, getFeature, convertFrequency); +const getThreshold = state => state.threshold ? state.threshold.list[state.curve.curveIdx].value * 1.0 : false; +const Convert2Peak = (feature, threshold, offset, upThreshold = false, lowThreshold = false) => { + const peak = []; + if (!feature || !feature.data) return peak; + const data = feature.data[0]; + const { + maxY, + peakUp, + thresRef, + minY, + upperThres, + lowerThres, + operation + } = feature; + const { + layout + } = operation; + + // if (!Format.isSECLayout(layout) && (upperThres || lowerThres)) { + if ((_format.default.isCyclicVoltaLayout(layout) || _format.default.isCDSLayout(layout)) && (upperThres || lowerThres)) { + let upperThresVal = upThreshold || upperThres; + if (!upperThresVal) { + upperThresVal = 1.0; + } + let lowerThresVal = lowThreshold || lowerThres; + if (!lowerThresVal) { + lowerThresVal = 1.0; + } + const yUpperThres = parseFloat(upperThresVal) / 100.0 * maxY; + const yLowerThres = parseFloat(lowerThresVal) / 100.0 * minY; + const corrOffset = offset || 0.0; + for (let i = 0; i < data.y.length; i += 1) { + const y = data.y[i]; + const overUpperThres = y >= yUpperThres; + const belowThres = y <= yLowerThres; + if (overUpperThres || belowThres) { + const x = data.x[i] - corrOffset; + peak.push({ + x, + y + }); + } + } + return peak; + } + const thresVal = threshold || thresRef; + const yThres = Number.parseFloat((thresVal * maxY / 100.0).toFixed(10)); + const corrOffset = offset || 0.0; + for (let i = 0; i < data.y.length; i += 1) { + const y = data.y[i]; + const overThres = peakUp && Math.abs(y) >= yThres || !peakUp && Math.abs(y) <= yThres; + if (overThres) { + const x = data.x[i] - corrOffset; + peak.push({ + x, + y + }); + } + } + return peak; +}; +exports.Convert2Peak = Convert2Peak; +const Feature2Peak = exports.Feature2Peak = (0, _reselect.createSelector)(getFeature, getThreshold, getShiftOffset, Convert2Peak); +const Convert2MaxMinPeak = (layout, feature, offset) => { + // eslint-disable-line + const peaks = { + max: [], + min: [], + pecker: [], + refIndex: -1 + }; + if (!_format.default.isCyclicVoltaLayout(layout) || !feature || !feature.data) return null; // eslint-disable-line + // const data = feature.data[0]; // eslint-disable-line + const { + volammetryData + } = feature; + if (volammetryData && volammetryData.length > 0) { + const maxArr = volammetryData.map(peakData => { + // peaks.refIndex = peakData.isRef === true ? idx : -1; + if (peakData.max.x === '') return null; + return { + x: Number(peakData.max.x), + y: Number(peakData.max.y) + }; + }); + const minArr = volammetryData.map(peakData => { + if (peakData.min.x === '') return null; + return { + x: Number(peakData.min.x), + y: Number(peakData.min.y) + }; + }); + const peckerArr = volammetryData.map(peakData => { + if (peakData.pecker.x === '') return null; + return { + x: Number(peakData.pecker.x), + y: Number(peakData.pecker.y) + }; + }); + const refIndex = volammetryData.findIndex(peakData => peakData.isRef === true); + peaks.max = maxArr; + peaks.min = minArr; + peaks.pecker = peckerArr; + peaks.refIndex = refIndex; + return peaks; + } + return peaks; +}; +exports.Convert2MaxMinPeak = Convert2MaxMinPeak; +const Feature2MaxMinPeak = exports.Feature2MaxMinPeak = (0, _reselect.createSelector)(getLayout, getFeature, getShiftOffset, Convert2MaxMinPeak); +const convertThresEndPts = (feature, threshold) => { + const { + maxY, + maxX, + minX, + thresRef + } = feature; + const thresVal = threshold || thresRef || 0; + if (!thresVal || !feature.data) return []; + const yThres = thresVal * maxY / 100.0; + const endPts = [{ + x: minX - 200, + y: yThres + }, { + x: maxX + 200, + y: yThres + }]; + return endPts; +}; +const ToThresEndPts = exports.ToThresEndPts = (0, _reselect.createSelector)(getFeature, getThreshold, convertThresEndPts); +const getShiftPeak = state => { + const { + curve, + shift + } = state; + const { + curveIdx + } = curve; + const { + shifts + } = shift; + const selectedShift = shifts[curveIdx]; + if (!selectedShift) { + return false; + } + return selectedShift.peak; +}; +const convertSfPeaks = (peak, offset) => { + if (!peak || !peak.x) return []; + return [{ + x: peak.x - offset, + y: peak.y + }]; +}; +const ToShiftPeaks = exports.ToShiftPeaks = (0, _reselect.createSelector)(getShiftPeak, getShiftOffset, convertSfPeaks); + +// - - - - - - - - - - - - - - - - - - - - - - +// ExtractJcamp +// - - - - - - - - - - - - - - - - - - - - - - +const readLayout = jcamp => { + const { + xType, + spectra + } = jcamp; + if (xType && _format.default.isNmrLayout(xType)) return xType; + const { + dataType + } = spectra[0]; + if (dataType) { + if (dataType.includes('INFRARED SPECTRUM')) { + return _list_layout.LIST_LAYOUT.IR; + } + if (dataType.includes('RAMAN SPECTRUM')) { + return _list_layout.LIST_LAYOUT.RAMAN; + } + if (dataType.includes('UV/VIS SPECTRUM')) { + if (dataType.includes('HPLC')) { + return _list_layout.LIST_LAYOUT.HPLC_UVVIS; + } + return _list_layout.LIST_LAYOUT.UVVIS; + } + if (dataType.includes('THERMOGRAVIMETRIC ANALYSIS')) { + return _list_layout.LIST_LAYOUT.TGA; + } + if (dataType.includes('DIFFERENTIAL SCANNING CALORIMETRY')) { + return _list_layout.LIST_LAYOUT.DSC; + } + if (dataType.includes('X-RAY DIFFRACTION')) { + return _list_layout.LIST_LAYOUT.XRD; + } + if (dataType.includes('MASS SPECTRUM')) { + return _list_layout.LIST_LAYOUT.MS; + } + if (dataType.includes('CYCLIC VOLTAMMETRY')) { + return _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY; + } + if (dataType.includes('CIRCULAR DICHROISM SPECTROSCOPY')) { + return _list_layout.LIST_LAYOUT.CDS; + } + if (dataType.includes('SIZE EXCLUSION CHROMATOGRAPHY')) { + return _list_layout.LIST_LAYOUT.SEC; + } + if (dataType.includes('GAS CHROMATOGRAPHY')) { + return _list_layout.LIST_LAYOUT.GC; + } + if (dataType.includes('SORPTION-DESORPTION MEASUREMENT')) { + return _list_layout.LIST_LAYOUT.AIF; + } + if (dataType.includes('Emissions')) { + return _list_layout.LIST_LAYOUT.EMISSIONS; + } + if (dataType.includes('DLS ACF')) { + return _list_layout.LIST_LAYOUT.DLS_ACF; + } + if (dataType.includes('DLS intensity')) { + return _list_layout.LIST_LAYOUT.DLS_INTENSITY; + } + } + return false; +}; +const extrSpectraShare = (spectra, layout) => spectra.map(s => Object.assign({ + layout +}, s)).filter(r => r != null); +const extrSpectraMs = (jcamp, layout) => { + const scanCount = jcamp.info.$CSSCANCOUNT || 1; + const spc = extrSpectraShare(jcamp.spectra.slice(0, scanCount), layout); + let spectra = spc || []; + if (jcamp.info.UNITS && jcamp.info.SYMBOL) { + const units = jcamp.info.UNITS.split(','); + const symbol = jcamp.info.SYMBOL.split(','); + let xUnit = null; + let yUnit = null; + symbol.forEach((sym, idx) => { + const currSymbol = sym.replace(' ', '').toLowerCase(); + if (currSymbol === 'x') { + xUnit = units[idx].trim(); + } else if (currSymbol === 'y') { + yUnit = units[idx].trim(); + } + }); + spectra = spectra.map(sp => { + const spectrum = sp; + if (xUnit) { + spectrum.xUnit = xUnit; + } + if (yUnit) { + spectrum.yUnit = yUnit; + } + return spectrum; + }); + } + return spectra; +}; +const extrSpectraNi = (jcamp, layout) => { + const categorys = jcamp.info.$CSCATEGORY || ['SPECTRUM']; + const targetIdx = categorys.indexOf('SPECTRUM'); + const spectrum = extrSpectraShare(jcamp.spectra, layout)[targetIdx]; + return [spectrum] || [jcamp.spectra[0]]; +}; +const calcThresRef = (s, peakUp) => { + const ys = s && s.data[0].y; + if (!ys) return null; + const ref = peakUp ? Math.min(...ys.map(a => Math.abs(a))) : Math.max(...ys); + return peakUp ? Math.floor(ref * 100 * 100 / s.maxY) / 100 : Math.ceil(ref * 100 * 100 / s.maxY) / 100; +}; +const calcUpperThres = s => { + const ys = s && s.data[0].y; + if (!ys) return null; + const ref = Math.max(...ys); + return Math.floor(ref * 100 * 100 / s.maxY) / 100; +}; +const calcLowerThres = s => { + const ys = s && s.data[0].y; + if (!ys) return null; + const ref = Math.min(...ys); + return Math.ceil(ref * 100 * 100 / s.minY) / 100; +}; +const extractShift = (s, jcamp) => { + const shift = { + selectX: false, + solventName: false, + solventValue: false + }; + if (!s) return shift; + if (s && s.sampleDescription) { + const desc = s.sampleDescription; + const info = desc.split(/;|=/); + return { + selectX: parseFloat(info[1]), + solventName: info[3], + solventValue: parseFloat(info[5]) + }; + } + return { + selectX: parseFloat(jcamp.info.$CSSOLVENTX) || false, + solventName: jcamp.info.$CSSOLVENTNAME || false, + solventValue: parseFloat(jcamp.info.$CSSOLVENTVALUE) || false + }; +}; +const extractVoltammetryData = jcamp => { + const { + info + } = jcamp; + if (!info.$CSCYCLICVOLTAMMETRYDATA) return null; + const regx = /[^0-9.,E,e,-]/g; + const rawData = info.$CSCYCLICVOLTAMMETRYDATA.split('\n'); + const peakStack = rawData.map(line => { + const splittedLine = line.replace(regx, '').split(','); + const isRef = splittedLine.length > 8 && splittedLine[8] === '1'; + return { + max: { + x: splittedLine[0], + y: splittedLine[1] + }, + min: { + x: splittedLine[2], + y: splittedLine[3] + }, + ratio: splittedLine[4], + delta: splittedLine[5], + pecker: { + x: splittedLine[6], + y: splittedLine[7] + }, + isRef + }; + }); + return peakStack; +}; +const buildPeakFeature = (jcamp, layout, peakUp, s, thresRef, upperThres = false, lowerThres = false) => { + // eslint-disable-line + const { + xType, + info + } = jcamp; + const subTyp = xType ? ` - ${xType}` : ''; + return Object.assign({ + typ: s.dataType + subTyp, + peakUp, + thresRef, + scanCount: +info.$CSSCANCOUNT, + scanAutoTarget: +info.$CSSCANAUTOTARGET, + scanEditTarget: +info.$CSSCANEDITTARGET, + shift: extractShift(s, jcamp), + operation: { + layout, + nucleus: xType || '' + }, + observeFrequency: info['.OBSERVEFREQUENCY'], + solventName: info['.SOLVENTNAME'], + upperThres, + lowerThres, + volammetryData: extractVoltammetryData(jcamp), + scanRate: +info.$CSSCANRATE || 0.1, + weAreaValue: info.$CSWEAREAVALUE || '', + weAreaUnit: info.$CSWEAREAUNIT || '', + currentMode: info.$CSCURRENTMODE || '' + }, s); +}; +const maxArray = arr => { + let len = arr.length; + let max = -Infinity; + while (len--) { + max = arr[len] > max ? arr[len] : max; + } + return max; +}; +const calcIntgRefArea = (spectra, stack) => { + if (stack.length === 0) return 1; + const data = spectra[0].data[0]; + const xs = data.x; + const ys = data.y; + const maxY = maxArray(ys); + const xyk = calcXYK(xs, ys, maxY, 0); + const { + xL, + xU, + area + } = stack[0]; + const rawArea = (0, _integration.getArea)(xL, xU, xyk); + const raw2realRatio = rawArea / area; + return { + raw2realRatio + }; +}; +const buildIntegFeature = (jcamp, spectra) => { + const { + $OBSERVEDINTEGRALS, + $OBSERVEDMULTIPLETS + } = jcamp.info; + const regx = /[^0-9.,-]/g; + let stack = []; + if ($OBSERVEDINTEGRALS) { + const its = $OBSERVEDINTEGRALS.split('\n').slice(1); + const itStack = its.map(t => { + const ts = t.replace(regx, '').split(','); + return { + xL: parseFloat(ts[0]), + xU: parseFloat(ts[1]), + area: parseFloat(ts[2]), + absoluteArea: parseFloat(ts[3]) + }; + }); + stack = [...stack, ...itStack]; + } + if ($OBSERVEDMULTIPLETS) { + const mps = $OBSERVEDMULTIPLETS.split('\n'); + const mpStack = mps.map(m => { + const ms = m.replace(regx, '').split(','); + return { + xL: parseFloat(ms[1]), + xU: parseFloat(ms[2]), + area: parseFloat(ms[4]) + }; + }); + stack = [...stack, ...mpStack]; + } + const { + raw2realRatio + } = calcIntgRefArea(spectra, stack); + const mStack = stack.map(st => Object.assign({}, st, { + area: st.area * raw2realRatio + })); + return { + refArea: raw2realRatio, + refFactor: 1, + shift: 0, + stack: mStack, + originStack: stack + }; +}; + +/* +const range = (head, tail, length) => { + const actTail = tail || length - 1; + return ( + Array(actTail - head + 1).fill().map((_, idx) => head + idx) + ); +}; +*/ + +const buildSimFeature = jcamp => { + const { + $CSSIMULATIONPEAKS + } = jcamp.info; + let nmrSimPeaks = $CSSIMULATIONPEAKS ? $CSSIMULATIONPEAKS.split('\n') : []; + nmrSimPeaks = nmrSimPeaks.map(x => parseFloat(x).toFixed(2)); + return { + nmrSimPeaks + }; +}; +const buildMpyFeature = jcamp => { + const { + $OBSERVEDMULTIPLETS, + $OBSERVEDMULTIPLETSPEAKS + } = jcamp.info; + const regx = /[^A-Za-z0-9.,-]/g; + const regxNum = /[^0-9.]/g; + let stack = []; + if (!$OBSERVEDMULTIPLETSPEAKS) return { + stack: [] + }; + const allPeaks = $OBSERVEDMULTIPLETSPEAKS.split('\n').map(p => p.replace(regx, '').split(',')); + if ($OBSERVEDMULTIPLETS) { + const mp = $OBSERVEDMULTIPLETS.split('\n'); + const mpStack = mp.map(m => { + const ms = m.replace(regx, '').split(','); + const idx = ms[0]; + let ys = []; + const peaks = allPeaks.map(p => { + if (p[0] === idx) { + ys = [...ys, parseFloat(p[2])]; + return { + x: parseFloat(p[1]), + y: parseFloat(p[2]) + }; + } + return null; + }).filter(r => r != null); + let js = m.split(','); + js = js[js.length - 1].split(' ').map(j => parseFloat(j.replace(regxNum, ''))).filter(Boolean); + return { + js, + mpyType: ms[6], + xExtent: { + xL: parseFloat(ms[1]), + xU: parseFloat(ms[2]) + }, + yExtent: { + yL: Math.min(...ys), + yU: Math.max(...ys) + }, + peaks + }; + }); + stack = [...stack, ...mpStack]; + } + return { + stack, + shift: 0, + smExtext: false + }; +}; +const isPeakTable = s => s.dataType && (s.dataType.includes('PEAKTABLE') || s.dataType.includes('PEAK ASSIGNMENTS')); +const extrFeaturesNi = (jcamp, layout, peakUp, spectra) => { + const nfs = {}; + const category = jcamp.info.$CSCATEGORY; + if (category) { + const idxEditPeak = category.indexOf('EDIT_PEAK'); + if (idxEditPeak >= 0) { + const sEP = jcamp.spectra[idxEditPeak]; + const thresRef = calcThresRef(sEP, peakUp); + nfs.editPeak = buildPeakFeature(jcamp, layout, peakUp, sEP, thresRef); + } + const idxAutoPeak = category.indexOf('AUTO_PEAK'); + if (idxAutoPeak >= 0) { + const sAP = jcamp.spectra[idxAutoPeak]; + const thresRef = calcThresRef(sAP, peakUp); + nfs.autoPeak = buildPeakFeature(jcamp, layout, peakUp, sAP, thresRef); + } + nfs.integration = buildIntegFeature(jcamp, spectra); + nfs.multiplicity = buildMpyFeature(jcamp); + nfs.simulation = buildSimFeature(jcamp); + return nfs; + } + + // workaround for legacy design + const features = jcamp.spectra.map(s => { + const thresRef = calcThresRef(s, peakUp); + return isPeakTable(s) ? buildPeakFeature(jcamp, layout, peakUp, s, thresRef) : null; + }).filter(r => r != null); + const integration = buildIntegFeature(jcamp, spectra); + const multiplicity = buildMpyFeature(jcamp); + const simulation = buildSimFeature(jcamp); + return { + editPeak: features[0], + autoPeak: features[1], + integration, + multiplicity, + simulation + }; +}; +const getBoundary = s => { + const { + x, + y + } = s.data[0]; + const maxX = Math.max(...x); + const minX = Math.min(...x); + const maxY = Math.max(...y); + const minY = Math.min(...y); + return { + maxX, + minX, + maxY, + minY + }; +}; +const extrFeaturesXrd = (jcamp, layout, peakUp) => { + const base = jcamp.spectra[0]; + const features = jcamp.spectra.map(s => { + const upperThres = _format.default.isXRDLayout(layout) ? 100 : calcUpperThres(s); + const lowerThres = _format.default.isXRDLayout(layout) ? 100 : calcLowerThres(s); + const cpo = buildPeakFeature(jcamp, layout, peakUp, s, 100, upperThres, lowerThres); + const bnd = getBoundary(s); + return Object.assign({}, base, cpo, bnd); + }).filter(r => r != null); + const category = jcamp.info.$CSCATEGORY; + if (category) { + const idxEditPeak = category.indexOf('EDIT_PEAK'); + if (idxEditPeak >= 0) { + const sEP = jcamp.spectra[idxEditPeak]; + const thresRef = calcThresRef(sEP, peakUp); + features.editPeak = buildPeakFeature(jcamp, layout, peakUp, sEP, thresRef); + } + const idxAutoPeak = category.indexOf('AUTO_PEAK'); + if (idxAutoPeak >= 0) { + const sAP = jcamp.spectra[idxAutoPeak]; + const thresRef = calcThresRef(sAP, peakUp); + features.autoPeak = buildPeakFeature(jcamp, layout, peakUp, sAP, thresRef); + } + } + return features; +}; +const extrFeaturesCylicVolta = (jcamp, layout, peakUp) => { + const base = jcamp.spectra[0]; + const features = jcamp.spectra.map(s => { + const upperThres = _format.default.isXRDLayout(layout) ? 100 : calcUpperThres(s); + const lowerThres = _format.default.isXRDLayout(layout) ? 100 : calcLowerThres(s); + const cpo = buildPeakFeature(jcamp, layout, peakUp, s, 100, upperThres, lowerThres); + const bnd = getBoundary(s); + let detector = ''; + let secData = null; + if (_format.default.isSECLayout(layout)) { + const { + info + } = jcamp; + detector = info.$DETECTOR ? info.$DETECTOR : ''; + const { + D, + MN, + MP, + MW + } = info; + secData = { + d: D, + mn: MN, + mp: MP, + mw: MW + }; + } + return Object.assign({}, base, cpo, bnd, { + detector, + secData + }); + }).filter(r => r != null); + return features; +}; +const extrFeaturesMs = (jcamp, layout, peakUp) => { + // const nfs = {}; + // const category = jcamp.info.$CSCATEGORY; + // const scanCount = parseInt(jcamp.info.$CSSCANCOUNT, 10) - 1; + // if (category) { + // const idxEditPeak = category.indexOf('EDIT_PEAK'); + // if (idxEditPeak >= 0) { + // const sEP = jcamp.spectra[idxEditPeak + scanCount]; + // const thresRef = calcThresRef(sEP, peakUp); + // nfs.editPeak = buildPeakFeature(jcamp, layout, peakUp, sEP, thresRef); + // } + // const idxAutoPeak = category.indexOf('AUTO_PEAK'); + // if (idxAutoPeak >= 0) { + // const sAP = jcamp.spectra[idxAutoPeak + scanCount]; + // const thresRef = calcThresRef(sAP, peakUp); + // nfs.autoPeak = buildPeakFeature(jcamp, layout, peakUp, sAP, thresRef); + // } + // return nfs; + // } + // // workaround for legacy design + const thresRef = jcamp.info && jcamp.info.$CSTHRESHOLD * 100 || 5; + const base = jcamp.spectra[0]; + const features = jcamp.spectra.map(s => { + const cpo = buildPeakFeature(jcamp, layout, peakUp, s, +thresRef.toFixed(4)); + const bnd = getBoundary(s); + return Object.assign({}, base, cpo, bnd); + }).filter(r => r != null); + return features; +}; +const extractTemperature = jcamp => { + if ('$CSAUTOMETADATA' in jcamp.info) { + const match = jcamp.info.$CSAUTOMETADATA.match(/TEMPERATURE=([\d.]+)/); + if (match !== null) { + const temperature = match[1]; + return temperature; + } + } + return 'xxx'; +}; +const ExtractJcamp = source => { + const jcamp = _jcampconverter.default.convert(source, { + xy: true, + keepRecordsRegExp: /(\$CSTHRESHOLD|\$CSSCANAUTOTARGET|\$CSSCANEDITTARGET|\$CSSCANCOUNT|\$CSSOLVENTNAME|\$CSSOLVENTVALUE|\$CSSOLVENTX|\$CSCATEGORY|\$CSITAREA|\$CSITFACTOR|\$OBSERVEDINTEGRALS|\$OBSERVEDMULTIPLETS|\$OBSERVEDMULTIPLETSPEAKS|\.SOLVENTNAME|\.OBSERVEFREQUENCY|\$CSSIMULATIONPEAKS|\$CSUPPERTHRESHOLD|\$CSLOWERTHRESHOLD|\$CSCYCLICVOLTAMMETRYDATA|UNITS|SYMBOL|\$CSAUTOMETADATA|\$DETECTOR|MN|MW|D|MP|MELTINGPOINT|TG|\$CSSCANRATE|\$CSSPECTRUMDIRECTION|\$CSWEAREAVALUE|\$CSWEAREAUNIT|\$CSCURRENTMODE)/ // eslint-disable-line + }); + const layout = readLayout(jcamp); + const peakUp = !_format.default.isIrLayout(layout); + const spectra = _format.default.isMsLayout(layout) ? extrSpectraMs(jcamp, layout) : extrSpectraNi(jcamp, layout); + let features = {}; + if (_format.default.isMsLayout(layout)) { + features = extrFeaturesMs(jcamp, layout, peakUp); + } else if (_format.default.isXRDLayout(layout)) { + features = extrFeaturesXrd(jcamp, layout, peakUp); + const temperature = extractTemperature(jcamp); + return { + spectra, + features, + layout, + temperature + }; + } else if (_format.default.isCyclicVoltaLayout(layout) || _format.default.isSECLayout(layout) || _format.default.isAIFLayout(layout) || _format.default.isCDSLayout(layout) || _format.default.isGCLayout(layout)) { + features = extrFeaturesCylicVolta(jcamp, layout, peakUp); + } else { + features = extrFeaturesNi(jcamp, layout, peakUp, spectra); + if (_format.default.isDSCLayout(layout)) { + const { + info + } = jcamp; + const { + MELTINGPOINT, + TG + } = info; + const dscMetaData = { + meltingPoint: MELTINGPOINT, + tg: TG + }; + features = Object.assign({}, features, { + dscMetaData + }); + } + } + // const features = Format.isMsLayout(layout) + // ? extrFeaturesMs(jcamp, layout, peakUp) + // : ((Format.isXRDLayout(layout) || Format.isCyclicVoltaLayout(layout)) + // ? extrFeaturesXrd(jcamp, layout, peakUp) : extrFeaturesNi(jcamp, layout, peakUp, spectra)); + + return { + spectra, + features, + layout + }; +}; +exports.ExtractJcamp = ExtractJcamp; +const Convert2Scan = (feature, scanSt) => { + const { + scanAutoTarget, + scanEditTarget + } = feature; + const { + target, + isAuto + } = scanSt; + const hasEdit = !!scanEditTarget; + const defaultIdx = isAuto || !hasEdit ? scanAutoTarget : scanEditTarget; + return target || defaultIdx; +}; +exports.Convert2Scan = Convert2Scan; +const Convert2Thres = (feature, thresSt) => { + const value = thresSt.value || feature.thresRef; + return value; +}; +exports.Convert2Thres = Convert2Thres; +const Convert2DValue = (doubleTheta, lambda = 0.15406, isRadian = true) => { + let theta = doubleTheta / 2; + if (isRadian) { + theta = theta / 180 * Math.PI; + } + const sinTheta = Math.sin(theta); + const dValue = lambda / (2 * sinTheta); + return dValue; +}; +exports.Convert2DValue = Convert2DValue; +const GetCyclicVoltaRatio = (y_max_peak, y_min_peak, y_pecker) => { + const firstExpr = Math.abs(y_min_peak) / Math.abs(y_max_peak); + const secondExpr = 0.485 * Math.abs(y_pecker) / Math.abs(y_max_peak); + const ratio = firstExpr + secondExpr + 0.086; + return ratio; +}; +exports.GetCyclicVoltaRatio = GetCyclicVoltaRatio; +const GetCyclicVoltaPeakSeparate = (x_max_peak, x_min_peak) => { + const delta = Math.abs(x_max_peak - x_min_peak); + return delta; +}; +exports.GetCyclicVoltaPeakSeparate = GetCyclicVoltaPeakSeparate; +const GetCyclicVoltaPreviousShift = (cyclicVolta, curveIdx) => { + if (!cyclicVolta) { + return 0.0; + } + const { + spectraList + } = cyclicVolta; + if (spectraList.length <= curveIdx) { + return 0.0; + } + const { + shift, + hasRefPeak + } = spectraList[curveIdx]; + const { + prevValue + } = shift; + return hasRefPeak ? prevValue : -prevValue; +}; +exports.GetCyclicVoltaPreviousShift = GetCyclicVoltaPreviousShift; \ No newline at end of file diff --git a/dist/helpers/compass.js b/dist/helpers/compass.js new file mode 100644 index 00000000..502bb9b3 --- /dev/null +++ b/dist/helpers/compass.js @@ -0,0 +1,271 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getCurvePointFromEvent = exports.clearIntegrationPreview = exports.TfRescale = exports.MouseMove = exports.MountCompass = exports.ClickCompass = void 0; +var _format = _interopRequireDefault(require("./format")); +var _chem = require("./chem"); +var _sweep = require("./sweep.js"); +var _integration_draft = require("./integration_draft.js"); +var _integration_split = require("./integration_split"); +// eslint-disable-line import/extensions + +// eslint-disable-line import/extensions + +const d3 = require('d3'); +const TfRescale = focus => { + const xt = focus.scales.x; + const yt = focus.scales.y; + return { + xt, + yt + }; +}; +exports.TfRescale = TfRescale; +const fetchPt = (event, focus, xt) => { + const rawMouseX = d3.pointer(event, focus.root.node())[0]; + const mouseX = xt.invert(rawMouseX); + const bisectDate = d3.bisector(d => +d.x).left; + const dt = focus.data; + const ls = dt.length; + const sortData = ls > 0 && dt[0].x > dt[ls - 1].x ? [...dt].reverse() : dt; + const idx = bisectDate(sortData, +mouseX); + return sortData[Math.min(idx, ls - 1)]; +}; +const fetchFreePt = (event, focus, xt, yt) => { + const rawMouseX = d3.pointer(event, focus.root.node())[0]; + const rawMouseY = d3.pointer(event, focus.root.node())[1]; + const mouseX = xt.invert(rawMouseX); + const mouseY = yt.invert(rawMouseY); + const distance2 = (x1, x2, y1, y2) => { + const dx = x1 - x2; + const dy = y1 - y2; + return dx * dx + dy * dy; + }; + let minDistance = Number.MAX_VALUE; + const dt = focus.data; + let selectPoint = null; + dt.forEach(pt => { + const distance = distance2(pt.x, mouseX, pt.y, mouseY); + if (minDistance > distance) { + minDistance = distance; + selectPoint = pt; + } + }); + return selectPoint; +}; +const clearIntegrationPreview = focus => { + if (!focus || !focus.root) return; + focus.root.select('.integration-preview-line').remove(); +}; +exports.clearIntegrationPreview = clearIntegrationPreview; +const drawIntegrationPreview = (focus, firstPoint, nextPoint) => { + if (!firstPoint || !nextPoint) return; + const { + xt, + yt + } = TfRescale(focus); + const preview = focus.root.select('.integration-preview'); + const line = preview.selectAll('.integration-preview-line').data([{ + firstPoint, + nextPoint + }]); + line.enter().append('line').attr('class', 'integration-preview-line').attr('stroke', 'red').attr('stroke-width', 2).attr('stroke-dasharray', '4,3').style('pointer-events', 'none').merge(line).attr('x1', d => xt(d.firstPoint.x)).attr('y1', d => yt(d.firstPoint.y)).attr('x2', d => xt(d.nextPoint.x)).attr('y2', d => yt(d.nextPoint.y)); +}; +const getCurvePointFromEvent = (event, focus) => { + const { + xt, + yt + } = TfRescale(focus); + if (_format.default.isCyclicVoltaLayout(focus.layout)) { + return fetchFreePt(event, focus, xt, yt); + } + return fetchPt(event, focus, xt); +}; +exports.getCurvePointFromEvent = getCurvePointFromEvent; +const cancelIntegrationDraft = focus => { + Object.assign(focus, { + firstIntegrationPoint: null + }); + clearIntegrationPreview(focus); + (0, _integration_draft.forgetPendingIntegrationDraft)(); +}; +const updateIntegrationPreview = (event, focus) => { + if (!focus.isUiAddIntgSt || !focus.firstIntegrationPoint) return; + const pt = getCurvePointFromEvent(event, focus); + if (!pt) return; + drawIntegrationPreview(focus, focus.firstIntegrationPoint, pt); +}; +const updateIntegrationSplitPreview = (event, focus) => { + if (!focus.isUiSplitIntgSt) return; + const { + splitX, + target + } = (0, _integration_split.getIntegrationSplitTargetFromEvent)(event, focus); + if (!target) { + (0, _integration_split.clearIntegrationSplitPreview)(focus); + return; + } + const { + shift = 0, + ignoreRef = false + } = focus.integrationSplitTargets || {}; + (0, _integration_split.drawIntegrationSplitPreview)(focus, target, splitX, shift, ignoreRef); +}; +const MouseMove = (event, focus) => { + const { + xt, + yt + } = TfRescale(focus); + const { + freq, + layout, + wavelength + } = focus; + if (_format.default.isCyclicVoltaLayout(layout)) { + const pt = fetchFreePt(event, focus, xt, yt); + if (pt) { + const tx = xt(pt.x); + const ty = yt(pt.y); + focus.root.select('.compass').attr('transform', `translate(${tx},${ty})`); + focus.root.select('.x-hover-line').attr('y1', 0 - ty).attr('y2', focus.h - ty); + focus.root.select('.cursor-txt').attr('transform', `translate(${tx},${10})`).text(pt.x.toFixed(3)); + if (freq) { + focus.root.select('.cursor-txt-hz').attr('transform', `translate(${tx},${20})`).text(`${(pt.x * freq).toFixed(3)} Hz`); + } else { + focus.root.select('.cursor-txt-hz').text(''); + } + } + } else { + const pt = fetchPt(event, focus, xt); + if (pt) { + const tx = xt(pt.x); + const ty = yt(pt.y); + focus.root.select('.compass').attr('transform', `translate(${tx},${ty})`); + focus.root.select('.x-hover-line').attr('y1', 0 - ty).attr('y2', focus.h - ty); + if (_format.default.isXRDLayout(layout)) { + let dValue = 0.0; + if (wavelength) { + dValue = (0, _chem.Convert2DValue)(pt.x, wavelength.value).toExponential(2); + } else { + dValue = (0, _chem.Convert2DValue)(pt.x).toExponential(2); + } + focus.root.select('.cursor-txt-hz').attr('transform', `translate(${tx},${ty - 30})`).text(`2Theta: ${pt.x.toExponential(2)}, d-value: ${dValue}`); + } else if (_format.default.isTGALayout(layout) || _format.default.isDSCLayout(layout)) { + focus.root.select('.cursor-txt').attr('transform', `translate(${tx},${10})`).text(`X: ${pt.x.toFixed(3)}, Y: ${pt.y.toFixed(3)}`); + } else { + focus.root.select('.cursor-txt').attr('transform', `translate(${tx},${10})`).text(pt.x.toFixed(3)); + if (freq) { + focus.root.select('.cursor-txt-hz').attr('transform', `translate(${tx},${20})`).text(`${(pt.x * freq).toFixed(3)} Hz`); + } else { + focus.root.select('.cursor-txt-hz').text(''); + } + } + } + } + updateIntegrationPreview(event, focus); + updateIntegrationSplitPreview(event, focus); +}; +exports.MouseMove = MouseMove; +const clickIntegrationPoint = (event, focus) => { + const pt = getCurvePointFromEvent(event, focus); + if (!pt) return; + const { + firstIntegrationPoint, + selectUiSweepAct + } = focus; + if (!firstIntegrationPoint) { + // Keep the draft local to D3; the second click emits the existing sweep payload. + const draftPoint = { + x: pt.x, + y: pt.y, + jcampIdx: focus.jcampIdx, + dataLength: focus.data.length + }; + Object.assign(focus, { + firstIntegrationPoint: draftPoint + }); + (0, _integration_draft.setPendingIntegrationDraft)({ + jcampIdx: focus.jcampIdx, + dataLength: focus.data.length, + cancel: () => cancelIntegrationDraft(focus) + }); + drawIntegrationPreview(focus, draftPoint, draftPoint); + return; + } + cancelIntegrationDraft(focus); + if (firstIntegrationPoint.x === pt.x) { + return; + } + selectUiSweepAct((0, _sweep.buildSweepPayloadFromXBounds)(focus, firstIntegrationPoint.x, pt.x)); +}; +const ClickCompass = (event, focus) => { + event.stopPropagation(); + event.preventDefault(); + if (focus.isUiAddIntgSt) { + clickIntegrationPoint(event, focus); + return; + } + if (focus.isUiSplitIntgSt) { + const { + splitX, + target + } = (0, _integration_split.getIntegrationSplitTargetFromEvent)(event, focus); + if (!target) return; + (0, _integration_split.clearIntegrationSplitPreview)(focus); + focus.splitIntegrationAct({ + curveIdx: focus.jcampIdx, + target, + splitX, + data: focus.data + }); + return; + } + const { + xt, + yt + } = TfRescale(focus); + let pt = fetchPt(event, focus, xt); + const { + layout, + cyclicvoltaSt, + jcampIdx + } = focus; + if (_format.default.isCyclicVoltaLayout(layout)) { + pt = fetchFreePt(event, focus, xt, yt); + const onPeak = false; + if (cyclicvoltaSt) { + const { + spectraList + } = cyclicvoltaSt; + const spectra = spectraList[jcampIdx]; + const voltammetryPeakIdx = spectra.selectedIdx; + focus.clickUiTargetAct(pt, onPeak, voltammetryPeakIdx, jcampIdx); + } else { + focus.clickUiTargetAct(pt, onPeak); + } + } else { + focus.clickUiTargetAct(pt, false); + } +}; +exports.ClickCompass = ClickCompass; +const MountCompass = focus => { + const { + root, + w, + h + } = focus; + const compass = root.append('g').attr('class', 'compass'); + const cursor = root.append('g').attr('class', 'cursor'); + const preview = root.append('g').attr('class', 'integration-preview').attr('clip-path', 'url(#clip)'); + const overlay = root.append('rect').attr('class', 'overlay-focus').attr('width', w).attr('height', h).attr('opacity', 0.0); + compass.append('line').attr('class', 'x-hover-line hover-line').attr('stroke', '#777').attr('stroke-width', 1).attr('stroke-dasharray', 2, 2); + compass.append('circle').attr('r', 4).attr('fill', 'none').attr('stroke', '#777').attr('stroke-width', 2); + cursor.append('text').attr('class', 'cursor-txt').attr('font-family', 'Helvetica').style('font-size', '12px').style('text-anchor', 'middle'); + cursor.append('text').attr('class', 'cursor-txt-hz').attr('font-family', 'Helvetica').style('font-size', '12px').style('text-anchor', 'middle').style('fill', '#D68910'); + preview.selectAll('*').remove(); + overlay.on('mousemove', event => MouseMove(event, focus)).on('click', event => ClickCompass(event, focus)); +}; +exports.MountCompass = MountCompass; \ No newline at end of file diff --git a/dist/helpers/converter.js b/dist/helpers/converter.js new file mode 100644 index 00000000..da24848d --- /dev/null +++ b/dist/helpers/converter.js @@ -0,0 +1,99 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ToXY = exports.PksEdit = exports.PeckersEdit = exports.IsSame = void 0; +const ToXY = data => { + const length = data ? data.length : 0; + if (length === 0) return []; + let peaks = []; + let i = 0; + for (i = 0; i < length; i += 1) { + const { + x, + y + } = data[i]; + peaks = [...peaks, [x, y]]; + } + return peaks; +}; +exports.ToXY = ToXY; +const IsSame = (one, two) => Math.abs((one - two) * 10000000) < 1.0; +exports.IsSame = IsSame; +const pksRmNeg = (dataPks, editPeakSt) => { + const { + selectedIdx, + peaks + } = editPeakSt; + const selectedEditPeaks = peaks[selectedIdx]; + if (!selectedEditPeaks) { + return dataPks; + } + const { + neg + } = selectedEditPeaks; + if (!neg) { + return dataPks; + } + const negXs = neg.map(n => n.x); + const result = dataPks.map(p => { + const idx = negXs.findIndex(nx => IsSame(nx, p.x)); + return idx >= 0 ? null : p; + }).filter(r => r != null); + return result; +}; +const pksAddPos = (dataPks, editPeakSt) => { + const { + selectedIdx, + peaks + } = editPeakSt; + const selectedEditPeaks = peaks[selectedIdx]; + if (!selectedEditPeaks) { + return dataPks; + } + const { + pos + } = selectedEditPeaks; + if (!pos) { + return dataPks; + } + const posXs = pos.map(p => p.x); + const posPks = dataPks.map(p => { + const idx = posXs.findIndex(px => px === p.x); + return idx >= 0 ? null : p; + }).filter(r => r != null); + const result = [...posPks, ...pos]; + return result; +}; +const PksEdit = (dataPks, editPeakSt, voltammetryPeak = false) => { + if (voltammetryPeak && voltammetryPeak.length > 0) { + let modDataPks = []; + voltammetryPeak.forEach(peak => { + if (peak.max) { + modDataPks = [...modDataPks, peak.max]; + } + if (peak.min) { + modDataPks = [...modDataPks, peak.min]; + } + }); + modDataPks = modDataPks.sort((a, b) => a.x - b.x); + return modDataPks; + } + let modDataPks = pksAddPos(dataPks, editPeakSt); + modDataPks = pksRmNeg(modDataPks, editPeakSt); + modDataPks = modDataPks.sort((a, b) => a.x - b.x); + return modDataPks; +}; +exports.PksEdit = PksEdit; +const PeckersEdit = voltammetryPeak => { + let modDataPeckers = []; + voltammetryPeak.forEach(peak => { + if (peak.pecker) { + modDataPeckers = [...modDataPeckers, peak.pecker]; + } + }); + modDataPeckers = modDataPeckers.sort((a, b) => a.x - b.x); + return modDataPeckers; +}; +exports.PeckersEdit = PeckersEdit; \ No newline at end of file diff --git a/dist/helpers/extractParams.js b/dist/helpers/extractParams.js new file mode 100644 index 00000000..cbb4f86a --- /dev/null +++ b/dist/helpers/extractParams.js @@ -0,0 +1,85 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.extractParams = void 0; +var _format = _interopRequireDefault(require("./format")); +const getScanIdx = (entity, scanSt) => { + const { + target, + isAuto + } = scanSt; + const { + features, + spectra + } = entity; + const defaultFeat = features.editPeak || features.autoPeak || features[0]; + const hasEdit = !!defaultFeat.scanEditTarget; + const defaultIdx = isAuto || !hasEdit ? defaultFeat.scanAutoTarget : defaultFeat.scanEditTarget; + const defaultCount = +spectra.length; + let idx = +(target || defaultIdx || 0); + if (idx > defaultCount) { + idx = defaultCount; + } + return idx - 1; +}; +const extrShare = (entity, thresSt, scanIdx = 0) => { + const { + spectra, + features + } = entity; + // const { autoPeak, editPeak } = features; // TBD + const autoPeak = features.autoPeak || features[scanIdx] || features[0]; + const editPeak = features.editPeak || features[scanIdx] || features[0]; + const hasEdit = editPeak && editPeak.data ? editPeak.data[0].x.length > 0 : false; + const feature = hasEdit && thresSt.isEdit ? editPeak : autoPeak; + const { + integration, + multiplicity + } = features; + return { + spectra, + feature, + hasEdit, + integration, + multiplicity + }; +}; +const extrMs = (entity, thresSt, scanSt) => { + const scanIdx = getScanIdx(entity, scanSt); + const { + spectra, + feature, + hasEdit + } = extrShare(entity, thresSt, scanIdx); + const topic = spectra[scanIdx].data[0]; + return { + topic, + feature, + hasEdit + }; +}; +const extrNi = (entity, thresSt) => { + const scanIdx = 0; + const { + spectra, + feature, + hasEdit, + integration, + multiplicity + } = extrShare(entity, thresSt, scanIdx); + const topic = spectra[0].data[0]; + return { + topic, + feature, + hasEdit, + integration, + multiplicity + }; +}; +const extractParams = (entity, thresSt, scanSt) => _format.default.isMsLayout(entity.layout) ? extrMs(entity, thresSt, scanSt) : extrNi(entity, thresSt); + +// eslint-disable-line +exports.extractParams = extractParams; \ No newline at end of file diff --git a/dist/helpers/extractPeaksEdit.js b/dist/helpers/extractPeaksEdit.js new file mode 100644 index 00000000..92af43f4 --- /dev/null +++ b/dist/helpers/extractPeaksEdit.js @@ -0,0 +1,75 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.extractPeaksEdit = exports.extractAutoPeaks = exports.extractAreaUnderCurve = void 0; +var _converter = require("./converter"); +var _chem = require("./chem"); +var _shift = require("./shift"); +var _format = _interopRequireDefault(require("./format")); +var _integration = require("./integration"); +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 extractPeaksEdit = (feature, editPeakSt, thresSt, shiftSt, layoutSt, atIndex = 0) => { + const offset = _format.default.isMsLayout(layoutSt) ? msOffset() : niOffset(shiftSt, atIndex); + const peaks = (0, _chem.Convert2Peak)(feature, thresSt.value, offset); + const peaksEdit = (0, _converter.PksEdit)(peaks, editPeakSt); + return peaksEdit; +}; +exports.extractPeaksEdit = extractPeaksEdit; +const extractAutoPeaks = (feature, thresSt, shiftSt, layoutSt, atIndex = 0) => { + const offset = _format.default.isMsLayout(layoutSt) ? msOffset() : niOffset(shiftSt, atIndex); + const peaks = (0, _chem.Convert2Peak)(feature, thresSt.value, offset); + return peaks; +}; +exports.extractAutoPeaks = extractAutoPeaks; +const getAUCValue = (integrationSt, layoutSt) => { + const { + refArea, + refFactor, + stack + } = integrationSt; + if (Array.isArray(stack) && stack.length > 0) { + const data = stack.at(-1); + const ignoreRef = _format.default.isHplcUvVisLayout(layoutSt); + return (0, _integration.calcArea)(data, refArea, refFactor, ignoreRef); + } + return 0; +}; +const extractAreaUnderCurve = (allIntegrationSt, presentIntegrationSt, layoutSt) => { + if (_format.default.isHplcUvVisLayout(layoutSt) && Array.isArray(allIntegrationSt) && presentIntegrationSt) { + const results = []; + allIntegrationSt.forEach(inte => { + const { + integrations + } = inte; + const subResults = []; + integrations.forEach(subInte => { + const aucVal = getAUCValue(subInte, layoutSt); + subResults.push(aucVal); + }); + results.push(subResults); + }); + return results; + } + return null; +}; + +// eslint-disable-line +exports.extractAreaUnderCurve = extractAreaUnderCurve; \ No newline at end of file diff --git a/dist/helpers/focus.js b/dist/helpers/focus.js new file mode 100644 index 00000000..c48f6dc7 --- /dev/null +++ b/dist/helpers/focus.js @@ -0,0 +1,10 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.mpyIdTag = exports.itgIdTag = void 0; +const itgIdTag = d => `${Math.round(1000 * d.xL)}-${Math.round(1000 * d.xU)}`; +exports.itgIdTag = itgIdTag; +const mpyIdTag = d => `${Math.round(1000 * d.xExtent.xL)}-${Math.round(1000 * d.xExtent.xU)}`; +exports.mpyIdTag = mpyIdTag; \ No newline at end of file diff --git a/dist/helpers/format.js b/dist/helpers/format.js new file mode 100644 index 00000000..b528beb8 --- /dev/null +++ b/dist/helpers/format.js @@ -0,0 +1,656 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _jcampconverter = _interopRequireDefault(require("jcampconverter")); +var _converter = require("./converter"); +var _list_layout = require("../constants/list_layout"); +var _multiplicity_calc = require("./multiplicity_calc"); +/* eslint-disable prefer-destructuring */ +/* eslint-disable no-mixed-operators, prefer-object-spread, +function-paren-newline, no-unused-vars, default-param-last */ + +const spectraDigit = layout => { + switch (layout) { + case _list_layout.LIST_LAYOUT.IR: + case _list_layout.LIST_LAYOUT.RAMAN: + case _list_layout.LIST_LAYOUT.UVVIS: + case _list_layout.LIST_LAYOUT.HPLC_UVVIS: + case _list_layout.LIST_LAYOUT.TGA: + case _list_layout.LIST_LAYOUT.DSC: + case _list_layout.LIST_LAYOUT.XRD: + case _list_layout.LIST_LAYOUT.CDS: + case _list_layout.LIST_LAYOUT.SEC: + case _list_layout.LIST_LAYOUT.GC: + case _list_layout.LIST_LAYOUT.MS: + return 0; + case _list_layout.LIST_LAYOUT.C13: + return 1; + case _list_layout.LIST_LAYOUT.H1: + case _list_layout.LIST_LAYOUT.F19: + case _list_layout.LIST_LAYOUT.P31: + case _list_layout.LIST_LAYOUT.N15: + case _list_layout.LIST_LAYOUT.Si29: + case _list_layout.LIST_LAYOUT.PLAIN: + case _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY: + default: + return 2; + } +}; +const fixDigit = (input, precision) => { + const output = input || 0.0; + return output.toFixed(precision); +}; +const buildData = entity => { + if (!entity) return { + isExist: false + }; + const sp = entity && entity.spectrum; + const xLabel = sp ? `X (${sp.xUnit})` : ''; + const yLabel = sp ? `Y (${sp.yUnit})` : ''; + return { + entity, + xLabel, + yLabel, + isExist: true + }; +}; +const toPeakStr = peaks => { + const arr = peaks.map(p => `${p.x},${p.y}`); + const str = arr.join('#'); + return str; +}; +const spectraOps = { + [_list_layout.LIST_LAYOUT.PLAIN]: { + head: '', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.H1]: { + head: '1H', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.C13]: { + head: '13C', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.F19]: { + head: '19F', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.P31]: { + head: '31P', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.N15]: { + head: '15N', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.Si29]: { + head: '29Si', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.IR]: { + head: 'IR', + tail: ' cm-1' + }, + [_list_layout.LIST_LAYOUT.RAMAN]: { + head: 'RAMAN', + tail: ' cm-1' + }, + [_list_layout.LIST_LAYOUT.UVVIS]: { + head: 'UV-VIS (absorption, solvent), λmax', + tail: ' nm' + }, + [_list_layout.LIST_LAYOUT.HPLC_UVVIS]: { + head: 'HPLC UV/VIS (transmittance)', + tail: '' + }, + [_list_layout.LIST_LAYOUT.TGA]: { + head: 'THERMOGRAVIMETRIC ANALYSIS', + tail: ' SECONDS' + }, + [_list_layout.LIST_LAYOUT.DSC]: { + head: 'DIFFERENTIAL SCANNING CALORIMETRY', + tail: ' SECONDS' + }, + [_list_layout.LIST_LAYOUT.MS]: { + head: 'MASS', + tail: ' m/z' + }, + [_list_layout.LIST_LAYOUT.XRD]: { + head: 'XRD', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY]: { + head: 'CV', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.CDS]: { + head: 'CIRCULAR DICHROISM SPECTROSCOPY', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.SEC]: { + head: 'SIZE EXCLUSION CHROMATOGRAPHY', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.GC]: { + head: 'GAS CHROMATOGRAPHY', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.EMISSIONS]: { + head: 'EMISSION', + tail: '.' + }, + [_list_layout.LIST_LAYOUT.DLS_INTENSITY]: { + head: 'DLS', + tail: '.' + } +}; +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; + return peaks.map(p => (0, _converter.IsSame)(p.x, refValue) ? null : p).filter(r => r != null); +}; +const formatedMS = (peaks, maxY, decimal = 2, isAscend = true) => { + const ascendFunc = (a, b) => parseFloat(a) - parseFloat(b); + const descendFunc = (a, b) => parseFloat(b) - parseFloat(a); + const sortFunc = isAscend ? ascendFunc : descendFunc; + let ordered = {}; + peaks.forEach(p => { + const x = fixDigit(p.x, decimal); + const better = !ordered[x] || p.y > ordered[x]; + if (better) { + ordered = Object.assign({}, ordered, { + [x]: p.y + }); + } + }); + ordered = Object.keys(ordered).sort(sortFunc).map(k => ({ + x: k, + y: ordered[k] + })); + return ordered.map(o => `${o.x} (${parseInt(100 * o.y / maxY, 10)})`).join(', '); +}; +const emLevel = (boundary, val, lowerIsStronger) => { + const { + maxY, + minY + } = boundary; + const ratio = lowerIsStronger ? 100 * (val - minY) / (maxY - minY) : 100 * (maxY - val) / (maxY - minY); + if (ratio > 85) return 'vw'; + if (ratio > 60) return 'w'; + if (ratio > 45) return 'm'; + if (ratio > 30) return 's'; + return 'vs'; +}; +const formatedEm = (peaks, maxY, decimal = 2, isAscend = true, isIntensity = false, boundary = {}, lowerIsStronger = false) => { + const ascendFunc = (a, b) => parseFloat(a) - parseFloat(b); + const descendFunc = (a, b) => parseFloat(b) - parseFloat(a); + const sortFunc = isAscend ? ascendFunc : descendFunc; + let ordered = {}; + peaks.forEach(p => { + const x = fixDigit(p.x, decimal); + const better = !ordered[x] || p.y > ordered[x]; + if (better) { + ordered = Object.assign({}, ordered, { + [x]: p.y + }); + } + }); + ordered = Object.keys(ordered).sort(sortFunc).map(k => ({ + x: k, + y: ordered[k] + })); + if (isIntensity) { + return ordered.map(o => `${o.x} (${emLevel(boundary, o.y, lowerIsStronger)})`).join(', '); + } + return ordered.map(o => `${o.x}`).join(', '); +}; +const formatedUvVis = (peaks, maxY, decimal = 2, isAscend = true, isIntensity = false, boundary = {}, lowerIsStronger = false) => { + const ascendFunc = (a, b) => parseFloat(a) - parseFloat(b); + const descendFunc = (a, b) => parseFloat(b) - parseFloat(a); + const sortFunc = isAscend ? ascendFunc : descendFunc; + let ordered = {}; + peaks.forEach(p => { + const x = fixDigit(p.x, decimal); + const better = !ordered[x] || p.y > ordered[x]; + if (better) { + ordered = Object.assign({}, ordered, { + [x]: p.y + }); + } + }); + ordered = Object.keys(ordered).sort(sortFunc).map(k => ({ + x: k, + y: ordered[k] + })); + + // return ordered.map(o => `${o.x} (${o.y.toFixed(2)})`) + // .join(', '); + return ordered.map(o => `${o.x}`).join(', '); +}; +const formatedEmissions = (peaks, maxY, decimal = 2, isAscend = true, isIntensity = false, boundary = {}, lowerIsStronger = false) => { + const ascendFunc = (a, b) => parseFloat(a) - parseFloat(b); + const descendFunc = (a, b) => parseFloat(b) - parseFloat(a); + const sortFunc = isAscend ? ascendFunc : descendFunc; + let ordered = {}; + peaks.forEach(p => { + const x = fixDigit(p.x, decimal); + const better = !ordered[x] || p.y > ordered[x]; + if (better) { + ordered = Object.assign({}, ordered, { + [x]: p.y + }); + } + }); + ordered = Object.keys(ordered).sort(sortFunc).map(k => ({ + x: k, + y: ordered[k] + })); + return ordered.map(o => `${o.x} nm (${fixDigit(o.y, 2)} a.u.)`).join(', '); +}; +const formatedDLSIntensity = (peaks, maxY, decimal = 2, isAscend = true, isIntensity = false, boundary = {}, lowerIsStronger = false) => { + const ascendFunc = (a, b) => parseFloat(a) - parseFloat(b); + const descendFunc = (a, b) => parseFloat(b) - parseFloat(a); + const sortFunc = isAscend ? ascendFunc : descendFunc; + let ordered = {}; + peaks.forEach(p => { + const x = fixDigit(p.x, decimal); + const better = !ordered[x] || p.y > ordered[x]; + if (better) { + ordered = Object.assign({}, ordered, { + [x]: fixDigit(p.y, 2) + }); + } + }); + ordered = Object.keys(ordered).sort(sortFunc).map(k => ({ + x: k, + y: ordered[k] + })); + return ordered.map(o => `${o.x} nm (${o.y} %)`).join(', '); +}; +const formatedHplcUvVis = (peaks, decimal = 2, integration) => { + let stack = []; + if (integration) { + stack = integration.stack; + } + let ordered = {}; + peaks.forEach(p => { + const x = fixDigit(p.x, decimal); + const better = !ordered[x] || p.y > ordered[x]; + if (better) { + ordered = Object.assign({}, ordered, { + [x]: p.y + }); + } + }); + ordered = Object.keys(ordered).map(k => ({ + x: k, + y: ordered[k] + })); + 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})`; + } + }); + } + arrResult.push(pStr); + }); + return arrResult.join(', '); +}; +const formatedXRD = (peaks, isAscend = true, waveLength, temperature) => { + const ascendFunc = (a, b) => parseFloat(a) - parseFloat(b); + const descendFunc = (a, b) => parseFloat(b) - parseFloat(a); + const sortFunc = isAscend ? ascendFunc : descendFunc; + let ordered = {}; + peaks.forEach(p => { + const x = fixDigit(p.x, 1); + const better = !ordered[x] || p.y > ordered[x]; + if (better) { + ordered = Object.assign({}, ordered, { + [x]: p.y + }); + } + }); + const XRDSource = waveLength.label; + const XRDWavelength = `${waveLength.value} ${waveLength.unit}`; + ordered = Object.keys(ordered).sort(sortFunc).map(k => ({ + x: k, + y: ordered[k] + })); + return `(${XRDSource}, ${XRDWavelength}, ${temperature} °C), 2θ [°] (d [nm]): ${ordered.map(o => `${o.x} (${fixDigit(o.y, 2)})`).join(', ')}`; +}; +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; + const result = peaksXY.map(p => { + const srcX = parseFloat(p[0]); + const x = (0, _converter.IsSame)(srcX, rmShiftX) ? null : srcX; + if (!x) return null; + const y = parseFloat(p[1]); + return { + x, + y + }; + }).filter(r => r != null); + return result; +}; +const peaksBody = ({ + peaks, + layout, + decimal, + shift, + isAscend, + isIntensity = false, + boundary = {}, + integration, + atIndex = 0, + waveLength, + temperature +}) => { + const result = rmShiftFromPeaks(peaks, shift, atIndex); + const ascendFunc = (a, b) => parseFloat(a.x) - parseFloat(b.x); + const descendFunc = (a, b) => parseFloat(b.x) - parseFloat(a.x); + const sortFunc = isAscend ? ascendFunc : descendFunc; + const ordered = result.sort(sortFunc); + const maxY = Math.max(...ordered.map(o => o.y)); + if (layout === _list_layout.LIST_LAYOUT.MS) { + return formatedMS(ordered, maxY, decimal, isAscend); + } + if (layout === _list_layout.LIST_LAYOUT.IR) { + return formatedEm(ordered, maxY, decimal, isAscend, isIntensity, boundary, true); + } + if (layout === _list_layout.LIST_LAYOUT.UVVIS) { + return formatedUvVis(ordered, maxY, decimal, isAscend, isIntensity, boundary, false); + } + if (layout === _list_layout.LIST_LAYOUT.HPLC_UVVIS) { + return formatedHplcUvVis(ordered, decimal, integration); + } + if (layout === _list_layout.LIST_LAYOUT.EMISSIONS) { + return formatedEmissions(ordered, maxY, decimal, isAscend, isIntensity, boundary, false); + } + if (layout === _list_layout.LIST_LAYOUT.DLS_INTENSITY) { + return formatedDLSIntensity(ordered, maxY, decimal, isAscend, isIntensity, boundary, false); + } + if (layout === _list_layout.LIST_LAYOUT.RAMAN || layout === _list_layout.LIST_LAYOUT.TGA || layout === _list_layout.LIST_LAYOUT.DSC || layout === _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY || layout === _list_layout.LIST_LAYOUT.CDS || layout === _list_layout.LIST_LAYOUT.SEC || layout === _list_layout.LIST_LAYOUT.GC) { + return formatedEm(ordered, maxY, decimal, isAscend, isIntensity, boundary, false); + } + if (layout === _list_layout.LIST_LAYOUT.XRD) { + return formatedXRD(ordered, isAscend, waveLength, temperature); + } + return ordered.map(o => fixDigit(o.x, decimal)).join(', '); +}; +const peaksWrapper = (layout, shift, atIndex = 0) => { + let solvTxt = ''; + const { + shifts + } = shift; + const selectedShift = shifts[atIndex]; + if (selectedShift.ref.label) { + solvTxt = ` (${selectedShift.ref.label})`; + } + if (layout === _list_layout.LIST_LAYOUT.PLAIN || layout === _list_layout.LIST_LAYOUT.DLS_ACF) { + return { + head: '', + tail: '' + }; + } + const ops = spectraOps[layout]; + if (!ops) { + return { + head: '', + tail: '' + }; + } + return { + head: `${ops.head}${solvTxt} = `, + tail: ops.tail + }; +}; +const isNmrLayout = layoutSt => [_list_layout.LIST_LAYOUT.H1, _list_layout.LIST_LAYOUT.C13, _list_layout.LIST_LAYOUT.F19, _list_layout.LIST_LAYOUT.P31, _list_layout.LIST_LAYOUT.N15, _list_layout.LIST_LAYOUT.Si29].indexOf(layoutSt) >= 0; +const is29SiLayout = layoutSt => _list_layout.LIST_LAYOUT.Si29 === layoutSt; +const is15NLayout = layoutSt => _list_layout.LIST_LAYOUT.N15 === layoutSt; +const is31PLayout = layoutSt => _list_layout.LIST_LAYOUT.P31 === layoutSt; +const is19FLayout = layoutSt => _list_layout.LIST_LAYOUT.F19 === layoutSt; +const is13CLayout = layoutSt => _list_layout.LIST_LAYOUT.C13 === layoutSt; +const is1HLayout = layoutSt => _list_layout.LIST_LAYOUT.H1 === layoutSt; +const isMsLayout = layoutSt => _list_layout.LIST_LAYOUT.MS === layoutSt; +const isIrLayout = layoutSt => [_list_layout.LIST_LAYOUT.IR, 'INFRARED'].indexOf(layoutSt) >= 0; +const isRamanLayout = layoutSt => _list_layout.LIST_LAYOUT.RAMAN === layoutSt; +const isUvVisLayout = layoutSt => _list_layout.LIST_LAYOUT.UVVIS === layoutSt; +const isHplcUvVisLayout = layoutSt => _list_layout.LIST_LAYOUT.HPLC_UVVIS === layoutSt; +const isTGALayout = layoutSt => _list_layout.LIST_LAYOUT.TGA === layoutSt; +const isDSCLayout = layoutSt => _list_layout.LIST_LAYOUT.DSC === layoutSt; +const isXRDLayout = layoutSt => _list_layout.LIST_LAYOUT.XRD === layoutSt; +const isCyclicVoltaLayout = layoutSt => _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY === layoutSt; +const isCDSLayout = layoutSt => _list_layout.LIST_LAYOUT.CDS === layoutSt; +const isSECLayout = layoutSt => _list_layout.LIST_LAYOUT.SEC === layoutSt; +const isGCLayout = layoutSt => _list_layout.LIST_LAYOUT.GC === layoutSt; +const isEmWaveLayout = layoutSt => [_list_layout.LIST_LAYOUT.IR, _list_layout.LIST_LAYOUT.RAMAN, _list_layout.LIST_LAYOUT.UVVIS, _list_layout.LIST_LAYOUT.HPLC_UVVIS].indexOf(layoutSt) >= 0; +const hasMultiCurves = layoutSt => [_list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY, _list_layout.LIST_LAYOUT.SEC, _list_layout.LIST_LAYOUT.GC, _list_layout.LIST_LAYOUT.AIF].indexOf(layoutSt) >= 0; +const isAIFLayout = layoutSt => _list_layout.LIST_LAYOUT.AIF === layoutSt; +const isEmissionsLayout = layoutSt => _list_layout.LIST_LAYOUT.EMISSIONS === layoutSt; +const isDLSACFLayout = layoutSt => _list_layout.LIST_LAYOUT.DLS_ACF === layoutSt; +const isDLSIntensityLayout = layoutSt => _list_layout.LIST_LAYOUT.DLS_INTENSITY === layoutSt; +const getNmrTyp = layout => { + switch (layout) { + case _list_layout.LIST_LAYOUT.H1: + return 'H'; + case _list_layout.LIST_LAYOUT.C13: + return 'C'; + case _list_layout.LIST_LAYOUT.F19: + return 'F'; + case _list_layout.LIST_LAYOUT.P31: + return 'P'; + case _list_layout.LIST_LAYOUT.N15: + return 'N'; + case _list_layout.LIST_LAYOUT.Si29: + return 'Si'; + default: + return ''; + } +}; +const formatPeaksByPrediction = (peaks, layout, isAscend, decimal, predictions = []) => { + const pDict = {}; + peaks.forEach(p => { + pDict[p.x.toFixed(decimal)] = 0; + }); + predictions.forEach(p => { + const key = p.real.toFixed(decimal); + if (typeof pDict[key] === 'number') { + pDict[key] += 1; + } + }); + const typ = getNmrTyp(layout); + const ascendFunc = (a, b) => parseFloat(a.k) - parseFloat(b.k); + const descendFunc = (a, b) => parseFloat(b.k) - parseFloat(a.k); + const sortFunc = isAscend ? ascendFunc : descendFunc; + const pArr = Object.keys(pDict).map(k => { + if (pDict[k] === 1) return { + k, + v: k + }; + return { + k, + v: `${k} (${pDict[k]}${typ})` + }; + }).sort(sortFunc); + const body = pArr.map(p => p.v).join(', '); + return body; +}; +const compareColors = idx => ['#ABB2B9', '#EDBB99', '#ABEBC6', '#D2B4DE', '#F9E79F'][idx % 5]; +const mutiEntitiesColors = idx => ['#2980b9', '#e4b423', '#8e44ad', '#2c3e50', '#6D214F', '#182C61', '#BDC581'][idx % 7]; +const strNumberFixedDecimal = (number, decimal = -1) => { + if (decimal <= 0) { + return `${number}`; + } + return number.toFixed(Math.max(decimal, (number.toString().split('.')[1] || []).length)); +}; +const strNumberFixedLength = (number, maxLength = -1) => { + if (maxLength <= 0) { + return `${number}`; + } + const splittedNum = number.toString().split('.') || []; + if (splittedNum.length === 0) { + return `${number}`; + } + const integerPart = splittedNum[0]; + if (number >= 0 && maxLength <= integerPart.length || number < 0 && maxLength <= integerPart.length - 1) { + // eslint-disable-line + return `${Math.round(number)}`; + } + const lengthToFix = number >= 0 ? maxLength - integerPart.length : maxLength - integerPart.length + 1; // eslint-disable-line + + return number.toFixed(lengthToFix); +}; +const inlineNotation = (layout, data, sampleName = '') => { + let formattedString = ''; + let quillData = []; + const { + scanRate, + voltaData + } = data; + switch (layout) { + case _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY: + { + if (!voltaData) { + break; + } + let refString = ''; + let nonRefString = ''; + let refOps = []; + const nonRefOps = []; + const { + listPeaks, + xyData + } = voltaData; + const { + x + } = xyData; + listPeaks.forEach(item => { + const { + isRef, + e12, + max, + min + } = item; + const e12Str = e12 ? strNumberFixedLength(e12, 3) : '0'; + const scanRateStr = scanRate ? strNumberFixedLength(scanRate, 3) : '0'; + if (isRef) { + const posNegString = x[0] > x[1] ? 'neg.' : 'pos.'; + refString = `CV ( mM in vs. Ref (Fc+/Fc) = ${e12Str} V, v = ${scanRateStr} V/s, to ${posNegString}):`; + refOps = [{ + insert: 'CV ( mM in vs. Ref ' + }, { + insert: '(Fc' + }, { + insert: '+', + attributes: { + script: 'super' + } + }, { + insert: '/Fc) ' + }, { + insert: `= ${e12Str} V, v = ${scanRateStr} V/s, to ${posNegString}):` + }]; + } else { + const delta = max && min ? strNumberFixedLength(Math.abs(max.x - min.x) * 1000, 3) : '0'; + nonRefString += `\nE1/2 = ([${sampleName}] , ΔEp) = ${e12Str} V (${delta} mV)`; + const currentNoneOps = [{ + insert: '\nE' + }, { + insert: '1/2', + attributes: { + script: 'sub' + } + }, { + insert: ` = ([${sampleName}] , ΔE` + }, { + insert: 'p', + attributes: { + script: 'sub' + } + }, { + insert: `) = ${e12Str} V (${delta} mV)` + }]; + nonRefOps.push(...currentNoneOps); + } + }); + formattedString = refString + nonRefString; + quillData = [...refOps, ...nonRefOps]; + break; + } + default: + break; + } + return { + quillData, + formattedString + }; +}; +const Format = { + toPeakStr, + buildData, + spectraDigit, + spectraOps, + peaksBody, + peaksWrapper, + rmRef, + rmShiftFromPeaks, + isNmrLayout, + is13CLayout, + is1HLayout, + is19FLayout, + is31PLayout, + is15NLayout, + is29SiLayout, + isMsLayout, + isIrLayout, + isRamanLayout, + isUvVisLayout, + isHplcUvVisLayout, + isTGALayout, + isDSCLayout, + isXRDLayout, + isCyclicVoltaLayout, + isCDSLayout, + isSECLayout, + isEmissionsLayout, + isDLSIntensityLayout, + isEmWaveLayout, + isGCLayout, + fixDigit, + formatPeaksByPrediction, + formatedMS, + formatedEm, + calcMpyCenter: _multiplicity_calc.calcMpyCenter, + compareColors, + mutiEntitiesColors, + hasMultiCurves, + isAIFLayout, + isDLSACFLayout, + strNumberFixedDecimal, + formatedXRD, + strNumberFixedLength, + inlineNotation +}; +var _default = exports.default = Format; \ No newline at end of file diff --git a/dist/helpers/init.js b/dist/helpers/init.js new file mode 100644 index 00000000..494047e6 --- /dev/null +++ b/dist/helpers/init.js @@ -0,0 +1,67 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.InitTip = exports.InitScale = exports.InitPathCall = exports.InitAxisCall = void 0; +var _d3Tip = _interopRequireDefault(require("d3-tip")); +var _format = _interopRequireDefault(require("./format")); +const d3 = require('d3'); +const InitScale = (target, reverse = true) => { + const xRange = reverse ? [target.w, 0] : [0, target.w]; + const x = d3.scaleLinear().range(xRange); + const y = d3.scaleLinear().range([target.h, 0]); + return { + x, + y + }; +}; +exports.InitScale = InitScale; +const InitAxisCall = count => { + const yAxisFormat = d3.format('.2n'); + const xAxisCall = d3.axisBottom().ticks(10); + const yAxisCall = d3.axisLeft().ticks(count).tickFormat(yAxisFormat); + return { + x: xAxisCall, + y: yAxisCall + }; +}; +exports.InitAxisCall = InitAxisCall; +const InitPathCall = target => { + const line = d3.line().x(d => target.scales.x(d.x)).y(d => target.scales.y(d.y)); + return line; +}; +exports.InitPathCall = InitPathCall; +const tpStyle = () => { + const stBorder = ' border: 2px solid #aaa;'; + const stBorderRadius = ' border-radius: 5px;'; + const stBackground = ' background: #555;'; + const stColor = ' color: #fff;'; + const stPadding = ' padding: 8px;'; + const stOpacity = ' opacity: 0.9; '; + const stZindex = ' z-index: 1999;'; + const stFontFamily = ' font-family: Helvetica;'; + const style = stBorder + stBorderRadius + stBackground + stColor + stPadding + stOpacity + stPadding + stZindex + stFontFamily; + return style; +}; +const tpDiv = (d, digits, yFactor = 1) => ` +
+ x: ${_format.default.fixDigit(d.x, digits)} +
+ y: ${d3.format('.2~e')(d.y * (yFactor || 1))} +
+ `; +const InitTip = () => { + d3.select('.peak-tp').remove(); + const tip = (0, _d3Tip.default)().attr('class', 'd3-tip').html(({ + d, + layout, + yFactor + }) => tpDiv(d, _format.default.spectraDigit(layout), yFactor || 1)); + return tip; +}; +exports.InitTip = InitTip; \ No newline at end of file diff --git a/dist/helpers/integration.js b/dist/helpers/integration.js new file mode 100644 index 00000000..53dfdc96 --- /dev/null +++ b/dist/helpers/integration.js @@ -0,0 +1,62 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getLinearBaseline = exports.getIntegrationPoints = exports.getArea = exports.getAbsoluteArea = exports.calcArea = void 0; +var _calc = require("./calc"); +/* eslint-disable no-mixed-operators */ + +const getArea = (xL, xU, data) => { + let [iL, iU] = [data.length - 1, 0]; + for (let i = 0; i < data.length; i += 1) { + const pt = data[i]; + if (xL <= pt.x && pt.x <= xU) { + if (iL > i) { + iL = i; + } + if (i > iU) { + iU = i; + } + } + } + return Math.abs(data[iU].k - data[iL].k); +}; +exports.getArea = getArea; +const getIntegrationPoints = (xL, xU, data) => data.filter(d => d && Number.isFinite(d.x) && Number.isFinite(d.y) && d.x > xL && d.x < xU); +exports.getIntegrationPoints = getIntegrationPoints; +const getLinearBaseline = points => { + if (!points || !points[0]) return () => 0; + const point1 = points[0]; + const point2 = points[points.length - 1]; + const slope = (0, _calc.calcSlope)(point1.x, point1.y, point2.x, point2.y); + return point => point1.y + slope * (point.x - point1.x); +}; +exports.getLinearBaseline = getLinearBaseline; +const getAbsoluteArea = (xL, xU, data) => { + const ps = getIntegrationPoints(xL, xU, data); + if (ps.length < 2) return 0; + const baselineY = getLinearBaseline(ps); + let area = 0; + if (ps.length > 1) { + for (let i = 1; i < ps.length; i += 1) { + const pt = ps[i]; + const delta = Math.abs(pt.y - baselineY(pt)); + area += delta; + } + } + return area; +}; +exports.getAbsoluteArea = getAbsoluteArea; +const calcArea = (d, refArea, refFactor, ignoreRef = false) => { + if (ignoreRef) { + const { + absoluteArea + } = d; + return !absoluteArea ? 0 : d.absoluteArea.toFixed(2); + } + return (d.area * refFactor / refArea).toFixed(2); +}; + +// eslint-disable-line +exports.calcArea = calcArea; \ No newline at end of file diff --git a/dist/helpers/integration_draft.js b/dist/helpers/integration_draft.js new file mode 100644 index 00000000..7563cad8 --- /dev/null +++ b/dist/helpers/integration_draft.js @@ -0,0 +1,38 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.setPendingIntegrationDraft = exports.hasPendingIntegrationDraft = exports.forgetPendingIntegrationDraft = exports.confirmCancelPendingIntegration = exports.clearPendingIntegrationDraft = void 0; +let pendingIntegrationDraft = null; +const cancelMessage = 'You are currently creating an integration. Are you sure you want to cancel it?'; +const hasPendingIntegrationDraft = () => !!pendingIntegrationDraft; +exports.hasPendingIntegrationDraft = hasPendingIntegrationDraft; +const setPendingIntegrationDraft = draft => { + pendingIntegrationDraft = draft; +}; +exports.setPendingIntegrationDraft = setPendingIntegrationDraft; +const forgetPendingIntegrationDraft = () => { + pendingIntegrationDraft = null; +}; +exports.forgetPendingIntegrationDraft = forgetPendingIntegrationDraft; +const clearPendingIntegrationDraft = () => { + const draft = pendingIntegrationDraft; + pendingIntegrationDraft = null; + if (draft && typeof draft.cancel === 'function') { + draft.cancel(); + } +}; +exports.clearPendingIntegrationDraft = clearPendingIntegrationDraft; +const confirmCancelPendingIntegration = () => { + if (!hasPendingIntegrationDraft()) return true; + const shouldCancel = typeof window === 'undefined' || typeof window.confirm !== 'function' + // This confirmation intentionally protects an in-progress two-click integration. + // eslint-disable-next-line no-alert + || window.confirm(cancelMessage); + if (shouldCancel) { + clearPendingIntegrationDraft(); + } + return shouldCancel; +}; +exports.confirmCancelPendingIntegration = confirmCancelPendingIntegration; \ No newline at end of file diff --git a/dist/helpers/integration_split.js b/dist/helpers/integration_split.js new file mode 100644 index 00000000..dc245c74 --- /dev/null +++ b/dist/helpers/integration_split.js @@ -0,0 +1,96 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.resolveSplitPreviewExtent = exports.interpolateY = exports.getSplitXFromEvent = exports.getIntegrationSplitTargetFromEvent = exports.getIntegrationSplitTarget = exports.getIntegrationBounds = exports.drawIntegrationSplitPreview = exports.clearIntegrationSplitPreview = exports.NMR_SPLIT_PREVIEW_EXTENT = void 0; +var _integration = require("./integration"); +const d3 = require('d3'); +const SPLIT_PREVIEW_CLASS = 'integration-split-preview-line'; +const NMR_SPLIT_PREVIEW_EXTENT = exports.NMR_SPLIT_PREVIEW_EXTENT = { + y1: 38, + y2: 55 +}; +const getIntegrationBounds = (target, shift = 0) => [target.xL - shift, target.xU - shift].sort((a, b) => a - b); +exports.getIntegrationBounds = getIntegrationBounds; +const getSplitXFromEvent = (event, focus) => { + const rawMouseX = d3.pointer(event, focus.root.node())[0]; + return focus.scales.x.invert(rawMouseX); +}; +exports.getSplitXFromEvent = getSplitXFromEvent; +const clearIntegrationSplitPreview = focus => { + if (!focus || !focus.root) return; + focus.root.select(`.${SPLIT_PREVIEW_CLASS}`).remove(); +}; +exports.clearIntegrationSplitPreview = clearIntegrationSplitPreview; +const getIntegrationSplitTarget = (focus, splitX) => { + const splitTargets = focus && focus.integrationSplitTargets; + if (!splitTargets || !Number.isFinite(splitX)) return null; + const { + stack = [], + shift = 0 + } = splitTargets; + return stack.find(target => { + const [xL, xU] = getIntegrationBounds(target, shift); + return splitX > xL && splitX < xU; + }); +}; +exports.getIntegrationSplitTarget = getIntegrationSplitTarget; +const getIntegrationSplitTargetFromEvent = (event, focus) => { + const splitX = getSplitXFromEvent(event, focus); + const target = getIntegrationSplitTarget(focus, splitX); + return { + splitX, + target + }; +}; +exports.getIntegrationSplitTargetFromEvent = getIntegrationSplitTargetFromEvent; +const interpolateY = (points, x) => { + if (!points || points.length === 0) return null; + const sortedPoints = [...points].sort((a, b) => a.x - b.x); + const upperIndex = sortedPoints.findIndex(point => point.x >= x); + if (upperIndex <= 0) return sortedPoints[0].y; + if (upperIndex < 0) return sortedPoints[sortedPoints.length - 1].y; + const lowerPoint = sortedPoints[upperIndex - 1]; + const upperPoint = sortedPoints[upperIndex]; + const xDelta = upperPoint.x - lowerPoint.x; + if (xDelta === 0) return lowerPoint.y; + const ratio = (x - lowerPoint.x) / xDelta; + return lowerPoint.y + ratio * (upperPoint.y - lowerPoint.y); +}; +exports.interpolateY = interpolateY; +const resolveSplitPreviewExtent = (focus, target, splitX, shift, ignoreRef) => { + if (!focus || !target || !focus.scales || !focus.scales.y) return null; + const yt = focus.scales.y; + const [xL, xU] = getIntegrationBounds(target, shift); + if (!Number.isFinite(splitX) || splitX <= xL || splitX >= xU) return null; + if (!ignoreRef) { + return NMR_SPLIT_PREVIEW_EXTENT; + } + const points = (0, _integration.getIntegrationPoints)(xL, xU, focus.data); + if (points.length < 2) return null; + const baselineY = (0, _integration.getLinearBaseline)(points); + const curveY = interpolateY(points, splitX); + if (!Number.isFinite(curveY)) return null; + return { + y1: yt(baselineY({ + x: splitX + })), + y2: yt(curveY) + }; +}; +exports.resolveSplitPreviewExtent = resolveSplitPreviewExtent; +const drawIntegrationSplitPreview = (focus, target, splitX, shift, ignoreRef) => { + const extent = resolveSplitPreviewExtent(focus, target, splitX, shift, ignoreRef); + if (!extent) { + clearIntegrationSplitPreview(focus); + return; + } + const xt = focus.scales.x; + const x = xt(splitX); + const preview = focus.root.select('.integration-preview'); + preview.raise(); + const line = preview.selectAll(`.${SPLIT_PREVIEW_CLASS}`).data([extent]); + line.enter().append('line').attr('class', SPLIT_PREVIEW_CLASS).attr('stroke', 'red').attr('stroke-width', 2).attr('stroke-dasharray', '4,3').style('pointer-events', 'none').merge(line).attr('x1', x).attr('x2', x).attr('y1', d => d.y1).attr('y2', d => d.y2); +}; +exports.drawIntegrationSplitPreview = drawIntegrationSplitPreview; \ No newline at end of file diff --git a/dist/helpers/mount.js b/dist/helpers/mount.js new file mode 100644 index 00000000..5f1bb693 --- /dev/null +++ b/dist/helpers/mount.js @@ -0,0 +1,110 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.MountThresLine = exports.MountTags = exports.MountRef = exports.MountPath = exports.MountMarker = exports.MountMainFrame = exports.MountGrid = exports.MountComparePath = exports.MountClip = exports.MountBars = exports.MountAxisLabelY = exports.MountAxisLabelX = exports.MountAxis = void 0; +var _compass = require("./compass"); +const MountTags = target => { + const igbPath = target.root.append('g').attr('class', 'igbPath-clip').attr('clip-path', 'url(#clip)'); + const igcPath = target.root.append('g').attr('class', 'igcPath-clip').attr('clip-path', 'url(#clip)'); + const igtPath = target.root.append('g').attr('class', 'igtPath-clip').attr('clip-path', 'url(#clip)'); + const pPath = target.root.append('g').attr('class', 'pPath-clip').attr('clip-path', 'url(#clip)'); + const bpPath = target.root.append('g').attr('class', 'bpPath-clip').attr('clip-path', 'url(#clip)'); + const bpTxt = target.root.append('g').attr('class', 'bpTxt-clip').attr('clip-path', 'url(#clip)'); + const mpybPath = target.root.append('g').attr('class', 'mpybPath-clip').attr('clip-path', 'url(#clip)'); + const mpyt1Path = target.root.append('g').attr('class', 'mpyt1Path-clip').attr('clip-path', 'url(#clip)'); + const mpyt2Path = target.root.append('g').attr('class', 'mpyt2Path-clip').attr('clip-path', 'url(#clip)'); + const mpypPath = target.root.append('g').attr('class', 'mpypPath-clip').attr('clip-path', 'url(#clip)'); + const aucPath = target.root.append('g').attr('class', 'aucPath-clip').attr('clip-path', 'url(#clip)'); + const peckerPath = target.root.append('g').attr('class', 'peckerPath-clip').attr('clip-path', 'url(#clip)'); + return { + pPath, + bpPath, + bpTxt, + igbPath, + igcPath, + igtPath, + mpybPath, + mpyt1Path, + mpyt2Path, + mpypPath, + aucPath, + peckerPath // eslint-disable-line + }; +}; +exports.MountTags = MountTags; +const MountBars = target => { + const bars = target.root.append('g').attr('class', 'bars-clip').attr('clip-path', 'url(#clip)'); + return bars; +}; +exports.MountBars = MountBars; +const MountRef = target => { + const ref = target.root.append('g').attr('class', 'ref-clip').attr('clip-path', 'url(#ref-clip)'); + return ref; +}; +exports.MountRef = MountRef; +const MountPath = (target, color) => { + const path = target.root.append('g').attr('class', 'line-clip').attr('clip-path', 'url(#clip)').append('path').attr('class', 'line').style('fill', 'none').style('stroke', color).style('stroke-width', 1).on('click', event => (0, _compass.ClickCompass)(event, target)); + return path; +}; +exports.MountPath = MountPath; +const MountComparePath = (target, color, id, alpha = 1) => { + const path = target.root.append('g').attr('class', 'line-clip-compare').attr('id', id).attr('clip-path', 'url(#clip)').append('path').attr('class', 'line').style('fill', 'none').style('stroke', color).style('stroke-opacity', alpha).style('stroke-width', 1).style('stroke-dasharray', '30, 3').on('click', event => (0, _compass.ClickCompass)(event, target)); + return path; +}; +exports.MountComparePath = MountComparePath; +const MountThresLine = (target, color) => { + const thresLineUp = target.root.append('g').attr('class', 'line-clip').attr('clip-path', 'url(#clip)').append('path').attr('class', 'thresholdUp').style('stroke-dasharray', '3, 3').style('fill', 'none').style('stroke', color).style('stroke-width', 1); + const thresLineDw = target.root.append('g').attr('class', 'line-clip').attr('clip-path', 'url(#clip)').append('path').attr('class', 'thresholdDw').style('stroke-dasharray', '3, 3').style('fill', 'none').style('stroke', color).style('stroke-width', 1); + return [thresLineUp, thresLineDw]; +}; +exports.MountThresLine = MountThresLine; +const MountGrid = target => { + const gridTrans = `translate(0, ${target.h})`; + const xGrid = target.root.append('g').attr('class', 'x-grid').attr('transform', gridTrans); + const yGrid = target.root.append('g').attr('class', 'y-grid'); + return { + x: xGrid, + y: yGrid + }; +}; +exports.MountGrid = MountGrid; +const MountAxis = target => { + const xAxisTrans = `translate(0, ${target.h})`; + const xAxis = target.root.append('g').attr('class', 'x-axis').attr('transform', xAxisTrans); + const yAxis = target.root.append('g').attr('class', 'y-axis'); + return { + x: xAxis, + y: yAxis + }; +}; +exports.MountAxis = MountAxis; +const MountAxisLabelX = target => { + const xTrans = `translate(${target.w / 2}, ${target.h + 30})`; + target.root.append('text').attr('text-anchor', 'middle').attr('transform', xTrans).attr('class', 'xLabel').attr('font-family', 'Helvetica').style('font-size', '12px'); +}; +exports.MountAxisLabelX = MountAxisLabelX; +const MountAxisLabelY = target => { + const yR = 'rotate(-90)'; + const yTrans = `translate(${16 - target.margin.l}, ${target.h / 2}) ${yR}`; + target.root.append('text').attr('text-anchor', 'middle').attr('transform', yTrans).attr('class', 'yLabel').attr('font-family', 'Helvetica').style('font-size', '12px'); +}; +exports.MountAxisLabelY = MountAxisLabelY; +const MountMarker = (target, color) => { + const tTrans = `translate(${target.w - 80}, -10)`; + const lTrans = `translate(${target.w - 200}, -18)`; + target.root.append('text').attr('text-anchor', 'middle').attr('transform', tTrans).attr('class', 'mark-text').attr('font-family', 'Helvetica'); + target.root.append('rect').attr('transform', lTrans).attr('width', 30).attr('height', 5).attr('class', 'mark-line').style('fill', color); +}; +exports.MountMarker = MountMarker; +const MountClip = target => { + target.svg.append('defs').append('clipPath').attr('id', 'clip').append('rect').attr('width', target.w).attr('height', target.h).attr('x', 0).attr('y', 0); +}; +exports.MountClip = MountClip; +const MountMainFrame = (target, name) => { + const transFrame = `translate(${target.margin.l}, ${target.margin.t})`; + const clsName = `${name}-main`; + target.svg.append('g').attr('class', clsName).attr('transform', transFrame); +}; +exports.MountMainFrame = MountMainFrame; \ No newline at end of file diff --git a/dist/helpers/multiplicity.js b/dist/helpers/multiplicity.js new file mode 100644 index 00000000..c1a420b4 --- /dev/null +++ b/dist/helpers/multiplicity.js @@ -0,0 +1,42 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.mpyBasicPatterns = exports.groupInterval = exports.getInterval = void 0; +const mpyBasicPatterns = exports.mpyBasicPatterns = ['s', 'd', 't', 'q', 'quint', 'h', 'sept', 'o', 'n']; +const getInterval = peaks => { + let itvs = []; + for (let idx = 0; idx < peaks.length - 1; idx += 1) { + const itv = Math.abs(peaks[idx + 1].x - peaks[idx].x); + itvs = [...itvs, itv]; + } + return itvs; +}; +exports.getInterval = getInterval; +const groupInterval = itvs => { + let gitvs = []; + itvs.forEach(vv => { + let applied = false; + gitvs.forEach((gv, idx) => { + if (applied) return; + if (Math.abs((gv.c - vv) / gv.c) <= 0.03) { + const c = (gv.c * gv.es.length + vv) / (gv.es.length + 1); + const es = [...gv.es, vv]; + gitvs = [...gitvs.filter((v, i) => i !== idx), { + c, + es + }]; + applied = true; + } + }); + if (!applied) { + gitvs = [...gitvs, { + c: vv, + es: [vv] + }]; + } + }); + return gitvs; +}; +exports.groupInterval = groupInterval; \ No newline at end of file diff --git a/dist/helpers/multiplicity_calc.js b/dist/helpers/multiplicity_calc.js new file mode 100644 index 00000000..f3bce198 --- /dev/null +++ b/dist/helpers/multiplicity_calc.js @@ -0,0 +1,102 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.calcMpyJStr = exports.calcMpyCoup = exports.calcMpyCenter = void 0; +var _jAnalyzer = _interopRequireDefault(require("../third_party/jAnalyzer")); +var _multiplicity = require("./multiplicity"); +var _multiplicity_verify_basic = require("./multiplicity_verify_basic"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const centerX = (ps, shift) => { + const pxs = ps.map(p => p.x).sort((a, b) => a - b); + const centIdx = (ps.length - 1) / 2; + if (centIdx < 0) return 0; + return pxs[centIdx] - shift; +}; +const calcMpyCenter = (ps, shift, typ) => { + const count = ps.length; + const avgX = ps.reduce((sum, nxt) => sum + nxt.x, 0) / ps.length - shift; + if (typ === 'm') return avgX; + if (count % 2 === 0) return avgX; + return centerX(ps, shift); +}; +exports.calcMpyCenter = calcMpyCenter; +const calcMpyJStr = js => { + if (!Array.isArray(js) || js.length === 0) return ' - '; + const cJ = js.map(j => j.toFixed(3)).join(', '); + return `${cJ}`; +}; +exports.calcMpyJStr = calcMpyJStr; +const calcMpyPeakWidth = (x, metaSt) => { + const { + intervalL, + intervalR, + deltaX + } = metaSt.peaks; + let idxL = null; + intervalL.every((l, idx) => { + if (l.x < x) { + idxL = idx - 1; + return false; + } + return true; + }); + let idxR = null; + intervalR.every((l, idx) => { + if (l.x < x) { + idxR = idx; + return false; + } + return true; + }); + if (!idxL || !idxR) return 10 * deltaX; + return Math.abs(intervalL[idxL].x - intervalR[idxR].x); +}; +const calcMpyCoup = (pks, metaSt) => { + if (pks.length === 0) return { + type: '', + js: '' + }; + const orderPks = pks.sort((a, b) => b.x - a.x); + const { + observeFrequency + } = metaSt.peaks; + const peaks = orderPks.map(p => ({ + x: p.x, + intensity: p.y, + width: calcMpyPeakWidth(p.x, metaSt) + })); + const signal = { + nbPeaks: peaks.length, + observe: observeFrequency, + nucleus: '1H', + peaks + }; + _jAnalyzer.default.compilePattern(signal); + const type = signal.multiplicity; + const js = signal.nmrJs ? signal.nmrJs.map(j => j.coupling).sort() : []; + const isTPCMatch = (0, _multiplicity_verify_basic.verifyTypePeakCount)(type, peaks); + if (!isTPCMatch) return { + type: 'm', + js: [] + }; + if (['s', 'm'].indexOf(type) >= 0) return { + type, + js + }; + const oivs = (0, _multiplicity.getInterval)(orderPks); + if (type === 't') return (0, _multiplicity_verify_basic.verifyTypeT)(type, js, oivs, metaSt); + if (type === 'q') return (0, _multiplicity_verify_basic.verifyTypeQ)(type, js, oivs, metaSt); + if (type === 'quint') return (0, _multiplicity_verify_basic.verifyTypeQuint)(type, js, oivs, metaSt); + if (type === 'h') return (0, _multiplicity_verify_basic.verifyTypeH)(type, js, oivs, metaSt); + if (type === 'sept') return (0, _multiplicity_verify_basic.verifyTypeSept)(type, js, oivs, metaSt); + if (type === 'o') return (0, _multiplicity_verify_basic.verifyTypeO)(type, js, oivs, metaSt); + return { + type, + js + }; +}; +exports.calcMpyCoup = calcMpyCoup; \ No newline at end of file diff --git a/dist/helpers/multiplicity_complat.js b/dist/helpers/multiplicity_complat.js new file mode 100644 index 00000000..66d321ed --- /dev/null +++ b/dist/helpers/multiplicity_complat.js @@ -0,0 +1,100 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.calcMpyComplat = void 0; +var _multiplicity = require("./multiplicity"); +const calcMpyComplat = origPeaks => { + const peaks = origPeaks.sort((a, b) => a.x - b.x); + const count = peaks.length; + const itvs = (0, _multiplicity.getInterval)(peaks); + const gitvs = (0, _multiplicity.groupInterval)(itvs); + let type = 'm'; + let js = []; + switch (count) { + case 1: + type = 's'; + js = []; + break; + case 2: + if (gitvs.length === 1) { + type = 'd'; + js = gitvs.map(g => g.c); + break; + } + break; + case 3: + if (gitvs.length === 1) { + type = 't'; + js = gitvs.map(g => g.c); + break; + } + break; + case 4: + if (gitvs.length === 1) { + type = 'q'; + js = gitvs.map(g => g.c); + break; + } else if (gitvs.length === 2) { + type = 'dd'; + js = gitvs.map(g => g.c); + break; + } + break; + case 5: + if (gitvs.length === 1) { + type = 'quint'; + js = gitvs.map(g => g.c); + break; + } + break; + case 6: + if (gitvs.length === 1) { + type = 'h'; + js = gitvs.map(g => g.c); + break; + } else if (gitvs.length === 2) { + type = 'dt'; + js = gitvs.map(g => g.c); + break; + } + // td + break; + case 7: + if (gitvs.length === 1) { + type = 'sept'; + js = gitvs.map(g => g.c); + break; + } else if (gitvs.length === 3) { + type = 'ddd'; + js = gitvs.map(g => g.c); + break; + } + // td + break; + case 8: + if (gitvs.length === 1) { + type = 'o'; + js = gitvs.map(g => g.c); + break; + } else if (gitvs.length === 2) { + type = 'dq'; + js = gitvs.map(g => g.c); + break; + } else if (gitvs.length === 3) { + type = 'ddd'; + js = gitvs.map(g => g.c); + break; + } + // td + break; + default: + break; + } + return { + type, + js + }; +}; +exports.calcMpyComplat = calcMpyComplat; \ No newline at end of file diff --git a/dist/helpers/multiplicity_manual.js b/dist/helpers/multiplicity_manual.js new file mode 100644 index 00000000..e649508f --- /dev/null +++ b/dist/helpers/multiplicity_manual.js @@ -0,0 +1,125 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.calcMpyManual = void 0; +var _multiplicity = require("./multiplicity"); +/* eslint-disable prefer-object-spread, default-param-last, no-mixed-operators */ + +const isTypeM = mpyType => mpyType === 'm'; +const isTypeBasic = mpyType => _multiplicity.mpyBasicPatterns.slice(1).indexOf(mpyType) >= 0; +const outputTypeM = k => Object.assign({}, k, { + mpyType: 'm', + js: [] +}); +const outputTypeBasic = (k, mpyType, ivs, freq) => { + const numIvs = ivs.length || 1; + const js = [freq * ivs.reduce((sum, x) => sum + x) / numIvs]; + return Object.assign({}, k, { + mpyType, + js + }); +}; +const outputTypeDD = (k, mpyType, ivs, freq) => { + if (ivs.length >= 2) { + const js = [freq * ivs[0], freq * (ivs[0] + ivs[1])]; + return Object.assign({}, k, { + mpyType, + js + }); + } + return Object.assign({}, k, { + mpyType, + js: [] + }); +}; +const outputTypeDT = (k, mpyType, ivs, freq) => { + if (ivs.length >= 4) { + const js = [freq * ivs[0], freq * (ivs[1] + ivs[2] + ivs[3])]; + return Object.assign({}, k, { + mpyType, + js + }); + } + return Object.assign({}, k, { + mpyType, + js: [] + }); +}; +const outputTypeTD = (k, mpyType, ivs, freq) => { + if (ivs.length >= 2) { + const js = [freq * ivs[0], freq * (ivs[0] + ivs[1])]; + return Object.assign({}, k, { + mpyType, + js + }); + } + return Object.assign({}, k, { + mpyType, + js: [] + }); +}; +const outputTypeDQ = (k, mpyType, ivs, freq) => { + if (ivs.length >= 2) { + const js = [freq * ivs[0], freq * (ivs[0] + ivs[1])]; + return Object.assign({}, k, { + mpyType, + js + }); + } // only consider J = ([1,2], [1,3]), not J = ([1,2], [1,5]) + return Object.assign({}, k, { + mpyType, + js: [] + }); +}; +const outputTypeQD = (k, mpyType, ivs, freq) => { + if (ivs.length >= 2) { + const js = [freq * ivs[0], freq * (ivs[0] + ivs[1])]; + return Object.assign({}, k, { + mpyType, + js + }); + } + return Object.assign({}, k, { + mpyType, + js: [] + }); +}; +const outputTypeDDD = (k, mpyType, ivs, freq) => { + if (ivs.length >= 3) { + const js = [freq * ivs[0], freq * (ivs[0] + ivs[1]), freq * (ivs[0] + ivs[1] + ivs[2] + ivs[3])]; + return Object.assign({}, k, { + mpyType, + js + }); + } + return Object.assign({}, k, { + mpyType, + js: [] + }); +}; +const calcMpyManual = (k, mpyType, metaSt) => { + const { + observeFrequency + } = metaSt.peaks; + const freq = observeFrequency || 1.0; + const ivs = (0, _multiplicity.getInterval)(k.peaks); + if (ivs.length === 0) return Object.assign({}, k, { + mpyType, + js: [] + }); + if (isTypeM(mpyType)) return outputTypeM(k); + if (isTypeBasic(mpyType)) return outputTypeBasic(k, mpyType, ivs, freq); + if (mpyType === 'dd') return outputTypeDD(k, mpyType, ivs, freq); + if (mpyType === 'dt') return outputTypeDT(k, mpyType, ivs, freq); + if (mpyType === 'td') return outputTypeTD(k, mpyType, ivs, freq); + if (mpyType === 'dq') return outputTypeDQ(k, mpyType, ivs, freq); + if (mpyType === 'qd') return outputTypeQD(k, mpyType, ivs, freq); + if (mpyType === 'ddd') return outputTypeDDD(k, mpyType, ivs, freq); + return Object.assign({}, k, { + mpyType, + js: [] + }); +}; +exports.calcMpyManual = calcMpyManual; \ No newline at end of file diff --git a/dist/helpers/multiplicity_verify_basic.js b/dist/helpers/multiplicity_verify_basic.js new file mode 100644 index 00000000..f1d7df7a --- /dev/null +++ b/dist/helpers/multiplicity_verify_basic.js @@ -0,0 +1,249 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.verifyTypeT = exports.verifyTypeSept = exports.verifyTypeQuint = exports.verifyTypeQ = exports.verifyTypePeakCount = exports.verifyTypeO = exports.verifyTypeH = void 0; +/* eslint-disable prefer-object-spread, default-param-last, no-mixed-operators */ +// --------------------------------------------------------------- +// verifyTypePeakCount +// --------------------------------------------------------------- + +const verifyTypePeakCount = (type, peaks) => { + const isBasicWrong = type === 's' && peaks.length > 1 || type === 'd' && peaks.length > 2 || type === 't' && peaks.length > 3 || type === 'q' && peaks.length > 4 || type === 'quint' && peaks.length > 5 || type === 'h' && peaks.length > 6 || type === 'sept' && peaks.length > 7 || type === 'o' && peaks.length > 8 || type === 'n' && peaks.length > 9; + let limit = 1; + let mStr = type; + limit *= 5 ** (mStr.match(/quint/g) || []).length; + mStr = mStr.replace(/quint/g, ''); + limit *= 7 ** (mStr.match(/sept/g) || []).length; + mStr = mStr.replace(/sept/g, ''); + limit *= 2 ** (mStr.match(/d/g) || []).length; + mStr = mStr.replace(/d/g, ''); + limit *= 3 ** (mStr.match(/t/g) || []).length; + mStr = mStr.replace(/t/g, ''); + limit *= 4 ** (mStr.match(/q/g) || []).length; + mStr = mStr.replace(/q/g, ''); + limit *= 6 ** (mStr.match(/h/g) || []).length; + mStr = mStr.replace(/h/g, ''); + limit *= 8 ** (mStr.match(/o/g) || []).length; + mStr = mStr.replace(/o/g, ''); + limit *= 9 ** (mStr.match(/n/g) || []).length; + mStr = mStr.replace(/n/g, ''); + const isAdvanWrong = peaks.length > limit; + return !(isBasicWrong || isAdvanWrong); +}; + +// --------------------------------------------------------------- +// Basic Multiplicity verification +// --------------------------------------------------------------- +exports.verifyTypePeakCount = verifyTypePeakCount; +const allowedTolerance = 0.15; +const faktor = 1.1; +const passRuleIntervalCounts = (oivs, limit) => oivs.length === limit; +const getRuleParams = (oivs, metaSt) => { + const { + deltaX, + observeFrequency + } = metaSt.peaks; + const sivs = [...oivs].sort((a, b) => b - a); + const ref = sivs[0]; + const rDeltaX = Math.abs(2 * deltaX / ref); + const tTolerance = rDeltaX > allowedTolerance ? rDeltaX : allowedTolerance; + const tolerance = Math.abs(tTolerance * faktor); + const roivs = oivs.map(oiv => oiv / ref); + const rsivs = sivs.map(siv => siv / ref); + return { + roivs, + rsivs, + tolerance, + observeFrequency + }; +}; +const verifyTypeT = (type, js, oivs, metaSt) => { + if (!passRuleIntervalCounts(oivs, 2)) return { + type: 'm', + js: [] + }; + const { + rsivs, + tolerance + } = getRuleParams(oivs, metaSt); + const isT = Math.abs(rsivs[0] - rsivs[1]) < tolerance; + if (isT) return { + type, + js + }; + return { + type: 'm', + js: [] + }; +}; +exports.verifyTypeT = verifyTypeT; +const verifyTypeQ = (type, js, oivs, metaSt) => { + if (!passRuleIntervalCounts(oivs, 3)) return { + type: 'm', + js: [] + }; + const { + roivs, + rsivs, + tolerance, + observeFrequency + } = getRuleParams(oivs, metaSt); + const isQ = Math.abs(rsivs[0] - rsivs[2]) < tolerance; + if (isQ) return { + type, + js + }; + const isDD = Math.abs(roivs[0] - roivs[2]) < tolerance && Math.abs(roivs[0] - roivs[1]) >= tolerance; + const ddJs = [oivs[0] * observeFrequency, (oivs[0] + oivs[1]) * observeFrequency]; + if (isDD) return { + type: 'dd', + js: ddJs + }; + return { + type: 'm', + js: [] + }; +}; +exports.verifyTypeQ = verifyTypeQ; +const verifyTypeQuint = (type, js, oivs, metaSt) => { + if (!passRuleIntervalCounts(oivs, 4)) return { + type: 'm', + js: [] + }; + const { + rsivs, + tolerance + } = getRuleParams(oivs, metaSt); + const isQuint = Math.abs(rsivs[0] - rsivs[3]) < tolerance; + if (isQuint) return { + type, + js + }; + return { + type: 'm', + js: [] + }; +}; +exports.verifyTypeQuint = verifyTypeQuint; +const verifyTypeH = (type, js, oivs, metaSt) => { + if (!passRuleIntervalCounts(oivs, 5)) return { + type: 'm', + js: [] + }; + const { + roivs, + rsivs, + tolerance, + observeFrequency + } = getRuleParams(oivs, metaSt); + const isH = Math.abs(rsivs[0] - rsivs[4]) < tolerance; + if (isH) return { + type, + js + }; + const isTD = Math.abs(roivs[0] - roivs[2]) < tolerance && Math.abs(roivs[0] - roivs[4]) < tolerance && Math.abs(roivs[1] - roivs[3]) < tolerance && Math.abs(roivs[0] - roivs[1]) >= tolerance; + const tdJs = [oivs[0] * observeFrequency, (oivs[0] + oivs[1]) * observeFrequency]; + if (isTD) return { + type: 'td', + js: tdJs + }; + const isDT1 = Math.abs(roivs[0] - roivs[1]) < tolerance && Math.abs(roivs[0] - roivs[3]) < tolerance && Math.abs(roivs[0] - roivs[4]) < tolerance && Math.abs(roivs[0] - roivs[2]) >= tolerance; + const dt1Js = [oivs[0] * observeFrequency, (oivs[1] + oivs[2] + oivs[3]) * observeFrequency]; + if (isDT1) return { + type: 'dt', + js: dt1Js + }; + const isDT2 = Math.abs(roivs[0] - roivs[4]) < tolerance && Math.abs(roivs[0] - roivs[1] - roivs[2]) < tolerance && Math.abs(roivs[0] - roivs[2] - roivs[3]) < tolerance && Math.abs(roivs[0] - roivs[2]) >= tolerance; + const dt2Js = [oivs[0] * observeFrequency, (oivs[1] + oivs[2] + oivs[3]) * observeFrequency]; + if (isDT2) return { + type: 'dt', + js: dt2Js + }; + return { + type: 'm', + js: [] + }; +}; +exports.verifyTypeH = verifyTypeH; +const verifyTypeSept = (type, js, oivs, metaSt) => { + if (!passRuleIntervalCounts(oivs, 6)) return { + type: 'm', + js: [] + }; + const { + roivs, + rsivs, + tolerance, + observeFrequency + } = getRuleParams(oivs, metaSt); + const isSept = Math.abs(rsivs[0] - rsivs[5]) < tolerance; + if (isSept) return { + type, + js + }; + const isDDD = Math.abs(roivs[0] - roivs[2]) < tolerance && Math.abs(roivs[0] - roivs[3]) < tolerance && Math.abs(roivs[0] - roivs[5]) < tolerance && Math.abs(roivs[1] - roivs[4]) < tolerance && Math.abs(roivs[0] - roivs[1]) >= tolerance; + const dddJs = [oivs[0] * observeFrequency, (oivs[0] + oivs[1]) * observeFrequency, (oivs[0] + oivs[1] + oivs[2]) * observeFrequency]; + if (isDDD) return { + type: 'ddd', + js: dddJs + }; + return { + type: 'm', + js: [] + }; +}; +exports.verifyTypeSept = verifyTypeSept; +const verifyTypeO = (type, js, oivs, metaSt) => { + if (!passRuleIntervalCounts(oivs, 7)) return { + type: 'm', + js: [] + }; + const { + roivs, + rsivs, + tolerance, + observeFrequency + } = getRuleParams(oivs, metaSt); + const isO = Math.abs(rsivs[0] - rsivs[6]) < tolerance; + if (isO) return { + type, + js + }; + const isQD = Math.abs(roivs[0] - roivs[2]) < tolerance && Math.abs(roivs[0] - roivs[4]) < tolerance && Math.abs(roivs[0] - roivs[6]) < tolerance && Math.abs(roivs[1] - roivs[3]) < tolerance && Math.abs(roivs[1] - roivs[5]) < tolerance; + const qdJs = [oivs[0] * observeFrequency, (oivs[0] + oivs[1]) * observeFrequency]; + if (isQD) return { + type: 'qd', + js: qdJs + }; + const isDQ1 = Math.abs(roivs[0] - roivs[1] - roivs[2]) < tolerance && Math.abs(roivs[0] - roivs[2] - roivs[3]) < tolerance && Math.abs(roivs[0] - roivs[3] - roivs[4]) < tolerance && Math.abs(roivs[0] - roivs[4] - roivs[5]) < tolerance && Math.abs(roivs[0] - roivs[6]) < tolerance && Math.abs(roivs[1] - roivs[5]) < tolerance; + const dq1Js = [oivs[0] * observeFrequency, (oivs[0] + oivs[1]) * observeFrequency]; + if (isDQ1) return { + type: 'dq', + js: dq1Js + }; + const isDQ2 = Math.abs(roivs[0] - roivs[1]) < tolerance && Math.abs(roivs[0] - roivs[2]) < tolerance && Math.abs(roivs[0] - roivs[4]) < tolerance && Math.abs(roivs[0] - roivs[5]) < tolerance && Math.abs(roivs[0] - roivs[6]) < tolerance && Math.abs(roivs[0] - roivs[3]) >= tolerance; + const dq2Js = [oivs[0] * observeFrequency, (oivs[0] + oivs[1] + oivs[2] + oivs[3]) * observeFrequency]; + if (isDQ2) return { + type: 'dq', + js: dq2Js + }; + const isDDD1 = Math.abs(roivs[0] - roivs[2]) < tolerance && Math.abs(roivs[0] - roivs[4]) < tolerance && Math.abs(roivs[0] - roivs[6]) < tolerance && Math.abs(roivs[1] - roivs[5]) < tolerance; + const ddd1Js = [oivs[0] * observeFrequency, (oivs[0] + oivs[1]) * observeFrequency, (oivs[0] + oivs[1] + oivs[2] + oivs[3]) * observeFrequency]; + if (isDDD1) return { + type: 'ddd', + js: ddd1Js + }; + const isDDD2 = Math.abs(roivs[0] - roivs[2] - roivs[3]) < tolerance && Math.abs(roivs[0] - roivs[3] - roivs[4]) < tolerance && Math.abs(roivs[0] - roivs[6]) < tolerance && Math.abs(roivs[1] - roivs[5]) < tolerance && Math.abs(roivs[0] - roivs[1]) >= tolerance; + const ddd2Js = [oivs[0] * observeFrequency, (oivs[0] + oivs[1]) * observeFrequency, (oivs[0] + oivs[1] + oivs[2]) * observeFrequency]; + if (isDDD2) return { + type: 'ddd', + js: ddd2Js + }; + return { + type: 'm', + js: [] + }; +}; +exports.verifyTypeO = verifyTypeO; \ No newline at end of file diff --git a/dist/helpers/shift.js b/dist/helpers/shift.js new file mode 100644 index 00000000..917f161e --- /dev/null +++ b/dist/helpers/shift.js @@ -0,0 +1,35 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +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 FromManualToOffset = (ref, peak) => { + if (!peak || ref.name === shiftNone.name) return 0; + const offset = peak.x - ref.value; + return offset || 0; +}; +exports.FromManualToOffset = FromManualToOffset; +const CalcResidualX = (origRef, origApex, nextApex) => { + if (!nextApex) return 0.0; // nextApex = false + if (origRef.name === shiftNone.name) return 0.0; + const origApexX = origApex ? origApex.x : 0.0; + const origShift = origApexX === 0.0 ? 0.0 : origRef.value; // orig shift + const resX = origShift - origApexX; + return resX; +}; +exports.CalcResidualX = CalcResidualX; +const VirtalPts = (pts, resX) => pts.map(pt => Object.assign({ + x: pt.x + resX, + y: pt.y +})); +exports.VirtalPts = VirtalPts; +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 diff --git a/dist/helpers/sweep.js b/dist/helpers/sweep.js new file mode 100644 index 00000000..b65f4519 --- /dev/null +++ b/dist/helpers/sweep.js @@ -0,0 +1,39 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.resolveVisibleYExtent = exports.buildSweepPayloadFromXBounds = void 0; +const resolveVisibleYExtent = focus => { + const { + currentExtent, + h, + scales + } = focus; + if (currentExtent && currentExtent.yExtent) { + return currentExtent.yExtent; + } + const yes = [h, 0].map(scales.y.invert).sort((a, b) => a - b); + return { + yL: yes[0], + yU: yes[1] + }; +}; +exports.resolveVisibleYExtent = resolveVisibleYExtent; +const buildSweepPayloadFromXBounds = (focus, xA, xB) => { + const { + data, + dataPks + } = focus; + const xes = [xA, xB].map(x => Number(x)).sort((a, b) => a - b); + return { + xExtent: { + xL: xes[0], + xU: xes[1] + }, + yExtent: resolveVisibleYExtent(focus), + data, + dataPks + }; +}; +exports.buildSweepPayloadFromXBounds = buildSweepPayloadFromXBounds; \ No newline at end of file diff --git a/dist/helpers/zoom.js b/dist/helpers/zoom.js new file mode 100644 index 00000000..3fafd924 --- /dev/null +++ b/dist/helpers/zoom.js @@ -0,0 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +const d3 = require('d3'); +const resetZoom = main => { + main.svg.call(main.zoom.transform, d3.zoomIdentity); + main.svg.selectAll('.brush').call(main.brush.move, null); +}; +const MountZoom = (main, zoomed) => { + const zoomedCb = event => zoomed(event, main); + const resetZoomCb = event => { + event.stopPropagation(); + event.preventDefault(); + resetZoom(main); + }; + main.zoom.on('zoom', zoomedCb); + main.svg.call(main.zoom).on('contextmenu.zoom', resetZoomCb); +}; +var _default = exports.default = MountZoom; \ No newline at end of file diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 00000000..88f8ad09 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,877 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +var _react = _interopRequireDefault(require("react")); +var _reactDom = _interopRequireDefault(require("react-dom")); +var _material = require("@mui/material"); +var _reactQuill = _interopRequireDefault(require("react-quill")); +var _app = require("./app"); +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")); +var _nmr13c_jcamp = _interopRequireDefault(require("./__tests__/fixtures/nmr13c_jcamp")); +var _nmr19f_jcamp = _interopRequireDefault(require("./__tests__/fixtures/nmr19f_jcamp")); +var _nmr31p_jcamp = _interopRequireDefault(require("./__tests__/fixtures/nmr31p_jcamp")); +var _nmr15n_jcamp = _interopRequireDefault(require("./__tests__/fixtures/nmr15n_jcamp")); +var _nmr29si_jcamp = _interopRequireDefault(require("./__tests__/fixtures/nmr29si_jcamp")); +var _ir_jcamp = _interopRequireDefault(require("./__tests__/fixtures/ir_jcamp")); +var _compare_ir_1_jcamp = _interopRequireDefault(require("./__tests__/fixtures/compare_ir_1_jcamp")); +var _compare_ir_2_jcamp = _interopRequireDefault(require("./__tests__/fixtures/compare_ir_2_jcamp")); +var _raman_jcamp = _interopRequireDefault(require("./__tests__/fixtures/raman_jcamp")); +var _ms_jcamp = _interopRequireDefault(require("./__tests__/fixtures/ms_jcamp")); +var _nmr_result = _interopRequireDefault(require("./__tests__/fixtures/nmr_result")); +var _ir_result = _interopRequireDefault(require("./__tests__/fixtures/ir_result")); +var _phenylalanin = _interopRequireDefault(require("./__tests__/fixtures/phenylalanin")); +var _compare_uv_vis_jcamp = _interopRequireDefault(require("./__tests__/fixtures/compare_uv_vis_jcamp")); +var _uv_vis_jcamp = _interopRequireDefault(require("./__tests__/fixtures/uv_vis_jcamp")); +var _hplc_uvvis_jcamp = _interopRequireDefault(require("./__tests__/fixtures/hplc_uvvis_jcamp")); +var _hplc_uvvis_jcamp_ = _interopRequireDefault(require("./__tests__/fixtures/hplc_uvvis_jcamp_2")); +var _tga_jcamp = _interopRequireDefault(require("./__tests__/fixtures/tga_jcamp")); +var _dsc_jcamp = _interopRequireDefault(require("./__tests__/fixtures/dsc_jcamp")); +var _xrd_jcamp_ = _interopRequireDefault(require("./__tests__/fixtures/xrd_jcamp_1")); +var _xrd_jcamp_2 = _interopRequireDefault(require("./__tests__/fixtures/xrd_jcamp_2")); +var _cyclic_voltammetry_ = _interopRequireDefault(require("./__tests__/fixtures/cyclic_voltammetry_1")); +var _cyclic_voltammetry_2 = _interopRequireDefault(require("./__tests__/fixtures/cyclic_voltammetry_2")); +var _cyclic_voltammetry_3 = _interopRequireDefault(require("./__tests__/fixtures/cyclic_voltammetry_3")); +var _cds_jcamp = _interopRequireDefault(require("./__tests__/fixtures/cds_jcamp")); +var _sec_1_jcamp = _interopRequireDefault(require("./__tests__/fixtures/sec_1_jcamp")); +var _sec_2_jcamp = _interopRequireDefault(require("./__tests__/fixtures/sec_2_jcamp")); +var _sec_3_jcamp = _interopRequireDefault(require("./__tests__/fixtures/sec_3_jcamp")); +var _sec_4_jcamp = _interopRequireDefault(require("./__tests__/fixtures/sec_4_jcamp")); +var _aif_jcamp_ = _interopRequireDefault(require("./__tests__/fixtures/aif_jcamp_1")); +var _aif_jcamp_2 = _interopRequireDefault(require("./__tests__/fixtures/aif_jcamp_2")); +var _gc_1_jcamp = _interopRequireDefault(require("./__tests__/fixtures/gc_1_jcamp")); +var _gc_2_jcamp = _interopRequireDefault(require("./__tests__/fixtures/gc_2_jcamp")); +var _gc_3_jcamp = _interopRequireDefault(require("./__tests__/fixtures/gc_3_jcamp")); +var _emissions_jcamp = _interopRequireDefault(require("./__tests__/fixtures/emissions_jcamp")); +var _dls_acf_jcamp = _interopRequireDefault(require("./__tests__/fixtures/dls_acf_jcamp")); +var _dls_intensity_jcamp = _interopRequireDefault(require("./__tests__/fixtures/dls_intensity_jcamp")); +var _qDescValue = require("./__tests__/fixtures/qDescValue"); +require("./__tests__/style/svg.css"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, default-param-last, no-nested-ternary */ + +const pickSelectedSpectrumFromPayload = payload => { + const spectraList = payload?.spectra_list; + if (!Array.isArray(spectraList)) return payload || {}; + if (spectraList.length === 0) return {}; + 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); +const nmr13CDeptEntity = _app.FN.ExtractJcamp(_nmr13c_dept_jcamp.default); +const nmr19FEntity = _app.FN.ExtractJcamp(_nmr19f_jcamp.default); +const nmr31PEntity = _app.FN.ExtractJcamp(_nmr31p_jcamp.default); +const nmr15NEntity = _app.FN.ExtractJcamp(_nmr15n_jcamp.default); +const nmr29SiEntity = _app.FN.ExtractJcamp(_nmr29si_jcamp.default); +const irEntity = _app.FN.ExtractJcamp(_ir_jcamp.default); +const compIr1Entity = _app.FN.ExtractJcamp(_compare_ir_1_jcamp.default); +const compIr2Entity = _app.FN.ExtractJcamp(_compare_ir_2_jcamp.default); +const ramanEntity = _app.FN.ExtractJcamp(_raman_jcamp.default); +const msEntity = _app.FN.ExtractJcamp(_ms_jcamp.default); +const uvVisEntity = _app.FN.ExtractJcamp(_uv_vis_jcamp.default); +const compUvVisEntity = _app.FN.ExtractJcamp(_compare_uv_vis_jcamp.default); +const hplcUVVisEntity = _app.FN.ExtractJcamp(_hplc_uvvis_jcamp.default); +const hplcUVVisEntity2 = _app.FN.ExtractJcamp(_hplc_uvvis_jcamp_.default); +const tgaEntity = _app.FN.ExtractJcamp(_tga_jcamp.default); +const dscEntity = _app.FN.ExtractJcamp(_dsc_jcamp.default); +const xrdEntity1 = _app.FN.ExtractJcamp(_xrd_jcamp_.default); +const xrdEntity2 = _app.FN.ExtractJcamp(_xrd_jcamp_2.default); +const cyclicVoltaEntity1 = _app.FN.ExtractJcamp(_cyclic_voltammetry_.default); +const cyclicVoltaEntity2 = _app.FN.ExtractJcamp(_cyclic_voltammetry_2.default); +const cyclicVoltaEntity3 = _app.FN.ExtractJcamp(_cyclic_voltammetry_3.default); +const cdsEntity = _app.FN.ExtractJcamp(_cds_jcamp.default); +const secEntity1 = _app.FN.ExtractJcamp(_sec_1_jcamp.default); +const secEntity2 = _app.FN.ExtractJcamp(_sec_2_jcamp.default); +const secEntity3 = _app.FN.ExtractJcamp(_sec_3_jcamp.default); +const secEntity4 = _app.FN.ExtractJcamp(_sec_4_jcamp.default); +const aifEntity1 = _app.FN.ExtractJcamp(_aif_jcamp_.default); +const aifEntity2 = _app.FN.ExtractJcamp(_aif_jcamp_2.default); +const gcEntity1 = _app.FN.ExtractJcamp(_gc_1_jcamp.default); +const gcEntity2 = _app.FN.ExtractJcamp(_gc_2_jcamp.default); +const gcEntity3 = _app.FN.ExtractJcamp(_gc_3_jcamp.default); +const emissionsEntity = _app.FN.ExtractJcamp(_emissions_jcamp.default); +const dlsAcfEntity = _app.FN.ExtractJcamp(_dls_acf_jcamp.default); +const dlsIntensityEntity = _app.FN.ExtractJcamp(_dls_intensity_jcamp.default); +class DemoWriteIr extends _react.default.Component { + constructor(props) { + super(props); + this.state = { + typ: 'nmr 1h', + desc: '', + predictions: false, + molecule: '', + showOthers: false, + descChanged: '' + }; + this.onClick = this.onClick.bind(this); + this.writeMpy = this.writeMpy.bind(this); + this.writePeak = this.writePeak.bind(this); + this.formatPks = this.formatPks.bind(this); + this.formatMpy = this.formatMpy.bind(this); + this.savePeaks = this.savePeaks.bind(this); + this.predictOp = this.predictOp.bind(this); + this.updatInput = this.updatInput.bind(this); + this.loadEntity = this.loadEntity.bind(this); + this.loadQuill = this.loadQuill.bind(this); + this.onShowOthers = this.onShowOthers.bind(this); + this.loadOthers = this.loadOthers.bind(this); + this.onDescriptionChanged = this.onDescriptionChanged.bind(this); + this.loadMultiEntities = this.loadMultiEntities.bind(this); + } + onClick(typ) { + return () => { + this.setState({ + typ, + desc: '', + predictions: false, + molecule: '' + }); + }; + } + onShowOthers(jcamp) { + // eslint-disable-line + this.setState({ + showOthers: true + }); + } + onDescriptionChanged(content) { + // console.log(content) + this.setState({ + descChanged: content + }); + } + loadEntity() { + const { + typ + } = this.state; + switch (typ) { + case 'nmr 1h': + return nmr1HEntity; + case 'nmr 13c': + return nmr13CEntity; + case 'nmr 13c dept': + return nmr13CDeptEntity; + case 'nmr 19f': + return nmr19FEntity; + case 'nmr 31p': + return nmr31PEntity; + case 'nmr 15n': + return nmr15NEntity; + case 'nmr 29si': + return nmr29SiEntity; + case 'ir': + return irEntity; + case 'raman': + return ramanEntity; + case 'uv/vis': + return uvVisEntity; + case 'hplc uv/vis': + return hplcUVVisEntity; + case 'tga': + return tgaEntity; + case 'dsc': + return dscEntity; + case 'xrd': + return xrdEntity1; + case 'cyclic volta': + return cyclicVoltaEntity2; + case 'cds': + return cdsEntity; + case 'sec': + return secEntity1; + case 'aif': + return aifEntity1; + case 'emissions': + return emissionsEntity; + case 'dls acf': + return dlsAcfEntity; + case 'dls intensity': + return dlsIntensityEntity; + case 'gc': + return gcEntity1; + case 'ms': + default: + return msEntity; + } + } + loadMultiEntities() { + const { + typ + } = this.state; + switch (typ) { + case 'cyclic volta': + return [cyclicVoltaEntity1, cyclicVoltaEntity2, cyclicVoltaEntity3]; + case 'multi': + return [nmr1HEntity, nmr1HEntity2]; + case 'multi hplc': + return [hplcUVVisEntity, hplcUVVisEntity2]; + case 'multi ir': + return [compIr1Entity, compIr2Entity]; + case 'multi xrd': + return [xrdEntity1, xrdEntity2]; + case 'sec': + return [secEntity1, secEntity2, secEntity3, secEntity4]; + case 'aif': + return [aifEntity1, aifEntity2]; + case 'gc': + return [gcEntity1, gcEntity2, gcEntity3]; + default: + return false; + } + } + loadQuill() { + const { + typ + } = this.state; + switch (typ) { + case 'nmr 1h': + return _qDescValue.q1H; + case 'nmr 13c': + return _qDescValue.q13C; + case 'nmr 13c dept': + return _qDescValue.q13C; + case 'ir': + return _qDescValue.qIR; + case 'nmr 19f': + case 'nmr 31p': + case 'nmr 15n': + case 'nmr 29si': + case 'raman': + case 'uv/vis': + case 'hplc uv/vis': + case 'tga': + case 'dsc': + case 'xrd': + case 'ms': + case 'cyclic volta': + case 'cds': + case 'sec': + case 'aif': + case 'emissions': + case 'dls acf': + case 'dls intensity': + case 'gc': + default: + return false; + } + } + loadOthers() { + const { + showOthers, + typ + } = this.state; + const isIr = typ === 'ir'; + const isXRD = typ === 'xrd'; + const others = showOthers ? isIr ? [compIr1Entity, compIr2Entity] : isXRD ? [xrdEntity2] : [compUvVisEntity] : []; + return { + others, + addOthersCb: this.onShowOthers + }; + } + rmDollarSign(target) { + return target.replace(/\$/g, ''); + } + formatPks({ + peaks, + layout, + shift, + isAscend, + decimal, + isIntensity, + integration, + waveLength, + cyclicvoltaSt, + curveSt + }) { + const entity = this.loadEntity(); + const safeLayout = layout || entity?.layout; + const { + features + } = entity; + const { + temperature + } = entity; + const { + maxY, + minY + } = Array.isArray(features) ? {} : features.editPeak || features.autoPeak; + const boundary = { + maxY, + minY + }; + const shiftForFormatting = normalizeShiftForFormatting(shift); + const body = _app.FN.peaksBody({ + peaks, + layout: safeLayout, + decimal, + shift: shiftForFormatting, + isAscend, + isIntensity, + boundary, + integration, + waveLength, + temperature + }); + const wrapper = _app.FN.peaksWrapper(safeLayout, shiftForFormatting); + let desc = this.rmDollarSign(wrapper.head) + body + wrapper.tail; + if (_app.FN.isCyclicVoltaLayout(safeLayout) && cyclicvoltaSt?.spectraList && curveSt?.listCurves) { + const { + spectraList + } = cyclicvoltaSt; + const { + curveIdx, + listCurves + } = curveSt; + const selectedVolta = spectraList[curveIdx]; + const selectedCurve = listCurves[curveIdx]; + if (!selectedVolta || !selectedCurve?.feature) return desc; + const { + feature + } = selectedCurve; + const { + scanRate + } = feature; + const data = { + scanRate, + voltaData: { + listPeaks: selectedVolta.list, + xyData: feature.data[0] + } + }; + const inlineData = _app.FN.inlineNotation(layout, data); + const { + formattedString + } = inlineData; + desc = formattedString; + } + return desc; + } + formatMpy({ + multiplicity, + integration, + shift, + isAscend, + decimal, + layout + }) { + // obsv freq + const entity = this.loadEntity(); + const { + features + } = entity; + const { + observeFrequency + } = Array.isArray(features) ? features[0] : features.editPeak || features.autoPeak; + const freq = observeFrequency[0]; + const freqStr = freq ? `${parseInt(freq, 10)} MHz, ` : ''; + // multiplicity + const { + refArea, + refFactor + } = integration; + const shiftVal = multiplicity.shift; + const ms = multiplicity.stack; + const is = integration.stack; + const macs = ms.map(m => { + const { + peaks, + mpyType, + xExtent + } = m; + const { + xL, + xU + } = xExtent; + const it = is.filter(i => i.xL === xL && i.xU === xU)[0] || { + area: 0 + }; + const area = it.area * refFactor / refArea; // eslint-disable-line + const center = _app.FN.calcMpyCenter(peaks, shiftVal, mpyType); + const safeCenter = Number.isFinite(center) ? center : 0; + const xs = m.peaks.map(p => p.x).sort((a, b) => a - b); + const [aIdx, bIdx] = isAscend ? [0, xs.length - 1] : [xs.length - 1, 0]; + const hasValidRange = xs.length > 0 && Number.isFinite(shiftVal); + const mxA = mpyType === 'm' && hasValidRange ? (xs[aIdx] - shiftVal).toFixed(decimal) : safeCenter.toFixed(decimal); + const mxB = mpyType === 'm' && hasValidRange ? (xs[bIdx] - shiftVal).toFixed(decimal) : safeCenter.toFixed(decimal); + return Object.assign({}, m, { + area, + center, + mxA, + mxB + }); + }).sort((a, b) => isAscend ? a.center - b.center : b.center - a.center); + const str = macs.map(m => { + const c = m.center; + const type = m.mpyType; + const it = Math.round(m.area); + const js = m.js.map(j => `J = ${j.toFixed(1)} Hz`).join(', '); + const atomCount = layout === '1H' ? `, ${it}H` : ''; + const location = type === 'm' ? `${m.mxA}–${m.mxB}` : `${c.toFixed(decimal)}`; + return m.js.length === 0 ? `${location} (${type}${atomCount})` : `${location} (${type}, ${js}${atomCount})`; + }).join(', '); + const shiftRef = shift?.ref || {}; + const { + label, + value, + name + } = shiftRef; + const hasValidShiftRef = !!label && Number.isFinite(value) && typeof name === 'string'; + const solvent = hasValidShiftRef ? `${name.split('(')[0].trim()} [${value.toFixed(decimal)} ppm], ` : ''; + return `${layout} NMR (${freqStr}${solvent}ppm) δ = ${str}.`; + } + writeMpy(payload) { + const { + layout, + shift, + isAscend, + decimal, + multiplicity, + integration + } = pickSelectedSpectrumFromPayload(payload); + if (!_app.FN.isNmrLayout(layout)) return; + const desc = this.formatMpy({ + multiplicity, + integration, + shift, + isAscend, + decimal, + layout + }); + this.setState({ + desc + }); + } + writePeak(payload) { + const { + peaks, + layout, + shift, + isAscend, + decimal, + isIntensity, + integration, + waveLength, + cyclicvoltaSt, + curveSt + } = pickSelectedSpectrumFromPayload(payload); + const desc = this.formatPks({ + peaks, + layout, + shift, + isAscend, + decimal, + isIntensity, + integration, + waveLength, + // eslint-disable-line + cyclicvoltaSt, + curveSt // eslint-disable-line + }); + this.setState({ + desc + }); + } + savePeaks(payload) { + const { + peaks, + layout, + shift, + isAscend, + decimal, + analysis, + isIntensity, + integration, + multiplicity, + waveLength + } = pickSelectedSpectrumFromPayload(payload); + const entity = this.loadEntity(); + const safeLayout = layout || entity?.layout; + const { + features + } = entity; + const { + temperature + } = entity; + const { + maxY, + minY + } = Array.isArray(features) ? features[0] : features.editPeak || features.autoPeak; + const boundary = { + maxY, + minY + }; + const shiftForFormatting = normalizeShiftForFormatting(shift); + const body = _app.FN.peaksBody({ + peaks, + layout: safeLayout, + decimal, + shift: shiftForFormatting, + isAscend, + isIntensity, + boundary, + waveLength, + temperature + }); + /*eslint-disable */ + console.log(analysis); + console.log(integration); + console.log(multiplicity); + if (shift?.ref?.label) { + const label = this.rmDollarSign(shift.ref.label); + alert(`Peaks: ${body}` + '\n' + '- - - - - - - - - - -' + '\n' + `Shift solvent = ${label}, ${shift.ref.value}ppm` + '\n'); + } else { + alert(`Peaks: ${body}` + '\n'); + } + /*eslint-disable */ + } + predictOp({ + multiplicity, + curveSt + }) { + const { + curveIdx + } = curveSt; + const { + multiplicities + } = multiplicity; + const selectedMultiplicity = multiplicities[curveIdx]; + const { + stack, + shift + } = selectedMultiplicity; + const targets = stack.map(stk => { + const { + mpyType, + peaks + } = stk; + return _app.FN.CalcMpyCenter(peaks, shift, mpyType); + }); + // console.log(targets) + const { + molecule, + typ + } = this.state; + const predictions = { + running: true + }; + this.setState({ + predictions + }); + // simulate fetching... + const result = typ === 'ir' ? _ir_result.default : _nmr_result.default; + setTimeout(() => { + this.setState({ + predictions: result + }); + }, 2000); + } + updatInput(e) { + const molecule = e.target.value; + this.setState({ + molecule + }); + } + render() { + const { + desc, + predictions, + molecule, + typ + } = this.state; + const entity = this.loadEntity(); + const qDescVal = this.loadQuill(); + const multiEntities = this.loadMultiEntities(); + let operations = [{ + name: 'write peaks', + value: this.writePeak + }, { + name: 'save', + value: this.savePeaks + }].filter(r => r.value); + if (_app.FN.isNmrLayout(entity.layout)) { + operations = [{ + name: 'write multiplicity', + value: this.writeMpy + }, ...operations]; + } + const refreshCb = () => alert('Refresch simulation!'); + const forecast = { + btnCb: this.predictOp, + refreshCb, + inputCb: this.updatInput, + molecule: molecule, + predictions + }; + const molSvg = ['nmr 1h', 'ir', 'cyclic volta'].indexOf(typ) >= 0 ? _phenylalanin.default.path : ''; + const others = this.loadOthers(); + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + style: { + width: Math.round(window.innerWidth * 0.96) + }, + children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + style: { + margin: '0 0 15px 55px' + }, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('nmr 1h'), + children: "NMR 1H" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('nmr 13c'), + children: "NMR 13C" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('nmr 13c dept'), + children: "NMR 13C DEPT" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('nmr 19f'), + children: "NMR 19F" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('nmr 31p'), + children: "NMR 31P" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('nmr 15n'), + children: "NMR 15N" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('nmr 29si'), + children: "NMR 29Si" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('ir'), + children: "IR" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('raman'), + children: "RAMAN" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + id: "btn-uv-vis", + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('uv/vis'), + children: "UV/VIS" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + id: "btn-hplc", + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('hplc uv/vis'), + children: "HPLC UV/VIS" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + id: "btn-tga", + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('tga'), + children: "TGA" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + id: "btn-dsc", + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('dsc'), + children: "DSC" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + id: "btn-xrd", + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('xrd'), + children: "XRD" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + id: "btn-cv", + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('cyclic volta'), + children: "CV" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('cds'), + children: "CDS" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + id: "btn-sec", + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('sec'), + children: "SEC" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + id: "btn-sec", + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('gc'), + children: "GC" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + id: "btn-sod", + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('aif'), + children: "SORPTION-DESORPTION" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('emissions'), + children: "EMISSIONS" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('dls acf'), + children: "DLS ACF" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('dls intensity'), + children: "DLS intensity" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('ms'), + children: "MS" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('multi'), + children: "Multi NMR" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('multi ir'), + children: "Multi IR" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('multi hplc'), + children: "Multi HPLC" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, { + variant: "contained", + style: { + margin: '0 10px 0 10px' + }, + onClick: this.onClick('multi xrd'), + children: "Multi XRD" + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_app.SpectraEditor, { + entity: entity, + multiEntities: multiEntities, + others: others, + editorOnly: false, + descriptions: desc, + canChangeDescription: true, + onDescriptionChanged: this.onDescriptionChanged, + molSvg: molSvg, + exactMass: '123.0', + userManualLink: { + cv: "https://www.chemotion.net/chemotionsaurus/docs/eln/chemspectra/cvanalysis" + }, + forecast: forecast, + operations: operations + }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", { + children: "Description Changed" + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactQuill.default, { + className: 'card-sv-quill', + value: this.state.descChanged, + modules: { + toolbar: false + }, + readOnly: true + })] + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, { + container: true, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, { + item: true, + xs: 10, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.InputBase, { + style: { + margin: '0 0 0 63px' + }, + placeholder: "Description", + multiline: true, + fullWidth: true, + rows: "2", + margin: "dense", + value: desc + }) + }) + })] + }); + } +} + +// - - - DOM - - - +_reactDom.default.render(/*#__PURE__*/(0, _jsxRuntime.jsx)(DemoWriteIr, {}), document.getElementById('root')); \ No newline at end of file diff --git a/dist/layer_content.js b/dist/layer_content.js new file mode 100644 index 00000000..56a2fb51 --- /dev/null +++ b/dist/layer_content.js @@ -0,0 +1,104 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _index = _interopRequireDefault(require("./components/d3_line/index")); +var _index2 = _interopRequireDefault(require("./components/d3_rect/index")); +var _forecast_viewer = _interopRequireDefault(require("./components/forecast_viewer")); +var _format = _interopRequireDefault(require("./helpers/format")); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, default-param-last, react/function-component-definition */ + +const extractLayout = (forecast, layoutSt) => { + const isEmpty = Object.keys(forecast).length === 0 && forecast.constructor === Object; + const isNmr = _format.default.isNmrLayout(layoutSt); + const isMs = _format.default.isMsLayout(layoutSt); + const isIr = _format.default.isIrLayout(layoutSt); + const isUvvis = _format.default.isUvVisLayout(layoutSt) || _format.default.isHplcUvVisLayout(layoutSt); + const isXRD = _format.default.isXRDLayout(layoutSt); + const showForecast = !isEmpty && (isNmr || isIr || isUvvis || isXRD); + return { + showForecast, + isNmr, + isIr, + isMs, + isUvvis, + isXRD + }; +}; +const Content = ({ + topic, + feature, + cLabel, + xLabel, + yLabel, + forecast, + operations, + layoutSt +}) => { + const { + showForecast, + isNmr, + isIr, + isMs, + isUvvis, + isXRD + } = extractLayout(forecast, layoutSt); + if (showForecast) { + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_forecast_viewer.default, { + topic: topic, + cLabel: cLabel, + xLabel: xLabel, + yLabel: yLabel, + feature: feature, + forecast: forecast, + isNmr: isNmr, + isIr: isIr, + isUvvis: isUvvis, + isXRD: isXRD, + operations: operations + }); + } + if (isMs) { + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_index2.default, { + topic: topic, + cLabel: cLabel, + xLabel: xLabel, + yLabel: yLabel, + feature: feature, + isHidden: false + }); + } + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_index.default, { + topic: topic, + cLabel: cLabel, + xLabel: xLabel, + yLabel: yLabel, + feature: feature, + isHidden: false + }); +}; +const mapStateToProps = (state, _) => ( +// eslint-disable-line +{ + layoutSt: state.layout +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({}, dispatch); +Content.propTypes = { + topic: _propTypes.default.object.isRequired, + feature: _propTypes.default.object.isRequired, + cLabel: _propTypes.default.string.isRequired, + xLabel: _propTypes.default.string.isRequired, + yLabel: _propTypes.default.string.isRequired, + forecast: _propTypes.default.object.isRequired, + operations: _propTypes.default.array.isRequired, + layoutSt: _propTypes.default.string.isRequired +}; +var _default = exports.default = (0, _redux.compose)((0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps))(Content); \ No newline at end of file diff --git a/dist/layer_init.js b/dist/layer_init.js new file mode 100644 index 00000000..3df80431 --- /dev/null +++ b/dist/layer_init.js @@ -0,0 +1,256 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _styles = require("@mui/styles"); +var _submit = require("./actions/submit"); +var _manager = require("./actions/manager"); +var _meta = require("./actions/meta"); +var _jcamp = require("./actions/jcamp"); +var _layer_prism = _interopRequireDefault(require("./layer_prism")); +var _format = _interopRequireDefault(require("./helpers/format")); +var _multi_jcamps_viewer = _interopRequireDefault(require("./components/multi_jcamps_viewer")); +var _curve = require("./actions/curve"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const styles = () => ({}); +class LayerInit extends _react.default.Component { + constructor(props) { + super(props); + this.normChange = this.normChange.bind(this); + this.execReset = this.execReset.bind(this); + this.initReducer = this.initReducer.bind(this); + this.updateOthers = this.updateOthers.bind(this); + this.updateMultiEntities = this.updateMultiEntities.bind(this); + } + componentDidMount() { + this.execReset(); + this.initReducer(); + this.updateOthers(); + this.updateMultiEntities(); + } + componentDidUpdate(prevProps) { + this.normChange(prevProps); + this.updateOthers(); + this.updateMultiEntities(); + } + normChange(prevProps) { + const { + entity + } = this.props; + if (prevProps.entity !== entity) { + this.execReset(); + } + } + execReset() { + const { + entity, + updateMetaPeaksAct, + resetInitCommonAct, + resetInitMsAct, + resetInitNmrAct, + resetInitCommonWithIntergationAct, + resetDetectorAct, + updateDSCMetaDataAct, + resetMultiplicityAct + } = this.props; + resetInitCommonAct(); + resetDetectorAct(); + const { + layout, + features + } = entity; + 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; + resetInitMsAct(baseFeat); + } else if (_format.default.isNmrLayout(layout)) { + const { + integration, + multiplicity, + simulation + } = features; + updateMetaPeaksAct(entity); + resetInitNmrAct({ + integration, + multiplicity, + simulation + }); + } else if (_format.default.isHplcUvVisLayout(layout)) { + const { + integration + } = features; + updateMetaPeaksAct(entity); + resetInitCommonWithIntergationAct({ + integration + }); + } else if (_format.default.isDSCLayout(layout)) { + const { + dscMetaData + } = features; + updateDSCMetaDataAct(dscMetaData); + } else { + resetMultiplicityAct(); + } + } + initReducer() { + const { + operations, + updateOperationAct + } = this.props; + updateOperationAct(operations[0]); + } + updateOthers() { + const { + others, + addOthersAct + } = this.props; + addOthersAct(others); + } + updateMultiEntities() { + const { + multiEntities, + setAllCurvesAct, + entity + } = this.props; + const isMultiSpectra = Array.isArray(multiEntities) && multiEntities.length > 1; + if (isMultiSpectra) { + setAllCurvesAct(multiEntities); + return; + } + if (_format.default.isCyclicVoltaLayout(entity.layout)) { + const payload = Array.isArray(multiEntities) && multiEntities.length > 0 ? multiEntities : [entity]; + setAllCurvesAct(payload); + return; + } + setAllCurvesAct(false); + } + render() { + const { + entity, + cLabel, + xLabel, + yLabel, + forecast, + operations, + descriptions, + molSvg, + editorOnly, + exactMass, + canChangeDescription, + onDescriptionChanged, + multiEntities, + entityFileNames, + userManualLink + } = this.props; + const target = entity.spectra[0]; + const { + layout + } = entity; + const xxLabel = !xLabel && xLabel === '' ? `X (${target.xUnit})` : xLabel; + const yyLabel = !yLabel && yLabel === '' ? `Y (${target.yUnit})` : yLabel; + const isMultiSpectra = Array.isArray(multiEntities) && multiEntities.length > 1; + if (isMultiSpectra) { + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_multi_jcamps_viewer.default, { + multiEntities: multiEntities, + entityFileNames: entityFileNames, + userManualLink: userManualLink, + molSvg: molSvg, + exactMass: exactMass, + operations: operations, + descriptions: descriptions, + canChangeDescription: canChangeDescription, + onDescriptionChanged: onDescriptionChanged + }); + } else if (_format.default.isCyclicVoltaLayout(layout)) { + // eslint-disable-line + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_multi_jcamps_viewer.default, { + multiEntities: [entity], + entityFileNames: entityFileNames, + userManualLink: userManualLink, + molSvg: molSvg, + exactMass: exactMass, + operations: operations, + descriptions: descriptions, + canChangeDescription: canChangeDescription, + onDescriptionChanged: onDescriptionChanged + }); + } + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_layer_prism.default, { + entity: entity, + cLabel: cLabel, + xLabel: xxLabel, + yLabel: yyLabel, + forecast: forecast, + operations: operations, + descriptions: descriptions, + molSvg: molSvg, + editorOnly: editorOnly, + exactMass: exactMass, + canChangeDescription: canChangeDescription, + onDescriptionChanged: onDescriptionChanged + }); + } +} +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({ + resetInitCommonAct: _manager.resetInitCommon, + resetInitNmrAct: _manager.resetInitNmr, + resetInitMsAct: _manager.resetInitMs, + resetInitCommonWithIntergationAct: _manager.resetInitCommonWithIntergation, + resetDetectorAct: _manager.resetDetector, + resetMultiplicityAct: _manager.resetMultiplicity, + updateOperationAct: _submit.updateOperation, + updateMetaPeaksAct: _meta.updateMetaPeaks, + addOthersAct: _jcamp.addOthers, + setAllCurvesAct: _curve.setAllCurves, + updateDSCMetaDataAct: _meta.updateDSCMetaData +}, dispatch); +LayerInit.propTypes = { + entity: _propTypes.default.object.isRequired, + multiEntities: _propTypes.default.array, + // eslint-disable-line + entityFileNames: _propTypes.default.array, + // eslint-disable-line + others: _propTypes.default.object.isRequired, + cLabel: _propTypes.default.string.isRequired, + xLabel: _propTypes.default.string.isRequired, + yLabel: _propTypes.default.string.isRequired, + molSvg: _propTypes.default.string.isRequired, + editorOnly: _propTypes.default.bool.isRequired, + exactMass: _propTypes.default.string.isRequired, + forecast: _propTypes.default.object.isRequired, + operations: _propTypes.default.array.isRequired, + descriptions: _propTypes.default.array.isRequired, + resetInitCommonAct: _propTypes.default.func.isRequired, + resetInitNmrAct: _propTypes.default.func.isRequired, + resetInitMsAct: _propTypes.default.func.isRequired, + resetInitCommonWithIntergationAct: _propTypes.default.func.isRequired, + updateOperationAct: _propTypes.default.func.isRequired, + updateMetaPeaksAct: _propTypes.default.func.isRequired, + addOthersAct: _propTypes.default.func.isRequired, + canChangeDescription: _propTypes.default.bool.isRequired, + onDescriptionChanged: _propTypes.default.func, + // eslint-disable-line + setAllCurvesAct: _propTypes.default.func.isRequired, + userManualLink: _propTypes.default.object, + // eslint-disable-line + resetDetectorAct: _propTypes.default.func.isRequired, + resetMultiplicityAct: _propTypes.default.func.isRequired, + updateDSCMetaDataAct: _propTypes.default.func.isRequired +}; +var _default = exports.default = (0, _reactRedux.connect)( +// eslint-disable-line +mapStateToProps, mapDispatchToProps)((0, _styles.withStyles)(styles)(LayerInit)); // eslint-disable-line \ No newline at end of file diff --git a/dist/layer_prism.js b/dist/layer_prism.js new file mode 100644 index 00000000..7680524a --- /dev/null +++ b/dist/layer_prism.js @@ -0,0 +1,150 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _react = _interopRequireDefault(require("react")); +var _propTypes = _interopRequireDefault(require("prop-types")); +var _reactRedux = require("react-redux"); +var _redux = require("redux"); +var _Grid = _interopRequireDefault(require("@mui/material/Grid")); +var _styles = require("@mui/styles"); +var _index = _interopRequireDefault(require("./components/panel/index")); +var _index2 = _interopRequireDefault(require("./components/cmd_bar/index")); +var _layer_content = _interopRequireDefault(require("./layer_content")); +var _list_ui = require("./constants/list_ui"); +var _extractParams = require("./helpers/extractParams"); +var _jsxRuntime = require("react/jsx-runtime"); +/* eslint-disable prefer-object-spread, default-param-last, +react/function-component-definition, react/require-default-props +*/ + +const styles = () => ({}); +const LayerPrism = ({ + entity, + cLabel, + xLabel, + yLabel, + forecast, + operations, + descriptions, + molSvg, + editorOnly, + exactMass, + thresSt, + scanSt, + uiSt, + canChangeDescription, + onDescriptionChanged +}) => { + const { + topic, + feature, + hasEdit, + integration + } = (0, _extractParams.extractParams)(entity, thresSt, scanSt); + if (!topic) return null; + const { + viewer + } = uiSt; + if (viewer === _list_ui.LIST_UI_VIEWER_TYPE.ANALYSIS) { + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index2.default, { + feature: feature, + hasEdit: hasEdit, + forecast: forecast, + operations: operations, + editorOnly: editorOnly + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: "react-spectrum-editor", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Grid.default, { + container: true, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Grid.default, { + item: true, + xs: 12, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_layer_content.default, { + topic: topic, + feature: feature, + cLabel: cLabel, + xLabel: xLabel, + yLabel: yLabel, + forecast: forecast, + operations: operations + }) + }) + }) + })] + }); + } + return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index2.default, { + feature: feature, + hasEdit: hasEdit, + forecast: forecast, + operations: operations, + editorOnly: editorOnly + }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { + className: "react-spectrum-editor", + children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Grid.default, { + container: true, + children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Grid.default, { + item: true, + xs: 9, + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_layer_content.default, { + topic: topic, + feature: feature, + cLabel: cLabel, + xLabel: xLabel, + yLabel: yLabel, + forecast: forecast, + operations: operations + }) + }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Grid.default, { + item: true, + xs: 3, + align: "center", + children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_index.default, { + feature: feature, + integration: integration, + editorOnly: editorOnly, + molSvg: molSvg, + exactMass: exactMass, + descriptions: descriptions, + canChangeDescription: canChangeDescription, + onDescriptionChanged: onDescriptionChanged + }) + })] + }) + })] + }); +}; +const mapStateToProps = (state, props) => ( +// eslint-disable-line +{ + scanSt: state.scan, + thresSt: state.threshold.list[state.curve.curveIdx], + uiSt: state.ui +}); +const mapDispatchToProps = dispatch => (0, _redux.bindActionCreators)({}, dispatch); +LayerPrism.propTypes = { + entity: _propTypes.default.object.isRequired, + cLabel: _propTypes.default.string.isRequired, + xLabel: _propTypes.default.string.isRequired, + yLabel: _propTypes.default.string.isRequired, + molSvg: _propTypes.default.string.isRequired, + editorOnly: _propTypes.default.bool.isRequired, + exactMass: _propTypes.default.string, + forecast: _propTypes.default.object.isRequired, + operations: _propTypes.default.array.isRequired, + descriptions: _propTypes.default.array.isRequired, + thresSt: _propTypes.default.object.isRequired, + scanSt: _propTypes.default.object.isRequired, + uiSt: _propTypes.default.object.isRequired, + canChangeDescription: _propTypes.default.bool.isRequired, + onDescriptionChanged: _propTypes.default.func +}; +var _default = exports.default = (0, _reactRedux.connect)( +// eslint-disable-line +mapStateToProps, mapDispatchToProps)((0, _styles.withStyles)(styles)(LayerPrism)); // eslint-disable-line \ No newline at end of file diff --git a/dist/reducers/index.js b/dist/reducers/index.js new file mode 100644 index 00000000..50868a3b --- /dev/null +++ b/dist/reducers/index.js @@ -0,0 +1,51 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _redux = require("redux"); +var _reducer_threshold = _interopRequireDefault(require("./reducer_threshold")); +var _reducer_edit_peak = _interopRequireDefault(require("./reducer_edit_peak")); +var _reducer_status = _interopRequireDefault(require("./reducer_status")); +var _reducer_manager = _interopRequireDefault(require("./reducer_manager")); +var _reducer_layout = _interopRequireDefault(require("./reducer_layout")); +var _reducer_shift = _interopRequireDefault(require("./reducer_shift")); +var _reducer_scan = _interopRequireDefault(require("./reducer_scan")); +var _reducer_forecast = _interopRequireDefault(require("./reducer_forecast")); +var _reducer_ui = _interopRequireDefault(require("./reducer_ui")); +var _reducer_submit = _interopRequireDefault(require("./reducer_submit")); +var _reducer_integration = _interopRequireDefault(require("./reducer_integration")); +var _reducer_multiplicity = _interopRequireDefault(require("./reducer_multiplicity")); +var _reducer_simulation = _interopRequireDefault(require("./reducer_simulation")); +var _reducer_meta = _interopRequireDefault(require("./reducer_meta")); +var _reducer_jcamp = _interopRequireDefault(require("./reducer_jcamp")); +var _reducer_wavelength = _interopRequireDefault(require("./reducer_wavelength")); +var _reducer_voltammetry = _interopRequireDefault(require("./reducer_voltammetry")); +var _reducer_curve = _interopRequireDefault(require("./reducer_curve")); +var _reducer_axes = _interopRequireDefault(require("./reducer_axes")); +var _reducer_detector = _interopRequireDefault(require("./reducer_detector")); +const rootReducer = (0, _redux.combineReducers)({ + threshold: _reducer_threshold.default, + editPeak: _reducer_edit_peak.default, + status: _reducer_status.default, + manager: _reducer_manager.default, + layout: _reducer_layout.default, + shift: _reducer_shift.default, + scan: _reducer_scan.default, + forecast: _reducer_forecast.default, + ui: _reducer_ui.default, + submit: _reducer_submit.default, + integration: _reducer_integration.default, + multiplicity: _reducer_multiplicity.default, + simulation: _reducer_simulation.default, + meta: _reducer_meta.default, + jcamp: _reducer_jcamp.default, + wavelength: _reducer_wavelength.default, + cyclicvolta: _reducer_voltammetry.default, + curve: _reducer_curve.default, + axesUnits: _reducer_axes.default, + detector: _reducer_detector.default +}); +var _default = exports.default = rootReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_axes.js b/dist/reducers/reducer_axes.js new file mode 100644 index 00000000..b9544a85 --- /dev/null +++ b/dist/reducers/reducer_axes.js @@ -0,0 +1,56 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +/* eslint-disable default-param-last, prefer-object-spread */ + +const initialState = { + axes: [{ + xUnit: '', + yUnit: '' + }] +}; +const updateAxis = (state, payload, isYAxis = false) => { + const { + value, + curveIndex + } = payload; + const { + axes + } = state; + let selectedAxes = axes[curveIndex]; + if (!selectedAxes) { + selectedAxes = { + xUnit: '', + yUnit: '' + }; + } + let newAxes = null; + if (isYAxis) { + newAxes = Object.assign({}, selectedAxes, { + yUnit: value + }); + } else { + newAxes = Object.assign({}, selectedAxes, { + xUnit: value + }); + } + axes[curveIndex] = newAxes; + return Object.assign({}, state, { + axes + }); +}; +const axesReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.AXES.UPDATE_X_AXIS: + return updateAxis(state, action.payload); + case _action_type.AXES.UPDATE_Y_AXIS: + return updateAxis(state, action.payload, true); + default: + return state; + } +}; +var _default = exports.default = axesReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_curve.js b/dist/reducers/reducer_curve.js new file mode 100644 index 00000000..91c11de1 --- /dev/null +++ b/dist/reducers/reducer_curve.js @@ -0,0 +1,78 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +var _extractParams = require("../helpers/extractParams"); +var _chem = require("../helpers/chem"); +var _format = _interopRequireDefault(require("../helpers/format")); +/* eslint-disable prefer-object-spread, default-param-last, max-len */ + +const initialState = { + listCurves: [], + curveIdx: 0, + isShowAllCurve: false +}; +const setAllCurves = (state, action) => { + const { + payload + } = action; + if (payload) { + const entities = payload.map((entity, idx) => { + const { + topic, + feature, + hasEdit, + integration, + multiplicity + } = (0, _extractParams.extractParams)(entity, { + isEdit: true + }); + // const layout = entity.layout; + const { + layout + } = entity; + const maxminPeak = (0, _chem.Convert2MaxMinPeak)(layout, feature, 0); + const color = _format.default.mutiEntitiesColors(idx); + return { + layout, + topic, + feature, + hasEdit, + integration, + multiplicity, + maxminPeak, + color, + curveIdx: idx + }; + }); + return Object.assign({}, state, { + curveIdx: 0, + listCurves: entities + }); + } + return Object.assign({}, state, { + curveIdx: 0, + listCurves: payload + }); +}; +const curveReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.CURVE.SELECT_WORKING_CURVE: + return Object.assign({}, state, { + curveIdx: action.payload + }); + case _action_type.CURVE.SET_ALL_CURVES: + return setAllCurves(state, action); + case _action_type.CURVE.SET_SHOULD_SHOW_ALL_CURVES: + return Object.assign({}, state, { + isShowAllCurve: action.payload + }); + default: + return state; + } +}; +var _default = exports.default = curveReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_detector.js b/dist/reducers/reducer_detector.js new file mode 100644 index 00000000..203cf350 --- /dev/null +++ b/dist/reducers/reducer_detector.js @@ -0,0 +1,50 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +/* eslint-disable no-case-declarations */ +/* eslint-disable default-param-last */ + +const initialState = { + curves: [{ + curveIdx: 0, + selectedDetector: '' + }] +}; +const findCurveIndex = (curves, targetCurveIdx) => curves.findIndex(curve => curve.curveIdx === targetCurveIdx); +const updateOrAppendCurve = (curves, targetCurveIdx, newCurve) => { + const existingCurveIndex = findCurveIndex(curves, targetCurveIdx); + if (existingCurveIndex !== -1) { + return curves.map((curve, index) => index === existingCurveIndex ? { + ...curve, + ...newCurve + } : curve); + } + return [...curves, newCurve]; +}; +const detectorReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.SEC.UPDATE_DETECTOR: + const { + curveIdx, + selectedDetector + } = action.payload; + // eslint-disable-next-line max-len + const updatedCurves = updateOrAppendCurve(state.curves, curveIdx, { + curveIdx, + selectedDetector + }); + return { + ...state, + curves: updatedCurves + }; + case _action_type.MANAGER.RESET_DETECTOR: + return initialState; + default: + return state; + } +}; +var _default = exports.default = detectorReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_edit_peak.js b/dist/reducers/reducer_edit_peak.js new file mode 100644 index 00000000..35d38bff --- /dev/null +++ b/dist/reducers/reducer_edit_peak.js @@ -0,0 +1,234 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.editPeakReducer = exports.default = void 0; +var _reduxUndo = _interopRequireDefault(require("redux-undo")); +var _action_type = require("../constants/action_type"); +var _undo_redo_config = require("./undo_redo_config"); +var _calc = require("../helpers/calc"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + selectedIdx: 0, + peaks: [{ + prevOffset: 0, + pos: [], + neg: [] + }] +}; +const defaultEmptyPeaks = { + prevOffset: 0, + pos: [], + neg: [] +}; +const addToPos = (state, action) => { + const { + peaks + } = state; + const { + payload + } = action; + const { + dataToAdd, + curveIdx + } = payload; + let selectedEditPeaks = peaks[curveIdx]; + if (!selectedEditPeaks) { + selectedEditPeaks = defaultEmptyPeaks; + } + const oriPosState = selectedEditPeaks.pos; + const oriNegState = selectedEditPeaks.neg; + const idxN = oriNegState.findIndex(n => (0, _calc.almostEqual)(n.x, dataToAdd.x)); + if (idxN >= 0) { + // rm the peak from oriNegState if it is already deleted. + const neg = [...oriNegState.slice(0, idxN), ...oriNegState.slice(idxN + 1)]; + const newSelectedEditPeaks = Object.assign({}, selectedEditPeaks, { + neg + }); + const newPeaks = [...peaks]; + newPeaks[curveIdx] = newSelectedEditPeaks; + return Object.assign({}, state, { + peaks: newPeaks + }); + } + const idxP = oriPosState.findIndex(p => (0, _calc.almostEqual)(p.x, dataToAdd.x)); + if (idxP < 0) { + // add the peak + const pos = [...oriPosState, dataToAdd]; + const newSelectedEditPeaks = Object.assign({}, selectedEditPeaks, { + pos + }); + const newPeaks = [...peaks]; + newPeaks[curveIdx] = newSelectedEditPeaks; + return Object.assign({}, state, { + peaks: newPeaks, + selectedIdx: curveIdx + }); + } + return state; +}; +const rmFromPos = (state, action) => { + const { + selectedIdx, + peaks + } = state; + const selectedEditPeaks = peaks[selectedIdx]; + const oriPosState = selectedEditPeaks.pos; + const idx = oriPosState.findIndex(p => p.x === action.payload.x); + const pos = [...oriPosState.slice(0, idx), ...oriPosState.slice(idx + 1)]; + const newSelectedEditPeaks = Object.assign({}, selectedEditPeaks, { + pos + }); + const newPeaks = [...peaks]; + newPeaks[selectedIdx] = newSelectedEditPeaks; + return Object.assign({}, state, { + peaks: newPeaks + }); +}; +const addToNeg = (state, action) => { + const { + peaks + } = state; + const { + payload + } = action; + const { + dataToAdd, + curveIdx + } = payload; + let selectedEditPeaks = peaks[curveIdx]; + if (!selectedEditPeaks) { + selectedEditPeaks = defaultEmptyPeaks; + } + const oriPosState = selectedEditPeaks.pos; + const oriNegState = selectedEditPeaks.neg; + const idxP = oriPosState.findIndex(n => n.x === dataToAdd.x); + if (idxP >= 0) { + const pos = [...oriPosState.slice(0, idxP), ...oriPosState.slice(idxP + 1)]; + const newSelectedEditPeaks = Object.assign({}, selectedEditPeaks, { + pos + }); + const newPeaks = [...peaks]; + newPeaks[curveIdx] = newSelectedEditPeaks; + return Object.assign({}, state, { + peaks: newPeaks + }); + } + const idxN = oriNegState.findIndex(n => n.x === dataToAdd.x); + if (idxN < 0) { + const neg = [...oriNegState, dataToAdd]; + const newSelectedEditPeaks = Object.assign({}, selectedEditPeaks, { + neg + }); + const newPeaks = [...peaks]; + newPeaks[curveIdx] = newSelectedEditPeaks; + return Object.assign({}, state, { + peaks: newPeaks, + selectedIdx: curveIdx + }); + } + return state; +}; +const rmFromNeg = (state, action) => { + const { + selectedIdx, + peaks + } = state; + const selectedEditPeaks = peaks[selectedIdx]; + const oriNegState = selectedEditPeaks.neg; + const idx = oriNegState.findIndex(n => n.x === action.payload.x); + const neg = [...oriNegState.slice(0, idx), ...oriNegState.slice(idx + 1)]; + const newSelectedEditPeaks = Object.assign({}, selectedEditPeaks, { + neg + }); + const newPeaks = [...peaks]; + newPeaks[selectedIdx] = newSelectedEditPeaks; + return Object.assign({}, state, { + peaks: newPeaks + }); +}; +const processShift = (state, action) => { + const { + payload + } = action; + const { + curveIdx + } = action.payload; + const { + peaks + } = state; + let selectedEditPeaks = peaks[curveIdx]; + if (!selectedEditPeaks) { + selectedEditPeaks = defaultEmptyPeaks; + } + const { + pos, + neg, + prevOffset + } = payload; + const newSelectedEditPeaks = Object.assign({}, selectedEditPeaks, { + pos, + neg, + prevOffset + }); + const newPeaks = [...peaks]; + newPeaks[curveIdx] = newSelectedEditPeaks; + return Object.assign({}, state, { + peaks: newPeaks + }); +}; +const clearAllPeaks = (state, action) => { + const { + curveIdx, + dataPeaks + } = action.payload; + const { + peaks + } = state; + const selectedEditPeaks = peaks[curveIdx]; + const { + pos + } = selectedEditPeaks; + const newSelectedEditPeaks = Object.assign({}, selectedEditPeaks, { + pos: [], + neg: [...pos, ...dataPeaks] + }); + const newPeaks = [...peaks]; + newPeaks[curveIdx] = newSelectedEditPeaks; + return Object.assign({}, state, { + peaks: newPeaks + }); +}; +const editPeakReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.EDITPEAK.ADD_POSITIVE: + return addToPos(state, action); + case _action_type.EDITPEAK.ADD_NEGATIVE: + return addToNeg(state, action); + case _action_type.EDITPEAK.RM_POSITIVE: + return rmFromPos(state, action); + case _action_type.EDITPEAK.RM_NEGATIVE: + return rmFromNeg(state, action); + case _action_type.EDITPEAK.SHIFT: + return processShift(state, action); + case _action_type.EDITPEAK.CLEAR_ALL: + return clearAllPeaks(state, action); + case _action_type.MANAGER.RESETALL: + return { + selectedIdx: 0, + peaks: [{ + prevOffset: 0, + pos: [], + neg: [] + }] + }; + default: + return _undo_redo_config.undoRedoActions.indexOf(action.type) >= 0 ? Object.assign({}, state) : state; + } +}; +exports.editPeakReducer = editPeakReducer; +const undoableEditPeakReducer = (0, _reduxUndo.default)(editPeakReducer, _undo_redo_config.undoRedoConfig); +var _default = exports.default = undoableEditPeakReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_forecast.js b/dist/reducers/reducer_forecast.js new file mode 100644 index 00000000..7dcb6c05 --- /dev/null +++ b/dist/reducers/reducer_forecast.js @@ -0,0 +1,120 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + predictions: { + outline: {}, + output: { + result: [] + } + } +}; +const updateIrResl = (stResl, plPred) => { + const { + sma, + identity, + value + } = plPred; + const { + svgs + } = stResl; + const prevFgs = stResl.fgs; + const nextVal = { + [`status${identity}`]: value + }; + const nextFgs = prevFgs.map(fg => { + if (fg.sma === sma) { + return Object.assign({}, fg, nextVal); + } + return fg; + }); + const nextResult = { + type: 'ir', + fgs: nextFgs, + svgs + }; + return nextResult; +}; +const updateIrStatus = (state, action) => { + const { + predictions + } = action.payload; + const { + outline, + output + } = state.predictions; + const stResl = output.result[0]; + const nextResl = updateIrResl(stResl, predictions); + return Object.assign({}, state, { + predictions: { + outline, + output: { + result: [nextResl] + } + } + }); +}; +const updateNmrResl = (stResl, plPred) => { + const { + idx, + atom, + identity, + value + } = plPred; + const preResult = stResl; + const nextShifts = preResult.shifts.map((s, index) => { + if (s.atom === atom && index === idx) { + return Object.assign({}, s, { + [`status${identity}`]: value + }); + } + return s; + }); + const nextResult = Object.assign({}, preResult, { + shifts: nextShifts + }); + return nextResult; +}; +const updateNmrStatus = (state, action) => { + const { + predictions + } = action.payload; + const { + outline, + output + } = state.predictions; + const stResl = output.result[0]; + const nextResl = updateNmrResl(stResl, predictions); + const newSt = Object.assign({}, state, { + predictions: { + outline, + output: { + result: [nextResl] + } + } + }); + return newSt; +}; +const forecastReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.FORECAST.INIT_STATUS: + if (!action.payload) return state; + return Object.assign({}, action.payload); + case _action_type.FORECAST.SET_IR_STATUS: + return updateIrStatus(state, action); + case _action_type.FORECAST.SET_NMR_STATUS: + return updateNmrStatus(state, action); + case _action_type.FORECAST.CLEAR_STATUS: + case _action_type.MANAGER.RESETALL: + return initialState; + default: + return state; + } +}; +var _default = exports.default = forecastReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_integration.js b/dist/reducers/reducer_integration.js new file mode 100644 index 00000000..ce3daefe --- /dev/null +++ b/dist/reducers/reducer_integration.js @@ -0,0 +1,298 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.integrationReducer = exports.default = void 0; +var _reduxUndo = _interopRequireDefault(require("redux-undo")); +var _action_type = require("../constants/action_type"); +var _integration = require("../helpers/integration"); +var _undo_redo_config = require("./undo_redo_config"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + selectedIdx: 0, + integrations: [{ + stack: [], + refArea: 1, + refFactor: 1, + shift: 0, + edited: false + }] +}; +const defaultEmptyIntegration = { + stack: [], + refArea: 1, + refFactor: 1, + shift: 0, + edited: false +}; +const addToStack = (state, action) => { + const { + newData, + curveIdx + } = action.payload; + const { + integrations + } = state; + let selectedIntegration = integrations[curveIdx]; + if (selectedIntegration === false || selectedIntegration === undefined) { + selectedIntegration = defaultEmptyIntegration; + } + const { + stack, + refArea, + shift + } = selectedIntegration; + const { + xExtent, + data + } = newData; + const { + xL, + xU + } = xExtent; + if (!Number.isFinite(xL) || !Number.isFinite(xU) || xU - xL === 0) { + return state; + } + const area = (0, _integration.getArea)(xL, xU, data); + const defaultRefArea = stack.length === 0 ? area : refArea; + const absoluteArea = (0, _integration.getAbsoluteArea)(xL, xU, data); // area depends on y baseline + const newStack = [...stack, { + xL: xL + shift, + xU: xU + shift, + area, + absoluteArea + }]; + const newIntegration = Object.assign({}, selectedIntegration, { + stack: newStack, + refArea: defaultRefArea + }); + const newArrIntegration = [...integrations]; + newArrIntegration[curveIdx] = newIntegration; + return Object.assign({}, state, { + integrations: newArrIntegration, + selectedIdx: curveIdx + }); +}; +const rmFromStack = (state, action) => { + const { + dataToRemove, + curveIdx + } = action.payload; + const { + xL, + xU, + xExtent + } = dataToRemove; + const { + integrations + } = state; + const selectedIntegration = integrations[curveIdx]; + const { + stack + } = selectedIntegration; + let [txL, txU] = [0, 0]; + if (Number.isFinite(xL) && Number.isFinite(xU)) { + [txL, txU] = [xL, xU]; + } else if (xExtent) { + [txL, txU] = [xExtent.xL, xExtent.xU]; + } else { + return state; + } + const newStack = stack.filter(k => k.xL !== txL && k.xU !== txU); + const newIntegration = Object.assign({}, selectedIntegration, { + stack: newStack + }); + const newArrIntegration = [...integrations]; + newArrIntegration[curveIdx] = newIntegration; + return Object.assign({}, state, { + integrations: newArrIntegration, + selectedIdx: curveIdx + }); +}; +const hasEnoughDataResolution = (xL, xU, data) => { + const [lower, upper] = [xL, xU].sort((a, b) => a - b); + const points = data.filter(pt => pt && Number.isFinite(pt.x) && pt.x >= lower && pt.x <= upper); + if (points.length < 2) return false; + return points.some(pt => pt.x !== points[0].x); +}; +const buildSplitStackItem = (xL, xU, data, shift) => { + const [lower, upper] = [xL, xU].sort((a, b) => a - b); + const area = (0, _integration.getArea)(lower, upper, data); + const absoluteArea = (0, _integration.getAbsoluteArea)(lower, upper, data); + return { + xL: lower + shift, + xU: upper + shift, + area, + absoluteArea + }; +}; +const splitStack = (state, action) => { + const { + curveIdx, + target, + splitX, + data + } = action.payload; + if (!Number.isFinite(curveIdx) || !target || !Array.isArray(data)) { + return state; + } + const { + integrations + } = state; + const selectedIntegration = integrations[curveIdx]; + if (!selectedIntegration || selectedIntegration === false) { + return state; + } + const { + stack, + shift + } = selectedIntegration; + const targetIndex = stack.findIndex(item => item.xL === target.xL && item.xU === target.xU); + if (targetIndex < 0 || !Number.isFinite(splitX)) { + return state; + } + const original = stack[targetIndex]; + const [xL, xU] = [original.xL - shift, original.xU - shift].sort((a, b) => a - b); + if (!Number.isFinite(xL) || !Number.isFinite(xU) || splitX <= xL || splitX >= xU) { + return state; + } + if (!hasEnoughDataResolution(xL, splitX, data) || !hasEnoughDataResolution(splitX, xU, data)) { + return state; + } + const leftIntegration = buildSplitStackItem(xL, splitX, data, shift); + const rightIntegration = buildSplitStackItem(splitX, xU, data, shift); + const newStack = [...stack.slice(0, targetIndex), leftIntegration, rightIntegration, ...stack.slice(targetIndex + 1)]; + const newIntegration = Object.assign({}, selectedIntegration, { + stack: newStack + }); + const newArrIntegration = [...integrations]; + newArrIntegration[curveIdx] = newIntegration; + return Object.assign({}, state, { + integrations: newArrIntegration, + selectedIdx: curveIdx + }); +}; +const setRef = (state, action) => { + const { + refData, + curveIdx + } = action.payload; + const { + integrations + } = state; + const selectedIntegration = integrations[curveIdx]; + const { + stack + } = selectedIntegration; + const { + xL, + xU + } = refData; + const ref = stack.filter(k => k.xL === xL && k.xU === xU)[0]; + if (!ref) { + return state; + } + const refArea = ref.area; + const newIntegration = Object.assign({}, selectedIntegration, { + refArea + }); + const newArrIntegration = [...integrations]; + newArrIntegration[curveIdx] = newIntegration; + return Object.assign({}, state, { + integrations: newArrIntegration, + selectedIdx: curveIdx + }); +}; +const setFkr = (state, action) => { + const { + payload + } = action; + const { + curveIdx, + factor + } = payload; + const { + integrations + } = state; + const selectedIntegration = integrations[curveIdx]; + const val = parseFloat(factor); + const refFactor = val < 0.01 ? 0.01 : val; + const newIntegration = Object.assign({}, selectedIntegration, { + refFactor + }); + const newArrIntegration = [...integrations]; + newArrIntegration[curveIdx] = newIntegration; + return Object.assign({}, state, { + integrations: newArrIntegration + }); +}; +const setShift = (state, action) => { + const { + selectedIdx, + integrations + } = state; + const selectedIntegration = integrations[selectedIdx]; + const shift = action.payload.prevOffset; + const newIntegration = Object.assign({}, selectedIntegration, { + shift + }); + const newArrIntegration = [...integrations]; + newArrIntegration[selectedIdx] = newIntegration; + return Object.assign({}, state, { + integrations: newArrIntegration + }); +}; +const resetAll = (state, action) => { + const newState = action.payload; + return Object.assign({}, state, newState); +}; +const clearAll = (state, action) => { + const { + payload + } = action; + const { + curveIdx + } = payload; + const { + integrations + } = state; + const newIntegration = Object.assign({}, defaultEmptyIntegration, { + edited: true + }); + const newArrIntegration = [...integrations]; + newArrIntegration[curveIdx] = newIntegration; + return Object.assign({}, state, { + integrations: newArrIntegration, + selectedIdx: curveIdx + }); +}; +const integrationReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.UI.SWEEP.SELECT_INTEGRATION: + return addToStack(state, action); + case _action_type.INTEGRATION.RM_ONE: + return rmFromStack(state, action); + case _action_type.INTEGRATION.SPLIT: + return splitStack(state, action); + case _action_type.INTEGRATION.SET_REF: + return setRef(state, action); + case _action_type.INTEGRATION.SET_FKR: + return setFkr(state, action); + case _action_type.INTEGRATION.RESET_ALL_RDC: + return resetAll(state, action); + case _action_type.INTEGRATION.CLEAR_ALL: + return clearAll(state, action); + case _action_type.EDITPEAK.SHIFT: + return setShift(state, action); + case _action_type.MANAGER.RESETALL: + return state; + default: + return _undo_redo_config.undoRedoActions.indexOf(action.type) >= 0 ? Object.assign({}, state) : state; + } +}; +exports.integrationReducer = integrationReducer; +const undoableIntegrationReducer = (0, _reduxUndo.default)(integrationReducer, _undo_redo_config.undoRedoConfig); +var _default = exports.default = undoableIntegrationReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_jcamp.js b/dist/reducers/reducer_jcamp.js new file mode 100644 index 00000000..6c18ec0f --- /dev/null +++ b/dist/reducers/reducer_jcamp.js @@ -0,0 +1,99 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + selectedIdx: 0, + jcamps: [{ + others: [], + addOthersCb: false + }] +}; +const addOthers = (state, { + others, + addOthersCb +}) => { + const { + selectedIdx, + jcamps + } = state; + const selectedJcamp = jcamps[selectedIdx]; + if (selectedJcamp.others.length > 5) return state; + const decoOthers = others.map(o => Object.assign({}, o, { + show: true + })); + const newJcamp = Object.assign({}, selectedJcamp, { + others: [...decoOthers].slice(0, 5), + addOthersCb + }); + jcamps[selectedIdx] = newJcamp; + return Object.assign({}, state, { + jcamps + }); +}; +const rmOthersOne = (state, payload) => { + const { + selectedIdx, + jcamps + } = state; + const selectedJcamp = jcamps[selectedIdx]; + const idx = payload; + const { + others + } = selectedJcamp; + const nextOther = others.filter((_, i) => i !== idx); + const newJcamp = Object.assign({}, selectedJcamp, { + others: nextOther + }); + jcamps[selectedIdx] = newJcamp; + return Object.assign({}, state, { + jcamps + }); +}; +const toggleShow = (state, payload) => { + const { + selectedIdx, + jcamps + } = state; + const selectedJcamp = jcamps[selectedIdx]; + const idx = payload; + const { + others + } = selectedJcamp; + const nextOthers = others.map((o, i) => { + if (i !== idx) return o; + const currentShow = o.show; + return Object.assign({}, o, { + show: !currentShow + }); + }); + const newJcamp = Object.assign({}, selectedJcamp, { + others: nextOthers + }); + jcamps[selectedIdx] = newJcamp; + return Object.assign({}, state, { + jcamps + }); +}; +const layoutReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.JCAMP.ADD_OTHERS: + return addOthers(state, action.payload); + case _action_type.JCAMP.RM_OTHERS_ONE: + return rmOthersOne(state, action.payload); + case _action_type.JCAMP.TOGGLE_SHOW: + return toggleShow(state, action.payload); + case _action_type.JCAMP.CLEAR_ALL: + return initialState; + case _action_type.MANAGER.RESETALL: + return initialState; + default: + return state; + } +}; +var _default = exports.default = layoutReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_layout.js b/dist/reducers/reducer_layout.js new file mode 100644 index 00000000..c264e570 --- /dev/null +++ b/dist/reducers/reducer_layout.js @@ -0,0 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +var _list_layout = require("../constants/list_layout"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = _list_layout.LIST_LAYOUT.C13; +const layoutReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.LAYOUT.UPDATE: + return action.payload; + case _action_type.MANAGER.RESETALL: + return action.payload.operation.layout; + default: + return state; + } +}; +var _default = exports.default = layoutReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_manager.js b/dist/reducers/reducer_manager.js new file mode 100644 index 00000000..2de439dd --- /dev/null +++ b/dist/reducers/reducer_manager.js @@ -0,0 +1,17 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +/* eslint-disable prefer-object-spread, default-param-last */ +// import { MANAGER } from '../constants/action_type'; + +const initialState = {}; +const managerReducer = (state = initialState, action) => { + switch (action.type) { + default: + return state; + } +}; +var _default = exports.default = managerReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_meta.js b/dist/reducers/reducer_meta.js new file mode 100644 index 00000000..3b92cb0e --- /dev/null +++ b/dist/reducers/reducer_meta.js @@ -0,0 +1,40 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + peaks: { + intervalL: null, + intervalR: null, + observeFrequency: null, + deltaX: null + }, + dscMetaData: { + meltingPoint: null, + tg: null + } +}; +const updateMetaData = (state, action) => { + const { + dscMetaData + } = action.payload; + return Object.assign({}, state, { + dscMetaData + }); +}; +const metaReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.META.UPDATE_PEAKS_RDC: + return Object.assign({}, state, action.payload); + case _action_type.META.UPDATE_META_DATA_RDC: + return updateMetaData(state, action); + default: + return state; + } +}; +var _default = exports.default = metaReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_multiplicity.js b/dist/reducers/reducer_multiplicity.js new file mode 100644 index 00000000..651182e1 --- /dev/null +++ b/dist/reducers/reducer_multiplicity.js @@ -0,0 +1,199 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _reduxUndo = _interopRequireDefault(require("redux-undo")); +var _action_type = require("../constants/action_type"); +var _undo_redo_config = require("./undo_redo_config"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + selectedIdx: 0, + multiplicities: [{ + stack: [], + shift: 0, + smExtext: false, + edited: false + }] +}; +const defaultEmptyMultiplicity = { + stack: [], + shift: 0, + smExtext: false, + edited: false +}; +const setShift = (state, action) => { + const shift = action.payload.prevOffset; + const { + selectedIdx, + multiplicities + } = state; + const selectedMulti = multiplicities[selectedIdx]; + const newSelectedMulti = Object.assign({}, selectedMulti, { + shift + }); + const newMultiplicities = [...multiplicities]; + newMultiplicities[selectedIdx] = newSelectedMulti; + return Object.assign({}, state, { + multiplicities: newMultiplicities + }); +}; +const rmFromStack = (state, action) => { + const { + dataToRemove, + curveIdx + } = action.payload; + const { + multiplicities + } = state; + const selectedMulti = multiplicities[curveIdx]; + const { + stack + } = selectedMulti; + const { + xL, + xU, + xExtent + } = dataToRemove; + let [txL, txU] = [0, 0]; + if (xL && xU) { + // rm click integration + [txL, txU] = [xL, xU]; + } else if (xExtent) { + // rm click multiplicity + [txL, txU] = [xExtent.xL, xExtent.xU]; + } else { + return state; + } + const newStack = stack.filter(k => { + const [kxL, kxU] = [k.xExtent.xL, k.xExtent.xU]; + return kxL !== txL && kxU !== txU; + }); + const newSmExtext = newStack[0] ? newStack[0].xExtent : false; + const newSelectedMulti = Object.assign({}, selectedMulti, { + stack: newStack, + smExtext: newSmExtext + }); + const newMultiplicities = [...multiplicities]; + newMultiplicities[curveIdx] = newSelectedMulti; + return Object.assign({}, state, { + multiplicities: newMultiplicities, + selectedIdx: curveIdx + }); +}; +const updateMpyJ = (state, action) => { + const { + payload + } = action; + const { + xExtent, + value + } = payload; + if (!value && value !== '') return state; + const { + selectedIdx, + multiplicities + } = state; + const selectedMulti = multiplicities[selectedIdx]; + const { + stack + } = selectedMulti; + const regx = /[^0-9.,-]/g; + const js = value.replace(regx, '').split(',').map(j => parseFloat(j)).filter(j => j); + const newStack = stack.map(k => { + if (k.xExtent.xL === xExtent.xL && k.xExtent.xU === xExtent.xU) { + if (k.mpyType === 'm') return Object.assign({}, k, { + js: [] + }); + return Object.assign({}, k, { + js + }); + } + return k; + }); + const newSelectedMulti = Object.assign({}, selectedMulti, { + stack: newStack + }); + const newMultiplicities = [...multiplicities]; + newMultiplicities[selectedIdx] = newSelectedMulti; + return Object.assign({}, state, { + multiplicities: newMultiplicities + }); +}; +const clickMpyOne = (state, action) => { + const { + payload + } = action; + const { + curveIdx, + payloadData + } = payload; + const { + multiplicities + } = state; + const selectedMulti = multiplicities[curveIdx]; + const newSelectedMulti = Object.assign({}, selectedMulti, { + smExtext: payloadData + }); + const newMultiplicities = [...multiplicities]; + newMultiplicities[curveIdx] = newSelectedMulti; + return Object.assign({}, state, { + multiplicities: newMultiplicities, + selectedIdx: curveIdx + }); +}; +const clearAll = (state, action) => { + const { + payload + } = action; + const { + curveIdx + } = payload; + const { + multiplicities + } = state; + const newSelectedMulti = Object.assign({}, defaultEmptyMultiplicity, { + edited: true + }); + const newMultiplicities = [...multiplicities]; + newMultiplicities[curveIdx] = newSelectedMulti; + return Object.assign({}, state, { + multiplicities: newMultiplicities + }); +}; +const multiplicityReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.EDITPEAK.SHIFT: + return setShift(state, action); + case _action_type.INTEGRATION.RM_ONE: + return rmFromStack(state, action); + case _action_type.UI.SWEEP.SELECT_MULTIPLICITY_RDC: + case _action_type.MULTIPLICITY.PEAK_RM_BY_PANEL_RDC: + case _action_type.MULTIPLICITY.PEAK_RM_BY_UI_RDC: + case _action_type.MULTIPLICITY.PEAK_ADD_BY_UI_RDC: + case _action_type.MULTIPLICITY.RESET_ONE_RDC: + return action.payload; + case _action_type.MULTIPLICITY.UPDATE_J: + return updateMpyJ(state, action); + case _action_type.MULTIPLICITY.TYPE_SELECT_RDC: + return action.payload; + case _action_type.MULTIPLICITY.ONE_CLICK: + case _action_type.MULTIPLICITY.ONE_CLICK_BY_UI: + return clickMpyOne(state, action); + case _action_type.MULTIPLICITY.RESET_ALL_RDC: + return action.payload; + case _action_type.MULTIPLICITY.CLEAR_ALL: + return clearAll(state, action); + case _action_type.MANAGER.RESETALL: + return state; + case _action_type.MANAGER.RESET_MULTIPLICITY: + return initialState; + default: + return _undo_redo_config.undoRedoActions.indexOf(action.type) >= 0 ? Object.assign({}, state) : state; + } +}; +const undoableMultiplicityReducer = (0, _reduxUndo.default)(multiplicityReducer, _undo_redo_config.undoRedoConfig); +var _default = exports.default = undoableMultiplicityReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_scan.js b/dist/reducers/reducer_scan.js new file mode 100644 index 00000000..99fe706b --- /dev/null +++ b/dist/reducers/reducer_scan.js @@ -0,0 +1,46 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + target: false, + count: 1, + isAuto: true +}; +const setTarget = (state, payload) => Object.assign({}, state, { + target: payload +}); +const resetAll = (state, payload) => { + const { + scanCount, + scanEditTarget + } = payload; + return Object.assign({}, state, { + target: false, + count: parseInt(scanCount, 10), + isAuto: !scanEditTarget + }); +}; +const toggleIsAuto = state => Object.assign({}, state, { + isAuto: !state.isAuto, + target: false +}); +const scanReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.SCAN.SET_TARGET: + case _action_type.SCAN.RESET_TARGET: + return setTarget(state, action.payload); + case _action_type.SCAN.TOGGLE_ISAUTO: + return toggleIsAuto(state); + case _action_type.MANAGER.RESET_INIT_MS: + return resetAll(state, action.payload); + default: + return state; + } +}; +var _default = exports.default = scanReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_shift.js b/dist/reducers/reducer_shift.js new file mode 100644 index 00000000..310ac7d8 --- /dev/null +++ b/dist/reducers/reducer_shift.js @@ -0,0 +1,245 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +var _list_shift = require("../constants/list_shift"); +var _shift = require("../helpers/shift"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const shiftNone = _list_shift.LIST_SHIFT_1H[0]; +const normalizeShiftName = input => (input || '').toString().normalize('NFKD').replace(/[^a-z0-9]+/gi, '').toLowerCase(); + +// const initialState = { +// ref: shiftNone, +// peak: false, +// enable: true, +// }; + +const initialState = { + selectedIdx: 0, + shifts: [{ + ref: shiftNone, + peak: false, + enable: true + }] +}; +const defaultEmptyShift = { + ref: shiftNone, + peak: false, + enable: true +}; +const resetRef = payload => { + const { + shift, + layout + } = payload; + if (!shift || !shift.solventName || !shift.solventValue) return shiftNone; + const name = shift.solventName; + const normalizedName = normalizeShiftName(name); + let target = false; + const listShift = (0, _list_shift.getListShift)(layout); + listShift.forEach(l => { + if (normalizeShiftName(l.name) === normalizedName) { + target = l; + } + }); + return target || shiftNone[0]; +}; +const resetEnable = payload => { + const { + typ + } = payload.operation; + switch (typ) { + case 'NMR': + return true; + default: + return false; + } +}; +const resetShift = (state, action) => { + const { + payload + } = action; + const { + curvesInfo + } = payload; + const { + isMultiCurve, + curveIdx, + numberOfCurve + } = curvesInfo; + const { + shifts + } = state; + let selectedShift = shifts[curveIdx]; + if (selectedShift === false || selectedShift === undefined) { + selectedShift = defaultEmptyShift; + } + if (isMultiCurve) { + for (let idx = 0; idx < numberOfCurve; idx += 1) { + const checkShift = shifts[idx]; + if (!checkShift) { + shifts[idx] = defaultEmptyShift; + } + } + } + const newShift = Object.assign({}, defaultEmptyShift, { + ref: resetRef(payload), + enable: resetEnable(payload) + }); + shifts[curveIdx] = newShift; + return Object.assign({}, state, { + shifts, + selectedIdx: curveIdx + }); +}; +const updateShift = (state, action) => { + // eslint-disable-line + const { + selectedIdx, + shifts + } = state; + let selectedShift = shifts[selectedIdx]; + if (selectedShift === false || selectedShift === undefined) { + selectedShift = defaultEmptyShift; + } + const newShift = Object.assign({}, selectedShift, { + ref: false, + enable: selectedShift.enable + }); + shifts[selectedIdx] = newShift; + return Object.assign({}, state, { + shifts, + selectedIdx + }); +}; +const setRef = (state, action) => { + const { + payload + } = action; + const { + dataToSet, + curveIdx + } = payload; + const { + shifts + } = state; + let selectedShift = shifts[curveIdx]; + if (selectedShift === false || selectedShift === undefined) { + selectedShift = defaultEmptyShift; + } + const newShift = Object.assign({}, selectedShift, { + ref: dataToSet, + enable: true + }); + shifts[curveIdx] = newShift; + return Object.assign({}, state, { + shifts, + selectedIdx: curveIdx + }); +}; +const setPeak = (state, action) => { + const { + payload + } = action; + const { + dataToSet, + curveIdx + } = payload; + const { + shifts + } = state; + let selectedShift = shifts[curveIdx]; + if (selectedShift === false || selectedShift === undefined) { + selectedShift = defaultEmptyShift; + } + const resX = (0, _shift.CalcResidualX)(selectedShift.ref, selectedShift.peak, dataToSet); + const trueApex = (0, _shift.RealPts)([dataToSet], resX)[0]; + const isSamePt = selectedShift.peak.x === trueApex.x; + const truePeak = trueApex && trueApex.x && !isSamePt ? trueApex : false; + const newShift = Object.assign({}, selectedShift, { + peak: truePeak, + enable: true + }); + shifts[curveIdx] = newShift; + return Object.assign({}, state, { + shifts, + selectedIdx: curveIdx + }); +}; +const removePeak = (state, action) => { + // eslint-disable-line + const { + selectedIdx, + shifts + } = state; + let selectedShift = shifts[selectedIdx]; + if (selectedShift === false || selectedShift === undefined) { + selectedShift = defaultEmptyShift; + } + const newShift = Object.assign({}, selectedShift, { + peak: false, + enable: true + }); + shifts[selectedIdx] = newShift; + return Object.assign({}, state, { + shifts, + selectedIdx + }); +}; +const addNegative = (state, action) => { + const { + payload + } = action; + const { + dataToAdd, + curveIdx + } = payload; + const { + shifts + } = state; + let selectedShift = shifts[curveIdx]; + if (selectedShift === false || selectedShift === undefined) { + selectedShift = defaultEmptyShift; + } + const rmApex = selectedShift.peak.x === dataToAdd.x; + if (!rmApex) { + return state; + } + const newShift = Object.assign({}, selectedShift, { + peak: false, + enable: true + }); + shifts[curveIdx] = newShift; + return Object.assign({}, state, { + shifts, + selectedIdx: curveIdx + }); +}; +const shiftReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.SHIFT.SET_REF: + return setRef(state, action); + case _action_type.SHIFT.SET_PEAK: + { + return setPeak(state, action); + } + case _action_type.SHIFT.RM_PEAK: + return removePeak(state, action); + case _action_type.EDITPEAK.ADD_NEGATIVE: + { + return addNegative(state, action); + } + case _action_type.LAYOUT.UPDATE: + return updateShift(initialState, action); + case _action_type.MANAGER.RESETSHIFT: + // case MANAGER.RESETALL: + return resetShift(initialState, action); + default: + return state; + } +}; +var _default = exports.default = shiftReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_simulation.js b/dist/reducers/reducer_simulation.js new file mode 100644 index 00000000..81d0df10 --- /dev/null +++ b/dist/reducers/reducer_simulation.js @@ -0,0 +1,25 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + nmrSimPeaks: [] +}; +const resetAll = (state, action) => { + const newState = action.payload; + return Object.assign({}, state, newState); +}; +const simulatioinReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.SIMULATION.RESET_ALL_RDC: + return resetAll(state, action); + default: + return state; + } +}; +var _default = exports.default = simulatioinReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_status.js b/dist/reducers/reducer_status.js new file mode 100644 index 00000000..e14b5f7c --- /dev/null +++ b/dist/reducers/reducer_status.js @@ -0,0 +1,45 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + btnSubmit: false +}; +const statusReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.STATUS.TOGGLEBTNSUBMIT: + return Object.assign({}, state, { + btnSubmit: false + } // !state.btnSubmit + ); + case _action_type.STATUS.TOGGLEBTNALL: + return Object.assign({}, state, { + btnSubmit: false + } // !state.btnSubmit + ); + case _action_type.STATUS.ENABLEBTNALL: + case _action_type.EDITPEAK.ADDPOSITIVE: + case _action_type.EDITPEAK.RMPOSITIVE: + case _action_type.EDITPEAK.ADDNEGATIVE: + case _action_type.EDITPEAK.RMNEGATIVE: + case _action_type.THRESHOLD.UPDATE_VALUE: + case _action_type.THRESHOLD.RESET_VALUE: + return Object.assign({}, state, { + btnSubmit: false + }); + case _action_type.LAYOUT.UPDATE: + return Object.assign({}, state, { + btnSubmit: false + }); + case _action_type.MANAGER.RESETALL: + return initialState; + default: + return initialState; + } +}; +var _default = exports.default = statusReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_submit.js b/dist/reducers/reducer_submit.js new file mode 100644 index 00000000..6fc47bf8 --- /dev/null +++ b/dist/reducers/reducer_submit.js @@ -0,0 +1,59 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +var _format = _interopRequireDefault(require("../helpers/format")); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + isAscend: false, + isIntensity: true, + decimal: 2, + operation: { + name: 'empty' + } +}; +const updateOperation = action => ({ + operation: action.payload || initialState.operation +}); +const submitReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.SUBMIT.TOGGLE_IS_ASCEND: + return Object.assign({}, state, { + isAscend: !state.isAscend + }); + case _action_type.SUBMIT.TOGGLE_IS_INTENSITY: + return Object.assign({}, state, { + isIntensity: !state.isIntensity + }); + case _action_type.SUBMIT.UPDATE_OPERATION: + return Object.assign({}, state, updateOperation(action)); + case _action_type.SUBMIT.UPDATE_DECIMAL: + return Object.assign({}, state, { + decimal: action.payload.target.value + }); + case _action_type.LAYOUT.UPDATE: + { + const decimal = _format.default.spectraDigit(action.payload); + return Object.assign({}, state, { + decimal + }); + } + case _action_type.MANAGER.RESETALL: + { + const decimal = _format.default.spectraDigit(action.payload.operation.layout); + return Object.assign({}, state, { + decimal, + isIntensity: true, + isAscend: false + }); + } + default: + return state; + } +}; +var _default = exports.default = submitReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_threshold.js b/dist/reducers/reducer_threshold.js new file mode 100644 index 00000000..7fc7f727 --- /dev/null +++ b/dist/reducers/reducer_threshold.js @@ -0,0 +1,222 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + selectedIdx: 0, + list: [{ + 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; + const { + list, + selectedIdx + } = state; + if (payload) { + const { + value, + curveIdx + } = payload; + const selectedThres = list[curveIdx]; + const newSelectedThres = Object.assign({}, selectedThres, { + value + }); + const newListThres = [...list]; + newListThres[curveIdx] = newSelectedThres; + return Object.assign({}, state, { + list: newListThres, + selectedIdx: curveIdx + }); + } + const selectedThres = list[selectedIdx]; + const newSelectedThres = Object.assign({}, selectedThres, { + value: payload + }); + const newListThres = [...list]; + newListThres[selectedIdx] = newSelectedThres; + return Object.assign({}, state, { + list: newListThres + }); +}; +const setThresHoldUpper = (state, action) => { + const { + payload + } = action; + const { + list, + selectedIdx + } = state; + if (payload) { + const { + value, + curveIdx + } = payload; + const selectedThres = list[curveIdx]; + const newSelectedThres = Object.assign({}, selectedThres, { + upper: value + }); + const newListThres = [...list]; + newListThres[curveIdx] = newSelectedThres; + return Object.assign({}, state, { + list: newListThres, + selectedIdx: curveIdx + }); + } + const selectedThres = list[selectedIdx]; + const newSelectedThres = Object.assign({}, selectedThres, { + upper: payload + }); + const newListThres = [...list]; + newListThres[selectedIdx] = newSelectedThres; + return Object.assign({}, state, { + list: newListThres + }); +}; +const setThresHoldLower = (state, action) => { + const { + payload + } = action; + const { + list, + selectedIdx + } = state; + if (payload) { + const { + value, + curveIdx + } = payload; + const selectedThres = list[curveIdx]; + const newSelectedThres = Object.assign({}, selectedThres, { + lower: value + }); + const newListThres = [...list]; + newListThres[curveIdx] = newSelectedThres; + return Object.assign({}, state, { + list: newListThres, + selectedIdx: curveIdx + }); + } + const selectedThres = list[selectedIdx]; + const newSelectedThres = Object.assign({}, selectedThres, { + lower: payload + }); + const newListThres = [...list]; + newListThres[selectedIdx] = newSelectedThres; + return Object.assign({}, state, { + list: newListThres + }); +}; +const setThresHoldIsEdit = state => { + const { + list, + selectedIdx + } = state; + const selectedThres = list[selectedIdx]; + const { + isEdit + } = selectedThres; + const newSelectedThres = Object.assign({}, selectedThres, { + isEdit: !isEdit + }); + const newListThres = [...list]; + newListThres[selectedIdx] = newSelectedThres; + return Object.assign({}, state, { + list: newListThres + }); +}; +const resetAll = (state, action) => { + const { + payload + } = action; + const { + list + } = state; + const newList = list.map(item => ({ + isEdit: item.isEdit, + value: payload && payload.thresRef, + upper: item.upper, + lower: item.lower + })); + return Object.assign({}, state, { + selectedIdx: 0, + list: newList + }); +}; +const setListThreshold = (state, action) => { + const { + payload + } = action; + const { + list + } = state; + if (payload && payload.length > list.length) { + const newList = payload.map(() => ({ + isEdit: true, + value: false, + upper: false, + lower: false + })); + return Object.assign({}, state, { + list: newList + }); + } + return state; +}; +const resetInitCommon = state => { + const { + list + } = state; + const newList = list.map(item => ({ + isEdit: true, + value: item.value, + upper: item.upper, + lower: item.lower + })); + return Object.assign({}, state, { + selectedIdx: 0, + list: newList + }); +}; +const thresholdReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.CURVE.SET_ALL_CURVES: + return setListThreshold(state, action); + case _action_type.THRESHOLD.UPDATE_VALUE: + return setThresHoldValue(state, action); + case _action_type.THRESHOLD.UPDATE_UPPER_VALUE: + return setThresHoldUpper(state, action); + case _action_type.THRESHOLD.UPDATE_LOWER_VALUE: + return setThresHoldLower(state, action); + case _action_type.THRESHOLD.RESET_VALUE: + return setThresHoldValue(state, action); + case _action_type.THRESHOLD.TOGGLE_ISEDIT: + return setThresHoldIsEdit(state); + case _action_type.MANAGER.RESET_INIT_COMMON: + return resetInitCommon(state); + case _action_type.MANAGER.RESETALL: + return resetAll(state, action); + default: + return state; + } +}; +var _default = exports.default = thresholdReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_ui.js b/dist/reducers/reducer_ui.js new file mode 100644 index 00000000..14b7533f --- /dev/null +++ b/dist/reducers/reducer_ui.js @@ -0,0 +1,49 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +var _list_ui = require("../constants/list_ui"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + viewer: _list_ui.LIST_UI_VIEWER_TYPE.SPECTRUM, + sweepType: _list_ui.LIST_UI_SWEEP_TYPE.ZOOMIN, + sweepExtent: { + xExtent: false, + yExtent: false + }, + jcampIdx: 0 +}; +const uiReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.UI.VIEWER.SET_TYPE: + return Object.assign({}, state, { + viewer: action.payload + }); + case _action_type.UI.SWEEP.SET_TYPE: + if (action.payload === _list_ui.LIST_UI_SWEEP_TYPE.ZOOMRESET) { + return Object.assign({}, state, { + sweepExtent: { + xExtent: false, + yExtent: false + } + }); + } + return Object.assign({}, state, { + sweepType: action.payload, + jcampIdx: action.jcampIdx + }); + case _action_type.UI.SWEEP.SELECT_ZOOMIN: + return Object.assign({}, state, { + sweepExtent: action.payload + }); + case _action_type.MANAGER.RESETALL: + return initialState; + default: + return state; + } +}; +var _default = exports.default = uiReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_voltammetry.js b/dist/reducers/reducer_voltammetry.js new file mode 100644 index 00000000..dd9e7727 --- /dev/null +++ b/dist/reducers/reducer_voltammetry.js @@ -0,0 +1,603 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _action_type = require("../constants/action_type"); +var _chem = require("../helpers/chem"); +/* eslint-disable prefer-object-spread, default-param-last */ + +const initialState = { + spectraList: [], + areaValue: 1.0, + areaUnit: 'cm²', + useCurrentDensity: false +}; +const initSpectra = { + list: [], + origin: [], + selectedIdx: -1, + isWorkMaxPeak: true, + jcampIdx: -1, + shift: { + ref: null, + val: 0, + prevValue: 0 + }, + hasRefPeak: false, + history: [] +}; +const addPairPeak = (state, action) => { + const { + payload + } = action; + const { + spectraList + } = state; + if (payload !== undefined) { + let spectra = spectraList[payload]; + if (!spectra) { + spectra = initSpectra; + spectraList.push(spectra); + } + const { + list, + selectedIdx + } = spectra; + let index = selectedIdx; + index += 1; + const newList = list.map(item => { + // eslint-disable-line + return { + ...item + }; + }); + newList.push({ + min: null, + max: null, + isRef: false, + e12: null, + createdAt: Date.now() + }); + spectraList[payload] = Object.assign({}, spectra, { + list: newList, + selectedIdx: index, + origin: [...newList] + }); // eslint-disable-line + return Object.assign({}, state, { + spectraList + }); + } + return state; +}; +const removePairPeak = (state, action) => { + const { + payload + } = action; + const { + spectraList + } = state; + if (payload !== undefined) { + const { + index, + jcampIdx + } = payload; + const spectra = spectraList[jcampIdx]; + if (spectra) { + const { + list, + origin + } = spectra; + list.splice(index, 1); + origin.splice(index, 1); + spectraList[jcampIdx] = Object.assign({}, spectra, { + list, + selectedIdx: index, + origin + }); + return Object.assign({}, state, { + spectraList + }); + } + return state; + } + return state; +}; +const getE12 = data => { + if (data.max && data.min) { + const { + max, + min + } = data; + const delta = (0, _chem.GetCyclicVoltaPeakSeparate)(max.x, min.x); + return Math.min(max.x, min.x) + 0.5 * delta; + } + return null; +}; +const addPeak = (state, action, isMax = true) => { + const { + payload + } = action; + const { + spectraList + } = state; + if (payload) { + const { + peak, + index, + jcampIdx + } = payload; + const spectra = spectraList[jcampIdx]; + const { + list + } = spectra; + const newList = list.map(item => { + // eslint-disable-line + return { + ...item + }; + }); + let pairPeak = newList[index]; + if (isMax) { + pairPeak = Object.assign({}, pairPeak, { + max: peak + }); + } else { + pairPeak = Object.assign({}, pairPeak, { + min: peak + }); + } + pairPeak.e12 = getE12(pairPeak); + pairPeak.updatedAt = Date.now(); + newList[index] = pairPeak; + spectraList[jcampIdx] = Object.assign({}, spectra, { + list: newList, + selectedIdx: index, + jcampIdx, + origin: [...newList] + }); + return Object.assign({}, state, { + spectraList + }); + } + return state; +}; +const removePeak = (state, action, isMax = true) => { + const { + payload + } = action; + const { + spectraList + } = state; + if (payload) { + const { + index, + jcampIdx + } = payload; + const spectra = spectraList[jcampIdx]; + const { + list + } = spectra; + const newList = list; + const pairPeak = newList[index]; + if (isMax) { + pairPeak.max = null; + } else { + pairPeak.min = null; + } + pairPeak.e12 = getE12(pairPeak); + pairPeak.updatedAt = Date.now(); + newList[index] = pairPeak; + spectraList[jcampIdx] = Object.assign({}, spectra, { + list: newList, + selectedIdx: index, + jcampIdx, + origin: [...newList] + }); + return Object.assign({}, state, { + spectraList + }); + } + return state; +}; +const selectPairPeak = (state, action) => { + const { + payload + } = action; + if (payload !== undefined) { + const { + spectraList + } = state; + const { + index, + jcampIdx + } = payload; + const spectra = spectraList[jcampIdx]; + if (spectra) { + spectraList[jcampIdx] = Object.assign({}, spectra, { + selectedIdx: index + }); + return Object.assign({}, state, { + spectraList + }); + } + return state; + } + return state; +}; +const setWorkWithMaxPeak = (state, action) => { + const { + payload + } = action; + if (payload !== undefined) { + const { + spectraList + } = state; + const { + isMax, + jcampIdx + } = payload; + const spectra = spectraList[jcampIdx]; + if (spectra) { + spectraList[jcampIdx] = Object.assign({}, spectra, { + isWorkMaxPeak: isMax + }); + return Object.assign({}, state, { + spectraList + }); + } + return state; + } + return state; +}; +const addPecker = (state, action) => { + const { + payload + } = action; + const { + spectraList + } = state; + if (payload) { + const { + peak, + index, + jcampIdx + } = payload; + const spectra = spectraList[jcampIdx]; + const { + list + } = spectra; + const newList = list; + const pairPeak = newList[index]; + pairPeak.pecker = peak; + pairPeak.updatedAt = Date.now(); + newList[index] = pairPeak; + spectraList[jcampIdx] = Object.assign({}, spectra, { + list: newList, + selectedIdx: index, + jcampIdx, + origin: [...newList] + }); + return Object.assign({}, state, { + spectraList + }); + } + return state; +}; +const removePecker = (state, action) => { + const { + payload + } = action; + const { + spectraList + } = state; + if (payload) { + const { + index, + jcampIdx + } = payload; + const spectra = spectraList[jcampIdx]; + const { + list + } = spectra; + const newList = list; + const pairPeak = newList[index]; + pairPeak.pecker = null; + pairPeak.updatedAt = Date.now(); + newList[index] = pairPeak; + spectraList[jcampIdx] = Object.assign({}, spectra, { + list: newList, + selectedIdx: index, + jcampIdx, + origin: [...newList] + }); + return Object.assign({}, state, { + spectraList + }); + } + return state; +}; +const setRef = (state, action) => { + const { + payload + } = action; + const { + spectraList + } = state; + if (payload) { + const { + jcampIdx + } = payload; + const spectra = spectraList[jcampIdx]; + const { + list, + shift, + hasRefPeak, + history + } = spectra; + const newShift = Object.assign({}, shift); + const refPeaks = list.filter(pairPeak => pairPeak.isRef === true); + let offset = 0.0; + if (hasRefPeak) { + const currRefPeaks = refPeaks[0]; + newShift.ref = currRefPeaks; + const { + val + } = shift; + const { + e12 + } = currRefPeaks; + offset = e12 - val; + const newList = spectra.list.map(pairPeak => { + //eslint-disable-line + const { + max, + min, + pecker, + isRef + } = pairPeak; + let newMax = null; + let newMin = null; + let newPecker = null; + if (max) { + newMax = hasRefPeak ? { + x: max.x - offset, + y: max.y + } : { + x: max.x + parseFloat(offset), + y: max.y + }; + } + if (min) { + newMin = hasRefPeak ? { + x: min.x - offset, + y: min.y + } : { + x: min.x + parseFloat(offset), + y: min.y + }; + } + if (pecker) { + newPecker = hasRefPeak ? { + x: pecker.x - offset, + y: pecker.y + } : { + x: pecker.x + parseFloat(offset), + y: pecker.y + }; //eslint-disable-line + } + const newPairPeak = Object.assign({}, pairPeak, { + max: newMax, + min: newMin, + pecker: newPecker + }); //eslint-disable-line + newPairPeak.e12 = getE12(newPairPeak); + newPairPeak.updatedAt = Date.now(); + if (isRef) { + newShift.ref = newPairPeak; + newShift.prevValue += offset; + } + return newPairPeak; + }); + history.push(...[newList]); + spectra.list = newList; + } else { + newShift.ref = null; + const { + val + } = newShift; + offset = val; + const newList = spectra.origin.map(pairPeak => { + //eslint-disable-line + const { + max, + min, + pecker + } = pairPeak; + let newMax = null; + let newMin = null; + let newPecker = null; + if (max) { + newMax = { + x: max.x + parseFloat(val), + y: max.y + }; + } + if (min) { + newMin = { + x: min.x + parseFloat(val), + y: min.y + }; + } + if (pecker) { + newPecker = { + x: pecker.x + parseFloat(val), + y: pecker.y + }; + } + const newPairPeak = Object.assign({}, pairPeak, { + max: newMax, + min: newMin, + pecker: newPecker, + isRef: false + }); //eslint-disable-line + newPairPeak.e12 = getE12(newPairPeak); + newPairPeak.updatedAt = Date.now(); + return newPairPeak; + }); + history.push(...[newList]); + spectra.list = newList; + newShift.prevValue = parseFloat(offset); + } + spectraList[jcampIdx] = Object.assign({}, spectra, { + shift: newShift, + jcampIdx + }); + return Object.assign({}, state, { + spectraList + }); + } + return state; +}; +const selectRefPeaks = (state, action) => { + const { + payload + } = action; + const { + spectraList + } = state; + if (payload) { + const { + index, + jcampIdx, + checked + } = payload; + const spectra = spectraList[jcampIdx]; + const { + list, + shift, + history + } = spectra; + const newShift = shift; + const newList = list; + newList.forEach((pairPeak, idx) => { + const newPairPeak = pairPeak; + newPairPeak.isRef = false; + newPairPeak.updatedAt = Date.now(); + if (idx === index) { + newPairPeak.isRef = checked; + newList[index] = newPairPeak; + } + }); + const refPeaks = newList.filter(pairPeak => pairPeak.isRef === true); + const hasRefPeak = refPeaks.length > 0; + history.push(...[newList]); + spectraList[jcampIdx] = Object.assign({}, spectra, { + list: newList, + selectedIdx: index, + jcampIdx, + hasRefPeak, + shift: newShift + }); + return Object.assign({}, state, { + spectraList + }); + } + return state; +}; +const selectRefFactor = (state, action) => { + const { + payload + } = action; + const { + spectraList + } = state; + if (payload) { + const { + factor, + curveIdx + } = payload; + const spectra = spectraList[curveIdx]; + const { + shift + } = spectra; + const newShift = Object.assign({}, shift); + newShift.val = factor; + spectraList[curveIdx] = Object.assign({}, spectra, { + shift: newShift, + jcampIdx: curveIdx + }); + return Object.assign({}, state, { + spectraList + }); + } + return state; +}; +const cyclicVoltaReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.CYCLIC_VOLTA_METRY.ADD_PAIR_PEAKS: + return addPairPeak(state, action); + case _action_type.CYCLIC_VOLTA_METRY.REMOVE_PAIR_PEAKS: + return removePairPeak(state, action); + case _action_type.CYCLIC_VOLTA_METRY.ADD_MAX_PEAK: + return addPeak(state, action); + case _action_type.CYCLIC_VOLTA_METRY.REMOVE_MAX_PEAK: + return removePeak(state, action); + case _action_type.CYCLIC_VOLTA_METRY.ADD_MIN_PEAK: + return addPeak(state, action, false); + case _action_type.CYCLIC_VOLTA_METRY.REMOVE_MIN_PEAK: + return removePeak(state, action, false); + case _action_type.CYCLIC_VOLTA_METRY.WORK_WITH_MAX_PEAK: + return setWorkWithMaxPeak(state, action); + case _action_type.CYCLIC_VOLTA_METRY.SELECT_PAIR_PEAK: + return selectPairPeak(state, action); + case _action_type.CYCLIC_VOLTA_METRY.ADD_PECKER: + return addPecker(state, action); + case _action_type.CYCLIC_VOLTA_METRY.REMOVE_PECKER: + return removePecker(state, action); + case _action_type.CYCLIC_VOLTA_METRY.SET_REF: + return setRef(state, action); + case _action_type.CYCLIC_VOLTA_METRY.SELECT_REF_PEAK: + return selectRefPeaks(state, action); + case _action_type.CYCLIC_VOLTA_METRY.SET_FACTOR: + return selectRefFactor(state, action); + case _action_type.CYCLIC_VOLTA_METRY.RESETALL: + return Object.assign({}, state, { + spectraList: [] + }); + case _action_type.CYCLIC_VOLTA_METRY.SET_AREA_VALUE: + { + const { + value + } = action.payload; + if (value === '') { + return Object.assign({}, state, { + areaValue: '' + }); + } + const areaValue = Number.isFinite(value) ? value : state.areaValue; + return Object.assign({}, state, { + areaValue + }); + } + case _action_type.CYCLIC_VOLTA_METRY.SET_AREA_UNIT: + { + const { + unit + } = action.payload; + return Object.assign({}, state, { + areaUnit: unit + }); + } + case _action_type.CYCLIC_VOLTA_METRY.TOGGLE_DENSITY: + { + return Object.assign({}, state, { + useCurrentDensity: !state.useCurrentDensity + }); + } + default: + return state; + } +}; +var _default = exports.default = cyclicVoltaReducer; \ No newline at end of file diff --git a/dist/reducers/reducer_wavelength.js b/dist/reducers/reducer_wavelength.js new file mode 100644 index 00000000..7c6de7ab --- /dev/null +++ b/dist/reducers/reducer_wavelength.js @@ -0,0 +1,20 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _list_wavelength = require("../constants/list_wavelength"); +var _action_type = require("../constants/action_type"); +/* eslint-disable default-param-last */ + +const initialState = _list_wavelength.LIST_WAVE_LENGTH[0]; +const wavelengthReducer = (state = initialState, action) => { + switch (action.type) { + case _action_type.XRD.UPDATE_WAVE_LENGTH: + return action.payload; + default: + return state; + } +}; +var _default = exports.default = wavelengthReducer; \ No newline at end of file diff --git a/dist/reducers/undo_redo_config.js b/dist/reducers/undo_redo_config.js new file mode 100644 index 00000000..122961e6 --- /dev/null +++ b/dist/reducers/undo_redo_config.js @@ -0,0 +1,17 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.undoRedoConfig = exports.undoRedoActions = void 0; +var _reduxUndo = require("redux-undo"); +var _action_type = require("../constants/action_type"); +const undoRedoActions = exports.undoRedoActions = [_action_type.EDITPEAK.ADD_POSITIVE, _action_type.EDITPEAK.ADD_NEGATIVE, _action_type.EDITPEAK.RM_POSITIVE, _action_type.EDITPEAK.RM_NEGATIVE, _action_type.EDITPEAK.SHIFT, _action_type.EDITPEAK.CLEAR_ALL, _action_type.MANAGER.RESETALL, _action_type.MANAGER.RESETSHIFT, _action_type.MANAGER.RESET_INIT_COMMON, _action_type.MANAGER.RESET_INIT_NMR, _action_type.MANAGER.RESET_INIT_MS, _action_type.MANAGER.RESET_INIT_COMMON_WITH_INTERGATION, _action_type.UI.SWEEP.SELECT_INTEGRATION, _action_type.UI.SWEEP.SELECT_MULTIPLICITY_RDC, _action_type.INTEGRATION.RM_ONE, _action_type.INTEGRATION.SET_REF, _action_type.INTEGRATION.SET_FKR, _action_type.INTEGRATION.SPLIT, _action_type.INTEGRATION.RESET_ALL, _action_type.INTEGRATION.CLEAR_ALL, _action_type.MULTIPLICITY.PEAK_RM_BY_PANEL_RDC, _action_type.MULTIPLICITY.PEAK_RM_BY_UI_RDC, _action_type.MULTIPLICITY.PEAK_ADD_BY_UI_RDC, _action_type.MULTIPLICITY.RESET_ONE_RDC, _action_type.MULTIPLICITY.UPDATE_J, _action_type.MULTIPLICITY.TYPE_SELECT_RDC, _action_type.MULTIPLICITY.ONE_CLICK, _action_type.MULTIPLICITY.ONE_CLICK_BY_UI, _action_type.MULTIPLICITY.RESET_ALL_RDC, _action_type.MULTIPLICITY.CLEAR_ALL]; +const undoRedoConfig = exports.undoRedoConfig = { + debug: false, + limit: 10, + ignoreInitialState: true, + filter: (0, _reduxUndo.includeAction)(undoRedoActions), + clearHistoryType: [_action_type.EDITPEAK.SHIFT, _action_type.EDITPEAK.CLEAR_ALL, _action_type.MANAGER.RESETALL, _action_type.MANAGER.RESETSHIFT, _action_type.MANAGER.RESET_INIT_COMMON, _action_type.MANAGER.RESET_INIT_NMR, _action_type.MANAGER.RESET_INIT_MS, _action_type.MANAGER.RESET_INIT_COMMON_WITH_INTERGATION], + neverSkipReducer: [_action_type.EDITPEAK.SHIFT, _action_type.EDITPEAK.CLEAR_ALL, _action_type.MANAGER.RESETALL, _action_type.MANAGER.RESETSHIFT, _action_type.MANAGER.RESET_INIT_COMMON, _action_type.MANAGER.RESET_INIT_NMR, _action_type.MANAGER.RESET_INIT_MS, _action_type.MANAGER.RESET_INIT_COMMON_WITH_INTERGATION] +}; \ No newline at end of file diff --git a/dist/sagas/index.js b/dist/sagas/index.js new file mode 100644 index 00000000..6b9c1884 --- /dev/null +++ b/dist/sagas/index.js @@ -0,0 +1,17 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = rootSaga; +var _effects = require("redux-saga/effects"); +var _saga_edit_peak = _interopRequireDefault(require("./saga_edit_peak")); +var _saga_manager = _interopRequireDefault(require("./saga_manager")); +var _saga_ui = _interopRequireDefault(require("./saga_ui")); +var _saga_meta = _interopRequireDefault(require("./saga_meta")); +var _saga_multiplicity = _interopRequireDefault(require("./saga_multiplicity")); +var _saga_multi_entities = _interopRequireDefault(require("./saga_multi_entities")); +function* rootSaga() { + yield (0, _effects.all)([..._saga_edit_peak.default, ..._saga_manager.default, ..._saga_ui.default, ..._saga_meta.default, ..._saga_multiplicity.default, ..._saga_multi_entities.default]); +} \ No newline at end of file diff --git a/dist/sagas/saga_edit_peak.js b/dist/sagas/saga_edit_peak.js new file mode 100644 index 00000000..a68ad59d --- /dev/null +++ b/dist/sagas/saga_edit_peak.js @@ -0,0 +1,75 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _effects = require("redux-saga/effects"); +var _action_type = require("../constants/action_type"); +var _shift = require("../helpers/shift"); +var _list_shift = require("../constants/list_shift"); +const getShift = state => state.shift; +const getEditPeak = state => state.editPeak.present; +function* addVirtualFactor(action) { + const originShift = yield (0, _effects.select)(getShift); + const origEPeak = yield (0, _effects.select)(getEditPeak); + const { + payload + } = action; + const { + curveIdx + } = payload; + const { + peaks + } = origEPeak; + let currentOriginPeaks = peaks[curveIdx]; + if (currentOriginPeaks === false || currentOriginPeaks === undefined) { + currentOriginPeaks = { + prevOffset: 0, + pos: [], + neg: [] + }; + } + let currentOriginShift = originShift.shifts[curveIdx]; + if (currentOriginShift === false || currentOriginShift === undefined) { + const shiftNone = _list_shift.LIST_SHIFT_1H[0]; + currentOriginShift = { + ref: shiftNone, + peak: false, + enable: true + }; + } + const origRef = currentOriginShift.ref; + const origApex = currentOriginShift.peak; + const { + prevOffset, + pos, + neg + } = currentOriginPeaks; + const absOffset = (0, _shift.FromManualToOffset)(origRef, origApex); + const relOffset = prevOffset - absOffset; + const nextPos = (0, _shift.VirtalPts)(pos, relOffset); + const nextNeg = (0, _shift.VirtalPts)(neg, relOffset); + yield (0, _effects.put)({ + type: _action_type.EDITPEAK.SHIFT, + payload: Object.assign({}, payload, { + // eslint-disable-line + prevOffset: absOffset, + pos: nextPos, + neg: nextNeg + }) + }); +} +const editPeakSagas = [(0, _effects.takeEvery)(_action_type.SHIFT.SET_REF, addVirtualFactor), (0, _effects.takeEvery)(_action_type.SHIFT.SET_PEAK, addVirtualFactor)]; +var _default = exports.default = editPeakSagas; +/* LOGIC + -no po - tg + | picked | another | absoffset | prevOffset | relative | newOffset +------------------------------------------------------------------- +0 | 40 20 - - - 0 +1 | 180 160 -140 0 140 140 +2 | 80 60 -40 -140 -100 100 +3 | 20 0 +20 -100 -120 +------------------------------------------------------------------- + +*/ \ No newline at end of file diff --git a/dist/sagas/saga_manager.js b/dist/sagas/saga_manager.js new file mode 100644 index 00000000..e87aec57 --- /dev/null +++ b/dist/sagas/saga_manager.js @@ -0,0 +1,97 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _effects = require("redux-saga/effects"); +var _action_type = require("../constants/action_type"); +const getLayout = state => state.layout; +const getCurveSt = state => state.curve; +const getIntegrationSt = state => state.integration.present; +function* resetShift(action) { + const curveSt = yield (0, _effects.select)(getCurveSt); + const layout = yield (0, _effects.select)(getLayout); + const { + payload + } = action; + const { + curveIdx, + listCurves + } = curveSt; + const numberOfCurve = listCurves.length; + yield (0, _effects.put)({ + type: _action_type.MANAGER.RESETSHIFT, + payload: Object.assign( + // eslint-disable-line + {}, payload, { + layout, + curvesInfo: { + isMultiCurve: numberOfCurve > 0, + curveIdx, + numberOfCurve + } + }) + }); +} +function* resetInitNmr(action) { + const curveSt = yield (0, _effects.select)(getCurveSt); + const integationSt = yield (0, _effects.select)(getIntegrationSt); + const { + curveIdx + } = curveSt; + const { + integration, + simulation + } = action.payload; + const { + integrations + } = integationSt; + const newArrIntegration = [...integrations]; + newArrIntegration[curveIdx] = integration; + 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 + }); + } + if (simulation) { + yield (0, _effects.put)({ + type: _action_type.SIMULATION.RESET_ALL_RDC, + payload: simulation + }); + } +} +function* resetInitCommonWithIntergation(action) { + const curveSt = yield (0, _effects.select)(getCurveSt); + const integationSt = yield (0, _effects.select)(getIntegrationSt); + const { + curveIdx + } = curveSt; + const { + integration + } = action.payload; + const { + integrations + } = integationSt; + const newArrIntegration = [...integrations]; + newArrIntegration[curveIdx] = integration; + 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 + }); + } +} +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_meta.js b/dist/sagas/saga_meta.js new file mode 100644 index 00000000..6b3be549 --- /dev/null +++ b/dist/sagas/saga_meta.js @@ -0,0 +1,44 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _effects = require("redux-saga/effects"); +var _action_type = require("../constants/action_type"); +var _peakInterval = require("../third_party/peakInterval"); +function* updateMetaPeaks(action) { + const { + payload + } = action; + const { + intervalL, + intervalR + } = (0, _peakInterval.getPeakIntervals)(payload); + const { + observeFrequency, + data + } = payload.spectra[0]; + const deltaX = Math.abs(data[0].x[0] - data[0].x[1]); + yield (0, _effects.put)({ + type: _action_type.META.UPDATE_PEAKS_RDC, + payload: { + peaks: { + intervalL, + intervalR, + observeFrequency, + deltaX + } + } + }); +} +function* updateMetaData(action) { + yield (0, _effects.put)({ + type: _action_type.META.UPDATE_META_DATA_RDC, + payload: { + dscMetaData: action.payload + } + }); +} +const metaSagas = [(0, _effects.takeEvery)(_action_type.META.UPDATE_PEAKS, updateMetaPeaks), (0, _effects.takeEvery)(_action_type.META.UPDATE_META_DATA, updateMetaData)]; +var _default = exports.default = metaSagas; \ No newline at end of file diff --git a/dist/sagas/saga_multi_entities.js b/dist/sagas/saga_multi_entities.js new file mode 100644 index 00000000..ee648abd --- /dev/null +++ b/dist/sagas/saga_multi_entities.js @@ -0,0 +1,234 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _effects = require("redux-saga/effects"); +var _action_type = require("../constants/action_type"); +var _list_layout = require("../constants/list_layout"); +/* eslint-disable no-plusplus */ + +const getLayoutSt = state => state.layout; +const getCurveSt = state => state.curve; +const getIntegrationSt = state => state.integration.present; +const getMultiplicitySt = state => state.multiplicity.present; +function getMaxMinPeak(curve) { + return curve.maxminPeak; +} +function* setCyclicVoltametry(action) { + // eslint-disable-line + const curveSt = yield (0, _effects.select)(getCurveSt); + const { + listCurves + } = curveSt; + if (listCurves) { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.RESETALL, + payload: null + }); + const numberOfCurves = listCurves.length; + if (numberOfCurves <= 0) { + return; + } + const firstCurve = listCurves[0]; + const { + layout + } = firstCurve; + if (layout !== _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY) { + return; + } + const cvSt = yield (0, _effects.select)(state => state.cyclicvolta); + const { + feature + } = firstCurve; + if (feature) { + const { + weAreaValue, + weAreaUnit, + currentMode + } = feature; + if (typeof weAreaUnit === 'string' && weAreaUnit.length > 0) { + const unit = weAreaUnit.replace('^2', '²'); + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.SET_AREA_UNIT, + payload: { + unit + } + }); + } else { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.SET_AREA_UNIT, + payload: { + unit: 'cm²' + } + }); + } + if (weAreaValue !== undefined && weAreaValue !== null) { + let numeric = null; + if (typeof weAreaValue === 'string') { + const parsed = parseFloat(weAreaValue); + if (!Number.isNaN(parsed)) numeric = parsed; + } else if (Number.isFinite(weAreaValue)) { + numeric = weAreaValue; + } + if (Number.isFinite(numeric)) { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.SET_AREA_VALUE, + payload: { + value: numeric + } + }); + } else { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.SET_AREA_VALUE, + payload: { + value: 1.0 + } + }); + } + } + if (typeof currentMode === 'string' && currentMode.length > 0) { + const wantDensity = currentMode.toUpperCase() === 'DENSITY'; + if (!!cvSt.useCurrentDensity !== wantDensity) { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.TOGGLE_DENSITY, + payload: null + }); + } + } + } + for (let index = 0; index < listCurves.length; index++) { + const curve = listCurves[index]; + const maxminPeak = getMaxMinPeak(curve); + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.ADD_PAIR_PEAKS, + payload: index + }); + for (let pidx = 0; pidx < maxminPeak.max.length; pidx++) { + const maxPeak = maxminPeak.max[pidx]; + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.ADD_MAX_PEAK, + payload: { + peak: maxPeak, + index: pidx, + jcampIdx: index + } + }); + const minPeak = maxminPeak.min[pidx]; + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.ADD_MIN_PEAK, + payload: { + peak: minPeak, + index: pidx, + jcampIdx: index + } + }); + const pecker = maxminPeak.pecker[pidx]; + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.ADD_PECKER, + payload: { + peak: pecker, + index: pidx, + jcampIdx: index + } + }); + } + const { + refIndex + } = maxminPeak; + if (refIndex > -1) { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.SELECT_REF_PEAK, + payload: { + index: refIndex, + jcampIdx: index, + checked: true + } + }); + } + } + } +} +function* setCyclicVoltametryRef(action) { + // eslint-disable-line + const layoutSt = yield (0, _effects.select)(getLayoutSt); + if (layoutSt !== _list_layout.LIST_LAYOUT.CYCLIC_VOLTAMMETRY) { + return; + } + const curveSt = yield (0, _effects.select)(getCurveSt); + const { + curveIdx + } = curveSt; + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.SET_REF, + payload: { + jcampIdx: curveIdx + } + }); +} +function* setInitIntegrations(action) { + // eslint-disable-line + const curveSt = yield (0, _effects.select)(getCurveSt); + const { + listCurves + } = curveSt; + if (listCurves) { + for (let index = 0; index < listCurves.length; index++) { + const integationSt = yield (0, _effects.select)(getIntegrationSt); + const multiplicitySt = yield (0, _effects.select)(getMultiplicitySt); + const curve = listCurves[index]; + const { + integration, + multiplicity, + simulation + } = curve; + if (integration) { + const { + integrations + } = integationSt; + const newArrIntegration = [...integrations]; + if (index < newArrIntegration.length) { + newArrIntegration[index] = integration; + } else { + newArrIntegration.push(integration); + } + const payload = Object.assign({}, integationSt, { + integrations: newArrIntegration, + selectedIdx: index + }); // eslint-disable-line + yield (0, _effects.put)({ + type: _action_type.INTEGRATION.RESET_ALL_RDC, + payload + }); + } + if (multiplicity) { + const { + multiplicities + } = multiplicitySt; + const newArrMultiplicities = [...multiplicities]; + if (index < newArrMultiplicities.length) { + newArrMultiplicities[index] = multiplicity; + } else { + newArrMultiplicities.push(multiplicity); + } + const payload = Object.assign({}, multiplicitySt, { + multiplicities: newArrMultiplicities, + selectedIdx: index + }); // eslint-disable-line + yield (0, _effects.put)({ + type: _action_type.MULTIPLICITY.RESET_ALL_RDC, + payload + }); + } + if (simulation) { + yield (0, _effects.put)({ + type: _action_type.SIMULATION.RESET_ALL_RDC, + payload: simulation + }); + } + } + } +} +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)]; +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 new file mode 100644 index 00000000..56645954 --- /dev/null +++ b/dist/sagas/saga_multiplicity.js @@ -0,0 +1,378 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _effects = require("redux-saga/effects"); +var _action_type = require("../constants/action_type"); +var _multiplicity_calc = require("../helpers/multiplicity_calc"); +var _multiplicity_manual = require("../helpers/multiplicity_manual"); +const getMetaSt = state => state.meta; +const getCurveSt = state => state.curve; +const getMultiplicitySt = state => state.multiplicity.present; +function* selectMpy(action) { + const metaSt = yield (0, _effects.select)(getMetaSt); + const mpySt = yield (0, _effects.select)(getMultiplicitySt); + const { + newData, + curveIdx + } = action.payload; + const { + multiplicities + } = mpySt; + let selectedMulti = multiplicities[curveIdx]; + if (selectedMulti === false || selectedMulti === undefined) { + selectedMulti = { + stack: [], + shift: 0, + smExtext: false, + edited: false + }; + } + const { + xExtent, + yExtent, + dataPks + } = newData; + const { + shift, + stack + } = selectedMulti; + const { + xL, + xU + } = xExtent; + const { + yL, + yU + } = yExtent; + let peaks = dataPks.filter(p => xL <= p.x && p.x <= xU && yL <= p.y && p.y <= yU); + peaks = peaks.map(pk => ({ + x: pk.x + shift, + y: pk.y + })); + const newXExtemt = { + xL: xL + shift, + xU: xU + shift + }; + const coupling = (0, _multiplicity_calc.calcMpyCoup)(peaks, metaSt); + const m = { + peaks, + xExtent: newXExtemt, + yExtent, + mpyType: coupling.type, + js: coupling.js + }; + const newStack = [...stack, m]; + const newSelectedMulti = Object.assign( + // eslint-disable-line + {}, selectedMulti, { + stack: newStack, + smExtext: newXExtemt + }); + const newMultiplicities = [...multiplicities]; + newMultiplicities[curveIdx] = newSelectedMulti; + const payload = Object.assign( + // eslint-disable-line + {}, mpySt, { + multiplicities: newMultiplicities, + selectedIdx: curveIdx + }); + yield (0, _effects.put)({ + type: _action_type.UI.SWEEP.SELECT_MULTIPLICITY_RDC, + payload + }); +} +function* addUiPeakToStack(action) { + const metaSt = yield (0, _effects.select)(getMetaSt); + const mpySt = yield (0, _effects.select)(getMultiplicitySt); + const curveSt = yield (0, _effects.select)(getCurveSt); + const { + curveIdx + } = curveSt; + const { + multiplicities + } = mpySt; + const selectedMulti = multiplicities[curveIdx]; + const { + shift, + stack, + smExtext + } = selectedMulti; + let { + x, + y + } = action.payload; // eslint-disable-line + if (!x || !y) return; + x += shift; + const newPeak = { + x, + y + }; + const { + xL, + xU + } = smExtext; + if (x < xL || xU < x) return; + let isDuplicate = false; + const newStack = stack.map(k => { + if (k.xExtent.xL === xL && k.xExtent.xU === xU) { + const existXs = k.peaks.map(pk => pk.x); + if (existXs.indexOf(newPeak.x) >= 0) { + isDuplicate = true; + return k; + } + const newPks = [...k.peaks, newPeak]; + const coupling = (0, _multiplicity_calc.calcMpyCoup)(newPks, metaSt); + return Object.assign( + // eslint-disable-line + {}, k, { + peaks: newPks, + mpyType: coupling.type, + js: coupling.js + }); + } + return k; + }); + if (isDuplicate) return; + const newSelectedMulti = Object.assign({}, selectedMulti, { + stack: newStack + }); // eslint-disable-line + const newMultiplicities = [...multiplicities]; + newMultiplicities[curveIdx] = newSelectedMulti; + const payload = Object.assign({}, mpySt, { + multiplicities: newMultiplicities + }); // eslint-disable-line + + yield (0, _effects.put)({ + type: _action_type.MULTIPLICITY.PEAK_ADD_BY_UI_RDC, + payload + }); +} +const rmPeakFromStack = (action, metaSt, mpySt, curveIdx = 0) => { + const { + peak, + xExtent + } = action.payload; + const { + multiplicities + } = mpySt; + const selectedMulti = multiplicities[curveIdx]; + const { + stack + } = selectedMulti; + let newStack = stack.map(k => { + if (k.xExtent.xL === xExtent.xL && k.xExtent.xU === xExtent.xU) { + const newPks = k.peaks.filter(pk => pk.x !== peak.x); + const coupling = (0, _multiplicity_calc.calcMpyCoup)(newPks, metaSt); + return Object.assign( + // eslint-disable-line + {}, k, { + peaks: newPks, + mpyType: coupling.type, + js: coupling.js + }); + } + return k; + }); + newStack = newStack.filter(k => k.peaks.length !== 0); + if (newStack.length === 0) { + const newSelectedMulti = Object.assign({}, selectedMulti, { + stack: newStack, + smExtext: false + }); // eslint-disable-line + multiplicities[curveIdx] = newSelectedMulti; + return Object.assign({}, mpySt, { + multiplicities + }); // eslint-disable-line + } + const noSmExtext = newStack.map(k => k.xExtent.xL === xExtent.xL && k.xExtent.xU === xExtent.xU ? 1 : 0).reduce((a, s) => a + s) === 0; + const newSmExtext = noSmExtext ? newStack[0].xExtent : xExtent; + const newSelectedMulti = Object.assign({}, selectedMulti, { + stack: newStack, + smExtext: newSmExtext + }); // eslint-disable-line + const newMultiplicities = [...multiplicities]; + newMultiplicities[curveIdx] = newSelectedMulti; + return Object.assign({}, mpySt, { + multiplicities: newMultiplicities + }); // eslint-disable-line +}; +function* rmPanelPeakFromStack(action) { + const metaSt = yield (0, _effects.select)(getMetaSt); + const mpySt = yield (0, _effects.select)(getMultiplicitySt); + const curveSt = yield (0, _effects.select)(getCurveSt); + const { + curveIdx + } = curveSt; + const payload = rmPeakFromStack(action, metaSt, mpySt, curveIdx); + yield (0, _effects.put)({ + type: _action_type.MULTIPLICITY.PEAK_RM_BY_PANEL_RDC, + payload + }); +} +function* rmUiPeakFromStack(action) { + const metaSt = yield (0, _effects.select)(getMetaSt); + const mpySt = yield (0, _effects.select)(getMultiplicitySt); + const curveSt = yield (0, _effects.select)(getCurveSt); + const { + curveIdx + } = curveSt; + const { + multiplicities + } = mpySt; + const selectedMulti = multiplicities[curveIdx]; + const peak = action.payload; + const xExtent = selectedMulti.smExtext; + const newAction = Object.assign({}, action, { + payload: { + peak, + xExtent + } + }); // eslint-disable-line + + const payload = rmPeakFromStack(newAction, metaSt, mpySt, curveIdx); + yield (0, _effects.put)({ + type: _action_type.MULTIPLICITY.PEAK_RM_BY_UI_RDC, + payload + }); +} +function* resetInitNmr(action) { + const { + multiplicity + } = action.payload; + const curveSt = yield (0, _effects.select)(getCurveSt); + const mpySt = yield (0, _effects.select)(getMultiplicitySt); + const { + curveIdx + } = curveSt; + const { + multiplicities + } = mpySt; + const newMultiplicities = [...multiplicities]; + newMultiplicities[curveIdx] = multiplicity; + 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, + // }); + // } + + // const { stack } = multiplicity; + // const newStack = stack.map((k) => { + // const { peaks } = k; + // const coupling = calcMpyCoup(peaks, metaSt); + // return Object.assign( + // {}, + // k, + // { + // peaks, + // mpyType: coupling.type, + // js: coupling.js, + // }, + // ); + // }); + // const payload = Object.assign({}, mpySt, { stack: newStack }); + // yield put({ + // type: MULTIPLICITY.RESET_ALL_RDC, + // payload, + // }); +} +function* resetOne(action) { + const xExtent = action.payload; + const metaSt = yield (0, _effects.select)(getMetaSt); + const mpySt = yield (0, _effects.select)(getMultiplicitySt); + const curveSt = yield (0, _effects.select)(getCurveSt); + const { + curveIdx + } = curveSt; + const { + multiplicities + } = mpySt; + const selectedMulti = multiplicities[curveIdx]; + const { + stack + } = selectedMulti; + const newStack = stack.map(k => { + if (k.xExtent.xL === xExtent.xL && k.xExtent.xU === xExtent.xU) { + const { + peaks + } = k; + const coupling = (0, _multiplicity_calc.calcMpyCoup)(peaks, metaSt); + return Object.assign( + // eslint-disable-line + {}, k, { + peaks, + mpyType: coupling.type, + js: coupling.js + }); + } + return k; + }); + const newSelectedMulti = Object.assign({}, selectedMulti, { + stack: newStack + }); // eslint-disable-line + const newMultiplicities = [...multiplicities]; + newMultiplicities[curveIdx] = newSelectedMulti; + const payload = Object.assign({}, mpySt, { + multiplicities: newMultiplicities + }); // eslint-disable-line + yield (0, _effects.put)({ + type: _action_type.MULTIPLICITY.RESET_ONE_RDC, + payload + }); +} +function* selectMpyType(action) { + const mpySt = yield (0, _effects.select)(getMultiplicitySt); + const metaSt = yield (0, _effects.select)(getMetaSt); + const curveSt = yield (0, _effects.select)(getCurveSt); + const { + curveIdx + } = curveSt; + const { + multiplicities + } = mpySt; + const selectedMulti = multiplicities[curveIdx]; + const { + mpyType, + xExtent + } = action.payload; + const { + stack + } = selectedMulti; + const newStack = stack.map(k => { + const isTargetStack = k.xExtent.xL === xExtent.xL && k.xExtent.xU === xExtent.xU; + if (isTargetStack) return (0, _multiplicity_manual.calcMpyManual)(k, mpyType, metaSt); + return k; + }); + const newSelectedMulti = Object.assign({}, selectedMulti, { + stack: newStack + }); // eslint-disable-line + const newMultiplicities = [...multiplicities]; + newMultiplicities[curveIdx] = newSelectedMulti; + const payload = Object.assign({}, mpySt, { + multiplicities: newMultiplicities + }); // eslint-disable-line + + yield (0, _effects.put)({ + type: _action_type.MULTIPLICITY.TYPE_SELECT_RDC, + payload + }); +} +const multiplicitySagas = [(0, _effects.takeEvery)(_action_type.UI.SWEEP.SELECT_MULTIPLICITY, selectMpy), (0, _effects.takeEvery)(_action_type.MULTIPLICITY.PEAK_ADD_BY_UI_SAG, addUiPeakToStack), (0, _effects.takeEvery)(_action_type.MULTIPLICITY.PEAK_RM_BY_PANEL, rmPanelPeakFromStack), (0, _effects.takeEvery)(_action_type.MULTIPLICITY.PEAK_RM_BY_UI, rmUiPeakFromStack), (0, _effects.takeEvery)(_action_type.MULTIPLICITY.TYPE_SELECT, selectMpyType), (0, _effects.takeEvery)(_action_type.MULTIPLICITY.RESET_ONE, resetOne), (0, _effects.takeEvery)(_action_type.MANAGER.RESET_INIT_NMR, resetInitNmr)]; +var _default = exports.default = multiplicitySagas; \ No newline at end of file diff --git a/dist/sagas/saga_ui.js b/dist/sagas/saga_ui.js new file mode 100644 index 00000000..adf16e94 --- /dev/null +++ b/dist/sagas/saga_ui.js @@ -0,0 +1,321 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _effects = require("redux-saga/effects"); +var _action_type = require("../constants/action_type"); +var _list_ui = require("../constants/list_ui"); +var _list_layout = require("../constants/list_layout"); +const getUiSt = state => state.ui; +const getCurveSt = state => state.curve; +const calcPeaks = payload => { + const { + xExtent, + yExtent, + dataPks + } = payload; + if (!dataPks) return []; + const { + xL, + xU + } = xExtent; + const { + yL, + yU + } = yExtent; + const peaks = dataPks.filter(p => xL <= p.x && p.x <= xU && yL <= p.y && p.y <= yU); + return peaks; +}; +function* selectUiSweep(action) { + const uiSt = yield (0, _effects.select)(getUiSt); + const { + payload + } = action; + const curveSt = yield (0, _effects.select)(getCurveSt); + const { + curveIdx + } = curveSt; + switch (uiSt.sweepType) { + case _list_ui.LIST_UI_SWEEP_TYPE.ZOOMIN: + yield (0, _effects.put)({ + type: _action_type.UI.SWEEP.SELECT_ZOOMIN, + payload + }); + break; + case _list_ui.LIST_UI_SWEEP_TYPE.ZOOMRESET: + yield (0, _effects.put)({ + type: _action_type.UI.SWEEP.SELECT_ZOOMRESET, + payload + }); + break; + case _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_ADD: + yield (0, _effects.put)({ + type: _action_type.UI.SWEEP.SELECT_INTEGRATION, + payload: { + newData: payload, + curveIdx + } + }); + break; + case _list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_SWEEP_ADD: + const peaks = calcPeaks(payload); // eslint-disable-line + if (peaks.length === 0) { + break; + } + const newPayload = Object.assign({}, payload, { + peaks + }); // eslint-disable-line + + yield (0, _effects.put)({ + type: _action_type.UI.SWEEP.SELECT_INTEGRATION, + payload: { + newData: newPayload, + curveIdx + } + }); + yield (0, _effects.put)({ + type: _action_type.UI.SWEEP.SELECT_MULTIPLICITY, + payload: { + newData: newPayload, + curveIdx + } + }); + break; + default: + break; + } + return null; +} +const getLayoutSt = state => state.layout; +function* scrollUiWheel(action) { + const layoutSt = yield (0, _effects.select)(getLayoutSt); + const { + payload + } = action; + const { + xExtent, + yExtent, + direction + } = payload; + const { + yL, + yU + } = yExtent; + const [yeL, yeU] = [yL + (yU - yL) * 0.1, yU - (yU - yL) * 0.1]; + const scale = direction ? 0.8 : 1.25; + let nextExtent = { + xExtent: false, + yExtent: false + }; + let [nyeL, nyeU, h, nytL, nytU] = [0, 1, 1, 0, 1]; + switch (layoutSt) { + case _list_layout.LIST_LAYOUT.IR: + case _list_layout.LIST_LAYOUT.RAMAN: + [nyeL, nyeU] = [yeL + (yeU - yeL) * (1 - scale), yeU]; + h = nyeU - nyeL; + [nytL, nytU] = [nyeL - 0.125 * h, nyeU + 0.125 * h]; + nextExtent = { + xExtent, + yExtent: { + yL: nytL, + yU: nytU + } + }; + break; + case _list_layout.LIST_LAYOUT.MS: + [nyeL, nyeU] = [0, yeL + (yeU - yeL) * scale]; + h = nyeU - nyeL; + [nytL, nytU] = [nyeL - 0.125 * h, nyeU + 0.125 * h]; + nextExtent = { + xExtent, + yExtent: { + yL: nytL, + yU: nytU + } + }; + break; + case _list_layout.LIST_LAYOUT.UVVIS: + case _list_layout.LIST_LAYOUT.HPLC_UVVIS: + case _list_layout.LIST_LAYOUT.TGA: + case _list_layout.LIST_LAYOUT.DSC: + case _list_layout.LIST_LAYOUT.XRD: + default: + [nyeL, nyeU] = [yeL, yeL + (yeU - yeL) * scale]; + h = nyeU - nyeL; + [nytL, nytU] = [nyeL - 0.125 * h, nyeU + 0.125 * h]; + nextExtent = { + xExtent, + yExtent: { + yL: nytL, + yU: nytU + } + }; + break; + } + yield (0, _effects.put)({ + type: _action_type.UI.SWEEP.SELECT_ZOOMIN, + payload: nextExtent + }); +} +const getUiSweepType = state => state.ui.sweepType; +function* clickUiTarget(action) { + const { + payload, + onPeak, + voltammetryPeakIdx, + onPecker + } = action; + const uiSweepType = yield (0, _effects.select)(getUiSweepType); + const curveSt = yield (0, _effects.select)(getCurveSt); + const { + curveIdx + } = curveSt; + if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.PEAK_ADD && !onPeak) { + yield (0, _effects.put)({ + type: _action_type.EDITPEAK.ADD_POSITIVE, + payload: { + dataToAdd: payload, + curveIdx + } + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.PEAK_DELETE && onPeak) { + yield (0, _effects.put)({ + type: _action_type.EDITPEAK.ADD_NEGATIVE, + payload: { + dataToAdd: payload, + curveIdx + } + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.ANCHOR_SHIFT && onPeak) { + yield (0, _effects.put)({ + type: _action_type.SHIFT.SET_PEAK, + payload: { + dataToSet: payload, + curveIdx + } + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_RM && onPeak) { + yield (0, _effects.put)({ + type: _action_type.INTEGRATION.RM_ONE, + payload: { + dataToRemove: payload, + curveIdx + } + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_ONE_RM && onPeak) { + yield (0, _effects.put)({ + type: _action_type.INTEGRATION.RM_ONE, + payload: { + dataToRemove: payload, + curveIdx + } + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.INTEGRATION_SET_REF && onPeak) { + yield (0, _effects.put)({ + type: _action_type.INTEGRATION.SET_REF, + payload: { + refData: payload, + curveIdx + } + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_ONE_CLICK && onPeak) { + const { + xExtent, + xL, + xU + } = payload; + if (xExtent) { + yield (0, _effects.put)({ + type: _action_type.MULTIPLICITY.ONE_CLICK_BY_UI, + payload: { + payloadData: xExtent, + curveIdx + } + }); + } else if (xL && xU) { + yield (0, _effects.put)({ + type: _action_type.MULTIPLICITY.ONE_CLICK_BY_UI, + payload: { + payloadData: { + xL, + xU + }, + curveIdx + } + }); + } + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_PEAK_ADD) { + yield (0, _effects.put)({ + type: _action_type.MULTIPLICITY.PEAK_ADD_BY_UI_SAG, + payload + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.MULTIPLICITY_PEAK_RM && onPeak) { + yield (0, _effects.put)({ + type: _action_type.MULTIPLICITY.PEAK_RM_BY_UI, + payload + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MAX_PEAK && !onPeak) { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.ADD_MAX_PEAK, + payload: { + peak: payload, + index: voltammetryPeakIdx, + jcampIdx: curveIdx + } + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MAX_PEAK && onPeak) { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.REMOVE_MAX_PEAK, + payload: { + index: voltammetryPeakIdx, + jcampIdx: curveIdx + } + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_MIN_PEAK && !onPeak) { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.ADD_MIN_PEAK, + payload: { + peak: payload, + index: voltammetryPeakIdx, + jcampIdx: curveIdx + } + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_MIN_PEAK && onPeak) { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.REMOVE_MIN_PEAK, + payload: { + index: voltammetryPeakIdx, + jcampIdx: curveIdx + } + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_ADD_PECKER && !onPecker) { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.ADD_PECKER, + payload: { + peak: payload, + index: voltammetryPeakIdx, + jcampIdx: curveIdx + } + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_RM_PECKER && onPecker) { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.REMOVE_PECKER, + payload: { + index: voltammetryPeakIdx, + jcampIdx: curveIdx + } + }); + } else if (uiSweepType === _list_ui.LIST_UI_SWEEP_TYPE.CYCLIC_VOLTA_SET_REF && onPeak) { + yield (0, _effects.put)({ + type: _action_type.CYCLIC_VOLTA_METRY.SET_REF, + payload: { + index: voltammetryPeakIdx, + jcampIdx: curveIdx + } + }); + } +} +const managerSagas = [(0, _effects.takeEvery)(_action_type.UI.CLICK_TARGET, clickUiTarget), (0, _effects.takeEvery)(_action_type.UI.SWEEP.SELECT, selectUiSweep), (0, _effects.takeEvery)(_action_type.UI.WHEEL.SCROLL, scrollUiWheel)]; +var _default = exports.default = managerSagas; \ No newline at end of file diff --git a/dist/setupTests.js b/dist/setupTests.js new file mode 100644 index 00000000..fc453d7d --- /dev/null +++ b/dist/setupTests.js @@ -0,0 +1,8 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +var _enzyme = _interopRequireDefault(require("enzyme")); +var _enzymeAdapterReact = _interopRequireDefault(require("@wojtekmaj/enzyme-adapter-react-17")); +_enzyme.default.configure({ + adapter: new _enzymeAdapterReact.default() +}); \ No newline at end of file diff --git a/dist/third_party/jAnalyzer.js b/dist/third_party/jAnalyzer.js new file mode 100644 index 00000000..7d25625b --- /dev/null +++ b/dist/third_party/jAnalyzer.js @@ -0,0 +1,590 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +/* eslint-disable */ +// https://github.com/cheminfo-js/spectra/blob/master/packages/spectra-data/src/peakPicking/jAnalyzer.js + +/* + * This library implements the J analyser described by Cobas et al in the paper: + * A two-stage approach to automatic determination of 1H NMR coupling constants + */ +const patterns = ['s', 'd', 't', 'q', 'quint', 'h', 'sept', 'o', 'n']; +let symRatio = 1.5; +let maxErrorIter1 = 2.5; // Hz +let maxErrorIter2 = 1; // Hz +var _default = exports.default = { + /** + * The compilation process implements at the first stage a normalization procedure described by Golotvin et al. + * embedding in peak-component-counting method described by Hoyes et al. + * @param {object} signal + * @private + */ + compilePattern: function compilePattern(signal) { + signal.multiplicity = 'm'; + // 1.1 symmetrize + // It will add a set of peaks(signal.peaksComp) to the signal that will be used during + // the compilation process. The unit of those peaks will be in Hz + signal.symRank = symmetrizeChoiseBest(signal, maxErrorIter1, 1); + signal.asymmetric = true; + // Is the signal symmetric? + if (signal.symRank >= 0.95 && signal.peaksComp.length < 32) { + signal.asymmetric = false; + var i, j, n, P1, n2, maxFlagged; + let k = 1; + let Jc = []; + + // Loop over the possible number of coupling contributing to the multiplet + for (n = 0; n < 9; n++) { + // 1.2 Normalize. It makes a deep copy of the peaks before to modify them. + let peaks = normalize(signal, n); + // signal.peaksCompX = peaks; + let validPattern = false; // It will change to true, when we find the good patter + // Lets check if the signal could be a singulet. + if (peaks.length === 1 && n === 0) { + validPattern = true; + } else { + if (peaks.length <= 1) { + continue; + } + } + // 1.3 Establish a range for the Heights Hi [peaks.intensity*0.85,peaks.intensity*1.15]; + let ranges = getRanges(peaks); + n2 = Math.pow(2, n); + + // 1.4 Find a combination of integer heights Hi, one from each Si, that sums to 2^n. + let heights = null; + let counter = 1; + while (!validPattern && (heights = getNextCombination(ranges, n2)) !== null && counter < 400) { + // 2.1 Number the components of the multiplet consecutively from 1 to 2n, + // starting at peak 1 + let numbering = new Array(heights.length); + k = 1; + for (i = 0; i < heights.length; i++) { + numbering[i] = new Array(heights[i]); + for (j = 0; j < heights[i]; j++) { + numbering[i][j] = k++; + } + } + Jc = []; // The array to store the detected j-coupling + // 2.2 Set j = 1; J1 = P2 - P1. Flag components 1 and 2 as accounted for. + j = 1; + Jc.push(peaks[1].x - peaks[0].x); + P1 = peaks[0].x; + numbering[0].splice(0, 1); // Flagged + numbering[1].splice(0, 1); // Flagged + k = 1; + let nFlagged = 2; + maxFlagged = Math.pow(2, n) - 1; + while (Jc.length < n && nFlagged < maxFlagged && k < peaks.length) { + counter += 1; + // 4.1. Increment j. Set k to the number of the first unflagged component. + j++; + while (k < peaks.length && numbering[k].length === 0) { + k++; + } + if (k < peaks.length) { + // 4.2 Jj = Pk - P1. + Jc.push(peaks[k].x - peaks[0].x); + // Flag component k and, for each sum of the... + numbering[k].splice(0, 1); // Flageed + nFlagged++; + // Flag the other components of the multiplet + for (let u = 2; u <= j; u++) { + // TODO improve those loops + let jSum = 0; + for (i = 0; i < u; i++) { + jSum += Jc[i]; + } + for (i = 1; i < numbering.length; i++) { + // Maybe 0.25 Hz is too much? + if (Math.abs(peaks[i].x - (P1 + jSum)) < 0.25) { + numbering[i].splice(0, 1); // Flageed + nFlagged++; + break; + } + } + } + } + } + // Calculate the ideal patter by using the extracted j-couplings + let pattern = idealPattern(Jc); + // Compare the ideal pattern with the proposed intensities. + // All the intensities have to match to accept the multiplet + validPattern = true; + for (i = 0; i < pattern.length; i++) { + if (pattern[i].intensity !== heights[i]) { + validPattern = false; + } + } + } + // If we found a valid pattern we should inform about the pattern. + if (validPattern) { + updateSignal(signal, Jc); + } + } + } + // Before to return, change the units of peaksComp from Hz to PPM again + for (i = 0; i < signal.peaksComp.length; i++) { + signal.peaksComp[i].x /= signal.observe; + } + } +}; +/** + * @private + * update the signal + * @param {*} signal + * @param {*} Jc + */ +function updateSignal(signal, Jc) { + // Update the limits of the signal + let peaks = signal.peaksComp; // Always in Hz + let nbPeaks = peaks.length; + signal.startX = peaks[0].x / signal.observe - peaks[0].width; + signal.stopX = peaks[nbPeaks - 1].x / signal.observe + peaks[nbPeaks - 1].width; + + // signal.integralData.from = peaks[0].x / signal.observe - peaks[0].width * 3; + // signal.integralData.to = + // peaks[nbPeaks - 1].x / signal.observe + peaks[nbPeaks - 1].width * 3; + + // Compile the pattern and format the constant couplings + signal.maskPattern = signal.mask2; + signal.multiplicity = abstractPattern(signal, Jc); + signal.pattern = signal.multiplicity; // Our library depends on this parameter, but it is old + // console.log(signal); + /* if (DEBUG) { + console.log('Final j-couplings: ' + JSON.stringify(Jc)); + }*/ +} + +/** + * Returns the multiplet in the compact format + * @param {object} signal + * @param {object} Jc + * @return {string} + * @private + */ +function abstractPattern(signal, Jc) { + let tol = 0.05; + let i; + let pattern = ''; + let cont = 1; + let newNmrJs = []; + if (Jc && Jc.length > 0) { + Jc.sort(function (a, b) { + return Math.abs(b) - Math.abs(a); + }); + for (i = 0; i < Jc.length - 1; i++) { + if (Math.abs(Jc[i] - Jc[i + 1]) < tol) { + cont++; + } else { + newNmrJs.push({ + coupling: Math.abs(Jc[i]), + multiplicity: patterns[cont] + }); + pattern += patterns[cont]; + cont = 1; + } + } + newNmrJs.push({ + coupling: Math.abs(Jc[i]), + multiplicity: patterns[cont] + }); + pattern += patterns[cont]; + signal.nmrJs = newNmrJs; + } else { + pattern = 's'; + // if (Math.abs(signal.startX - signal.stopX) * signal.observe > 16) { + // pattern = 'br s'; + // } + } + return pattern; +} + +/** + * This function creates an ideal pattern from the given J-couplings + * @private + * @param {Array} Jc + * @return {*[]} + * @private + */ +function idealPattern(Jc) { + let hsum = Math.pow(2, Jc.length); + let i, j; + let pattern = [{ + x: 0, + intensity: hsum + }]; + // To split the initial height + for (i = 0; i < Jc.length; i++) { + for (j = pattern.length - 1; j >= 0; j--) { + pattern.push({ + x: pattern[j].x + Jc[i] / 2, + intensity: pattern[j].intensity / 2 + }); + pattern[j].x = pattern[j].x - Jc[i] / 2; + pattern[j].intensity = pattern[j].intensity / 2; + } + } + // To sum the heights in the same positions + pattern.sort(function compare(a, b) { + return a.x - b.x; + }); + for (j = pattern.length - 2; j >= 0; j--) { + if (Math.abs(pattern[j].x - pattern[j + 1].x) < 0.1) { + pattern[j].intensity += pattern[j + 1].intensity; + pattern.splice(j + 1, 1); + } + } + return pattern; +} + +/** + * Find a combination of integer heights Hi, one from each Si, that sums to 2n. + * @param {object} ranges + * @param {number} value + * @return {*} + * @private + */ +function getNextCombination(ranges, value) { + let half = Math.ceil(ranges.values.length * 0.5); + let lng = ranges.values.length; + let sum = 0; + let i, ok; + while (sum !== value) { + // Update the indexes to point at the next possible combination + ok = false; + while (!ok) { + ok = true; + ranges.currentIndex[ranges.active]++; + if (ranges.currentIndex[ranges.active] >= ranges.values[ranges.active].length) { + // In this case, there is no more possible combinations + if (ranges.active + 1 === half) { + return null; + } else { + // If this happens we need to try the next active peak + ranges.currentIndex[ranges.active] = 0; + ok = false; + ranges.active++; + } + } else { + ranges.active = 0; + } + } + // Sum the heights for this combination + sum = 0; + for (i = 0; i < half; i++) { + sum += ranges.values[i][ranges.currentIndex[i]] * 2; + } + if (ranges.values.length % 2 !== 0) { + sum -= ranges.values[half - 1][ranges.currentIndex[half - 1]]; + } + } + // If the sum is equal to the expected value, fill the array to return + if (sum === value) { + let heights = new Array(lng); + for (i = 0; i < half; i++) { + heights[i] = ranges.values[i][ranges.currentIndex[i]]; + heights[lng - i - 1] = ranges.values[i][ranges.currentIndex[i]]; + } + return heights; + } + return null; +} + +/** + * This function generates the possible values that each peak can contribute + * to the multiplet. + * @param {Array} peaks Array of objects with peaks information {intensity} + * @return {{values: Array, currentIndex: Array, active: number}} + * @private + */ +function getRanges(peaks) { + let ranges = new Array(peaks.length); + let currentIndex = new Array(peaks.length); + let min, max; + ranges[0] = [1]; + ranges[peaks.length - 1] = [1]; + currentIndex[0] = -1; + currentIndex[peaks.length - 1] = 0; + for (let i = 1; i < peaks.length - 1; i++) { + min = Math.round(peaks[i].intensity * 0.85); + max = Math.round(peaks[i].intensity * 1.15); + ranges[i] = []; + for (let j = min; j <= max; j++) { + ranges[i].push(j); + } + currentIndex[i] = 0; + } + return { + values: ranges, + currentIndex: currentIndex, + active: 0 + }; +} +/** + * Performs a symmetrization of the signal by using different aproximations to the center. + * It will return the result of the symmetrization that removes less peaks from the signal + * @param {object} signal + * @param {number} maxError + * @param {number} iteration + * @return {*} + * @private + */ +function symmetrizeChoiseBest(signal, maxError, iteration) { + let symRank1 = symmetrize(signal, maxError, iteration); + let tmpPeaks = signal.peaksComp; + let tmpMask = signal.mask; + let cs = signal.delta1; + signal.delta1 = (signal.peaks[0].x + signal.peaks[signal.peaks.length - 1].x) / 2; + let symRank2 = symmetrize(signal, maxError, iteration); + if (signal.peaksComp.length > tmpPeaks.length) { + return symRank2; + } else { + signal.delta1 = cs; + signal.peaksComp = tmpPeaks; + signal.mask = tmpMask; + return symRank1; + } +} + +/** + * This function will return a set of symmetric peaks that will + * be the enter point for the patter compilation process. + * @param {object} signal + * @param {number} maxError + * @param {number} iteration + * @return {number} + * @private + */ +function symmetrize(signal, maxError, iteration) { + // Before to symmetrize we need to keep only the peaks that possibly conforms the multiplete + let max, min, avg, ratio, avgWidth, i; + let peaks = new Array(signal.peaks.length); + // Make a deep copy of the peaks and convert PPM ot HZ + for (i = 0; i < peaks.length; i++) { + peaks[i] = { + x: signal.peaks[i].x * signal.observe, + intensity: signal.peaks[i].intensity, + width: signal.peaks[i].width + }; + } + // Join the peaks that are closer than 0.25 Hz + for (i = peaks.length - 2; i >= 0; i--) { + if (Math.abs(peaks[i].x - peaks[i + 1].x) < 0.25) { + peaks[i].x = peaks[i].x * peaks[i].intensity + peaks[i + 1].x * peaks[i + 1].intensity; + peaks[i].intensity = peaks[i].intensity + peaks[i + 1].intensity; + peaks[i].x /= peaks[i].intensity; + peaks[i].intensity /= 2; + peaks[i].width += peaks[i + 1].width; + peaks.splice(i + 1, 1); + } + } + signal.peaksComp = peaks; + let nbPeaks = peaks.length; + let mask = new Array(nbPeaks); + signal.mask = mask; + let left = 0; + let right = peaks.length - 1; + let cs = signal.delta1 * signal.observe; + let middle = [(peaks[0].x + peaks[nbPeaks - 1].x) / 2, 1]; + maxError = error(Math.abs(cs - middle[0])); + let heightSum = 0; + // We try to symmetrize the extreme peaks. We consider as candidates for symmetricing those which have + // ratio smaller than 3 + for (i = 0; i < nbPeaks; i++) { + mask[i] = true; + heightSum += signal.peaks[i].intensity; + } + while (left <= right) { + mask[left] = true; + mask[right] = true; + if (left === right) { + if (nbPeaks > 2 && Math.abs(peaks[left].x - cs) > maxError) { + mask[left] = false; + } + } else { + max = Math.max(peaks[left].intensity, peaks[right].intensity); + min = Math.min(peaks[left].intensity, peaks[right].intensity); + ratio = max / min; + if (ratio > symRatio) { + if (peaks[left].intensity === min) { + mask[left] = false; + right++; + } else { + mask[right] = false; + left--; + } + } else { + let diffL = Math.abs(peaks[left].x - cs); + let diffR = Math.abs(peaks[right].x - cs); + if (Math.abs(diffL - diffR) < maxError) { + avg = Math.min(peaks[left].intensity, peaks[right].intensity); + avgWidth = Math.min(peaks[left].width, peaks[right].width); + peaks[left].intensity = peaks[right].intensity = avg; + peaks[left].width = peaks[right].width = avgWidth; + middle = [middle[0] + (peaks[right].x + peaks[left].x) / 2, middle[1] + 1]; + } else { + if (Math.max(diffL, diffR) === diffR) { + mask[right] = false; + left--; + } else { + mask[left] = false; + right++; + } + } + } + } + left++; + right--; + // Only alter cs if it is the first iteration of the sym process. + if (iteration === 1) { + cs = chemicalShift(peaks, mask); + // There is not more available peaks + if (isNaN(cs)) { + return 0; + } + } + maxError = error(Math.abs(cs - middle[0] / middle[1])); + } + // To remove the weak peaks and recalculate the cs + for (i = nbPeaks - 1; i >= 0; i--) { + if (mask[i] === false) { + peaks.splice(i, 1); + } + } + cs = chemicalShift(peaks); + if (isNaN(cs)) { + return 0; + } + signal.delta1 = cs / signal.observe; + // Now, the peak should be symmetric in heights, but we need to know if it is symmetric in x + let symFactor = 0; + let weight = 0; + if (peaks.length > 1) { + for (i = Math.ceil(peaks.length / 2) - 1; i >= 0; i--) { + symFactor += (3 + Math.min(Math.abs(peaks[i].x - cs), Math.abs(peaks[peaks.length - 1 - i].x - cs))) / (3 + Math.max(Math.abs(peaks[i].x - cs), Math.abs(peaks[peaks.length - 1 - i].x - cs))) * peaks[i].intensity; + weight += peaks[i].intensity; + } + symFactor /= weight; + } else { + if (peaks.length === 1) { + symFactor = 1; + } + } + let newSumHeights = 0; + for (i = 0; i < peaks.length; i++) { + newSumHeights += peaks[i].intensity; + } + symFactor -= (heightSum - newSumHeights) / heightSum * 0.12; // Removed peaks penalty + // Sometimes we need a second opinion after the first symmetrization. + if (symFactor > 0.8 && symFactor < 0.97 && iteration < 2) { + return symmetrize(signal, maxErrorIter2, 2); + } else { + // Center the given pattern at cs and symmetrize x + if (peaks.length > 1) { + let dxi; + for (i = Math.ceil(peaks.length / 2) - 1; i >= 0; i--) { + dxi = (peaks[i].x - peaks[peaks.length - 1 - i].x) / 2.0; + peaks[i].x = cs + dxi; + peaks[peaks.length - 1 - i].x = cs - dxi; + } + } + } + return symFactor; +} +/** + * Error validator + * @param {number} value + * @return {number} + * @private + */ +function error(value) { + let maxError = value * 2.5; + if (maxError < 0.75) { + maxError = 0.75; + } + if (maxError > 3) { + maxError = 3; + } + return maxError; +} +/** + * @private + * 2 stages normalizarion of the peaks heights to Math.pow(2,n). + * Creates a new mask with the peaks that could contribute to the multiplete + * @param {object} signal + * @param {number} n + * @return {*} + */ +function normalize(signal, n) { + // Perhaps this is slow + let peaks = JSON.parse(JSON.stringify(signal.peaksComp)); + let norm = 0; + let norm2 = 0; + for (var i = 0; i < peaks.length; i++) { + norm += peaks[i].intensity; + } + norm = Math.pow(2, n) / norm; + signal.mask2 = JSON.parse(JSON.stringify(signal.mask)); + let index = signal.mask2.length - 1; + for (i = peaks.length - 1; i >= 0; i--) { + peaks[i].intensity *= norm; + while (index >= 0 && signal.mask2[index] === false) { + index--; + } + if (peaks[i].intensity < 0.75) { + peaks.splice(i, 1); + signal.mask2[index] = false; + } else { + norm2 += peaks[i].intensity; + } + index--; + } + norm2 = Math.pow(2, n) / norm2; + for (i = peaks.length - 1; i >= 0; i--) { + peaks[i].intensity *= norm2; + } + return peaks; +} + +/** + * @private + * Calculates the chemical shift as the weighted sum of the peaks + * @param {Array} peaks + * @param {Array} mask + * @return {number} + */ +function chemicalShift(peaks, mask) { + let sum = 0; + let cs = 0; + let i, area; + if (mask) { + for (i = 0; i < peaks.length; i++) { + if (mask[i] === true) { + area = getArea(peaks[i]); + sum += area; + cs += area * peaks[i].x; + } + } + } else { + for (i = 0; i < peaks.length; i++) { + area = getArea(peaks[i]); + sum += area; + cs += area * peaks[i].x; + } + } + return cs / sum; +} + +/** + * Return the area of a Lorentzian function + * @param {object} peak - object with peak information + * @return {number} + * @private + */ +function getArea(peak) { + return Math.abs(peak.intensity * peak.width * 1.57); // 1.772453851); +} \ No newline at end of file diff --git a/dist/third_party/peakInterval.js b/dist/third_party/peakInterval.js new file mode 100644 index 00000000..406996d0 --- /dev/null +++ b/dist/third_party/peakInterval.js @@ -0,0 +1,105 @@ +"use strict"; + +var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getPeakIntervals = void 0; +var _mlSavitzkyGolayGeneralized = _interopRequireDefault(require("ml-savitzky-golay-generalized")); +// https://github.com/mljs/global-spectral-deconvolution/blob/master/src/gsd.js + +/* eslint-disable no-plusplus, operator-linebreak */ + +const options = { + sgOptions: { + windowSize: 9, + polynomial: 3 + }, + minMaxRatio: 0.00025, + broadRatio: 0.0, + maxCriteria: true, + smoothY: true, + realTopDetection: false, + heightFactor: 0, + boundaries: false, + derivativeThreshold: -1 +}; +const getPeakIntervals = entity => { + const data = entity.spectra[0].data[0]; + const X = data.x; + const dX = X[1] - X[0]; + const Y = (0, _mlSavitzkyGolayGeneralized.default)(data.y, data.x, { + windowSize: options.sgOptions.windowSize, + polynomial: options.sgOptions.polynomial, + derivative: 0 + }); + const dY = (0, _mlSavitzkyGolayGeneralized.default)(data.y, data.x, { + windowSize: options.sgOptions.windowSize, + polynomial: options.sgOptions.polynomial, + derivative: 1 + }); + const ddY = (0, _mlSavitzkyGolayGeneralized.default)(data.y, data.x, { + windowSize: options.sgOptions.windowSize, + polynomial: options.sgOptions.polynomial, + derivative: 2 + }); + let maxDdy = 0; + let maxY = 0; + for (let i = 0; i < Y.length; i++) { + if (Math.abs(ddY[i]) > maxDdy) { + maxDdy = Math.abs(ddY[i]); + } + if (Math.abs(Y[i]) > maxY) { + maxY = Math.abs(Y[i]); + } + } + let lastMax = null; + let lastMin = null; + const minddY = new Array(Y.length - 2); + const intervalL = new Array(Y.length); + const intervalR = new Array(Y.length); + const broadMask = new Array(Y.length - 2); + let minddYLen = 0; + let intervalLLen = 0; + let intervalRLen = 0; + let broadMaskLen = 0; + for (let i = 1; i < Y.length - 1; ++i) { + // filter based on derivativeThreshold + if (Math.abs(dY[i]) > options.derivativeThreshold) { + // Minimum in first derivative + if (dY[i] < dY[i - 1] && dY[i] <= dY[i + 1] || dY[i] <= dY[i - 1] && dY[i] < dY[i + 1]) { + lastMin = { + x: X[i], + index: i + }; + if (dX > 0 && lastMax !== null) { + intervalL[intervalLLen++] = lastMax; + intervalR[intervalRLen++] = lastMin; + } + } + + // Maximum in first derivative + if (dY[i] >= dY[i - 1] && dY[i] > dY[i + 1] || dY[i] > dY[i - 1] && dY[i] >= dY[i + 1]) { + lastMax = { + x: X[i], + index: i + }; + if (dX < 0 && lastMin !== null) { + intervalL[intervalLLen++] = lastMax; + intervalR[intervalRLen++] = lastMin; + } + } + } + // Minimum in second derivative + if (ddY[i] < ddY[i - 1] && ddY[i] < ddY[i + 1]) { + // TODO should we change this to have 3 arrays ? Huge overhead creating arrays + minddY[minddYLen++] = i; // ( [X[i], Y[i], i] ); + broadMask[broadMaskLen++] = Math.abs(ddY[i]) <= options.broadRatio * maxDdy; + } + } + return { + intervalL, + intervalR + }; +}; +exports.getPeakIntervals = getPeakIntervals; \ No newline at end of file diff --git a/src/__tests__/units/actions/integration.test.tsx b/src/__tests__/units/actions/integration.test.tsx index 859f35ac..125af940 100644 --- a/src/__tests__/units/actions/integration.test.tsx +++ b/src/__tests__/units/actions/integration.test.tsx @@ -1,5 +1,10 @@ import { - clearIntegrationAll, setIntegrationFkr, sweepIntegration, + addVisualSplitLine, + clearIntegrationAll, + removeVisualSplitLine, + setIntegrationFkr, + splitIntegration, + sweepIntegration, } from "../../../actions/integration"; import { INTEGRATION } from "../../../constants/action_type"; @@ -23,4 +28,22 @@ describe('Test redux action for integrations', () => { expect(type).toEqual(INTEGRATION.CLEAR_ALL) expect(payload).toEqual(payloadToBeSent) }) + + it('Split integration', () => { + const { type, payload } = splitIntegration(payloadToBeSent) + expect(type).toEqual(INTEGRATION.SPLIT) + expect(payload).toEqual(payloadToBeSent) + }) + + it('Add visual split line', () => { + const { type, payload } = addVisualSplitLine(payloadToBeSent) + expect(type).toEqual(INTEGRATION.ADD_VISUAL_SPLIT) + expect(payload).toEqual(payloadToBeSent) + }) + + it('Remove visual split line', () => { + const { type, payload } = removeVisualSplitLine(payloadToBeSent) + expect(type).toEqual(INTEGRATION.RM_VISUAL_SPLIT) + expect(payload).toEqual(payloadToBeSent) + }) }) diff --git a/src/__tests__/units/helpers/chem.test.tsx b/src/__tests__/units/helpers/chem.test.tsx index 743fad87..1a801491 100644 --- a/src/__tests__/units/helpers/chem.test.tsx +++ b/src/__tests__/units/helpers/chem.test.tsx @@ -3,6 +3,7 @@ import { ToFrequency, Convert2Scan, Convert2Thres, GetComparisons, Convert2DValue, GetCyclicVoltaRatio, GetCyclicVoltaPeakSeparate, convertTopic, Convert2MaxMinPeak, Feature2MaxMinPeak, GetCyclicVoltaShiftOffset, GetCyclicVoltaPreviousShift, + buildIntegFeature, } from "../../../helpers/chem"; import nmr1HJcamp from "../../fixtures/nmr1h_jcamp"; import aifJcamp1 from "../../fixtures/aif_jcamp_1"; @@ -519,6 +520,82 @@ describe('Test for chem helper', () => { }) }) + describe('Test build integration feature with persistent visualSplitGroupId', () => { + const linearSpectra = [{ data: [{ x: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], y: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0] }] }] + const buildJcamp = (records: Record) => ({ + info: { ...records }, + spectra: [{}], + }) + + it('returns integrations without groupId for legacy JCAMPs', () => { + const jcamp: any = buildJcamp({ + $OBSERVEDINTEGRALS: '\n0, 10, 5, 5', + }) + const feature = buildIntegFeature(jcamp, linearSpectra) + expect(feature.stack).toHaveLength(1) + expect(feature.stack[0]).toMatchObject({ xL: 0, xU: 10 }) + expect(feature.stack[0].visualSplitGroupId).toBeUndefined() + }) + + it('attaches a visualSplitGroupId from the JCAMP record', () => { + const jcamp: any = buildJcamp({ + $OBSERVEDINTEGRALS: '\n0, 4, 2, 2\n4, 10, 3, 3', + $OBSERVEDINTEGRALSGROUPS: '\n0, vsg-abc-1\n1, vsg-abc-1', + }) + const feature = buildIntegFeature(jcamp, linearSpectra) + expect(feature.stack[0].visualSplitGroupId).toBe('vsg-abc-1') + expect(feature.stack[1].visualSplitGroupId).toBe('vsg-abc-1') + }) + + it('preserves alphanumeric and dash characters in the groupId token', () => { + const jcamp: any = buildJcamp({ + $OBSERVEDINTEGRALS: '\n0, 10, 5, 5', + $OBSERVEDINTEGRALSGROUPS: '\n0, vsg-token_42-XYZ', + }) + const feature = buildIntegFeature(jcamp, linearSpectra) + expect(feature.stack[0].visualSplitGroupId).toBe('vsg-token_42-XYZ') + }) + + it('ignores groupId rows pointing to non existing integrations', () => { + const jcamp: any = buildJcamp({ + $OBSERVEDINTEGRALS: '\n0, 10, 5, 5', + $OBSERVEDINTEGRALSGROUPS: '\n5, vsg-orphan', + }) + const feature = buildIntegFeature(jcamp, linearSpectra) + expect(feature.stack[0].visualSplitGroupId).toBeUndefined() + }) + + it('keeps the groupId after the area normalisation pass', () => { + const jcamp: any = buildJcamp({ + $OBSERVEDINTEGRALS: '\n0, 10, 5, 5', + $OBSERVEDINTEGRALSGROUPS: '\n0, vsg-abc', + }) + const feature = buildIntegFeature(jcamp, linearSpectra) + expect(feature.stack[0].visualSplitGroupId).toBe('vsg-abc') + expect(feature.stack[0].area).toBeDefined() + }) + + it('parses GROUPS records that have no leading newline (header-less convention)', () => { + const jcamp: any = buildJcamp({ + $OBSERVEDINTEGRALS: '\n0, 4, 2, 2\n4, 10, 3, 3', + $OBSERVEDINTEGRALSGROUPS: '0, vsg-headerless\n1, vsg-headerless', + }) + const feature = buildIntegFeature(jcamp, linearSpectra) + expect(feature.stack[0].visualSplitGroupId).toBe('vsg-headerless') + expect(feature.stack[1].visualSplitGroupId).toBe('vsg-headerless') + }) + + it('ignores an arbitrary header line and still maps all valid rows', () => { + const jcamp: any = buildJcamp({ + $OBSERVEDINTEGRALS: '\n0, 4, 2, 2\n4, 10, 3, 3', + $OBSERVEDINTEGRALSGROUPS: ' (X Y)\n0, vsg-hdr\n1, vsg-hdr', + }) + const feature = buildIntegFeature(jcamp, linearSpectra) + expect(feature.stack[0].visualSplitGroupId).toBe('vsg-hdr') + expect(feature.stack[1].visualSplitGroupId).toBe('vsg-hdr') + }) + }) + describe('Test get previous offset CV layout', () => { const voltaData = {"spectraList":[{"list":[{"min":{"x":-1.5404,"y":-0.00000307144},"max":{"x":0.10003,"y":0.00000285434},"isRef":true,"e12":-0.720185,"pecker":{"x":0.380242,"y":0.00000164361}}],"selectedIdx":0,"isWorkMaxPeak":true,"jcampIdx":0,"shift":{"ref":{"min":{"x":-1.5404,"y":-0.00000307144},"max":{"x":0.10003,"y":0.00000285434},"isRef":true,"e12":-0.720185,"pecker":{"x":0.380242,"y":0.00000164361}},"val":0, "prevValue":0.5},"hasRefPeak":true},{"list":[{"min":{"x":-1.48904,"y":-0.000033747399999999995},"max":{"x":0.929483,"y":0.00023741},"isRef":true,"e12":-0.27977849999999993}],"selectedIdx":0,"isWorkMaxPeak":true,"jcampIdx":1,"shift":{"ref":{"min":{"x":-1.48904,"y":-0.000033747399999999995},"max":{"x":0.929483,"y":0.00023741},"isRef":true,"e12":-0.27977849999999993},"val":1.5}},{"list":[{"min":{"x":0.45977,"y":-0.000226347},"max":{"x":1.00943,"y":0.000371349},"isRef":false,"e12":0.7346}],"selectedIdx":0,"isWorkMaxPeak":true,"jcampIdx":2,"shift":{"ref":null,"val":0}}]} const voltaDataNoRef = {"spectraList":[{"list":[{"min":{"x":-1.5404,"y":-0.00000307144},"max":{"x":0.10003,"y":0.00000285434},"isRef":false,"e12":-0.720185,"pecker":{"x":0.380242,"y":0.00000164361}}],"selectedIdx":0,"isWorkMaxPeak":true,"jcampIdx":0,"shift":{"ref":null,"val":0, "prevValue":0.5},"hasRefPeak":false},{"list":[{"min":{"x":-1.48904,"y":-0.000033747399999999995},"max":{"x":0.929483,"y":0.00023741},"isRef":true,"e12":-0.27977849999999993}],"selectedIdx":0,"isWorkMaxPeak":true,"jcampIdx":1,"shift":{"ref":null,"val":1.5}},{"list":[{"min":{"x":0.45977,"y":-0.000226347},"max":{"x":1.00943,"y":0.000371349},"isRef":false,"e12":0.7346}],"selectedIdx":0,"isWorkMaxPeak":true,"jcampIdx":2,"shift":{"ref":null,"val":0}}]} diff --git a/src/__tests__/units/helpers/integration.test.tsx b/src/__tests__/units/helpers/integration.test.tsx index 4a46892b..8c28da53 100644 --- a/src/__tests__/units/helpers/integration.test.tsx +++ b/src/__tests__/units/helpers/integration.test.tsx @@ -1,4 +1,18 @@ -import { calcArea, getAbsoluteArea, getArea } from "../../../helpers/integration"; +import { + buildSplitIntervals, + calcArea, + generateVisualSplitGroupId, + getAbsoluteArea, + getAbsoluteAreaWithBaseline, + getArea, + getIntegrationPoints, + getLinearBaseline, + getSplitAreas, + getVisualSplitGroupBoundaries, + getVisualSplitGroups, + normalizeSplitLines, + splitAreaProportionally, +} from "../../../helpers/integration"; describe('Test helper for integration', () => { describe('Test get area', () => { @@ -25,6 +39,171 @@ describe('Test helper for integration', () => { }) }) + describe('Test AUC baseline', () => { + it('starts the baseline at the first integration point, not at y=0', () => { + const points = getIntegrationPoints(0, 3, [ + {x: 1, y: 10}, + {x: 2, y: 12}, + ]); + const baselineY = getLinearBaseline(points); + + expect(baselineY(points[0])).toEqual(points[0].y); + expect(baselineY(points[1])).toEqual(points[1].y); + }) + + it('uses a deterministic linear baseline for interior points', () => { + const points = getIntegrationPoints(0, 4, [ + {x: 1, y: 10}, + {x: 2, y: 14}, + {x: 3, y: 12}, + ]); + const baselineY = getLinearBaseline(points); + + expect(baselineY(points[1])).toEqual(11); + }) + }) + + describe('Test visual split areas', () => { + const splitData = [ + {x: 1, y: 0, k: 1}, + {x: 2, y: 4, k: 2}, + {x: 3, y: 4, k: 3}, + {x: 4, y: 4, k: 4}, + {x: 5, y: 0, k: 5}, + ]; + + it('builds sorted intervals from internal split lines only', () => { + expect(buildSplitIntervals(0, 10, [8, -1, 4, 11])).toEqual([ + { xL: 0, xU: 4 }, + { xL: 4, xU: 8 }, + { xL: 8, xU: 10 }, + ]); + }) + + it('calculates split areas with the global integration baseline', () => { + const mainPoints = getIntegrationPoints(0, 6, splitData); + const baselineY = getLinearBaseline(mainPoints); + + expect(getAbsoluteAreaWithBaseline(2.5, 6, splitData, baselineY)).toEqual(4); + expect(getSplitAreas(0, 6, [2.5], splitData)).toEqual([ + { xL: 0, xU: 2.5, area: 1, absoluteArea: 4 }, + { xL: 2.5, xU: 6, area: 2, absoluteArea: 4 }, + ]); + }) + }) + + describe('Test visual split groups', () => { + it('returns no groups for an empty stack', () => { + expect(getVisualSplitGroups([])).toEqual([]) + expect(getVisualSplitGroups(null as any)).toEqual([]) + }) + + it('returns singleton groups for items without groupId', () => { + const stack = [ + { xL: 0, xU: 4, area: 4, absoluteArea: 0 }, + { xL: 5, xU: 9, area: 4, absoluteArea: 0 }, + ] + const groups = getVisualSplitGroups(stack) + expect(groups).toHaveLength(2) + expect(groups[0].isMerged).toBe(false) + expect(groups[1].isMerged).toBe(false) + }) + + it('groups consecutive items sharing a visualSplitGroupId', () => { + const stack = [ + { xL: 0, xU: 4, area: 4, absoluteArea: 0, visualSplitGroupId: 'g1' }, + { xL: 4, xU: 7, area: 3, absoluteArea: 0, visualSplitGroupId: 'g1' }, + { xL: 8, xU: 10, area: 2, absoluteArea: 0 }, + ] + const groups = getVisualSplitGroups(stack) + expect(groups).toHaveLength(2) + expect(groups[0].items).toHaveLength(2) + expect(groups[0].xL).toEqual(0) + expect(groups[0].xU).toEqual(7) + expect(groups[0].isMerged).toBe(true) + expect(groups[1].isMerged).toBe(false) + }) + + it('treats non-consecutive items with the same id as separate groups', () => { + const stack = [ + { xL: 0, xU: 3, area: 3, absoluteArea: 0, visualSplitGroupId: 'g1' }, + { xL: 4, xU: 6, area: 2, absoluteArea: 0 }, + { xL: 7, xU: 10, area: 3, absoluteArea: 0, visualSplitGroupId: 'g1' }, + ] + const groups = getVisualSplitGroups(stack) + expect(groups).toHaveLength(3) + expect(groups[0].isMerged).toBe(false) + expect(groups[2].isMerged).toBe(false) + }) + + it('returns the internal boundaries between merged items', () => { + const stack = [ + { xL: 0, xU: 4, area: 4, absoluteArea: 0, visualSplitGroupId: 'g1' }, + { xL: 4, xU: 7, area: 3, absoluteArea: 0, visualSplitGroupId: 'g1' }, + { xL: 7, xU: 10, area: 3, absoluteArea: 0, visualSplitGroupId: 'g1' }, + ] + const groups = getVisualSplitGroups(stack) + expect(getVisualSplitGroupBoundaries(groups[0])).toEqual([4, 7]) + }) + + it('returns no boundaries for non-merged groups', () => { + const groups = getVisualSplitGroups([{ xL: 0, xU: 4, area: 4, absoluteArea: 0 }]) + expect(getVisualSplitGroupBoundaries(groups[0])).toEqual([]) + }) + }) + + describe('Test generate visual split group id', () => { + it('produces unique stable ids', () => { + const a = generateVisualSplitGroupId() + const b = generateVisualSplitGroupId() + expect(typeof a).toBe('string') + expect(a.startsWith('vsg-')).toBe(true) + expect(a).not.toEqual(b) + }) + }) + + describe('Test normalize split lines', () => { + it('returns an empty array when value is missing or invalid', () => { + expect(normalizeSplitLines(undefined)).toEqual([]) + expect(normalizeSplitLines(null as any)).toEqual([]) + expect(normalizeSplitLines('foo' as any)).toEqual([]) + expect(normalizeSplitLines([NaN, undefined, null, 'x'] as any)).toEqual([]) + }) + + it('keeps finite values, deduplicates them and returns ascending order', () => { + expect(normalizeSplitLines([4, 2, 4, 1.5, NaN, '3' as any])).toEqual([1.5, 2, 3, 4]) + }) + }) + + describe('Test split area proportionally', () => { + it('preserves the original total exactly while keeping the data ratio', () => { + const result = splitAreaProportionally(300, 200, 40) + expect(result.left + result.right).toBeCloseTo(300, 9) + expect(result.left / result.right).toBeCloseTo(200 / 40, 9) + }) + + it('returns zeros when the original total is zero', () => { + expect(splitAreaProportionally(0, 10, 20)).toEqual({ left: 0, right: 0 }) + }) + + it('splits evenly when the raw measurements cannot infer a proportion', () => { + expect(splitAreaProportionally(8, 0, 0)).toEqual({ left: 4, right: 4 }) + }) + + it('handles a single non-zero side by giving all the mass to it', () => { + const result = splitAreaProportionally(10, 0, 4) + expect(result.left).toEqual(0) + expect(result.right).toEqual(10) + }) + + it('gracefully treats non finite inputs as zero', () => { + expect(splitAreaProportionally(NaN as any, 1, 1)).toEqual({ left: 0, right: 0 }) + const result = splitAreaProportionally(10, NaN as any, 5) + expect(result.left).toEqual(0) + expect(result.right).toEqual(10) + }) + }) + describe('Test calculate area', () => { it('Do not ignore ref', () => { const data = { area: 0.5 } diff --git a/src/__tests__/units/helpers/integration_draft.test.tsx b/src/__tests__/units/helpers/integration_draft.test.tsx new file mode 100644 index 00000000..ab4cca80 --- /dev/null +++ b/src/__tests__/units/helpers/integration_draft.test.tsx @@ -0,0 +1,50 @@ +import { + clearPendingIntegrationDraft, + confirmCancelPendingIntegration, + forgetPendingIntegrationDraft, + hasPendingIntegrationDraft, + setPendingIntegrationDraft, +} from "../../../helpers/integration_draft"; + +describe('Test helper for pending integration draft', () => { + afterEach(() => { + jest.restoreAllMocks(); + forgetPendingIntegrationDraft(); + }); + + it('tracks pending draft state', () => { + expect(hasPendingIntegrationDraft()).toBe(false); + + setPendingIntegrationDraft({}); + + expect(hasPendingIntegrationDraft()).toBe(true); + }); + + it('clears a draft and invokes its cancel callback', () => { + const cancel = jest.fn(); + setPendingIntegrationDraft({ cancel }); + + clearPendingIntegrationDraft(); + + expect(cancel).toHaveBeenCalledTimes(1); + expect(hasPendingIntegrationDraft()).toBe(false); + }); + + it('keeps a draft when cancellation is refused', () => { + jest.spyOn(window, 'confirm').mockReturnValue(false); + setPendingIntegrationDraft({}); + + expect(confirmCancelPendingIntegration()).toBe(false); + expect(hasPendingIntegrationDraft()).toBe(true); + }); + + it('clears a draft when cancellation is accepted', () => { + const cancel = jest.fn(); + jest.spyOn(window, 'confirm').mockReturnValue(true); + setPendingIntegrationDraft({ cancel }); + + expect(confirmCancelPendingIntegration()).toBe(true); + expect(cancel).toHaveBeenCalledTimes(1); + expect(hasPendingIntegrationDraft()).toBe(false); + }); +}); diff --git a/src/__tests__/units/helpers/integration_split.test.tsx b/src/__tests__/units/helpers/integration_split.test.tsx new file mode 100644 index 00000000..7c9d4dc3 --- /dev/null +++ b/src/__tests__/units/helpers/integration_split.test.tsx @@ -0,0 +1,193 @@ +import { + NMR_SPLIT_PREVIEW_EXTENT, + getIntegrationBounds, + getIntegrationSplitTarget, + getVisualSplitLineAtX, + getVisualSplitLines, + interpolateY, + isAlreadyVisuallySplit, + isMergedVisualSplitGroup, + resolveSplitPreviewExtent, +} from "../../../helpers/integration_split"; + +describe('Test helper for integration split preview', () => { + const data = [ + { x: 0, y: 0 }, + { x: 2, y: 4 }, + { x: 4, y: 2 }, + { x: 6, y: 0 }, + ]; + + it('resolves shifted integration bounds in data coordinates', () => { + expect(getIntegrationBounds({ xL: 1, xU: 7 }, 1)).toEqual([0, 6]); + }); + + it('finds a split target only when the x position is inside an integration', () => { + const focus = { + integrationSplitTargets: { + stack: [{ xL: 0, xU: 3 }, { xL: 5, xU: 8 }], + shift: 0, + }, + }; + + expect(getIntegrationSplitTarget(focus, 2)).toEqual({ xL: 0, xU: 3 }); + expect(getIntegrationSplitTarget(focus, 4)).toBeUndefined(); + }); + + it('interpolates y values between neighbouring points', () => { + expect(interpolateY(data, 3)).toEqual(3); + }); + + it('resolves visual split boundaries from the stack groups in data coordinates', () => { + const stack = [ + { xL: 1, xU: 4, visualSplitGroupId: 'g1' }, + { xL: 4, xU: 6, visualSplitGroupId: 'g1' }, + ]; + expect(getVisualSplitLines(stack, 1)).toEqual([3]); + }); + + it('returns no visual split lines when no group is merged', () => { + const stack = [ + { xL: 0, xU: 4 }, + { xL: 4, xU: 6 }, + ]; + expect(getVisualSplitLines(stack, 0)).toEqual([]); + }); + + it('finds an existing visual split line by screen tolerance', () => { + const focus = { + scales: { x: (value: number) => value * 10 }, + }; + const stack = [ + { xL: 0, xU: 3, visualSplitGroupId: 'g1' }, + { xL: 3, xU: 6, visualSplitGroupId: 'g1' }, + ]; + + expect(getVisualSplitLineAtX(focus, stack, 3.4, 0, 5)).toEqual(3); + expect(getVisualSplitLineAtX(focus, stack, 3.7, 0, 5)).toBeNull(); + }); + + it('uses fixed NMR preview bounds and rejects edge positions', () => { + const focus = { scales: { y: (value: number) => value }, data }; + const target = { xL: 0, xU: 6 }; + + expect(resolveSplitPreviewExtent(focus, target, 3, 0, false)).toEqual(NMR_SPLIT_PREVIEW_EXTENT); + expect(resolveSplitPreviewExtent(focus, target, 0, 0, false)).toBeNull(); + }); + + describe('isAlreadyVisuallySplit', () => { + it('returns false for a pristine integration that has never been split', () => { + expect(isAlreadyVisuallySplit({ xL: 0, xU: 10 })).toBe(false); + }); + + it('returns true for a child stack item that carries a visualSplitGroupId', () => { + expect(isAlreadyVisuallySplit({ xL: 0, xU: 4, visualSplitGroupId: 'g1' })).toBe(true); + }); + + it('returns true for a merged group container exposed to the UI', () => { + expect(isAlreadyVisuallySplit({ + xL: 0, xU: 10, isMerged: true, groupId: 'g1', + })).toBe(true); + }); + + it('returns false for a singleton group container', () => { + expect(isAlreadyVisuallySplit({ + xL: 0, xU: 10, isMerged: false, groupId: null, + })).toBe(false); + }); + + it('returns false for nullish or empty input', () => { + expect(isAlreadyVisuallySplit(null as any)).toBe(false); + expect(isAlreadyVisuallySplit(undefined as any)).toBe(false); + }); + }); + + describe('isMergedVisualSplitGroup', () => { + it('returns true only for a merged group container', () => { + expect(isMergedVisualSplitGroup({ + xL: 0, xU: 10, isMerged: true, groupId: 'g1', + })).toBe(true); + }); + + it('returns false for a raw child stack item carrying a groupId', () => { + expect(isMergedVisualSplitGroup({ xL: 0, xU: 4, visualSplitGroupId: 'g1' })).toBe(false); + }); + + it('returns false for a pristine integration and for nullish input', () => { + expect(isMergedVisualSplitGroup({ xL: 0, xU: 10 })).toBe(false); + expect(isMergedVisualSplitGroup(null as any)).toBe(false); + expect(isMergedVisualSplitGroup(undefined as any)).toBe(false); + }); + }); + + it('resolves HPLC preview bounds between baseline and curve', () => { + const focus = { + scales: { y: (value: number) => value }, + data: [ + { x: 0, y: 0 }, + { x: 2, y: 4 }, + { x: 3, y: 6 }, + { x: 4, y: 4 }, + { x: 6, y: 0 }, + ], + }; + + const extent = resolveSplitPreviewExtent(focus, { xL: 0, xU: 6 }, 3, 0, true); + + expect(extent).toEqual({ y1: 4, y2: 6 }); + }); + + it('uses the merged group bounds for the baseline when previewing on a visual split child', () => { + const stack = [ + { xL: 0, xU: 3, visualSplitGroupId: 'g1' }, + { xL: 3, xU: 6, visualSplitGroupId: 'g1' }, + ]; + const focus: any = { + scales: { y: (value: number) => value }, + data: [ + { x: 0, y: 0 }, + { x: 1, y: 2 }, + { x: 2, y: 4 }, + { x: 3, y: 6 }, + { x: 4, y: 4 }, + { x: 5, y: 2 }, + { x: 6, y: 0 }, + ], + integrationSplitTargets: { stack, shift: 0, ignoreRef: true }, + }; + + const childExtent = resolveSplitPreviewExtent(focus, stack[1], 4, 0, true); + const groupExtent = resolveSplitPreviewExtent( + focus, + { xL: 0, xU: 6, isMerged: true }, + 4, + 0, + true, + ); + + expect(childExtent).toEqual(groupExtent); + expect(childExtent).toEqual({ y1: 2, y2: 4 }); + }); + + it('falls back to the local target bounds when no visual split context is available', () => { + const focus: any = { + scales: { y: (value: number) => value }, + data: [ + { x: 0, y: 10 }, + { x: 1, y: 2 }, + { x: 2, y: 4 }, + { x: 3, y: 6 }, + { x: 4, y: 4 }, + { x: 5, y: 2 }, + { x: 6, y: 10 }, + ], + }; + + const targetOnly = resolveSplitPreviewExtent(focus, { xL: 1, xU: 5 }, 3, 0, true); + const fullData = resolveSplitPreviewExtent(focus, { xL: 0, xU: 6 }, 3, 0, true); + + expect(targetOnly).not.toBeNull(); + expect(fullData).not.toBeNull(); + expect(targetOnly).not.toEqual(fullData); + }); +}); diff --git a/src/__tests__/units/helpers/sweep.test.tsx b/src/__tests__/units/helpers/sweep.test.tsx new file mode 100644 index 00000000..3e2037a9 --- /dev/null +++ b/src/__tests__/units/helpers/sweep.test.tsx @@ -0,0 +1,42 @@ +import { buildSweepPayloadFromXBounds } from "../../../helpers/sweep"; + +describe('Test helper for sweep payloads', () => { + const data = [{ x: 0, y: 1, k: 0 }, { x: 2, y: 2, k: 1 }]; + const dataPks = [{ x: 1, y: 2 }]; + + it('builds the same integration sweep shape from two x bounds', () => { + const focus = { + data, + dataPks, + currentExtent: { + yExtent: { yL: -1, yU: 3 }, + }, + }; + + const payload = buildSweepPayloadFromXBounds(focus, 2, 0); + + expect(payload).toEqual({ + xExtent: { xL: 0, xU: 2 }, + yExtent: { yL: -1, yU: 3 }, + data, + dataPks, + }); + }); + + it('accepts zero as a valid lower x bound', () => { + const focus = { + data, + dataPks, + h: 10, + scales: { + y: { + invert: (value) => value, + }, + }, + }; + + const payload = buildSweepPayloadFromXBounds(focus, 0, 1); + + expect(payload.xExtent).toEqual({ xL: 0, xU: 1 }); + }); +}); diff --git a/src/__tests__/units/reducers/reducer_integration.test.tsx b/src/__tests__/units/reducers/reducer_integration.test.tsx new file mode 100644 index 00000000..602d30e0 --- /dev/null +++ b/src/__tests__/units/reducers/reducer_integration.test.tsx @@ -0,0 +1,840 @@ +import { INTEGRATION } from "../../../constants/action_type"; +import undoableIntegrationReducer, { integrationReducer } from "../../../reducers/reducer_integration"; +import { ActionCreators, newHistory } from "redux-undo"; + +describe('Test redux reducer for integrations', () => { + const data = [ + { x: 0, y: 0, k: 0 }, + { x: 2, y: 3, k: 2 }, + { x: 4, y: 4, k: 4 }, + { x: 6, y: 2, k: 6 }, + { x: 8, y: 1, k: 8 }, + { x: 10, y: 0, k: 10 }, + ]; + + it('splits one integration into two calculated integrations atomically', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [{ xL: 0, xU: 10, area: 10, absoluteArea: 0 }], + refArea: 10, + refFactor: 2, + shift: 0, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 10 }, + splitX: 4, + data, + }, + }); + + expect(newState.integrations[0].stack).toHaveLength(2); + expect(newState.integrations[0].stack[0]).toMatchObject({ xL: 0, xU: 4, area: 4 }); + expect(newState.integrations[0].stack[1]).toMatchObject({ xL: 4, xU: 10, area: 6 }); + expect(newState.integrations[0].refArea).toEqual(10); + expect(newState.integrations[0].refFactor).toEqual(2); + }); + + it('keeps shifted stored bounds while calculating with data coordinates', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [{ xL: 1, xU: 11, area: 10, absoluteArea: 0 }], + refArea: 10, + refFactor: 1, + shift: 1, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.SPLIT, + payload: { + curveIdx: 0, + target: { xL: 1, xU: 11 }, + splitX: 4, + data, + }, + }); + + expect(newState.integrations[0].stack[0]).toMatchObject({ xL: 1, xU: 5, area: 4 }); + expect(newState.integrations[0].stack[1]).toMatchObject({ xL: 5, xU: 11, area: 6 }); + }); + + it('rejects splits that do not leave enough data resolution on both sides', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [{ xL: 0, xU: 10, area: 10, absoluteArea: 0 }], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 10 }, + splitX: 1, + data, + }, + }); + + expect(newState).toBe(state); + }); + + it('removes an integration whose lower x bound is zero', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [ + { xL: 0, xU: 10, area: 10, absoluteArea: 0 }, + { xL: 12, xU: 14, area: 2, absoluteArea: 0 }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.RM_ONE, + payload: { + curveIdx: 0, + dataToRemove: { xL: 0, xU: 10 }, + }, + }); + + expect(newState.integrations[0].stack).toEqual([{ xL: 12, xU: 14, area: 2, absoluteArea: 0 }]); + }); + + it('undoes a split in one step', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [{ xL: 0, xU: 10, area: 10, absoluteArea: 0 }], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + const splitPresent = integrationReducer(state, { + type: INTEGRATION.SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 10 }, + splitX: 4, + data, + }, + }); + const splitState: any = newHistory([state], splitPresent, []); + const undoneState = undoableIntegrationReducer(splitState, ActionCreators.undo()); + + expect(splitState.present.integrations[0].stack).toHaveLength(2); + expect(undoneState.present.integrations[0].stack).toEqual(state.integrations[0].stack); + }); + + it('undoes the very first integration added after a fresh load', () => { + const freshPresent = { + selectedIdx: 0, + integrations: [{ + stack: [], + refArea: 1, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + const fresh: any = newHistory([], freshPresent, []); + + const afterAdd = undoableIntegrationReducer(fresh, { + type: 'UI_SWEEP_SELECT_INTEGRATION', + payload: { + curveIdx: 0, + newData: { xExtent: { xL: 2, xU: 8 }, data }, + }, + }); + + expect(afterAdd.past).toHaveLength(1); + expect(afterAdd.present.integrations[0].stack).toHaveLength(1); + + const undone = undoableIntegrationReducer(afterAdd, ActionCreators.undo()); + expect(undone.present.integrations[0].stack).toHaveLength(0); + + const redone = undoableIntegrationReducer(undone, ActionCreators.redo()); + expect(redone.present.integrations[0].stack).toHaveLength(1); + }); + + it('records a split in the undoable history when dispatched through the undoable reducer', () => { + const seed: any = { + selectedIdx: 0, + integrations: [{ + stack: [{ xL: 0, xU: 10, area: 10, absoluteArea: 0 }], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + const seedHistory: any = newHistory([], seed, []); + + const afterSplit = undoableIntegrationReducer(seedHistory, { + type: INTEGRATION.SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 10 }, + splitX: 4, + data, + }, + }); + + expect(afterSplit.past).toHaveLength(1); + expect(afterSplit.present.integrations[0].stack).toHaveLength(2); + + const undone = undoableIntegrationReducer(afterSplit, ActionCreators.undo()); + expect(undone.present.integrations[0].stack).toEqual(seed.integrations[0].stack); + + const redone = undoableIntegrationReducer(undone, ActionCreators.redo()); + expect(redone.present.integrations[0].stack).toEqual(afterSplit.present.integrations[0].stack); + }); + + it('adds a visual split line by splitting the target into two grouped stack items', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [{ xL: 0, xU: 10, area: 10, absoluteArea: 0 }], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.ADD_VISUAL_SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 10 }, + splitX: 4, + data, + }, + }); + + expect(newState.integrations[0].stack).toHaveLength(2); + expect(newState.integrations[0].stack[0]).toMatchObject({ xL: 0, xU: 4, area: 4 }); + expect(newState.integrations[0].stack[1]).toMatchObject({ xL: 4, xU: 10, area: 6 }); + const groupId = newState.integrations[0].stack[0].visualSplitGroupId; + expect(typeof groupId).toBe('string'); + expect(newState.integrations[0].stack[1].visualSplitGroupId).toBe(groupId); + }); + + it('stores shifted visual split bounds consistently with the integration stack item', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [{ xL: 1, xU: 11, area: 10, absoluteArea: 0 }], + refArea: 10, + refFactor: 1, + shift: 1, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.ADD_VISUAL_SPLIT, + payload: { + curveIdx: 0, + target: { xL: 1, xU: 11 }, + splitX: 4, + data, + }, + }); + + expect(newState.integrations[0].stack[0]).toMatchObject({ xL: 1, xU: 5 }); + expect(newState.integrations[0].stack[1]).toMatchObject({ xL: 5, xU: 11 }); + expect(newState.integrations[0].stack[0].visualSplitGroupId) + .toBe(newState.integrations[0].stack[1].visualSplitGroupId); + }); + + it('rejects splitting a child item that already belongs to a visual split group', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [ + { xL: 0, xU: 6, area: 6, absoluteArea: 0, visualSplitGroupId: 'g1' }, + { xL: 6, xU: 10, area: 4, absoluteArea: 0, visualSplitGroupId: 'g1' }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.ADD_VISUAL_SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 6 }, + splitX: 4, + data, + }, + }); + + expect(newState).toBe(state); + }); + + it('rejects a second visual split on a previously split integration restored from storage', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [ + { xL: 0, xU: 4, area: 4, absoluteArea: 0, visualSplitGroupId: 'persisted-vsg' }, + { xL: 4, xU: 10, area: 6, absoluteArea: 0, visualSplitGroupId: 'persisted-vsg' }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.ADD_VISUAL_SPLIT, + payload: { + curveIdx: 0, + target: { xL: 4, xU: 10 }, + splitX: 6, + data, + }, + }); + + expect(newState).toBe(state); + }); + + it('preserves the original stored area when visually splitting a JCAMP-loaded integration', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [{ xL: 0, xU: 10, area: 300, absoluteArea: 60 }], + refArea: 300, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.ADD_VISUAL_SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 10 }, + splitX: 4, + data, + }, + }); + + const [leftChild, rightChild] = newState.integrations[0].stack; + expect(leftChild.area + rightChild.area).toBeCloseTo(300, 9); + expect(leftChild.absoluteArea + rightChild.absoluteArea).toBeCloseTo(60, 9); + expect(leftChild.area).toBeGreaterThan(0); + expect(rightChild.area).toBeGreaterThan(0); + }); + + it('recomputes each side from raw data on a regular split (independent integrations)', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [{ xL: 0, xU: 10, area: 300, absoluteArea: 60 }], + refArea: 300, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 10 }, + splitX: 4, + data, + }, + }); + + const [leftChild, rightChild] = newState.integrations[0].stack; + expect(leftChild).toMatchObject({ xL: 0, xU: 4, area: 4 }); + expect(rightChild).toMatchObject({ xL: 4, xU: 10, area: 6 }); + expect(leftChild.area + rightChild.area).toBeCloseTo(10, 9); + }); + + it('restores the exact total area when removing a visual split line', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [{ xL: 0, xU: 10, area: 300, absoluteArea: 60 }], + refArea: 300, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const split = integrationReducer(state, { + type: INTEGRATION.ADD_VISUAL_SPLIT, + payload: { + curveIdx: 0, target: { xL: 0, xU: 10 }, splitX: 4, data, + }, + }); + expect(split.integrations[0].stack).toHaveLength(2); + + const merged = integrationReducer(split, { + type: INTEGRATION.RM_VISUAL_SPLIT, + payload: { curveIdx: 0, splitX: 4, data }, + }); + + expect(merged.integrations[0].stack).toHaveLength(1); + expect(merged.integrations[0].stack[0].area).toBeCloseTo(300, 9); + expect(merged.integrations[0].stack[0].absoluteArea).toBeCloseTo(60, 9); + }); + + it('allows visually splitting again after the only split line has been removed', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [ + { xL: 0, xU: 4, area: 4, absoluteArea: 0, visualSplitGroupId: 'g1' }, + { xL: 4, xU: 10, area: 6, absoluteArea: 0, visualSplitGroupId: 'g1' }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const merged = integrationReducer(state, { + type: INTEGRATION.RM_VISUAL_SPLIT, + payload: { curveIdx: 0, splitX: 4, data }, + }); + expect(merged.integrations[0].stack).toHaveLength(1); + expect(merged.integrations[0].stack[0].visualSplitGroupId).toBeUndefined(); + + const reSplit = integrationReducer(merged, { + type: INTEGRATION.ADD_VISUAL_SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 10 }, + splitX: 6, + data, + }, + }); + + expect(reSplit.integrations[0].stack).toHaveLength(2); + expect(reSplit.integrations[0].stack[0]).toMatchObject({ xL: 0, xU: 6 }); + expect(reSplit.integrations[0].stack[1]).toMatchObject({ xL: 6, xU: 10 }); + const newGroupId = reSplit.integrations[0].stack[0].visualSplitGroupId; + expect(typeof newGroupId).toBe('string'); + expect(reSplit.integrations[0].stack[1].visualSplitGroupId).toBe(newGroupId); + }); + + it('removes a visual split line by merging the two adjacent grouped items', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [ + { xL: 0, xU: 4, area: 4, absoluteArea: 0, visualSplitGroupId: 'g1' }, + { xL: 4, xU: 6, area: 2, absoluteArea: 0, visualSplitGroupId: 'g1' }, + { xL: 6, xU: 10, area: 4, absoluteArea: 0, visualSplitGroupId: 'g1' }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.RM_VISUAL_SPLIT, + payload: { curveIdx: 0, splitX: 4, data }, + }); + + expect(newState.integrations[0].stack).toHaveLength(2); + expect(newState.integrations[0].stack[0]).toMatchObject({ xL: 0, xU: 6, visualSplitGroupId: 'g1' }); + expect(newState.integrations[0].stack[1]).toMatchObject({ xL: 6, xU: 10, visualSplitGroupId: 'g1' }); + }); + + it('strips the orphan groupId when one child of a visual split group is removed', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [ + { xL: 0, xU: 4, area: 4, absoluteArea: 0, visualSplitGroupId: 'g1' }, + { xL: 4, xU: 10, area: 6, absoluteArea: 0, visualSplitGroupId: 'g1' }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const afterRemove = integrationReducer(state, { + type: INTEGRATION.RM_ONE, + payload: { curveIdx: 0, dataToRemove: { xL: 0, xU: 4 } }, + }); + + expect(afterRemove.integrations[0].stack).toHaveLength(1); + expect(afterRemove.integrations[0].stack[0]).toMatchObject({ xL: 4, xU: 10 }); + expect(afterRemove.integrations[0].stack[0].visualSplitGroupId).toBeUndefined(); + + const reSplit = integrationReducer(afterRemove, { + type: INTEGRATION.ADD_VISUAL_SPLIT, + payload: { + curveIdx: 0, + target: { xL: 4, xU: 10 }, + splitX: 6, + data, + }, + }); + expect(reSplit.integrations[0].stack).toHaveLength(2); + const reSplitGroupId = reSplit.integrations[0].stack[0].visualSplitGroupId; + expect(typeof reSplitGroupId).toBe('string'); + expect(reSplit.integrations[0].stack[1].visualSplitGroupId).toBe(reSplitGroupId); + }); + + it('extracts the outer part of a left visual split child and keeps the inner part in the group', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [ + { xL: 0, xU: 4, area: 4, absoluteArea: 40, visualSplitGroupId: 'g1' }, + { xL: 4, xU: 10, area: 6, absoluteArea: 60, visualSplitGroupId: 'g1' }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const afterSplit = integrationReducer(state, { + type: INTEGRATION.SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 4 }, + splitX: 2, + data, + }, + }); + + expect(afterSplit.integrations[0].stack).toHaveLength(3); + const [extracted, kept, sibling] = afterSplit.integrations[0].stack; + + expect(extracted).toMatchObject({ xL: 0, xU: 2 }); + expect(extracted.visualSplitGroupId).toBeUndefined(); + + expect(kept).toMatchObject({ xL: 2, xU: 4, visualSplitGroupId: 'g1' }); + expect(sibling).toMatchObject({ xL: 4, xU: 10, visualSplitGroupId: 'g1' }); + + expect(extracted.area + kept.area).toBeCloseTo(4); + expect(extracted.absoluteArea + kept.absoluteArea).toBeCloseTo(40); + }); + + it('extracts the outer part of a right visual split child and keeps the inner part in the group', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [ + { xL: 0, xU: 4, area: 4, absoluteArea: 40, visualSplitGroupId: 'g1' }, + { xL: 4, xU: 10, area: 6, absoluteArea: 60, visualSplitGroupId: 'g1' }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const afterSplit = integrationReducer(state, { + type: INTEGRATION.SPLIT, + payload: { + curveIdx: 0, + target: { xL: 4, xU: 10 }, + splitX: 8, + data, + }, + }); + + expect(afterSplit.integrations[0].stack).toHaveLength(3); + const [sibling, kept, extracted] = afterSplit.integrations[0].stack; + + expect(sibling).toMatchObject({ xL: 0, xU: 4, visualSplitGroupId: 'g1' }); + expect(kept).toMatchObject({ xL: 4, xU: 8, visualSplitGroupId: 'g1' }); + expect(extracted).toMatchObject({ xL: 8, xU: 10 }); + expect(extracted.visualSplitGroupId).toBeUndefined(); + + expect(kept.area + extracted.area).toBeCloseTo(6); + expect(kept.absoluteArea + extracted.absoluteArea).toBeCloseTo(60); + }); + + it('keeps both halves in the group when regular-splitting a middle child', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [ + { xL: 0, xU: 2, area: 2, absoluteArea: 20, visualSplitGroupId: 'g1' }, + { xL: 2, xU: 8, area: 6, absoluteArea: 60, visualSplitGroupId: 'g1' }, + { xL: 8, xU: 10, area: 2, absoluteArea: 20, visualSplitGroupId: 'g1' }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const afterSplit = integrationReducer(state, { + type: INTEGRATION.SPLIT, + payload: { + curveIdx: 0, + target: { xL: 2, xU: 8 }, + splitX: 5, + data, + }, + }); + + expect(afterSplit.integrations[0].stack).toHaveLength(4); + expect(afterSplit.integrations[0].stack.every((it: any) => it.visualSplitGroupId === 'g1')).toBe(true); + }); + + it('drops the groupId when removing the last visual split line of a group', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [ + { xL: 0, xU: 4, area: 4, absoluteArea: 0, visualSplitGroupId: 'g1' }, + { xL: 4, xU: 10, area: 6, absoluteArea: 0, visualSplitGroupId: 'g1' }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.RM_VISUAL_SPLIT, + payload: { curveIdx: 0, splitX: 4, data }, + }); + + expect(newState.integrations[0].stack).toHaveLength(1); + expect(newState.integrations[0].stack[0]).toMatchObject({ xL: 0, xU: 10 }); + expect(newState.integrations[0].stack[0].visualSplitGroupId).toBeUndefined(); + }); + + it('undoes adding a visual split line in one step', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [{ xL: 0, xU: 10, area: 10, absoluteArea: 0 }], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + const splitPresent = integrationReducer(state, { + type: INTEGRATION.ADD_VISUAL_SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 10 }, + splitX: 4, + data, + }, + }); + const splitState: any = newHistory([state], splitPresent, []); + const undoneState = undoableIntegrationReducer(splitState, ActionCreators.undo()); + + expect(splitState.present.integrations[0].stack).toHaveLength(2); + expect(undoneState.present.integrations[0].stack).toEqual(state.integrations[0].stack); + }); + + it('undoes extracting a child from a visual split group in one step', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [ + { xL: 0, xU: 4, area: 4, absoluteArea: 40, visualSplitGroupId: 'g1' }, + { xL: 4, xU: 10, area: 6, absoluteArea: 60, visualSplitGroupId: 'g1' }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + const splitPresent = integrationReducer(state, { + type: INTEGRATION.SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 4 }, + splitX: 2, + data, + }, + }); + const splitState: any = newHistory([state], splitPresent, []); + const undoneState = undoableIntegrationReducer(splitState, ActionCreators.undo()); + + expect(splitState.present.integrations[0].stack).toHaveLength(3); + expect(splitState.present.integrations[0].stack[0].visualSplitGroupId).toBeUndefined(); + expect(splitState.present.integrations[0].stack[1].visualSplitGroupId).toBe('g1'); + expect(splitState.present.integrations[0].stack[2].visualSplitGroupId).toBe('g1'); + + expect(undoneState.present.integrations[0].stack).toEqual(state.integrations[0].stack); + expect(undoneState.present.integrations[0].stack[0].visualSplitGroupId).toBe('g1'); + expect(undoneState.present.integrations[0].stack[1].visualSplitGroupId).toBe('g1'); + + const redoneState = undoableIntegrationReducer(undoneState, ActionCreators.redo()); + expect(redoneState.present.integrations[0].stack).toEqual(splitPresent.integrations[0].stack); + }); + + it('undoes removing a visual split line in one step', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [ + { xL: 0, xU: 4, area: 4, absoluteArea: 0, visualSplitGroupId: 'g1' }, + { xL: 4, xU: 10, area: 6, absoluteArea: 0, visualSplitGroupId: 'g1' }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + const removedPresent = integrationReducer(state, { + type: INTEGRATION.RM_VISUAL_SPLIT, + payload: { curveIdx: 0, splitX: 4, data }, + }); + const removedState: any = newHistory([state], removedPresent, []); + const undoneState = undoableIntegrationReducer(removedState, ActionCreators.undo()); + + expect(removedState.present.integrations[0].stack).toHaveLength(1); + expect(undoneState.present.integrations[0].stack).toEqual(state.integrations[0].stack); + }); + + it('preserves visualSplitGroupId metadata when restoring integrations via RESET_ALL_RDC', () => { + const initial: any = { + selectedIdx: 0, + integrations: [{ + stack: [], + refArea: 1, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + const restored = integrationReducer(initial, { + type: INTEGRATION.RESET_ALL_RDC, + payload: { + selectedIdx: 0, + integrations: [ + { + stack: [ + { xL: 0, xU: 3, area: 3, absoluteArea: 1, visualSplitGroupId: 'g1' }, + { xL: 3, xU: 10, area: 7, absoluteArea: 4, visualSplitGroupId: 'g1' }, + ], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }, + ], + }, + }); + + expect(restored.integrations[0].stack[0]).toMatchObject({ + xL: 0, xU: 3, visualSplitGroupId: 'g1', + }); + expect(restored.integrations[0].stack[1]).toMatchObject({ + xL: 3, xU: 10, visualSplitGroupId: 'g1', + }); + }); + + it('preserves visualSplitGroupId across multi-curve integrations via RESET_ALL_RDC', () => { + const initial: any = { + selectedIdx: 0, + integrations: [ + { stack: [], refArea: 1, refFactor: 1, shift: 0, edited: false }, + { stack: [], refArea: 1, refFactor: 1, shift: 0, edited: false }, + ], + }; + const restored = integrationReducer(initial, { + type: INTEGRATION.RESET_ALL_RDC, + payload: { + selectedIdx: 1, + integrations: [ + { + stack: [{ xL: 0, xU: 4, area: 4, absoluteArea: 2 }], + refArea: 4, + refFactor: 1, + shift: 0, + edited: false, + }, + { + stack: [ + { xL: 5, xU: 7, area: 2, absoluteArea: 1, visualSplitGroupId: 'g2' }, + { xL: 7, xU: 9, area: 2, absoluteArea: 1, visualSplitGroupId: 'g2' }, + ], + refArea: 4, + refFactor: 1, + shift: 0, + edited: false, + }, + ], + }, + }); + + expect(restored.integrations[0].stack[0].visualSplitGroupId).toBeUndefined(); + expect(restored.integrations[1].stack[0].visualSplitGroupId).toBe('g2'); + expect(restored.integrations[1].stack[1].visualSplitGroupId).toBe('g2'); + }); + + it('rejects visual split lines too close to an integration edge', () => { + const state: any = { + selectedIdx: 0, + integrations: [{ + stack: [{ xL: 0, xU: 10, area: 10, absoluteArea: 0 }], + refArea: 10, + refFactor: 1, + shift: 0, + edited: false, + }], + }; + + const newState = integrationReducer(state, { + type: INTEGRATION.ADD_VISUAL_SPLIT, + payload: { + curveIdx: 0, + target: { xL: 0, xU: 10 }, + splitX: 0, + data, + }, + }); + + expect(newState).toBe(state); + }); +}); diff --git a/src/actions/integration.js b/src/actions/integration.js index b594b852..0d25e9e8 100644 --- a/src/actions/integration.js +++ b/src/actions/integration.js @@ -21,8 +21,32 @@ const clearIntegrationAll = (payload) => ( } ); +const splitIntegration = (payload) => ( + { + type: INTEGRATION.SPLIT, + payload, + } +); + +const addVisualSplitLine = (payload) => ( + { + type: INTEGRATION.ADD_VISUAL_SPLIT, + payload, + } +); + +const removeVisualSplitLine = (payload) => ( + { + type: INTEGRATION.RM_VISUAL_SPLIT, + payload, + } +); + export { + addVisualSplitLine, sweepIntegration, setIntegrationFkr, clearIntegrationAll, + removeVisualSplitLine, + splitIntegration, }; // eslint-disable-line diff --git a/src/actions/ui.js b/src/actions/ui.js index 46424f92..4e9a8c51 100644 --- a/src/actions/ui.js +++ b/src/actions/ui.js @@ -1,19 +1,38 @@ import { UI } from '../constants/action_type'; +import { LIST_UI_SWEEP_TYPE } from '../constants/list_ui'; +import { confirmCancelPendingIntegration } from '../helpers/integration_draft.js'; // eslint-disable-line import/extensions -const setUiViewerType = (payload) => ( - { +const keepIntegrationMode = (jcampIdx = 0) => ({ + type: UI.SWEEP.SET_TYPE, + payload: LIST_UI_SWEEP_TYPE.INTEGRATION_ADD, + jcampIdx, +}); + +const setUiViewerType = (payload) => { + if (!confirmCancelPendingIntegration()) { + return keepIntegrationMode(); + } + + return { type: UI.VIEWER.SET_TYPE, payload, + }; +}; + +const setUiSweepType = (payload, jcampIdx = 0) => { + if ( + payload !== LIST_UI_SWEEP_TYPE.INTEGRATION_ADD + && !confirmCancelPendingIntegration() + ) { + return keepIntegrationMode(jcampIdx); } -); -const setUiSweepType = (payload, jcampIdx = 0) => ( - { + return { type: UI.SWEEP.SET_TYPE, payload, jcampIdx, - } -); + }; +}; const selectUiSweep = (payload) => ( { diff --git a/src/components/cmd_bar/04_integration.js b/src/components/cmd_bar/04_integration.js index 77356585..c3388c29 100644 --- a/src/components/cmd_bar/04_integration.js +++ b/src/components/cmd_bar/04_integration.js @@ -12,7 +12,7 @@ import Tooltip from '@mui/material/Tooltip'; import TextField from '@mui/material/TextField'; import Icon from '@mdi/react'; -import { mdiReflectVertical, mdiMathIntegral } from '@mdi/js'; +import { mdiClose, mdiReflectVertical, mdiMathIntegral } from '@mdi/js'; import { clearIntegrationAll, setIntegrationFkr, @@ -21,6 +21,7 @@ import { setUiSweepType } from '../../actions/ui'; import { LIST_UI_SWEEP_TYPE, } from '../../constants/list_ui'; +import { clearPendingIntegrationDraft } from '../../helpers/integration_draft.js'; // eslint-disable-line import/extensions import Cfg from '../../helpers/cfg'; import TriBtn from './tri_btn'; import { MuButton, commonStyle, focusStyle } from './common'; @@ -34,6 +35,13 @@ const styles = () => ( }, txtIcon: { }, + cancelBtn: { + borderColor: '#d32f2f', + color: '#d32f2f', + '&:hover': { + backgroundColor: '#ffebee', + }, + }, }, commonStyle, ) @@ -92,13 +100,36 @@ const iconColor = (criteria) => (criteria ? '#fff' : '#000'); const Integration = ({ classes, ignoreRef, isDisableSt, isFocusAddIntgSt, isFocusRmIntgSt, isFocusSetRefSt, + isFocusSplitIntgSt, isFocusVisualSplitIntgSt, setUiSweepTypeAct, setIntegrationFkrAct, clearIntegrationAllAct, curveSt, integrationSt, }) => { - const onSweepIntegtAdd = () => setUiSweepTypeAct(LIST_UI_SWEEP_TYPE.INTEGRATION_ADD); + const { curveIdx } = curveSt; + const onCancelTool = () => setUiSweepTypeAct(LIST_UI_SWEEP_TYPE.ZOOMIN, curveIdx); + const onSweepIntegtAdd = () => { + if (isFocusAddIntgSt) { + clearPendingIntegrationDraft(); + onCancelTool(); + return; + } + setUiSweepTypeAct(LIST_UI_SWEEP_TYPE.INTEGRATION_ADD, curveIdx); + }; const onSweepIntegtRm = () => setUiSweepTypeAct(LIST_UI_SWEEP_TYPE.INTEGRATION_RM); const onSweepIntegtSR = () => setUiSweepTypeAct(LIST_UI_SWEEP_TYPE.INTEGRATION_SET_REF); - const { curveIdx } = curveSt; + const onSweepIntegtSplit = () => { + if (isFocusSplitIntgSt) { + onCancelTool(); + return; + } + setUiSweepTypeAct(LIST_UI_SWEEP_TYPE.INTEGRATION_SPLIT, curveIdx); + }; + const onSweepIntegtVisualSplit = () => { + if (isFocusVisualSplitIntgSt) { + onCancelTool(); + return; + } + setUiSweepTypeAct(LIST_UI_SWEEP_TYPE.INTEGRATION_VISUAL_SPLIT, curveIdx); + }; const onClearAll = () => clearIntegrationAllAct({ curveIdx }); return ( @@ -108,7 +139,7 @@ const Integration = ({ - + + { + isFocusAddIntgSt + ? null + : + + } @@ -147,24 +182,82 @@ const Integration = ({ - Set Integration Reference}> + { + ignoreRef + ? null + : ( + Set Integration Reference}> + + + + + + + ) + } + Split Integration}> + + + + { + isFocusSplitIntgSt + ? null + : / + } + + + + Visual Split Integration}> + { + isFocusVisualSplitIntgSt + ? null + : | + } @@ -197,6 +290,8 @@ const mapStateToProps = (state, props) => ( // eslint-disable-line isFocusAddIntgSt: state.ui.sweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_ADD, isFocusRmIntgSt: state.ui.sweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_RM, isFocusSetRefSt: state.ui.sweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_SET_REF, + isFocusSplitIntgSt: state.ui.sweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_SPLIT, + isFocusVisualSplitIntgSt: state.ui.sweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_VISUAL_SPLIT, ignoreRef: Format.isHplcUvVisLayout(state.layout), curveSt: state.curve, integrationSt: state.integration.present, @@ -217,6 +312,8 @@ Integration.propTypes = { isFocusAddIntgSt: PropTypes.bool.isRequired, isFocusRmIntgSt: PropTypes.bool.isRequired, isFocusSetRefSt: PropTypes.bool.isRequired, + isFocusSplitIntgSt: PropTypes.bool.isRequired, + isFocusVisualSplitIntgSt: PropTypes.bool.isRequired, ignoreRef: PropTypes.bool.isRequired, setUiSweepTypeAct: PropTypes.func.isRequired, setIntegrationFkrAct: PropTypes.func.isRequired, diff --git a/src/components/d3_line/index.js b/src/components/d3_line/index.js index d42c2796..ebe1a4c7 100644 --- a/src/components/d3_line/index.js +++ b/src/components/d3_line/index.js @@ -10,6 +10,9 @@ import { } from '../../helpers/chem'; import { resetAll } from '../../actions/manager'; import { selectUiSweep, scrollUiWheel, clickUiTarget } from '../../actions/ui'; +import { + addVisualSplitLine, removeVisualSplitLine, splitIntegration, +} from '../../actions/integration'; import LineFocus from './line_focus'; import { drawMain, drawLabel, drawDisplay, drawDestroy, @@ -24,23 +27,35 @@ class ViewerLine extends React.Component { constructor(props) { super(props); - const { clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct } = props; + const { + clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct, splitIntegrationAct, + addVisualSplitLineAct, removeVisualSplitLineAct, + } = props; this.rootKlass = '.d3Line'; this.focus = new LineFocus({ - W, H, clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct, + W, + H, + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct, + splitIntegrationAct, + addVisualSplitLineAct, + removeVisualSplitLineAct, }); this.normChange = this.normChange.bind(this); + this.syncFocusActions = this.syncFocusActions.bind(this); } componentDidMount() { const { seed, peak, cLabel, xLabel, yLabel, feature, freq, comparisons, tTrEndPts, tSfPeaks, editPeakSt, layoutSt, integationSt, mtplySt, - sweepExtentSt, isUiAddIntgSt, isUiNoBrushSt, + sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, isHidden, wavelength, axesUnitsSt, resetAllAct, } = this.props; + this.syncFocusActions(); drawDestroy(this.rootKlass); resetAllAct(feature); @@ -71,6 +86,8 @@ class ViewerLine extends React.Component { mtplySt, sweepExtentSt, isUiAddIntgSt, + isUiSplitIntgSt, + isUiVisualSplitIntgSt, isUiNoBrushSt, wavelength, }); @@ -82,9 +99,10 @@ class ViewerLine extends React.Component { const { seed, peak, cLabel, xLabel, yLabel, freq, comparisons, tTrEndPts, tSfPeaks, editPeakSt, layoutSt, integationSt, mtplySt, - sweepExtentSt, isUiAddIntgSt, isUiNoBrushSt, + sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, isHidden, wavelength, axesUnitsSt, } = this.props; + this.syncFocusActions(); this.normChange(prevProps); let xxLabel = xLabel; @@ -113,6 +131,8 @@ class ViewerLine extends React.Component { mtplySt, sweepExtentSt, isUiAddIntgSt, + isUiSplitIntgSt, + isUiVisualSplitIntgSt, isUiNoBrushSt, wavelength, }); @@ -124,6 +144,22 @@ class ViewerLine extends React.Component { drawDestroy(this.rootKlass); } + syncFocusActions() { + if (!this.focus) return; + const { + clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct, + splitIntegrationAct, addVisualSplitLineAct, removeVisualSplitLineAct, + } = this.props; + Object.assign(this.focus, { + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct, + splitIntegrationAct, + addVisualSplitLineAct, + removeVisualSplitLineAct, + }); + } + normChange(prevProps) { const { feature, resetAllAct } = this.props; const oldFeature = prevProps.feature; @@ -153,6 +189,8 @@ const mapStateToProps = (state, props) => ( mtplySt: state.multiplicity.present, sweepExtentSt: state.ui.sweepExtent, isUiAddIntgSt: state.ui.sweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_ADD, + isUiSplitIntgSt: state.ui.sweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_SPLIT, + isUiVisualSplitIntgSt: state.ui.sweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_VISUAL_SPLIT, isUiNoBrushSt: LIST_NON_BRUSH_TYPES.indexOf(state.ui.sweepType) < 0, wavelength: state.wavelength, axesUnitsSt: state.axesUnits, @@ -165,6 +203,9 @@ const mapDispatchToProps = (dispatch) => ( clickUiTargetAct: clickUiTarget, selectUiSweepAct: selectUiSweep, scrollUiWheelAct: scrollUiWheel, + splitIntegrationAct: splitIntegration, + addVisualSplitLineAct: addVisualSplitLine, + removeVisualSplitLineAct: removeVisualSplitLine, addNewCylicVoltaPairPeakAct: addNewCylicVoltaPairPeak, addCylicVoltaMaxPeakAct: addCylicVoltaMaxPeak, addCylicVoltaMinPeakAct: addCylicVoltaMinPeak, @@ -191,11 +232,16 @@ ViewerLine.propTypes = { mtplySt: PropTypes.object.isRequired, sweepExtentSt: PropTypes.object.isRequired, isUiAddIntgSt: PropTypes.bool.isRequired, + isUiSplitIntgSt: PropTypes.bool.isRequired, + isUiVisualSplitIntgSt: PropTypes.bool.isRequired, isUiNoBrushSt: PropTypes.bool.isRequired, resetAllAct: PropTypes.func.isRequired, clickUiTargetAct: PropTypes.func.isRequired, selectUiSweepAct: PropTypes.func.isRequired, scrollUiWheelAct: PropTypes.func.isRequired, + splitIntegrationAct: PropTypes.func.isRequired, + addVisualSplitLineAct: PropTypes.func.isRequired, + removeVisualSplitLineAct: PropTypes.func.isRequired, isHidden: PropTypes.bool.isRequired, wavelength: PropTypes.object.isRequired, axesUnitsSt: PropTypes.object.isRequired, diff --git a/src/components/d3_line/line_focus.js b/src/components/d3_line/line_focus.js index b308e9d3..ca37975b 100644 --- a/src/components/d3_line/line_focus.js +++ b/src/components/d3_line/line_focus.js @@ -8,14 +8,24 @@ import { } from '../../helpers/mount'; import MountBrush from '../../helpers/brush'; import { TfRescale, MountCompass } from '../../helpers/compass'; +import { + clearIntegrationSplitPreview, + drawIntegrationSplitPreview, + drawIntegrationVisualSplitLines, + getSplitXFromEvent, + getVisualSplitLineAtX, + isAlreadyVisuallySplit, + isMergedVisualSplitGroup, +} from '../../helpers/integration_split'; import { PksEdit } from '../../helpers/converter'; import { itgIdTag, mpyIdTag } from '../../helpers/focus'; -import { calcArea } from '../../helpers/integration'; +import { + calcArea, getIntegrationPoints, getLinearBaseline, getVisualSplitGroups, +} from '../../helpers/integration'; import { calcMpyCenter } from '../../helpers/multiplicity_calc'; import Format from '../../helpers/format'; import Cfg from '../../helpers/cfg'; import { LIST_LAYOUT } from '../../constants/list_layout'; -import { calcSlope } from '../../helpers/calc'; const d3 = require('d3'); @@ -23,6 +33,7 @@ class LineFocus { constructor(props) { const { W, H, clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct, + splitIntegrationAct, addVisualSplitLineAct, removeVisualSplitLineAct, } = props; this.jcampIdx = 0; @@ -38,6 +49,9 @@ class LineFocus { this.clickUiTargetAct = clickUiTargetAct; this.selectUiSweepAct = selectUiSweepAct; this.scrollUiWheelAct = scrollUiWheelAct; + this.splitIntegrationAct = splitIntegrationAct; + this.addVisualSplitLineAct = addVisualSplitLineAct; + this.removeVisualSplitLineAct = removeVisualSplitLineAct; this.brush = d3.brush(); this.brushX = d3.brushX(); @@ -63,6 +77,11 @@ class LineFocus { this.shouldUpdate = {}; this.freq = false; this.layout = LIST_LAYOUT.H1; + this.isUiAddIntgSt = false; + this.isUiSplitIntgSt = false; + this.isUiVisualSplitIntgSt = false; + this.integrationSplitTargets = null; + this.firstIntegrationPoint = null; this.getShouldUpdate = this.getShouldUpdate.bind(this); this.resetShouldUpdate = this.resetShouldUpdate.bind(this); @@ -81,6 +100,11 @@ class LineFocus { this.drawMtply = this.drawMtply.bind(this); this.drawComparisons = this.drawComparisons.bind(this); this.onClickTarget = this.onClickTarget.bind(this); + this.onClickIntegrationTarget = this.onClickIntegrationTarget.bind(this); + this.onClickVisualSplitLine = this.onClickVisualSplitLine.bind(this); + this.onIntegrationMouseMove = this.onIntegrationMouseMove.bind(this); + this.clearSplitPreview = this.clearSplitPreview.bind(this); + this.drawVisualSplitLines = this.drawVisualSplitLines.bind(this); this.mergedPeaks = this.mergedPeaks.bind(this); this.isFirefox = typeof InstallTrigger !== 'undefined'; @@ -233,40 +257,119 @@ class LineFocus { this.clickUiTargetAct(data, onPeak); } + clearSplitPreview() { + clearIntegrationSplitPreview(this); + } + + onIntegrationMouseMove(event, data, shift, ignoreRef) { + if (!this.isUiSplitIntgSt && !this.isUiVisualSplitIntgSt) return; + if (this.isUiVisualSplitIntgSt && isAlreadyVisuallySplit(data)) { + this.clearSplitPreview(); + return; + } + if (this.isUiSplitIntgSt && isMergedVisualSplitGroup(data)) { + this.clearSplitPreview(); + return; + } + const splitX = getSplitXFromEvent(event, this); + drawIntegrationSplitPreview(this, data, splitX, shift, ignoreRef); + } + + onClickIntegrationTarget(event, data) { + if (!this.isUiSplitIntgSt && !this.isUiVisualSplitIntgSt) { + this.onClickTarget(event, data); + return; + } + + event.stopPropagation(); + event.preventDefault(); + const splitX = getSplitXFromEvent(event, this); + this.clearSplitPreview(); + if (this.isUiVisualSplitIntgSt) { + const { stack = [], shift = 0 } = this.integrationSplitTargets || {}; + const existingSplitX = getVisualSplitLineAtX(this, stack, splitX, shift); + if (Number.isFinite(existingSplitX)) { + if (typeof this.removeVisualSplitLineAct !== 'function') return; + this.removeVisualSplitLineAct({ + curveIdx: this.jcampIdx, + splitX: existingSplitX, + data: this.data, + }); + return; + } + if (isAlreadyVisuallySplit(data)) return; + if (typeof this.addVisualSplitLineAct !== 'function') return; + this.addVisualSplitLineAct({ + curveIdx: this.jcampIdx, + target: data, + splitX, + data: this.data, + }); + return; + } + + if (isMergedVisualSplitGroup(data)) return; + this.splitIntegrationAct({ + curveIdx: this.jcampIdx, + target: data, + splitX, + data: this.data, + }); + } + + onClickVisualSplitLine(event, splitX) { + event.stopPropagation(); + event.preventDefault(); + this.clearSplitPreview(); + if (typeof this.removeVisualSplitLineAct !== 'function') return; + this.removeVisualSplitLineAct({ + curveIdx: this.jcampIdx, + splitX, + data: this.data, + }); + } + + drawVisualSplitLines(stack, shift, ignoreRef) { + drawIntegrationVisualSplitLines( + this, + stack, + shift, + ignoreRef, + this.isUiVisualSplitIntgSt, + this.onClickVisualSplitLine, + ); + } + mergedPeaks(editPeakSt) { if (!editPeakSt) return this.dataPks; this.dataPks = PksEdit(this.dataPks, editPeakSt); return this.dataPks; } - drawAUC(stack) { + drawAUC(stack, shift = 0) { const { xt, yt } = TfRescale(this); - const auc = this.tags.aucPath.selectAll('path').data(stack); + const groups = getVisualSplitGroups(stack).map((group) => ({ + xL: group.xL, + xU: group.xU, + isMerged: group.isMerged, + groupId: group.groupId, + target: group.items[0], + })); + const auc = this.tags.aucPath.selectAll('path').data(groups); auc.exit() .attr('class', 'exit') .remove(); const integCurve = (border) => { const { xL, xU } = border; - const ps = this.data.filter((d) => d.x > xL && d.x < xU); + const ps = getIntegrationPoints(xL - shift, xU - shift, this.data); if (!ps[0]) return null; - const point1 = ps[0]; - const point2 = ps[ps.length - 1]; - const slope = calcSlope(point1.x, point1.y, point2.x, point2.y); - let lastDY = point1.y; + const baselineY = getLinearBaseline(ps); return d3.area() .x((d) => xt(d.x)) - .y0((d, index) => { - if (index > 0) { - const lastD = ps[index - 1]; - const y = slope * (d.x - lastD.x) + lastDY; - lastDY = y; - return yt(y); - } - return yt(0); - }) + .y0((d) => yt(baselineY(d))) .y1((d) => yt(d.y))(ps); }; @@ -295,8 +398,10 @@ class LineFocus { .style('fill', 'red'); d3.select(`#auc${itgIdTag(d)}`) .style('fill-opacity', 0.2); + this.clearSplitPreview(); }) - .on('click', (event, d) => this.onClickTarget(event, d)); + .on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, true)) + .on('click', (event, d) => this.onClickIntegrationTarget(event, d)); } drawPeaks(editPeakSt) { @@ -394,12 +499,23 @@ class LineFocus { const isDisable = Cfg.btnCmdIntg(this.layout); const ignoreRef = Format.isHplcUvVisLayout(this.layout); const itgs = isDisable ? [] : stack; + Object.assign(this, { + integrationSplitTargets: { stack: itgs, shift, ignoreRef }, + }); - const igbp = this.tags.igbPath.selectAll('path').data(itgs); + const igGroups = getVisualSplitGroups(itgs).map((group) => ({ + xL: group.xL, + xU: group.xU, + isMerged: group.isMerged, + groupId: group.groupId, + target: group.items[0], + })); + + const igbp = this.tags.igbPath.selectAll('path').data(igGroups); igbp.exit() .attr('class', 'exit') .remove(); - const igcp = this.tags.igcPath.selectAll('path').data(itgs); + const igcp = this.tags.igcPath.selectAll('path').data(igGroups); igcp.exit() .attr('class', 'exit') .remove(); @@ -416,11 +532,12 @@ class LineFocus { .attr('class', 'exit') .remove(); auc.merge(auc); + this.drawVisualSplitLines([], shift, ignoreRef); return; } if (ignoreRef) { - this.drawAUC(stack); + this.drawAUC(stack, shift); } else { // rescale for zoom const { xt } = TfRescale(this); @@ -461,8 +578,10 @@ class LineFocus { .attr('stroke', '#228B22'); d3.select(`#igtp${itgIdTag(d)}`) .style('fill', '#228B22'); + this.clearSplitPreview(); }) - .on('click', (event, d) => this.onClickTarget(event, d)); + .on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, ignoreRef)) + .on('click', (event, d) => this.onClickIntegrationTarget(event, d)); const integCurve = (border) => { const { xL, xU } = border; @@ -505,8 +624,10 @@ class LineFocus { .attr('stroke', '#228B22'); d3.select(`#igtp${itgIdTag(d)}`) .style('fill', '#228B22'); + this.clearSplitPreview(); }) - .on('click', (event, d) => this.onClickTarget(event, d)); + .on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, ignoreRef)) + .on('click', (event, d) => this.onClickIntegrationTarget(event, d)); igtp.enter() .append('text') @@ -534,9 +655,12 @@ class LineFocus { .attr('stroke', '#228B22'); d3.select(`#igtp${itgIdTag(d)}`) .style('fill', '#228B22'); + this.clearSplitPreview(); }) - .on('click', (event, d) => this.onClickTarget(event, d)); + .on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, ignoreRef)) + .on('click', (event, d) => this.onClickIntegrationTarget(event, d)); } + this.drawVisualSplitLines(itgs, shift, ignoreRef); } drawMtply(mtplySt) { @@ -793,7 +917,7 @@ class LineFocus { create({ filterSeed, filterPeak, tTrEndPts, tSfPeaks, freq, comparisons, editPeakSt, layoutSt, integationSt, mtplySt, - sweepExtentSt, isUiAddIntgSt, isUiNoBrushSt, + sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, wavelength, }) { this.svg = d3.select('.d3Svg'); @@ -804,6 +928,8 @@ class LineFocus { this.scales = InitScale(this, this.reverseXAxis(layoutSt)); this.setTip(); this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, freq, layoutSt, wavelength); + Object.assign(this, { isUiSplitIntgSt, isUiVisualSplitIntgSt }); + if (!isUiSplitIntgSt && !isUiVisualSplitIntgSt) this.clearSplitPreview(); MountCompass(this); this.axis = MountAxis(this); @@ -833,12 +959,14 @@ class LineFocus { update({ filterSeed, filterPeak, tTrEndPts, tSfPeaks, freq, comparisons, editPeakSt, layoutSt, integationSt, mtplySt, - sweepExtentSt, isUiAddIntgSt, isUiNoBrushSt, + sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, wavelength, }) { this.root = d3.select(this.rootKlass).selectAll('.focus-main'); this.scales = InitScale(this, this.reverseXAxis(layoutSt)); this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, freq, layoutSt, wavelength); + Object.assign(this, { isUiSplitIntgSt, isUiVisualSplitIntgSt }); + if (!isUiSplitIntgSt && !isUiVisualSplitIntgSt) this.clearSplitPreview(); if (this.data && this.data.length > 0) { this.setConfig(sweepExtentSt); diff --git a/src/components/d3_multi/index.js b/src/components/d3_multi/index.js index d95c8928..573ab341 100644 --- a/src/components/d3_multi/index.js +++ b/src/components/d3_multi/index.js @@ -12,7 +12,10 @@ import { import Format from '../../helpers/format'; import { resetAll } from '../../actions/manager'; import { selectUiSweep, scrollUiWheel, clickUiTarget } from '../../actions/ui'; -import { LIST_NON_BRUSH_TYPES } from '../../constants/list_ui'; +import { + addVisualSplitLine, removeVisualSplitLine, splitIntegration, +} from '../../actions/integration'; +import { LIST_UI_SWEEP_TYPE, LIST_NON_BRUSH_TYPES } from '../../constants/list_ui'; import { addNewCylicVoltaPairPeak, addCylicVoltaMaxPeak, addCylicVoltaMinPeak } from '../../actions/cyclic_voltammetry'; import MultiFocus from './multi_focus'; @@ -29,7 +32,8 @@ class ViewerMulti extends React.Component { super(props); const { - entities, clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct, + entities, clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct, splitIntegrationAct, + addVisualSplitLineAct, removeVisualSplitLineAct, } = this.props; this.rootKlass = '.d3Line'; this.containerRef = React.createRef(); @@ -37,21 +41,31 @@ class ViewerMulti extends React.Component { this.resizeObserver = null; this.focus = new MultiFocus({ - W, H, entities, clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct, + W, + H, + entities, + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct, + splitIntegrationAct, + addVisualSplitLineAct, + removeVisualSplitLineAct, }); this.normChange = this.normChange.bind(this); this.handleResize = this.handleResize.bind(this); + this.syncFocusActions = this.syncFocusActions.bind(this); } componentDidMount() { + this.syncFocusActions(); this.renderChart(this.props, true); this.setupResizeObserver(); const { curveSt, seed, peak, cLabel, xLabel, yLabel, feature, tTrEndPts, tSfPeaks, editPeakSt, layoutSt, - sweepExtentSt, isUiNoBrushSt, + sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, isHidden, resetAllAct, cyclicvoltaSt, integationSt, mtplySt, axesUnitsSt, } = this.props; @@ -93,6 +107,9 @@ class ViewerMulti extends React.Component { editPeakSt, layoutSt, sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiVisualSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, integationSt, @@ -108,10 +125,11 @@ class ViewerMulti extends React.Component { entities, curveSt, seed, peak, cLabel, xLabel, yLabel, tTrEndPts, tSfPeaks, editPeakSt, layoutSt, - sweepExtentSt, isUiNoBrushSt, + sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, isHidden, cyclicvoltaSt, integationSt, mtplySt, axesUnitsSt, } = this.props; + this.syncFocusActions(); this.normChange(prevProps); let xxLabel = xLabel; @@ -151,6 +169,9 @@ class ViewerMulti extends React.Component { editPeakSt, layoutSt, sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, + isUiVisualSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, integationSt, @@ -201,6 +222,22 @@ class ViewerMulti extends React.Component { this.resizeObserver.observe(this.containerRef.current); } + syncFocusActions() { + if (!this.focus) return; + const { + clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct, + splitIntegrationAct, addVisualSplitLineAct, removeVisualSplitLineAct, + } = this.props; + Object.assign(this.focus, { + clickUiTargetAct, + selectUiSweepAct, + scrollUiWheelAct, + splitIntegrationAct, + addVisualSplitLineAct, + removeVisualSplitLineAct, + }); + } + teardownResizeObserver() { if (this.resizeObserver) { this.resizeObserver.disconnect(); @@ -221,10 +258,10 @@ class ViewerMulti extends React.Component { curveSt, seed, peak, cLabel, xLabel, yLabel, feature, tTrEndPts, tSfPeaks, editPeakSt, layoutSt, - sweepExtentSt, isUiNoBrushSt, + sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiNoBrushSt, isHidden, resetAllAct, cyclicvoltaSt, integationSt, mtplySt, axesUnitsSt, - entities, clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct, + entities, clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct, splitIntegrationAct, } = props; const size = this.getTargetSize(layoutSt); @@ -260,6 +297,7 @@ class ViewerMulti extends React.Component { clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct, + splitIntegrationAct, }); drawMain(this.rootKlass, size.width, size.height); @@ -272,6 +310,8 @@ class ViewerMulti extends React.Component { editPeakSt, layoutSt, sweepExtentSt, + isUiAddIntgSt, + isUiSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, integationSt, @@ -305,6 +345,9 @@ const mapStateToProps = (state, props) => ( editPeakSt: state.editPeak.present, layoutSt: state.layout, sweepExtentSt: state.ui.sweepExtent, + isUiAddIntgSt: state.ui.sweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_ADD, + isUiSplitIntgSt: state.ui.sweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_SPLIT, + isUiVisualSplitIntgSt: state.ui.sweepType === LIST_UI_SWEEP_TYPE.INTEGRATION_VISUAL_SPLIT, isUiNoBrushSt: LIST_NON_BRUSH_TYPES.indexOf(state.ui.sweepType) < 0, cyclicvoltaSt: state.cyclicvolta, maxminPeakSt: Feature2MaxMinPeak(state, props), @@ -320,6 +363,9 @@ const mapDispatchToProps = (dispatch) => ( clickUiTargetAct: clickUiTarget, selectUiSweepAct: selectUiSweep, scrollUiWheelAct: scrollUiWheel, + splitIntegrationAct: splitIntegration, + addVisualSplitLineAct: addVisualSplitLine, + removeVisualSplitLineAct: removeVisualSplitLine, addNewCylicVoltaPairPeakAct: addNewCylicVoltaPairPeak, addCylicVoltaMaxPeakAct: addCylicVoltaMaxPeak, addCylicVoltaMinPeakAct: addCylicVoltaMinPeak, @@ -341,11 +387,17 @@ ViewerMulti.propTypes = { integationSt: PropTypes.object.isRequired, mtplySt: PropTypes.object.isRequired, sweepExtentSt: PropTypes.object.isRequired, + isUiAddIntgSt: PropTypes.bool.isRequired, + isUiSplitIntgSt: PropTypes.bool.isRequired, + isUiVisualSplitIntgSt: PropTypes.bool.isRequired, isUiNoBrushSt: PropTypes.bool.isRequired, resetAllAct: PropTypes.func.isRequired, clickUiTargetAct: PropTypes.func.isRequired, selectUiSweepAct: PropTypes.func.isRequired, scrollUiWheelAct: PropTypes.func.isRequired, + splitIntegrationAct: PropTypes.func.isRequired, + addVisualSplitLineAct: PropTypes.func.isRequired, + removeVisualSplitLineAct: PropTypes.func.isRequired, isHidden: PropTypes.bool, cyclicvoltaSt: PropTypes.object.isRequired, maxminPeakSt: PropTypes.object, diff --git a/src/components/d3_multi/multi_focus.js b/src/components/d3_multi/multi_focus.js index 22a76a34..e44423f5 100644 --- a/src/components/d3_multi/multi_focus.js +++ b/src/components/d3_multi/multi_focus.js @@ -11,6 +11,15 @@ import { import { PksEdit, PeckersEdit } from '../../helpers/converter'; import MountBrush from '../../helpers/brush'; import { TfRescale, MountCompass } from '../../helpers/compass'; +import { + clearIntegrationSplitPreview, + drawIntegrationSplitPreview, + drawIntegrationVisualSplitLines, + getSplitXFromEvent, + getVisualSplitLineAtX, + isAlreadyVisuallySplit, + isMergedVisualSplitGroup, +} from '../../helpers/integration_split'; import { LIST_LAYOUT } from '../../constants/list_layout'; import Format from '../../helpers/format'; import { @@ -18,9 +27,10 @@ import { } from '../../helpers/chem'; import Cfg from '../../helpers/cfg'; import { itgIdTag, mpyIdTag } from '../../helpers/focus'; -import { calcArea } from '../../helpers/integration'; +import { + calcArea, getIntegrationPoints, getLinearBaseline, getVisualSplitGroups, +} from '../../helpers/integration'; import { calcMpyCenter } from '../../helpers/multiplicity_calc'; -import { calcSlope } from '../../helpers/calc'; const d3 = require('d3'); @@ -28,6 +38,7 @@ class MultiFocus { constructor(props) { const { W, H, clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct, entities, + splitIntegrationAct, addVisualSplitLineAct, removeVisualSplitLineAct, } = props; this.entities = entities; @@ -45,6 +56,9 @@ class MultiFocus { this.clickUiTargetAct = clickUiTargetAct; this.selectUiSweepAct = selectUiSweepAct; this.scrollUiWheelAct = scrollUiWheelAct; + this.splitIntegrationAct = splitIntegrationAct; + this.addVisualSplitLineAct = addVisualSplitLineAct; + this.removeVisualSplitLineAct = removeVisualSplitLineAct; this.brush = d3.brush(); this.brushX = d3.brushX(); @@ -72,6 +86,11 @@ class MultiFocus { this.shouldUpdate = {}; // this.freq = false; this.layout = LIST_LAYOUT.CYCLIC_VOLTAMMETRY; + this.isUiAddIntgSt = false; + this.isUiSplitIntgSt = false; + this.isUiVisualSplitIntgSt = false; + this.integrationSplitTargets = null; + this.firstIntegrationPoint = null; this.getShouldUpdate = this.getShouldUpdate.bind(this); this.resetShouldUpdate = this.resetShouldUpdate.bind(this); @@ -90,6 +109,11 @@ class MultiFocus { this.drawMtply = this.drawMtply.bind(this); this.drawAUC = this.drawAUC.bind(this); this.onClickTarget = this.onClickTarget.bind(this); + this.onClickIntegrationTarget = this.onClickIntegrationTarget.bind(this); + this.onClickVisualSplitLine = this.onClickVisualSplitLine.bind(this); + this.onIntegrationMouseMove = this.onIntegrationMouseMove.bind(this); + this.clearSplitPreview = this.clearSplitPreview.bind(this); + this.drawVisualSplitLines = this.drawVisualSplitLines.bind(this); this.mergedPeaks = this.mergedPeaks.bind(this); this.setDataPecker = this.setDataPecker.bind(this); this.drawPeckers = this.drawPeckers.bind(this); @@ -338,6 +362,89 @@ class MultiFocus { } } + clearSplitPreview() { + clearIntegrationSplitPreview(this); + } + + onIntegrationMouseMove(event, data, shift, ignoreRef) { + if (!this.isUiSplitIntgSt && !this.isUiVisualSplitIntgSt) return; + if (this.isUiVisualSplitIntgSt && isAlreadyVisuallySplit(data)) { + this.clearSplitPreview(); + return; + } + if (this.isUiSplitIntgSt && isMergedVisualSplitGroup(data)) { + this.clearSplitPreview(); + return; + } + const splitX = getSplitXFromEvent(event, this); + drawIntegrationSplitPreview(this, data, splitX, shift, ignoreRef); + } + + onClickIntegrationTarget(event, data) { + if (!this.isUiSplitIntgSt && !this.isUiVisualSplitIntgSt) { + this.onClickTarget(event, data); + return; + } + + event.stopPropagation(); + event.preventDefault(); + const splitX = getSplitXFromEvent(event, this); + this.clearSplitPreview(); + if (this.isUiVisualSplitIntgSt) { + const { stack = [], shift = 0 } = this.integrationSplitTargets || {}; + const existingSplitX = getVisualSplitLineAtX(this, stack, splitX, shift); + if (Number.isFinite(existingSplitX)) { + if (typeof this.removeVisualSplitLineAct !== 'function') return; + this.removeVisualSplitLineAct({ + curveIdx: this.jcampIdx, + splitX: existingSplitX, + data: this.data, + }); + return; + } + if (isAlreadyVisuallySplit(data)) return; + if (typeof this.addVisualSplitLineAct !== 'function') return; + this.addVisualSplitLineAct({ + curveIdx: this.jcampIdx, + target: data, + splitX, + data: this.data, + }); + return; + } + + if (isMergedVisualSplitGroup(data)) return; + this.splitIntegrationAct({ + curveIdx: this.jcampIdx, + target: data, + splitX, + data: this.data, + }); + } + + onClickVisualSplitLine(event, splitX) { + event.stopPropagation(); + event.preventDefault(); + this.clearSplitPreview(); + if (typeof this.removeVisualSplitLineAct !== 'function') return; + this.removeVisualSplitLineAct({ + curveIdx: this.jcampIdx, + splitX, + data: this.data, + }); + } + + drawVisualSplitLines(stack, shift, ignoreRef) { + drawIntegrationVisualSplitLines( + this, + stack, + shift, + ignoreRef, + this.isUiVisualSplitIntgSt, + this.onClickVisualSplitLine, + ); + } + onClickPecker(event, data) { event.stopPropagation(); event.preventDefault(); @@ -371,34 +478,30 @@ class MultiFocus { return this.dataPeckers; } - drawAUC(stack) { + drawAUC(stack, shift = 0) { const { xt, yt } = TfRescale(this); - const auc = this.tags.aucPath.selectAll('path').data(stack); + const groups = getVisualSplitGroups(stack).map((group) => ({ + xL: group.xL, + xU: group.xU, + isMerged: group.isMerged, + groupId: group.groupId, + target: group.items[0], + })); + const auc = this.tags.aucPath.selectAll('path').data(groups); auc.exit() .attr('class', 'exit') .remove(); const integCurve = (border) => { const { xL, xU } = border; - const ps = this.data.filter((d) => d.x > xL && d.x < xU); + const ps = getIntegrationPoints(xL - shift, xU - shift, this.data); if (!ps[0]) return null; - const point1 = ps[0]; - const point2 = ps[ps.length - 1]; - const slope = calcSlope(point1.x, point1.y, point2.x, point2.y); - let lastDY = point1.y; + const baselineY = getLinearBaseline(ps); return d3.area() .x((d) => xt(d.x)) - .y0((d, index) => { - if (index > 0) { - const lastD = ps[index - 1]; - const y = slope * (d.x - lastD.x) + lastDY; - lastDY = y; - return yt(y); - } - return yt(0); - }) + .y0((d) => yt(baselineY(d))) .y1((d) => yt(d.y))(ps); }; @@ -427,8 +530,10 @@ class MultiFocus { .style('fill', 'red'); d3.select(`#auc${itgIdTag(d)}`) .style('fill-opacity', 0.2); + this.clearSplitPreview(); }) - .on('click', (event, d) => this.onClickTarget(event, d)); + .on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, true)) + .on('click', (event, d) => this.onClickIntegrationTarget(event, d)); } drawPeaks(editPeakSt) { @@ -612,6 +717,9 @@ class MultiFocus { const { integrations } = integationSt; 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() @@ -626,6 +734,7 @@ class MultiFocus { igtp.exit() .attr('class', 'exit') .remove(); + this.drawVisualSplitLines([], 0, false); return; } @@ -636,12 +745,23 @@ class MultiFocus { const isDisable = Cfg.btnCmdIntg(this.layout); const ignoreRef = Format.isHplcUvVisLayout(this.layout); const itgs = isDisable ? [] : stack; + Object.assign(this, { + integrationSplitTargets: { stack: itgs, shift, ignoreRef }, + }); + + const igGroups = getVisualSplitGroups(itgs).map((group) => ({ + xL: group.xL, + xU: group.xU, + isMerged: group.isMerged, + groupId: group.groupId, + target: group.items[0], + })); - const igbp = this.tags.igbPath.selectAll('path').data(itgs); + const igbp = this.tags.igbPath.selectAll('path').data(igGroups); igbp.exit() .attr('class', 'exit') .remove(); - const igcp = this.tags.igcPath.selectAll('path').data(itgs); + const igcp = this.tags.igcPath.selectAll('path').data(igGroups); igcp.exit() .attr('class', 'exit') .remove(); @@ -658,11 +778,12 @@ class MultiFocus { .attr('class', 'exit') .remove(); auc.merge(auc); + this.drawVisualSplitLines([], shift, ignoreRef); return; } if (ignoreRef) { - this.drawAUC(stack); + this.drawAUC(stack, shift); } else { // rescale for zoom const { xt } = TfRescale(this); @@ -703,8 +824,10 @@ class MultiFocus { .attr('stroke', '#228B22'); d3.select(`#igtp${itgIdTag(d)}`) .style('fill', '#228B22'); + this.clearSplitPreview(); }) - .on('click', (event, d) => this.onClickTarget(event, d)); + .on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, ignoreRef)) + .on('click', (event, d) => this.onClickIntegrationTarget(event, d)); const integCurve = (border) => { const { xL, xU } = border; @@ -747,8 +870,10 @@ class MultiFocus { .attr('stroke', '#228B22'); d3.select(`#igtp${itgIdTag(d)}`) .style('fill', '#228B22'); + this.clearSplitPreview(); }) - .on('click', (event, d) => this.onClickTarget(event, d)); + .on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, ignoreRef)) + .on('click', (event, d) => this.onClickIntegrationTarget(event, d)); igtp.enter() .append('text') @@ -776,9 +901,12 @@ class MultiFocus { .attr('stroke', '#228B22'); d3.select(`#igtp${itgIdTag(d)}`) .style('fill', '#228B22'); + this.clearSplitPreview(); }) - .on('click', (event, d) => this.onClickTarget(event, d)); + .on('mousemove', (event, d) => this.onIntegrationMouseMove(event, d, shift, ignoreRef)) + .on('click', (event, d) => this.onClickIntegrationTarget(event, d)); } + this.drawVisualSplitLines(itgs, shift, ignoreRef); } drawMtply(mtplySt) { @@ -1050,7 +1178,7 @@ class MultiFocus { curveSt, filterSeed, filterPeak, tTrEndPts, tSfPeaks, editPeakSt, layoutSt, - sweepExtentSt, isUiNoBrushSt, + sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, integationSt, mtplySt, }) { @@ -1066,6 +1194,8 @@ class MultiFocus { this.scales = InitScale(this, this.reverseXAxis(layoutSt)); this.setTip(); this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, layoutSt, cyclicvoltaSt, jcampIdx); + Object.assign(this, { isUiSplitIntgSt, isUiVisualSplitIntgSt }); + if (!isUiSplitIntgSt && !isUiVisualSplitIntgSt) this.clearSplitPreview(); MountCompass(this); this.axis = MountAxis(this); @@ -1089,7 +1219,7 @@ class MultiFocus { this.drawInteg(integationSt); this.drawMtply(mtplySt); } - MountBrush(this, false, isUiNoBrushSt); + MountBrush(this, isUiAddIntgSt, isUiNoBrushSt); this.resetShouldUpdate(editPeakSt); } @@ -1097,7 +1227,7 @@ class MultiFocus { entities, curveSt, filterSeed, filterPeak, tTrEndPts, tSfPeaks, editPeakSt, layoutSt, - sweepExtentSt, isUiNoBrushSt, cyclicvoltaSt, + sweepExtentSt, isUiAddIntgSt, isUiSplitIntgSt, isUiVisualSplitIntgSt, isUiNoBrushSt, cyclicvoltaSt, integationSt, mtplySt, }) { this.root = d3.select(this.rootKlass).selectAll('.focus-main'); @@ -1109,6 +1239,8 @@ class MultiFocus { this.entities = entities; this.setDataParams(filterSeed, filterPeak, tTrEndPts, tSfPeaks, layoutSt, cyclicvoltaSt, jcampIdx); + Object.assign(this, { isUiSplitIntgSt, isUiVisualSplitIntgSt }); + if (!isUiSplitIntgSt && !isUiVisualSplitIntgSt) this.clearSplitPreview(); if (this.data && this.data.length > 0) { this.setConfig(sweepExtentSt); @@ -1123,7 +1255,7 @@ class MultiFocus { this.drawInteg(integationSt); this.drawMtply(mtplySt); } - MountBrush(this, false, isUiNoBrushSt); + MountBrush(this, isUiAddIntgSt, isUiNoBrushSt); this.resetShouldUpdate(editPeakSt); } } diff --git a/src/components/d3_rect/rect_focus.js b/src/components/d3_rect/rect_focus.js index 650c3370..a33d364f 100644 --- a/src/components/d3_rect/rect_focus.js +++ b/src/components/d3_rect/rect_focus.js @@ -51,6 +51,8 @@ class RectFocus { this.factor = 0.125; this.currentExtent = null; this.layout = LIST_LAYOUT.MS; + this.isUiAddIntgSt = false; + this.firstIntegrationPoint = null; this.setTip = this.setTip.bind(this); this.setDataParams = this.setDataParams.bind(this); diff --git a/src/components/panel/graph_selection.js b/src/components/panel/graph_selection.js index ab65afa2..56f2054b 100644 --- a/src/components/panel/graph_selection.js +++ b/src/components/panel/graph_selection.js @@ -55,12 +55,13 @@ const GraphSelectionPanel = ({ if (subLayoutsInfo) { subLayoutValues = Object.keys(subLayoutsInfo); } + const subLayoutKey = subLayoutValues.join('|'); const [selectedSubLayout, setSelectedSublayout] = useState(subLayoutValues[0]); useEffect(() => { setSelectedSublayout(subLayoutValues[0]); - }, subLayoutValues); + }, [subLayoutKey]); if (!curveSt) { return (); diff --git a/src/constants/action_type.js b/src/constants/action_type.js index 1781efa7..29c0bb9a 100644 --- a/src/constants/action_type.js +++ b/src/constants/action_type.js @@ -88,6 +88,9 @@ const INTEGRATION = { RESET_ALL_RDC: 'INTEGRATION_RESET_ALL_RDC', CLEAR_ALL: 'INTEGRATION_CLEAR_ALL', SWEEP: 'INTEGRATION_SWEEP', + SPLIT: 'INTEGRATION_SPLIT', + ADD_VISUAL_SPLIT: 'INTEGRATION_ADD_VISUAL_SPLIT', + RM_VISUAL_SPLIT: 'INTEGRATION_RM_VISUAL_SPLIT', }; const SIMULATION = { diff --git a/src/constants/list_ui.js b/src/constants/list_ui.js index c8aab9bd..f3ad16f0 100644 --- a/src/constants/list_ui.js +++ b/src/constants/list_ui.js @@ -10,6 +10,8 @@ const LIST_UI_SWEEP_TYPE = { INTEGRATION_RM: 'integration remove', INTEGRATION_REF: 'integration reference', INTEGRATION_SET_REF: 'integration set ref', + INTEGRATION_SPLIT: 'integration split', + INTEGRATION_VISUAL_SPLIT: 'integration visual split', MULTIPLICITY_SWEEP_ADD: 'multiplicity sweep add', MULTIPLICITY_ONE_CLICK: 'multiplicity one click', MULTIPLICITY_ONE_RM: 'multiplicity one remove', @@ -34,6 +36,8 @@ const LIST_NON_BRUSH_TYPES = [ LIST_UI_SWEEP_TYPE.ANCHOR_SHIFT, LIST_UI_SWEEP_TYPE.INTEGRATION_RM, LIST_UI_SWEEP_TYPE.INTEGRATION_SET_REF, + LIST_UI_SWEEP_TYPE.INTEGRATION_SPLIT, + LIST_UI_SWEEP_TYPE.INTEGRATION_VISUAL_SPLIT, LIST_UI_SWEEP_TYPE.MULTIPLICITY_PEAK_ADD, LIST_UI_SWEEP_TYPE.MULTIPLICITY_PEAK_RM, LIST_UI_SWEEP_TYPE.MULTIPLICITY_ONE_CLICK, diff --git a/src/helpers/brush.js b/src/helpers/brush.js index e4e63e5d..4b4f36db 100644 --- a/src/helpers/brush.js +++ b/src/helpers/brush.js @@ -1,9 +1,16 @@ /* eslint-disable prefer-object-spread */ -import { MouseMove } from './compass'; +import { clearIntegrationPreview, MouseMove } from './compass'; +import { clearPendingIntegrationDraft } from './integration_draft.js'; // eslint-disable-line import/extensions +import { buildSweepPayloadFromXBounds } from './sweep.js'; // eslint-disable-line import/extensions const d3 = require('d3'); +const selectBrushLayer = (focus, selector) => { + const svg = focus.svg || d3.select('.d3Svg'); + return svg.selectAll(selector); +}; + const wheeled = (focus, event) => { const { currentExtent, scrollUiWheelAct } = focus; // WORKAROUND: firefox wheel compatibilty @@ -14,7 +21,7 @@ const wheeled = (focus, event) => { const brushed = (focus, isUiAddIntgSt, event) => { const { - selectUiSweepAct, data, dataPks, brush, w, h, scales, + selectUiSweepAct, data, dataPks, brush, brushX, w, h, scales, } = focus; const selection = event.selection && event.selection.reverse(); if (!selection) return; @@ -23,53 +30,60 @@ const brushed = (focus, isUiAddIntgSt, event) => { let xExtent = { xL: xes[0], xU: xes[1] }; let yExtent = { yL: yes[0], yU: yes[1] }; if (isUiAddIntgSt) { - xes = selection.map(scales.x.invert).sort((a, b) => a - b); - xExtent = { xL: xes[0], xU: xes[1] }; - } else { - const [begPt, endPt] = selection; - xes = [begPt[0], endPt[0]].map(scales.x.invert).sort((a, b) => a - b); - yes = [begPt[1], endPt[1]].map(scales.y.invert).sort((a, b) => a - b); - xExtent = { xL: xes[0], xU: xes[1] }; - yExtent = { yL: yes[0], yU: yes[1] }; + const payload = buildSweepPayloadFromXBounds( + focus, + scales.x.invert(selection[0]), + scales.x.invert(selection[1]), + ); + selectUiSweepAct(payload); + if (brushX) { + selectBrushLayer(focus, '.brushX').call(brushX.move, null); + } + return; } + const [begPt, endPt] = selection; + xes = [begPt[0], endPt[0]].map(scales.x.invert).sort((a, b) => a - b); + yes = [begPt[1], endPt[1]].map(scales.y.invert).sort((a, b) => a - b); + xExtent = { xL: xes[0], xU: xes[1] }; + yExtent = { yL: yes[0], yU: yes[1] }; selectUiSweepAct({ xExtent, yExtent, data, dataPks, }); - d3.select('.d3Svg').selectAll('.brush').call(brush.move, null); + selectBrushLayer(focus, '.brush').call(brush.move, null); }; const MountBrush = (focus, isUiAddIntgSt, isUiNoBrushSt) => { const { - root, svg, brush, brushX, w, h, + root, svg, brush, w, h, } = focus; svg.selectAll('.brush').remove(); svg.selectAll('.brushX').remove(); + Object.assign(focus, { isUiAddIntgSt }); + const { firstIntegrationPoint, data, jcampIdx } = focus; + const isSameIntegrationDraft = firstIntegrationPoint + && firstIntegrationPoint.jcampIdx === jcampIdx + && firstIntegrationPoint.dataLength === data.length; + if (!isUiAddIntgSt || (firstIntegrationPoint && !isSameIntegrationDraft)) { + clearPendingIntegrationDraft(); + Object.assign(focus, { firstIntegrationPoint: null }); + clearIntegrationPreview(focus); + } const brushedCb = (event) => brushed(focus, isUiAddIntgSt, event); const wheeledCb = (event) => wheeled(focus, event); - if (isUiNoBrushSt) { - const target = isUiAddIntgSt ? brushX : brush; - target.handleSize(10) + if (isUiNoBrushSt && !isUiAddIntgSt) { + brush.handleSize(10) .extent([[0, 0], [w, h]]) .on('end', brushedCb); - // append brush components - const klass = isUiAddIntgSt ? 'brushX' : 'brush'; root.append('g') - .attr('class', klass) + .attr('class', 'brush') .on('mousemove', (event) => MouseMove(event, focus)) - .call(target); + .call(brush); } svg.on('wheel', wheeledCb); }; export default MountBrush; - -// const resetedCb = () => reseted(main); -// main.svg.on('dblclick', resetedCb); -// const reseted = (main) => { -// const { selectUiSweepAct } = main; -// selectUiSweepAct({ xExtent: false, yExtent: false }); -// }; diff --git a/src/helpers/chem.js b/src/helpers/chem.js index 491a1529..16dca3f2 100644 --- a/src/helpers/chem.js +++ b/src/helpers/chem.js @@ -511,20 +511,42 @@ const calcIntgRefArea = (spectra, stack) => { return { raw2realRatio }; }; +const parseObservedIntegralGroups = (rawValue) => { + if (!rawValue) return {}; + const tokenRegx = /[^A-Za-z0-9._-]/g; + const groupsByIdx = {}; + rawValue.split('\n').forEach((line) => { + const cells = line.split(',').map((c) => c.replace(tokenRegx, '')); + if (cells.length < 2) return; + const idx = parseInt(cells[0], 10); + const groupId = cells[1]; + if (!Number.isInteger(idx) || idx < 0 || !groupId) return; + groupsByIdx[idx] = groupId; + }); + return groupsByIdx; +}; + const buildIntegFeature = (jcamp, spectra) => { - const { $OBSERVEDINTEGRALS, $OBSERVEDMULTIPLETS } = jcamp.info; + const { + $OBSERVEDINTEGRALS, $OBSERVEDMULTIPLETS, $OBSERVEDINTEGRALSGROUPS, + } = jcamp.info; const regx = /[^0-9.,-]/g; let stack = []; if ($OBSERVEDINTEGRALS) { const its = $OBSERVEDINTEGRALS.split('\n').slice(1); - const itStack = its.map((t) => { + const groupsByIdx = parseObservedIntegralGroups($OBSERVEDINTEGRALSGROUPS); + const itStack = its.map((t, idx) => { const ts = t.replace(regx, '').split(','); - return { + const item = { xL: parseFloat(ts[0]), xU: parseFloat(ts[1]), area: parseFloat(ts[2]), absoluteArea: parseFloat(ts[3]), }; + const groupId = groupsByIdx[idx]; + return groupId + ? Object.assign({}, item, { visualSplitGroupId: groupId }) + : item; }); stack = [...stack, ...itStack]; } @@ -787,7 +809,7 @@ const ExtractJcamp = (source) => { source, { xy: true, - keepRecordsRegExp: /(\$CSTHRESHOLD|\$CSSCANAUTOTARGET|\$CSSCANEDITTARGET|\$CSSCANCOUNT|\$CSSOLVENTNAME|\$CSSOLVENTVALUE|\$CSSOLVENTX|\$CSCATEGORY|\$CSITAREA|\$CSITFACTOR|\$OBSERVEDINTEGRALS|\$OBSERVEDMULTIPLETS|\$OBSERVEDMULTIPLETSPEAKS|\.SOLVENTNAME|\.OBSERVEFREQUENCY|\$CSSIMULATIONPEAKS|\$CSUPPERTHRESHOLD|\$CSLOWERTHRESHOLD|\$CSCYCLICVOLTAMMETRYDATA|UNITS|SYMBOL|\$CSAUTOMETADATA|\$DETECTOR|MN|MW|D|MP|MELTINGPOINT|TG|\$CSSCANRATE|\$CSSPECTRUMDIRECTION|\$CSWEAREAVALUE|\$CSWEAREAUNIT|\$CSCURRENTMODE)/, // eslint-disable-line + keepRecordsRegExp: /(\$CSTHRESHOLD|\$CSSCANAUTOTARGET|\$CSSCANEDITTARGET|\$CSSCANCOUNT|\$CSSOLVENTNAME|\$CSSOLVENTVALUE|\$CSSOLVENTX|\$CSCATEGORY|\$CSITAREA|\$CSITFACTOR|\$OBSERVEDINTEGRALS|\$OBSERVEDINTEGRALSGROUPS|\$OBSERVEDMULTIPLETS|\$OBSERVEDMULTIPLETSPEAKS|\.SOLVENTNAME|\.OBSERVEFREQUENCY|\$CSSIMULATIONPEAKS|\$CSUPPERTHRESHOLD|\$CSLOWERTHRESHOLD|\$CSCYCLICVOLTAMMETRYDATA|UNITS|SYMBOL|\$CSAUTOMETADATA|\$DETECTOR|MN|MW|D|MP|MELTINGPOINT|TG|\$CSSCANRATE|\$CSSPECTRUMDIRECTION|\$CSWEAREAVALUE|\$CSWEAREAUNIT|\$CSCURRENTMODE)/, // eslint-disable-line }, ); const layout = readLayout(jcamp); @@ -886,4 +908,5 @@ export { GetCyclicVoltaRatio, GetCyclicVoltaPeakSeparate, Feature2MaxMinPeak, convertTopic, Convert2MaxMinPeak, GetCyclicVoltaShiftOffset, GetCyclicVoltaPreviousShift, + buildIntegFeature, }; diff --git a/src/helpers/compass.js b/src/helpers/compass.js index 038faaff..2a4c76d6 100644 --- a/src/helpers/compass.js +++ b/src/helpers/compass.js @@ -1,5 +1,17 @@ import Format from './format'; import { Convert2DValue } from './chem'; +import { buildSweepPayloadFromXBounds } from './sweep.js'; // eslint-disable-line import/extensions +import { + forgetPendingIntegrationDraft, + setPendingIntegrationDraft, +} from './integration_draft.js'; // eslint-disable-line import/extensions +import { + clearIntegrationSplitPreview, + drawIntegrationSplitPreview, + getIntegrationSplitTargetFromEvent, + getVisualSplitLineAtX, + isAlreadyVisuallySplit, +} from './integration_split'; const d3 = require('d3'); @@ -10,26 +22,17 @@ const TfRescale = (focus) => { }; const fetchPt = (event, focus, xt) => { - // const rawMouseX = focus.isFirefox // WORKAROUND d3.mouse firefox compatibility - // ? d3.event.offsetX - // : d3.mouse(focus.root.node())[0]; const rawMouseX = d3.pointer(event, focus.root.node())[0]; const mouseX = xt.invert(rawMouseX); const bisectDate = d3.bisector((d) => +d.x).left; const dt = focus.data; const ls = dt.length; - const sortData = ls > 0 && dt[0].x > dt[ls - 1].x ? dt.reverse() : dt; + const sortData = ls > 0 && dt[0].x > dt[ls - 1].x ? [...dt].reverse() : dt; const idx = bisectDate(sortData, +mouseX); - return sortData[idx]; + return sortData[Math.min(idx, ls - 1)]; }; const fetchFreePt = (event, focus, xt, yt) => { - // const rawMouseX = focus.isFirefox // WORKAROUND d3.mouse firefox compatibility - // ? d3.event.offsetX - // : d3.mouse(focus.root.node())[0]; - // const rawMouseY = focus.isFirefox // WORKAROUND d3.mouse firefox compatibility - // ? d3.event.offsetY - // : d3.mouse(focus.root.node())[1]; const rawMouseX = d3.pointer(event, focus.root.node())[0]; const rawMouseY = d3.pointer(event, focus.root.node())[1]; const mouseX = xt.invert(rawMouseX); @@ -55,6 +58,73 @@ const fetchFreePt = (event, focus, xt, yt) => { return selectPoint; }; +const clearIntegrationPreview = (focus) => { + if (!focus || !focus.root) return; + focus.root.select('.integration-preview-line').remove(); +}; + +const drawIntegrationPreview = (focus, firstPoint, nextPoint) => { + if (!firstPoint || !nextPoint) return; + const { xt, yt } = TfRescale(focus); + const preview = focus.root.select('.integration-preview'); + const line = preview.selectAll('.integration-preview-line').data([{ + firstPoint, + nextPoint, + }]); + + line.enter() + .append('line') + .attr('class', 'integration-preview-line') + .attr('stroke', 'red') + .attr('stroke-width', 2) + .attr('stroke-dasharray', '4,3') + .style('pointer-events', 'none') + .merge(line) + .attr('x1', (d) => xt(d.firstPoint.x)) + .attr('y1', (d) => yt(d.firstPoint.y)) + .attr('x2', (d) => xt(d.nextPoint.x)) + .attr('y2', (d) => yt(d.nextPoint.y)); +}; + +const getCurvePointFromEvent = (event, focus) => { + const { xt, yt } = TfRescale(focus); + if (Format.isCyclicVoltaLayout(focus.layout)) { + return fetchFreePt(event, focus, xt, yt); + } + return fetchPt(event, focus, xt); +}; + +const cancelIntegrationDraft = (focus) => { + Object.assign(focus, { firstIntegrationPoint: null }); + clearIntegrationPreview(focus); + forgetPendingIntegrationDraft(); +}; + +const updateIntegrationPreview = (event, focus) => { + if (!focus.isUiAddIntgSt || !focus.firstIntegrationPoint) return; + const pt = getCurvePointFromEvent(event, focus); + if (!pt) return; + drawIntegrationPreview(focus, focus.firstIntegrationPoint, pt); +}; + +const updateIntegrationSplitPreview = (event, focus) => { + if (!focus.isUiSplitIntgSt && !focus.isUiVisualSplitIntgSt) return; + + const { splitX, target } = getIntegrationSplitTargetFromEvent(event, focus); + if (!target) { + clearIntegrationSplitPreview(focus); + return; + } + + if (focus.isUiVisualSplitIntgSt && isAlreadyVisuallySplit(target)) { + clearIntegrationSplitPreview(focus); + return; + } + + const { shift = 0, ignoreRef = false } = focus.integrationSplitTargets || {}; + drawIntegrationSplitPreview(focus, target, splitX, shift, ignoreRef); +}; + const MouseMove = (event, focus) => { const { xt, yt } = TfRescale(focus); const { freq, layout, wavelength } = focus; @@ -117,11 +187,92 @@ const MouseMove = (event, focus) => { } } } + updateIntegrationPreview(event, focus); + updateIntegrationSplitPreview(event, focus); +}; + +const clickIntegrationPoint = (event, focus) => { + const pt = getCurvePointFromEvent(event, focus); + if (!pt) return; + + const { firstIntegrationPoint, selectUiSweepAct } = focus; + if (!firstIntegrationPoint) { + // Keep the draft local to D3; the second click emits the existing sweep payload. + const draftPoint = { + x: pt.x, + y: pt.y, + jcampIdx: focus.jcampIdx, + dataLength: focus.data.length, + }; + Object.assign(focus, { firstIntegrationPoint: draftPoint }); + setPendingIntegrationDraft({ + jcampIdx: focus.jcampIdx, + dataLength: focus.data.length, + cancel: () => cancelIntegrationDraft(focus), + }); + drawIntegrationPreview(focus, draftPoint, draftPoint); + return; + } + + cancelIntegrationDraft(focus); + if (firstIntegrationPoint.x === pt.x) { + return; + } + + selectUiSweepAct(buildSweepPayloadFromXBounds( + focus, + firstIntegrationPoint.x, + pt.x, + )); }; const ClickCompass = (event, focus) => { event.stopPropagation(); event.preventDefault(); + if (focus.isUiAddIntgSt) { + clickIntegrationPoint(event, focus); + return; + } + if (focus.isUiSplitIntgSt) { + const { splitX, target } = getIntegrationSplitTargetFromEvent(event, focus); + if (!target) return; + + clearIntegrationSplitPreview(focus); + focus.splitIntegrationAct({ + curveIdx: focus.jcampIdx, + target, + splitX, + data: focus.data, + }); + return; + } + if (focus.isUiVisualSplitIntgSt) { + const { splitX, target } = getIntegrationSplitTargetFromEvent(event, focus); + if (!target) return; + + const { stack = [], shift = 0 } = focus.integrationSplitTargets || {}; + const existingSplitX = getVisualSplitLineAtX(focus, stack, splitX, shift); + clearIntegrationSplitPreview(focus); + if (Number.isFinite(existingSplitX)) { + if (typeof focus.removeVisualSplitLineAct !== 'function') return; + focus.removeVisualSplitLineAct({ + curveIdx: focus.jcampIdx, + splitX: existingSplitX, + data: focus.data, + }); + return; + } + if (isAlreadyVisuallySplit(target)) return; + if (typeof focus.addVisualSplitLineAct !== 'function') return; + focus.addVisualSplitLineAct({ + curveIdx: focus.jcampIdx, + target, + splitX, + data: focus.data, + }); + return; + } + const { xt, yt } = TfRescale(focus); let pt = fetchPt(event, focus, xt); const { layout, cyclicvoltaSt, jcampIdx } = focus; @@ -147,6 +298,9 @@ const MountCompass = (focus) => { .attr('class', 'compass'); const cursor = root.append('g') .attr('class', 'cursor'); + const preview = root.append('g') + .attr('class', 'integration-preview') + .attr('clip-path', 'url(#clip)'); const overlay = root.append('rect') .attr('class', 'overlay-focus') .attr('width', w) @@ -174,6 +328,8 @@ const MountCompass = (focus) => { .style('text-anchor', 'middle') .style('fill', '#D68910'); + preview.selectAll('*').remove(); + overlay .on('mousemove', (event) => MouseMove(event, focus)) .on('click', (event) => ClickCompass(event, focus)); @@ -184,4 +340,6 @@ export { TfRescale, ClickCompass, MouseMove, + getCurvePointFromEvent, + clearIntegrationPreview, }; diff --git a/src/helpers/integration.js b/src/helpers/integration.js index 8fbfc1ab..865066e0 100644 --- a/src/helpers/integration.js +++ b/src/helpers/integration.js @@ -14,23 +14,35 @@ const getArea = (xL, xU, data) => { return Math.abs(data[iU].k - data[iL].k); }; +const getIntegrationPoints = (xL, xU, data) => data.filter((d) => ( + d + && Number.isFinite(d.x) + && Number.isFinite(d.y) + && d.x > xL + && d.x < xU +)); + +const getLinearBaseline = (points) => { + if (!points || !points[0]) return () => 0; + + const point1 = points[0]; + const point2 = points[points.length - 1]; + const slope = calcSlope(point1.x, point1.y, point2.x, point2.y); + + return (point) => point1.y + slope * (point.x - point1.x); +}; + const getAbsoluteArea = (xL, xU, data) => { - const ps = data.filter((d) => d.x > xL && d.x < xU); - if (!ps[0]) return 0; + const ps = getIntegrationPoints(xL, xU, data); + if (ps.length < 2) return 0; + const baselineY = getLinearBaseline(ps); let area = 0; - const point1 = ps[0]; - const point2 = ps[ps.length - 1]; - const slope = calcSlope(point1.x, point1.y, point2.x, point2.y); - let lastDY = point1.y; if (ps.length > 1) { for (let i = 1; i < ps.length; i += 1) { const pt = ps[i]; - const lastD = ps[i - 1]; - const y = slope * (pt.x - lastD.x) + lastDY; - lastDY = y; - const delta = Math.abs(pt.y - y); + const delta = Math.abs(pt.y - baselineY(pt)); area += delta; } } @@ -38,6 +50,57 @@ const getAbsoluteArea = (xL, xU, data) => { return area; }; +const normalizeSplitLines = (splitLines) => { + if (!Array.isArray(splitLines)) return []; + const finiteValues = splitLines + .map((value) => parseFloat(value)) + .filter((value) => Number.isFinite(value)); + return [...new Set(finiteValues)].sort((a, b) => a - b); +}; + +const buildSplitIntervals = (xL, xU, splitLines = []) => { + if (!Number.isFinite(xL) || !Number.isFinite(xU)) return []; + + const [lower, upper] = [xL, xU].sort((a, b) => a - b); + if (lower === upper) return []; + + const sortedSplitLines = normalizeSplitLines(splitLines) + .filter((splitX) => splitX > lower && splitX < upper); + + return [lower, ...sortedSplitLines, upper].slice(1).map((right, index, bounds) => ({ + xL: index === 0 ? lower : bounds[index - 1], + xU: right, + })); +}; + +const getAbsoluteAreaWithBaseline = (xL, xU, data, baselineY) => { + const ps = getIntegrationPoints(xL, xU, data); + if (ps.length < 2 || typeof baselineY !== 'function') return 0; + + let area = 0; + for (let i = 1; i < ps.length; i += 1) { + const pt = ps[i]; + const baselineValue = baselineY(pt); + if (Number.isFinite(baselineValue)) { + area += Math.abs(pt.y - baselineValue); + } + } + return area; +}; + +const getSplitAreas = (xL, xU, splitLines, data) => { + const intervals = buildSplitIntervals(xL, xU, splitLines); + const mainPoints = getIntegrationPoints(xL, xU, data); + const baselineY = getLinearBaseline(mainPoints); + + return intervals.map((interval) => ({ + xL: interval.xL, + xU: interval.xU, + area: getArea(interval.xL, interval.xU, data), + absoluteArea: getAbsoluteAreaWithBaseline(interval.xL, interval.xU, data, baselineY), + })); +}; + const calcArea = (d, refArea, refFactor, ignoreRef = false) => { if (ignoreRef) { const { absoluteArea } = d; @@ -46,4 +109,84 @@ const calcArea = (d, refArea, refFactor, ignoreRef = false) => { return (d.area * refFactor / refArea).toFixed(2); }; -export { getArea, calcArea, getAbsoluteArea }; // eslint-disable-line +const splitAreaProportionally = (totalArea, leftRaw, rightRaw) => { + const safeTotal = Number.isFinite(totalArea) ? totalArea : 0; + if (safeTotal === 0) return { left: 0, right: 0 }; + + const safeLeft = Number.isFinite(leftRaw) ? Math.max(leftRaw, 0) : 0; + const safeRight = Number.isFinite(rightRaw) ? Math.max(rightRaw, 0) : 0; + const rawSum = safeLeft + safeRight; + if (rawSum <= 0) { + const half = safeTotal / 2; + return { left: half, right: half }; + } + + const ratio = safeTotal / rawSum; + return { left: safeLeft * ratio, right: safeRight * ratio }; +}; + +let visualSplitGroupCounter = 0; +const generateVisualSplitGroupId = () => { + visualSplitGroupCounter += 1; + const seed = (typeof Date !== 'undefined' && Date.now) + ? Date.now().toString(36) + : Math.random().toString(36).slice(2); + return `vsg-${seed}-${visualSplitGroupCounter}`; +}; + +const getVisualSplitGroups = (stack = []) => { + if (!Array.isArray(stack) || stack.length === 0) return []; + const groups = []; + let current = null; + stack.forEach((item) => { + const groupId = item && item.visualSplitGroupId; + if (groupId && current && current.groupId === groupId) { + current.items.push(item); + current.xL = Math.min(current.xL, item.xL); + current.xU = Math.max(current.xU, item.xU); + } else { + current = { + groupId: groupId || null, + items: [item], + xL: item.xL, + xU: item.xU, + isMerged: false, + }; + groups.push(current); + } + }); + groups.forEach((group) => { + // eslint-disable-next-line no-param-reassign + group.isMerged = !!group.groupId && group.items.length > 1; + }); + return groups; +}; + +const getVisualSplitGroupBoundaries = (group) => { + if (!group || !group.isMerged) return []; + const sortedItems = [...group.items].sort((a, b) => a.xL - b.xL); + const boundaries = []; + for (let i = 0; i < sortedItems.length - 1; i += 1) { + const current = sortedItems[i]; + const next = sortedItems[i + 1]; + const boundary = (current.xU + next.xL) / 2; + if (Number.isFinite(boundary)) boundaries.push(boundary); + } + return boundaries; +}; + +export { + buildSplitIntervals, + calcArea, + generateVisualSplitGroupId, + getAbsoluteArea, + getAbsoluteAreaWithBaseline, + getArea, + getIntegrationPoints, + getLinearBaseline, + getSplitAreas, + getVisualSplitGroupBoundaries, + getVisualSplitGroups, + normalizeSplitLines, + splitAreaProportionally, +}; // eslint-disable-line diff --git a/src/helpers/integration_draft.js b/src/helpers/integration_draft.js new file mode 100644 index 00000000..b911c1e9 --- /dev/null +++ b/src/helpers/integration_draft.js @@ -0,0 +1,45 @@ +let pendingIntegrationDraft = null; + +const cancelMessage = 'You are currently creating an integration. Are you sure you want to cancel it?'; + +const hasPendingIntegrationDraft = () => !!pendingIntegrationDraft; + +const setPendingIntegrationDraft = (draft) => { + pendingIntegrationDraft = draft; +}; + +const forgetPendingIntegrationDraft = () => { + pendingIntegrationDraft = null; +}; + +const clearPendingIntegrationDraft = () => { + const draft = pendingIntegrationDraft; + pendingIntegrationDraft = null; + if (draft && typeof draft.cancel === 'function') { + draft.cancel(); + } +}; + +const confirmCancelPendingIntegration = () => { + if (!hasPendingIntegrationDraft()) return true; + + const shouldCancel = typeof window === 'undefined' + || typeof window.confirm !== 'function' + // This confirmation intentionally protects an in-progress two-click integration. + // eslint-disable-next-line no-alert + || window.confirm(cancelMessage); + + if (shouldCancel) { + clearPendingIntegrationDraft(); + } + + return shouldCancel; +}; + +export { + clearPendingIntegrationDraft, + confirmCancelPendingIntegration, + forgetPendingIntegrationDraft, + hasPendingIntegrationDraft, + setPendingIntegrationDraft, +}; diff --git a/src/helpers/integration_split.js b/src/helpers/integration_split.js new file mode 100644 index 00000000..4331c577 --- /dev/null +++ b/src/helpers/integration_split.js @@ -0,0 +1,242 @@ +import { + getIntegrationPoints, + getLinearBaseline, + getVisualSplitGroupBoundaries, + getVisualSplitGroups, +} from './integration'; + +const d3 = require('d3'); + +const SPLIT_PREVIEW_CLASS = 'integration-split-preview-line'; +const VISUAL_SPLIT_CLASS = 'integration-visual-split-line'; +const VISUAL_SPLIT_HIT_TOLERANCE_PX = 6; +const NMR_SPLIT_PREVIEW_EXTENT = { y1: 38, y2: 55 }; + +const getIntegrationBounds = (target, shift = 0) => ( + [target.xL - shift, target.xU - shift].sort((a, b) => a - b) +); + +const getSplitXFromEvent = (event, focus) => { + const rawMouseX = d3.pointer(event, focus.root.node())[0]; + return focus.scales.x.invert(rawMouseX); +}; + +const clearIntegrationSplitPreview = (focus) => { + if (!focus || !focus.root) return; + focus.root.select(`.${SPLIT_PREVIEW_CLASS}`).remove(); +}; + +const isAlreadyVisuallySplit = (target) => { + if (!target) return false; + return !!target.visualSplitGroupId || target.isMerged === true; +}; + +const isMergedVisualSplitGroup = (target) => !!(target && target.isMerged === true); + +const resolveBaselineContextBounds = (focus, target, shift) => { + if (target && target.isMerged === true) { + return getIntegrationBounds(target, shift); + } + const groupId = target && target.visualSplitGroupId; + const stack = focus && focus.integrationSplitTargets && focus.integrationSplitTargets.stack; + if (groupId && Array.isArray(stack)) { + const siblings = stack.filter((item) => item && item.visualSplitGroupId === groupId); + if (siblings.length > 1) { + const lower = Math.min(...siblings.map((item) => item.xL)) - shift; + const upper = Math.max(...siblings.map((item) => item.xU)) - shift; + return [lower, upper].sort((a, b) => a - b); + } + } + return getIntegrationBounds(target, shift); +}; + +const getIntegrationSplitTarget = (focus, splitX) => { + const splitTargets = focus && focus.integrationSplitTargets; + if (!splitTargets || !Number.isFinite(splitX)) return null; + + const { stack = [], shift = 0 } = splitTargets; + return stack.find((target) => { + const [xL, xU] = getIntegrationBounds(target, shift); + return splitX > xL && splitX < xU; + }); +}; + +const getIntegrationSplitTargetFromEvent = (event, focus) => { + const splitX = getSplitXFromEvent(event, focus); + const target = getIntegrationSplitTarget(focus, splitX); + + return { splitX, target }; +}; + +const getVisualSplitLines = (stack = [], shift = 0) => { + if (!Array.isArray(stack)) return []; + return getVisualSplitGroups(stack) + .filter((group) => group.isMerged) + .reduce((acc, group) => { + getVisualSplitGroupBoundaries(group).forEach((boundary) => { + const value = boundary - shift; + if (Number.isFinite(value)) acc.push(value); + }); + return acc; + }, []); +}; + +const getVisualSplitLineAtX = ( + focus, + stack, + splitX, + shift = 0, + pixelTolerance = VISUAL_SPLIT_HIT_TOLERANCE_PX, +) => { + if (!focus || !focus.scales || !focus.scales.x || !Number.isFinite(splitX)) return null; + + const xt = focus.scales.x; + const splitXPx = xt(splitX); + const nearest = getVisualSplitLines(stack, shift).reduce((acc, lineX) => { + const distance = Math.abs(xt(lineX) - splitXPx); + if (!acc || distance < acc.distance) { + return { splitX: lineX, distance }; + } + return acc; + }, null); + + return nearest && nearest.distance <= pixelTolerance ? nearest.splitX : null; +}; + +const interpolateY = (points, x) => { + if (!points || points.length === 0) return null; + const sortedPoints = [...points].sort((a, b) => a.x - b.x); + const upperIndex = sortedPoints.findIndex((point) => point.x >= x); + if (upperIndex < 0) return sortedPoints[sortedPoints.length - 1].y; + if (upperIndex === 0) return sortedPoints[0].y; + + const lowerPoint = sortedPoints[upperIndex - 1]; + const upperPoint = sortedPoints[upperIndex]; + const xDelta = upperPoint.x - lowerPoint.x; + if (xDelta === 0) return lowerPoint.y; + + const ratio = (x - lowerPoint.x) / xDelta; + return lowerPoint.y + ratio * (upperPoint.y - lowerPoint.y); +}; + +const resolveSplitPreviewExtent = (focus, target, splitX, shift, ignoreRef) => { + if (!focus || !target || !focus.scales || !focus.scales.y) return null; + + const yt = focus.scales.y; + const [xL, xU] = getIntegrationBounds(target, shift); + if (!Number.isFinite(splitX) || splitX <= xL || splitX >= xU) return null; + + if (!ignoreRef) { + return NMR_SPLIT_PREVIEW_EXTENT; + } + + const [contextXL, contextXU] = resolveBaselineContextBounds(focus, target, shift); + const points = getIntegrationPoints(contextXL, contextXU, focus.data); + if (points.length < 2) return null; + + const baselineY = getLinearBaseline(points); + const curveY = interpolateY(points, splitX); + const baseY = baselineY({ x: splitX }); + if (!Number.isFinite(curveY) || !Number.isFinite(baseY)) return null; + + return { + y1: yt(baseY), + y2: yt(curveY), + }; +}; + +const drawIntegrationSplitPreview = (focus, target, splitX, shift, ignoreRef) => { + const extent = resolveSplitPreviewExtent(focus, target, splitX, shift, ignoreRef); + if (!extent) { + clearIntegrationSplitPreview(focus); + return; + } + + const xt = focus.scales.x; + const x = xt(splitX); + const preview = focus.root.select('.integration-preview'); + preview.raise(); + const line = preview.selectAll(`.${SPLIT_PREVIEW_CLASS}`).data([extent]); + + line.enter() + .append('line') + .attr('class', SPLIT_PREVIEW_CLASS) + .attr('stroke', 'red') + .attr('stroke-width', 2) + .attr('stroke-dasharray', '4,3') + .style('pointer-events', 'none') + .merge(line) + .attr('x1', x) + .attr('x2', x) + .attr('y1', (d) => d.y1) + .attr('y2', (d) => d.y2); +}; + +const drawIntegrationVisualSplitLines = ( + focus, + stack = [], + shift = 0, + ignoreRef = false, + isInteractive = false, + onClickLine = null, +) => { + if (!focus || !focus.tags || !focus.tags.visualSplitPath) return; + + const groups = getVisualSplitGroups(stack).filter((group) => group.isMerged); + const splitLines = groups.reduce((acc, group) => { + const groupTarget = { xL: group.xL, xU: group.xU }; + getVisualSplitGroupBoundaries(group).forEach((boundary) => { + const splitX = boundary - shift; + const extent = resolveSplitPreviewExtent(focus, groupTarget, splitX, shift, ignoreRef); + if (extent) { + acc.push({ + splitX, + key: `${group.groupId || `${group.xL}-${group.xU}`}-${splitX}`, + ...extent, + }); + } + }); + return acc; + }, []); + + const xt = focus.scales.x; + const lines = focus.tags.visualSplitPath + .selectAll(`line.${VISUAL_SPLIT_CLASS}`) + .data(splitLines, (d) => d.key); + + lines.exit() + .attr('class', 'exit') + .remove(); + + lines.enter() + .append('line') + .attr('class', VISUAL_SPLIT_CLASS) + .attr('stroke', 'red') + .attr('stroke-width', 2) + .merge(lines) + .attr('x1', (d) => xt(d.splitX)) + .attr('x2', (d) => xt(d.splitX)) + .attr('y1', (d) => d.y1) + .attr('y2', (d) => d.y2) + .style('pointer-events', isInteractive ? 'stroke' : 'none') + .on('click', isInteractive && onClickLine + ? (event, d) => onClickLine(event, d.splitX) + : null); +}; + +export { + NMR_SPLIT_PREVIEW_EXTENT, + clearIntegrationSplitPreview, + drawIntegrationSplitPreview, + drawIntegrationVisualSplitLines, + getIntegrationBounds, + getIntegrationSplitTarget, + getIntegrationSplitTargetFromEvent, + getSplitXFromEvent, + getVisualSplitLineAtX, + getVisualSplitLines, + interpolateY, + isAlreadyVisuallySplit, + isMergedVisualSplitGroup, + resolveSplitPreviewExtent, +}; diff --git a/src/helpers/mount.js b/src/helpers/mount.js index d91d7a5f..2c4cf9a1 100644 --- a/src/helpers/mount.js +++ b/src/helpers/mount.js @@ -34,11 +34,14 @@ const MountTags = (target) => { const aucPath = target.root.append('g') .attr('class', 'aucPath-clip') .attr('clip-path', 'url(#clip)'); + const visualSplitPath = target.root.append('g') + .attr('class', 'integration-visual-splits') + .attr('clip-path', 'url(#clip)'); const peckerPath = target.root.append('g') .attr('class', 'peckerPath-clip') .attr('clip-path', 'url(#clip)'); return { - pPath, bpPath, bpTxt, igbPath, igcPath, igtPath, mpybPath, mpyt1Path, mpyt2Path, mpypPath, aucPath, peckerPath, // eslint-disable-line + pPath, bpPath, bpTxt, igbPath, igcPath, igtPath, mpybPath, mpyt1Path, mpyt2Path, mpypPath, aucPath, visualSplitPath, peckerPath, // eslint-disable-line }; }; diff --git a/src/helpers/sweep.js b/src/helpers/sweep.js new file mode 100644 index 00000000..6a30b8bc --- /dev/null +++ b/src/helpers/sweep.js @@ -0,0 +1,23 @@ +const resolveVisibleYExtent = (focus) => { + const { currentExtent, h, scales } = focus; + if (currentExtent && currentExtent.yExtent) { + return currentExtent.yExtent; + } + + const yes = [h, 0].map(scales.y.invert).sort((a, b) => a - b); + return { yL: yes[0], yU: yes[1] }; +}; + +const buildSweepPayloadFromXBounds = (focus, xA, xB) => { + const { data, dataPks } = focus; + const xes = [xA, xB].map((x) => Number(x)).sort((a, b) => a - b); + + return { + xExtent: { xL: xes[0], xU: xes[1] }, + yExtent: resolveVisibleYExtent(focus), + data, + dataPks, + }; +}; + +export { buildSweepPayloadFromXBounds, resolveVisibleYExtent }; diff --git a/src/layer_prism.js b/src/layer_prism.js index a81a4d26..0d13fc94 100644 --- a/src/layer_prism.js +++ b/src/layer_prism.js @@ -21,14 +21,21 @@ const styles = () => ({ const LayerPrism = ({ entity, cLabel, xLabel, yLabel, forecast, operations, descriptions, molSvg, editorOnly, exactMass, - thresSt, scanSt, uiSt, + thresSt, scanSt, uiSt, curveSt, integrationSt, canChangeDescription, onDescriptionChanged, }) => { const { - topic, feature, hasEdit, integration, + topic, feature, hasEdit, integration: initialIntegration, } = extractParams(entity, thresSt, scanSt); if (!topic) return null; + const curveIdx = (curveSt && Number.isFinite(curveSt.curveIdx)) ? curveSt.curveIdx : 0; + const liveIntegrations = (integrationSt && Array.isArray(integrationSt.integrations)) + ? integrationSt.integrations + : null; + const liveIntegration = liveIntegrations ? liveIntegrations[curveIdx] : null; + const integration = liveIntegration || initialIntegration; + const { viewer } = uiSt; if (viewer === LIST_UI_VIEWER_TYPE.ANALYSIS) { return ( @@ -104,6 +111,8 @@ const mapStateToProps = (state, props) => ( // eslint-disable-line scanSt: state.scan, thresSt: state.threshold.list[state.curve.curveIdx], uiSt: state.ui, + curveSt: state.curve, + integrationSt: state.integration.present, } ); @@ -126,6 +135,8 @@ LayerPrism.propTypes = { thresSt: PropTypes.object.isRequired, scanSt: PropTypes.object.isRequired, uiSt: PropTypes.object.isRequired, + curveSt: PropTypes.object.isRequired, + integrationSt: PropTypes.object.isRequired, canChangeDescription: PropTypes.bool.isRequired, onDescriptionChanged: PropTypes.func, }; diff --git a/src/reducers/reducer_integration.js b/src/reducers/reducer_integration.js index 01352986..869a12d4 100644 --- a/src/reducers/reducer_integration.js +++ b/src/reducers/reducer_integration.js @@ -3,7 +3,12 @@ import undoable from 'redux-undo'; import { UI, INTEGRATION, EDITPEAK, MANAGER, } from '../constants/action_type'; -import { getArea, getAbsoluteArea } from '../helpers/integration'; +import { + generateVisualSplitGroupId, + getAbsoluteArea, + getArea, + splitAreaProportionally, +} from '../helpers/integration'; import { undoRedoConfig, undoRedoActions } from './undo_redo_config'; const initialState = { @@ -39,7 +44,7 @@ const addToStack = (state, action) => { const { stack, refArea, shift } = selectedIntegration; const { xExtent, data } = newData; const { xL, xU } = xExtent; - if (!xL || !xU || (xU - xL) === 0) { return state; } + if (!Number.isFinite(xL) || !Number.isFinite(xU) || (xU - xL) === 0) { return state; } const area = getArea(xL, xU, data); const defaultRefArea = stack.length === 0 ? area : refArea; @@ -61,6 +66,20 @@ const addToStack = (state, action) => { return Object.assign({}, state, { integrations: newArrIntegration, selectedIdx: curveIdx }); }; +const dropOrphanVisualSplitGroupIds = (stack) => { + const groupCounts = stack.reduce((acc, item) => { + const groupId = item && item.visualSplitGroupId; + if (groupId) acc[groupId] = (acc[groupId] || 0) + 1; + return acc; + }, {}); + return stack.map((item) => { + const groupId = item && item.visualSplitGroupId; + if (!groupId || groupCounts[groupId] > 1) return item; + const { visualSplitGroupId, ...rest } = item; + return rest; + }); +}; + const rmFromStack = (state, action) => { const { dataToRemove, curveIdx } = action.payload; const { xL, xU, xExtent } = dataToRemove; @@ -71,14 +90,325 @@ const rmFromStack = (state, action) => { const { stack } = selectedIntegration; let [txL, txU] = [0, 0]; - if (xL && xU) { // rm click integration + if (Number.isFinite(xL) && Number.isFinite(xU)) { [txL, txU] = [xL, xU]; - } else if (xExtent) { // rm click multiplicity + } else if (xExtent) { [txL, txU] = [xExtent.xL, xExtent.xU]; } else { return state; } - const newStack = stack.filter((k) => k.xL !== txL && k.xU !== txU); + const filteredStack = stack.filter((k) => k.xL !== txL && k.xU !== txU); + const newStack = dropOrphanVisualSplitGroupIds(filteredStack); + + const newIntegration = Object.assign({}, selectedIntegration, { stack: newStack }); + const newArrIntegration = [...integrations]; + newArrIntegration[curveIdx] = newIntegration; + + return Object.assign({}, state, { integrations: newArrIntegration, selectedIdx: curveIdx }); +}; + +const hasEnoughDataResolution = (xL, xU, data) => { + const [lower, upper] = [xL, xU].sort((a, b) => a - b); + const points = data.filter((pt) => ( + pt + && Number.isFinite(pt.x) + && pt.x >= lower + && pt.x <= upper + )); + if (points.length < 2) return false; + + return points.some((pt) => pt.x !== points[0].x); +}; + +const computeProportionalSplitAreas = (xL, splitX, xU, data, original) => { + const areaParts = splitAreaProportionally( + original.area, + getArea(xL, splitX, data), + getArea(splitX, xU, data), + ); + const absParts = splitAreaProportionally( + original.absoluteArea, + getAbsoluteArea(xL, splitX, data), + getAbsoluteArea(splitX, xU, data), + ); + return { + leftArea: areaParts.left, + rightArea: areaParts.right, + leftAbs: absParts.left, + rightAbs: absParts.right, + }; +}; + +const buildSplitStackItem = (xL, xU, shift, area, absoluteArea) => { + const [lower, upper] = [xL, xU].sort((a, b) => a - b); + return { + xL: lower + shift, + xU: upper + shift, + area, + absoluteArea, + }; +}; + +const getVisualSplitTolerance = (xL, xU) => Math.max(Math.abs(xU - xL) * 1e-6, Number.EPSILON); + +const findTargetIntegrationIndex = (stack, target) => stack.findIndex((item) => ( + item.xL === target.xL && item.xU === target.xU +)); + +const buildVisualSplitItem = (xL, xU, shift, area, absoluteArea, groupId) => { + const [lower, upper] = [xL, xU].sort((a, b) => a - b); + const item = { + xL: lower + shift, + xU: upper + shift, + area, + absoluteArea, + }; + if (groupId) item.visualSplitGroupId = groupId; + return item; +}; + +const isVisuallySplit = (stack, item) => { + if (!item || !item.visualSplitGroupId) return false; + return stack.some((other) => ( + other !== item && other.visualSplitGroupId === item.visualSplitGroupId + )); +}; + +const buildRawSplitPart = (xL, xU, shift, data) => buildSplitStackItem( + xL, + xU, + shift, + getArea(xL, xU, data), + getAbsoluteArea(xL, xU, data), +); + +const findVisualSplitNeighborhood = (stack, original, shift, xL, xU) => { + const groupId = original && original.visualSplitGroupId; + if (!groupId) return { hasLeft: false, hasRight: false }; + + const tolerance = getVisualSplitTolerance(xL, xU); + const isGroupSibling = (item) => ( + item !== original && item.visualSplitGroupId === groupId + ); + + return { + hasLeft: stack.some((item) => ( + isGroupSibling(item) && Math.abs((item.xU - shift) - xL) <= tolerance + )), + hasRight: stack.some((item) => ( + isGroupSibling(item) && Math.abs((item.xL - shift) - xU) <= tolerance + )), + }; +}; + +const buildSplitParts = (original, xL, splitX, xU, shift, data, stack) => { + if (!original.visualSplitGroupId) { + return [ + buildRawSplitPart(xL, splitX, shift, data), + buildRawSplitPart(splitX, xU, shift, data), + ]; + } + + const { hasLeft, hasRight } = findVisualSplitNeighborhood(stack, original, shift, xL, xU); + if (!hasLeft && !hasRight) { + return [ + buildRawSplitPart(xL, splitX, shift, data), + buildRawSplitPart(splitX, xU, shift, data), + ]; + } + + const groupId = original.visualSplitGroupId; + const { + leftArea, rightArea, leftAbs, rightAbs, + } = computeProportionalSplitAreas(xL, splitX, xU, data, original); + + const leftStaysInGroup = hasLeft; + const rightStaysInGroup = hasRight; + + const leftPart = leftStaysInGroup + ? buildVisualSplitItem(xL, splitX, shift, leftArea, leftAbs, groupId) + : buildSplitStackItem(xL, splitX, shift, leftArea, leftAbs); + const rightPart = rightStaysInGroup + ? buildVisualSplitItem(splitX, xU, shift, rightArea, rightAbs, groupId) + : buildSplitStackItem(splitX, xU, shift, rightArea, rightAbs); + + return [leftPart, rightPart]; +}; + +const splitStack = (state, action) => { + const { + curveIdx, target, splitX, data, + } = action.payload; + + if (!Number.isFinite(curveIdx) || !target || !Array.isArray(data)) { + return state; + } + + const { integrations } = state; + const selectedIntegration = integrations[curveIdx]; + if (!selectedIntegration || selectedIntegration === false) { + return state; + } + + const { stack, shift } = selectedIntegration; + const targetIndex = findTargetIntegrationIndex(stack, target); + if (targetIndex < 0 || !Number.isFinite(splitX)) { + return state; + } + + const original = stack[targetIndex]; + const [xL, xU] = [original.xL - shift, original.xU - shift].sort((a, b) => a - b); + if (!Number.isFinite(xL) || !Number.isFinite(xU) || splitX <= xL || splitX >= xU) { + return state; + } + + if (!hasEnoughDataResolution(xL, splitX, data) || !hasEnoughDataResolution(splitX, xU, data)) { + return state; + } + + const [leftIntegration, rightIntegration] = buildSplitParts( + original, xL, splitX, xU, shift, data, stack, + ); + + const newStack = dropOrphanVisualSplitGroupIds([ + ...stack.slice(0, targetIndex), + leftIntegration, + rightIntegration, + ...stack.slice(targetIndex + 1), + ]); + + const newIntegration = Object.assign({}, selectedIntegration, { stack: newStack }); + const newArrIntegration = [...integrations]; + newArrIntegration[curveIdx] = newIntegration; + + return Object.assign({}, state, { integrations: newArrIntegration, selectedIdx: curveIdx }); +}; + +const addVisualSplitLine = (state, action) => { + const { + curveIdx, target, splitX, data, + } = action.payload; + + if (!Number.isFinite(curveIdx) || !target || !Number.isFinite(splitX) || !Array.isArray(data)) { + return state; + } + + const { integrations } = state; + const selectedIntegration = integrations[curveIdx]; + if (!selectedIntegration || selectedIntegration === false) { + return state; + } + + const { stack, shift } = selectedIntegration; + const targetIndex = findTargetIntegrationIndex(stack, target); + if (targetIndex < 0) return state; + + const original = stack[targetIndex]; + + if (original.visualSplitGroupId || isVisuallySplit(stack, original)) { + return state; + } + + const [xL, xU] = [original.xL - shift, original.xU - shift].sort((a, b) => a - b); + const tolerance = getVisualSplitTolerance(xL, xU); + if (splitX <= xL + tolerance || splitX >= xU - tolerance) { + return state; + } + if (!hasEnoughDataResolution(xL, splitX, data) || !hasEnoughDataResolution(splitX, xU, data)) { + return state; + } + + const groupId = generateVisualSplitGroupId(); + const { + leftArea, rightArea, leftAbs, rightAbs, + } = computeProportionalSplitAreas(xL, splitX, xU, data, original); + const leftItem = buildVisualSplitItem(xL, splitX, shift, leftArea, leftAbs, groupId); + const rightItem = buildVisualSplitItem(splitX, xU, shift, rightArea, rightAbs, groupId); + + if (leftItem.xL >= leftItem.xU + || rightItem.xL >= rightItem.xU + || leftItem.xU !== rightItem.xL) { + return state; + } + + const newStack = [ + ...stack.slice(0, targetIndex), + leftItem, + rightItem, + ...stack.slice(targetIndex + 1), + ]; + + const newIntegration = Object.assign({}, selectedIntegration, { stack: newStack }); + const newArrIntegration = [...integrations]; + newArrIntegration[curveIdx] = newIntegration; + + return Object.assign({}, state, { integrations: newArrIntegration, selectedIdx: curveIdx }); +}; + +const removeVisualSplitLine = (state, action) => { + const { + curveIdx, splitX, data, + } = action.payload; + + if (!Number.isFinite(curveIdx) || !Number.isFinite(splitX) || !Array.isArray(data)) { + return state; + } + + const { integrations } = state; + const selectedIntegration = integrations[curveIdx]; + if (!selectedIntegration || selectedIntegration === false) { + return state; + } + + const { stack, shift } = selectedIntegration; + if (!Array.isArray(stack) || stack.length < 2) return state; + + const tolerance = getVisualSplitTolerance( + Math.min(...stack.map((s) => s.xL - shift)), + Math.max(...stack.map((s) => s.xU - shift)), + ); + + let mergeStartIdx = -1; + for (let i = 0; i < stack.length - 1; i += 1) { + const left = stack[i]; + const right = stack[i + 1]; + const gapTolerance = Math.max(tolerance, Math.abs(left.xU - right.xL)); + if ( + left.visualSplitGroupId + && left.visualSplitGroupId === right.visualSplitGroupId + && Math.abs((left.xU - shift) - splitX) <= gapTolerance + && Math.abs((right.xL - shift) - splitX) <= gapTolerance + ) { + mergeStartIdx = i; + break; + } + } + if (mergeStartIdx < 0) return state; + + const leftItem = stack[mergeStartIdx]; + const rightItem = stack[mergeStartIdx + 1]; + const groupId = leftItem.visualSplitGroupId; + const mergedXL = Math.min(leftItem.xL, rightItem.xL) - shift; + const mergedXU = Math.max(leftItem.xU, rightItem.xU) - shift; + + const remainingInGroup = stack.filter((item, idx) => ( + idx !== mergeStartIdx && idx !== mergeStartIdx + 1 && item.visualSplitGroupId === groupId + )); + const keepGroupId = remainingInGroup.length > 0; + const mergedItem = buildVisualSplitItem( + mergedXL, + mergedXU, + shift, + (leftItem.area || 0) + (rightItem.area || 0), + (leftItem.absoluteArea || 0) + (rightItem.absoluteArea || 0), + keepGroupId ? groupId : null, + ); + + const newStack = [ + ...stack.slice(0, mergeStartIdx), + mergedItem, + ...stack.slice(mergeStartIdx + 2), + ]; const newIntegration = Object.assign({}, selectedIntegration, { stack: newStack }); const newArrIntegration = [...integrations]; @@ -159,6 +489,12 @@ const integrationReducer = (state = initialState, action) => { return addToStack(state, action); case INTEGRATION.RM_ONE: return rmFromStack(state, action); + case INTEGRATION.SPLIT: + return splitStack(state, action); + case INTEGRATION.ADD_VISUAL_SPLIT: + return addVisualSplitLine(state, action); + case INTEGRATION.RM_VISUAL_SPLIT: + return removeVisualSplitLine(state, action); case INTEGRATION.SET_REF: return setRef(state, action); case INTEGRATION.SET_FKR: @@ -183,4 +519,6 @@ const undoableIntegrationReducer = undoable( undoRedoConfig, ); +export { integrationReducer }; + export default undoableIntegrationReducer; diff --git a/src/reducers/undo_redo_config.js b/src/reducers/undo_redo_config.js index 2c52e048..b24db61e 100644 --- a/src/reducers/undo_redo_config.js +++ b/src/reducers/undo_redo_config.js @@ -11,7 +11,8 @@ const undoRedoActions = [ MANAGER.RESET_INIT_MS, MANAGER.RESET_INIT_COMMON_WITH_INTERGATION, UI.SWEEP.SELECT_INTEGRATION, UI.SWEEP.SELECT_MULTIPLICITY_RDC, - INTEGRATION.RM_ONE, INTEGRATION.SET_REF, INTEGRATION.SET_FKR, + INTEGRATION.RM_ONE, INTEGRATION.SET_REF, INTEGRATION.SET_FKR, INTEGRATION.SPLIT, + INTEGRATION.ADD_VISUAL_SPLIT, INTEGRATION.RM_VISUAL_SPLIT, INTEGRATION.RESET_ALL, INTEGRATION.CLEAR_ALL, MULTIPLICITY.PEAK_RM_BY_PANEL_RDC, MULTIPLICITY.PEAK_RM_BY_UI_RDC, MULTIPLICITY.PEAK_ADD_BY_UI_RDC, MULTIPLICITY.RESET_ONE_RDC, @@ -23,7 +24,7 @@ const undoRedoActions = [ const undoRedoConfig = { debug: false, limit: 10, - ignoreInitialState: true, + ignoreInitialState: false, filter: includeAction(undoRedoActions), clearHistoryType: [ EDITPEAK.SHIFT, EDITPEAK.CLEAR_ALL,