Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/EditorControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,46 @@ class EditorControls extends Component {
}
break;

case EDITOR_ACTIONS.MOVE_TO:
// checking if fromIndex and toIndex is a number because
// gives errors if index is 0 (falsy value)
if (payload.path && !isNaN(payload.fromIndex) && !isNaN(payload.toIndex)) {
function move(container) {
const movedEl = container[payload.fromIndex];
const replacedEl = container[payload.toIndex];
container[payload.toIndex] = movedEl;
container[payload.fromIndex] = replacedEl;
}

if (payload.path === 'data') {
move(graphDiv.data);
}

if (payload.path === 'layout.images') {
move(graphDiv.layout.images);
}

if (payload.path === 'layout.shapes') {
move(graphDiv.layout.shapes);
}

if (payload.path === 'layout.annotations') {
move(graphDiv.layout.annotations);
}

const updatedData = payload.path.startsWith('data')
? graphDiv.data.slice()
: graphDiv.data;
const updatedLayout = payload.path.startsWith('layout')
? Object.assign({}, graphDiv.layout)
: graphDiv.layout;

if (this.props.onUpdate) {
this.props.onUpdate(updatedData, updatedLayout, graphDiv._transitionData._frames);
}
}
break;

default:
throw new Error(this.localize('must specify an action type to handleEditorUpdate'));
}
Expand Down
5 changes: 3 additions & 2 deletions src/components/containers/AnnotationAccordion.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class AnnotationAccordion extends Component {
layout: {annotations = [], meta = []},
localize: _,
} = this.context;
const {canAdd, children} = this.props;
const {canAdd, children, canReorder} = this.props;

const content =
annotations.length &&
Expand Down Expand Up @@ -50,7 +50,7 @@ class AnnotationAccordion extends Component {
};

