react-cheminfo 0.69.0 → 0.71.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -2
- package/lib/about/core/providers.d.ts +6 -0
- package/lib/about/core/providers.d.ts.map +1 -1
- package/lib/about/core/providers.js +7 -0
- package/lib/about/core/providers.js.map +1 -1
- package/lib/about/ui/AboutProvidedBy.js +34 -0
- package/lib/about/ui/AboutProvidedBy.js.map +1 -1
- package/lib/chart/ui/useChartPointer.d.ts +9 -1
- package/lib/chart/ui/useChartPointer.d.ts.map +1 -1
- package/lib/chart/ui/useChartPointer.js +11 -3
- package/lib/chart/ui/useChartPointer.js.map +1 -1
- package/lib/credits/core/credits.d.ts +41 -0
- package/lib/credits/core/credits.d.ts.map +1 -1
- package/lib/credits/core/credits.js +50 -0
- package/lib/credits/core/credits.js.map +1 -1
- package/lib/download/core/drawingMarkup.d.ts +8 -1
- package/lib/download/core/drawingMarkup.d.ts.map +1 -1
- package/lib/download/core/drawingMarkup.js +13 -1
- package/lib/download/core/drawingMarkup.js.map +1 -1
- package/lib/download/core/figurePng.d.ts +14 -0
- package/lib/download/core/figurePng.d.ts.map +1 -1
- package/lib/download/core/figurePng.js +21 -6
- package/lib/download/core/figurePng.js.map +1 -1
- package/lib/download/core/figureSvg.d.ts.map +1 -1
- package/lib/download/core/figureSvg.js +5 -3
- package/lib/download/core/figureSvg.js.map +1 -1
- package/lib/download/core/figureTarget.d.ts +13 -0
- package/lib/download/core/figureTarget.d.ts.map +1 -1
- package/lib/download/core/figureTarget.js +25 -5
- package/lib/download/core/figureTarget.js.map +1 -1
- package/lib/download/core/svgBounds.d.ts +15 -0
- package/lib/download/core/svgBounds.d.ts.map +1 -1
- package/lib/download/core/svgBounds.js +20 -0
- package/lib/download/core/svgBounds.js.map +1 -1
- package/lib/download/core/svgToPng.d.ts.map +1 -1
- package/lib/download/core/svgToPng.js +4 -2
- package/lib/download/core/svgToPng.js.map +1 -1
- package/lib/format/core/relativeTime.d.ts +8 -3
- package/lib/format/core/relativeTime.d.ts.map +1 -1
- package/lib/format/core/relativeTime.js +19 -7
- package/lib/format/core/relativeTime.js.map +1 -1
- package/lib/host/core/connectHost.d.ts +25 -34
- package/lib/host/core/connectHost.d.ts.map +1 -1
- package/lib/host/core/connectHost.js +43 -89
- package/lib/host/core/connectHost.js.map +1 -1
- package/lib/host/core/hostOutputs.d.ts +58 -0
- package/lib/host/core/hostOutputs.d.ts.map +1 -0
- package/lib/host/core/hostOutputs.js +151 -0
- package/lib/host/core/hostOutputs.js.map +1 -0
- package/lib/host/core/index.d.ts +9 -3
- package/lib/host/core/index.d.ts.map +1 -1
- package/lib/host/core/index.js +4 -1
- package/lib/host/core/index.js.map +1 -1
- package/lib/host/core/irKinds.d.ts +124 -0
- package/lib/host/core/irKinds.d.ts.map +1 -0
- package/lib/host/core/irKinds.js +46 -0
- package/lib/host/core/irKinds.js.map +1 -0
- package/lib/host/core/kinds.d.ts +13 -5
- package/lib/host/core/kinds.d.ts.map +1 -1
- package/lib/host/core/kinds.js +11 -5
- package/lib/host/core/kinds.js.map +1 -1
- package/lib/host/core/protocol.d.ts +50 -7
- package/lib/host/core/protocol.d.ts.map +1 -1
- package/lib/host/core/protocol.js +10 -6
- package/lib/host/core/protocol.js.map +1 -1
- package/lib/host/core/selectInputs.d.ts +37 -0
- package/lib/host/core/selectInputs.d.ts.map +1 -0
- package/lib/host/core/selectInputs.js +110 -0
- package/lib/host/core/selectInputs.js.map +1 -0
- package/lib/host/testing/sciPeaks.d.ts +31 -0
- package/lib/host/testing/sciPeaks.d.ts.map +1 -1
- package/lib/host/testing/sciPeaks.js +73 -0
- package/lib/host/testing/sciPeaks.js.map +1 -1
- package/lib/host/testing/standInHost.d.ts +9 -1
- package/lib/host/testing/standInHost.d.ts.map +1 -1
- package/lib/host/testing/standInHost.js +19 -1
- package/lib/host/testing/standInHost.js.map +1 -1
- package/lib/host/ui/useHostConnection.d.ts +13 -5
- package/lib/host/ui/useHostConnection.d.ts.map +1 -1
- package/lib/host/ui/useHostConnection.js +4 -3
- package/lib/host/ui/useHostConnection.js.map +1 -1
- package/lib/host-testing.d.ts +2 -2
- package/lib/host-testing.d.ts.map +1 -1
- package/lib/host-testing.js +2 -2
- package/lib/host-testing.js.map +1 -1
- package/lib/i18n/core/chromeCatalog.d.ts +1 -1
- package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
- package/lib/ir/core/handPicking.d.ts +58 -0
- package/lib/ir/core/handPicking.d.ts.map +1 -0
- package/lib/ir/core/handPicking.js +130 -0
- package/lib/ir/core/handPicking.js.map +1 -0
- package/lib/ir/core/index.d.ts +3 -0
- package/lib/ir/core/index.d.ts.map +1 -1
- package/lib/ir/core/index.js +3 -0
- package/lib/ir/core/index.js.map +1 -1
- package/lib/ir/core/irBandState.d.ts +57 -0
- package/lib/ir/core/irBandState.d.ts.map +1 -0
- package/lib/ir/core/irBandState.js +119 -0
- package/lib/ir/core/irBandState.js.map +1 -0
- package/lib/ir/core/irCommands.d.ts +11 -0
- package/lib/ir/core/irCommands.d.ts.map +1 -1
- package/lib/ir/core/irCommands.js +6 -0
- package/lib/ir/core/irCommands.js.map +1 -1
- package/lib/ir/core/irPeaks.d.ts +28 -0
- package/lib/ir/core/irPeaks.d.ts.map +1 -0
- package/lib/ir/core/irPeaks.js +66 -0
- package/lib/ir/core/irPeaks.js.map +1 -0
- package/lib/ir/core/irReducer.d.ts +8 -0
- package/lib/ir/core/irReducer.d.ts.map +1 -1
- package/lib/ir/core/irReducer.js +15 -32
- package/lib/ir/core/irReducer.js.map +1 -1
- package/lib/ir/core/irSpectrum.d.ts +15 -0
- package/lib/ir/core/irSpectrum.d.ts.map +1 -1
- package/lib/ir/core/irSpectrum.js.map +1 -1
- package/lib/ir/core/irState.d.ts +9 -6
- package/lib/ir/core/irState.d.ts.map +1 -1
- package/lib/ir/core/irState.js +10 -6
- package/lib/ir/core/irState.js.map +1 -1
- package/lib/ir/core/pickBands.d.ts +14 -0
- package/lib/ir/core/pickBands.d.ts.map +1 -1
- package/lib/ir/core/pickBands.js +39 -33
- package/lib/ir/core/pickBands.js.map +1 -1
- package/lib/ir/core/ramanBands.d.ts +9 -1
- package/lib/ir/core/ramanBands.d.ts.map +1 -1
- package/lib/ir/core/ramanBands.js +2 -2
- package/lib/ir/core/ramanBands.js.map +1 -1
- package/lib/ir/core/spectrumBands.d.ts +46 -0
- package/lib/ir/core/spectrumBands.d.ts.map +1 -0
- package/lib/ir/core/spectrumBands.js +98 -0
- package/lib/ir/core/spectrumBands.js.map +1 -0
- package/lib/ir/ui/BandRow.d.ts +5 -0
- package/lib/ir/ui/BandRow.d.ts.map +1 -1
- package/lib/ir/ui/BandRow.js +4 -3
- package/lib/ir/ui/BandRow.js.map +1 -1
- package/lib/ir/ui/BandsPanel.d.ts +5 -0
- package/lib/ir/ui/BandsPanel.d.ts.map +1 -1
- package/lib/ir/ui/BandsPanel.js +36 -16
- package/lib/ir/ui/BandsPanel.js.map +1 -1
- package/lib/ir/ui/BandsToolbar.d.ts +9 -0
- package/lib/ir/ui/BandsToolbar.d.ts.map +1 -0
- package/lib/ir/ui/BandsToolbar.js +61 -0
- package/lib/ir/ui/BandsToolbar.js.map +1 -0
- package/lib/ir/ui/IrCanvas.d.ts +2 -1
- package/lib/ir/ui/IrCanvas.d.ts.map +1 -1
- package/lib/ir/ui/IrCanvas.js +19 -5
- package/lib/ir/ui/IrCanvas.js.map +1 -1
- package/lib/ir/ui/IrChart.d.ts.map +1 -1
- package/lib/ir/ui/IrChart.js +8 -1
- package/lib/ir/ui/IrChart.js.map +1 -1
- package/lib/ir/ui/IrDocumentationDialog.js +1 -1
- package/lib/ir/ui/IrDocumentationDialog.js.map +1 -1
- package/lib/ir/ui/IrEditor.d.ts +4 -2
- package/lib/ir/ui/IrEditor.d.ts.map +1 -1
- package/lib/ir/ui/IrEditor.js.map +1 -1
- package/lib/ir/ui/IrShell.d.ts.map +1 -1
- package/lib/ir/ui/IrShell.js +6 -0
- package/lib/ir/ui/IrShell.js.map +1 -1
- package/lib/ir/ui/IrStatusBar.d.ts.map +1 -1
- package/lib/ir/ui/IrStatusBar.js +1 -1
- package/lib/ir/ui/IrStatusBar.js.map +1 -1
- package/lib/ir/ui/IrToolbar.d.ts +4 -3
- package/lib/ir/ui/IrToolbar.d.ts.map +1 -1
- package/lib/ir/ui/IrToolbar.js +5 -4
- package/lib/ir/ui/IrToolbar.js.map +1 -1
- package/lib/ir/ui/VibrationalEditor.d.ts +2 -1
- package/lib/ir/ui/VibrationalEditor.d.ts.map +1 -1
- package/lib/ir/ui/VibrationalEditor.js.map +1 -1
- package/lib/ir/ui/irActions.d.ts +5 -0
- package/lib/ir/ui/irActions.d.ts.map +1 -1
- package/lib/ir/ui/irActions.js +6 -0
- package/lib/ir/ui/irActions.js.map +1 -1
- package/lib/ir/ui/irChartProps.d.ts +4 -1
- package/lib/ir/ui/irChartProps.d.ts.map +1 -1
- package/lib/ir/ui/useIrEditor.d.ts +5 -11
- package/lib/ir/ui/useIrEditor.d.ts.map +1 -1
- package/lib/ir/ui/useIrEditor.js +17 -18
- package/lib/ir/ui/useIrEditor.js.map +1 -1
- package/lib/locales/de.json +63 -0
- package/lib/locales/en.json +63 -0
- package/lib/locales/es.json +63 -0
- package/lib/locales/fr.json +63 -0
- package/lib/locales/it.json +63 -0
- package/lib/mass/core/analyseSpectrum.d.ts +85 -0
- package/lib/mass/core/analyseSpectrum.d.ts.map +1 -0
- package/lib/mass/core/analyseSpectrum.js +101 -0
- package/lib/mass/core/analyseSpectrum.js.map +1 -0
- package/lib/mass/core/ascendingTrace.d.ts +24 -0
- package/lib/mass/core/ascendingTrace.d.ts.map +1 -0
- package/lib/mass/core/ascendingTrace.js +57 -0
- package/lib/mass/core/ascendingTrace.js.map +1 -0
- package/lib/mass/core/centroidPeaks.d.ts +77 -0
- package/lib/mass/core/centroidPeaks.d.ts.map +1 -0
- package/lib/mass/core/centroidPeaks.js +62 -0
- package/lib/mass/core/centroidPeaks.js.map +1 -0
- package/lib/mass/core/detectMassFormat.d.ts +24 -0
- package/lib/mass/core/detectMassFormat.d.ts.map +1 -0
- package/lib/mass/core/detectMassFormat.js +105 -0
- package/lib/mass/core/detectMassFormat.js.map +1 -0
- package/lib/mass/core/downloadSpectra.d.ts +24 -0
- package/lib/mass/core/downloadSpectra.d.ts.map +1 -0
- package/lib/mass/core/downloadSpectra.js +35 -0
- package/lib/mass/core/downloadSpectra.js.map +1 -0
- package/lib/mass/core/fromJcamp.d.ts +46 -0
- package/lib/mass/core/fromJcamp.d.ts.map +1 -0
- package/lib/mass/core/fromJcamp.js +123 -0
- package/lib/mass/core/fromJcamp.js.map +1 -0
- package/lib/mass/core/fromText.d.ts +30 -0
- package/lib/mass/core/fromText.d.ts.map +1 -0
- package/lib/mass/core/fromText.js +39 -0
- package/lib/mass/core/fromText.js.map +1 -0
- package/lib/mass/core/index.d.ts +70 -0
- package/lib/mass/core/index.d.ts.map +1 -0
- package/lib/mass/core/index.js +42 -0
- package/lib/mass/core/index.js.map +1 -0
- package/lib/mass/core/jcampTechnique.d.ts +38 -0
- package/lib/mass/core/jcampTechnique.d.ts.map +1 -0
- package/lib/mass/core/jcampTechnique.js +70 -0
- package/lib/mass/core/jcampTechnique.js.map +1 -0
- package/lib/mass/core/labelStackBox.d.ts +49 -0
- package/lib/mass/core/labelStackBox.d.ts.map +1 -0
- package/lib/mass/core/labelStackBox.js +44 -0
- package/lib/mass/core/labelStackBox.js.map +1 -0
- package/lib/mass/core/labelledPeaks.d.ts +79 -0
- package/lib/mass/core/labelledPeaks.d.ts.map +1 -0
- package/lib/mass/core/labelledPeaks.js +74 -0
- package/lib/mass/core/labelledPeaks.js.map +1 -0
- package/lib/mass/core/loadMassSpectra.d.ts +57 -0
- package/lib/mass/core/loadMassSpectra.d.ts.map +1 -0
- package/lib/mass/core/loadMassSpectra.js +45 -0
- package/lib/mass/core/loadMassSpectra.js.map +1 -0
- package/lib/mass/core/loadedMassSpectrum.d.ts +42 -0
- package/lib/mass/core/loadedMassSpectrum.d.ts.map +1 -0
- package/lib/mass/core/loadedMassSpectrum.js +15 -0
- package/lib/mass/core/loadedMassSpectrum.js.map +1 -0
- package/lib/mass/core/massCommands.d.ts +170 -0
- package/lib/mass/core/massCommands.d.ts.map +1 -0
- package/lib/mass/core/massCommands.js +95 -0
- package/lib/mass/core/massCommands.js.map +1 -0
- package/lib/mass/core/massDomain.d.ts +121 -0
- package/lib/mass/core/massDomain.d.ts.map +1 -0
- package/lib/mass/core/massDomain.js +179 -0
- package/lib/mass/core/massDomain.js.map +1 -0
- package/lib/mass/core/massEditorApi.d.ts +65 -0
- package/lib/mass/core/massEditorApi.d.ts.map +1 -0
- package/lib/mass/core/massEditorApi.js +10 -0
- package/lib/mass/core/massEditorApi.js.map +1 -0
- package/lib/mass/core/massFormat.d.ts +78 -0
- package/lib/mass/core/massFormat.d.ts.map +1 -0
- package/lib/mass/core/massFormat.js +168 -0
- package/lib/mass/core/massFormat.js.map +1 -0
- package/lib/mass/core/massMeta.d.ts +85 -0
- package/lib/mass/core/massMeta.d.ts.map +1 -0
- package/lib/mass/core/massMeta.js +182 -0
- package/lib/mass/core/massMeta.js.map +1 -0
- package/lib/mass/core/massReducer.d.ts +82 -0
- package/lib/mass/core/massReducer.d.ts.map +1 -0
- package/lib/mass/core/massReducer.js +163 -0
- package/lib/mass/core/massReducer.js.map +1 -0
- package/lib/mass/core/massResolution.d.ts +92 -0
- package/lib/mass/core/massResolution.d.ts.map +1 -0
- package/lib/mass/core/massResolution.js +126 -0
- package/lib/mass/core/massResolution.js.map +1 -0
- package/lib/mass/core/massSidePanels.d.ts +28 -0
- package/lib/mass/core/massSidePanels.d.ts.map +1 -0
- package/lib/mass/core/massSidePanels.js +12 -0
- package/lib/mass/core/massSidePanels.js.map +1 -0
- package/lib/mass/core/massSpectrum.d.ts +206 -0
- package/lib/mass/core/massSpectrum.d.ts.map +1 -0
- package/lib/mass/core/massSpectrum.js +41 -0
- package/lib/mass/core/massSpectrum.js.map +1 -0
- package/lib/mass/core/massState.d.ts +145 -0
- package/lib/mass/core/massState.d.ts.map +1 -0
- package/lib/mass/core/massState.js +102 -0
- package/lib/mass/core/massState.js.map +1 -0
- package/lib/mass/core/massStateEdits.d.ts +51 -0
- package/lib/mass/core/massStateEdits.d.ts.map +1 -0
- package/lib/mass/core/massStateEdits.js +90 -0
- package/lib/mass/core/massStateEdits.js.map +1 -0
- package/lib/mass/core/massSummary.d.ts +77 -0
- package/lib/mass/core/massSummary.d.ts.map +1 -0
- package/lib/mass/core/massSummary.js +174 -0
- package/lib/mass/core/massSummary.js.map +1 -0
- package/lib/mass/core/mzDecimalsSetting.d.ts +36 -0
- package/lib/mass/core/mzDecimalsSetting.d.ts.map +1 -0
- package/lib/mass/core/mzDecimalsSetting.js +42 -0
- package/lib/mass/core/mzDecimalsSetting.js.map +1 -0
- package/lib/mass/core/nearestPeak.d.ts +75 -0
- package/lib/mass/core/nearestPeak.d.ts.map +1 -0
- package/lib/mass/core/nearestPeak.js +99 -0
- package/lib/mass/core/nearestPeak.js.map +1 -0
- package/lib/mass/core/peakLabelLines.d.ts +77 -0
- package/lib/mass/core/peakLabelLines.d.ts.map +1 -0
- package/lib/mass/core/peakLabelLines.js +54 -0
- package/lib/mass/core/peakLabelLines.js.map +1 -0
- package/lib/mass/core/peakLabelPlan.d.ts +109 -0
- package/lib/mass/core/peakLabelPlan.d.ts.map +1 -0
- package/lib/mass/core/peakLabelPlan.js +143 -0
- package/lib/mass/core/peakLabelPlan.js.map +1 -0
- package/lib/mass/core/peakTable.d.ts +54 -0
- package/lib/mass/core/peakTable.d.ts.map +1 -0
- package/lib/mass/core/peakTable.js +53 -0
- package/lib/mass/core/peakTable.js.map +1 -0
- package/lib/mass/core/resolutionOf.d.ts +60 -0
- package/lib/mass/core/resolutionOf.d.ts.map +1 -0
- package/lib/mass/core/resolutionOf.js +84 -0
- package/lib/mass/core/resolutionOf.js.map +1 -0
- package/lib/mass/core/scanFilter.d.ts +25 -0
- package/lib/mass/core/scanFilter.d.ts.map +1 -0
- package/lib/mass/core/scanFilter.js +107 -0
- package/lib/mass/core/scanFilter.js.map +1 -0
- package/lib/mass/core/spectrumJcamp.d.ts +47 -0
- package/lib/mass/core/spectrumJcamp.d.ts.map +1 -0
- package/lib/mass/core/spectrumJcamp.js +167 -0
- package/lib/mass/core/spectrumJcamp.js.map +1 -0
- package/lib/mass/core/toMassSpectrum.d.ts +67 -0
- package/lib/mass/core/toMassSpectrum.d.ts.map +1 -0
- package/lib/mass/core/toMassSpectrum.js +78 -0
- package/lib/mass/core/toMassSpectrum.js.map +1 -0
- package/lib/mass/core/traceIcon.d.ts +82 -0
- package/lib/mass/core/traceIcon.d.ts.map +1 -0
- package/lib/mass/core/traceIcon.js +66 -0
- package/lib/mass/core/traceIcon.js.map +1 -0
- package/lib/mass/core/writtenResolution.d.ts +40 -0
- package/lib/mass/core/writtenResolution.d.ts.map +1 -0
- package/lib/mass/core/writtenResolution.js +77 -0
- package/lib/mass/core/writtenResolution.js.map +1 -0
- package/lib/mass/ui/MassAboutDialog.d.ts +16 -0
- package/lib/mass/ui/MassAboutDialog.d.ts.map +1 -0
- package/lib/mass/ui/MassAboutDialog.js +44 -0
- package/lib/mass/ui/MassAboutDialog.js.map +1 -0
- package/lib/mass/ui/MassCanvas.d.ts +46 -0
- package/lib/mass/ui/MassCanvas.d.ts.map +1 -0
- package/lib/mass/ui/MassCanvas.js +84 -0
- package/lib/mass/ui/MassCanvas.js.map +1 -0
- package/lib/mass/ui/MassChart.d.ts +21 -0
- package/lib/mass/ui/MassChart.d.ts.map +1 -0
- package/lib/mass/ui/MassChart.js +139 -0
- package/lib/mass/ui/MassChart.js.map +1 -0
- package/lib/mass/ui/MassCommandProvider.d.ts +15 -0
- package/lib/mass/ui/MassCommandProvider.d.ts.map +1 -0
- package/lib/mass/ui/MassCommandProvider.js +14 -0
- package/lib/mass/ui/MassCommandProvider.js.map +1 -0
- package/lib/mass/ui/MassCommandTooltip.d.ts +16 -0
- package/lib/mass/ui/MassCommandTooltip.d.ts.map +1 -0
- package/lib/mass/ui/MassCommandTooltip.js +22 -0
- package/lib/mass/ui/MassCommandTooltip.js.map +1 -0
- package/lib/mass/ui/MassDocumentationDialog.d.ts +18 -0
- package/lib/mass/ui/MassDocumentationDialog.d.ts.map +1 -0
- package/lib/mass/ui/MassDocumentationDialog.js +70 -0
- package/lib/mass/ui/MassDocumentationDialog.js.map +1 -0
- package/lib/mass/ui/MassEditor.d.ts +78 -0
- package/lib/mass/ui/MassEditor.d.ts.map +1 -0
- package/lib/mass/ui/MassEditor.js +42 -0
- package/lib/mass/ui/MassEditor.js.map +1 -0
- package/lib/mass/ui/MassHeader.d.ts +12 -0
- package/lib/mass/ui/MassHeader.d.ts.map +1 -0
- package/lib/mass/ui/MassHeader.js +20 -0
- package/lib/mass/ui/MassHeader.js.map +1 -0
- package/lib/mass/ui/MassLogo.d.ts +21 -0
- package/lib/mass/ui/MassLogo.d.ts.map +1 -0
- package/lib/mass/ui/MassLogo.js +36 -0
- package/lib/mass/ui/MassLogo.js.map +1 -0
- package/lib/mass/ui/MassPointerTracker.d.ts +44 -0
- package/lib/mass/ui/MassPointerTracker.d.ts.map +1 -0
- package/lib/mass/ui/MassPointerTracker.js +29 -0
- package/lib/mass/ui/MassPointerTracker.js.map +1 -0
- package/lib/mass/ui/MassShell.d.ts +29 -0
- package/lib/mass/ui/MassShell.d.ts.map +1 -0
- package/lib/mass/ui/MassShell.js +133 -0
- package/lib/mass/ui/MassShell.js.map +1 -0
- package/lib/mass/ui/MassSidePanel.d.ts +32 -0
- package/lib/mass/ui/MassSidePanel.d.ts.map +1 -0
- package/lib/mass/ui/MassSidePanel.js +44 -0
- package/lib/mass/ui/MassSidePanel.js.map +1 -0
- package/lib/mass/ui/MassStateProvider.d.ts +19 -0
- package/lib/mass/ui/MassStateProvider.d.ts.map +1 -0
- package/lib/mass/ui/MassStateProvider.js +16 -0
- package/lib/mass/ui/MassStateProvider.js.map +1 -0
- package/lib/mass/ui/MassStatusBar.d.ts +15 -0
- package/lib/mass/ui/MassStatusBar.d.ts.map +1 -0
- package/lib/mass/ui/MassStatusBar.js +50 -0
- package/lib/mass/ui/MassStatusBar.js.map +1 -0
- package/lib/mass/ui/MassToolbar.d.ts +24 -0
- package/lib/mass/ui/MassToolbar.d.ts.map +1 -0
- package/lib/mass/ui/MassToolbar.js +36 -0
- package/lib/mass/ui/MassToolbar.js.map +1 -0
- package/lib/mass/ui/MetadataPanel.d.ts +35 -0
- package/lib/mass/ui/MetadataPanel.d.ts.map +1 -0
- package/lib/mass/ui/MetadataPanel.js +100 -0
- package/lib/mass/ui/MetadataPanel.js.map +1 -0
- package/lib/mass/ui/MzDecimalsMenu.d.ts +28 -0
- package/lib/mass/ui/MzDecimalsMenu.d.ts.map +1 -0
- package/lib/mass/ui/MzDecimalsMenu.js +40 -0
- package/lib/mass/ui/MzDecimalsMenu.js.map +1 -0
- package/lib/mass/ui/PeakLabels.d.ts +93 -0
- package/lib/mass/ui/PeakLabels.d.ts.map +1 -0
- package/lib/mass/ui/PeakLabels.js +91 -0
- package/lib/mass/ui/PeakLabels.js.map +1 -0
- package/lib/mass/ui/PeakTable.d.ts +34 -0
- package/lib/mass/ui/PeakTable.d.ts.map +1 -0
- package/lib/mass/ui/PeakTable.js +49 -0
- package/lib/mass/ui/PeakTable.js.map +1 -0
- package/lib/mass/ui/PeaksPanel.d.ts +31 -0
- package/lib/mass/ui/PeaksPanel.d.ts.map +1 -0
- package/lib/mass/ui/PeaksPanel.js +53 -0
- package/lib/mass/ui/PeaksPanel.js.map +1 -0
- package/lib/mass/ui/SpectraPanel.d.ts +17 -0
- package/lib/mass/ui/SpectraPanel.d.ts.map +1 -0
- package/lib/mass/ui/SpectraPanel.js +76 -0
- package/lib/mass/ui/SpectraPanel.js.map +1 -0
- package/lib/mass/ui/SpectrumRow.d.ts +26 -0
- package/lib/mass/ui/SpectrumRow.d.ts.map +1 -0
- package/lib/mass/ui/SpectrumRow.js +141 -0
- package/lib/mass/ui/SpectrumRow.js.map +1 -0
- package/lib/mass/ui/SpectrumTrace.d.ts +46 -0
- package/lib/mass/ui/SpectrumTrace.d.ts.map +1 -0
- package/lib/mass/ui/SpectrumTrace.js +68 -0
- package/lib/mass/ui/SpectrumTrace.js.map +1 -0
- package/lib/mass/ui/index.d.ts +69 -0
- package/lib/mass/ui/index.d.ts.map +1 -0
- package/lib/mass/ui/index.js +42 -0
- package/lib/mass/ui/index.js.map +1 -0
- package/lib/mass/ui/massActions.d.ts +52 -0
- package/lib/mass/ui/massActions.d.ts.map +1 -0
- package/lib/mass/ui/massActions.js +62 -0
- package/lib/mass/ui/massActions.js.map +1 -0
- package/lib/mass/ui/massChartProps.d.ts +128 -0
- package/lib/mass/ui/massChartProps.d.ts.map +1 -0
- package/lib/mass/ui/massChartProps.js +14 -0
- package/lib/mass/ui/massChartProps.js.map +1 -0
- package/lib/mass/ui/massCommandContext.d.ts +36 -0
- package/lib/mass/ui/massCommandContext.d.ts.map +1 -0
- package/lib/mass/ui/massCommandContext.js +45 -0
- package/lib/mass/ui/massCommandContext.js.map +1 -0
- package/lib/mass/ui/massHostPanels.d.ts +48 -0
- package/lib/mass/ui/massHostPanels.d.ts.map +1 -0
- package/lib/mass/ui/massHostPanels.js +41 -0
- package/lib/mass/ui/massHostPanels.js.map +1 -0
- package/lib/mass/ui/massStateContext.d.ts +26 -0
- package/lib/mass/ui/massStateContext.d.ts.map +1 -0
- package/lib/mass/ui/massStateContext.js +37 -0
- package/lib/mass/ui/massStateContext.js.map +1 -0
- package/lib/mass/ui/useIntensityZoom.d.ts +20 -0
- package/lib/mass/ui/useIntensityZoom.d.ts.map +1 -0
- package/lib/mass/ui/useIntensityZoom.js +42 -0
- package/lib/mass/ui/useIntensityZoom.js.map +1 -0
- package/lib/mass/ui/useMassEditor.d.ts +52 -0
- package/lib/mass/ui/useMassEditor.d.ts.map +1 -0
- package/lib/mass/ui/useMassEditor.js +93 -0
- package/lib/mass/ui/useMassEditor.js.map +1 -0
- package/lib/mass/ui/useMassFiles.d.ts +22 -0
- package/lib/mass/ui/useMassFiles.d.ts.map +1 -0
- package/lib/mass/ui/useMassFiles.js +71 -0
- package/lib/mass/ui/useMassFiles.js.map +1 -0
- package/lib/mass/ui/useMassHandle.d.ts +15 -0
- package/lib/mass/ui/useMassHandle.d.ts.map +1 -0
- package/lib/mass/ui/useMassHandle.js +35 -0
- package/lib/mass/ui/useMassHandle.js.map +1 -0
- package/lib/mass/ui/useMassPointer.d.ts +70 -0
- package/lib/mass/ui/useMassPointer.d.ts.map +1 -0
- package/lib/mass/ui/useMassPointer.js +57 -0
- package/lib/mass/ui/useMassPointer.js.map +1 -0
- package/lib/mass/ui/useMassShortcuts.d.ts +21 -0
- package/lib/mass/ui/useMassShortcuts.d.ts.map +1 -0
- package/lib/mass/ui/useMassShortcuts.js +66 -0
- package/lib/mass/ui/useMassShortcuts.js.map +1 -0
- package/lib/mass/ui/useMassSide.d.ts +27 -0
- package/lib/mass/ui/useMassSide.d.ts.map +1 -0
- package/lib/mass/ui/useMassSide.js +45 -0
- package/lib/mass/ui/useMassSide.js.map +1 -0
- package/lib/mass/ui/useMassZoom.d.ts +92 -0
- package/lib/mass/ui/useMassZoom.d.ts.map +1 -0
- package/lib/mass/ui/useMassZoom.js +65 -0
- package/lib/mass/ui/useMassZoom.js.map +1 -0
- package/lib/mass.d.ts +16 -0
- package/lib/mass.d.ts.map +1 -0
- package/lib/mass.js +16 -0
- package/lib/mass.js.map +1 -0
- package/lib/panel/ui/PanelRail.d.ts.map +1 -1
- package/lib/panel/ui/PanelRail.js +6 -1
- package/lib/panel/ui/PanelRail.js.map +1 -1
- package/lib/slides/ui/slideSanitizeSchema.d.ts +3 -2
- package/lib/slides/ui/slideSanitizeSchema.d.ts.map +1 -1
- package/lib/slides/ui/slideSanitizeSchema.js +82 -2
- package/lib/slides/ui/slideSanitizeSchema.js.map +1 -1
- package/package.json +6 -1
- package/src/about/core/providers.ts +7 -0
- package/src/about/ui/AboutProvidedBy.tsx +49 -0
- package/src/chart/ui/useChartPointer.ts +18 -4
- package/src/credits/core/credits.ts +53 -0
- package/src/download/core/drawingMarkup.ts +18 -1
- package/src/download/core/figurePng.ts +21 -8
- package/src/download/core/figureSvg.ts +5 -2
- package/src/download/core/figureTarget.ts +28 -3
- package/src/download/core/svgBounds.ts +21 -0
- package/src/download/core/svgToPng.ts +3 -4
- package/src/format/core/relativeTime.ts +29 -7
- package/src/host/core/connectHost.ts +77 -120
- package/src/host/core/hostOutputs.ts +197 -0
- package/src/host/core/index.ts +13 -7
- package/src/host/core/irKinds.ts +92 -0
- package/src/host/core/kinds.ts +16 -6
- package/src/host/core/protocol.ts +50 -8
- package/src/host/core/selectInputs.ts +145 -0
- package/src/host/testing/sciPeaks.ts +140 -0
- package/src/host/testing/standInHost.ts +30 -5
- package/src/host/ui/useHostConnection.ts +17 -8
- package/src/host-testing.ts +4 -0
- package/src/ir/core/handPicking.ts +154 -0
- package/src/ir/core/index.ts +14 -0
- package/src/ir/core/irBandState.ts +155 -0
- package/src/ir/core/irCommands.ts +7 -0
- package/src/ir/core/irPeaks.ts +79 -0
- package/src/ir/core/irReducer.ts +27 -34
- package/src/ir/core/irSpectrum.ts +16 -0
- package/src/ir/core/irState.ts +15 -8
- package/src/ir/core/pickBands.ts +46 -39
- package/src/ir/core/ramanBands.ts +2 -2
- package/src/ir/core/spectrumBands.ts +113 -0
- package/src/ir/ui/BandRow.tsx +20 -2
- package/src/ir/ui/BandsPanel.tsx +46 -88
- package/src/ir/ui/BandsToolbar.tsx +152 -0
- package/src/ir/ui/IrCanvas.tsx +22 -11
- package/src/ir/ui/IrChart.tsx +10 -1
- package/src/ir/ui/IrDocumentationDialog.tsx +7 -0
- package/src/ir/ui/IrEditor.tsx +4 -2
- package/src/ir/ui/IrShell.tsx +5 -0
- package/src/ir/ui/IrStatusBar.tsx +4 -1
- package/src/ir/ui/IrToolbar.tsx +14 -5
- package/src/ir/ui/VibrationalEditor.tsx +2 -1
- package/src/ir/ui/irActions.ts +11 -0
- package/src/ir/ui/irChartProps.ts +4 -1
- package/src/ir/ui/useIrEditor.ts +21 -21
- package/src/locales/de.json +63 -0
- package/src/locales/en.json +63 -0
- package/src/locales/es.json +63 -0
- package/src/locales/fr.json +63 -0
- package/src/locales/it.json +63 -0
- package/src/mass/core/analyseSpectrum.ts +191 -0
- package/src/mass/core/ascendingTrace.ts +62 -0
- package/src/mass/core/centroidPeaks.ts +114 -0
- package/src/mass/core/detectMassFormat.ts +113 -0
- package/src/mass/core/downloadSpectra.ts +52 -0
- package/src/mass/core/fromJcamp.ts +151 -0
- package/src/mass/core/fromText.ts +58 -0
- package/src/mass/core/index.ts +194 -0
- package/src/mass/core/jcampTechnique.ts +87 -0
- package/src/mass/core/labelStackBox.ts +75 -0
- package/src/mass/core/labelledPeaks.ts +135 -0
- package/src/mass/core/loadMassSpectra.ts +86 -0
- package/src/mass/core/loadedMassSpectrum.ts +43 -0
- package/src/mass/core/massCommands.ts +156 -0
- package/src/mass/core/massDomain.ts +229 -0
- package/src/mass/core/massEditorApi.ts +68 -0
- package/src/mass/core/massFormat.ts +187 -0
- package/src/mass/core/massMeta.ts +239 -0
- package/src/mass/core/massReducer.ts +232 -0
- package/src/mass/core/massResolution.ts +169 -0
- package/src/mass/core/massSidePanels.ts +27 -0
- package/src/mass/core/massSpectrum.ts +220 -0
- package/src/mass/core/massState.ts +206 -0
- package/src/mass/core/massStateEdits.ts +104 -0
- package/src/mass/core/massSummary.ts +213 -0
- package/src/mass/core/mzDecimalsSetting.ts +52 -0
- package/src/mass/core/nearestPeak.ts +130 -0
- package/src/mass/core/peakLabelLines.ts +112 -0
- package/src/mass/core/peakLabelPlan.ts +246 -0
- package/src/mass/core/peakTable.ts +82 -0
- package/src/mass/core/resolutionOf.ts +100 -0
- package/src/mass/core/scanFilter.ts +119 -0
- package/src/mass/core/spectrumJcamp.ts +186 -0
- package/src/mass/core/toMassSpectrum.ts +126 -0
- package/src/mass/core/traceIcon.ts +105 -0
- package/src/mass/core/writtenResolution.ts +81 -0
- package/src/mass/ui/MassAboutDialog.tsx +89 -0
- package/src/mass/ui/MassCanvas.tsx +170 -0
- package/src/mass/ui/MassChart.tsx +266 -0
- package/src/mass/ui/MassCommandProvider.tsx +34 -0
- package/src/mass/ui/MassCommandTooltip.tsx +36 -0
- package/src/mass/ui/MassDocumentationDialog.tsx +136 -0
- package/src/mass/ui/MassEditor.tsx +130 -0
- package/src/mass/ui/MassHeader.tsx +38 -0
- package/src/mass/ui/MassLogo.tsx +84 -0
- package/src/mass/ui/MassPointerTracker.tsx +72 -0
- package/src/mass/ui/MassShell.tsx +237 -0
- package/src/mass/ui/MassSidePanel.tsx +107 -0
- package/src/mass/ui/MassStateProvider.tsx +32 -0
- package/src/mass/ui/MassStatusBar.tsx +108 -0
- package/src/mass/ui/MassToolbar.tsx +120 -0
- package/src/mass/ui/MetadataPanel.tsx +201 -0
- package/src/mass/ui/MzDecimalsMenu.tsx +86 -0
- package/src/mass/ui/PeakLabels.tsx +242 -0
- package/src/mass/ui/PeakTable.tsx +199 -0
- package/src/mass/ui/PeaksPanel.tsx +182 -0
- package/src/mass/ui/SpectraPanel.tsx +153 -0
- package/src/mass/ui/SpectrumRow.tsx +246 -0
- package/src/mass/ui/SpectrumTrace.tsx +119 -0
- package/src/mass/ui/index.ts +69 -0
- package/src/mass/ui/massActions.ts +109 -0
- package/src/mass/ui/massChartProps.ts +130 -0
- package/src/mass/ui/massCommandContext.tsx +65 -0
- package/src/mass/ui/massHostPanels.ts +71 -0
- package/src/mass/ui/massStateContext.ts +48 -0
- package/src/mass/ui/useIntensityZoom.ts +55 -0
- package/src/mass/ui/useMassEditor.ts +156 -0
- package/src/mass/ui/useMassFiles.ts +91 -0
- package/src/mass/ui/useMassHandle.ts +58 -0
- package/src/mass/ui/useMassPointer.ts +120 -0
- package/src/mass/ui/useMassShortcuts.ts +85 -0
- package/src/mass/ui/useMassSide.ts +63 -0
- package/src/mass/ui/useMassZoom.ts +157 -0
- package/src/mass.ts +16 -0
- package/src/panel/ui/PanelRail.tsx +5 -1
- package/src/slides/ui/slideSanitizeSchema.ts +86 -2
- package/styles/chrome.css +7 -0
- package/styles/slides.css +47 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import type { DragEvent } from 'react';
|
|
2
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import type { MassPlotClick } from '../core/massEditorApi.ts';
|
|
5
|
+
import type { MassDomain } from '../core/massSpectrum.ts';
|
|
6
|
+
import type { NearestPeak } from '../core/nearestPeak.ts';
|
|
7
|
+
|
|
8
|
+
import { MassChart } from './MassChart.tsx';
|
|
9
|
+
import { MassStatusBar } from './MassStatusBar.tsx';
|
|
10
|
+
import { useMassActions, useMassEditorState } from './massStateContext.ts';
|
|
11
|
+
|
|
12
|
+
export interface MassCanvasProps {
|
|
13
|
+
/**
|
|
14
|
+
* The m/z of the peak the pointer is resting on, wherever it was hovered —
|
|
15
|
+
* the chart or the peak table.
|
|
16
|
+
* @default null
|
|
17
|
+
*/
|
|
18
|
+
highlightMz?: number | null;
|
|
19
|
+
/**
|
|
20
|
+
* Told which peak the pointer has come to rest on over the chart.
|
|
21
|
+
* @default undefined
|
|
22
|
+
*/
|
|
23
|
+
onHighlight?: (mz: number | null) => void;
|
|
24
|
+
/**
|
|
25
|
+
* The `id` put on the box the chart is drawn in, which the "Save figure"
|
|
26
|
+
* panel is pointed at.
|
|
27
|
+
* @default undefined
|
|
28
|
+
*/
|
|
29
|
+
chartId?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Told of a click on the plot, with the spectrum selected at that moment.
|
|
32
|
+
* @default undefined
|
|
33
|
+
*/
|
|
34
|
+
onPlotClick?: (click: MassPlotClick) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Read files dropped on the chart.
|
|
37
|
+
* @default undefined — a drop is left to the page
|
|
38
|
+
*/
|
|
39
|
+
onDropFiles?: (files: File[]) => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The chart, and the status bar under it.
|
|
44
|
+
*
|
|
45
|
+
* This is where the state meets the drawing: the chart holds nothing, so
|
|
46
|
+
* everything it draws is read out of the state here and every gesture it reports
|
|
47
|
+
* is written back. The window in particular goes **both** ways — the chart is
|
|
48
|
+
* told what to show and reports what it arrives at — which is what lets a toolbar
|
|
49
|
+
* button, a row of the peak table and a drag all move it.
|
|
50
|
+
*
|
|
51
|
+
* The chart is also where files are dropped, because that is where a chemist
|
|
52
|
+
* expects the spectrum to land; the box is outlined while one is held over it.
|
|
53
|
+
* @param props - Component props.
|
|
54
|
+
* @returns The canvas.
|
|
55
|
+
*/
|
|
56
|
+
export function MassCanvas(props: MassCanvasProps) {
|
|
57
|
+
const {
|
|
58
|
+
highlightMz = null,
|
|
59
|
+
onHighlight,
|
|
60
|
+
chartId,
|
|
61
|
+
onPlotClick,
|
|
62
|
+
onDropFiles,
|
|
63
|
+
} = props;
|
|
64
|
+
const { state, visibleSpectra } = useMassEditorState();
|
|
65
|
+
const actions = useMassActions();
|
|
66
|
+
const { domain, tool, selectedId } = state.view;
|
|
67
|
+
const { showLabels, labelLimit, showCharges, mzDecimals } = state.settings;
|
|
68
|
+
const [isDropping, setIsDropping] = useState(false);
|
|
69
|
+
|
|
70
|
+
const onDomainChange = useCallback(
|
|
71
|
+
(next: MassDomain) => actions.setDomain(next),
|
|
72
|
+
[actions],
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const onPeakHover = useMemo(
|
|
76
|
+
() =>
|
|
77
|
+
onHighlight === undefined
|
|
78
|
+
? undefined
|
|
79
|
+
: (peak: NearestPeak | null) => onHighlight(peak?.mz ?? null),
|
|
80
|
+
[onHighlight],
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Which spectrum was selected is the viewer's to say, not the chart's: the
|
|
84
|
+
// chart only knows where the press landed.
|
|
85
|
+
const onClick = useMemo(
|
|
86
|
+
() =>
|
|
87
|
+
onPlotClick === undefined
|
|
88
|
+
? undefined
|
|
89
|
+
: (mz: number, peak: NearestPeak | null) =>
|
|
90
|
+
onPlotClick({
|
|
91
|
+
mz,
|
|
92
|
+
peakMz: peak?.mz ?? null,
|
|
93
|
+
spectrumId: selectedId,
|
|
94
|
+
}),
|
|
95
|
+
[onPlotClick, selectedId],
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const drop =
|
|
99
|
+
onDropFiles === undefined
|
|
100
|
+
? {}
|
|
101
|
+
: {
|
|
102
|
+
onDragOver: (event: DragEvent<HTMLDivElement>) => {
|
|
103
|
+
if (!event.dataTransfer.types.includes('Files')) return;
|
|
104
|
+
// Without this the browser opens the file in place of the page.
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
event.dataTransfer.dropEffect = 'copy';
|
|
107
|
+
if (!isDropping) setIsDropping(true);
|
|
108
|
+
},
|
|
109
|
+
onDragLeave: () => setIsDropping(false),
|
|
110
|
+
onDrop: (event: DragEvent<HTMLDivElement>) => {
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
setIsDropping(false);
|
|
113
|
+
const files = [...event.dataTransfer.files];
|
|
114
|
+
if (files.length > 0) onDropFiles(files);
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<div style={rootStyle}>
|
|
120
|
+
<div
|
|
121
|
+
id={chartId}
|
|
122
|
+
style={isDropping ? droppingChartStyle : chartStyle}
|
|
123
|
+
{...drop}
|
|
124
|
+
>
|
|
125
|
+
<MassChart
|
|
126
|
+
spectra={visibleSpectra}
|
|
127
|
+
labelledId={selectedId}
|
|
128
|
+
showLabels={showLabels}
|
|
129
|
+
labelLimit={labelLimit}
|
|
130
|
+
showCharges={showCharges}
|
|
131
|
+
// A number chosen holds for every mass on the chart; left to the
|
|
132
|
+
// spectra, the chart decides per spectrum, labels and crosshair alike.
|
|
133
|
+
{...(mzDecimals === 'auto' ? {} : { mzDecimals })}
|
|
134
|
+
highlightMz={highlightMz}
|
|
135
|
+
domain={domain}
|
|
136
|
+
onDomainChange={onDomainChange}
|
|
137
|
+
drag={tool === 'box' ? 'box' : 'xAxis'}
|
|
138
|
+
// The shell runs `f` through its command table, where it is listed.
|
|
139
|
+
answersKeys={false}
|
|
140
|
+
emphasisedId={visibleSpectra.length > 1 ? selectedId : null}
|
|
141
|
+
{...(onPeakHover === undefined ? {} : { onPeakHover })}
|
|
142
|
+
{...(onClick === undefined ? {} : { onPlotClick: onClick })}
|
|
143
|
+
/>
|
|
144
|
+
</div>
|
|
145
|
+
<MassStatusBar />
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const rootStyle = {
|
|
151
|
+
display: 'flex',
|
|
152
|
+
flexDirection: 'column',
|
|
153
|
+
flex: '1 1 1px',
|
|
154
|
+
minWidth: 0,
|
|
155
|
+
minHeight: 0,
|
|
156
|
+
} as const;
|
|
157
|
+
|
|
158
|
+
/** The chart takes what the status bar does not. */
|
|
159
|
+
const chartStyle = {
|
|
160
|
+
flex: '1 1 1px',
|
|
161
|
+
minHeight: 0,
|
|
162
|
+
outline: '2px dashed transparent',
|
|
163
|
+
outlineOffset: -4,
|
|
164
|
+
} as const;
|
|
165
|
+
|
|
166
|
+
/** The same box while a file is held over it, saying where it will land. */
|
|
167
|
+
const droppingChartStyle = {
|
|
168
|
+
...chartStyle,
|
|
169
|
+
outlineColor: 'var(--accent, currentColor)',
|
|
170
|
+
} as const;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import { useId, useLayoutEffect, useMemo, useRef } from 'react';
|
|
3
|
+
|
|
4
|
+
import { sameChartDomain } from '../../chart/core/chartDomain.ts';
|
|
5
|
+
import { MARGIN, plotRect } from '../../chart/core/chartGeometry.ts';
|
|
6
|
+
import { Axes } from '../../chart/ui/Axes.tsx';
|
|
7
|
+
import { AxisGuide } from '../../chart/ui/AxisGuide.tsx';
|
|
8
|
+
import { SelectionRect } from '../../chart/ui/SelectionRect.tsx';
|
|
9
|
+
import { chartSurfaceStyle } from '../../chart/ui/chartSurface.ts';
|
|
10
|
+
import { useChartSize } from '../../chart/ui/useChartSize.ts';
|
|
11
|
+
import type { MassSpectrum } from '../core/massSpectrum.ts';
|
|
12
|
+
import { DEFAULT_LABEL_LIMIT } from '../core/massState.ts';
|
|
13
|
+
import { mzDecimalsOf } from '../core/resolutionOf.ts';
|
|
14
|
+
|
|
15
|
+
import { MassPointerTracker } from './MassPointerTracker.tsx';
|
|
16
|
+
import { PeakLabels } from './PeakLabels.tsx';
|
|
17
|
+
import { SpectrumTrace } from './SpectrumTrace.tsx';
|
|
18
|
+
import type { MassChartProps } from './massChartProps.ts';
|
|
19
|
+
import { useMassPointer } from './useMassPointer.ts';
|
|
20
|
+
import { useMassZoom } from './useMassZoom.ts';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The chart: every layer of a mass spectrum, in one SVG.
|
|
24
|
+
*
|
|
25
|
+
* The axes go under the data, so a grid line is never read as a peak. Only the
|
|
26
|
+
* data band is clipped, so a zoomed trace stops at the plot edge while the
|
|
27
|
+
* labels over the tallest peaks are allowed out over the margin — clip those and
|
|
28
|
+
* the base peak of every spectrum loses its label. The guide, the drag rectangle
|
|
29
|
+
* and the crosshair go over everything, because each answers a question being
|
|
30
|
+
* asked right now.
|
|
31
|
+
*
|
|
32
|
+
* Nothing is held here. The spectra, what is labelled, what is being looked at
|
|
33
|
+
* and the window are all props, and every gesture comes back out as a callback
|
|
34
|
+
* — so one component draws an editor's chart, a report page's single spectrum
|
|
35
|
+
* and a test.
|
|
36
|
+
* @param props - Component props.
|
|
37
|
+
* @returns The chart.
|
|
38
|
+
*/
|
|
39
|
+
export function MassChart(props: MassChartProps): ReactElement {
|
|
40
|
+
const {
|
|
41
|
+
spectra,
|
|
42
|
+
labelledId,
|
|
43
|
+
showLabels = true,
|
|
44
|
+
labelLimit = DEFAULT_LABEL_LIMIT,
|
|
45
|
+
showCharges = false,
|
|
46
|
+
mzDecimals: askedDecimals,
|
|
47
|
+
annotationLabels,
|
|
48
|
+
highlightMz = null,
|
|
49
|
+
onPeakHover,
|
|
50
|
+
domain = null,
|
|
51
|
+
onDomainChange,
|
|
52
|
+
onPlotClick,
|
|
53
|
+
drag = 'xAxis',
|
|
54
|
+
answersKeys = true,
|
|
55
|
+
emphasisedId = null,
|
|
56
|
+
width,
|
|
57
|
+
height,
|
|
58
|
+
children,
|
|
59
|
+
} = props;
|
|
60
|
+
|
|
61
|
+
const [containerRef, measured] = useChartSize<HTMLDivElement>();
|
|
62
|
+
const svgRef = useRef<SVGSVGElement | null>(null);
|
|
63
|
+
|
|
64
|
+
const plot = useMemo(
|
|
65
|
+
() =>
|
|
66
|
+
plotRect({
|
|
67
|
+
width: width ?? measured.width,
|
|
68
|
+
height: height ?? measured.height,
|
|
69
|
+
}),
|
|
70
|
+
[width, height, measured.width, measured.height],
|
|
71
|
+
);
|
|
72
|
+
// Read back off the plot rather than off the measurement, so a chart briefly
|
|
73
|
+
// laid out at nothing still draws its axes inside a box that holds them.
|
|
74
|
+
const frameWidth = plot.right + MARGIN.right;
|
|
75
|
+
const frameHeight = plot.bottom + MARGIN.bottom;
|
|
76
|
+
|
|
77
|
+
// The host's window goes *into* the zoom rather than over it, so a drag is
|
|
78
|
+
// always measured against what is on screen.
|
|
79
|
+
const zoom = useMassZoom({
|
|
80
|
+
spectra,
|
|
81
|
+
svgRef,
|
|
82
|
+
plot,
|
|
83
|
+
domain,
|
|
84
|
+
drag,
|
|
85
|
+
keys: answersKeys,
|
|
86
|
+
...(onPlotClick === undefined
|
|
87
|
+
? {}
|
|
88
|
+
: { onClick: ({ mz, peak }) => onPlotClick(mz, peak) }),
|
|
89
|
+
});
|
|
90
|
+
const { domain: shown, xScale, yScale, selection, handlers } = zoom;
|
|
91
|
+
|
|
92
|
+
// A chart holding nothing still builds scales on a placeholder window, and a
|
|
93
|
+
// placeholder reported would become a window the host imposes on whatever is
|
|
94
|
+
// dropped next — drawn entirely off screen.
|
|
95
|
+
const holdsNothing = spectra.length === 0;
|
|
96
|
+
const reported = useRef(shown);
|
|
97
|
+
// Before the browser paints, so a host that stores the window and hands it
|
|
98
|
+
// back does so in the frame the gesture landed in rather than the one after.
|
|
99
|
+
useLayoutEffect(() => {
|
|
100
|
+
if (holdsNothing || sameChartDomain(reported.current, shown)) return;
|
|
101
|
+
reported.current = shown;
|
|
102
|
+
onDomainChange?.(shown);
|
|
103
|
+
}, [holdsNothing, shown, onDomainChange]);
|
|
104
|
+
|
|
105
|
+
const pointer = useMassPointer({
|
|
106
|
+
svgRef,
|
|
107
|
+
plot,
|
|
108
|
+
xScale,
|
|
109
|
+
yScale,
|
|
110
|
+
spectra,
|
|
111
|
+
...(onPeakHover === undefined ? {} : { onPeak: onPeakHover }),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const labelled = useMemo(
|
|
115
|
+
() => labelledSpectrum(spectra, labelledId),
|
|
116
|
+
[spectra, labelledId],
|
|
117
|
+
);
|
|
118
|
+
const painted = useMemo(
|
|
119
|
+
() => emphasisedLast(spectra, emphasisedId),
|
|
120
|
+
[spectra, emphasisedId],
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// The crosshair reads every drawn spectrum, so it is written to what they
|
|
124
|
+
// resolve together; the labels are of one spectrum, and written to its own.
|
|
125
|
+
const readoutDecimals = useMemo(
|
|
126
|
+
() => askedDecimals ?? mzDecimalsOf(spectra),
|
|
127
|
+
[askedDecimals, spectra],
|
|
128
|
+
);
|
|
129
|
+
const labelDecimals = useMemo(
|
|
130
|
+
() =>
|
|
131
|
+
askedDecimals ??
|
|
132
|
+
(labelled === null ? readoutDecimals : mzDecimalsOf([labelled])),
|
|
133
|
+
[askedDecimals, labelled, readoutDecimals],
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// React's generated id carries the punctuation it keeps its own ids apart
|
|
137
|
+
// with, which a URL fragment is under no obligation to accept.
|
|
138
|
+
const clipId = `mass-plot-${useId().replaceAll(/\W/g, '')}`;
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<div ref={containerRef} style={containerStyle}>
|
|
142
|
+
<svg
|
|
143
|
+
ref={svgRef}
|
|
144
|
+
role="img"
|
|
145
|
+
aria-label={CHART_LABEL}
|
|
146
|
+
width={frameWidth}
|
|
147
|
+
height={frameHeight}
|
|
148
|
+
viewBox={`0 0 ${frameWidth} ${frameHeight}`}
|
|
149
|
+
style={chartSurfaceStyle(pointer.position !== null)}
|
|
150
|
+
{...handlers}
|
|
151
|
+
>
|
|
152
|
+
<defs>
|
|
153
|
+
<clipPath id={clipId}>
|
|
154
|
+
<rect
|
|
155
|
+
x={plot.left}
|
|
156
|
+
y={plot.top}
|
|
157
|
+
width={plot.width}
|
|
158
|
+
height={plot.height}
|
|
159
|
+
/>
|
|
160
|
+
</clipPath>
|
|
161
|
+
</defs>
|
|
162
|
+
|
|
163
|
+
<Axes
|
|
164
|
+
plot={plot}
|
|
165
|
+
xDomain={shown.x}
|
|
166
|
+
yDomain={shown.y}
|
|
167
|
+
xScale={xScale}
|
|
168
|
+
yScale={yScale}
|
|
169
|
+
xTitle="m/z"
|
|
170
|
+
yTitle="relative intensity (%)"
|
|
171
|
+
/>
|
|
172
|
+
|
|
173
|
+
<g clipPath={`url(#${clipId})`}>
|
|
174
|
+
{painted.map((spectrum) => (
|
|
175
|
+
<SpectrumTrace
|
|
176
|
+
key={spectrum.id}
|
|
177
|
+
spectrum={spectrum}
|
|
178
|
+
plot={plot}
|
|
179
|
+
xScale={xScale}
|
|
180
|
+
yScale={yScale}
|
|
181
|
+
strokeWidth={
|
|
182
|
+
spectrum.id === emphasisedId ? EMPHASISED_WIDTH : undefined
|
|
183
|
+
}
|
|
184
|
+
/>
|
|
185
|
+
))}
|
|
186
|
+
{children}
|
|
187
|
+
</g>
|
|
188
|
+
|
|
189
|
+
{showLabels && labelled !== null ? (
|
|
190
|
+
<PeakLabels
|
|
191
|
+
spectrum={labelled}
|
|
192
|
+
plot={plot}
|
|
193
|
+
xScale={xScale}
|
|
194
|
+
yScale={yScale}
|
|
195
|
+
limit={labelLimit}
|
|
196
|
+
showCharges={showCharges}
|
|
197
|
+
mzDecimals={labelDecimals}
|
|
198
|
+
{...(annotationLabels === undefined ? {} : { annotationLabels })}
|
|
199
|
+
color={spectra.length > 1 ? labelled.color : undefined}
|
|
200
|
+
/>
|
|
201
|
+
) : null}
|
|
202
|
+
<AxisGuide value={highlightMz} plot={plot} xScale={xScale} />
|
|
203
|
+
<SelectionRect selection={selection} plot={plot} />
|
|
204
|
+
<MassPointerTracker
|
|
205
|
+
position={pointer.position}
|
|
206
|
+
readout={pointer.readout}
|
|
207
|
+
plot={plot}
|
|
208
|
+
mzDecimals={readoutDecimals}
|
|
209
|
+
/>
|
|
210
|
+
</svg>
|
|
211
|
+
</div>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* The one spectrum whose peaks are labelled.
|
|
217
|
+
* @param spectra - What the chart draws.
|
|
218
|
+
* @param labelledId - The identity asked for, `null` for none, absent to let the
|
|
219
|
+
* chart take the first.
|
|
220
|
+
* @returns The spectrum, or `null` when none answers.
|
|
221
|
+
*/
|
|
222
|
+
function labelledSpectrum(
|
|
223
|
+
spectra: readonly MassSpectrum[],
|
|
224
|
+
labelledId: string | null | undefined,
|
|
225
|
+
): MassSpectrum | null {
|
|
226
|
+
if (labelledId === undefined) return spectra[0] ?? null;
|
|
227
|
+
if (labelledId === null) return null;
|
|
228
|
+
for (const spectrum of spectra) {
|
|
229
|
+
if (spectrum.id === labelledId) return spectrum;
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* The traces in painting order, with the emphasised one last — last is in
|
|
236
|
+
* front, and in front is what lets a trace crossing a dozen others be followed.
|
|
237
|
+
* @param spectra - The traces, in the order they were opened.
|
|
238
|
+
* @param emphasisedId - Identity of the one to raise, `null` for none.
|
|
239
|
+
* @returns The order to paint in — the array itself when nothing moves.
|
|
240
|
+
*/
|
|
241
|
+
function emphasisedLast(
|
|
242
|
+
spectra: readonly MassSpectrum[],
|
|
243
|
+
emphasisedId: string | null,
|
|
244
|
+
): readonly MassSpectrum[] {
|
|
245
|
+
if (emphasisedId === null) return spectra;
|
|
246
|
+
const at = spectra.findIndex((spectrum) => spectrum.id === emphasisedId);
|
|
247
|
+
if (at === -1 || at === spectra.length - 1) return spectra;
|
|
248
|
+
return [
|
|
249
|
+
...spectra.slice(0, at),
|
|
250
|
+
...spectra.slice(at + 1),
|
|
251
|
+
spectra[at] as MassSpectrum,
|
|
252
|
+
];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** What the chart is, and what can be done to it, for whoever cannot see it. */
|
|
256
|
+
const CHART_LABEL =
|
|
257
|
+
'Mass spectrum. Drag across it to zoom the m/z axis, drag below the baseline to take the intensity axis with it, turn the wheel to scale the intensity, and double click to fit everything again.';
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* How thick the trace every panel is about is drawn: barely thicker, so it
|
|
261
|
+
* reads as the same kind of thing as the others.
|
|
262
|
+
*/
|
|
263
|
+
const EMPHASISED_WIDTH = 2;
|
|
264
|
+
|
|
265
|
+
/** The box the chart is measured against, and fills. */
|
|
266
|
+
const containerStyle = { width: '100%', height: '100%' } as const;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
|
|
4
|
+
import type { MassCommandHandlers } from '../core/massCommands.ts';
|
|
5
|
+
|
|
6
|
+
import type { MassCommandContextValue } from './massCommandContext.tsx';
|
|
7
|
+
import { massCommandContext } from './massCommandContext.tsx';
|
|
8
|
+
|
|
9
|
+
export interface MassCommandProviderProps {
|
|
10
|
+
/** What each command does. */
|
|
11
|
+
handlers: MassCommandHandlers;
|
|
12
|
+
/** The viewer. */
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Put the command handlers where the toolbar and the panels can reach them.
|
|
18
|
+
* @param props - Component props.
|
|
19
|
+
* @returns The provider.
|
|
20
|
+
*/
|
|
21
|
+
export function MassCommandProvider(props: MassCommandProviderProps) {
|
|
22
|
+
const { handlers, children } = props;
|
|
23
|
+
|
|
24
|
+
const value = useMemo<MassCommandContextValue>(
|
|
25
|
+
() => ({ handlers, run: (id) => handlers[id]?.() }),
|
|
26
|
+
[handlers],
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<massCommandContext.Provider value={value}>
|
|
31
|
+
{children}
|
|
32
|
+
</massCommandContext.Provider>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { TooltipHelpContent } from 'react-science/ui';
|
|
2
|
+
|
|
3
|
+
import type { MassCommand, MassCommandId } from '../core/massCommands.ts';
|
|
4
|
+
import { massCommands } from '../core/massCommands.ts';
|
|
5
|
+
|
|
6
|
+
export interface MassCommandTooltipProps {
|
|
7
|
+
/** Which command the tooltip is about. */
|
|
8
|
+
id: MassCommandId;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* One command's tooltip: its title, what it does, and every way to do it.
|
|
13
|
+
*
|
|
14
|
+
* `TooltipHelpContent` from react-science, which is what NMRium's tooltips and
|
|
15
|
+
* the infrared viewer's are, so a page holding these viewers side by side
|
|
16
|
+
* explains itself the same way in all of them.
|
|
17
|
+
* @param props - Component props.
|
|
18
|
+
* @returns The tooltip's content.
|
|
19
|
+
*/
|
|
20
|
+
export function MassCommandTooltip(props: MassCommandTooltipProps) {
|
|
21
|
+
const { id } = props;
|
|
22
|
+
const entry: MassCommand = massCommands[id];
|
|
23
|
+
const shortcuts = entry.shortcut?.label ?? entry.shortcut?.keys;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<TooltipHelpContent
|
|
27
|
+
title={entry.title}
|
|
28
|
+
description={entry.description}
|
|
29
|
+
shortcuts={shortcuts === undefined ? undefined : [...shortcuts]}
|
|
30
|
+
subTitles={entry.gestures?.map((gesture) => ({
|
|
31
|
+
title: gesture.title,
|
|
32
|
+
shortcuts: [...gesture.shortcuts],
|
|
33
|
+
}))}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { DocumentationSection } from '../../panel/ui/DocumentationDialogFrame.tsx';
|
|
4
|
+
import { DocumentationDialogFrame } from '../../panel/ui/DocumentationDialogFrame.tsx';
|
|
5
|
+
import type { MassCommand } from '../core/massCommands.ts';
|
|
6
|
+
import { massCommandLabel, massCommands } from '../core/massCommands.ts';
|
|
7
|
+
import { HIGH_RESOLVING_POWER } from '../core/massResolution.ts';
|
|
8
|
+
|
|
9
|
+
export interface MassDocumentationDialogProps {
|
|
10
|
+
/** Whether the dialog is open. */
|
|
11
|
+
isOpen: boolean;
|
|
12
|
+
/** Close it. */
|
|
13
|
+
onClose: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* How to use the viewer, written in the viewer.
|
|
18
|
+
*
|
|
19
|
+
* In the component rather than on a site, so there is nowhere to leave for and
|
|
20
|
+
* nothing to keep in step: every key in the last section is read out of the
|
|
21
|
+
* same command table the toolbar and the key handler read, so a shortcut cannot
|
|
22
|
+
* be documented as one thing and bound as another.
|
|
23
|
+
* @param props - Component props.
|
|
24
|
+
* @returns The dialog.
|
|
25
|
+
*/
|
|
26
|
+
export function MassDocumentationDialog(props: MassDocumentationDialogProps) {
|
|
27
|
+
const { isOpen, onClose } = props;
|
|
28
|
+
return (
|
|
29
|
+
<DocumentationDialogFrame
|
|
30
|
+
isOpen={isOpen}
|
|
31
|
+
onClose={onClose}
|
|
32
|
+
title="Using the mass spectrum viewer"
|
|
33
|
+
sections={SECTIONS}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* One row of the gestures table: a command, its key and what it does.
|
|
40
|
+
* @param props - Component props.
|
|
41
|
+
* @param props.entry - The command, as the table words it.
|
|
42
|
+
* @returns The row.
|
|
43
|
+
*/
|
|
44
|
+
function CommandRow(props: { entry: MassCommand }): ReactNode {
|
|
45
|
+
const { entry } = props;
|
|
46
|
+
const keys = entry.shortcut?.label ?? entry.shortcut?.keys ?? [];
|
|
47
|
+
const gestures = (entry.gestures ?? []).flatMap(
|
|
48
|
+
(gesture) => gesture.shortcuts,
|
|
49
|
+
);
|
|
50
|
+
return (
|
|
51
|
+
<tr>
|
|
52
|
+
<th style={labelCellStyle}>{entry.title}</th>
|
|
53
|
+
<td style={keyCellStyle}>{[...keys, ...gestures].join(' · ')}</td>
|
|
54
|
+
<td style={cellStyle}>{entry.description}</td>
|
|
55
|
+
</tr>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const tableStyle = {
|
|
60
|
+
width: '100%',
|
|
61
|
+
borderCollapse: 'collapse',
|
|
62
|
+
fontSize: 12,
|
|
63
|
+
} as const;
|
|
64
|
+
|
|
65
|
+
const cellStyle = { padding: '3px 6px', verticalAlign: 'top' } as const;
|
|
66
|
+
|
|
67
|
+
const labelCellStyle = {
|
|
68
|
+
...cellStyle,
|
|
69
|
+
textAlign: 'left',
|
|
70
|
+
whiteSpace: 'nowrap',
|
|
71
|
+
} as const;
|
|
72
|
+
|
|
73
|
+
const keyCellStyle = {
|
|
74
|
+
...cellStyle,
|
|
75
|
+
whiteSpace: 'nowrap',
|
|
76
|
+
opacity: 0.7,
|
|
77
|
+
} as const;
|
|
78
|
+
|
|
79
|
+
/** What the guide says, in reading order. */
|
|
80
|
+
const SECTIONS: readonly DocumentationSection[] = [
|
|
81
|
+
{
|
|
82
|
+
id: 'open',
|
|
83
|
+
title: 'Opening a spectrum',
|
|
84
|
+
body: (
|
|
85
|
+
<p>
|
|
86
|
+
Drop JCAMP-DX files (<code>.jdx</code>, <code>.dx</code>) or two columns
|
|
87
|
+
of m/z and intensity (<code>.txt</code>, <code>.csv</code>,{' '}
|
|
88
|
+
<code>.xy</code>) on the chart, or use {massCommandLabel('openFiles')}.
|
|
89
|
+
A JCAMP holding several blocks, or a GC/MS export holding a page per
|
|
90
|
+
scan, gives one spectrum per block or page.
|
|
91
|
+
</p>
|
|
92
|
+
),
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: 'peaks',
|
|
96
|
+
title: 'Profile and centroid',
|
|
97
|
+
body: (
|
|
98
|
+
<p>
|
|
99
|
+
A continuous profile is peak picked as it is read; a peak table is taken
|
|
100
|
+
as it stands. Each row of the Spectra panel draws a spectrum as its
|
|
101
|
+
profile or as its picked peaks, never both. The peaks of the selected
|
|
102
|
+
spectrum are labelled on the chart: zoom onto a cluster and every line
|
|
103
|
+
of it is labelled.
|
|
104
|
+
</p>
|
|
105
|
+
),
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'resolution',
|
|
109
|
+
title: 'High or low resolution',
|
|
110
|
+
body: (
|
|
111
|
+
<p>
|
|
112
|
+
A profile is measured: the median resolving power of its tallest peaks,
|
|
113
|
+
at half height, is high resolution from{' '}
|
|
114
|
+
{HIGH_RESOLVING_POWER.toLocaleString('en-US')}. A peak table has no
|
|
115
|
+
widths, so the instrument its file names decides, and failing that the
|
|
116
|
+
decimals its masses were written with. Masses are written as high
|
|
117
|
+
resolution, to four decimals, or as nominal masses, whole. To write them
|
|
118
|
+
another way, pick the number of decimals in the Peaks panel toolbar; it
|
|
119
|
+
holds for the chart and the tables alike.
|
|
120
|
+
</p>
|
|
121
|
+
),
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: 'keys',
|
|
125
|
+
title: 'Gestures and keys',
|
|
126
|
+
body: (
|
|
127
|
+
<table style={tableStyle}>
|
|
128
|
+
<tbody>
|
|
129
|
+
{Object.values(massCommands).map((entry) => (
|
|
130
|
+
<CommandRow key={entry.title} entry={entry} />
|
|
131
|
+
))}
|
|
132
|
+
</tbody>
|
|
133
|
+
</table>
|
|
134
|
+
),
|
|
135
|
+
},
|
|
136
|
+
];
|