Skip to content

Commit e17d153

Browse files
committed
improve RectanglePositioner
1 parent 0944218 commit e17d153

File tree

6 files changed

+192
-36
lines changed

6 files changed

+192
-36
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import Field from './Field';
2+
import NumericInput from '../widgets/NumericInput';
3+
import PropTypes from 'prop-types';
4+
import React, {Component} from 'react';
5+
import {connectToContainer} from 'lib';
6+
import nestedProperty from 'plotly.js/src/lib/nested_property';
7+
8+
export class UnconnectedDualNumericFraction extends Component {
9+
constructor(props, context) {
10+
super(props, context);
11+
this.updatePlot = this.updatePlot.bind(this);
12+
this.updatePlot2 = this.updatePlot2.bind(this);
13+
}
14+
15+
updatePlot(value) {
16+
this.props.updatePlot(this.props.percentage ? value / 100 : value);
17+
}
18+
19+
updatePlot2(value) {
20+
this.props.updateContainer({
21+
[this.props.attr2]: this.props.percentage ? value / 100 : value,
22+
});
23+
}
24+
25+
render() {
26+
const {percentage, multiValued, attr2, step, min, max} = this.props;
27+
let fullValue = percentage
28+
? Math.round(100 * this.props.fullValue)
29+
: this.props.fullValue;
30+
let fullValue2 = nestedProperty(this.context.fullContainer, attr2).get();
31+
if (percentage) {
32+
fullValue2 = Math.round(100 * fullValue2);
33+
}
34+
let placeholder;
35+
let placeholder2;
36+
if (multiValued) {
37+
placeholder = fullValue;
38+
placeholder2 = fullValue2;
39+
fullValue = '';
40+
fullValue2 = '';
41+
}
42+
43+
return (
44+
<Field {...this.props}>
45+
<div className="numeric-input__wrapper">
46+
<NumericInput
47+
value={fullValue}
48+
defaultValue={this.props.defaultValue}
49+
placeholder={placeholder}
50+
step={step}
51+
min={min}
52+
max={max}
53+
onChange={this.updatePlot}
54+
onUpdate={this.updatePlot}
55+
showArrows={!this.props.hideArrows}
56+
showSlider={false}
57+
/>
58+
<NumericInput
59+
value={fullValue2}
60+
defaultValue={this.props.defaultValue}
61+
placeholder={placeholder2}
62+
step={step}
63+
min={min}
64+
max={max}
65+
onChange={this.updatePlot2}
66+
onUpdate={this.updatePlot2}
67+
showArrows={!this.props.hideArrows}
68+
showSlider={false}
69+
/>
70+
</div>
71+
</Field>
72+
);
73+
}
74+
}
75+
76+
UnconnectedDualNumericFraction.propTypes = {
77+
defaultValue: PropTypes.any,
78+
fullValue: PropTypes.any,
79+
min: PropTypes.number,
80+
max: PropTypes.number,
81+
multiValued: PropTypes.bool,
82+
hideArrows: PropTypes.bool,
83+
showSlider: PropTypes.bool,
84+
step: PropTypes.number,
85+
updatePlot: PropTypes.func,
86+
attr2: PropTypes.any,
87+
percentage: PropTypes.bool,
88+
...Field.propTypes,
89+
};
90+
91+
UnconnectedDualNumericFraction.contextTypes = {
92+
fullContainer: PropTypes.object,
93+
};
94+
95+
export default connectToContainer(UnconnectedDualNumericFraction);

src/components/fields/RectanglePositioner.js

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import Field from './Field';
22
import PropTypes from 'prop-types';
33
import React, {Component} from 'react';
44
import {connectToContainer} from 'lib';
5-
import {NumericFraction} from './derived';
65
import ResizableRect from 'react-resizable-rotatable-draggable';
6+
import RadioBlocks from '../widgets/RadioBlocks';
7+
import DualNumeric from './DualNumeric';
78

8-
const maxWidth = 300;
9+
const maxWidth = 286;
910
const gridRes = 8;
1011

1112
class UnconnectedRectanglePositioner extends Component {
@@ -18,6 +19,7 @@ class UnconnectedRectanglePositioner extends Component {
1819
y: ['yaxis.domain[0]', 'yaxis.domain[1]'],
1920
}
2021
: {x: ['domain.x[0]', 'domain.x[1]'], y: ['domain.y[0]', 'domain.y[1]']};
22+
this.state = {snap: true};
2123
}
2224

