Skip to content

Commit d022316

Browse files
introduce SymbolSelector
1 parent 8afad28 commit d022316

File tree

8 files changed

+648
-13
lines changed

8 files changed

+648
-13
lines changed

src/DefaultEditor.js

+6-13
Original file line numberDiff line numberDiff line change
@@ -143,24 +143,17 @@ class DefaultEditor extends Component {
143143
</Section>
144144

145145
<Section name={_('Points')}>
146-
<Numeric
147-
label={_('Marker Opacity')}
148-
step={0.1}
149-
attr="marker.opacity"
150-
/>
151-
152-
<ColorPicker label={_('Marker Color')} attr="marker.color" />
153-
146+
<ColorPicker label={_('Color')} attr="marker.color" />
147+
<Numeric label={_('Opacity')} step={0.1} attr="marker.opacity" />
154148
<Numeric label={_('Size')} attr="marker.size" />
155-
156-
<Numeric label={_('Line width')} attr="marker.line.width" />
149+
<SymbolSelector label={_('Symbol')} attr="marker.symbol" />
150+
<Numeric label={_('Border Width')} attr="marker.line.width" />
151+
<ColorPicker label={_('Border Color')} attr="marker.line.color" />
157152
</Section>
158153

159154
<Section name={_('Lines')}>
160155
<Numeric label={_('Width')} step={1.0} attr="line.width" />
161-
162-
<ColorPicker label={_('Line color')} attr="line.color" />
163-
156+
<ColorPicker label={_('Line Color')} attr="line.color" />
164157
<Radio
165158
label={_('Connect Gaps')}
166159
attr="connectgaps"
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import Field from './Field';
2+
import PropTypes from 'prop-types';
3+
import React, {Component} from 'react';
4+
import SymbolSelectorWidget from '../widgets/SymbolSelector';
5+
import nestedProperty from 'plotly.js/src/lib/nested_property';
6+
import {SYMBOLS} from '../../lib/constants';
7+
import {connectToContainer} from '../../lib';
8+
9+
class SymbolSelector extends Component {
10+
render() {
11+
const {fullContainer, fullValue, updatePlot} = this.props;
12+
13+
const markerColor = nestedProperty(fullContainer, 'marker.color').get();
14+
const borderWidth = nestedProperty(
15+
fullContainer,
16+
'marker.line.width'
17+
).get();
18+
19+
let borderColor = markerColor;
20+
if (borderWidth) {
21+
borderColor = nestedProperty(fullContainer, 'marker.line.color').get();
22+
}
23+
24+
let symbolOptions;
25+
if (this.props.is3D) {
26+
symbolOptions = SYMBOLS.filter(option => {
27+
return option.threeD;
28+
});
29+
} else {
30+
symbolOptions = [...SYMBOLS];
31+
}
32+
33+
return (
34+
<Field {...this.props}>
35+
<SymbolSelectorWidget
36+
markerColor={markerColor}
37+
borderColor={borderColor}
38+
value={fullValue()}
39+
onChange={updatePlot}
40+
symbolOptions={symbolOptions}
41+
/>
42+
</Field>
43+
);
44+
}
45+
}
46+
47+
SymbolSelector.propTypes = {
48+
defaultValue: PropTypes.number,
49+
fullValue: PropTypes.func,
50+
updatePlot: PropTypes.func,
51+
...Field.propTypes,
52+
};
53+
54+
SymbolSelector.defaultProps = {
55+
showArrows: true,
56+
};
57+
58+
export default connectToContainer(SymbolSelector);

src/components/fields/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Info from './Info';
77
import Radio from './Radio';
88
import DataSelector from './DataSelector';
99
import Numeric from './Numeric';
10+
import SymbolSelector from './SymbolSelector';
1011
import TraceSelector from './TraceSelector';
1112

1213
export {
@@ -19,5 +20,6 @@ export {
1920
Radio,
2021
DataSelector,
2122
Numeric,
23+
SymbolSelector,
2224
TraceSelector,
2325
};

src/components/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
Radio,
99
DataSelector,
1010
Numeric,
11+
SymbolSelector,
1112
TraceSelector,
1213
} from './fields';
1314

@@ -29,6 +30,7 @@ export {
2930
PanelMenuWrapper,
3031
Radio,
3132
Section,
33+
SymbolSelector,
3234
TraceAccordion,
3335
TraceSelector,
3436
};
+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import React, {Component} from 'react';
2+
import PropTypes from 'prop-types';
3+
import tinyColor from 'tinycolor2';
4+
import ModalBox from '../containers/ModalBox';
5+
6+
const tooLightFactor = 0.8;
7+
8+
function tooLight(color) {
9+
const hslColor = tinyColor(color).toHsl();
10+
return hslColor.l > tooLightFactor;
11+
}
12+
13+
export default class SymbolSelector extends Component {
14+
constructor(props) {
15+
super(props);
16+
this.state = {
17+
isOpen: false,
18+
};
19+
this.togglePanel = this.togglePanel.bind(this);
20+
}
21+
22+
shouldComponentUpdate(nextProps, nextState) {
23+
const {markerColor, borderColor} = this.props;
24+
const {
25+
markerColor: nextMarkerColor,
26+
borderColor: nextBorderColor,
27+
} = nextProps;
28+
29+
return (
30+
this.props.value !== nextProps.value ||
31+
this.state.isOpen !== nextState.isOpen ||
32+
markerColor !== nextMarkerColor ||
33+
borderColor !== nextBorderColor
34+
);
35+
}
36+
37+
togglePanel() {
38+
this.setState({isOpen: !this.state.isOpen});
39+
}
40+
41+
renderActiveOption() {
42+
const {markerColor, borderColor, symbolOptions, value} = this.props;
43+
const currentSymbol = symbolOptions.find(symbol => symbol.value === value);
44+
if (!currentSymbol) {
45+
return (
46+
<span
47+
style={{
48+
paddingTop: '5px',
49+
paddingLeft: '15px',
50+
}}
51+
>
52+
{'-'}
53+
</span>
54+
);
55+
}
56+
57+
const symbolStyle = {
58+
stroke: currentSymbol.fill === 'none' ? markerColor : borderColor,
59+
strokeOpacity: '1',
60+
strokeWidth: '2px',
61+
fill: currentSymbol.fill === 'none' ? 'none' : markerColor,
62+
};
63+
64+
return (
65+
<span>
66+
<svg width="18" height="18">
67+
<g transform="translate(8,8)">
68+
<path d={currentSymbol.label} style={symbolStyle} />
69+
</g>
70+
</svg>
71+
</span>
72+
);
73+
}
74+
75+
renderOptions() {
76+
const {markerColor, borderColor, symbolOptions} = this.props;
77+
return symbolOptions.map(option => {
78+
const {fill, value, label} = option;
79+
80+
const symbolStyle = {
81+
stroke: fill === 'none' ? markerColor : borderColor,
82+
strokeOpacity: '1',
83+
strokeWidth: '2px',
84+
fill: fill === 'none' ? 'none' : markerColor,
85+
};
86+
return (
87+
<div
88+
className="symbol-selector__item"
89+
key={value}
90+
onClick={this.props.onChange}
91+
>
92+
<svg
93+
width="28"
94+
height="28"
95+
className="symbol-selector__symbol"
96+
data-value={value}
97+
>
98+
<g transform="translate(14,14)">
99+
<path d={label} style={symbolStyle} />
100+
</g>
101+
</svg>
102+
</div>
103+
);
104+
});
105+
}
106+
107+
render() {
108+
const {isOpen} = this.state;
109+
const {markerColor} = this.props;
110+
111+
// TODO link these colors into theme
112+
const backgroundColor = tooLight(markerColor) ? '#bec8d9' : 'white';
113+
114+
return (
115+
<div>
116+
<div className="symbol-selector__toggle" onClick={this.togglePanel}>
117+
<span className="symbol-selector__toggle_option">
118+
{this.renderActiveOption()}
119+
</span>
120+
<span className="symbol-selector__toggle__caret">
121+
<i className="icon-caret-down" />
122+
</span>
123+
</div>
124+
{isOpen ? (
125+
<ModalBox
126+
onClose={this.togglePanel}
127+
backgroundColor={backgroundColor}
128+
>
129+
{this.renderOptions()}
130+
</ModalBox>
131+
) : null}
132+
</div>
133+
);
134+
}
135+
}
136+
137+
SymbolSelector.propTypes = {
138+
markerColor: PropTypes.string,
139+
borderColor: PropTypes.string,
140+
value: PropTypes.string,
141+
onChange: PropTypes.func,
142+
symbolOptions: PropTypes.array,
143+
};

src/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
Radio,
2626
Section,
2727
MenuPanel,
28+
SymbolSelector,
2829
TraceAccordion,
2930
TraceSelector,
3031
} from './components';
@@ -47,6 +48,7 @@ export {
4748
PanelMenuWrapper,
4849
Radio,
4950
Section,
51+
SymbolSelector,
5052
TraceAccordion,
5153
TraceSelector,
5254
connectAxesToLayout,

0 commit comments

Comments
 (0)