react-science 0.25.0 → 0.26.2
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/lib/app/explorer/MeasurementExplorer.js +29 -7
- package/lib/app/explorer/MeasurementExplorerWithState.js +2 -2
- package/lib/app/helpers/MeasurementPlot.js +28 -19
- package/lib/app/helpers/react-plot.js +1 -1
- package/lib/app/kinds/mass/MassPlotView.js +2 -2
- package/lib/app/kinds/mass/MeasurementMassPlot.js +49 -33
- package/lib/app/panels/measurement-info/MeasurementInfoPanel.js +13 -58
- package/lib/app-data/state/data/data.helpers.js +32 -8
- package/lib/components/forms/Checkbox.js +4 -5
- package/lib/components/forms/Input.js +3 -87
- package/lib/components/forms/TextArea.js +25 -0
- package/lib/components/forms/index.js +2 -0
- package/lib/components/forms/radio-group/ButtonRadioItem.js +76 -0
- package/lib/components/forms/radio-group/ClassicRadioItem.js +92 -0
- package/lib/components/forms/radio-group/RadioGroup.js +69 -0
- package/lib/components/forms/radio-group/index.js +17 -0
- package/lib/components/forms/styles.js +80 -0
- package/lib/components/forms/utils/SubText.js +20 -0
- package/lib/components/forms/utils/index.js +17 -0
- package/lib/components/header/PanelHeader.js +51 -0
- package/lib/components/header/index.js +1 -0
- package/lib/components/index.js +1 -0
- package/lib/components/info-panel/InfoPanel.js +94 -0
- package/lib/components/info-panel/index.js +17 -0
- package/lib/components/modal/ConfirmModal.js +3 -2
- package/lib/components/modal/Modal.js +3 -1
- package/lib/components/table/Table.js +2 -2
- package/lib/components/toolbar/PanelPreferencesToolbar.js +26 -0
- package/lib/components/toolbar/index.js +1 -0
- package/lib-esm/app/explorer/MeasurementExplorer.d.ts.map +1 -1
- package/lib-esm/app/explorer/MeasurementExplorer.js +30 -8
- package/lib-esm/app/explorer/MeasurementExplorer.js.map +1 -1
- package/lib-esm/app/explorer/MeasurementExplorerWithState.d.ts.map +1 -1
- package/lib-esm/app/explorer/MeasurementExplorerWithState.js +2 -2
- package/lib-esm/app/explorer/MeasurementExplorerWithState.js.map +1 -1
- package/lib-esm/app/helpers/MeasurementPlot.d.ts +3 -3
- package/lib-esm/app/helpers/MeasurementPlot.d.ts.map +1 -1
- package/lib-esm/app/helpers/MeasurementPlot.js +28 -19
- package/lib-esm/app/helpers/MeasurementPlot.js.map +1 -1
- package/lib-esm/app/helpers/react-plot.d.ts.map +1 -1
- package/lib-esm/app/helpers/react-plot.js +1 -1
- package/lib-esm/app/helpers/react-plot.js.map +1 -1
- package/lib-esm/app/kinds/mass/MassPlotView.d.ts.map +1 -1
- package/lib-esm/app/kinds/mass/MassPlotView.js +2 -2
- package/lib-esm/app/kinds/mass/MassPlotView.js.map +1 -1
- package/lib-esm/app/kinds/mass/MeasurementMassPlot.d.ts.map +1 -1
- package/lib-esm/app/kinds/mass/MeasurementMassPlot.js +49 -33
- package/lib-esm/app/kinds/mass/MeasurementMassPlot.js.map +1 -1
- package/lib-esm/app/panels/measurement-info/MeasurementInfoPanel.d.ts.map +1 -1
- package/lib-esm/app/panels/measurement-info/MeasurementInfoPanel.js +15 -60
- package/lib-esm/app/panels/measurement-info/MeasurementInfoPanel.js.map +1 -1
- package/lib-esm/app-data/state/data/data.helpers.d.ts +20 -12
- package/lib-esm/app-data/state/data/data.helpers.d.ts.map +1 -1
- package/lib-esm/app-data/state/data/data.helpers.js +30 -7
- package/lib-esm/app-data/state/data/data.helpers.js.map +1 -1
- package/lib-esm/components/forms/Checkbox.d.ts.map +1 -1
- package/lib-esm/components/forms/Checkbox.js +1 -2
- package/lib-esm/components/forms/Checkbox.js.map +1 -1
- package/lib-esm/components/forms/Input.d.ts +4 -2
- package/lib-esm/components/forms/Input.d.ts.map +1 -1
- package/lib-esm/components/forms/Input.js +3 -87
- package/lib-esm/components/forms/Input.js.map +1 -1
- package/lib-esm/components/forms/TextArea.d.ts +8 -0
- package/lib-esm/components/forms/TextArea.d.ts.map +1 -0
- package/lib-esm/components/forms/TextArea.js +19 -0
- package/lib-esm/components/forms/TextArea.js.map +1 -0
- package/lib-esm/components/forms/index.d.ts +2 -0
- package/lib-esm/components/forms/index.d.ts.map +1 -1
- package/lib-esm/components/forms/index.js +2 -0
- package/lib-esm/components/forms/index.js.map +1 -1
- package/lib-esm/components/forms/radio-group/ButtonRadioItem.d.ts +3 -0
- package/lib-esm/components/forms/radio-group/ButtonRadioItem.d.ts.map +1 -0
- package/lib-esm/components/forms/radio-group/ButtonRadioItem.js +50 -0
- package/lib-esm/components/forms/radio-group/ButtonRadioItem.js.map +1 -0
- package/lib-esm/components/forms/radio-group/ClassicRadioItem.d.ts +3 -0
- package/lib-esm/components/forms/radio-group/ClassicRadioItem.d.ts.map +1 -0
- package/lib-esm/components/forms/radio-group/ClassicRadioItem.js +66 -0
- package/lib-esm/components/forms/radio-group/ClassicRadioItem.js.map +1 -0
- package/lib-esm/components/forms/radio-group/RadioGroup.d.ts +18 -0
- package/lib-esm/components/forms/radio-group/RadioGroup.d.ts.map +1 -0
- package/lib-esm/components/forms/radio-group/RadioGroup.js +43 -0
- package/lib-esm/components/forms/radio-group/RadioGroup.js.map +1 -0
- package/lib-esm/components/forms/radio-group/index.d.ts +2 -0
- package/lib-esm/components/forms/radio-group/index.d.ts.map +1 -0
- package/lib-esm/components/forms/radio-group/index.js +2 -0
- package/lib-esm/components/forms/radio-group/index.js.map +1 -0
- package/lib-esm/components/forms/styles.d.ts +26 -0
- package/lib-esm/components/forms/styles.d.ts.map +1 -0
- package/lib-esm/components/forms/styles.js +75 -0
- package/lib-esm/components/forms/styles.js.map +1 -0
- package/lib-esm/components/forms/utils/SubText.d.ts +7 -0
- package/lib-esm/components/forms/utils/SubText.d.ts.map +1 -0
- package/lib-esm/components/forms/utils/SubText.js +17 -0
- package/lib-esm/components/forms/utils/SubText.js.map +1 -0
- package/lib-esm/components/forms/utils/index.d.ts +2 -0
- package/lib-esm/components/forms/utils/index.d.ts.map +1 -0
- package/lib-esm/components/forms/utils/index.js +2 -0
- package/lib-esm/components/forms/utils/index.js.map +1 -0
- package/lib-esm/components/header/PanelHeader.d.ts +10 -0
- package/lib-esm/components/header/PanelHeader.d.ts.map +1 -0
- package/lib-esm/components/header/PanelHeader.js +48 -0
- package/lib-esm/components/header/PanelHeader.js.map +1 -0
- package/lib-esm/components/header/index.d.ts +1 -0
- package/lib-esm/components/header/index.d.ts.map +1 -1
- package/lib-esm/components/header/index.js +1 -0
- package/lib-esm/components/header/index.js.map +1 -1
- package/lib-esm/components/index.d.ts +1 -0
- package/lib-esm/components/index.d.ts.map +1 -1
- package/lib-esm/components/index.js +1 -0
- package/lib-esm/components/index.js.map +1 -1
- package/lib-esm/components/info-panel/InfoPanel.d.ts +15 -0
- package/lib-esm/components/info-panel/InfoPanel.d.ts.map +1 -0
- package/lib-esm/components/info-panel/InfoPanel.js +91 -0
- package/lib-esm/components/info-panel/InfoPanel.js.map +1 -0
- package/lib-esm/components/info-panel/index.d.ts +2 -0
- package/lib-esm/components/info-panel/index.d.ts.map +1 -0
- package/lib-esm/components/info-panel/index.js +2 -0
- package/lib-esm/components/info-panel/index.js.map +1 -0
- package/lib-esm/components/modal/ConfirmModal.d.ts.map +1 -1
- package/lib-esm/components/modal/ConfirmModal.js +3 -2
- package/lib-esm/components/modal/ConfirmModal.js.map +1 -1
- package/lib-esm/components/modal/Modal.d.ts.map +1 -1
- package/lib-esm/components/modal/Modal.js +3 -1
- package/lib-esm/components/modal/Modal.js.map +1 -1
- package/lib-esm/components/table/Table.d.ts +2 -3
- package/lib-esm/components/table/Table.d.ts.map +1 -1
- package/lib-esm/components/table/Table.js +2 -2
- package/lib-esm/components/table/Table.js.map +1 -1
- package/lib-esm/components/toolbar/PanelPreferencesToolbar.d.ts +7 -0
- package/lib-esm/components/toolbar/PanelPreferencesToolbar.d.ts.map +1 -0
- package/lib-esm/components/toolbar/PanelPreferencesToolbar.js +23 -0
- package/lib-esm/components/toolbar/PanelPreferencesToolbar.js.map +1 -0
- package/lib-esm/components/toolbar/index.d.ts +1 -0
- package/lib-esm/components/toolbar/index.d.ts.map +1 -1
- package/lib-esm/components/toolbar/index.js +1 -0
- package/lib-esm/components/toolbar/index.js.map +1 -1
- package/package.json +16 -14
- package/src/app/explorer/MeasurementExplorer.tsx +34 -12
- package/src/app/explorer/MeasurementExplorerWithState.tsx +2 -2
- package/src/app/helpers/MeasurementPlot.tsx +44 -33
- package/src/app/helpers/react-plot.tsx +6 -4
- package/src/app/kinds/mass/MassPlotView.tsx +2 -2
- package/src/app/kinds/mass/MeasurementMassPlot.tsx +57 -37
- package/src/app/panels/measurement-info/MeasurementInfoPanel.tsx +14 -84
- package/src/app-data/state/data/data.helpers.ts +49 -16
- package/src/components/forms/Checkbox.tsx +2 -3
- package/src/components/forms/Input.tsx +14 -125
- package/src/components/forms/TextArea.tsx +45 -0
- package/src/components/forms/index.ts +2 -0
- package/src/components/forms/radio-group/ButtonRadioItem.tsx +77 -0
- package/src/components/forms/radio-group/ClassicRadioItem.tsx +95 -0
- package/src/components/forms/radio-group/RadioGroup.tsx +83 -0
- package/src/components/forms/radio-group/index.ts +1 -0
- package/src/components/forms/styles.ts +96 -0
- package/src/components/forms/utils/SubText.tsx +31 -0
- package/src/components/forms/utils/index.ts +1 -0
- package/src/components/header/PanelHeader.tsx +75 -0
- package/src/components/header/index.ts +1 -0
- package/src/components/index.ts +1 -0
- package/src/components/info-panel/InfoPanel.tsx +150 -0
- package/src/components/info-panel/index.ts +1 -0
- package/src/components/modal/ConfirmModal.tsx +3 -2
- package/src/components/modal/Modal.tsx +3 -1
- package/src/components/table/Table.tsx +3 -5
- package/src/components/toolbar/PanelPreferencesToolbar.tsx +46 -0
- package/src/components/toolbar/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-science",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.2",
|
|
4
4
|
"description": "React components to build scientific applications UI",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./app": {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"test-only": "vitest run --coverage "
|
|
51
51
|
},
|
|
52
52
|
"volta": {
|
|
53
|
-
"node": "18.17.
|
|
53
|
+
"node": "18.17.1"
|
|
54
54
|
},
|
|
55
55
|
"overrides": {
|
|
56
56
|
"react": "^18.2.0",
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"@lukeed/uuid": "^2.0.1",
|
|
68
68
|
"@popperjs/core": "^2.11.8",
|
|
69
69
|
"@radix-ui/react-checkbox": "^1.0.4",
|
|
70
|
+
"@radix-ui/react-radio-group": "^1.1.3",
|
|
70
71
|
"@radix-ui/react-select": "^1.2.2",
|
|
71
72
|
"@tanstack/react-query": "^4.32.6",
|
|
72
73
|
"@tanstack/react-table": "^8.9.3",
|
|
@@ -85,7 +86,7 @@
|
|
|
85
86
|
"netcdfjs": "^2.0.2",
|
|
86
87
|
"react-d3-utils": "^1.0.0",
|
|
87
88
|
"react-dropzone": "^14.2.3",
|
|
88
|
-
"react-error-boundary": "^4.0.
|
|
89
|
+
"react-error-boundary": "^4.0.11",
|
|
89
90
|
"react-icons": "^4.10.1",
|
|
90
91
|
"react-inspector": "^6.0.2",
|
|
91
92
|
"react-kbs": "^2.1.1",
|
|
@@ -100,31 +101,32 @@
|
|
|
100
101
|
"devDependencies": {
|
|
101
102
|
"@babel/core": "^7.22.10",
|
|
102
103
|
"@babel/eslint-parser": "^7.22.10",
|
|
103
|
-
"@playwright/experimental-ct-react": "^1.
|
|
104
|
-
"@playwright/test": "^1.
|
|
105
|
-
"@storybook/addon-essentials": "7.
|
|
106
|
-
"@storybook/
|
|
107
|
-
"@storybook/
|
|
108
|
-
"@storybook/react
|
|
104
|
+
"@playwright/experimental-ct-react": "^1.37.0",
|
|
105
|
+
"@playwright/test": "^1.37.0",
|
|
106
|
+
"@storybook/addon-essentials": "7.3.0",
|
|
107
|
+
"@storybook/addon-storysource": "7.3.0",
|
|
108
|
+
"@storybook/blocks": "7.3.0",
|
|
109
|
+
"@storybook/react": "7.3.0",
|
|
110
|
+
"@storybook/react-vite": "7.3.0",
|
|
109
111
|
"@types/babel__core": "^7.20.1",
|
|
110
112
|
"@types/d3-scale-chromatic": "^3.0.0",
|
|
111
|
-
"@types/lodash": "^4.14.
|
|
112
|
-
"@types/react": "^18.2.
|
|
113
|
+
"@types/lodash": "^4.14.197",
|
|
114
|
+
"@types/react": "^18.2.20",
|
|
113
115
|
"@types/react-dom": "^18.2.7",
|
|
114
116
|
"@types/react-inspector": "^4.0.2",
|
|
115
117
|
"@vitejs/plugin-react": "^4.0.4",
|
|
116
118
|
"@vitest/coverage-v8": "^0.34.1",
|
|
117
119
|
"cheminfo-font": "^1.11.0",
|
|
118
120
|
"cross-env": "^7.0.3",
|
|
119
|
-
"eslint": "^8.
|
|
121
|
+
"eslint": "^8.47.0",
|
|
120
122
|
"eslint-config-zakodium": "^8.0.2",
|
|
121
123
|
"eslint-plugin-storybook": "^0.6.13",
|
|
122
|
-
"prettier": "^3.0.
|
|
124
|
+
"prettier": "^3.0.2",
|
|
123
125
|
"react": "^18.2.0",
|
|
124
126
|
"react-dom": "^18.2.0",
|
|
125
127
|
"react-ocl": "^6.1.0",
|
|
126
128
|
"rimraf": "^5.0.1",
|
|
127
|
-
"storybook": "7.
|
|
129
|
+
"storybook": "7.3.0",
|
|
128
130
|
"typescript": "^5.1.6",
|
|
129
131
|
"vite": "^4.4.9",
|
|
130
132
|
"vitest": "^0.34.1"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import styled from '@emotion/styled';
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
3
|
import { FaExchangeAlt, FaArrowsAltH } from 'react-icons/fa';
|
|
4
4
|
|
|
5
5
|
import { MeasurementPlot, MeasurementPlotProps } from '../helpers/index';
|
|
@@ -37,18 +37,40 @@ const MeasurementExplorerAction = styled.div`
|
|
|
37
37
|
`;
|
|
38
38
|
|
|
39
39
|
export function MeasurementExplorer(props: MeasurementExplorerProps) {
|
|
40
|
-
const {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
const { measurement, width = '100%', height = '100%' } = props;
|
|
41
|
+
const measurementsArray = useMemo(
|
|
42
|
+
() => (Array.isArray(measurement) ? measurement : [measurement]),
|
|
43
|
+
[measurement],
|
|
44
|
+
);
|
|
45
|
+
const varNames = useMemo(() => {
|
|
46
|
+
const varNames: string[][] = [];
|
|
47
|
+
for (const [i, { data }] of measurementsArray.entries()) {
|
|
48
|
+
for (const { variables } of data) {
|
|
49
|
+
const names: string[] = [];
|
|
50
|
+
for (const varName in variables) {
|
|
51
|
+
if (i === 0) {
|
|
52
|
+
names.push(varName);
|
|
53
|
+
} else if (!varNames.flat().includes(varName)) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Measurements selected does not have the same variables `,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
varNames.push(names);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return varNames;
|
|
63
|
+
}, [measurementsArray]);
|
|
45
64
|
|
|
46
65
|
function defaultInfo(dataIndex: number) {
|
|
47
|
-
const varNames = Object.keys(data[dataIndex].variables);
|
|
48
66
|
return {
|
|
49
67
|
dataIndex,
|
|
50
|
-
xVariableName: varNames.includes('x')
|
|
51
|
-
|
|
68
|
+
xVariableName: varNames[dataIndex].includes('x')
|
|
69
|
+
? 'x'
|
|
70
|
+
: varNames[dataIndex][0],
|
|
71
|
+
yVariableName: varNames[dataIndex].includes('y')
|
|
72
|
+
? 'y'
|
|
73
|
+
: varNames[dataIndex][1],
|
|
52
74
|
};
|
|
53
75
|
}
|
|
54
76
|
|
|
@@ -67,9 +89,9 @@ export function MeasurementExplorer(props: MeasurementExplorerProps) {
|
|
|
67
89
|
const formatVarKey = `${varKey} - `;
|
|
68
90
|
return formatVarKey + label + formatUnit;
|
|
69
91
|
}
|
|
70
|
-
const { variables } = data[info.dataIndex];
|
|
92
|
+
const { variables } = measurementsArray[0].data[info.dataIndex];
|
|
71
93
|
const oppositeAxis = axis === 'x' ? 'yVariableName' : 'xVariableName';
|
|
72
|
-
return
|
|
94
|
+
return varNames[info.dataIndex].map((d) => {
|
|
73
95
|
if (d !== info[oppositeAxis]) {
|
|
74
96
|
return (
|
|
75
97
|
<option key={d} value={d}>
|
|
@@ -97,7 +119,7 @@ export function MeasurementExplorer(props: MeasurementExplorerProps) {
|
|
|
97
119
|
}
|
|
98
120
|
}}
|
|
99
121
|
>
|
|
100
|
-
{data.map((d, i) => (
|
|
122
|
+
{measurementsArray[0].data.map((d, i) => (
|
|
101
123
|
// eslint-disable-next-line react/no-array-index-key
|
|
102
124
|
<option key={i} value={i}>
|
|
103
125
|
{i}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { getCurrentMeasurementData, useAppState } from '../../app-data/index';
|
|
2
|
-
import { assertNotNull } from '../../components/index';
|
|
3
2
|
|
|
4
3
|
import { MeasurementExplorer } from './MeasurementExplorer';
|
|
5
4
|
|
|
6
5
|
export default function MeasurementExplorerWithState() {
|
|
7
6
|
const appState = useAppState();
|
|
8
7
|
const data = getCurrentMeasurementData(appState);
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
if (!data) return <div>No selected measurement</div>;
|
|
10
10
|
return (
|
|
11
11
|
<MeasurementExplorer
|
|
12
12
|
measurement={data.data}
|
|
@@ -6,10 +6,10 @@ import type { MeasurementBase, MeasurementAppView } from '../../app-data/index';
|
|
|
6
6
|
|
|
7
7
|
import { BasicComponent } from './index';
|
|
8
8
|
|
|
9
|
-
type Measurement = Pick<MeasurementBase, 'meta' | 'info' | 'data'>;
|
|
9
|
+
type Measurement = Pick<MeasurementBase, 'meta' | 'info' | 'data' | 'id'>;
|
|
10
10
|
export interface MeasurementPlotProps {
|
|
11
|
-
measurement: Measurement;
|
|
12
|
-
measurementDisplay: MeasurementAppView;
|
|
11
|
+
measurement: Measurement[] | Measurement;
|
|
12
|
+
measurementDisplay: MeasurementAppView[] | MeasurementAppView;
|
|
13
13
|
dataIndex?: number;
|
|
14
14
|
xVariableName?: string;
|
|
15
15
|
yVariableName?: string;
|
|
@@ -35,44 +35,55 @@ export function MeasurementPlot(props: MeasurementPlotProps) {
|
|
|
35
35
|
function MeasurementComponent(props: MeasurementPlotProps) {
|
|
36
36
|
const {
|
|
37
37
|
measurementDisplay,
|
|
38
|
-
measurement
|
|
38
|
+
measurement,
|
|
39
39
|
dataIndex = 0,
|
|
40
40
|
xVariableName = 'x',
|
|
41
41
|
yVariableName = 'y',
|
|
42
42
|
} = props;
|
|
43
43
|
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
44
|
+
const dataXY = useMemo(() => {
|
|
45
|
+
const measurementsArray = Array.isArray(measurement)
|
|
46
|
+
? measurement
|
|
47
|
+
: [measurement];
|
|
48
|
+
return measurementsArray.map(({ data, id }) => {
|
|
49
|
+
const { variables } = data[dataIndex];
|
|
50
|
+
const { [xVariableName]: x, [yVariableName]: y } = variables;
|
|
51
|
+
if (x === undefined || y === undefined) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Variable "${
|
|
54
|
+
x === undefined ? xVariableName : yVariableName
|
|
55
|
+
}" is not available in data. Only ${Object.keys(
|
|
56
|
+
data[dataIndex].variables,
|
|
57
|
+
).join(', ')} are available`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return { x, y, id };
|
|
61
|
+
});
|
|
62
|
+
}, [dataIndex, measurement, xVariableName, yVariableName]);
|
|
64
63
|
|
|
65
64
|
return (
|
|
66
65
|
<BasicComponent {...props}>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
66
|
+
{dataXY.map(({ x, y, id }, i) => {
|
|
67
|
+
const { color } = Array.isArray(measurementDisplay)
|
|
68
|
+
? measurementDisplay[i]
|
|
69
|
+
: measurementDisplay;
|
|
70
|
+
if (color.kind !== 'fixed') {
|
|
71
|
+
throw new Error(`unimplemented stroke for kind ${color.kind}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<LineSeries
|
|
76
|
+
key={id}
|
|
77
|
+
lineStyle={{
|
|
78
|
+
stroke: color.color,
|
|
79
|
+
}}
|
|
80
|
+
data={xyToXYObject({
|
|
81
|
+
x: x.data,
|
|
82
|
+
y: y.data,
|
|
83
|
+
})}
|
|
84
|
+
/>
|
|
85
|
+
);
|
|
86
|
+
})}
|
|
76
87
|
</BasicComponent>
|
|
77
88
|
);
|
|
78
89
|
}
|
|
@@ -47,10 +47,12 @@ export function BasicComponent(props: BasicComponentProps) {
|
|
|
47
47
|
flipHorizontalAxis = false,
|
|
48
48
|
} = props;
|
|
49
49
|
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
const [
|
|
51
|
+
{
|
|
52
|
+
info: { title },
|
|
53
|
+
data,
|
|
54
|
+
},
|
|
55
|
+
] = Array.isArray(measurement) ? measurement : [measurement];
|
|
54
56
|
|
|
55
57
|
const { x, y } = useMemo(() => {
|
|
56
58
|
const { variables } = data[dataIndex];
|
|
@@ -2,14 +2,14 @@ import {
|
|
|
2
2
|
getCurrentMeasurementData,
|
|
3
3
|
useAppState,
|
|
4
4
|
} from '../../../app-data/index';
|
|
5
|
-
import { assertNotNull } from '../../../components/index';
|
|
6
5
|
|
|
7
6
|
import { MeasurementMassPlot } from './MeasurementMassPlot';
|
|
8
7
|
|
|
9
8
|
export function MassPlotView() {
|
|
10
9
|
const appState = useAppState();
|
|
11
10
|
const data = getCurrentMeasurementData(appState);
|
|
12
|
-
|
|
11
|
+
|
|
12
|
+
if (!data) return <div>No selected measurement</div>;
|
|
13
13
|
return (
|
|
14
14
|
<MeasurementMassPlot
|
|
15
15
|
measurement={data.data}
|
|
@@ -21,23 +21,28 @@ interface Peak {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function MeasurementMassPlot(props: MeasurementPlotProps) {
|
|
24
|
-
const { measurement } = props;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
24
|
+
const { measurement: measurements } = props;
|
|
25
|
+
const measurementsArray = Array.isArray(measurements)
|
|
26
|
+
? measurements
|
|
27
|
+
: [measurements];
|
|
28
|
+
for (const measurement of measurementsArray) {
|
|
29
|
+
if (!measurement.data) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
'This is weird, the data property is not available on measurement',
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (measurement.data.length === 0) {
|
|
35
|
+
throw new Error('Data property is empty');
|
|
36
|
+
}
|
|
37
|
+
if (measurement.data.length > 1) {
|
|
38
|
+
throw new Error('Length of data property is larger than 1');
|
|
39
|
+
}
|
|
40
|
+
if (!measurement.data[0].variables.x) {
|
|
41
|
+
throw new Error('x variable in undefined');
|
|
42
|
+
}
|
|
43
|
+
if (!measurement.data[0].variables.y) {
|
|
44
|
+
throw new Error('y variable in undefined');
|
|
45
|
+
}
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
return (
|
|
@@ -48,33 +53,43 @@ export function MeasurementMassPlot(props: MeasurementPlotProps) {
|
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
function MassComponent(props: MeasurementPlotProps) {
|
|
51
|
-
const { measurement } = props;
|
|
56
|
+
const { measurement: measurements } = props;
|
|
52
57
|
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
|
|
58
|
+
const dataXY = useMemo(() => {
|
|
59
|
+
const measurementsArray = Array.isArray(measurements)
|
|
60
|
+
? measurements
|
|
61
|
+
: [measurements];
|
|
62
|
+
return measurementsArray.map(({ data, id }) => {
|
|
63
|
+
const { variables } = data[0];
|
|
64
|
+
const { x, y } = variables;
|
|
65
|
+
return { x, y, id };
|
|
66
|
+
});
|
|
67
|
+
}, [measurements]);
|
|
59
68
|
|
|
60
69
|
const { x: xDomain } = usePlotControllerAxes();
|
|
61
|
-
const {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const isContinuous = spectrum.isContinuous();
|
|
67
|
-
const profile =
|
|
68
|
-
isContinuous &&
|
|
69
|
-
xyToXYObject({
|
|
70
|
+
const { profiles, peaks } = useMemo(() => {
|
|
71
|
+
const profiles = [];
|
|
72
|
+
const peaks = [];
|
|
73
|
+
for (const { x, y, id } of dataXY) {
|
|
74
|
+
const spectrum = new Spectrum({
|
|
70
75
|
x: x.data,
|
|
71
76
|
y: y.data,
|
|
72
77
|
});
|
|
78
|
+
const isContinuous = spectrum.isContinuous();
|
|
79
|
+
const data =
|
|
80
|
+
isContinuous &&
|
|
81
|
+
xyToXYObject({
|
|
82
|
+
x: x.data,
|
|
83
|
+
y: y.data,
|
|
84
|
+
});
|
|
85
|
+
profiles.push({ data, id });
|
|
86
|
+
peaks.push(...spectrum.getPeaks(data));
|
|
87
|
+
}
|
|
73
88
|
return {
|
|
74
|
-
|
|
75
|
-
peaks
|
|
89
|
+
profiles,
|
|
90
|
+
peaks,
|
|
76
91
|
};
|
|
77
|
-
}, [
|
|
92
|
+
}, [dataXY]);
|
|
78
93
|
const bestPeaks = useMemo(
|
|
79
94
|
() =>
|
|
80
95
|
getBestPeaks(peaks, {
|
|
@@ -88,7 +103,12 @@ function MassComponent(props: MeasurementPlotProps) {
|
|
|
88
103
|
);
|
|
89
104
|
return (
|
|
90
105
|
<BasicComponent {...props}>
|
|
91
|
-
{
|
|
106
|
+
{profiles.map(
|
|
107
|
+
({ data, id }) =>
|
|
108
|
+
data && (
|
|
109
|
+
<LineSeries key={id} data={data} lineStyle={{ stroke: 'green' }} />
|
|
110
|
+
),
|
|
111
|
+
)}
|
|
92
112
|
<BarSeries data={peaks} lineStyle={{ stroke: 'red' }} />
|
|
93
113
|
<Annotations>
|
|
94
114
|
{bestPeaks.map(({ x, y, shortLabel }: Peak) => (
|
|
@@ -1,97 +1,27 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
|
|
3
1
|
import {
|
|
4
2
|
getCurrentMeasurementData,
|
|
5
3
|
useAppState,
|
|
6
4
|
} from '../../../app-data/index';
|
|
7
|
-
import {
|
|
5
|
+
import { InfoPanelData, InfoPanel } from '../../../components/index';
|
|
8
6
|
|
|
9
7
|
export function MeasurementInfoPanel() {
|
|
10
8
|
const appState = useAppState();
|
|
11
9
|
const measurement = getCurrentMeasurementData(appState);
|
|
12
|
-
const [search, setSearch] = useState('');
|
|
13
10
|
if (!measurement) return null;
|
|
14
|
-
const { meta, info } = measurement.data;
|
|
15
|
-
|
|
16
|
-
function viewData(data: Record<string, any>) {
|
|
17
|
-
return Object.keys(data).map((key) => {
|
|
18
|
-
const value = data[key];
|
|
19
|
-
if (
|
|
20
|
-
!key.toLowerCase().includes(search.toLowerCase()) &&
|
|
21
|
-
!valueSearch(value, search)
|
|
22
|
-
) {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
return (
|
|
26
|
-
<Table.Row key={key}>
|
|
27
|
-
<ValueRenderers.Text value={key} />
|
|
28
|
-
{valueCell(value)}
|
|
29
|
-
</Table.Row>
|
|
30
|
-
);
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
11
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
<input
|
|
37
|
-
style={{
|
|
38
|
-
border: 'solid 1px black',
|
|
39
|
-
width: '300px',
|
|
40
|
-
marginBottom: '10px',
|
|
41
|
-
padding: '3px',
|
|
42
|
-
}}
|
|
43
|
-
value={search}
|
|
44
|
-
placeholder="search for a parameter ..."
|
|
45
|
-
onChange={({ target }) => {
|
|
46
|
-
if (target.value !== undefined) setSearch(target.value);
|
|
47
|
-
}}
|
|
48
|
-
/>
|
|
49
|
-
<Table>
|
|
50
|
-
<Table.Header>
|
|
51
|
-
<ValueRenderers.Title value="Parameter" />
|
|
52
|
-
<ValueRenderers.Title value="Value" />
|
|
53
|
-
</Table.Header>
|
|
54
|
-
{viewData(info)}
|
|
55
|
-
{viewData(meta)}
|
|
56
|
-
</Table>
|
|
57
|
-
</div>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
12
|
+
// TODO: solution for multiple measurements
|
|
13
|
+
const { meta, info } = measurement.data[0];
|
|
60
14
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return <ValueRenderers.Object value={value} />;
|
|
72
|
-
case 'string':
|
|
73
|
-
return <ValueRenderers.Text value={value} />;
|
|
74
|
-
default:
|
|
75
|
-
<ValueRenderers.Text value={value} />;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
15
|
+
const data: InfoPanelData[] = [
|
|
16
|
+
{
|
|
17
|
+
description: 'Information',
|
|
18
|
+
data: info,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
description: 'Metadata',
|
|
22
|
+
data: meta,
|
|
23
|
+
},
|
|
24
|
+
];
|
|
78
25
|
|
|
79
|
-
|
|
80
|
-
* Search a string in different type of values
|
|
81
|
-
*
|
|
82
|
-
* @param value - Value to search in.
|
|
83
|
-
* @param search - Value to search for.
|
|
84
|
-
* @returns - If search exist in value
|
|
85
|
-
*/
|
|
86
|
-
function valueSearch(value: number | string | object, search: string): boolean {
|
|
87
|
-
switch (typeof value) {
|
|
88
|
-
case 'number':
|
|
89
|
-
return String(value).includes(search.toLowerCase());
|
|
90
|
-
case 'object':
|
|
91
|
-
return JSON.stringify(value).toLowerCase().includes(search.toLowerCase());
|
|
92
|
-
case 'string':
|
|
93
|
-
return value.toLowerCase().includes(search.toLowerCase());
|
|
94
|
-
default:
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
26
|
+
return <InfoPanel data={data} title="" />;
|
|
97
27
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { assertNotNull } from '../../../components/index';
|
|
2
2
|
import type { AppState, AppView } from '../index';
|
|
3
3
|
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
AppData,
|
|
6
|
+
MeasurementBase,
|
|
7
|
+
MeasurementKind,
|
|
8
|
+
Measurements,
|
|
9
|
+
} from './AppData';
|
|
5
10
|
import { measurementKinds } from './kinds';
|
|
6
11
|
|
|
7
12
|
export function getMeasurement(
|
|
@@ -46,20 +51,27 @@ export function getFirstMeasurementOrFail<Kind extends MeasurementKind>(
|
|
|
46
51
|
export function getCurrentMeasurement(state: AppState) {
|
|
47
52
|
const selectedMeasurement = getSelectedMeasurement(state.view);
|
|
48
53
|
if (!selectedMeasurement) return null;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
const measurements: MeasurementBase[] = [];
|
|
55
|
+
for (const id of selectedMeasurement.ids) {
|
|
56
|
+
const measurement = getMeasurement(
|
|
57
|
+
state.data.measurements,
|
|
58
|
+
selectedMeasurement.kind,
|
|
59
|
+
id,
|
|
60
|
+
);
|
|
61
|
+
if (!measurement) return null;
|
|
62
|
+
measurements.push(measurement);
|
|
63
|
+
}
|
|
64
|
+
return measurements;
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
export function getCurrentMeasurementData(state: AppState) {
|
|
58
68
|
const selectedMeasurement = getCurrentMeasurement(state);
|
|
59
69
|
if (!selectedMeasurement) return null;
|
|
60
|
-
const
|
|
61
|
-
const display =
|
|
62
|
-
|
|
70
|
+
const kindAndIds = getMeasurementKindAndIds(state.data, selectedMeasurement);
|
|
71
|
+
const display = selectedMeasurement.map(
|
|
72
|
+
({ id }) => state.view.measurements[id],
|
|
73
|
+
);
|
|
74
|
+
return { data: selectedMeasurement, display, kindAndIds };
|
|
63
75
|
}
|
|
64
76
|
|
|
65
77
|
export function getFirstSelectedMeasurementData(state: AppState) {
|
|
@@ -91,7 +103,10 @@ export interface MeasurementKindAndId {
|
|
|
91
103
|
kind: MeasurementKind;
|
|
92
104
|
id: string;
|
|
93
105
|
}
|
|
94
|
-
|
|
106
|
+
export interface MeasurementKindAndIds {
|
|
107
|
+
kind: MeasurementKind;
|
|
108
|
+
ids: string[];
|
|
109
|
+
}
|
|
95
110
|
export function getMeasurementKindAndId(data: AppData, measurementId: string) {
|
|
96
111
|
for (const kind of measurementKinds) {
|
|
97
112
|
const measurement = getMeasurement(data.measurements, kind, measurementId);
|
|
@@ -99,17 +114,35 @@ export function getMeasurementKindAndId(data: AppData, measurementId: string) {
|
|
|
99
114
|
}
|
|
100
115
|
throw new Error(`Measurement kind for ${measurementId} not found`);
|
|
101
116
|
}
|
|
102
|
-
|
|
117
|
+
export function getMeasurementKindAndIds(
|
|
118
|
+
data: AppData,
|
|
119
|
+
measurementId: MeasurementBase[],
|
|
120
|
+
) {
|
|
121
|
+
let found = false;
|
|
122
|
+
for (const kind of measurementKinds) {
|
|
123
|
+
for (const { id } of measurementId) {
|
|
124
|
+
const measurement = getMeasurement(data.measurements, kind, id);
|
|
125
|
+
if (measurement) found = true;
|
|
126
|
+
if (found && !measurement) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Measurement kind for ${measurementId.join(', ')} not found`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (found) return { kind, ids: measurementId };
|
|
133
|
+
}
|
|
134
|
+
throw new Error(`Measurement kind for ${measurementId.join(', ')} not found`);
|
|
135
|
+
}
|
|
103
136
|
export function getSelectedMeasurement(
|
|
104
137
|
view: AppView,
|
|
105
|
-
):
|
|
138
|
+
): MeasurementKindAndIds | null {
|
|
106
139
|
const { selectedKind, selectedMeasurements } = view;
|
|
107
140
|
if (!selectedKind) return null;
|
|
108
141
|
const kind = selectedKind;
|
|
109
142
|
const currentMeasurements = selectedMeasurements[kind];
|
|
110
|
-
if (!currentMeasurements || currentMeasurements.length
|
|
111
|
-
const
|
|
112
|
-
return { kind,
|
|
143
|
+
if (!currentMeasurements || currentMeasurements.length === 0) return null;
|
|
144
|
+
const ids = currentMeasurements;
|
|
145
|
+
return { kind, ids };
|
|
113
146
|
}
|
|
114
147
|
|
|
115
148
|
export function getSelectedMeasurementOrFail(view: AppView) {
|
|
@@ -4,6 +4,8 @@ import * as RadixCheckbox from '@radix-ui/react-checkbox';
|
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
5
|
import { RxCheck, RxMinus } from 'react-icons/rx/index';
|
|
6
6
|
|
|
7
|
+
import { disabledColor, enabledColor } from './styles';
|
|
8
|
+
|
|
7
9
|
export type CheckedState = boolean | 'indeterminate';
|
|
8
10
|
|
|
9
11
|
interface CheckboxProps {
|
|
@@ -13,9 +15,6 @@ interface CheckboxProps {
|
|
|
13
15
|
onChange?: (checked: CheckedState) => void;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
const enabledColor = '#1677ff';
|
|
17
|
-
const disabledColor = '#b8b8b8';
|
|
18
|
-
|
|
19
18
|
export function Checkbox(props: CheckboxProps) {
|
|
20
19
|
const {
|
|
21
20
|
checked = 'indeterminate',
|