return (
<LayoutPanel addAction={canAdd ? addAction : null}>
<LayoutPanel addAction={canAdd ? addAction : null} canReorder={canReorder}>
{content ? (
content
) : (
Expand All @@ -76,6 +76,7 @@ AnnotationAccordion.contextTypes = {
AnnotationAccordion.propTypes = {
children: PropTypes.node,
canAdd: PropTypes.bool,
canReorder: PropTypes.bool,
};

export default AnnotationAccordion;
5 changes: 3 additions & 2 deletions src/components/containers/ImageAccordion.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ImageAccordion extends Component {
layout: {images = []},
localize: _,
} = this.context;
const {canAdd, children} = this.props;
const {canAdd, children, canReorder} = this.props;

const content =
images.length &&
Expand Down Expand Up @@ -48,7 +48,7 @@ class ImageAccordion extends Component {
};

return (
<LayoutPanel addAction={canAdd ? addAction : null}>
<LayoutPanel addAction={canAdd ? addAction : null} canReorder={canReorder}>
{content ? (
content
) : (
Expand All @@ -74,6 +74,7 @@ ImageAccordion.contextTypes = {
ImageAccordion.propTypes = {
children: PropTypes.node,
canAdd: PropTypes.bool,
canReorder: PropTypes.bool,
};

export default ImageAccordion;
48 changes: 45 additions & 3 deletions src/components/containers/PlotlyFold.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class Fold extends Component {
if (!this.foldVisible && !this.props.messageIfEmpty) {
return null;
}
const {deleteContainer} = this.context;
const {deleteContainer, moveContainer} = this.context;
const {
canDelete,
children,
Expand All @@ -33,6 +33,8 @@ export class Fold extends Component {
icon: Icon,
messageIfEmpty,
name,
canMoveUp,
canMoveDown,
} = this.props;

const contentClass = classnames('fold__content', {
Expand All @@ -47,7 +49,7 @@ export class Fold extends Component {
'fold__top__arrow--open': !folded,
});

const arrowIcon = (
const arrowDownIcon = (
<div className={arrowClass}>
<div className="fold__top__arrow__wrapper">
<AngleDownIcon />
Expand All @@ -70,13 +72,50 @@ export class Fold extends Component {
</div>
) : null;

const movingControls = (canMoveDown || canMoveUp) && (
<div className="fold__top__moving-controls">
<span
className={`fold__top__moving-controls--up${canMoveUp ? '' : '--disabled'}`}
onClick={e => {
// prevents fold toggle to happen when clicking on moving arrow controls
e.stopPropagation();

if (canMoveUp) {
if (!moveContainer || typeof moveContainer !== 'function') {
throw new Error('moveContainer must be a function');
}
moveContainer('up');
}
}}
>
<AngleDownIcon />
</span>
<span
className={`fold__top__moving-controls--down${canMoveDown ? '' : '--disabled'}`}
onClick={e => {
// prevents fold toggle to happen when clicking on moving arrow controls
e.stopPropagation();
if (canMoveDown) {
if (!moveContainer || typeof moveContainer !== 'function') {
throw new Error('moveContainer must be a function');
}
moveContainer('down');
}
}}
>
<AngleDownIcon />
</span>
</div>
);

const foldHeader = !hideHeader && (
<div className={headerClass} onClick={toggleFold}>
<div className="fold__top__arrow-title">
{arrowIcon}
{arrowDownIcon}
{icon}
<div className="fold__top__title">{striptags(name)}</div>
</div>
{movingControls}
{deleteButton}
</div>
);
Expand Down Expand Up @@ -118,6 +157,8 @@ Fold.propTypes = {
icon: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
messageIfEmpty: PropTypes.string,
name: PropTypes.string,
canMoveUp: PropTypes.bool,
canMoveDown: PropTypes.bool,
};

Fold.contextTypes = {
Expand Down Expand Up @@ -175,6 +216,7 @@ PlotlyFold.plotly_editor_traits = {
PlotlyFold.contextTypes = Object.assign(
{
deleteContainer: PropTypes.func,
moveContainer: PropTypes.func,
},
containerConnectedContextTypes
);
Expand Down
7 changes: 7 additions & 0 deletions src/components/containers/PlotlyPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export class Panel extends Component {

render() {
const {individualFoldStates, hasError} = this.state;
const {canReorder} = this.props;

if (hasError) {
return <PanelError />;
Expand All @@ -97,6 +98,11 @@ export class Panel extends Component {
key: index,
folded: individualFoldStates[index] || false,
toggleFold: () => this.toggleFold(index),
canMoveUp: canReorder && individualFoldStates.length > 1 && index > 0,
canMoveDown:
canReorder &&
individualFoldStates.length > 1 &&
index !== individualFoldStates.length - 1,
});
}
return child;
Expand All @@ -122,6 +128,7 @@ Panel.propTypes = {
deleteAction: PropTypes.func,
noPadding: PropTypes.bool,
showExpandCollapse: PropTypes.bool,
canReorder: PropTypes.bool,
};

Panel.defaultProps = {
Expand Down
5 changes: 3 additions & 2 deletions src/components/containers/ShapeAccordion.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ShapeAccordion extends Component {
layout: {shapes = []},
localize: _,
} = this.context;
const {canAdd, children} = this.props;
const {canAdd, children, canReorder} = this.props;

const content =
shapes.length &&
Expand Down Expand Up @@ -48,7 +48,7 @@ class ShapeAccordion extends Component {
};

return (
<LayoutPanel addAction={canAdd ? addAction : null}>
<LayoutPanel addAction={canAdd ? addAction : null} canReorder={canReorder}>
{content ? (
content
) : (
Expand All @@ -74,6 +74,7 @@ ShapeAccordion.contextTypes = {
ShapeAccordion.propTypes = {
children: PropTypes.node,
canAdd: PropTypes.bool,
canReorder: PropTypes.bool,
};

export default ShapeAccordion;
5 changes: 3 additions & 2 deletions src/components/containers/TraceAccordion.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class TraceAccordion extends Component {
}

render() {
const {canAdd, canGroup} = this.props;
const {canAdd, canGroup, canReorder} = this.props;
const _ = this.context.localize;

if (canAdd) {
Expand All @@ -146,7 +146,7 @@ class TraceAccordion extends Component {
};
const traceFolds = this.renderTraceFolds();
return (
<PlotlyPanel addAction={addAction}>
<PlotlyPanel addAction={addAction} canReorder={canReorder}>
{traceFolds ? traceFolds : this.renderTracePanelHelp()}
</PlotlyPanel>
);
Expand Down Expand Up @@ -190,6 +190,7 @@ TraceAccordion.contextTypes = {
TraceAccordion.propTypes = {
canAdd: PropTypes.bool,
canGroup: PropTypes.bool,
canReorder: PropTypes.bool,
children: PropTypes.node,
traceFilterCondition: PropTypes.func,
};
Expand Down
1 change: 1 addition & 0 deletions src/default_panels/GraphCreatePanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const GraphCreatePanel = (props, {localize: _, setPanel}) => {
traceFilterCondition={t =>
!(t.transforms && t.transforms.some(tr => ['fit', 'moving-average'].includes(tr.type)))
}
canReorder
>
<TraceSelector label={_('Type')} attr="type" show />

Expand Down
2 changes: 1 addition & 1 deletion src/default_panels/StyleImagesPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '../components';

const StyleImagesPanel = (props, {localize: _}) => (
<ImageAccordion canAdd>
<ImageAccordion canAdd canReorder>
<Radio
attr="visible"
options={[{label: _('Show'), value: true}, {label: _('Hide'), value: false}]}
Expand Down
2 changes: 1 addition & 1 deletion src/default_panels/StyleNotesPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from '../components';

const StyleNotesPanel = (props, {localize: _}) => (
<AnnotationAccordion canAdd>
<AnnotationAccordion canAdd canReorder>
<PlotlySection name={_('Note Text')} attr="text">
<TextEditor attr="text" />
<FontSelector label={_('Typeface')} attr="font.family" />
Expand Down
2 changes: 1 addition & 1 deletion src/default_panels/StyleShapesPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from '../components';

const StyleShapesPanel = (props, {localize: _}) => (
<ShapeAccordion canAdd>
<ShapeAccordion canAdd canReorder>
<Radio
attr="visible"
options={[{label: _('Show'), value: true}, {label: _('Hide'), value: false}]}
Expand Down
18 changes: 18 additions & 0 deletions src/lib/connectAnnotationToLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default function connectAnnotationToLayout(WrappedComponent) {

this.deleteAnnotation = this.deleteAnnotation.bind(this);
this.updateAnnotation = this.updateAnnotation.bind(this);
this.moveAnnotation = this.moveAnnotation.bind(this);
this.setLocals(props, context);
}

Expand All @@ -35,6 +36,7 @@ export default function connectAnnotationToLayout(WrappedComponent) {
deleteContainer: this.deleteAnnotation,
container: this.container,
fullContainer: this.fullContainer,
moveContainer: this.moveAnnotation,
};
}

Expand All @@ -57,6 +59,21 @@ export default function connectAnnotationToLayout(WrappedComponent) {
}
}

moveAnnotation(direction) {
if (this.context.onUpdate) {
const annotationIndex = this.props.annotationIndex;
const desiredIndex = direction === 'up' ? annotationIndex - 1 : annotationIndex + 1;
this.context.onUpdate({
type: EDITOR_ACTIONS.MOVE_TO,
payload: {
fromIndex: annotationIndex,
toIndex: desiredIndex,
path: 'layout.annotations',
},
});
}
}

render() {
return <WrappedComponent {...this.props} />;
}
Expand Down Expand Up @@ -85,6 +102,7 @@ export default function connectAnnotationToLayout(WrappedComponent) {
container: PropTypes.object,
fullContainer: PropTypes.object,
getValObject: PropTypes.func,
moveContainer: PropTypes.func,
};

const {plotly_editor_traits} = WrappedComponent;
Expand Down
Loading