2325
sendUpdate({x, y, width, height, fieldWidthPx, fieldHeightPx}) {
@@ -26,7 +28,9 @@ class UnconnectedRectanglePositioner extends Component {
2628
const y0 = (fieldHeightPx - (height + y)) / fieldHeightPx;
2729
const y1 = (fieldHeightPx - y) / fieldHeightPx;
2830

29-
const snap = v => Math.round(v * gridRes) / gridRes;
31+
const snap = this.state.snap
32+
? v => Math.round(v * gridRes) / gridRes
33+
: v => v;
3034

3135
const payload = {};
3236

@@ -61,25 +65,48 @@ class UnconnectedRectanglePositioner extends Component {
6165
const left = fieldWidthPx * x[0];
6266
const top = fieldHeightPx * (1 - y[1]);
6367

68+
let zoomable = '';
69+
if (
70+
!fullContainer.xaxis ||
71+
!fullContainer.yaxis ||
72+
(!fullContainer.xaxis.overlaying && !fullContainer.yaxis.overlaying)
73+
) {
74+
zoomable = 'n, w, s, e, nw, ne, se, sw';
75+
} else if (!fullContainer.xaxis.overlaying) {
76+
zoomable = 'e, w';
77+
} else if (!fullContainer.yaxis.overlaying) {
78+
zoomable = 'n, s';
79+
}
80+
6481
return (
6582
<Field {...this.props} attr={attr}>
83+
<Field label={_('Snap to Grid')}>
84+
<RadioBlocks
85+
alignment="center"
86+
onOptionChange={snap => this.setState({snap: snap})}
87+
activeOption={this.state.snap}
88+
options={[
89+
{label: _('On'), value: true},
90+
{label: _('Off'), value: false},
91+
]}
92+
/>
93+
</Field>
6694
<div
95+
className="rect-container"
6796
style={{
6897
width: fieldWidthPx,
6998
height: fieldHeightPx,
70-
border: '1px solid grey',
71-
position: 'relative',
7299
}}
73100
>
74101
{Array(gridRes * gridRes)
75102
.fill(0)
76103
.map((v, i) => (
77104
<div
78105
key={i}
106+
className="rect-grid"
79107
style={{
80108
width: fieldWidthPx / gridRes - 1,
81109
height: fieldHeightPx / gridRes - 1,
82-
float: 'left',
83110
borderTop: i < gridRes ? '0' : '1px solid lightgray',
84111
borderLeft: i % gridRes ? '1px solid lightgray' : '0',
85112
}}
@@ -92,8 +119,8 @@ class UnconnectedRectanglePositioner extends Component {
92119
left={left}
93120
top={top}
94121
rotatable={false}
95-
draggable={false}
96-
zoomable="n, w, s, e, nw, ne, se, sw"
122+
draggable={!this.state.snap}
123+
zoomable={zoomable}
97124
onResize={style => {
98125
this.sendUpdate({
99126
fieldWidthPx,
@@ -104,12 +131,44 @@ class UnconnectedRectanglePositioner extends Component {
104131
y: style.top,
105132
});
106133
}}
134+
onDrag={(deltaX, deltaY) => {
135+
this.sendUpdate({
136+
fieldWidthPx,
137+
fieldHeightPx,
138+
width,
139+
height,
140+
x: left + deltaX,
141+
y: top + deltaY,
142+
});
143+
}}
107144
/>
108145
</div>
109-
<NumericFraction label={_('X Start')} attr={this.attr.x[0]} />
110-
<NumericFraction label={_('X End')} attr={this.attr.x[1]} />
111-
<NumericFraction label={_('Y Start')} attr={this.attr.y[0]} />
112-
<NumericFraction label={_('Y End')} attr={this.attr.y[1]} />
146+
{fullContainer.xaxis && fullContainer.xaxis.overlaying ? (
147+
''
148+
) : (
149+
<DualNumeric
150+
label={_('X')}
151+
attr={this.attr.x[0]}
152+
attr2={this.attr.x[1]}
153+
percentage
154+
step={1}
155+
min={0}
156+
max={100}
157+
/>
158+
)}
159+
{fullContainer.yaxis && fullContainer.yaxis.overlaying ? (
160+
''
161+
) : (
162+
<DualNumeric
163+
label={_('Y')}
164+
attr={this.attr.y[0]}
165+
attr2={this.attr.y[1]}
166+
percentage
167+
step={1}
168+
min={0}
169+
max={100}
170+
/>
171+
)}
113172
</Field>
114173
);
115174
}

src/components/fields/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Radio from './Radio';
1111
import TextEditor from './TextEditor';
1212
import DataSelector from './DataSelector';
1313
import Numeric from './Numeric';
14+
import DualNumeric from './DualNumeric';
1415
import AxisRangeValue from './AxisRangeValue';
1516
import Text from './Text';
1617
import SymbolSelector from './SymbolSelector';
@@ -78,6 +79,7 @@ export {
7879
LineDashSelector,
7980
LineShapeSelector,
8081
Numeric,
82+
DualNumeric,
8183
AxisRangeValue,
8284
Text,
8385
Radio,

src/components/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
LineDashSelector,
3131
LineShapeSelector,
3232
Numeric,
33+
DualNumeric,
3334
AxisRangeValue,
3435
Text,
3536
Radio,
@@ -125,6 +126,7 @@ export {
125126
LineDashSelector,
126127
LineShapeSelector,
127128
Numeric,
129+
DualNumeric,
128130
AxisRangeValue,
129131
Text,
130132
PlotlyPanel,

src/default_panels/GraphSubplotsPanel.js

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ import PropTypes from 'prop-types';
33
import {
44
SubplotAccordion,
55
RectanglePositioner,
6-
NumericFraction,
76
AxisOverlayDropdown,
87
PlotlySection,
9-
NumericFractionDomain,
108
TraceTypeSection,
119
AxisAnchorDropdown,
1210
AxisSide,
@@ -15,21 +13,14 @@ import {TRACE_TO_AXIS} from '../lib/constants';
1513

1614
const GraphSubplotsPanel = (props, {localize: _}) => (
1715
<SubplotAccordion canGroup>
18-
{/* <RectanglePositioner attr="domain.x[0]" />
19-
<RectanglePositioner attr="xaxis.domain[0]" cartesian /> */}
20-
<NumericFraction label={_('X Start')} attr={'domain.x[0]'} />
21-
<NumericFraction label={_('X End')} attr={'domain.x[1]'} />
22-
<NumericFraction label={_('Y Start')} attr={'domain.y[0]'} />
23-
<NumericFraction label={_('Y End')} attr={'domain.y[1]'} />
24-
25-
<PlotlySection name={_('X Boundaries')} attr="xaxis.domain[0]">
26-
<AxisOverlayDropdown label={_('Overlay')} attr="xaxis.overlaying" />
27-
<NumericFractionDomain
28-
label={_('Start Position')}
29-
attr="xaxis.domain[0]"
30-
/>
31-
<NumericFractionDomain label={_('End Position')} attr="xaxis.domain[1]" />
16+
<PlotlySection name={_('Boundaries')} attr="xaxis.domain[0]">
17+
<AxisOverlayDropdown label={_('X Overlay')} attr="xaxis.overlaying" />
18+
<AxisOverlayDropdown label={_('Y Overlay')} attr="yaxis.overlaying" />
3219
</PlotlySection>
20+
21+
<RectanglePositioner attr="domain.x[0]" />
22+
<RectanglePositioner attr="xaxis.domain[0]" cartesian />
23+
3324
<TraceTypeSection name={_('X Anchor')} traceTypes={TRACE_TO_AXIS.cartesian}>
3425
<AxisAnchorDropdown
3526
label={_('Anchor to')}
@@ -38,14 +29,6 @@ const GraphSubplotsPanel = (props, {localize: _}) => (
3829
/>
3930
<AxisSide label={_('Side')} attr="xaxis.side" />
4031
</TraceTypeSection>
41-
<PlotlySection name={_('Y Boundaries')} attr="yaxis.domain[0]">
42-
<AxisOverlayDropdown label={_('Overlay')} attr="yaxis.overlaying" />
43-
<NumericFractionDomain
44-
label={_('Start Position')}
45-
attr="yaxis.domain[0]"
46-
/>
47-
<NumericFractionDomain label={_('End Position')} attr="yaxis.domain[1]" />
48-
</PlotlySection>
4932
<TraceTypeSection name={_('Y Anchor')} traceTypes={TRACE_TO_AXIS.cartesian}>
5033
<AxisAnchorDropdown
5134
label={_('Anchor to')}

src/styles/components/fields/_field.scss

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@
7676
}
7777
}
7878
}
79+
80+
.rect,
81+
.square {
82+
border-color: var(--color-accent);
83+
}
84+
.rect-grid {
85+
border-color: var(--panel-background) !important;
86+
float: left;
87+
}
88+
.rect-container {
89+
margin: var(--spacing-eighth-unit) auto var(--spacing-quarter-unit) auto;
90+
border: 1px solid var(--color-border-default);
91+
position: relative;
92+
max-width: 294px;
93+
}
7994
}
8095

8196
.field .field {

0 commit comments

Comments
 (0)