Skip to content

Commit dc02c57

Browse files
authored
feat(CV): panel layout improvement (#283)
* Improve CV layout * Enhance layout and styling for MultiJcampsViewer and Cyclic Voltammetry Pane * chore(cv): hide Integration and Multiplicity in CmdBar for CV layout * fix(cv): ensure d3Svg is in view before triggering peak actions and adjust click coordinates * fixup: merge master - resolve conflicts
1 parent bf96aa7 commit dc02c57

5 files changed

Lines changed: 333 additions & 91 deletions

File tree

cypress/e2e/cv_spec.cy.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ describe('CV', () => {
1313

1414
function addMaxPeak(view, offset=0) {
1515
cy.get('.btn-sv-bar-addpeak').click()
16+
cy.get('.d3Svg').scrollIntoView()
1617
cy.get('.d3Svg')
1718
.trigger('click', 1110 + offset, 480, {
1819
which: 1,
@@ -22,24 +23,27 @@ describe('CV', () => {
2223

2324
function addMinPeak(view, offset=0) {
2425
cy.get('.btn-sv-bar-addpeak').click()
26+
cy.get('.d3Svg').scrollIntoView()
2527
cy.get('.d3Svg')
26-
.trigger('click', 1050 + offset, 1480, {
28+
.trigger('click', 1050 + offset, 750, {
2729
which: 1,
2830
view: view,
2931
})
3032
}
3133

3234
function addPecker(view, offset=0) {
3335
cy.get('.btn-sv-bar-addpecker').click()
36+
cy.get('.d3Svg').scrollIntoView()
3437
cy.get('.d3Svg')
35-
.trigger('click', 1350 + offset, 1480, {
38+
.trigger('click', 1350 + offset, 750, {
3639
which: 1,
3740
view: view,
3841
})
3942
}
4043

4144
function removeMaxPeak(view, offset=0) {
4245
cy.get('.btn-sv-bar-rmpeak').click()
46+
cy.get('.d3Svg').scrollIntoView()
4347
cy.get('.d3Svg')
4448
.trigger('click', 1110 + offset, 450, {
4549
which: 1,
@@ -49,17 +53,19 @@ describe('CV', () => {
4953

5054
function removeMinPeak(view, offset=0) {
5155
cy.get('.btn-sv-bar-rmpeak').click()
56+
cy.get('.d3Svg').scrollIntoView()
5257
cy.get('.d3Svg')
53-
.trigger('click', 1050 + offset, 900, {
58+
.trigger('click', 1050 + offset, 750, {
5459
which: 1,
5560
view: view,
5661
})
5762
}
5863

5964
function removePecker(view, offset=0) {
6065
cy.get('.btn-sv-bar-rmpecker').click()
66+
cy.get('.d3Svg').scrollIntoView()
6167
cy.get('.d3Svg')
62-
.trigger('click', 1350 + offset, 800, {
68+
.trigger('click', 1350 + offset, 750, {
6369
which: 1,
6470
view: view,
6571
})

src/components/d3_multi/index.js

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
Topic2Seed, Feature2Peak, ToThresEndPts, ToShiftPeaks,
1010
Feature2MaxMinPeak,
1111
} from '../../helpers/chem';
12+
import Format from '../../helpers/format';
1213
import { resetAll } from '../../actions/manager';
1314
import { selectUiSweep, scrollUiWheel, clickUiTarget } from '../../actions/ui';
1415
import { LIST_NON_BRUSH_TYPES } from '../../constants/list_ui';
@@ -31,15 +32,21 @@ class ViewerMulti extends React.Component {
3132
entities, clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct,
3233
} = this.props;
3334
this.rootKlass = '.d3Line';
35+
this.containerRef = React.createRef();
36+
this.currentSize = null;
37+
this.resizeObserver = null;
3438

3539
this.focus = new MultiFocus({
3640
W, H, entities, clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct,
3741
});
3842

3943
this.normChange = this.normChange.bind(this);
44+
this.handleResize = this.handleResize.bind(this);
4045
}
4146

4247
componentDidMount() {
48+
this.renderChart(this.props, true);
49+
this.setupResizeObserver();
4350
const {
4451
curveSt,
4552
seed, peak, cLabel, xLabel, yLabel, feature,
@@ -130,6 +137,10 @@ class ViewerMulti extends React.Component {
130137
const filterSeed = seed;
131138
const filterPeak = peak;
132139

140+
if (Format.isCyclicVoltaLayout(layoutSt)) {
141+
this.handleResize();
142+
}
143+
133144
this.focus.update({
134145
entities,
135146
curveSt,
@@ -152,6 +163,49 @@ class ViewerMulti extends React.Component {
152163

153164
componentWillUnmount() {
154165
drawDestroy(this.rootKlass);
166+
this.teardownResizeObserver();
167+
}
168+
169+
handleResize() {
170+
const { layoutSt } = this.props;
171+
if (!Format.isCyclicVoltaLayout(layoutSt)) return;
172+
const size = this.getContainerSize();
173+
if (!size) return;
174+
if (!this.currentSize
175+
|| size.width !== this.currentSize.width
176+
|| size.height !== this.currentSize.height) {
177+
this.renderChart(this.props, false);
178+
}
179+
}
180+
181+
getContainerSize() {
182+
const node = this.containerRef.current;
183+
if (!node) return null;
184+
const { clientWidth, clientHeight } = node;
185+
if (!clientWidth || !clientHeight) return null;
186+
return { width: clientWidth, height: clientHeight };
187+
}
188+
189+
getTargetSize(layoutSt) {
190+
if (Format.isCyclicVoltaLayout(layoutSt)) {
191+
const size = this.getContainerSize();
192+
if (size) return size;
193+
}
194+
return { width: W, height: H };
195+
}
196+
197+
setupResizeObserver() {
198+
if (typeof ResizeObserver === 'undefined') return;
199+
if (!this.containerRef.current || this.resizeObserver) return;
200+
this.resizeObserver = new ResizeObserver(this.handleResize);
201+
this.resizeObserver.observe(this.containerRef.current);
202+
}
203+
204+
teardownResizeObserver() {
205+
if (this.resizeObserver) {
206+
this.resizeObserver.disconnect();
207+
this.resizeObserver = null;
208+
}
155209
}
156210

157211
normChange(prevProps) {
@@ -162,9 +216,81 @@ class ViewerMulti extends React.Component {
162216
}
163217
}
164218

219+
renderChart(props, shouldReset) {
220+
const {
221+
curveSt,
222+
seed, peak, cLabel, xLabel, yLabel, feature,
223+
tTrEndPts, tSfPeaks, editPeakSt, layoutSt,
224+
sweepExtentSt, isUiNoBrushSt,
225+
isHidden, resetAllAct, cyclicvoltaSt,
226+
integationSt, mtplySt, axesUnitsSt,
227+
entities, clickUiTargetAct, selectUiSweepAct, scrollUiWheelAct,
228+
} = props;
229+
230+
const size = this.getTargetSize(layoutSt);
231+
this.currentSize = size;
232+
233+
drawDestroy(this.rootKlass);
234+
if (shouldReset) {
235+
resetAllAct(feature);
236+
}
237+
238+
let xxLabel = xLabel;
239+
let yyLabel = yLabel;
240+
241+
if (axesUnitsSt) {
242+
const { curveIdx } = curveSt;
243+
const { axes } = axesUnitsSt;
244+
let selectedAxes = axes[curveIdx];
245+
if (!selectedAxes) {
246+
selectedAxes = { xUnit: '', yUnit: '' };
247+
}
248+
const { xUnit, yUnit } = selectedAxes;
249+
xxLabel = xUnit === '' ? xLabel : xUnit;
250+
yyLabel = yUnit === '' ? yLabel : yUnit;
251+
}
252+
253+
const filterSeed = seed;
254+
const filterPeak = peak;
255+
256+
this.focus = new MultiFocus({
257+
W: size.width,
258+
H: size.height,
259+
entities,
260+
clickUiTargetAct,
261+
selectUiSweepAct,
262+
scrollUiWheelAct,
263+
});
264+
265+
drawMain(this.rootKlass, size.width, size.height);
266+
this.focus.create({
267+
curveSt,
268+
filterSeed,
269+
filterPeak,
270+
tTrEndPts,
271+
tSfPeaks,
272+
editPeakSt,
273+
layoutSt,
274+
sweepExtentSt,
275+
isUiNoBrushSt,
276+
cyclicvoltaSt,
277+
integationSt,
278+
mtplySt,
279+
});
280+
drawLabel(this.rootKlass, cLabel, xxLabel, yyLabel);
281+
drawDisplay(this.rootKlass, isHidden);
282+
drawArrowOnCurve(this.rootKlass, isHidden);
283+
}
284+
165285
render() {
286+
const { layoutSt } = this.props;
287+
const isCyclicVolta = Format.isCyclicVoltaLayout(layoutSt);
166288
return (
167-
<div className="d3Line" />
289+
<div
290+
className="d3Line"
291+
ref={this.containerRef}
292+
style={isCyclicVolta ? { height: '100%' } : undefined}
293+
/>
168294
);
169295
}
170296
}

src/components/multi_jcamps_viewer.js

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ react/require-default-props, react/no-unused-prop-types, react/jsx-boolean-value
33
prefer-object-spread */
44
import React from 'react';
55
import PropTypes from 'prop-types';
6+
import classNames from 'classnames';
67
import { connect } from 'react-redux';
78
import { bindActionCreators, compose } from 'redux';
89

910
import Grid from '@mui/material/Grid';
1011
import { withStyles } from '@mui/styles';
1112

1213
import PanelViewer from './panel/index';
14+
import CyclicVoltammetryPanel from './panel/cyclic_voltamery_data';
1315
import CmdBar from './cmd_bar/index';
1416
import ViewerMulti from './d3_multi/index';
1517

@@ -31,6 +33,32 @@ const styles = () => ({
3133
tabLabel: {
3234
fontSize: '14px',
3335
},
36+
cvEditor: {
37+
height: 'calc(90vh - 220px)',
38+
display: 'flex',
39+
flexDirection: 'column',
40+
minHeight: 0,
41+
overflow: 'hidden',
42+
},
43+
cvTopRow: {
44+
flex: '1 1 auto',
45+
minHeight: 0,
46+
overflow: 'hidden',
47+
},
48+
cvViewerCol: {
49+
height: '100%',
50+
minHeight: 0,
51+
display: 'flex',
52+
flexDirection: 'column',
53+
},
54+
cvViewerWrap: {
55+
flex: '1 1 auto',
56+
minHeight: 0,
57+
},
58+
cvPanelBelow: {
59+
marginTop: 16,
60+
width: '100%',
61+
},
3462
});
3563

3664
const seperatingSubLayout = (entities, featureCondition, layoutSt) => {
@@ -67,6 +95,8 @@ class MultiJcampsViewer extends React.Component { // eslint-disable-line
6795
const { integrations } = integrationSt;
6896
const currentIntegration = integrations[curveIdx];
6997

98+
const isCyclicVolta = Format.isCyclicVoltaLayout(layoutSt);
99+
70100
return (
71101
<div className={classes.root}>
72102
<CmdBar
@@ -75,16 +105,27 @@ class MultiJcampsViewer extends React.Component { // eslint-disable-line
75105
editorOnly={true}
76106
hideThreshold={!Format.isNmrLayout(layoutSt)}
77107
/>
78-
<div className="react-spectrum-editor">
79-
<Grid container>
80-
<Grid item xs={9}>
81-
<ViewerMulti
82-
entities={entities}
83-
topic={topic}
84-
xLabel={feature.xUnit}
85-
yLabel={feature.yUnit}
86-
feature={feature}
87-
/>
108+
<div className={classNames('react-spectrum-editor', isCyclicVolta && classes.cvEditor)}>
109+
<Grid container className={isCyclicVolta ? classes.cvTopRow : undefined}>
110+
<Grid item xs={9} className={isCyclicVolta ? classes.cvViewerCol : undefined}>
111+
<div className={isCyclicVolta ? classes.cvViewerWrap : undefined}>
112+
<ViewerMulti
113+
entities={entities}
114+
topic={topic}
115+
xLabel={feature.xUnit}
116+
yLabel={feature.yUnit}
117+
feature={feature}
118+
/>
119+
</div>
120+
{isCyclicVolta ? (
121+
<div className={classes.cvPanelBelow}>
122+
<CyclicVoltammetryPanel
123+
jcampIdx={curveIdx}
124+
feature={feature}
125+
userManualLink={userManualLink ? userManualLink.cv : undefined}
126+
/>
127+
</div>
128+
) : null}
88129
</Grid>
89130
<Grid item xs={3} align="center">
90131
<PanelViewer
@@ -99,6 +140,7 @@ class MultiJcampsViewer extends React.Component { // eslint-disable-line
99140
descriptions={descriptions}
100141
canChangeDescription={canChangeDescription}
101142
onDescriptionChanged={onDescriptionChanged}
143+
hideCyclicVolta={isCyclicVolta}
102144
/>
103145
</Grid>
104146
</Grid>

0 commit comments

Comments
 (0)