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",