mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
Add widget context menu
Widget deletion render is kinda broken, but deletion works
This commit is contained in:
parent
c6677e4553
commit
d9a2ae55a9
4 changed files with 168 additions and 37 deletions
|
@ -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",
|
||||
|
|
|
@ -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)}
|
||||
>
|
||||
<span>{widget.name}</span>
|
||||
<ContextMenuTrigger id={'widgetMenu' + this.props.index} attributes={{ style: { width: '100%', height: '100%' } }}>
|
||||
<div>{widget.name}</div>
|
||||
</ContextMenuTrigger>
|
||||
</Rnd>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="widget" style={{ width: Number(widget.width), height: Number(widget.height), left: Number(widget.x), top: Number(widget.y), position: 'relative' }}>{widget.name}</div>
|
||||
<div className="widget" style={{ width: Number(widget.width), height: Number(widget.height), left: Number(widget.x), top: Number(widget.y), position: 'absolute' }}>
|
||||
{widget.name}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
<div>
|
||||
<h1>{this.state.visualization.name}</h1>
|
||||
|
||||
<div>
|
||||
{this.state.editing ? (
|
||||
<div>
|
||||
<Button bsStyle="link" onClick={() => this.setState({ editing: false })}>Save</Button>
|
||||
<Button bsStyle="link" onClick={() => this.setState({ editing: false })}>Cancel</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Button bsStyle="link" onClick={() => this.setState({ editing: true })}>Edit</Button>
|
||||
)}
|
||||
<h1>
|
||||
{this.state.visualization.name}
|
||||
</h1>
|
||||
|
||||
<div>
|
||||
{this.state.editing ? (
|
||||
<div>
|
||||
<Button bsStyle="link" onClick={() => this.saveChanges()}>Save</Button>
|
||||
<Button bsStyle="link" onClick={() => this.discardChanges()}>Cancel</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Button bsStyle="link" onClick={() => this.setState({ editing: true })}>Edit</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{this.state.editing &&
|
||||
<div className="toolbox">
|
||||
<ToolboxItem name="Value" type="widget" />
|
||||
</div>
|
||||
}
|
||||
<div>
|
||||
{this.state.editing &&
|
||||
<div className="toolbox">
|
||||
<ToolboxItem name="Value" type="widget" />
|
||||
</div>
|
||||
}
|
||||
|
||||
<Dropzone onDrop={item => this.handleDrop(item)} editing={this.state.editing}>
|
||||
{this.state.visualization.widgets != null &&
|
||||
this.state.visualization.widgets.map((widget, index) => (
|
||||
<Widget key={index} data={widget} onWidgetChange={(w, i) => this.widgetChange(w, i)} editing={this.state.editing} index={index} />
|
||||
))}
|
||||
</Dropzone>
|
||||
|
||||
<Dropzone onDrop={item => this.handleDrop(item)} editing={this.state.editing}>
|
||||
{this.state.visualization.widgets != null &&
|
||||
this.state.visualization.widgets.map((widget, index) => (
|
||||
<Widget key={index} data={widget} onWidgetChange={this.widgetChange} editing={this.state.editing} />
|
||||
<ContextMenu id={'widgetMenu' + index} key={index}>
|
||||
<MenuItem data={{index: index}} onClick={(e, data) => this.editWidget(e, data)}>Edit</MenuItem>
|
||||
<MenuItem data={{index: index}} onClick={(e, data) => this.deleteWidget(e, data)}>Delete</MenuItem>
|
||||
</ContextMenu>
|
||||
))}
|
||||
</Dropzone>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue