Skip to content

Commit acaebc1

Browse files
authored
Merge pull request #739 from plotly/legendgroup
legendgroup and other legend attrs
2 parents 5627ff8 + 469f664 commit acaebc1

File tree

9 files changed

+108
-13
lines changed

9 files changed

+108
-13
lines changed

src/components/fields/AxesCreator.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ UnconnectedAxisCreator.propTypes = {
9999
attr: PropTypes.string,
100100
label: PropTypes.string,
101101
options: PropTypes.array,
102-
canAddAxis: PropTypes.bool,
103102
container: PropTypes.object,
104103
fullContainer: PropTypes.object,
105104
updateContainer: PropTypes.func,

src/components/fields/Field.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,14 @@ class Field extends Component {
7373
) : null}
7474
<div className={fieldClass}>
7575
{children}
76+
{extraComponent ? extraComponent : null}
7677
{multiValued && !suppressMultiValuedMessage ? (
7778
<MenuPanel label={getMultiValueText('title', _)} ownline question>
7879
<div className="info__title">{getMultiValueText('title', _)}</div>
7980
<div className="info__text">{getMultiValueText('text', _)}</div>
8081
<div className="info__sub-text">{getMultiValueText('subText', _)}</div>
8182
</MenuPanel>
8283
) : null}
83-
{extraComponent ? extraComponent : null}
8484
</div>
8585
{units ? (
8686
<div className={bem('field', 'units')}>

src/components/fields/GroupCreator.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import React, {Component} from 'react';
2+
import {connectToContainer} from 'lib';
3+
import Field from './Field';
4+
import Dropdown from './Dropdown';
5+
import PropTypes from 'prop-types';
6+
import Button from '../widgets/Button';
7+
import {PlusIcon} from 'plotly-icons';
8+
import {MULTI_VALUED} from 'lib/constants';
9+
10+
class UnconnectedGroupCreator extends Component {
11+
getAllGroups() {
12+
return [...new Set(this.context.data.map(t => t[this.props.attr]))].filter(g => Boolean(g));
13+
}
14+
15+
canAddGroup() {
16+
const {fullContainer, attr} = this.props;
17+
const currentGroup = fullContainer[attr];
18+
const currentTraceIndex = fullContainer.index;
19+
20+
if (fullContainer.index === MULTI_VALUED) {
21+
return this.getAllGroups().length === 0;
22+
}
23+
24+
return (
25+
!currentGroup ||
26+
this.context.fullData.some(d => d.index !== currentTraceIndex && d[attr] === currentGroup)
27+
);
28+
}
29+
30+
addAndUpdateGroup() {
31+
const allGroups = this.context.fullData
32+
.map(t => parseInt(t[this.props.attr], 10))
33+
.filter(n => Number.isInteger(n));
34+
// don't want to pass empty array to max
35+
allGroups.push(0);
36+
37+
const lastGroupNumber = Math.max.apply(Math, allGroups);
38+
39+
this.props.updatePlot(lastGroupNumber + 1);
40+
}
41+
42+
render() {
43+
const {localize: _} = this.context;
44+
const {attr, label, prefix, updatePlot} = this.props;
45+
46+
const options = [{label: _('None'), value: ''}];
47+
const allGroups = this.getAllGroups();
48+
allGroups.forEach(g => options.push({label: `${prefix} ${g}`, value: g}));
49+
options.sort((a, b) => a.value - b.value);
50+
51+
const icon = <PlusIcon />;
52+
const addButton = this.canAddGroup() ? (
53+
<Button variant="no-text" icon={icon} onClick={() => this.addAndUpdateGroup()} />
54+
) : (
55+
<Button variant="no-text--disabled" icon={icon} onClick={() => {}} />
56+
);
57+
58+
return (
59+
<Dropdown
60+
label={label}
61+
attr={attr}
62+
clearable={false}
63+
options={options}
64+
updatePlot={updatePlot}
65+
extraComponent={addButton}
66+
/>
67+
);
68+
}
69+
}
70+
71+
UnconnectedGroupCreator.propTypes = {
72+
attr: PropTypes.string,
73+
fullContainer: PropTypes.object,
74+
prefix: PropTypes.string,
75+
...Field.propTypes,
76+
};
77+
78+
UnconnectedGroupCreator.contextTypes = {
79+
localize: PropTypes.func,
80+
data: PropTypes.array,
81+
fullData: PropTypes.array,
82+
};
83+
84+
export default connectToContainer(UnconnectedGroupCreator);

src/components/fields/SubplotCreator.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1+
import React, {Component} from 'react';
12
import Dropdown from './Dropdown';
23
import Info from './Info';
34
import PropTypes from 'prop-types';
4-
import React, {Component} from 'react';
55
import {EDITOR_ACTIONS, SUBPLOT_TO_ATTR} from 'lib/constants';
66
import Button from '../widgets/Button';
77
import {PlusIcon} from 'plotly-icons';
88
import {connectToContainer, traceTypeToAxisType, getSubplotTitle} from 'lib';
99
import {PlotlySection} from 'components';
1010

1111
class UnconnectedSingleSubplotCreator extends Component {
12-
canAddAxis() {
12+
canAddSubplot() {
1313
const currentAxisId = this.props.fullContainer[this.props.attr];
1414
const currentTraceIndex = this.props.fullContainer.index;
1515
return this.context.fullData.some(
@@ -62,7 +62,7 @@ class UnconnectedSingleSubplotCreator extends Component {
6262

6363
render() {
6464
const icon = <PlusIcon />;
65-
const extraComponent = this.canAddAxis() ? (
65+
const extraComponent = this.canAddSubplot() ? (
6666
<Button variant="no-text" icon={icon} onClick={() => this.addAndUpdateSubplot()} />
6767
) : (
6868
<Button variant="no-text--disabled" icon={icon} onClick={() => {}} />
@@ -86,7 +86,6 @@ UnconnectedSingleSubplotCreator.propTypes = {
8686
layoutAttr: PropTypes.string,
8787
label: PropTypes.string,
8888
options: PropTypes.array,
89-
canAddAxis: PropTypes.bool,
9089
container: PropTypes.object,
9190
fullContainer: PropTypes.object,
9291
updateContainer: PropTypes.func,

src/components/fields/derived.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ export const BinningDropdown = connectToContainer(UnconnectedDropdown, {
145145
},
146146
});
147147

148-
export const ShowInLegend = connectToContainer(UnconnectedRadio, {
148+
export const ShowInLegend = connectToContainer(UnconnectedVisibilitySelect, {
149149
modifyPlotProps: (props, context, plotProps) => {
150150
plotProps.isVisible = context.fullLayout.showlegend;
151151
return plotProps;

src/components/fields/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import TraceSelector from './TraceSelector';
2121
import ErrorBars from './ErrorBars';
2222
import AxesCreator from './AxesCreator';
2323
import SubplotCreator from './SubplotCreator';
24+
import GroupCreator from './GroupCreator';
2425
import UpdateMenuButtons from './UpdateMenuButtons';
2526
import {FilterOperation, FilterValue} from './FilterOperation';
2627
import MarkerSize from './MarkerSize';
@@ -100,6 +101,7 @@ export {
100101
TraceSelector,
101102
AxesCreator,
102103
SubplotCreator,
104+
GroupCreator,
103105
AxisOverlayDropdown,
104106
AxisSide,
105107
UpdateMenuButtons,

src/components/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
RangesliderVisible,
4040
AxesCreator,
4141
SubplotCreator,
42+
GroupCreator,
4243
SymbolSelector,
4344
TextEditor,
4445
TraceOrientation,
@@ -146,6 +147,7 @@ export {
146147
SingleSidebarItem,
147148
AxesCreator,
148149
SubplotCreator,
150+
GroupCreator,
149151
SymbolSelector,
150152
TextEditor,
151153
SubplotAccordion,

src/default_panels/StyleLegendPanel.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,16 @@ const StyleLegendPanel = (props, {localize: _}) => (
6363
/>
6464
</PlotlySection>
6565
<PlotlySection name={_('Trace Order')}>
66-
<Radio
66+
<Dropdown
6767
attr="legend.traceorder"
6868
options={[
6969
{label: _('Normal'), value: 'normal'},
7070
{label: _('Reversed'), value: 'reversed'},
71+
{label: _('Grouped'), value: 'grouped'},
72+
{label: _('Reversed and Grouped'), value: 'reversed+grouped'},
7173
]}
7274
/>
75+
<Numeric label={_('Gap Between Groups')} attr="legend.tracegroupgap" units="px" />
7376
</PlotlySection>
7477
</PlotlyFold>
7578
</TraceRequiredPanel>

src/default_panels/StyleTracesPanel.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
ErrorBars,
3131
DataSelector,
3232
VisibilitySelect,
33+
GroupCreator,
3334
} from '../components';
3435
import {
3536
BinningNumeric,
@@ -43,12 +44,17 @@ import {
4344
const StyleTracesPanel = (props, {localize: _}) => (
4445
<TraceAccordion canGroup>
4546
<TextEditor label={_('Name')} attr="name" richTextOnly />
46-
<ShowInLegend
47-
label={_('Show in Legend')}
48-
attr="showlegend"
49-
options={[{label: _('Show'), value: true}, {label: _('Hide'), value: false}]}
50-
/>
5147
<NumericFraction label={_('Trace Opacity')} attr="opacity" />
48+
<PlotlySection name={_('Legend')}>
49+
<ShowInLegend
50+
label={_('Show in Legend')}
51+
attr="showlegend"
52+
options={[{label: _('Show'), value: true}, {label: _('Hide'), value: false}]}
53+
showOn={true}
54+
>
55+
<GroupCreator label={_('Legend Group')} prefix={_('Group')} attr="legendgroup" />
56+
</ShowInLegend>
57+
</PlotlySection>
5258
<PlotlySection name={_('Cones & Streamtubes')}>
5359
<Numeric label={_('Size')} attr="sizeref" />
5460
<Dropdown

0 commit comments

Comments
 (0)