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, 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/package.json b/package.json index 23547b7fd..23e4fc68e 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "draft-js-utils": "^1.3.3", "fast-isnumeric": "^1.1.2", "immutability-helper": "^3.0.0", - "plotly-icons": "1.3.12", - "plotly.js": "1.48.3", + "plotly-icons": "1.3.13", + "plotly.js": "1.49.5", "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..8a6704569 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) { @@ -283,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) { @@ -327,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 new file mode 100644 index 000000000..226edcd88 --- /dev/null +++ b/src/components/containers/MapboxLayersAccordion.js @@ -0,0 +1,73 @@ +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: 'raster', + below: 'traces', + }, + }); + } + }, + }; + + 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/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/fields/derived.js b/src/components/fields/derived.js index bc6d8822b..b749580b5 100644 --- a/src/components/fields/derived.js +++ b/src/components/fields/derived.js @@ -12,6 +12,8 @@ import {UnconnectedColorPicker} from './ColorPicker'; 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) => { @@ -574,8 +576,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 +663,54 @@ 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; + + 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'}, + {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'}, + ]); + plotProps.clearable = false; + }, +}); +MapboxStyleDropdown.contextTypes = { + mapBoxAccess: PropTypes.bool, + ...MapboxStyleDropdown.contextTypes, +}; + export const HoveronDropdown = connectToContainer(UnconnectedDropdown, { modifyPlotProps: (props, context, plotProps) => { const {localize: _} = context; 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/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..f17f151a8 100644 --- a/src/default_panels/GraphCreatePanel.js +++ b/src/default_panels/GraphCreatePanel.js @@ -11,6 +11,8 @@ import { TraceSelector, TraceTypeSection, LocationSelector, + Dropzone, + Numeric, } from '../components'; import { HistogramInfoVertical, @@ -29,6 +31,7 @@ const GraphCreatePanel = (props, {localize: _, setPanel}) => { > + @@ -68,6 +71,7 @@ const GraphCreatePanel = (props, {localize: _, setPanel}) => { }} attr="z" /> + 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}]} /> - + ( - - + + + + + + + + diff --git a/src/lib/connectLayersToMapbox.js b/src/lib/connectLayersToMapbox.js new file mode 100644 index 000000000..85dece6eb --- /dev/null +++ b/src/lib/connectLayersToMapbox.js @@ -0,0 +1,116 @@ +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.moveMapboxLayer = this.moveMapboxLayer.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, + moveContainer: this.moveMapboxLayer, + 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, + }, + }); + } + } + + 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 ; + } + } + + 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, + moveContainer: 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/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/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, 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'] && diff --git a/src/styles/components/containers/_modal.scss b/src/styles/components/containers/_modal.scss index c132faca7..c0de073bc 100644 --- a/src/styles/components/containers/_modal.scss +++ b/src/styles/components/containers/_modal.scss @@ -45,7 +45,7 @@ flex-direction: column; will-change: opacity, transform; flex-grow: 0; - margin: 5vh 10vw; + margin: 3vh 10vw; } &__header {