Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 27 additions & 81 deletions src/components/fields/MarkerColor.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import Field from './Field';
import PropTypes from 'prop-types';
import React, {Component, Fragment} from 'react';
import {adjustColorscale, connectToContainer} from 'lib';
import {connectToContainer} from 'lib';
import RadioBlocks from '../widgets/RadioBlocks';
import Color from './Color';
import MultiColorPicker from './MultiColorPicker';
import Colorscale from './Colorscale';
import Numeric from './Numeric';
import Radio from './Radio';
Expand Down Expand Up @@ -37,14 +37,16 @@ class UnconnectedMarkerColor extends Component {
constant: type === 'constant' ? props.fullValue : COLORS.mutedBlue,
variable: type === 'variable' ? props.fullValue : null,
},
constantSelectedOption:
selectedConstantColorOption:
type === 'constant' && props.multiValued ? 'multiple' : 'single',
};

this.setType = this.setType.bind(this);
this.setValue = this.setValue.bind(this);
this.setColor = this.setColor.bind(this);
this.setColorScale = this.setColorScale.bind(this);
this.setColors = this.setColors.bind(this);
this.onConstantColorOptionChange = this.onConstantColorOptionChange.bind(
this
);
}

setType(type) {
Expand All @@ -67,7 +69,7 @@ class UnconnectedMarkerColor extends Component {
}
}

