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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ The [custom editor example](https://github.com/plotly/react-plotly.js-editor/tre
* `<Fold />`: collapsable container within a `<Panel />`
* `<Section />`: uncollapsable container within a `<Panel />` or `<Fold />`
* `<MenuPanel />`: container child of `<Section />` that appears when a cog icon in the section title is clicked on
* `<SingleSidebarItem/>`: wraps any item you would like to see appear in the sidebar menu.

### General-purpose Fields

Expand All @@ -154,6 +155,12 @@ All Fields except `<Info />` accept an `attr` property to bind them to a key in
<img src="examples/components.png" alt="Components" width="432" height="692" border="1">
</p>

### Widgets

Simple component that takes in props and renders.

* `<Button/>`: simple button component, useful when combined with `<SingleSidebarItem/>` to add as menu item

### Special-Purpose Containers

* `<TraceAccordion />`: `<Panel />` whose children are replicated into `<Folds />` connected to traces via `connectTraceToPlot()`.
Expand Down
16 changes: 16 additions & 0 deletions examples/custom/src/CustomEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
Numeric,
LayoutPanel,
MenuPanel,
Button,
SingleSidebarItem,
} from 'react-plotly.js-editor';

export default class CustomEditor extends Component {
Expand Down Expand Up @@ -69,6 +71,20 @@ export default class CustomEditor extends Component {
</Section>
</Fold>
</LayoutPanel>
<SingleSidebarItem>
<Button
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeh I like this API!

variant="primary"
label="save"
onClick={() => alert('save button clicked!')}
/>
</SingleSidebarItem>
<SingleSidebarItem>
<Button
variant="secondary"
label="clear"
onClick={() => alert('clear button clicked!')}
/>
</SingleSidebarItem>
</PanelMenuWrapper>
);
}
Expand Down
63 changes: 33 additions & 30 deletions src/components/PanelMenuWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React, {cloneElement, Component} from 'react';
import SidebarGroup from './sidebar/SidebarGroup';
import {bem} from 'lib';
import SingleSidebarItem from './containers/SingleSidebarItem';

