Skip to content

Commit

Permalink
feat: customize measurement stroke color (#358)
Browse files Browse the repository at this point in the history
Ref: #313
  • Loading branch information
stropitek authored Nov 15, 2022
1 parent e419707 commit d3c41f8
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 31 deletions.
42 changes: 38 additions & 4 deletions src/app-data/appState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,21 @@ import {
import {
getFirstMeasurementOrFail,
getMeasurementOrFail,
iterateMeasurementEntries,
MeasurementKindAndId,
} from './data.helpers';

export interface MeasurementDisplay {
lineStroke: string;
}

export interface AppState {
data: DataState;
isLoading: boolean;
view: {
selectedMeasurements: Partial<Record<MeasurementKind, Array<string>>>;
selectedKind?: MeasurementKind;
measurements: Record<string, MeasurementDisplay>;
};
}

Expand All @@ -37,6 +44,7 @@ export function getEmptyAppState(): AppState {
isLoading: false,
view: {
selectedMeasurements: {},
measurements: {},
},
};
}
Expand Down Expand Up @@ -89,9 +97,16 @@ type AppStateAction =
| { type: 'LOAD_STATE'; payload: Omit<AppState, 'isLoading'> }
| {
type: 'SELECT_MEASUREMENT';
payload: { id: string; kind: MeasurementKind };
payload: MeasurementKindAndId;
}
| { type: 'SELECT_MEASUREMENT_KIND'; payload: MeasurementKind };
| { type: 'SELECT_MEASUREMENT_KIND'; payload: MeasurementKind }
| {
type: 'CHANGE_MEASUREMENT_DISPLAY';
payload: {
measurement: MeasurementKindAndId;
display: Partial<MeasurementDisplay>;
};
};

function actionHandler(draft: Draft<AppState>, action: AppStateAction) {
const type = action.type;
Expand All @@ -100,10 +115,11 @@ function actionHandler(draft: Draft<AppState>, action: AppStateAction) {
draft.data = getEmptyDataState();
return;
case 'ADD_MEASUREMENTS': {
mergeMeasurements(draft.data.measurements, action.payload);
const newMeasurements = action.payload;
mergeMeasurements(draft.data.measurements, newMeasurements);

for (const kind of Object.keys(kindsLabel).filter(
(k) => k in action.payload,
(k) => k in newMeasurements,
) as MeasurementKind[]) {
if (
!draft.view.selectedMeasurements[kind] &&
Expand All @@ -121,6 +137,12 @@ function actionHandler(draft: Draft<AppState>, action: AppStateAction) {
}
}

for (let measurement of iterateMeasurementEntries(newMeasurements)) {
draft.view.measurements[measurement.id] = {
lineStroke: 'red',
};
}

return;
}
case 'SELECT_MEASUREMENT': {
Expand Down Expand Up @@ -150,6 +172,18 @@ function actionHandler(draft: Draft<AppState>, action: AppStateAction) {
}
return;
}
case 'CHANGE_MEASUREMENT_DISPLAY': {
const measurement = getMeasurementOrFail(
draft.data.measurements,
action.payload.measurement.kind,
action.payload.measurement.id,
);
draft.view.measurements[measurement.id] = {
...draft.view.measurements[measurement.id],
...action.payload.display,
};
return;
}
case 'LOAD_START': {
draft.isLoading = true;
return;
Expand Down
52 changes: 49 additions & 3 deletions src/app-data/data.helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assertNotNull } from '../utils/assert';
import { assert, assertNotNull } from '../utils/assert';

import type { MeasurementKind, Measurements } from './DataState';
import { MeasurementKind, measurementKinds, Measurements } from './DataState';
import type { AppState } from './appState';

export function getMeasurement(
Expand Down Expand Up @@ -53,7 +53,37 @@ export function getCurrentMeasurement(state: AppState) {
);
}

export function getSelectedMeasurement(state: AppState) {
export function getCurrentMeasurementData(state: AppState) {
const selectedMeasurement = getCurrentMeasurement(state);
if (!selectedMeasurement) return null;
const kindAndId = getMeasurementKindAndId(state, selectedMeasurement.id);
const display = state.view.measurements[selectedMeasurement.id];
return { data: selectedMeasurement, display, kindAndId };
}

export interface MeasurementKindAndId {
kind: MeasurementKind;
id: string;
}

export function getMeasurementKindAndId(
state: AppState,
measurementId: string,
) {
for (let kind of measurementKinds) {
const measurement = getMeasurement(
state.data.measurements,
kind,
measurementId,
);
if (measurement) return { kind, id: measurementId };
}
throw new Error(`Measurement kind for ${measurementId} not found`);
}

export function getSelectedMeasurement(
state: AppState,
): MeasurementKindAndId | undefined {
const { selectedKind, selectedMeasurements } = state.view;
if (!selectedKind) return undefined;
const kind = selectedKind;
Expand All @@ -63,3 +93,19 @@ export function getSelectedMeasurement(state: AppState) {
const id = currentMeasurements[0];
return { kind, id };
}

export function getSelectedMeasurementOrFail(state: AppState) {
const selected = getSelectedMeasurement(state);
assert(selected);
return selected;
}

export function* iterateMeasurementEntries(
measurements: Partial<Measurements>,
) {
for (const measurementData of Object.values(measurements)) {
for (let x of measurementData.entries) {
yield x;
}
}
}
7 changes: 6 additions & 1 deletion src/app/explorer/MeasurementPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { xyToXYObject } from 'ml-spectra-processing';
import { useMemo } from 'react';
import { LineSeries, PlotController } from 'react-plot';

import type { MeasurementBase } from '../../app-data/index';
import type { MeasurementBase, MeasurementDisplay } from '../../app-data/index';
import { BasicComponent } from '../helpers/react-plot';

type Measurement = Pick<
Expand All @@ -11,6 +11,7 @@ type Measurement = Pick<
>;
export interface MeasurementPlotProps {
measurement: Measurement;
measurementDisplay: MeasurementDisplay;
dataIndex?: number;
xVariableName?: string;
yVariableName?: string;
Expand All @@ -34,6 +35,7 @@ export function MeasurementPlot(props: MeasurementPlotProps) {
}
function MeasurementComponent(props: MeasurementPlotProps) {
const {
measurementDisplay,
measurement: { data },
dataIndex = 0,
xVariableName = 'x',
Expand All @@ -59,6 +61,9 @@ function MeasurementComponent(props: MeasurementPlotProps) {
return (
<BasicComponent {...props}>
<LineSeries
lineStyle={{
stroke: measurementDisplay.lineStroke,
}}
data={xyToXYObject({
x: x.data,
y: y.data,
Expand Down
33 changes: 33 additions & 0 deletions src/app/panels/MeasurementPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { MeasurementDisplay, useAppDispatch } from '../../app-data/appState';
import type { MeasurementKindAndId } from '../../app-data/data.helpers';
import { ColorPicker } from '../../components/index';

export function MeasurementPanel(props: {
measurementDisplay: MeasurementDisplay;
measurement: MeasurementKindAndId;
}) {
const { measurement, measurementDisplay } = props;
const dispatch = useAppDispatch();

return (
<div style={{ display: 'flex', padding: 8 }}>
<div style={{ flex: '1 1 0' }}>Stroke color</div>
<ColorPicker
color={{
hex: measurementDisplay.lineStroke,
}}
onChangeComplete={({ hex }) => {
dispatch({
type: 'CHANGE_MEASUREMENT_DISPLAY',
payload: {
measurement,
display: {
lineStroke: hex,
},
},
});
}}
/>
</div>
);
}
22 changes: 16 additions & 6 deletions src/pages/big-map/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
import { css } from '@emotion/react';

import {
getCurrentMeasurement,
getSelectedMeasurement,
getCurrentMeasurementData,
useAppDispatch,
useAppState,
} from '../../app-data/index';
Expand All @@ -12,6 +11,7 @@ import {
MeasurementInfoPanel,
MeasurementsPanel,
} from '../../app/index';
import { MeasurementPanel } from '../../app/panels/MeasurementPanel';
import {
Accordion,
DropZoneContainer,
Expand Down Expand Up @@ -53,7 +53,8 @@ const mainCss = {
export default function MainLayout() {
const dispatch = useAppDispatch();
const appState = useAppState();
const measurement = getCurrentMeasurement(appState);

const measurement = getCurrentMeasurementData(appState);
const loadFiles = useLoadFiles();

return (
Expand Down Expand Up @@ -82,7 +83,8 @@ export default function MainLayout() {
<DropZoneContainer onDrop={loadFiles}>
{measurement ? (
<MeasurementExplorer
measurement={measurement}
measurementDisplay={measurement.display}
measurement={measurement.data}
width="100%"
height="100%"
kind={appState.view.selectedKind === 'mass' ? 'mass' : '1d'}
Expand All @@ -101,7 +103,7 @@ export default function MainLayout() {
payload: kind,
});
}}
selectedMeasurement={getSelectedMeasurement(appState)}
selectedMeasurement={measurement?.kindAndId}
onMeasurementSelect={({ measurement, kind }) => {
dispatch({
type: 'SELECT_MEASUREMENT',
Expand All @@ -110,9 +112,17 @@ export default function MainLayout() {
}}
/>
</Accordion.Item>
{measurement ? (
<Accordion.Item title="Measurement display" defaultOpened>
<MeasurementPanel
measurement={measurement.kindAndId}
measurementDisplay={measurement.display}
/>
</Accordion.Item>
) : null}
<Accordion.Item title="Measurement info" defaultOpened>
{measurement && (
<MeasurementInfoPanel measurement={measurement} />
<MeasurementInfoPanel measurement={measurement.data} />
)}
</Accordion.Item>
</Accordion>
Expand Down
18 changes: 14 additions & 4 deletions src/pages/demo/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useKbsGlobal } from 'react-kbs';

import {
download,
getCurrentMeasurement,
getCurrentMeasurementData,
getSelectedMeasurement,
useAppDispatch,
useAppState,
Expand All @@ -23,6 +23,7 @@ import {
MeasurementInfoPanel,
MeasurementsPanel,
} from '../../app/index';
import { MeasurementPanel } from '../../app/panels/MeasurementPanel';
import {
Accordion,
DropZoneContainer,
Expand Down Expand Up @@ -50,7 +51,7 @@ function ErrorFallback({ error, resetErrorBoundary }) {
export default function DropZoneArea() {
const dispatch = useAppDispatch();
const appState = useAppState();
const measurement = getCurrentMeasurement(appState);
const measurement = getCurrentMeasurementData(appState);
const hashSearchParams = useHashSearchParams();
const fileListParam = hashSearchParams.get('filelist');
const loadFiles = useLoadFiles();
Expand Down Expand Up @@ -157,7 +158,8 @@ export default function DropZoneArea() {
<DropZoneContainer onDrop={loadFiles}>
{measurement ? (
<MeasurementExplorer
measurement={measurement}
measurementDisplay={measurement.display}
measurement={measurement.data}
width="100%"
height="100%"
kind={
Expand Down Expand Up @@ -201,9 +203,17 @@ export default function DropZoneArea() {
/>
</div>
</Accordion.Item>
{measurement ? (
<Accordion.Item title="Measurement display">
<MeasurementPanel
measurement={measurement.kindAndId}
measurementDisplay={measurement.display}
/>
</Accordion.Item>
) : null}
<Accordion.Item title="Info Panel">
{measurement && (
<MeasurementInfoPanel measurement={measurement} />
<MeasurementInfoPanel measurement={measurement.data} />
)}
</Accordion.Item>
</Accordion>
Expand Down
Loading

0 comments on commit d3c41f8

Please sign in to comment.