From 212900bdf69c1d356f520772fc29fd3e33384ae6 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Thu, 12 Jul 2018 14:42:43 -0400 Subject: [PATCH 1/5] add GL enabled switch to TraceSelector --- dev/App.js | 11 ++- src/EditorControls.js | 13 +++- src/PlotlyEditor.js | 2 + src/components/fields/TraceSelector.js | 80 ++++++++++++++++++--- src/components/widgets/TraceTypeSelector.js | 17 ++++- src/lib/customTraceType.js | 22 +++--- 6 files changed, 122 insertions(+), 23 deletions(-) diff --git a/dev/App.js b/dev/App.js index 85da19506..51260be8e 100644 --- a/dev/App.js +++ b/dev/App.js @@ -44,10 +44,18 @@ const config = {mapboxAccessToken: ACCESS_TOKENS.MAPBOX, editable: true}; const traceTypesConfig = { traces: _ => [ { - value: 'scattergl', + value: 'scatter', icon: 'scatter', label: _('Scatter'), }, + { + value: 'line', + label: _('Line'), + }, + { + value: 'area', + label: _('Area'), + }, { value: 'bar', label: _('Bar'), @@ -155,6 +163,7 @@ class App extends Component { debug advancedTraceTypeSelector showFieldTooltips + // glByDefault // traceTypesConfig={traceTypesConfig} // makeDefaultTrace={() => ({type: 'scattergl', mode: 'markers'})} > diff --git a/src/EditorControls.js b/src/EditorControls.js index 04b20aae0..deb7137d8 100644 --- a/src/EditorControls.js +++ b/src/EditorControls.js @@ -49,6 +49,7 @@ class EditorControls extends Component { plotly: this.props.plotly, traceTypesConfig: this.props.traceTypesConfig, showFieldTooltips: this.props.showFieldTooltips, + glByDefault: this.props.glByDefault, }; } @@ -121,7 +122,14 @@ class EditorControls extends Component { // can't use default prop because plotly.js mutates it: // https://github.com/plotly/react-chart-editor/issues/509 - graphDiv.data.push(this.props.makeDefaultTrace()); + graphDiv.data.push( + this.props.makeDefaultTrace + ? this.props.makeDefaultTrace() + : { + type: `scatter${this.props.glByDefault ? 'gl' : ''}`, + mode: 'markers', + } + ); if (this.props.afterAddTrace) { this.props.afterAddTrace(payload); @@ -308,12 +316,12 @@ EditorControls.propTypes = { showFieldTooltips: PropTypes.bool, traceTypesConfig: PropTypes.object, makeDefaultTrace: PropTypes.func, + glByDefault: PropTypes.bool, }; EditorControls.defaultProps = { showFieldTooltips: false, locale: 'en', - makeDefaultTrace: () => ({type: 'scatter', mode: 'markers'}), traceTypesConfig: { categories: _ => categoryLayout(_), traces: _ => traceTypes(_), @@ -346,6 +354,7 @@ EditorControls.childContextTypes = { plotSchema: PropTypes.object, traceTypesConfig: PropTypes.object, showFieldTooltips: PropTypes.bool, + glByDefault: PropTypes.bool, }; export default EditorControls; diff --git a/src/PlotlyEditor.js b/src/PlotlyEditor.js index 0bddbf082..57fa4b0db 100644 --- a/src/PlotlyEditor.js +++ b/src/PlotlyEditor.js @@ -27,6 +27,7 @@ class PlotlyEditor extends Component { showFieldTooltips={this.props.showFieldTooltips} srcConverters={this.props.srcConverters} makeDefaultTrace={this.props.makeDefaultTrace} + glByDefault={this.props.glByDefault} > {this.props.children} @@ -77,6 +78,7 @@ PlotlyEditor.propTypes = { fromSrc: PropTypes.func.isRequired, }), makeDefaultTrace: PropTypes.func, + glByDefault: PropTypes.bool, }; PlotlyEditor.defaultProps = { diff --git a/src/components/fields/TraceSelector.js b/src/components/fields/TraceSelector.js index 0021ed85f..6f97766cb 100644 --- a/src/components/fields/TraceSelector.js +++ b/src/components/fields/TraceSelector.js @@ -10,13 +10,24 @@ import { import TraceTypeSelector, { TraceTypeSelectorButton, } from 'components/widgets/TraceTypeSelector'; +import RadioBlocks from 'components/widgets/RadioBlocks'; + import Field from './Field'; +export const glAvailable = type => { + return ['scatter', 'scatterpolar', 'scattergl', 'scatterpolargl'].includes( + type + ); +}; + class TraceSelector extends Component { constructor(props, context) { super(props, context); this.updatePlot = this.updatePlot.bind(this); + this.setGl = this.setGl.bind(this); + this.glEnabled = this.glEnabled.bind(this); + this.setTraceDefaults = this.setTraceDefaults.bind(this); this.setTraceDefaults( props.container, @@ -26,6 +37,10 @@ class TraceSelector extends Component { this.setLocals(props, context); } + glEnabled() { + return this.props.container.type.endsWith('gl') ? 'gl' : ''; + } + setLocals(props, context) { const _ = context.localize; if (props.traceOptions) { @@ -46,10 +61,11 @@ class TraceSelector extends Component { } } - setTraceDefaults(container, fullContainer, updateContainer) { + setTraceDefaults(container, fullContainer, updateContainer, gl) { if (container && !container.mode && fullContainer.type === 'scatter') { updateContainer({ - type: 'scatter', + type: + 'scatter' + (gl || this.context.glByDefault ? gl : this.glEnabled()), mode: fullContainer.mode || 'markers', }); } @@ -63,11 +79,28 @@ class TraceSelector extends Component { updatePlot(value) { const {updateContainer} = this.props; + const {glByDefault} = this.context; if (updateContainer) { - updateContainer(traceTypeToPlotlyInitFigure(value)); + updateContainer( + traceTypeToPlotlyInitFigure(value, this.glEnabled() || glByDefault) + ); } } + setGl(value) { + const {container, fullContainer, updateContainer} = this.props; + const gl = 'gl'; + + this.setTraceDefaults(container, fullContainer, updateContainer, value); + + const traceType = + this.fullValue.endsWith(gl) && value === '' + ? this.fullValue.slice(0, -gl.length) + : this.fullValue; + + updateContainer(traceTypeToPlotlyInitFigure(traceType, value)); + } + render() { const props = Object.assign({}, this.props, { fullValue: this.fullValue, @@ -75,17 +108,41 @@ class TraceSelector extends Component { options: this.traceOptions, clearable: false, }); + const {localize: _, advancedTraceTypeSelector} = this.context; + + const options = [ + {label: _('SVG'), value: ''}, + {label: _('WebGl'), value: 'gl'}, + ]; + // Check and see if the advanced selector prop is true - const {advancedTraceTypeSelector} = this.context; if (advancedTraceTypeSelector) { return ( - - this.context.openModal(TraceTypeSelector, props)} - /> - +
+ + + this.context.openModal(TraceTypeSelector, { + ...props, + glByDefault: this.context.glByDefault, + }) + } + /> + + {!glAvailable(this.props.container.type) ? ( + '' + ) : ( + + + + )} +
); } @@ -100,6 +157,7 @@ TraceSelector.contextTypes = { plotSchema: PropTypes.object, config: PropTypes.object, localize: PropTypes.func, + glByDefault: PropTypes.bool, }; TraceSelector.propTypes = { diff --git a/src/components/widgets/TraceTypeSelector.js b/src/components/widgets/TraceTypeSelector.js index a6d5f3c48..02cd3f43e 100644 --- a/src/components/widgets/TraceTypeSelector.js +++ b/src/components/widgets/TraceTypeSelector.js @@ -2,6 +2,7 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {SearchIcon, ThumnailViewIcon, GraphIcon} from 'plotly-icons'; import Modal from 'components/containers/Modal'; +import {glAvailable} from 'components/fields/TraceSelector'; import { traceTypeToPlotlyInitFigure, renderTraceIcon, @@ -75,8 +76,20 @@ const Item = ({item, active, handleClick, actions, showActions, complex}) => { class TraceTypeSelector extends Component { selectAndClose(value) { + const { + updateContainer, + glByDefault, + fullContainer: {type}, + } = this.props; const computedValue = traceTypeToPlotlyInitFigure(value); - this.props.updateContainer(computedValue); + if ( + (type.endsWith('gl') || (!glAvailable(type) && glByDefault)) && + glAvailable(computedValue.type) && + !computedValue.type.endsWith('gl') + ) { + computedValue.type = computedValue.type + 'gl'; + } + updateContainer(computedValue); this.context.handleClose(); } @@ -207,6 +220,8 @@ export class TraceTypeSelectorButton extends Component { TraceTypeSelector.propTypes = { updateContainer: PropTypes.func, fullValue: PropTypes.string, + fullContainer: PropTypes.object, + glByDefault: PropTypes.bool, }; TraceTypeSelector.contextTypes = { traceTypesConfig: PropTypes.object, diff --git a/src/lib/customTraceType.js b/src/lib/customTraceType.js index d78efe9dc..f54feb6de 100644 --- a/src/lib/customTraceType.js +++ b/src/lib/customTraceType.js @@ -1,31 +1,37 @@ export function plotlyTraceToCustomTrace(trace) { - const type = trace.type || 'scatter'; + const gl = 'gl'; + const type = trace.type.endsWith(gl) + ? trace.type.slice(0, -gl.length) + : trace.type || 'scatter'; + if ( - type === 'scatter' && + (type === 'scatter' || type === 'scattergl') && ['tozeroy', 'tozerox', 'tonexty', 'tonextx', 'toself', 'tonext'].includes( trace.fill ) ) { return 'area'; } else if ( - type === 'scatter' && + (type === 'scatter' || type === 'scattergl') && (trace.mode === 'lines' || trace.mode === 'lines+markers') ) { return 'line'; } else if (type === 'scatter3d' && trace.mode === 'lines') { return 'line3d'; } - return trace.type; + return type; } -export function traceTypeToPlotlyInitFigure(traceType) { +export function traceTypeToPlotlyInitFigure(traceType, gl = '') { switch (traceType) { case 'line': - return {type: 'scatter', mode: 'lines', fill: 'none'}; + return {type: 'scatter' + gl, mode: 'lines', fill: 'none'}; case 'scatter': - return {type: 'scatter', mode: 'markers', fill: 'none'}; + return {type: 'scatter' + gl, mode: 'markers', fill: 'none'}; case 'area': - return {type: 'scatter', fill: 'tozeroy'}; + return {type: 'scatter' + gl, fill: 'tozeroy'}; + case 'scatterpolar': + return {type: 'scatterpolar' + gl}; case 'ohlc': return { type: 'ohlc', From 825fe21adda2199ea2cc7d5871806d44f66db4e0 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Thu, 12 Jul 2018 15:11:21 -0400 Subject: [PATCH 2/5] remove GL traces from modal; rebalance modal --- src/components/widgets/TraceTypeSelector.js | 6 +-- src/lib/traceTypes.js | 44 ++++++++++----------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/components/widgets/TraceTypeSelector.js b/src/components/widgets/TraceTypeSelector.js index 02cd3f43e..c20993eae 100644 --- a/src/components/widgets/TraceTypeSelector.js +++ b/src/components/widgets/TraceTypeSelector.js @@ -101,9 +101,9 @@ class TraceTypeSelector extends Component { } = this.context; return categories(_).map((category, i) => { - const items = traces(_).filter( - ({category: {value}}) => value === category.value - ); + const items = traces(_) + .filter(({category: {value}}) => value === category.value) + .filter(i => i.value !== 'scattergl' && i.value !== 'scatterpolargl'); const MAX_ITEMS = 4; diff --git a/src/lib/traceTypes.js b/src/lib/traceTypes.js index 4fb542e5c..98f78ef0c 100644 --- a/src/lib/traceTypes.js +++ b/src/lib/traceTypes.js @@ -12,10 +12,10 @@ export const chartCategory = _ => { value: 'CHARTS_3D', label: _('3D charts'), }, - // FINANCIAL: { - // value: 'FINANCIAL', - // label: _('Finance'), - // }, + FINANCIAL: { + value: 'FINANCIAL', + label: _('Finance'), + }, DISTRIBUTIONS: { value: 'DISTRIBUTIONS', label: _('Distributions'), @@ -28,9 +28,9 @@ export const chartCategory = _ => { value: 'SPECIALIZED', label: _('Specialized'), }, - WEB_GL: { - value: 'WEB_GL', - label: _('WebGL'), + THREE_D: { + value: '3D', + label: _('3D'), }, }; }; @@ -38,11 +38,11 @@ export const chartCategory = _ => { // Layout specification for TraceTypeSelector.js export const categoryLayout = _ => [ chartCategory(_).SIMPLE, - chartCategory(_).WEB_GL, chartCategory(_).DISTRIBUTIONS, - chartCategory(_).SPECIALIZED, + chartCategory(_).THREE_D, chartCategory(_).MAPS, - // chartCategory(_).FINANCIAL, + chartCategory(_).FINANCIAL, + chartCategory(_).SPECIALIZED, ]; export const traceTypes = _ => [ @@ -89,22 +89,22 @@ export const traceTypes = _ => [ { value: 'scatter3d', label: _('3D Scatter'), - category: chartCategory(_).WEB_GL, + category: chartCategory(_).THREE_D, }, { value: 'line3d', label: _('3D Line'), - category: chartCategory(_).WEB_GL, + category: chartCategory(_).THREE_D, }, { value: 'surface', label: _('3D Surface'), - category: chartCategory(_).WEB_GL, + category: chartCategory(_).THREE_D, }, { value: 'mesh3d', label: _('3D Mesh'), - category: chartCategory(_).WEB_GL, + category: chartCategory(_).THREE_D, }, { value: 'box', @@ -174,34 +174,34 @@ export const traceTypes = _ => [ { value: 'candlestick', label: _('Candlestick'), - category: chartCategory(_).SPECIALIZED, + category: chartCategory(_).FINANCIAL, }, { value: 'ohlc', label: _('OHLC'), - category: chartCategory(_).SPECIALIZED, + category: chartCategory(_).FINANCIAL, }, // { // value: 'pointcloud', // label: _('Point Cloud'), - // category: chartCategory(_).WEB_GL, + // category: chartCategory(_).THREE_D, // }, { value: 'scattergl', icon: 'scatter', - label: _('Scatter GL'), - category: chartCategory(_).WEB_GL, + label: _('Scatter'), + category: chartCategory(_).THREE_D, }, { value: 'scatterpolargl', icon: 'scatterpolar', - label: _('Polar Scatter GL'), - category: chartCategory(_).WEB_GL, + label: _('Polar Scatter'), + category: chartCategory(_).THREE_D, }, // { // value: 'heatmapgl', // icon: 'heatmap', // label: _('Heatmap GL'), - // category: chartCategory(_).WEB_GL, + // category: chartCategory(_).THREE_D, // }, ]; From 5ce3d47b41a0840da6636bf134300b2319d5a8d1 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Thu, 12 Jul 2018 15:16:20 -0400 Subject: [PATCH 3/5] modal to close on Esc key --- src/components/containers/Modal.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/components/containers/Modal.js b/src/components/containers/Modal.js index 36f9dafdd..9b0e0b862 100644 --- a/src/components/containers/Modal.js +++ b/src/components/containers/Modal.js @@ -21,6 +21,26 @@ const ModalContent = ({children}) => ( ); class Modal extends Component { + constructor(props) { + super(props); + this.escFunction = this.escFunction.bind(this); + } + + escFunction(event) { + const escKeyCode = 27; + if (event.keyCode === escKeyCode) { + this.context.handleClose(); + } + } + + componentDidMount() { + document.addEventListener('keydown', this.escFunction, false); + } + + componentWillUnmount() { + document.removeEventListener('keydown', this.escFunction, false); + } + render() { const {children, title} = this.props; let classes = 'modal'; From d461d6f1512229f66b02dbc32ee04c5d11fdf0b2 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Thu, 12 Jul 2018 17:47:49 -0400 Subject: [PATCH 4/5] fix icon for scattergl --- src/lib/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/index.js b/src/lib/index.js index 619ae35ef..cfc47cd32 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -58,7 +58,10 @@ function renderTraceIcon(trace, prefix = 'Plot') { if (!trace) { return null; } - const componentName = `${prefix}${pascalCase(trace)}Icon`; + const gl = 'gl'; + const componentName = `${prefix}${pascalCase( + trace.endsWith(gl) ? trace.slice(0, -gl.length) : trace + )}Icon`; return PlotlyIcons[componentName] ? PlotlyIcons[componentName] : PlotlyIcons.PlotLineIcon; From 1a4eef76afe186ed17faf0fa0b719187664a7f4f Mon Sep 17 00:00:00 2001 From: dmt0 Date: Fri, 13 Jul 2018 11:26:32 -0400 Subject: [PATCH 5/5] don't include sattelite maps when there's not mapbox token --- src/EditorControls.js | 3 +++ src/PlotlyEditor.js | 3 +++ src/components/widgets/TraceTypeSelector.js | 8 +++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/EditorControls.js b/src/EditorControls.js index deb7137d8..3ddc20af5 100644 --- a/src/EditorControls.js +++ b/src/EditorControls.js @@ -50,6 +50,7 @@ class EditorControls extends Component { traceTypesConfig: this.props.traceTypesConfig, showFieldTooltips: this.props.showFieldTooltips, glByDefault: this.props.glByDefault, + mapBoxAccess: this.props.mapBoxAccess, }; } @@ -317,6 +318,7 @@ EditorControls.propTypes = { traceTypesConfig: PropTypes.object, makeDefaultTrace: PropTypes.func, glByDefault: PropTypes.bool, + mapBoxAccess: PropTypes.bool, }; EditorControls.defaultProps = { @@ -355,6 +357,7 @@ EditorControls.childContextTypes = { traceTypesConfig: PropTypes.object, showFieldTooltips: PropTypes.bool, glByDefault: PropTypes.bool, + mapBoxAccess: PropTypes.bool, }; export default EditorControls; diff --git a/src/PlotlyEditor.js b/src/PlotlyEditor.js index 57fa4b0db..054d3a9fe 100644 --- a/src/PlotlyEditor.js +++ b/src/PlotlyEditor.js @@ -28,6 +28,9 @@ class PlotlyEditor extends Component { srcConverters={this.props.srcConverters} makeDefaultTrace={this.props.makeDefaultTrace} glByDefault={this.props.glByDefault} + mapBoxAccess={Boolean( + this.props.config && this.props.config.mapboxAccessToken + )} > {this.props.children} diff --git a/src/components/widgets/TraceTypeSelector.js b/src/components/widgets/TraceTypeSelector.js index c20993eae..cd2815e06 100644 --- a/src/components/widgets/TraceTypeSelector.js +++ b/src/components/widgets/TraceTypeSelector.js @@ -97,14 +97,19 @@ class TraceTypeSelector extends Component { const {fullValue} = this.props; const { traceTypesConfig: {traces, categories, complex}, + mapBoxAccess, localize: _, } = this.context; return categories(_).map((category, i) => { - const items = traces(_) + let 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; let columnClasses = 'trace-grid__column'; @@ -227,6 +232,7 @@ TraceTypeSelector.contextTypes = { traceTypesConfig: PropTypes.object, handleClose: PropTypes.func, localize: PropTypes.func, + mapBoxAccess: PropTypes.bool, }; TraceTypeSelectorButton.propTypes = { handleClick: PropTypes.func.isRequired,