setValue(inputValue) {
setColor(inputValue) {
const {type} = this.state;

this.setState(
Expand Down Expand Up @@ -99,88 +101,32 @@ class UnconnectedMarkerColor extends Component {
);
}

setColors(colorscale, colorscaleType) {
const numberOfTraces = this.context.traceIndexes.length;
const colors = colorscale.map(c => c[1]);

let adjustedColors = colors;

if (colorscaleType !== 'categorical') {
adjustedColors = adjustColorscale(colors, numberOfTraces, colorscaleType);
}

if (
adjustedColors.every(c => c === adjustedColors[0]) ||
colorscaleType === 'categorical'
) {
adjustedColors = adjustColorscale(
colors,
numberOfTraces,
colorscaleType,
{repeat: true}
);
}

const updates = adjustedColors.map(color => ({
['marker.color']: color,
}));

onConstantColorOptionChange(value) {
this.setState({
colorscale: adjustedColors,
selectedConstantColorOption: value,
});

this.context.updateContainer(updates);
}

renderConstantControls() {
const _ = this.context.localize;
const constantOptions = [
{label: _('Single'), value: 'single'},
{label: _('Multiple'), value: 'multiple'},
];

if (this.context.traceIndexes.length > 1) {
return (
<div className="markercolor-constantcontrols__container">
<RadioBlocks
options={constantOptions}
activeOption={this.state.constantSelectedOption}
onOptionChange={value =>
this.setState({constantSelectedOption: value})
}
/>
<Info>
{this.state.constantSelectedOption === 'single'
? _('All traces will be colored in the the same color.')
: _(
'Each trace will be colored according to the selected colorscale.'
)}
</Info>
{this.state.constantSelectedOption === 'single' ? (
<Color
attr="marker.color"
updatePlot={this.setValue}
fullValue={this.state.value.constant}
/>
) : (
<Colorscale
suppressMultiValuedMessage
attr="marker.color"
updatePlot={this.setColors}
colorscale={this.state.colorscale}
initialCategory={'categorical'}
/>
)}
</div>
);
}

return (
<Color
attr="marker.color"
updatePlot={this.setValue}
fullValue={this.state.value.constant}
/>
<div className="markercolor-constantcontrols__container">
<MultiColorPicker
attr="marker.color"
multiColorMessage={_(
'Each trace will be colored according to the selected colorscale.'
)}
singleColorMessage={_(
'All traces will be colored in the the same color.'
)}
setColor={this.setColor}
setColorScale={this.setColorScale}
onConstantColorOptionChange={this.onConstantColorOptionChange}
parentSelectedConstantColorOption={
this.state.selectedConstantColorOption
}
/>
</div>
);
}

Expand Down
167 changes: 167 additions & 0 deletions src/components/fields/MultiColorPicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import React, {Component} from 'react';
import Color from './Color';
import Colorscale from './Colorscale';
import Info from './Info';
import Field from './Field';
import RadioBlocks from '../widgets/RadioBlocks';
import PropTypes from 'prop-types';
import {adjustColorscale, connectToContainer} from 'lib';

class UnconnectedMultiColorPicker extends Component {
constructor(props, context) {
super(props, context);
this.state = {
selectedConstantColorOption:
context.traceIndexes.length > 1 &&
props.fullValue.every(v => v[1] === props.fullValue[0][1])
? 'single'
: 'multiple',
};
this.setColor = this.setColor.bind(this);
this.setColors = this.setColors.bind(this);
}

setColor(color) {
this.props.updatePlot(color);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just pass updatePlot as a prop?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is passed, from the connect wrapper, I just prefer having a separate function,
one setColor, one setColors, that's all

}

setColors(colorscale, colorscaleType) {
const numberOfTraces = this.context.traceIndexes.length;
const colors = colorscale.map(c => c[1]);

let adjustedColors = colors;

if (colorscaleType !== 'categorical') {
adjustedColors = adjustColorscale(colors, numberOfTraces, colorscaleType);
}

if (
adjustedColors.every(c => c === adjustedColors[0]) ||
colorscaleType === 'categorical'
) {
adjustedColors = adjustColorscale(
colors,
numberOfTraces,
colorscaleType,
{repeat: true}
);
}

const updates = adjustedColors.map(color => ({
[this.props.attr]: color,
}));

this.context.updateContainer(updates);
}

render() {
const _ = this.context.localize;
const constantOptions = [
{label: _('Single'), value: 'single'},
{label: _('Multiple'), value: 'multiple'},
];
const selectedConstantColorOption = this.props
.parentSelectedConstantColorOption
? this.props.parentSelectedConstantColorOption
: this.state.selectedConstantColorOption;

const multiMessage = this.props.multiColorMessage
? this.props.multiColorMessage
: _('Each will be colored according to the selected colors.');

const singleMessage = this.props.singleColorMessage
? this.props.singleColorMessage
: _('All will be colored in the same color.');

if (this.context.traceIndexes.length > 1) {
return (
<Field {...this.props} suppressMultiValuedMessage>
<RadioBlocks
options={constantOptions}
activeOption={
this.props.parentSelectedConstantColorOption
? this.props.parentSelectedConstantColorOption
: this.state.selectedConstantColorOption
}
onOptionChange={
this.props.onConstantColorOptionChange
? this.props.onConstantColorOptionChange
: value => this.setState({selectedConstantColorOption: value})
}
/>
<Info>
{selectedConstantColorOption === 'single'
? singleMessage
: multiMessage}
</Info>
{selectedConstantColorOption === 'single' ? (
<Color
attr={this.props.attr}
updatePlot={
this.props.setColor ? this.props.setColor : this.setColor
}
/>
) : (
<Colorscale
suppressMultiValuedMessage
attr={this.props.attr}
updatePlot={this.setColors}
fullValue={this.props.fullValue}
initialCategory={'categorical'}
/>
)}
</Field>
);
}

return (
<Color
attr={this.props.attr}
updatePlot={this.props.setColor ? this.props.setColor : this.setColor}
label={this.props.label}
/>
);
}
}

UnconnectedMultiColorPicker.propTypes = {
multiColorMessage: PropTypes.string,
singleColorMessage: PropTypes.string,
updatePlot: PropTypes.func,
attr: PropTypes.string,
parentSelectedConstantColorOption: PropTypes.string,
onConstantColorOptionChange: PropTypes.func,
messageKeyWordSingle: PropTypes.string,
messageKeyWordPlural: PropTypes.string,
...Field.propTypes,
};

UnconnectedMultiColorPicker.contextTypes = {
localize: PropTypes.func,
updateContainer: PropTypes.func,
traceIndexes: PropTypes.array,
fullData: PropTypes.array,
};

export default connectToContainer(UnconnectedMultiColorPicker, {
modifyPlotProps(props, context, plotProps) {
if (plotProps.isVisible) {
plotProps.fullValue = context.traceIndexes
.map(index => {
const trace = context.fullData.filter(
trace => trace.index === index
)[0];

const properties = props.attr.split('.');
let value = trace;

properties.forEach(prop => {
value = value[prop];
});

return value;
})
.map(c => [0, c]);
}
},
});
3 changes: 3 additions & 0 deletions src/components/fields/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {FilterOperation, FilterValue} from './FilterOperation';
import MarkerSize from './MarkerSize';
import MarkerColor from './MarkerColor';
import VisibilitySelect from './VisibilitySelect';
import MultiColorPicker from './MultiColorPicker';

import {
AnnotationArrowRef,
AnnotationRef,
Expand Down Expand Up @@ -91,5 +93,6 @@ export {
TextPosition,
MarkerSize,
MarkerColor,
MultiColorPicker,
VisibilitySelect,
};
2 changes: 2 additions & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
TextPosition,
MarkerSize,
MarkerColor,
MultiColorPicker,
VisibilitySelect,
} from './fields';

Expand Down Expand Up @@ -144,5 +145,6 @@ export {
TextPosition,
MarkerSize,
MarkerColor,
MultiColorPicker,
VisibilitySelect,
};
Loading