class PanelsWithSidebar extends Component {
constructor(props) {
Expand All @@ -15,57 +16,59 @@ class PanelsWithSidebar extends Component {
};

this.setPanel = this.setPanel.bind(this);
this.renderGroup = this.renderGroup.bind(this);
this.renderSection = this.renderSection.bind(this);
}

setPanel(group, panel) {
this.setState({group, panel});
}

renderGroup(group, i) {
renderSection(section, i) {
if (section.type && section.type === SingleSidebarItem) {
const sectionWithKey = cloneElement(section, {key: i});
return <div>{sectionWithKey}</div>;
}
return (
<SidebarGroup
key={i}
selectedGroup={this.state.group}
selectedPanel={this.state.panel}
group={group.name}
panels={group.panels}
group={section.name}
panels={section.panels}
onChangeGroup={this.setPanel}
/>
);
}

computeMenuOptions(props) {
let obj, child, group, name;

let {children} = props;

if (!Array.isArray(children)) {
children = [children];
}

const {children} = props;
const sections = [];
const groupLookup = {};
let groupIndex;
const groups = [];

for (let i = 0; i < children.length; i++) {
child = children[i];
group = child.props.group;
name = child.props.name;

if (groupLookup.hasOwnProperty(group)) {
groupIndex = groupLookup[group];
obj = groups[groupIndex];
} else {
groupLookup[group] = groups.length;
obj = {name: group, panels: []};
groups.push(obj);

React.Children.forEach(children, child => {
const group = child.props.group;
const name = child.props.name;

if (group && name) {
let obj;
if (groupLookup.hasOwnProperty(group)) {
groupIndex = groupLookup[group];
obj = sections[groupIndex];
} else {
groupLookup[group] = sections.length;
obj = {name: group, panels: []};
sections.push(obj);
}
obj.panels.push(name);
}

obj.panels.push(name);
}
if (child.type === SingleSidebarItem) {
sections.push(child);
}
});

return groups;
return sections;
}

render() {
Expand All @@ -77,7 +80,7 @@ class PanelsWithSidebar extends Component {

return (
<div className={bem('plotly-editor', 'wrapper')}>
<div className={bem('sidebar')}>{menuOpts.map(this.renderGroup)}</div>
<div className={bem('sidebar')}>{menuOpts.map(this.renderSection)}</div>
{children.map((child, i) =>
cloneElement(child, {
key: i,
Expand Down
22 changes: 22 additions & 0 deletions src/components/containers/SingleSidebarItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import PropTypes from 'prop-types';
import React, {cloneElement, Component} from 'react';
import Button from '../widgets/Button';

export default class SingleSidebarItem extends Component {
render() {
let {children} = this.props;
children = React.Children.map(children, child => {
if (child.type === Button) {
return cloneElement(child, {className: 'button--menu'});
}
return child;
});
return children ? (
<div className="sidebar__item--single">{children}</div>
) : null;
}
}

SingleSidebarItem.propTypes = {
children: PropTypes.any,
};
2 changes: 2 additions & 0 deletions src/components/containers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import TraceAccordion from './TraceAccordion';
import TraceMarkerSection from './TraceMarkerSection';
import {LayoutPanel, AxesFold} from './derived';
import TraceRequiredPanel from './TraceRequiredPanel';
import SingleSidebarItem from './SingleSidebarItem';

export {
AnnotationAccordion,
Expand All @@ -19,4 +20,5 @@ export {
TraceRequiredPanel,
LayoutPanel,
AxesFold,
SingleSidebarItem,
};
9 changes: 7 additions & 2 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,20 @@ import {

import {
AnnotationAccordion,
AxesFold,
Fold,
LayoutPanel,
MenuPanel,
Panel,
Section,
TraceAccordion,
TraceMarkerSection,
TraceRequiredPanel,
LayoutPanel,
AxesFold,
SingleSidebarItem,
} from './containers';

import {Button} from './widgets';

import PanelMenuWrapper from './PanelMenuWrapper';

export {
Expand Down Expand Up @@ -70,4 +73,6 @@ export {
TraceSelector,
LayoutPanel,
AxesFold,
Button,
SingleSidebarItem,
};
12 changes: 7 additions & 5 deletions src/components/widgets/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class Button extends Component {
}

render() {
const {variant, className, icon, label, children, ...rest} = this.props;
const {children, className, icon, label, variant, ...rest} = this.props;

let classes = `button`;

Expand All @@ -18,7 +18,9 @@ class Button extends Component {
classes += ` button--default`;
}

classes += ` ${className}`;
if (className) {
classes += ` ${className}`;
}

const Icon = icon ? (
<div className={bem('button', 'icon')}>{icon}</div>
Expand All @@ -36,11 +38,11 @@ class Button extends Component {
}

Button.propTypes = {
variant: PropTypes.string,
label: PropTypes.any,
className: PropTypes.any,
children: PropTypes.node,
className: PropTypes.any,
icon: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
label: PropTypes.any,
variant: PropTypes.string,
};

export default Button;
2 changes: 2 additions & 0 deletions src/components/widgets/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Button from './Button';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hadn't added a widget index since widgets are meant to be internal. That said I am using some in the batch app so apparently they are not that internal. I suppose we may as expose more widgets!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could put Dropdown in here eventually as well (and other useful components folks may wish to use in their apps)

export {Button};
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import {
TraceSelector,
LayoutPanel,
AxesFold,
Button,
SingleSidebarItem,
} from './components';

export {
Expand Down Expand Up @@ -92,6 +94,8 @@ export {
walkObject,
LayoutPanel,
AxesFold,
Button,
SingleSidebarItem,
};

export default PlotlyEditor;
5 changes: 5 additions & 0 deletions src/styles/components/sidebar/_main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@
border-bottom: var(--border-light);
position: relative;
overflow: hidden;

&--single {
margin-top: 15px;
}

&::before {
content: '';
position: absolute;
Expand Down
4 changes: 3 additions & 1 deletion src/styles/components/widgets/_button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ button {
padding-left: 0;
}
}

&--menu {
min-width: 75px;
}
&--default {
@include button();
}
Expand Down