Skip to content

Commit 4e019fc

Browse files
support plotly.js@1.49.x mapbox features
1 parent c27a6e6 commit 4e019fc

18 files changed

+391
-95
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
"draft-js-utils": "^1.3.3",
1515
"fast-isnumeric": "^1.1.2",
1616
"immutability-helper": "^3.0.0",
17-
"plotly-icons": "1.3.12",
18-
"plotly.js": "1.48.3",
17+
"plotly-icons": "1.3.13",
18+
"plotly.js": "1.49.5",
1919
"prop-types": "^15.7.2",
2020
"raf": "^3.4.1",
2121
"react-color": "^2.17.0",

src/EditorControls.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
shamefullyDeleteRelatedAnalysisTransforms,
1919
shamefullyAdjustSizeref,
2020
shamefullyAdjustAxisDirection,
21+
shamefullyAdjustMapbox,
2122
} from './shame';
2223
import {EDITOR_ACTIONS} from './lib/constants';
2324
import isNumeric from 'fast-isnumeric';
@@ -87,6 +88,9 @@ class EditorControls extends Component {
8788
shamefullyAdjustAxisRef(graphDiv, payload);
8889
shamefullyAddTableColumns(graphDiv, payload);
8990
shamefullyAdjustSplitStyleTargetContainers(graphDiv, payload);
91+
if (!this.props.mapBoxAccess) {
92+
shamefullyAdjustMapbox(graphDiv, payload);
93+
}
9094

9195
for (let i = 0; i < payload.traceIndexes.length; i++) {
9296
for (const attr in payload.update) {
@@ -283,6 +287,19 @@ class EditorControls extends Component {
283287
}
284288
break;
285289

290+
case EDITOR_ACTIONS.DELETE_MAPBOXLAYER:
291+
if (isNumeric(payload.mapboxLayerIndex)) {
292+
graphDiv.layout[payload.mapboxId].layers.splice(payload.mapboxLayerIndex, 1);
293+
if (this.props.onUpdate) {
294+
this.props.onUpdate(
295+
graphDiv.data,
296+
Object.assign({}, graphDiv.layout),
297+
graphDiv._transitionData._frames
298+
);
299+
}
300+
}
301+
break;
302+
286303
case EDITOR_ACTIONS.DELETE_TRANSFORM:
287304
if (isNumeric(payload.transformIndex) && payload.traceIndex < graphDiv.data.length) {
288305
if (graphDiv.data[payload.traceIndex].transforms.length === 1) {
@@ -327,6 +344,10 @@ class EditorControls extends Component {
327344
move(graphDiv.layout.annotations);
328345
}
329346

347+
if (payload.path === 'layout.mapbox.layers') {
348+
move(graphDiv.layout[payload.mapboxId].layers);
349+
}
350+
330351
const updatedData = payload.path.startsWith('data')
331352
? graphDiv.data.slice()
332353
: graphDiv.data;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import PlotlyFold from './PlotlyFold';
2+
import PlotlyPanel from './PlotlyPanel';
3+
import PropTypes from 'prop-types';
4+
import React, {Component} from 'react';
5+
import {connectLayersToMapbox, getParsedTemplateString} from 'lib';
6+
7+
const MapboxLayersFold = connectLayersToMapbox(PlotlyFold);
8+
9+
class MapboxLayersAccordion extends Component {
10+
render() {
11+
const {
12+
fullContainer: {layers = []},
13+
localize: _,
14+
layout: meta,
15+
} = this.context;
16+
const {children} = this.props;
17+
18+
const content =
19+
layers.length &&
20+
layers.map((layer, i) => (
21+
<MapboxLayersFold
22+
key={i}
23+
mapboxLayerIndex={i}
24+
name={getParsedTemplateString(layer.name, {meta})}
25+
canDelete={true}
26+
>
27+
{children}
28+
</MapboxLayersFold>
29+
));
30+
31+
const addAction = {
32+
label: _('Layer'),
33+
handler: context => {
34+
const {fullContainer, updateContainer} = context;
35+
if (updateContainer) {
36+
const mapboxLayerIndex = Array.isArray(fullContainer.layers)
37+
? fullContainer.layers.length
38+
: 0;
39+
40+
updateContainer({
41+
[`layers[${mapboxLayerIndex}]`]: {
42+
name: `Layer ${mapboxLayerIndex}`,
43+
sourcetype: 'raster',
44+
below: 'traces',
45+
},
46+
});
47+
}
48+
},
49+
};
50+
51+
return (
52+
<PlotlyPanel addAction={addAction} canReorder>
53+
{content ? content : null}
54+
</PlotlyPanel>
55+
);
56+
}
57+
}
58+
59+
MapboxLayersAccordion.contextTypes = {
60+
fullContainer: PropTypes.object,
61+
localize: PropTypes.func,
62+
layout: PropTypes.object,
63+
};
64+
65+
MapboxLayersAccordion.propTypes = {
66+
children: PropTypes.node,
67+
};
68+
69+
MapboxLayersAccordion.plotly_editor_traits = {
70+
no_visibility_forcing: true,
71+
};
72+
73+
export default MapboxLayersAccordion;

src/components/containers/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import SliderAccordion from './SliderAccordion';
44
import ImageAccordion from './ImageAccordion';
55
import UpdateMenuAccordion from './UpdateMenuAccordion';
66
import RangeSelectorAccordion from './RangeSelectorAccordion';
7+
import MapboxLayersAccordion from './MapboxLayersAccordion';
78
import AxesFold from './AxesFold';
89
import PlotlyFold, {Fold} from './PlotlyFold';
910
import MenuPanel from './MenuPanel';
@@ -27,6 +28,7 @@ export {
2728
ImageAccordion,
2829
UpdateMenuAccordion,
2930
RangeSelectorAccordion,
31+
MapboxLayersAccordion,
3032
MenuPanel,
3133
PlotlyFold,
3234
Fold,

src/components/fields/Dropzone.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,10 @@ UnconnectedDropzone.propTypes = {
2626

2727
UnconnectedDropzone.displayName = 'UnconnectedDropzone';
2828

29-
export default connectToContainer(UnconnectedDropzone);
29+
function modifyPlotProps(props, context, plotProps) {
30+
if (context.container.type === 'choroplethmapbox') {
31+
plotProps.isVisible = true;
32+
}
33+
}
34+
35+
export default connectToContainer(UnconnectedDropzone, {modifyPlotProps});

src/components/fields/LocationSelector.js

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -91,31 +91,36 @@ class UnconnectedLocationSelector extends Component {
9191
container: {type: type},
9292
} = this.context;
9393

94-
return type === 'scattergeo' ? (
95-
<>
96-
<Field {...this.props} attr={this.props.attr}>
97-
<Radio
98-
options={[
99-
{value: 'latlon', label: _('Lat/Lon')},
100-
{value: 'location', label: _('Location')},
101-
]}
102-
fullValue={mode}
103-
updatePlot={this.setMode}
104-
attr={this.props.attr}
105-
/>
106-
</Field>
107-
{mode === 'latlon' ? (
108-
<>
109-
<DataSelector label={_('Latitude')} attr="lat" />
110-
<DataSelector label={_('Longitude')} attr="lon" />
111-
</>
112-
) : (
113-
<Location attr="type" />
114-
)}
115-
</>
116-
) : type === 'choropleth' ? (
117-
<Location attr="type" />
118-
) : (
94+
if (type === 'scattergeo') {
95+
return (
96+
<>
97+
<Field {...this.props} attr={this.props.attr}>
98+
<Radio
99+
options={[
100+
{value: 'latlon', label: _('Lat/Lon')},
101+
{value: 'location', label: _('Location')},
102+
]}
103+
fullValue={mode}
104+
updatePlot={this.setMode}
105+
attr={this.props.attr}
106+
/>
107+
</Field>
108+
{mode === 'latlon' ? (
109+
<>
110+
<DataSelector label={_('Latitude')} attr="lat" />
111+
<DataSelector label={_('Longitude')} attr="lon" />
112+
</>
113+
) : (
114+
<Location attr="type" />
115+
)}
116+
</>
117+
);
118+
} else if (type === 'choropleth') {
119+
return <Location attr="type" />;
120+
} else if (type === 'choroplethmapbox') {
121+
return <DataSelector label={_('Locations')} attr="locations" />;
122+
}
123+
return (
119124
<>
120125
<DataSelector label={_('Latitude')} attr="lat" />
121126
<DataSelector label={_('Longitude')} attr="lon" />

src/components/fields/SubplotCreator.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, {Component} from 'react';
22
import Dropdown from './Dropdown';
33
import Info from './Info';
44
import PropTypes from 'prop-types';
5-
import {EDITOR_ACTIONS, SUBPLOT_TO_ATTR} from 'lib/constants';
5+
import {EDITOR_ACTIONS, SUBPLOT_TO_ATTR, subplotName} from 'lib/constants';
66
import Button from '../widgets/Button';
77
import {PlusIcon} from 'plotly-icons';
88
import {connectToContainer, traceTypeToAxisType, getSubplotTitle} from 'lib';
@@ -127,7 +127,7 @@ class UnconnectedSubplotCreator extends Component {
127127
<SingleSubplotCreator
128128
attr={SUBPLOT_TO_ATTR[subplotType].data}
129129
layoutAttr={subplotType}
130-
label={SUBPLOT_TO_ATTR[subplotType].layout}
130+
label={subplotName(SUBPLOT_TO_ATTR[subplotType].layout, _)}
131131
options={getOptions(subplotType)}
132132
/>
133133
<Info>

src/components/fields/derived.js

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {UnconnectedColorPicker} from './ColorPicker';
1212
import {UnconnectedTextEditor} from './TextEditor';
1313
import {UnconnectedVisibilitySelect} from './VisibilitySelect';
1414
import {connectToContainer, getAllAxes, getAxisTitle, axisIdToAxisName} from 'lib';
15+
import PropTypes from 'prop-types';
16+
import Text from './Text';
1517

1618
export const AxisAnchorDropdown = connectToContainer(UnconnectedDropdown, {
1719
modifyPlotProps: (props, context, plotProps) => {
@@ -574,8 +576,14 @@ export const HoverInfo = connectToContainer(UnconnectedFlaglist, {
574576
} else if (container.lat || container.lon) {
575577
options = [{label: _('Longitude'), value: 'lon'}, {label: _('Latitude'), value: 'lat'}];
576578
}
577-
} else if (container.type === 'scattermapbox') {
579+
} else if (container.type === 'scattermapbox' || container.type === 'densitymapbox') {
578580
options = [{label: _('Longitude'), value: 'lon'}, {label: _('Latitude'), value: 'lat'}];
581+
} else if (container.type === 'densitymapbox') {
582+
options = [
583+
{label: _('Longitude'), value: 'lon'},
584+
{label: _('Latitude'), value: 'lat'},
585+
{label: _('Z'), value: 'z'},
586+
];
579587
} else if (container.type === 'scatterternary') {
580588
options = [
581589
{label: _('A'), value: 'a'},
@@ -655,6 +663,54 @@ export const FillDropdown = connectToContainer(UnconnectedDropdown, {
655663
},
656664
});
657665

666+
export const MapboxSourceArray = connectToContainer(Text, {
667+
modifyPlotProps: (props, context, plotProps) => {
668+
const {fullValue, updatePlot} = plotProps;
669+
if (plotProps.fullValue && plotProps.fullValue.length > 0) {
670+
plotProps.fullValue = fullValue[0];
671+
}
672+
673+
plotProps.updatePlot = v => {
674+
if (v.length) {
675+
updatePlot([v]);
676+
} else {
677+
updatePlot([]);
678+
}
679+
};
680+
},
681+
});
682+
683+
export const MapboxStyleDropdown = connectToContainer(UnconnectedDropdown, {
684+
modifyPlotProps: (props, context, plotProps) => {
685+
const {mapBoxAccess, localize: _} = context;
686+
687+
plotProps.options = (!mapBoxAccess
688+
? []
689+
: [
690+
{label: _('Mapbox Basic'), value: 'basic'},
691+
{label: _('Mapbox Outdoors'), value: 'outdoors'},
692+
{label: _('Mapbox Light'), value: 'light'},
693+
{label: _('Mapbox Dark'), value: 'dark'},
694+
{label: _('Mapbox Satellite'), value: 'satellite'},
695+
{label: _('Mapbox Satellite with Streets'), value: 'satellite-streets'},
696+
]
697+
).concat([
698+
{label: _('No tiles (white background)'), value: 'white-bg'},
699+
{label: _('Open Street Map'), value: 'open-street-map'},
700+
{label: _('Carto Positron'), value: 'carto-positron'},
701+
{label: _('Carto Dark Matter'), value: 'carto-darkmatter'},
702+
{label: _('Stamen Terrain'), value: 'stamen-terrain'},
703+
{label: _('Stamen Toner'), value: 'stamen-toner'},
704+
{label: _('Stamen Watercolor'), value: 'stamen-watercolor'},
705+
]);
706+
plotProps.clearable = false;
707+
},
708+
});
709+
MapboxStyleDropdown.contextTypes = {
710+
mapBoxAccess: PropTypes.bool,
711+
...MapboxStyleDropdown.contextTypes,
712+
};
713+
658714
export const HoveronDropdown = connectToContainer(UnconnectedDropdown, {
659715
modifyPlotProps: (props, context, plotProps) => {
660716
const {localize: _} = context;

0 commit comments

Comments
 (0)