diff --git a/package.json b/package.json
index 1830bf3..d794de0 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
"immutable": "^3.8.1",
"react": "^15.4.2",
"react-bootstrap": "^0.30.7",
+ "react-contextmenu": "^2.3.0",
"react-d3": "^0.4.0",
"react-dnd": "^2.2.4",
"react-dnd-html5-backend": "^2.2.4",
diff --git a/src/components/widget.js b/src/components/widget.js
index fa37ce3..424d799 100644
--- a/src/components/widget.js
+++ b/src/components/widget.js
@@ -9,24 +9,18 @@
import React, { Component } from 'react';
import Rnd from 'react-rnd';
+import { ContextMenuTrigger } from 'react-contextmenu';
import '../styles/widgets.css';
class Widget extends Component {
- constructor(props) {
- super(props);
-
- this.resizeStop = this.resizeStop.bind(this);
- this.dragStop = this.dragStop.bind(this);
- }
-
resizeStop(direction, styleSize, clientSize, delta) {
// update widget
var widget = this.props.data;
widget.width = styleSize.width;
widget.height = styleSize.height;
- this.props.onWidgetChange(widget);
+ this.props.onWidgetChange(widget, this.props.index);
}
dragStop(event, ui) {
@@ -35,7 +29,7 @@ class Widget extends Component {
widget.x = ui.position.left;
widget.y = ui.position.top;
- this.props.onWidgetChange(widget);
+ this.props.onWidgetChange(widget, this.props.index);
}
render() {
@@ -48,15 +42,19 @@ class Widget extends Component {
initial={{ x: Number(widget.x), y: Number(widget.y), width: widget.width, height: widget.height }}
bounds={'parent'}
className="widget"
- onResizeStop={this.resizeStop}
- onDragStop={this.dragStop}
+ onResizeStop={(direction, styleSize, clientSize, delta) => this.resizeStop(direction, styleSize, clientSize, delta)}
+ onDragStop={(event, ui) => this.dragStop(event, ui)}
>
- {widget.name}
+
);
} else {
return (
-
{widget.name}
+
+ {widget.name}
+
);
}
}
diff --git a/src/containers/visualization.js b/src/containers/visualization.js
index dc260f0..5257c9b 100644
--- a/src/containers/visualization.js
+++ b/src/containers/visualization.js
@@ -10,6 +10,7 @@
import React, { Component } from 'react';
import { Container } from 'flux/utils';
import { Button } from 'react-bootstrap';
+import { ContextMenu, MenuItem } from 'react-contextmenu';
import ToolboxItem from '../components/toolbox-item';
import Dropzone from '../components/dropzone';
@@ -32,11 +33,59 @@ class Visualization extends Component {
}
handleDrop(item) {
- console.log(item);
+ // add new widget
+ var widget = {
+ name: 'Name',
+ type: item.name,
+ width: 100,
+ height: 100,
+ x: 0,
+ y: 0,
+ z: 0
+ };
+
+ var visualization = this.state.visualization;
+ visualization.widgets.push(widget);
+
+ this.setState({ visualization: visualization });
+ this.forceUpdate();
}
- widgetChange(widget) {
- console.log(widget);
+ widgetChange(widget, index) {
+ // save changes temporarily
+ var visualization = this.state.visualization;
+ visualization.widgets[index] = widget;
+
+ this.setState({ visualization: visualization });
+ }
+
+ editWidget(e, data) {
+
+ }
+
+ deleteWidget(e, data) {
+ // delete widget temporarily
+ var visualization = this.state.visualization;
+ visualization.widgets.splice(data.index, 1);
+
+ this.setState({ visualization: visualization });
+ this.forceUpdate();
+ }
+
+ saveChanges() {
+ AppDispatcher.dispatch({
+ type: 'visualizations/start-edit',
+ data: this.state.visualization
+ });
+
+ this.setState({ editing: false });
+ }
+
+ discardChanges() {
+ this.setState({ editing: false, visualization: {} });
+
+ this.reloadVisualization();
+ this.forceUpdate();
}
componentWillMount() {
@@ -55,39 +104,56 @@ class Visualization extends Component {
// select visualization by param id
this.state.visualizations.forEach((visualization) => {
if (visualization._id === this.props.params.visualization) {
- this.setState({ visualization: visualization });
+ // JSON.parse(JSON.stringify(obj)) = deep clone to make also copy of widget objects inside
+ this.setState({ visualization: JSON.parse(JSON.stringify(visualization)) });
}
});
}
render() {
+ console.log(this.state.visualization.widgets);
+
return (
-
{this.state.visualization.name}
-
- {this.state.editing ? (
-
-
-
-
- ) : (
-
- )}
+
+ {this.state.visualization.name}
+
+
+
+ {this.state.editing ? (
+
+
+
+
+ ) : (
+
+ )}
+
- {this.state.editing &&
-
-
-
- }
+
+ {this.state.editing &&
+
+
+
+ }
+
+
this.handleDrop(item)} editing={this.state.editing}>
+ {this.state.visualization.widgets != null &&
+ this.state.visualization.widgets.map((widget, index) => (
+ this.widgetChange(w, i)} editing={this.state.editing} index={index} />
+ ))}
+
-
this.handleDrop(item)} editing={this.state.editing}>
{this.state.visualization.widgets != null &&
this.state.visualization.widgets.map((widget, index) => (
-
+
))}
-
+
);
}
diff --git a/src/styles/widgets.css b/src/styles/widgets.css
index 29f86ca..7fe801b 100644
--- a/src/styles/widgets.css
+++ b/src/styles/widgets.css
@@ -11,7 +11,73 @@
width: 100%;
height: 100%;
- padding: 5px 10px;
-
border: 1px solid lightgray;
}
+
+.react-contextmenu {
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ font-size: 16px;
+ color: #373a3c;
+ text-align: left;
+ background-color: #fff;
+ background-clip: padding-box;
+ border: 1px solid rgba(0,0,0,.15);
+ border-radius: .25rem;
+ outline: none;
+ opacity: 0;
+ pointer-events: none;
+ z-index: 100;
+}
+
+.react-contextmenu.react-contextmenu--visible {
+ opacity: 1;
+ pointer-events: auto;
+}
+
+.react-contextmenu-item {
+ width: 200px;
+ padding: 3px 20px;
+ font-weight: 400;
+ line-height: 1.5;
+ color: #373a3c;
+ text-align: inherit;
+ white-space: nowrap;
+ background: 0 0;
+ border: 0;
+ cursor: pointer;
+}
+
+.react-contextmenu-item.react-contextmenu-item--active,
+.react-contextmenu-item:hover {
+ color: #fff;
+ background-color: #0275d8;
+ border-color: #0275d8;
+ text-decoration: none;
+}
+
+.react-contextmenu-item--divider {
+ margin-bottom: 3px;
+ padding: 2px 0;
+ border-bottom: 1px solid rgba(0,0,0,.15);
+ cursor: inherit;
+}
+.react-contextmenu-item--divider:hover {
+ background-color: transparent;
+ border-color: rgba(0,0,0,.15);
+}
+
+.react-contextmenu-item.react-contextmenu-submenu {
+ padding: 0;
+}
+
+.react-contextmenu-item.react-contextmenu-submenu > .react-contextmenu-item {
+}
+
+.react-contextmenu-item.react-contextmenu-submenu > .react-contextmenu-item:after {
+ content: "▶";
+ display: inline-block;
+ position: absolute;
+ right: 7px;
+}