From 46de1b9e056be0d2c8a48019781bade0c0c4d488 Mon Sep 17 00:00:00 2001 From: Nicolas Kruchten Date: Fri, 23 Mar 2018 11:19:13 -0400 Subject: [PATCH 1/5] dev style tweaks --- dev/App.js | 5 ++--- dev/styles.css | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/dev/App.js b/dev/App.js index 4d642e746..9b75edfba 100644 --- a/dev/App.js +++ b/dev/App.js @@ -126,14 +126,13 @@ class App extends Component { placeholder={'Search for a mock'} /> -
-
-
Date: Mon, 26 Mar 2018 16:26:51 -0400 Subject: [PATCH 2/5] basic transforms --- dev/App.js | 36 ++++--- dev/mocks.json | 1 + dev/mocks/aggregate.json | 44 +++++++++ src/DefaultEditor.js | 2 + src/EditorControls.js | 18 ++++ src/components/containers/ModalBox.js | 4 +- src/components/containers/PanelHeader.js | 35 ++++++- .../containers/RangeSelectorAccordion.js | 9 +- .../containers/TransformAccordion.js | 66 +++++++++++++ src/components/containers/index.js | 2 + src/components/fields/DataSelector.js | 17 +--- src/components/index.js | 2 + src/default_panels/GraphTransformsPanel.js | 33 +++++++ src/default_panels/index.js | 2 + src/index.js | 4 + src/lib/connectTraceToPlot.js | 2 +- src/lib/connectTransformToTrace.js | 93 +++++++++++++++++++ src/lib/constants.js | 1 + src/lib/index.js | 2 + .../components/containers/_modalbox.scss | 6 +- 20 files changed, 345 insertions(+), 34 deletions(-) create mode 100644 dev/mocks/aggregate.json create mode 100644 src/components/containers/TransformAccordion.js create mode 100644 src/default_panels/GraphTransformsPanel.js create mode 100644 src/lib/connectTransformToTrace.js diff --git a/dev/App.js b/dev/App.js index 9b75edfba..b44223c1b 100644 --- a/dev/App.js +++ b/dev/App.js @@ -15,13 +15,21 @@ import 'brace/theme/textmate'; import ACCESS_TOKENS from '../accessTokens'; const dataSources = { - ints: [1, 2, 3, 4, 5], // eslint-disable-line no-magic-numbers - 'jagged ints': [2, 1, 3, 5, 4], // eslint-disable-line no-magic-numbers + ints: [1, 2, 3, 4, 5, 6], // eslint-disable-line no-magic-numbers + 'jagged ints': [2, 1, 3, 5, 4, 6], // eslint-disable-line no-magic-numbers + 'toggle ints': [1, -1, 1, -1, 1, -1], // eslint-disable-line no-magic-numbers 'big ints': [1000, 10100, 10000, 20000, 100000], // eslint-disable-line no-magic-numbers - dates: ['2010-01-01', '2010-07-01', '2011-01-01', '2011-07-01', '2012-01-01'], - months: ['January', 'February', 'March', 'April', 'May'], - colors: ['red', 'orange', 'yellow', 'green', 'blue'], - justblue: ['blue'], + dates: [ + '2010-01-01', + '2010-07-01', + '2011-01-01', + '2011-07-01', + '2012-01-01', + '2012-06-01', + ], + months: ['January', 'February', 'March', 'April', 'May', 'June'], + colors: ['red', 'orange', 'yellow', 'green', 'blue', 'indigo'], + 'blue and red': ['blue', 'red'], }; const dataSourceOptions = Object.keys(dataSources).map(name => ({ value: name, @@ -56,13 +64,14 @@ class App extends Component { } loadMock(mockIndex) { - fetch( - 'https://api.github.com/repos/plotly/plotly.js/contents/test/image/mocks/' + - this.state.mocks[mockIndex], - { - headers: new Headers({Accept: 'application/vnd.github.v3.raw'}), - } - ) + const mockName = this.state.mocks[mockIndex]; + const prefix = + mockName[0] === '/' + ? '' + : 'https://api.github.com/repos/plotly/plotly.js/contents/test/image/mocks/'; + fetch(prefix + mockName, { + headers: new Headers({Accept: 'application/vnd.github.v3.raw'}), + }) .then(response => response.json()) .then(figure => { const {data, layout, frames} = figure; @@ -151,6 +160,7 @@ class App extends Component { exec: this.loadJSON, }, ]} + editorProps={{$blockScrolling: true}} /> diff --git a/dev/mocks.json b/dev/mocks.json index 6cdcfb25a..790df9759 100644 --- a/dev/mocks.json +++ b/dev/mocks.json @@ -1,4 +1,5 @@ [ + "/mocks/aggregate.json", "0.json", "1.json", "10.json", diff --git a/dev/mocks/aggregate.json b/dev/mocks/aggregate.json new file mode 100644 index 000000000..f977a38ce --- /dev/null +++ b/dev/mocks/aggregate.json @@ -0,0 +1,44 @@ +{ + "data": [ + { + "type": "scatter", + "x": [ + "Moe", + "Larry", + "Curly", + "Moe", + "Larry", + "Curly", + "Moe", + "Larry", + "Curly", + "Moe", + "Larry", + "Curly" + ], + "y": [1, 6, 2, 8, 2, 9, 4, 5, 1, 5, 2, 8], + "mode": "markers", + "transforms": [ + { + "enabled" :true, + "type": "aggregate", + "groups": [ + "Moe", + "Larry", + "Curly", + "Moe", + "Larry", + "Curly", + "Moe", + "Larry", + "Curly", + "Moe", + "Larry", + "Curly" + ], + "aggregations": [{"target": "y", "func": "avg", "enabled": true}] + } + ] + } + ] +} diff --git a/src/DefaultEditor.js b/src/DefaultEditor.js index 6505c1590..170c534c1 100644 --- a/src/DefaultEditor.js +++ b/src/DefaultEditor.js @@ -4,6 +4,7 @@ import {PanelMenuWrapper} from './components'; import {localize} from './lib'; import { GraphCreatePanel, + GraphTransformsPanel, StyleLayoutPanel, StyleAxesPanel, StyleLegendPanel, @@ -20,6 +21,7 @@ const DefaultEditor = ({children, localize: _}) => ( + diff --git a/src/EditorControls.js b/src/EditorControls.js index 133d9241c..17d8af538 100644 --- a/src/EditorControls.js +++ b/src/EditorControls.js @@ -239,6 +239,24 @@ class EditorControls extends Component { } break; + case EDITOR_ACTIONS.DELETE_TRANSFORM: + if (isNumeric(payload.transformIndex)) { + for (let i = 0; i < graphDiv.data.length; i++) { + if ((graphDiv.data[i].uid === payload.traceUid) !== -1) { + graphDiv.data[i].transforms.splice(payload.transformIndex, 1); + if (this.props.onUpdate) { + this.props.onUpdate( + graphDiv.data, + Object.assign({}, graphDiv.layout), + graphDiv._transitionData._frames + ); + } + break; + } + } + } + break; + default: throw new Error('must specify an action type to handleEditorUpdate'); } diff --git a/src/components/containers/ModalBox.js b/src/components/containers/ModalBox.js index f2085ab47..69d2c23ec 100644 --- a/src/components/containers/ModalBox.js +++ b/src/components/containers/ModalBox.js @@ -4,9 +4,10 @@ import classnames from 'classnames'; export default class ModalBox extends Component { render() { - const {backgroundDark, children, onClose} = this.props; + const {backgroundDark, children, onClose, relative} = this.props; const modalboxClass = classnames('modalbox', { 'modalbox--dark': backgroundDark, + 'modalbox--relative': relative, }); return (
@@ -19,6 +20,7 @@ export default class ModalBox extends Component { ModalBox.propTypes = { backgroundDark: PropTypes.bool, + relative: PropTypes.bool, children: PropTypes.node, onClose: PropTypes.func, }; diff --git a/src/components/containers/PanelHeader.js b/src/components/containers/PanelHeader.js index 7502a7dff..6e2657196 100644 --- a/src/components/containers/PanelHeader.js +++ b/src/components/containers/PanelHeader.js @@ -3,8 +3,20 @@ import PropTypes from 'prop-types'; import React, {Component} from 'react'; import {PlusIcon, ResizeUpIcon, ResizeDownIcon} from 'plotly-icons'; import {localize} from 'lib'; +import ModalBox from './ModalBox'; class PanelHeader extends Component { + constructor() { + super(); + this.state = {addPanelOpen: false}; + + this.togglePanel = this.togglePanel.bind(this); + } + + togglePanel() { + this.setState({addPanelOpen: !this.state.addPanelOpen}); + } + render() { const { children, @@ -43,10 +55,29 @@ class PanelHeader extends Component {
) : null}
diff --git a/src/components/containers/RangeSelectorAccordion.js b/src/components/containers/RangeSelectorAccordion.js index 19b67074a..6e0ba93fc 100644 --- a/src/components/containers/RangeSelectorAccordion.js +++ b/src/components/containers/RangeSelectorAccordion.js @@ -27,7 +27,6 @@ class RangeSelectorAccordion extends Component { @@ -40,11 +39,15 @@ class RangeSelectorAccordion extends Component { handler: context => { const {fullContainer, updateContainer} = context; if (updateContainer) { - const shapeIndex = Array.isArray(fullContainer.rangeselector.buttons) + const rangeselectorIndex = Array.isArray( + fullContainer.rangeselector.buttons + ) ? fullContainer.rangeselector.buttons.length : 0; - updateContainer({[`rangeselector.buttons[${shapeIndex}]`]: {}}); + updateContainer({ + [`rangeselector.buttons[${rangeselectorIndex}]`]: {}, + }); } }, }; diff --git a/src/components/containers/TransformAccordion.js b/src/components/containers/TransformAccordion.js new file mode 100644 index 000000000..f57b8a8a8 --- /dev/null +++ b/src/components/containers/TransformAccordion.js @@ -0,0 +1,66 @@ +import Fold from './Fold'; +import Panel from './Panel'; +import PropTypes from 'prop-types'; +import React, {Component} from 'react'; +import {connectTransformToTrace, localize} from 'lib'; + +const TransformFold = connectTransformToTrace(Fold); + +class TransformAccordion extends Component { + render() { + const {fullContainer: {transforms = []}} = this.context; + const {children, localize: _} = this.props; + + const transformTypes = [ + {label: _('Filter'), type: 'filter'}, + {label: _('Split'), type: 'groupby'}, + {label: _('Aggregate'), type: 'aggregate'}, + ]; + + const filteredTransforms = transforms.filter(({type}) => Boolean(type)); + const content = + filteredTransforms.length && + filteredTransforms.map((tr, i) => ( + type === tr.type)[0].label} + canDelete={true} + > + {children} + + )); + + const addAction = { + label: _('Transform'), + handler: transformTypes.map(({label, type}) => { + return { + label, + handler: context => { + const {fullContainer, updateContainer} = context; + if (updateContainer) { + const transformIndex = Array.isArray(fullContainer.transforms) + ? fullContainer.transforms.length + : 0; + const key = `transforms[${transformIndex}]`; + updateContainer({[key]: {type}}); + } + }, + }; + }), + }; + + return {content ? content : null}; + } +} + +TransformAccordion.contextTypes = { + fullContainer: PropTypes.object, +}; + +TransformAccordion.propTypes = { + children: PropTypes.node, + localize: PropTypes.func, +}; + +export default localize(TransformAccordion); diff --git a/src/components/containers/index.js b/src/components/containers/index.js index ad2e255d0..27e3b1865 100644 --- a/src/components/containers/index.js +++ b/src/components/containers/index.js @@ -10,6 +10,7 @@ import MenuPanel from './MenuPanel'; import Panel from './Panel'; import Section from './Section'; import TraceAccordion from './TraceAccordion'; +import TransformAccordion from './TransformAccordion'; import TraceMarkerSection from './TraceMarkerSection'; import {LayoutPanel, TraceTypeSection} from './derived'; import TraceRequiredPanel from './TraceRequiredPanel'; @@ -30,6 +31,7 @@ export { Panel, Section, TraceAccordion, + TransformAccordion, TraceMarkerSection, TraceRequiredPanel, LayoutPanel, diff --git a/src/components/fields/DataSelector.js b/src/components/fields/DataSelector.js index 219d14cb2..7b8e00526 100644 --- a/src/components/fields/DataSelector.js +++ b/src/components/fields/DataSelector.js @@ -24,18 +24,10 @@ export class UnconnectedDataSelector extends Component { setLocals(props, context) { this.dataSources = context.dataSources || {}; this.dataSourceOptions = context.dataSourceOptions || []; - this.dataSrcExists = false; - if (attributeIsData(props.attrMeta)) { - this.dataSrcExists = true; - this.srcAttr = props.attr + 'src'; - this.srcProperty = nestedProperty(props.container, this.srcAttr); - } - if (this.dataSrcExists) { - this.fullValue = this.srcProperty.get(); - } else { - this.fullValue = this.props.fullValue; - } + this.srcAttr = props.attr + 'src'; + this.srcProperty = nestedProperty(props.container, this.srcAttr); + this.fullValue = this.srcProperty.get(); this.is2D = (props.attr === 'z' && @@ -77,11 +69,10 @@ export class UnconnectedDataSelector extends Component { ) { update[this.props.attr] = update[this.props.attr][0]; } - update[this.srcAttr] = value; } else { - update[this.srcAttr] = value; update[this.props.attr] = this.dataSources[value] || null; } + update[this.srcAttr] = value; this.props.updateContainer(update); } diff --git a/src/components/index.js b/src/components/index.js index 3b52037a6..fecb01853 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -51,6 +51,7 @@ import { AnnotationAccordion, ShapeAccordion, SliderAccordion, + TransformAccordion, ImageAccordion, UpdateMenuAccordion, RangeSelectorAccordion, @@ -84,6 +85,7 @@ export { ImageAccordion, UpdateMenuAccordion, AnnotationArrowRef, + TransformAccordion, RangesliderVisible, AnnotationRef, PositioningRef, diff --git a/src/default_panels/GraphTransformsPanel.js b/src/default_panels/GraphTransformsPanel.js new file mode 100644 index 000000000..010b328ee --- /dev/null +++ b/src/default_panels/GraphTransformsPanel.js @@ -0,0 +1,33 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + Radio, + TransformAccordion, + TraceAccordion, + DataSelector, +} from '../components'; +import {localize} from '../lib'; + +const GraphTransformsPanel = ({localize: _}) => { + return ( + + + + + + + + ); +}; + +GraphTransformsPanel.propTypes = { + localize: PropTypes.func, +}; + +export default localize(GraphTransformsPanel); diff --git a/src/default_panels/index.js b/src/default_panels/index.js index 1625a2d34..515b8d812 100644 --- a/src/default_panels/index.js +++ b/src/default_panels/index.js @@ -1,4 +1,5 @@ import GraphCreatePanel from './GraphCreatePanel'; +import GraphTransformsPanel from './GraphTransformsPanel'; import StyleLayoutPanel from './StyleLayoutPanel'; import StyleAxesPanel from './StyleAxesPanel'; import StyleLegendPanel from './StyleLegendPanel'; @@ -12,6 +13,7 @@ import StyleUpdateMenusPanel from './StyleUpdateMenusPanel'; export { GraphCreatePanel, + GraphTransformsPanel, StyleLayoutPanel, StyleAxesPanel, StyleLegendPanel, diff --git a/src/index.js b/src/index.js index b672430fd..62ea57f4d 100644 --- a/src/index.js +++ b/src/index.js @@ -6,6 +6,7 @@ import { connectShapeToLayout, connectImageToLayout, connectAxesToLayout, + connectTransformToTrace, connectLayoutToPlot, connectToContainer, connectTraceToPlot, @@ -28,6 +29,7 @@ import { ArrowSelector, AxesFold, AxesRange, + TransformAccordion, NTicks, DTicks, AxesSelector, @@ -92,6 +94,7 @@ export { AnnotationRef, PositioningRef, ArrowSelector, + TransformAccordion, AxesFold, AxesRange, NTicks, @@ -149,6 +152,7 @@ export { connectShapeToLayout, connectImageToLayout, connectAxesToLayout, + connectTransformToTrace, connectLayoutToPlot, connectToContainer, connectRangeSelectorToAxis, diff --git a/src/lib/connectTraceToPlot.js b/src/lib/connectTraceToPlot.js index f6c06492b..6194bb4d2 100644 --- a/src/lib/connectTraceToPlot.js +++ b/src/lib/connectTraceToPlot.js @@ -31,7 +31,7 @@ export default function connectTraceToPlot(WrappedComponent) { let fullTrace = {}; for (let i = 0; i < fullData.length; i++) { - if (trace.uid === fullData[i]._fullInput.uid) { + if (trace.uid === fullData[i]._fullInput._input.uid) { fullTrace = fullData[i]._fullInput; break; } diff --git a/src/lib/connectTransformToTrace.js b/src/lib/connectTransformToTrace.js new file mode 100644 index 000000000..2b2e67092 --- /dev/null +++ b/src/lib/connectTransformToTrace.js @@ -0,0 +1,93 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import {getDisplayName} from '../lib'; +import {EDITOR_ACTIONS} from './constants'; + +export default function connectTransformToTrace(WrappedComponent) { + class TransformConnectedComponent extends Component { + constructor(props, context) { + super(props, context); + + this.deleteTransform = this.deleteTransform.bind(this); + this.updateTransform = this.updateTransform.bind(this); + this.setLocals(props, context); + } + + componentWillReceiveProps(nextProps, nextContext) { + this.setLocals(nextProps, nextContext); + } + + setLocals(props, context) { + const {transformIndex} = props; + const {container, fullContainer} = context; + + const transforms = container.transforms || []; + const fullTransforms = fullContainer.transforms || []; + this.container = transforms[transformIndex]; + this.fullContainer = fullTransforms[transformIndex]; + } + + getChildContext() { + return { + updateContainer: this.updateTransform, + deleteContainer: this.deleteTransform, + container: this.container, + fullContainer: this.fullContainer, + }; + } + + updateTransform(update) { + const newUpdate = {}; + const {transformIndex} = this.props; + for (const key in update) { + const newkey = `transforms[${transformIndex}].${key}`; + newUpdate[newkey] = update[key]; + } + this.context.updateContainer(newUpdate); + } + + deleteTransform() { + if (this.context.onUpdate) { + this.context.onUpdate({ + type: EDITOR_ACTIONS.DELETE_TRANSFORM, + payload: { + traceUid: this.context.fullContainer._input.uid, + transformIndex: this.props.transformIndex, + }, + }); + } + } + + render() { + return ; + } + } + + TransformConnectedComponent.displayName = `TransformConnected${getDisplayName( + WrappedComponent + )}`; + + TransformConnectedComponent.propTypes = { + transformIndex: PropTypes.number.isRequired, + }; + + TransformConnectedComponent.contextTypes = { + container: PropTypes.object, + fullContainer: PropTypes.object, + data: PropTypes.array, + onUpdate: PropTypes.func, + updateContainer: PropTypes.func, + }; + + TransformConnectedComponent.childContextTypes = { + updateContainer: PropTypes.func, + deleteContainer: PropTypes.func, + container: PropTypes.object, + fullContainer: PropTypes.object, + }; + + const {plotly_editor_traits} = WrappedComponent; + TransformConnectedComponent.plotly_editor_traits = plotly_editor_traits; + + return TransformConnectedComponent; +} diff --git a/src/lib/constants.js b/src/lib/constants.js index 2ca9aa02b..d50c4e012 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -37,6 +37,7 @@ export const EDITOR_ACTIONS = { DELETE_SHAPE: 'plotly-editor-delete-shape', DELETE_IMAGE: 'plotly-editor-delete-image', DELETE_RANGESELECTOR: 'plotly-editor-delete-rangeselector', + DELETE_TRANSFORM: 'plotly-editor-delete-transform', }; export const DEFAULT_FONTS = [ diff --git a/src/lib/index.js b/src/lib/index.js index b025a4c28..2cbd0ada7 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -5,6 +5,7 @@ import connectSliderToLayout from './connectSliderToLayout'; import connectImageToLayout from './connectImageToLayout'; import connectUpdateMenuToLayout from './connectUpdateMenuToLayout'; import connectRangeSelectorToAxis from './connectRangeSelectorToAxis'; +import connectTransformToTrace from './connectTransformToTrace'; import connectAxesToLayout from './connectAxesToLayout'; import connectLayoutToPlot, {getLayoutContext} from './connectLayoutToPlot'; import connectToContainer, { @@ -82,6 +83,7 @@ export { connectLayoutToPlot, connectToContainer, connectRangeSelectorToAxis, + connectTransformToTrace, connectTraceToPlot, containerConnectedContextTypes, computeTraceOptionsFromSchema, diff --git a/src/styles/components/containers/_modalbox.scss b/src/styles/components/containers/_modalbox.scss index 8673b4d52..a96f3ea42 100644 --- a/src/styles/components/containers/_modalbox.scss +++ b/src/styles/components/containers/_modalbox.scss @@ -9,7 +9,7 @@ box-shadow: var(--box-shadow-base); left: calc(var(--spacing-quarter-unit) * -1); width: calc(100% + var(--spacing-half-unit)); - top:calc(100% + var(--spacing-quarter-unit)); + top: calc(100% + var(--spacing-quarter-unit)); background-color: var(--color-background-top); @include z-index('cloud'); @@ -25,4 +25,8 @@ &--dark { background-color: var(--color-background-inverse); } + + &--relative { + position: relative; + } } From 7889d7754db0f36991988f94a7ab6d4d52c67c1a Mon Sep 17 00:00:00 2001 From: Nicolas Kruchten Date: Tue, 27 Mar 2018 11:26:43 -0400 Subject: [PATCH 3/5] aggregations list --- src/components/containers/Section.js | 2 +- src/default_panels/GraphTransformsPanel.js | 55 ++++++++++++++- src/index.js | 2 + src/lib/connect-to-field.js | 0 src/lib/connectAggregationToTransform.js | 79 ++++++++++++++++++++++ src/lib/index.js | 2 + 6 files changed, 137 insertions(+), 3 deletions(-) delete mode 100644 src/lib/connect-to-field.js create mode 100644 src/lib/connectAggregationToTransform.js diff --git a/src/components/containers/Section.js b/src/components/containers/Section.js index 665f91f5a..2cd50ea44 100644 --- a/src/components/containers/Section.js +++ b/src/components/containers/Section.js @@ -59,7 +59,7 @@ class Section extends Component { } return (
- + {this.props.name && } {this.children}
); diff --git a/src/default_panels/GraphTransformsPanel.js b/src/default_panels/GraphTransformsPanel.js index 010b328ee..fdc406bc3 100644 --- a/src/default_panels/GraphTransformsPanel.js +++ b/src/default_panels/GraphTransformsPanel.js @@ -1,12 +1,59 @@ -import React from 'react'; +import React, {Component} from 'react'; import PropTypes from 'prop-types'; import { Radio, TransformAccordion, TraceAccordion, DataSelector, + Dropdown, + Section, } from '../components'; -import {localize} from '../lib'; +import {connectAggregationToTransform, localize} from '../lib'; + +const AggregationSection = connectAggregationToTransform(Section); + +class UnlocalizedAggregations extends Component { + render() { + const {fullContainer: {aggregations = []}} = this.context; + const {localize: _} = this.props; + if (aggregations.length === 0) { + return null; + } + + return aggregations.map(({target}, i) => ( + + + + )); + } +} + +UnlocalizedAggregations.contextTypes = { + fullContainer: PropTypes.object, +}; + +UnlocalizedAggregations.propTypes = { + localize: PropTypes.func, +}; + +const Aggregations = localize(UnlocalizedAggregations); const GraphTransformsPanel = ({localize: _}) => { return ( @@ -21,6 +68,10 @@ const GraphTransformsPanel = ({localize: _}) => { /> + +
+ +
); diff --git a/src/index.js b/src/index.js index 62ea57f4d..21efd9823 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ import EditorControls from './EditorControls'; import { connectAnnotationToLayout, connectShapeToLayout, + connectAggregationToTransform, connectImageToLayout, connectAxesToLayout, connectTransformToTrace, @@ -96,6 +97,7 @@ export { ArrowSelector, TransformAccordion, AxesFold, + connectAggregationToTransform, AxesRange, NTicks, DTicks, diff --git a/src/lib/connect-to-field.js b/src/lib/connect-to-field.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/lib/connectAggregationToTransform.js b/src/lib/connectAggregationToTransform.js new file mode 100644 index 000000000..6befbb286 --- /dev/null +++ b/src/lib/connectAggregationToTransform.js @@ -0,0 +1,79 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import {getDisplayName} from '../lib'; + +export default function connectAggregationToTransform(WrappedComponent) { + class AggregationConnectedComponent extends Component { + constructor(props, context) { + super(props, context); + + this.updateAggregation = this.updateAggregation.bind(this); + this.setLocals(props, context); + } + + componentWillReceiveProps(nextProps, nextContext) { + this.setLocals(nextProps, nextContext); + } + + setLocals(props, context) { + const {aggregationIndex} = props; + const {container, fullContainer} = context; + + const aggregations = container.aggregations || []; + const fullAggregations = fullContainer.aggregations || []; + this.container = aggregations[aggregationIndex]; + this.fullContainer = fullAggregations[aggregationIndex]; + } + + getChildContext() { + return { + updateContainer: this.updateAggregation, + container: this.container, + fullContainer: this.fullContainer, + }; + } + + updateAggregation(update) { + const newUpdate = {}; + const path = `aggregations[${this.props.aggregationIndex}]`; + for (const key in update) { + newUpdate[`${path}.${key}`] = update[key]; + } + newUpdate[`${path}.target`] = this.fullContainer.target; + newUpdate[`${path}.enabled`] = true; + this.context.updateContainer(newUpdate); + } + + render() { + return ; + } + } + + AggregationConnectedComponent.displayName = `AggregationConnected${getDisplayName( + WrappedComponent + )}`; + + AggregationConnectedComponent.propTypes = { + aggregationIndex: PropTypes.number.isRequired, + }; + + AggregationConnectedComponent.contextTypes = { + container: PropTypes.object, + fullContainer: PropTypes.object, + data: PropTypes.array, + onUpdate: PropTypes.func, + updateContainer: PropTypes.func, + }; + + AggregationConnectedComponent.childContextTypes = { + updateContainer: PropTypes.func, + deleteContainer: PropTypes.func, + container: PropTypes.object, + fullContainer: PropTypes.object, + }; + + const {plotly_editor_traits} = WrappedComponent; + AggregationConnectedComponent.plotly_editor_traits = plotly_editor_traits; + + return AggregationConnectedComponent; +} diff --git a/src/lib/index.js b/src/lib/index.js index 2cbd0ada7..37524b2f7 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -6,6 +6,7 @@ import connectImageToLayout from './connectImageToLayout'; import connectUpdateMenuToLayout from './connectUpdateMenuToLayout'; import connectRangeSelectorToAxis from './connectRangeSelectorToAxis'; import connectTransformToTrace from './connectTransformToTrace'; +import connectAggregationToTransform from './connectAggregationToTransform'; import connectAxesToLayout from './connectAxesToLayout'; import connectLayoutToPlot, {getLayoutContext} from './connectLayoutToPlot'; import connectToContainer, { @@ -84,6 +85,7 @@ export { connectToContainer, connectRangeSelectorToAxis, connectTransformToTrace, + connectAggregationToTransform, connectTraceToPlot, containerConnectedContextTypes, computeTraceOptionsFromSchema, From 8e0229e4e7febca637307a7dba18af43cb2fa339 Mon Sep 17 00:00:00 2001 From: Nicolas Kruchten Date: Tue, 27 Mar 2018 11:38:16 -0400 Subject: [PATCH 4/5] move transforms panel to dev --- dev/App.js | 3 ++- src/DefaultEditor.js | 2 -- src/index.js | 2 ++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dev/App.js b/dev/App.js index b44223c1b..b68472d0f 100644 --- a/dev/App.js +++ b/dev/App.js @@ -6,7 +6,7 @@ import 'react-select/dist/react-select.css'; import brace from 'brace'; // eslint-disable-line no-unused-vars import AceEditor from 'react-ace'; import Select from 'react-select'; -import PlotlyEditor, {DefaultEditor, Panel} from '../src'; +import PlotlyEditor, {DefaultEditor, Panel, GraphTransformsPanel} from '../src'; import Inspector from 'react-inspector'; import 'brace/mode/json'; import 'brace/theme/textmate'; @@ -118,6 +118,7 @@ class App extends Component { advancedTraceTypeSelector > +