From 7f9e8e2ed7ccff4df3b2c6ca02824ec1489164a1 Mon Sep 17 00:00:00 2001
From: Nicolas Kruchten
Date: Wed, 11 Sep 2019 15:41:41 -0400
Subject: [PATCH 1/7] basic support for no-token mapbox
---
package.json | 2 +-
src/EditorControls.js | 4 +++
src/components/fields/derived.js | 36 ++++++++++++++++++++-
src/components/widgets/TraceTypeSelector.js | 8 ++---
src/default_panels/GraphCreatePanel.js | 2 ++
src/default_panels/StyleMapsPanel.js | 23 ++++++++-----
src/lib/constants.js | 6 ++--
src/lib/traceTypes.js | 22 +++++++++----
src/shame.js | 7 ++++
9 files changed, 85 insertions(+), 25 deletions(-)
diff --git a/package.json b/package.json
index 23547b7fd..f8d2204f7 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"fast-isnumeric": "^1.1.2",
"immutability-helper": "^3.0.0",
"plotly-icons": "1.3.12",
- "plotly.js": "1.48.3",
+ "plotly.js": "1.49.4",
"prop-types": "^15.7.2",
"raf": "^3.4.1",
"react-color": "^2.17.0",
diff --git a/src/EditorControls.js b/src/EditorControls.js
index 1db450871..e9aa082e8 100644
--- a/src/EditorControls.js
+++ b/src/EditorControls.js
@@ -18,6 +18,7 @@ import {
shamefullyDeleteRelatedAnalysisTransforms,
shamefullyAdjustSizeref,
shamefullyAdjustAxisDirection,
+ shamefullyAdjustMapbox,
} from './shame';
import {EDITOR_ACTIONS} from './lib/constants';
import isNumeric from 'fast-isnumeric';
@@ -87,6 +88,9 @@ class EditorControls extends Component {
shamefullyAdjustAxisRef(graphDiv, payload);
shamefullyAddTableColumns(graphDiv, payload);
shamefullyAdjustSplitStyleTargetContainers(graphDiv, payload);
+ if (!this.props.mapBoxAccess) {
+ shamefullyAdjustMapbox(graphDiv, payload);
+ }
for (let i = 0; i < payload.traceIndexes.length; i++) {
for (const attr in payload.update) {
diff --git a/src/components/fields/derived.js b/src/components/fields/derived.js
index bc6d8822b..24375586e 100644
--- a/src/components/fields/derived.js
+++ b/src/components/fields/derived.js
@@ -574,8 +574,14 @@ export const HoverInfo = connectToContainer(UnconnectedFlaglist, {
} else if (container.lat || container.lon) {
options = [{label: _('Longitude'), value: 'lon'}, {label: _('Latitude'), value: 'lat'}];
}
- } else if (container.type === 'scattermapbox') {
+ } else if (container.type === 'scattermapbox' || container.type === 'densitymapbox') {
options = [{label: _('Longitude'), value: 'lon'}, {label: _('Latitude'), value: 'lat'}];
+ } else if (container.type === 'densitymapbox') {
+ options = [
+ {label: _('Longitude'), value: 'lon'},
+ {label: _('Latitude'), value: 'lat'},
+ {label: _('Z'), value: 'z'},
+ ];
} else if (container.type === 'scatterternary') {
options = [
{label: _('A'), value: 'a'},
@@ -655,6 +661,34 @@ export const FillDropdown = connectToContainer(UnconnectedDropdown, {
},
});
+export const MapboxStyleDropdown = connectToContainer(UnconnectedDropdown, {
+ modifyPlotProps: (props, context, plotProps) => {
+ const {mapBoxAccess, localize: _} = context;
+
+ plotProps.options = [
+ {label: _('No tiles (white background)'), value: 'white-bg'},
+ {label: _('Open Street Map'), value: 'open-street-map'},
+ {label: _('Carto Positron'), value: 'carto-positron'},
+ {label: _('Carto Dark Matter'), value: 'carto-darkmatter'},
+ {label: _('Stamen Terrain'), value: 'stamen-terrain'},
+ {label: _('Stamen Toner'), value: 'stamen-toner'},
+ {label: _('Stamen Watercolor'), value: 'stamen-watercolor'},
+ ].concat(
+ !mapBoxAccess
+ ? []
+ : [
+ {label: _('Mapbox Basic'), value: 'basic'},
+ {label: _('Mapbox Outdoors'), value: 'outdoors'},
+ {label: _('Mapbox Light'), value: 'light'},
+ {label: _('Mapbox Dark'), value: 'dark'},
+ {label: _('Mapbox Satellite'), value: 'satellite'},
+ {label: _('Mapbox Satellite with Streets'), value: 'satellite-streets'},
+ ]
+ );
+ plotProps.clearable = false;
+ },
+});
+
export const HoveronDropdown = connectToContainer(UnconnectedDropdown, {
modifyPlotProps: (props, context, plotProps) => {
const {localize: _} = context;
diff --git a/src/components/widgets/TraceTypeSelector.js b/src/components/widgets/TraceTypeSelector.js
index 394b1a9db..2ff19a2cb 100644
--- a/src/components/widgets/TraceTypeSelector.js
+++ b/src/components/widgets/TraceTypeSelector.js
@@ -137,20 +137,16 @@ class TraceTypeSelector extends Component {
renderCategories() {
const {fullValue} = this.props;
- const {mapBoxAccess, localize: _, chartHelp} = this.context;
+ const {localize: _, chartHelp} = this.context;
const {
traceTypesConfig: {traces, categories, complex},
} = this.props;
return categories(_).map((category, i) => {
- let items = traces(_)
+ const items = traces(_)
.filter(({category: {value}}) => value === category.value)
.filter(i => i.value !== 'scattergl' && i.value !== 'scatterpolargl');
- if (!mapBoxAccess) {
- items = items.filter(i => i.value !== 'scattermapbox');
- }
-
const MAX_ITEMS = 4;
const columnClasses =
diff --git a/src/default_panels/GraphCreatePanel.js b/src/default_panels/GraphCreatePanel.js
index ac4d522ad..9af973cd9 100644
--- a/src/default_panels/GraphCreatePanel.js
+++ b/src/default_panels/GraphCreatePanel.js
@@ -11,6 +11,7 @@ import {
TraceSelector,
TraceTypeSection,
LocationSelector,
+ Numeric,
} from '../components';
import {
HistogramInfoVertical,
@@ -68,6 +69,7 @@ const GraphCreatePanel = (props, {localize: _, setPanel}) => {
}}
attr="z"
/>
+
diff --git a/src/default_panels/StyleMapsPanel.js b/src/default_panels/StyleMapsPanel.js
index 097e11c14..29661fadf 100644
--- a/src/default_panels/StyleMapsPanel.js
+++ b/src/default_panels/StyleMapsPanel.js
@@ -11,17 +11,24 @@ import {
const StyleMapsPanel = (props, {localize: _}) => (
-
+
diff --git a/src/lib/constants.js b/src/lib/constants.js
index a0f4dcf74..0f00f036f 100644
--- a/src/lib/constants.js
+++ b/src/lib/constants.js
@@ -78,7 +78,7 @@ export const TRACE_TO_AXIS = {
ternary: ['scatterternary'],
gl3d: ['scatter3d', 'surface', 'mesh3d', 'cone', 'streamtube'],
geo: ['scattergeo', 'choropleth'],
- mapbox: ['scattermapbox'],
+ mapbox: ['scattermapbox', 'choroplethmapbox', 'densitymapbox'],
polar: ['scatterpolar', 'scatterpolargl', 'barpolar'],
};
@@ -104,8 +104,8 @@ export const subplotName = (type, _) =>
ternary: _('Ternary'),
gl3d: _('Scene'),
scene: _('Scene'),
- geo: _('Geo'),
- mapbox: _('Mapbox'),
+ geo: _('Map'),
+ mapbox: _('Tile Map'),
polar: _('Polar'),
}[type]);
diff --git a/src/lib/traceTypes.js b/src/lib/traceTypes.js
index ed41f57bc..a837a00cf 100644
--- a/src/lib/traceTypes.js
+++ b/src/lib/traceTypes.js
@@ -145,14 +145,9 @@ export const traceTypes = _ => [
label: _('2D Contour Histogram'),
category: chartCategory(_).DISTRIBUTIONS,
},
- {
- value: 'choropleth',
- label: _('Choropleth'),
- category: chartCategory(_).MAPS,
- },
{
value: 'scattermapbox',
- label: _('Satellite Map'),
+ label: _('Tile Map'),
category: chartCategory(_).MAPS,
},
{
@@ -160,6 +155,21 @@ export const traceTypes = _ => [
label: _('Atlas Map'),
category: chartCategory(_).MAPS,
},
+ {
+ value: 'choroplethmapbox',
+ label: _('Choropleth Tile Map'),
+ category: chartCategory(_).MAPS,
+ },
+ {
+ value: 'choropleth',
+ label: _('Choropleth Atlas Map'),
+ category: chartCategory(_).MAPS,
+ },
+ {
+ value: 'densitymapbox',
+ label: _('Density Tile Map'),
+ category: chartCategory(_).MAPS,
+ },
{
value: 'scatterpolar',
label: _('Polar Scatter'),
diff --git a/src/shame.js b/src/shame.js
index 10c40499e..3f84b778d 100644
--- a/src/shame.js
+++ b/src/shame.js
@@ -89,6 +89,13 @@ export const shamefullyAdjustGeo = ({layout}, {update}) => {
});
};
+export const shamefullyAdjustMapbox = gd => {
+ if (gd.layout && gd.layout.mapbox && gd.layout.mapbox.style) {
+ return;
+ }
+ nestedProperty(gd.layout, 'mapbox.style').set('open-street-map');
+};
+
export const shamefullyAddTableColumns = (graphDiv, {traceIndexes, update}) => {
if (
update['cells.values'] &&
From 9d0c53c9a5695317b6bdbd63b15cd8c1a21e37e2 Mon Sep 17 00:00:00 2001
From: Nicolas Kruchten
Date: Fri, 13 Sep 2019 14:52:21 -0400
Subject: [PATCH 2/7] mapbox++ checkpoint
---
dev/dataSources.js | 19 ++++++
src/components/fields/Dropzone.js | 8 ++-
src/components/fields/LocationSelector.js | 55 ++++++++--------
src/components/fields/SubplotCreator.js | 4 +-
src/components/widgets/Dropzone.js | 66 ++++++++++----------
src/default_panels/GraphCreatePanel.js | 2 +
src/default_panels/StyleImagesPanel.js | 2 +-
src/styles/components/containers/_modal.scss | 2 +-
8 files changed, 94 insertions(+), 64 deletions(-)
diff --git a/dev/dataSources.js b/dev/dataSources.js
index 83f88df7d..cb08bc40f 100644
--- a/dev/dataSources.js
+++ b/dev/dataSources.js
@@ -1725,6 +1725,25 @@ export default {
],
ints: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
+ states: [
+ 'AL',
+ 'AK',
+ 'AZ',
+ 'AR',
+ 'CA',
+ 'CO',
+ 'CT',
+ 'DE',
+ 'FL',
+ 'GA',
+ 'HI',
+ 'ID',
+ 'IL',
+ 'IN',
+ 'IA',
+ 'KS',
+ 'KY',
+ ],
'jagged ints': [2, 1, 3, 5, 4, 6],
'toggle ints': [1, -1, 1, -1, 1, -1],
'big ints': [1000, 10100, 10000, 20000, 100000],
diff --git a/src/components/fields/Dropzone.js b/src/components/fields/Dropzone.js
index 5bbc3028e..9d5932707 100644
--- a/src/components/fields/Dropzone.js
+++ b/src/components/fields/Dropzone.js
@@ -26,4 +26,10 @@ UnconnectedDropzone.propTypes = {
UnconnectedDropzone.displayName = 'UnconnectedDropzone';
-export default connectToContainer(UnconnectedDropzone);
+function modifyPlotProps(props, context, plotProps) {
+ if (context.container.type === 'choroplethmapbox') {
+ plotProps.isVisible = true;
+ }
+}
+
+export default connectToContainer(UnconnectedDropzone, {modifyPlotProps});
diff --git a/src/components/fields/LocationSelector.js b/src/components/fields/LocationSelector.js
index 20f2b7aad..78fe831d8 100644
--- a/src/components/fields/LocationSelector.js
+++ b/src/components/fields/LocationSelector.js
@@ -91,31 +91,36 @@ class UnconnectedLocationSelector extends Component {
container: {type: type},
} = this.context;
- return type === 'scattergeo' ? (
- <>
-
-
-
- {mode === 'latlon' ? (
- <>
-
-
- >
- ) : (
-
- )}
- >
- ) : type === 'choropleth' ? (
-
- ) : (
+ if (type === 'scattergeo') {
+ return (
+ <>
+
+
+
+ {mode === 'latlon' ? (
+ <>
+
+
+ >
+ ) : (
+
+ )}
+ >
+ );
+ } else if (type === 'choropleth') {
+ return ;
+ } else if (type === 'choroplethmapbox') {
+ return ;
+ }
+ return (
<>
diff --git a/src/components/fields/SubplotCreator.js b/src/components/fields/SubplotCreator.js
index 45e4bd186..953d6625d 100644
--- a/src/components/fields/SubplotCreator.js
+++ b/src/components/fields/SubplotCreator.js
@@ -2,7 +2,7 @@ import React, {Component} from 'react';
import Dropdown from './Dropdown';
import Info from './Info';
import PropTypes from 'prop-types';
-import {EDITOR_ACTIONS, SUBPLOT_TO_ATTR} from 'lib/constants';
+import {EDITOR_ACTIONS, SUBPLOT_TO_ATTR, subplotName} from 'lib/constants';
import Button from '../widgets/Button';
import {PlusIcon} from 'plotly-icons';
import {connectToContainer, traceTypeToAxisType, getSubplotTitle} from 'lib';
@@ -127,7 +127,7 @@ class UnconnectedSubplotCreator extends Component {
diff --git a/src/components/widgets/Dropzone.js b/src/components/widgets/Dropzone.js
index 7c41d61ea..c11bb6dab 100644
--- a/src/components/widgets/Dropzone.js
+++ b/src/components/widgets/Dropzone.js
@@ -5,17 +5,18 @@ import Drop from 'react-dropzone';
class Dropzone extends Component {
constructor(props, context) {
super(props, context);
- const _ = context.localize;
this.state = {
content: '',
};
this.validFiletypes = {
- image: _('image/jpeg, image/jpg, image/svg, image/png, image/gif, image/bmp, image/webp'),
+ image: 'image/jpeg, image/jpg, image/svg, image/png, image/gif, image/bmp, image/webp',
+ geojson: 'application/json',
};
this.onDrop = this.onDrop.bind(this);
+ this.parsingError = this.parsingError.bind(this);
this.renderSuccess = this.renderSuccess.bind(this);
}
@@ -27,6 +28,14 @@ class Dropzone extends Component {
);
}
+ if (this.props.fileType === 'geojson') {
+ return (
+
+
{_('GeoJSON loaded!')}
+
{value.features.length + _(' features detected.')}
+
+ );
+ }
return {_('File loaded!')}
;
}
@@ -48,7 +57,7 @@ class Dropzone extends Component {
_(' to upload here or click to choose a file from your computer.')}
- {this.props.fileType === 'image' ? (
+ {this.validFiletypes[this.props.fileType] ? (
{_('Supported formats are: ') +
this.validFiletypes[this.props.fileType].split('image/').join('') +
@@ -60,32 +69,34 @@ class Dropzone extends Component {
});
}
- onLoad(e) {
+ parsingError() {
const _ = this.context.localize;
const supportedFileTypes =
this.props.fileType === 'image'
? this.validFiletypes[this.props.fileType].split('image/').join('')
: this.validFiletypes[this.props.fileType];
- const parsingError = (
+ return (
-
{_('Yikes! An error occurred while parsing this file.')}
+ {_("Yikes! This doesn't look like a valid ") + this.props.fileType}
{_('Try again with a supported file format: ') + supportedFileTypes + '.'}
);
+ }
- if (this.props.fileType === 'image') {
- try {
- this.props.onUpdate(e.target.result);
- this.setState({
- content: this.renderSuccess(e.target.result),
- });
- } catch (error) {
- console.warn(error); // eslint-disable-line
- this.setState({
- content: parsingError,
- });
- }
+ onLoad(e) {
+ try {
+ const payload = e.target.result;
+ const parsedValue = this.props.fileType === 'image' ? payload : JSON.parse(payload);
+ this.props.onUpdate(parsedValue);
+ this.setState({
+ content: this.renderSuccess(parsedValue),
+ });
+ } catch (error) {
+ console.warn(error); // eslint-disable-line
+ this.setState({
+ content: this.parsingError(),
+ });
}
}
@@ -99,9 +110,6 @@ class Dropzone extends Component {
content: (
{_('Yikes! You can only upload one file at a time.')}
-
- {_('To upload multiple files, create multiple files and upload them individually.')}
-
),
});
@@ -111,24 +119,14 @@ class Dropzone extends Component {
reader.onload = e => this.onLoad(e);
if (this.props.fileType === 'image') {
reader.readAsDataURL(accepted[0]);
+ } else if (this.props.fileType === 'geojson') {
+ reader.readAsText(accepted[0]);
}
}
if (rejected.length) {
- const supportedFileTypes =
- this.props.fileType === 'image'
- ? this.validFiletypes[this.props.fileType].split('image/').join('')
- : this.validFiletypes[this.props.fileType];
-
this.setState({
- content: (
-
-
- {_("Yikes! This doesn't look like a valid ") + this.props.fileType + _(' to us. ')}
-
-
{_('Try again with a ') + supportedFileTypes + ' file.'}
-
- ),
+ content: this.parsingError(),
});
}
}
diff --git a/src/default_panels/GraphCreatePanel.js b/src/default_panels/GraphCreatePanel.js
index 9af973cd9..f17f151a8 100644
--- a/src/default_panels/GraphCreatePanel.js
+++ b/src/default_panels/GraphCreatePanel.js
@@ -11,6 +11,7 @@ import {
TraceSelector,
TraceTypeSection,
LocationSelector,
+ Dropzone,
Numeric,
} from '../components';
import {
@@ -30,6 +31,7 @@ const GraphCreatePanel = (props, {localize: _, setPanel}) => {
>
+
diff --git a/src/default_panels/StyleImagesPanel.js b/src/default_panels/StyleImagesPanel.js
index 71543ded8..011a8f8ee 100644
--- a/src/default_panels/StyleImagesPanel.js
+++ b/src/default_panels/StyleImagesPanel.js
@@ -17,7 +17,7 @@ const StyleImagesPanel = (props, {localize: _}) => (
options={[{label: _('Show'), value: true}, {label: _('Hide'), value: false}]}
/>
-
+
Date: Mon, 16 Sep 2019 15:14:47 -0400
Subject: [PATCH 3/7] wip
---
package.json | 2 +-
src/EditorControls.js | 13 +++
.../containers/MapboxLayersAccordion.js | 72 ++++++++++++++
src/components/containers/index.js | 2 +
src/components/fields/derived.js | 30 +++---
src/default_panels/StyleMapsPanel.js | 36 +++----
src/lib/connectLayersToMapbox.js | 97 +++++++++++++++++++
src/lib/index.js | 2 +
8 files changed, 220 insertions(+), 34 deletions(-)
create mode 100644 src/components/containers/MapboxLayersAccordion.js
create mode 100644 src/lib/connectLayersToMapbox.js
diff --git a/package.json b/package.json
index f8d2204f7..828602fbd 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
"draft-js-utils": "^1.3.3",
"fast-isnumeric": "^1.1.2",
"immutability-helper": "^3.0.0",
- "plotly-icons": "1.3.12",
+ "plotly-icons": "1.3.13",
"plotly.js": "1.49.4",
"prop-types": "^15.7.2",
"raf": "^3.4.1",
diff --git a/src/EditorControls.js b/src/EditorControls.js
index e9aa082e8..d1651b9f9 100644
--- a/src/EditorControls.js
+++ b/src/EditorControls.js
@@ -287,6 +287,19 @@ class EditorControls extends Component {
}
break;
+ case EDITOR_ACTIONS.DELETE_MAPBOXLAYER:
+ if (isNumeric(payload.mapboxLayerIndex)) {
+ graphDiv.layout[payload.mapboxId].layers.splice(payload.mapboxLayerIndex, 1);
+ if (this.props.onUpdate) {
+ this.props.onUpdate(
+ graphDiv.data,
+ Object.assign({}, graphDiv.layout),
+ graphDiv._transitionData._frames
+ );
+ }
+ }
+ break;
+
case EDITOR_ACTIONS.DELETE_TRANSFORM:
if (isNumeric(payload.transformIndex) && payload.traceIndex < graphDiv.data.length) {
if (graphDiv.data[payload.traceIndex].transforms.length === 1) {
diff --git a/src/components/containers/MapboxLayersAccordion.js b/src/components/containers/MapboxLayersAccordion.js
new file mode 100644
index 000000000..765525181
--- /dev/null
+++ b/src/components/containers/MapboxLayersAccordion.js
@@ -0,0 +1,72 @@
+import PlotlyFold from './PlotlyFold';
+import PlotlyPanel from './PlotlyPanel';
+import PropTypes from 'prop-types';
+import React, {Component} from 'react';
+import {connectLayersToMapbox, getParsedTemplateString} from 'lib';
+
+const MapboxLayersFold = connectLayersToMapbox(PlotlyFold);
+
+class MapboxLayersAccordion extends Component {
+ render() {
+ const {
+ fullContainer: {layers = []},
+ localize: _,
+ layout: meta,
+ } = this.context;
+ const {children} = this.props;
+
+ const content =
+ layers.length &&
+ layers.map((layer, i) => (
+
+ {children}
+
+ ));
+
+ const addAction = {
+ label: _('Layer'),
+ handler: context => {
+ const {fullContainer, updateContainer} = context;
+ if (updateContainer) {
+ const mapboxLayerIndex = Array.isArray(fullContainer.layers)
+ ? fullContainer.layers.length
+ : 0;
+
+ updateContainer({
+ [`layers[${mapboxLayerIndex}]`]: {
+ name: `Layer ${mapboxLayerIndex}`,
+ sourcetype: 'geojson',
+ source: {
+ type: 'FeatureCollection',
+ features: [],
+ },
+ },
+ });
+ }
+ },
+ };
+
+ return {content ? content : null};
+ }
+}
+
+MapboxLayersAccordion.contextTypes = {
+ fullContainer: PropTypes.object,
+ localize: PropTypes.func,
+ layout: PropTypes.object,
+};
+
+MapboxLayersAccordion.propTypes = {
+ children: PropTypes.node,
+};
+
+MapboxLayersAccordion.plotly_editor_traits = {
+ no_visibility_forcing: true,
+};
+
+export default MapboxLayersAccordion;
diff --git a/src/components/containers/index.js b/src/components/containers/index.js
index 84afee702..c8e4e66c5 100644
--- a/src/components/containers/index.js
+++ b/src/components/containers/index.js
@@ -4,6 +4,7 @@ import SliderAccordion from './SliderAccordion';
import ImageAccordion from './ImageAccordion';
import UpdateMenuAccordion from './UpdateMenuAccordion';
import RangeSelectorAccordion from './RangeSelectorAccordion';
+import MapboxLayersAccordion from './MapboxLayersAccordion';
import AxesFold from './AxesFold';
import PlotlyFold, {Fold} from './PlotlyFold';
import MenuPanel from './MenuPanel';
@@ -27,6 +28,7 @@ export {
ImageAccordion,
UpdateMenuAccordion,
RangeSelectorAccordion,
+ MapboxLayersAccordion,
MenuPanel,
PlotlyFold,
Fold,
diff --git a/src/components/fields/derived.js b/src/components/fields/derived.js
index 24375586e..962575138 100644
--- a/src/components/fields/derived.js
+++ b/src/components/fields/derived.js
@@ -12,6 +12,7 @@ import {UnconnectedColorPicker} from './ColorPicker';
import {UnconnectedTextEditor} from './TextEditor';
import {UnconnectedVisibilitySelect} from './VisibilitySelect';
import {connectToContainer, getAllAxes, getAxisTitle, axisIdToAxisName} from 'lib';
+import PropTypes from 'prop-types';
export const AxisAnchorDropdown = connectToContainer(UnconnectedDropdown, {
modifyPlotProps: (props, context, plotProps) => {
@@ -665,7 +666,17 @@ export const MapboxStyleDropdown = connectToContainer(UnconnectedDropdown, {
modifyPlotProps: (props, context, plotProps) => {
const {mapBoxAccess, localize: _} = context;
- plotProps.options = [
+ plotProps.options = (!mapBoxAccess
+ ? []
+ : [
+ {label: _('Mapbox Basic'), value: 'basic'},
+ {label: _('Mapbox Outdoors'), value: 'outdoors'},
+ {label: _('Mapbox Light'), value: 'light'},
+ {label: _('Mapbox Dark'), value: 'dark'},
+ {label: _('Mapbox Satellite'), value: 'satellite'},
+ {label: _('Mapbox Satellite with Streets'), value: 'satellite-streets'},
+ ]
+ ).concat([
{label: _('No tiles (white background)'), value: 'white-bg'},
{label: _('Open Street Map'), value: 'open-street-map'},
{label: _('Carto Positron'), value: 'carto-positron'},
@@ -673,21 +684,14 @@ export const MapboxStyleDropdown = connectToContainer(UnconnectedDropdown, {
{label: _('Stamen Terrain'), value: 'stamen-terrain'},
{label: _('Stamen Toner'), value: 'stamen-toner'},
{label: _('Stamen Watercolor'), value: 'stamen-watercolor'},
- ].concat(
- !mapBoxAccess
- ? []
- : [
- {label: _('Mapbox Basic'), value: 'basic'},
- {label: _('Mapbox Outdoors'), value: 'outdoors'},
- {label: _('Mapbox Light'), value: 'light'},
- {label: _('Mapbox Dark'), value: 'dark'},
- {label: _('Mapbox Satellite'), value: 'satellite'},
- {label: _('Mapbox Satellite with Streets'), value: 'satellite-streets'},
- ]
- );
+ ]);
plotProps.clearable = false;
},
});
+MapboxStyleDropdown.contextTypes = {
+ mapBoxAccess: PropTypes.bool,
+ ...MapboxStyleDropdown.contextTypes,
+};
export const HoveronDropdown = connectToContainer(UnconnectedDropdown, {
modifyPlotProps: (props, context, plotProps) => {
diff --git a/src/default_panels/StyleMapsPanel.js b/src/default_panels/StyleMapsPanel.js
index 29661fadf..8afb3f435 100644
--- a/src/default_panels/StyleMapsPanel.js
+++ b/src/default_panels/StyleMapsPanel.js
@@ -4,6 +4,8 @@ import {
SubplotAccordion,
PlotlySection,
Dropdown,
+ MapboxStyleDropdown,
+ MapboxLayersAccordion,
Radio,
Numeric,
ColorPicker,
@@ -12,26 +14,20 @@ import {
const StyleMapsPanel = (props, {localize: _}) => (
-
+
+
+
+
+
+
+
diff --git a/src/lib/connectLayersToMapbox.js b/src/lib/connectLayersToMapbox.js
new file mode 100644
index 000000000..ea45fa837
--- /dev/null
+++ b/src/lib/connectLayersToMapbox.js
@@ -0,0 +1,97 @@
+import React, {Component} from 'react';
+import PropTypes from 'prop-types';
+import {getDisplayName} from '../lib';
+import {EDITOR_ACTIONS} from './constants';
+
+export default function connectLayersToMapbox(WrappedComponent) {
+ class MapboxLayerConnectedComponent extends Component {
+ constructor(props, context) {
+ super(props, context);
+
+ this.deleteMapboxLayer = this.deleteMapboxLayer.bind(this);
+ this.updateMapboxLayer = this.updateMapboxLayer.bind(this);
+ this.setLocals(props, context);
+ }
+
+ componentWillReceiveProps(nextProps, nextContext) {
+ this.setLocals(nextProps, nextContext);
+ }
+
+ setLocals(props, context) {
+ const {mapboxLayerIndex} = props;
+ const {container, fullContainer} = context;
+
+ const mapboxLayers = container.layers || [];
+ const fullmapboxLayers = fullContainer.layers || [];
+ this.container = mapboxLayers[mapboxLayerIndex];
+ this.fullContainer = fullmapboxLayers[mapboxLayerIndex];
+ }
+
+ getChildContext() {
+ return {
+ getValObject: attr =>
+ !this.context.getValObject ? null : this.context.getValObject(`layers[].${attr}`),
+ updateContainer: this.updateMapboxLayer,
+ deleteContainer: this.deleteMapboxLayer,
+ container: this.container,
+ fullContainer: this.fullContainer,
+ };
+ }
+
+ updateMapboxLayer(update) {
+ const newUpdate = {};
+ const {mapboxLayerIndex} = this.props;
+ for (const key in update) {
+ const newkey = `layers[${mapboxLayerIndex}].${key}`;
+ newUpdate[newkey] = update[key];
+ }
+ this.context.updateContainer(newUpdate);
+ }
+
+ deleteMapboxLayer() {
+ if (this.context.onUpdate) {
+ this.context.onUpdate({
+ type: EDITOR_ACTIONS.DELETE_MAPBOXLAYER,
+ payload: {
+ mapboxId: this.context.fullContainer._subplot.id,
+ mapboxLayerIndex: this.props.mapboxLayerIndex,
+ },
+ });
+ }
+ }
+
+ render() {
+ return ;
+ }
+ }
+
+ MapboxLayerConnectedComponent.displayName = `MapboxLayerConnected${getDisplayName(
+ WrappedComponent
+ )}`;
+
+ MapboxLayerConnectedComponent.propTypes = {
+ mapboxLayerIndex: PropTypes.number.isRequired,
+ };
+
+ MapboxLayerConnectedComponent.contextTypes = {
+ container: PropTypes.object,
+ fullContainer: PropTypes.object,
+ data: PropTypes.array,
+ onUpdate: PropTypes.func,
+ updateContainer: PropTypes.func,
+ getValObject: PropTypes.func,
+ };
+
+ MapboxLayerConnectedComponent.childContextTypes = {
+ updateContainer: PropTypes.func,
+ deleteContainer: PropTypes.func,
+ container: PropTypes.object,
+ fullContainer: PropTypes.object,
+ getValObject: PropTypes.func,
+ };
+
+ const {plotly_editor_traits} = WrappedComponent;
+ MapboxLayerConnectedComponent.plotly_editor_traits = plotly_editor_traits;
+
+ return MapboxLayerConnectedComponent;
+}
diff --git a/src/lib/index.js b/src/lib/index.js
index d9fce5f0d..9fc53818b 100644
--- a/src/lib/index.js
+++ b/src/lib/index.js
@@ -7,6 +7,7 @@ import connectSliderToLayout from './connectSliderToLayout';
import connectImageToLayout from './connectImageToLayout';
import connectUpdateMenuToLayout from './connectUpdateMenuToLayout';
import connectRangeSelectorToAxis from './connectRangeSelectorToAxis';
+import connectLayersToMapbox from './connectLayersToMapbox';
import connectTransformToTrace from './connectTransformToTrace';
import connectAggregationToTransform from './connectAggregationToTransform';
import connectAxesToLayout from './connectAxesToLayout';
@@ -235,6 +236,7 @@ export {
connectLayoutToPlot,
connectNonCartesianSubplotToLayout,
connectRangeSelectorToAxis,
+ connectLayersToMapbox,
connectShapeToLayout,
connectSliderToLayout,
connectToContainer,
From 62ff50726b93d27b74caff1a09d1b13ce532e762 Mon Sep 17 00:00:00 2001
From: Nicolas Kruchten
Date: Tue, 17 Sep 2019 10:33:12 -0400
Subject: [PATCH 4/7] wip
---
src/EditorControls.js | 4 ++++
.../containers/MapboxLayersAccordion.js | 13 +++++++------
src/components/fields/derived.js | 18 ++++++++++++++++++
src/default_panels/StyleMapsPanel.js | 13 +++++--------
src/lib/connectLayersToMapbox.js | 19 +++++++++++++++++++
5 files changed, 53 insertions(+), 14 deletions(-)
diff --git a/src/EditorControls.js b/src/EditorControls.js
index d1651b9f9..8a6704569 100644
--- a/src/EditorControls.js
+++ b/src/EditorControls.js
@@ -344,6 +344,10 @@ class EditorControls extends Component {
move(graphDiv.layout.annotations);
}
+ if (payload.path === 'layout.mapbox.layers') {
+ move(graphDiv.layout[payload.mapboxId].layers);
+ }
+
const updatedData = payload.path.startsWith('data')
? graphDiv.data.slice()
: graphDiv.data;
diff --git a/src/components/containers/MapboxLayersAccordion.js b/src/components/containers/MapboxLayersAccordion.js
index 765525181..b85590b05 100644
--- a/src/components/containers/MapboxLayersAccordion.js
+++ b/src/components/containers/MapboxLayersAccordion.js
@@ -40,18 +40,19 @@ class MapboxLayersAccordion extends Component {
updateContainer({
[`layers[${mapboxLayerIndex}]`]: {
name: `Layer ${mapboxLayerIndex}`,
- sourcetype: 'geojson',
- source: {
- type: 'FeatureCollection',
- features: [],
- },
+ sourcetype: 'raster',
+ below: '',
},
});
}
},
};
- return {content ? content : null};
+ return (
+
+ {content ? content : null}
+
+ );
}
}
diff --git a/src/components/fields/derived.js b/src/components/fields/derived.js
index 962575138..b749580b5 100644
--- a/src/components/fields/derived.js
+++ b/src/components/fields/derived.js
@@ -13,6 +13,7 @@ import {UnconnectedTextEditor} from './TextEditor';
import {UnconnectedVisibilitySelect} from './VisibilitySelect';
import {connectToContainer, getAllAxes, getAxisTitle, axisIdToAxisName} from 'lib';
import PropTypes from 'prop-types';
+import Text from './Text';
export const AxisAnchorDropdown = connectToContainer(UnconnectedDropdown, {
modifyPlotProps: (props, context, plotProps) => {
@@ -662,6 +663,23 @@ export const FillDropdown = connectToContainer(UnconnectedDropdown, {
},
});
+export const MapboxSourceArray = connectToContainer(Text, {
+ modifyPlotProps: (props, context, plotProps) => {
+ const {fullValue, updatePlot} = plotProps;
+ if (plotProps.fullValue && plotProps.fullValue.length > 0) {
+ plotProps.fullValue = fullValue[0];
+ }
+
+ plotProps.updatePlot = v => {
+ if (v.length) {
+ updatePlot([v]);
+ } else {
+ updatePlot([]);
+ }
+ };
+ },
+});
+
export const MapboxStyleDropdown = connectToContainer(UnconnectedDropdown, {
modifyPlotProps: (props, context, plotProps) => {
const {mapBoxAccess, localize: _} = context;
diff --git a/src/default_panels/StyleMapsPanel.js b/src/default_panels/StyleMapsPanel.js
index 8afb3f435..e48a150f2 100644
--- a/src/default_panels/StyleMapsPanel.js
+++ b/src/default_panels/StyleMapsPanel.js
@@ -9,24 +9,21 @@ import {
Radio,
Numeric,
ColorPicker,
+ MapboxSourceArray,
} from '../components';
const StyleMapsPanel = (props, {localize: _}) => (
-
+
-
+
diff --git a/src/lib/connectLayersToMapbox.js b/src/lib/connectLayersToMapbox.js
index ea45fa837..85dece6eb 100644
--- a/src/lib/connectLayersToMapbox.js
+++ b/src/lib/connectLayersToMapbox.js
@@ -10,6 +10,7 @@ export default function connectLayersToMapbox(WrappedComponent) {
this.deleteMapboxLayer = this.deleteMapboxLayer.bind(this);
this.updateMapboxLayer = this.updateMapboxLayer.bind(this);
+ this.moveMapboxLayer = this.moveMapboxLayer.bind(this);
this.setLocals(props, context);
}
@@ -33,6 +34,7 @@ export default function connectLayersToMapbox(WrappedComponent) {
!this.context.getValObject ? null : this.context.getValObject(`layers[].${attr}`),
updateContainer: this.updateMapboxLayer,
deleteContainer: this.deleteMapboxLayer,
+ moveContainer: this.moveMapboxLayer,
container: this.container,
fullContainer: this.fullContainer,
};
@@ -60,6 +62,22 @@ export default function connectLayersToMapbox(WrappedComponent) {
}
}
+ moveMapboxLayer(direction) {
+ if (this.context.onUpdate) {
+ const mapboxLayerIndex = this.props.mapboxLayerIndex;
+ const desiredIndex = direction === 'up' ? mapboxLayerIndex - 1 : mapboxLayerIndex + 1;
+ this.context.onUpdate({
+ type: EDITOR_ACTIONS.MOVE_TO,
+ payload: {
+ fromIndex: mapboxLayerIndex,
+ toIndex: desiredIndex,
+ mapboxId: this.context.fullContainer._subplot.id,
+ path: 'layout.mapbox.layers',
+ },
+ });
+ }
+ }
+
render() {
return ;
}
@@ -85,6 +103,7 @@ export default function connectLayersToMapbox(WrappedComponent) {
MapboxLayerConnectedComponent.childContextTypes = {
updateContainer: PropTypes.func,
deleteContainer: PropTypes.func,
+ moveContainer: PropTypes.func,
container: PropTypes.object,
fullContainer: PropTypes.object,
getValObject: PropTypes.func,
From 4e0d19b039a49538a549cad4bf99c03f074ac536 Mon Sep 17 00:00:00 2001
From: Nicolas Kruchten
Date: Tue, 17 Sep 2019 14:06:36 -0400
Subject: [PATCH 5/7] fix dev app
---
dev/App.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dev/App.js b/dev/App.js
index 7ff298846..36305f3c9 100644
--- a/dev/App.js
+++ b/dev/App.js
@@ -14,7 +14,7 @@ import 'brace/theme/textmate';
// https://github.com/plotly/react-chart-editor#mapbox-access-tokens
import ACCESS_TOKENS from '../accessTokens';
-import {customConfigTest} from '../src/__stories__';
+// import {customConfigTest} from '../src/__stories__';
const dataSourceOptions = Object.keys(dataSources).map(name => ({
value: name,
From 57eb14f9c337b8cfda58ef9eb01de466153acddf Mon Sep 17 00:00:00 2001
From: Nicolas Kruchten
Date: Tue, 17 Sep 2019 21:50:21 -0400
Subject: [PATCH 6/7] below data by default
---
src/components/containers/MapboxLayersAccordion.js | 2 +-
src/default_panels/StyleMapsPanel.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/containers/MapboxLayersAccordion.js b/src/components/containers/MapboxLayersAccordion.js
index b85590b05..226edcd88 100644
--- a/src/components/containers/MapboxLayersAccordion.js
+++ b/src/components/containers/MapboxLayersAccordion.js
@@ -41,7 +41,7 @@ class MapboxLayersAccordion extends Component {
[`layers[${mapboxLayerIndex}]`]: {
name: `Layer ${mapboxLayerIndex}`,
sourcetype: 'raster',
- below: '',
+ below: 'traces',
},
});
}
diff --git a/src/default_panels/StyleMapsPanel.js b/src/default_panels/StyleMapsPanel.js
index e48a150f2..845cfd497 100644
--- a/src/default_panels/StyleMapsPanel.js
+++ b/src/default_panels/StyleMapsPanel.js
@@ -21,7 +21,7 @@ const StyleMapsPanel = (props, {localize: _}) => (
From b35fd7f0deb75481f3d80191b04a1d2e37e4f9f4 Mon Sep 17 00:00:00 2001
From: Nicolas Kruchten
Date: Wed, 18 Sep 2019 13:31:08 -0400
Subject: [PATCH 7/7] fix all react issues
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 828602fbd..23e4fc68e 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"fast-isnumeric": "^1.1.2",
"immutability-helper": "^3.0.0",
"plotly-icons": "1.3.13",
- "plotly.js": "1.49.4",
+ "plotly.js": "1.49.5",
"prop-types": "^15.7.2",
"raf": "^3.4.1",
"react-color": "^2.17.0",